mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-30 20:33:43 +01:00
Get rid off all MetaManipulation things.
This commit is contained in:
parent
361082813b
commit
3170edfeb6
63 changed files with 2422 additions and 2847 deletions
|
|
@ -51,10 +51,6 @@ public class ImcChecker
|
|||
return entry;
|
||||
}
|
||||
|
||||
public CachedEntry GetDefaultEntry(ImcManipulation imcManip, bool storeCache)
|
||||
=> GetDefaultEntry(new ImcIdentifier(imcManip.PrimaryId, imcManip.Variant, imcManip.ObjectType, imcManip.SecondaryId.Id,
|
||||
imcManip.EquipSlot, imcManip.BodySlot), storeCache);
|
||||
|
||||
private static ImcFile? GetFile(ImcIdentifier identifier)
|
||||
{
|
||||
if (_dataManager == null)
|
||||
|
|
|
|||
|
|
@ -69,10 +69,16 @@ public readonly record struct EqdpIdentifier(PrimaryId SetId, EquipSlot Slot, Ge
|
|||
jObj["Slot"] = Slot.ToString();
|
||||
return jObj;
|
||||
}
|
||||
|
||||
public MetaManipulationType Type
|
||||
=> MetaManipulationType.Eqdp;
|
||||
}
|
||||
|
||||
public readonly record struct EqdpEntryInternal(bool Material, bool Model)
|
||||
{
|
||||
public byte AsByte
|
||||
=> (byte)(Material ? Model ? 3 : 1 : Model ? 2 : 0);
|
||||
|
||||
private EqdpEntryInternal((bool, bool) val)
|
||||
: this(val.Item1, val.Item2)
|
||||
{ }
|
||||
|
|
@ -83,4 +89,7 @@ public readonly record struct EqdpEntryInternal(bool Material, bool Model)
|
|||
|
||||
public EqdpEntry ToEntry(EquipSlot slot)
|
||||
=> Eqdp.FromSlotAndBits(slot, Material, Model);
|
||||
|
||||
public override string ToString()
|
||||
=> $"Material: {Material}, Model: {Model}";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,110 +0,0 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta.Files;
|
||||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public readonly struct EqdpManipulation(EqdpIdentifier identifier, EqdpEntry entry) : IMetaManipulation<EqdpManipulation>
|
||||
{
|
||||
[JsonIgnore]
|
||||
public EqdpIdentifier Identifier { get; } = identifier;
|
||||
|
||||
public EqdpEntry Entry { get; } = entry;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public Gender Gender
|
||||
=> Identifier.Gender;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public ModelRace Race
|
||||
=> Identifier.Race;
|
||||
|
||||
public PrimaryId SetId
|
||||
=> Identifier.SetId;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public EquipSlot Slot
|
||||
=> Identifier.Slot;
|
||||
|
||||
[JsonConstructor]
|
||||
public EqdpManipulation(EqdpEntry entry, EquipSlot slot, Gender gender, ModelRace race, PrimaryId setId)
|
||||
: this(new EqdpIdentifier(setId, slot, Names.CombinedRace(gender, race)), Eqdp.Mask(slot) & entry)
|
||||
{ }
|
||||
|
||||
public EqdpManipulation Copy(EqdpManipulation entry)
|
||||
{
|
||||
if (entry.Slot != Slot)
|
||||
{
|
||||
var (bit1, bit2) = entry.Entry.ToBits(entry.Slot);
|
||||
return new EqdpManipulation(Identifier, Eqdp.FromSlotAndBits(Slot, bit1, bit2));
|
||||
}
|
||||
|
||||
return new EqdpManipulation(Identifier, entry.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)
|
||||
=> Gender == other.Gender
|
||||
&& Race == other.Race
|
||||
&& SetId == other.SetId
|
||||
&& Slot == other.Slot;
|
||||
|
||||
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);
|
||||
|
||||
public int CompareTo(EqdpManipulation other)
|
||||
{
|
||||
var r = Race.CompareTo(other.Race);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
var g = Gender.CompareTo(other.Gender);
|
||||
if (g != 0)
|
||||
return g;
|
||||
|
||||
var set = SetId.Id.CompareTo(other.SetId.Id);
|
||||
return set != 0 ? set : Slot.CompareTo(other.Slot);
|
||||
}
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
=> CharacterUtilityData.EqdpIdx(Names.CombinedRace(Gender, Race), Slot.IsAccessory());
|
||||
|
||||
public bool Apply(ExpandedEqdpFile file)
|
||||
{
|
||||
var entry = file[SetId];
|
||||
var mask = Eqdp.Mask(Slot);
|
||||
if ((entry & mask) == Entry)
|
||||
return false;
|
||||
|
||||
file[SetId] = (entry & ~mask) | Entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -50,6 +50,9 @@ public readonly record struct EqpIdentifier(PrimaryId SetId, EquipSlot Slot) : I
|
|||
jObj["Slot"] = Slot.ToString();
|
||||
return jObj;
|
||||
}
|
||||
|
||||
public MetaManipulationType Type
|
||||
=> MetaManipulationType.Eqp;
|
||||
}
|
||||
|
||||
public readonly record struct EqpEntryInternal(uint Value)
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public readonly struct EqpManipulation(EqpIdentifier identifier, EqpEntry entry) : IMetaManipulation<EqpManipulation>
|
||||
{
|
||||
[JsonIgnore]
|
||||
public EqpIdentifier Identifier { get; } = identifier;
|
||||
|
||||
[JsonConverter(typeof(ForceNumericFlagEnumConverter))]
|
||||
public EqpEntry Entry { get; } = entry;
|
||||
|
||||
public PrimaryId SetId
|
||||
=> Identifier.SetId;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public EquipSlot Slot
|
||||
=> Identifier.Slot;
|
||||
|
||||
[JsonConstructor]
|
||||
public EqpManipulation(EqpEntry entry, EquipSlot slot, PrimaryId setId)
|
||||
: this(new EqpIdentifier(setId, slot), Eqp.Mask(slot) & entry)
|
||||
{ }
|
||||
|
||||
public EqpManipulation Copy(EqpEntry entry)
|
||||
=> new(Identifier, entry);
|
||||
|
||||
public override string ToString()
|
||||
=> $"Eqp - {SetId} - {Slot}";
|
||||
|
||||
public bool Equals(EqpManipulation other)
|
||||
=> Slot == other.Slot
|
||||
&& SetId == other.SetId;
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is EqpManipulation other && Equals(other);
|
||||
|
||||
public override int GetHashCode()
|
||||
=> HashCode.Combine((int)Slot, SetId);
|
||||
|
||||
public int CompareTo(EqpManipulation other)
|
||||
{
|
||||
var set = SetId.Id.CompareTo(other.SetId.Id);
|
||||
return set != 0 ? set : Slot.CompareTo(other.Slot);
|
||||
}
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
=> MetaIndex.Eqp;
|
||||
|
||||
public bool Apply(ExpandedEqpFile file)
|
||||
{
|
||||
var entry = file[SetId];
|
||||
var mask = Eqp.Mask(Slot);
|
||||
if ((entry & mask) == Entry)
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -91,6 +91,9 @@ public readonly record struct EstIdentifier(PrimaryId SetId, EstType Slot, Gende
|
|||
jObj["Slot"] = Slot.ToString();
|
||||
return jObj;
|
||||
}
|
||||
|
||||
public MetaManipulationType Type
|
||||
=> MetaManipulationType.Est;
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(Converter))]
|
||||
|
|
@ -111,3 +114,16 @@ public readonly record struct EstEntry(ushort Value)
|
|||
=> new(serializer.Deserialize<ushort>(reader));
|
||||
}
|
||||
}
|
||||
|
||||
public static class EstTypeExtension
|
||||
{
|
||||
public static string ToName(this EstType type)
|
||||
=> type switch
|
||||
{
|
||||
EstType.Hair => "hair",
|
||||
EstType.Face => "face",
|
||||
EstType.Body => "top",
|
||||
EstType.Head => "met",
|
||||
_ => "unk",
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,108 +0,0 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta.Files;
|
||||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public readonly struct EstManipulation(EstIdentifier identifier, EstEntry entry) : IMetaManipulation<EstManipulation>
|
||||
{
|
||||
public static string ToName(EstType type)
|
||||
=> type switch
|
||||
{
|
||||
EstType.Hair => "hair",
|
||||
EstType.Face => "face",
|
||||
EstType.Body => "top",
|
||||
EstType.Head => "met",
|
||||
_ => "unk",
|
||||
};
|
||||
|
||||
[JsonIgnore]
|
||||
public EstIdentifier Identifier { get; } = identifier;
|
||||
public EstEntry Entry { get; } = entry;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public Gender Gender
|
||||
=> Identifier.Gender;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public ModelRace Race
|
||||
=> Identifier.Race;
|
||||
|
||||
public PrimaryId SetId
|
||||
=> Identifier.SetId;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public EstType Slot
|
||||
=> Identifier.Slot;
|
||||
|
||||
|
||||
[JsonConstructor]
|
||||
public EstManipulation(Gender gender, ModelRace race, EstType slot, PrimaryId setId, EstEntry entry)
|
||||
: this(new EstIdentifier(setId, slot, Names.CombinedRace(gender, race)), entry)
|
||||
{ }
|
||||
|
||||
public EstManipulation Copy(EstEntry entry)
|
||||
=> new(Identifier, entry);
|
||||
|
||||
|
||||
public override string ToString()
|
||||
=> $"Est - {SetId} - {Slot} - {Race.ToName()} {Gender.ToName()}";
|
||||
|
||||
public bool Equals(EstManipulation other)
|
||||
=> Gender == other.Gender
|
||||
&& Race == other.Race
|
||||
&& SetId == other.SetId
|
||||
&& Slot == other.Slot;
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is EstManipulation other && Equals(other);
|
||||
|
||||
public override int GetHashCode()
|
||||
=> HashCode.Combine((int)Gender, (int)Race, SetId, (int)Slot);
|
||||
|
||||
public int CompareTo(EstManipulation other)
|
||||
{
|
||||
var r = Race.CompareTo(other.Race);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
var g = Gender.CompareTo(other.Gender);
|
||||
if (g != 0)
|
||||
return g;
|
||||
|
||||
var s = Slot.CompareTo(other.Slot);
|
||||
return s != 0 ? s : SetId.Id.CompareTo(other.SetId.Id);
|
||||
}
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
=> (MetaIndex)Slot;
|
||||
|
||||
public bool Apply(EstFile file)
|
||||
{
|
||||
return file.SetEntry(Names.CombinedRace(Gender, Race), SetId.Id, Entry) switch
|
||||
{
|
||||
EstFile.EstEntryChange.Unchanged => false,
|
||||
EstFile.EstEntryChange.Changed => true,
|
||||
EstFile.EstEntryChange.Added => true,
|
||||
EstFile.EstEntryChange.Removed => true,
|
||||
_ => 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
public struct GlobalEqpCache : IService
|
||||
{
|
||||
private readonly HashSet<PrimaryId> _doNotHideEarrings = [];
|
||||
private readonly HashSet<PrimaryId> _doNotHideNecklace = [];
|
||||
private readonly HashSet<PrimaryId> _doNotHideBracelets = [];
|
||||
private readonly HashSet<PrimaryId> _doNotHideRingL = [];
|
||||
private readonly HashSet<PrimaryId> _doNotHideRingR = [];
|
||||
private bool _doNotHideVieraHats;
|
||||
private bool _doNotHideHrothgarHats;
|
||||
|
||||
public GlobalEqpCache()
|
||||
{ }
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_doNotHideEarrings.Clear();
|
||||
_doNotHideNecklace.Clear();
|
||||
_doNotHideBracelets.Clear();
|
||||
_doNotHideRingL.Clear();
|
||||
_doNotHideRingR.Clear();
|
||||
_doNotHideHrothgarHats = false;
|
||||
_doNotHideVieraHats = false;
|
||||
}
|
||||
|
||||
public unsafe EqpEntry Apply(EqpEntry original, CharacterArmor* armor)
|
||||
{
|
||||
if (_doNotHideVieraHats)
|
||||
original |= EqpEntry.HeadShowVieraHat;
|
||||
|
||||
if (_doNotHideHrothgarHats)
|
||||
original |= EqpEntry.HeadShowHrothgarHat;
|
||||
|
||||
if (_doNotHideEarrings.Contains(armor[5].Set))
|
||||
original |= EqpEntry.HeadShowEarrings | EqpEntry.HeadShowEarringsAura | EqpEntry.HeadShowEarringsHuman;
|
||||
|
||||
if (_doNotHideNecklace.Contains(armor[6].Set))
|
||||
original |= EqpEntry.BodyShowNecklace | EqpEntry.HeadShowNecklace;
|
||||
|
||||
if (_doNotHideBracelets.Contains(armor[7].Set))
|
||||
original |= EqpEntry.BodyShowBracelet | EqpEntry.HandShowBracelet;
|
||||
|
||||
if (_doNotHideRingR.Contains(armor[8].Set))
|
||||
original |= EqpEntry.HandShowRingR;
|
||||
|
||||
if (_doNotHideRingL.Contains(armor[9].Set))
|
||||
original |= EqpEntry.HandShowRingL;
|
||||
return original;
|
||||
}
|
||||
|
||||
public bool Add(GlobalEqpManipulation manipulation)
|
||||
=> manipulation.Type switch
|
||||
{
|
||||
GlobalEqpType.DoNotHideEarrings => _doNotHideEarrings.Add(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideNecklace => _doNotHideNecklace.Add(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideBracelets => _doNotHideBracelets.Add(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideRingR => _doNotHideRingR.Add(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideRingL => _doNotHideRingL.Add(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideHrothgarHats => !_doNotHideHrothgarHats && (_doNotHideHrothgarHats = true),
|
||||
GlobalEqpType.DoNotHideVieraHats => !_doNotHideVieraHats && (_doNotHideVieraHats = true),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
public bool Remove(GlobalEqpManipulation manipulation)
|
||||
=> manipulation.Type switch
|
||||
{
|
||||
GlobalEqpType.DoNotHideEarrings => _doNotHideEarrings.Remove(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideNecklace => _doNotHideNecklace.Remove(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideBracelets => _doNotHideBracelets.Remove(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideRingR => _doNotHideRingR.Remove(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideRingL => _doNotHideRingL.Remove(manipulation.Condition),
|
||||
GlobalEqpType.DoNotHideHrothgarHats => _doNotHideHrothgarHats && !(_doNotHideHrothgarHats = false),
|
||||
GlobalEqpType.DoNotHideVieraHats => _doNotHideVieraHats && !(_doNotHideVieraHats = false),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.Structs;
|
||||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
public readonly struct GlobalEqpManipulation : IMetaManipulation<GlobalEqpManipulation>, IMetaIdentifier
|
||||
public readonly struct GlobalEqpManipulation : IMetaIdentifier
|
||||
{
|
||||
public GlobalEqpType Type { get; init; }
|
||||
public PrimaryId Condition { get; init; }
|
||||
|
|
@ -70,8 +71,29 @@ public readonly struct GlobalEqpManipulation : IMetaManipulation<GlobalEqpManipu
|
|||
=> $"Global EQP - {Type}{(Condition != 0 ? $" - {Condition.Id}" : string.Empty)}";
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, object?> changedItems)
|
||||
{ }
|
||||
{
|
||||
var path = Type switch
|
||||
{
|
||||
GlobalEqpType.DoNotHideEarrings => GamePaths.Accessory.Mdl.Path(Condition, GenderRace.MidlanderMale, EquipSlot.Ears),
|
||||
GlobalEqpType.DoNotHideNecklace => GamePaths.Accessory.Mdl.Path(Condition, GenderRace.MidlanderMale, EquipSlot.Neck),
|
||||
GlobalEqpType.DoNotHideBracelets => GamePaths.Accessory.Mdl.Path(Condition, GenderRace.MidlanderMale, EquipSlot.Wrists),
|
||||
GlobalEqpType.DoNotHideRingR => GamePaths.Accessory.Mdl.Path(Condition, GenderRace.MidlanderMale, EquipSlot.RFinger),
|
||||
GlobalEqpType.DoNotHideRingL => GamePaths.Accessory.Mdl.Path(Condition, GenderRace.MidlanderMale, EquipSlot.LFinger),
|
||||
GlobalEqpType.DoNotHideHrothgarHats => string.Empty,
|
||||
GlobalEqpType.DoNotHideVieraHats => string.Empty,
|
||||
_ => string.Empty,
|
||||
};
|
||||
if (path.Length > 0)
|
||||
identifier.Identify(changedItems, path);
|
||||
else if (Type is GlobalEqpType.DoNotHideVieraHats)
|
||||
changedItems["All Hats for Viera"] = null;
|
||||
else if (Type is GlobalEqpType.DoNotHideHrothgarHats)
|
||||
changedItems["All Hats for Hrothgar"] = null;
|
||||
}
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
=> MetaIndex.Eqp;
|
||||
|
||||
MetaManipulationType IMetaIdentifier.Type
|
||||
=> MetaManipulationType.GlobalEqp;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,4 +36,7 @@ public readonly record struct GmpIdentifier(PrimaryId SetId) : IMetaIdentifier,
|
|||
jObj["SetId"] = SetId.Id.ToString();
|
||||
return jObj;
|
||||
}
|
||||
|
||||
public MetaManipulationType Type
|
||||
=> MetaManipulationType.Gmp;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
using Newtonsoft.Json;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta.Files;
|
||||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public readonly struct GmpManipulation(GmpIdentifier identifier, GmpEntry entry) : IMetaManipulation<GmpManipulation>
|
||||
{
|
||||
[JsonIgnore]
|
||||
public GmpIdentifier Identifier { get; } = identifier;
|
||||
|
||||
public GmpEntry Entry { get; } = entry;
|
||||
|
||||
public PrimaryId SetId
|
||||
=> Identifier.SetId;
|
||||
|
||||
[JsonConstructor]
|
||||
public GmpManipulation(GmpEntry entry, PrimaryId setId)
|
||||
: this(new GmpIdentifier(setId), entry)
|
||||
{ }
|
||||
|
||||
public GmpManipulation Copy(GmpEntry entry)
|
||||
=> new(Identifier, entry);
|
||||
|
||||
public override string ToString()
|
||||
=> $"Gmp - {SetId}";
|
||||
|
||||
public bool Equals(GmpManipulation other)
|
||||
=> SetId == other.SetId;
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is GmpManipulation other && Equals(other);
|
||||
|
||||
public override int GetHashCode()
|
||||
=> SetId.GetHashCode();
|
||||
|
||||
public int CompareTo(GmpManipulation other)
|
||||
=> SetId.Id.CompareTo(other.SetId.Id);
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
=> MetaIndex.Gmp;
|
||||
|
||||
public bool Apply(ExpandedGmpFile file)
|
||||
{
|
||||
var entry = file[SetId];
|
||||
if (entry == Entry)
|
||||
return false;
|
||||
|
||||
file[SetId] = Entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Validate()
|
||||
// No known conditions.
|
||||
=> true;
|
||||
}
|
||||
|
|
@ -4,6 +4,18 @@ using Penumbra.Interop.Structs;
|
|||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
public enum MetaManipulationType : byte
|
||||
{
|
||||
Unknown = 0,
|
||||
Imc = 1,
|
||||
Eqdp = 2,
|
||||
Eqp = 3,
|
||||
Est = 4,
|
||||
Gmp = 5,
|
||||
Rsp = 6,
|
||||
GlobalEqp = 7,
|
||||
}
|
||||
|
||||
public interface IMetaIdentifier
|
||||
{
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, object?> changedItems);
|
||||
|
|
@ -13,4 +25,8 @@ public interface IMetaIdentifier
|
|||
public bool Validate();
|
||||
|
||||
public JObject AddToJson(JObject jObj);
|
||||
|
||||
public MetaManipulationType Type { get; }
|
||||
|
||||
public string ToString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,6 @@ public readonly record struct ImcIdentifier(
|
|||
: this(primaryId, variant, slot.IsAccessory() ? ObjectType.Accessory : ObjectType.Equipment, 0, slot, BodySlot.Unknown)
|
||||
{ }
|
||||
|
||||
public ImcManipulation ToManipulation(ImcEntry entry)
|
||||
=> new(ObjectType, BodySlot, PrimaryId, SecondaryId.Id, Variant.Id, EquipSlot, entry);
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, object?> changedItems)
|
||||
=> AddChangedItems(identifier, changedItems, false);
|
||||
|
||||
|
|
@ -193,4 +190,7 @@ public readonly record struct ImcIdentifier(
|
|||
jObj["BodySlot"] = BodySlot.ToString();
|
||||
return jObj;
|
||||
}
|
||||
|
||||
public MetaManipulationType Type
|
||||
=> MetaManipulationType.Imc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,108 +0,0 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta.Files;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public readonly struct ImcManipulation : IMetaManipulation<ImcManipulation>
|
||||
{
|
||||
[JsonIgnore]
|
||||
public ImcIdentifier Identifier { get; private init; }
|
||||
|
||||
public ImcEntry Entry { get; private init; }
|
||||
|
||||
|
||||
public PrimaryId PrimaryId
|
||||
=> Identifier.PrimaryId;
|
||||
|
||||
public SecondaryId SecondaryId
|
||||
=> Identifier.SecondaryId;
|
||||
|
||||
public Variant Variant
|
||||
=> Identifier.Variant;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public ObjectType ObjectType
|
||||
=> Identifier.ObjectType;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public EquipSlot EquipSlot
|
||||
=> Identifier.EquipSlot;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public BodySlot BodySlot
|
||||
=> Identifier.BodySlot;
|
||||
|
||||
public ImcManipulation(EquipSlot equipSlot, ushort variant, PrimaryId primaryId, ImcEntry entry)
|
||||
: this(new ImcIdentifier(equipSlot, primaryId, variant), entry)
|
||||
{ }
|
||||
|
||||
public ImcManipulation(ImcIdentifier identifier, ImcEntry entry)
|
||||
{
|
||||
Identifier = identifier;
|
||||
Entry = entry;
|
||||
}
|
||||
|
||||
|
||||
// Variants were initially ushorts but got shortened to bytes.
|
||||
// There are still some manipulations around that have values > 255 for variant,
|
||||
// so we change the unused value to something nonsensical in that case, just so they do not compare equal,
|
||||
// and clamp the variant to 255.
|
||||
[JsonConstructor]
|
||||
internal ImcManipulation(ObjectType objectType, BodySlot bodySlot, PrimaryId primaryId, SecondaryId secondaryId, ushort variant,
|
||||
EquipSlot equipSlot, ImcEntry entry)
|
||||
{
|
||||
Entry = entry;
|
||||
var v = (Variant)Math.Clamp(variant, (ushort)0, byte.MaxValue);
|
||||
Identifier = objectType switch
|
||||
{
|
||||
ObjectType.Accessory or ObjectType.Equipment => new ImcIdentifier(primaryId, v, objectType, 0, equipSlot,
|
||||
variant > byte.MaxValue ? BodySlot.Body : BodySlot.Unknown),
|
||||
ObjectType.DemiHuman => new ImcIdentifier(primaryId, v, objectType, secondaryId, equipSlot, variant > byte.MaxValue ? BodySlot.Body : BodySlot.Unknown),
|
||||
_ => new ImcIdentifier(primaryId, v, objectType, secondaryId, equipSlot, bodySlot == BodySlot.Unknown ? BodySlot.Body : BodySlot.Unknown),
|
||||
};
|
||||
}
|
||||
|
||||
public ImcManipulation Copy(ImcEntry entry)
|
||||
=> new(Identifier, entry);
|
||||
|
||||
public override string ToString()
|
||||
=> Identifier.ToString();
|
||||
|
||||
public bool Equals(ImcManipulation other)
|
||||
=> Identifier == other.Identifier;
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is ImcManipulation other && Equals(other);
|
||||
|
||||
public override int GetHashCode()
|
||||
=> Identifier.GetHashCode();
|
||||
|
||||
public int CompareTo(ImcManipulation other)
|
||||
=> Identifier.CompareTo(other.Identifier);
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
=> Identifier.FileIndex();
|
||||
|
||||
public Utf8GamePath GamePath()
|
||||
=> Identifier.GamePath();
|
||||
|
||||
public bool Apply(ImcFile file)
|
||||
=> file.SetEntry(ImcFile.PartIndex(EquipSlot), Variant.Id, Entry);
|
||||
|
||||
public bool Validate(bool withMaterial)
|
||||
{
|
||||
if (!Identifier.Validate())
|
||||
return false;
|
||||
|
||||
if (withMaterial && Entry.MaterialId == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.Collections.Cache;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Util;
|
||||
using ImcEntry = Penumbra.GameData.Structs.ImcEntry;
|
||||
|
|
@ -7,7 +8,7 @@ using ImcEntry = Penumbra.GameData.Structs.ImcEntry;
|
|||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
[JsonConverter(typeof(Converter))]
|
||||
public class MetaDictionary : IEnumerable<MetaManipulation>
|
||||
public class MetaDictionary
|
||||
{
|
||||
private readonly Dictionary<ImcIdentifier, ImcEntry> _imc = [];
|
||||
private readonly Dictionary<EqpIdentifier, EqpEntryInternal> _eqp = [];
|
||||
|
|
@ -20,32 +21,50 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
public IReadOnlyDictionary<ImcIdentifier, ImcEntry> Imc
|
||||
=> _imc;
|
||||
|
||||
public IReadOnlyDictionary<EqpIdentifier, EqpEntryInternal> Eqp
|
||||
=> _eqp;
|
||||
|
||||
public IReadOnlyDictionary<EqdpIdentifier, EqdpEntryInternal> Eqdp
|
||||
=> _eqdp;
|
||||
|
||||
public IReadOnlyDictionary<EstIdentifier, EstEntry> Est
|
||||
=> _est;
|
||||
|
||||
public IReadOnlyDictionary<GmpIdentifier, GmpEntry> Gmp
|
||||
=> _gmp;
|
||||
|
||||
public IReadOnlyDictionary<RspIdentifier, RspEntry> Rsp
|
||||
=> _rsp;
|
||||
|
||||
public IReadOnlySet<GlobalEqpManipulation> GlobalEqp
|
||||
=> _globalEqp;
|
||||
|
||||
public int Count { get; private set; }
|
||||
|
||||
public int GetCount(MetaManipulation.Type type)
|
||||
public int GetCount(MetaManipulationType type)
|
||||
=> type switch
|
||||
{
|
||||
MetaManipulation.Type.Imc => _imc.Count,
|
||||
MetaManipulation.Type.Eqdp => _eqdp.Count,
|
||||
MetaManipulation.Type.Eqp => _eqp.Count,
|
||||
MetaManipulation.Type.Est => _est.Count,
|
||||
MetaManipulation.Type.Gmp => _gmp.Count,
|
||||
MetaManipulation.Type.Rsp => _rsp.Count,
|
||||
MetaManipulation.Type.GlobalEqp => _globalEqp.Count,
|
||||
_ => 0,
|
||||
MetaManipulationType.Imc => _imc.Count,
|
||||
MetaManipulationType.Eqdp => _eqdp.Count,
|
||||
MetaManipulationType.Eqp => _eqp.Count,
|
||||
MetaManipulationType.Est => _est.Count,
|
||||
MetaManipulationType.Gmp => _gmp.Count,
|
||||
MetaManipulationType.Rsp => _rsp.Count,
|
||||
MetaManipulationType.GlobalEqp => _globalEqp.Count,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
public bool CanAdd(IMetaIdentifier identifier)
|
||||
public bool Contains(IMetaIdentifier identifier)
|
||||
=> identifier switch
|
||||
{
|
||||
EqdpIdentifier eqdpIdentifier => !_eqdp.ContainsKey(eqdpIdentifier),
|
||||
EqpIdentifier eqpIdentifier => !_eqp.ContainsKey(eqpIdentifier),
|
||||
EstIdentifier estIdentifier => !_est.ContainsKey(estIdentifier),
|
||||
GlobalEqpManipulation globalEqpManipulation => !_globalEqp.Contains(globalEqpManipulation),
|
||||
GmpIdentifier gmpIdentifier => !_gmp.ContainsKey(gmpIdentifier),
|
||||
ImcIdentifier imcIdentifier => !_imc.ContainsKey(imcIdentifier),
|
||||
RspIdentifier rspIdentifier => !_rsp.ContainsKey(rspIdentifier),
|
||||
_ => false,
|
||||
EqdpIdentifier i => _eqdp.ContainsKey(i),
|
||||
EqpIdentifier i => _eqp.ContainsKey(i),
|
||||
EstIdentifier i => _est.ContainsKey(i),
|
||||
GlobalEqpManipulation i => _globalEqp.Contains(i),
|
||||
GmpIdentifier i => _gmp.ContainsKey(i),
|
||||
ImcIdentifier i => _imc.ContainsKey(i),
|
||||
RspIdentifier i => _rsp.ContainsKey(i),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
public void Clear()
|
||||
|
|
@ -69,17 +88,16 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
&& _gmp.SetEquals(other._gmp)
|
||||
&& _globalEqp.SetEquals(other._globalEqp);
|
||||
|
||||
public IEnumerator<MetaManipulation> GetEnumerator()
|
||||
=> _imc.Select(kvp => new MetaManipulation(new ImcManipulation(kvp.Key, kvp.Value)))
|
||||
.Concat(_eqp.Select(kvp => new MetaManipulation(new EqpManipulation(kvp.Key, kvp.Value.ToEntry(kvp.Key.Slot)))))
|
||||
.Concat(_eqdp.Select(kvp => new MetaManipulation(new EqdpManipulation(kvp.Key, kvp.Value.ToEntry(kvp.Key.Slot)))))
|
||||
.Concat(_est.Select(kvp => new MetaManipulation(new EstManipulation(kvp.Key, kvp.Value))))
|
||||
.Concat(_rsp.Select(kvp => new MetaManipulation(new RspManipulation(kvp.Key, kvp.Value))))
|
||||
.Concat(_gmp.Select(kvp => new MetaManipulation(new GmpManipulation(kvp.Key, kvp.Value))))
|
||||
.Concat(_globalEqp.Select(manip => new MetaManipulation(manip))).GetEnumerator();
|
||||
public IEnumerable<IMetaIdentifier> Identifiers
|
||||
=> _imc.Keys.Cast<IMetaIdentifier>()
|
||||
.Concat(_eqdp.Keys.Cast<IMetaIdentifier>())
|
||||
.Concat(_eqp.Keys.Cast<IMetaIdentifier>())
|
||||
.Concat(_est.Keys.Cast<IMetaIdentifier>())
|
||||
.Concat(_gmp.Keys.Cast<IMetaIdentifier>())
|
||||
.Concat(_rsp.Keys.Cast<IMetaIdentifier>())
|
||||
.Concat(_globalEqp.Cast<IMetaIdentifier>());
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
#region TryAdd
|
||||
|
||||
public bool TryAdd(ImcIdentifier identifier, ImcEntry entry)
|
||||
{
|
||||
|
|
@ -90,7 +108,6 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
public bool TryAdd(EqpIdentifier identifier, EqpEntryInternal entry)
|
||||
{
|
||||
if (!_eqp.TryAdd(identifier, entry))
|
||||
|
|
@ -103,7 +120,6 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
public bool TryAdd(EqpIdentifier identifier, EqpEntry entry)
|
||||
=> TryAdd(identifier, new EqpEntryInternal(entry, identifier.Slot));
|
||||
|
||||
|
||||
public bool TryAdd(EqdpIdentifier identifier, EqdpEntryInternal entry)
|
||||
{
|
||||
if (!_eqdp.TryAdd(identifier, entry))
|
||||
|
|
@ -152,6 +168,10 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Update
|
||||
|
||||
public bool Update(ImcIdentifier identifier, ImcEntry entry)
|
||||
{
|
||||
if (!_imc.ContainsKey(identifier))
|
||||
|
|
@ -161,7 +181,6 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
public bool Update(EqpIdentifier identifier, EqpEntryInternal entry)
|
||||
{
|
||||
if (!_eqp.ContainsKey(identifier))
|
||||
|
|
@ -174,7 +193,6 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
public bool Update(EqpIdentifier identifier, EqpEntry entry)
|
||||
=> Update(identifier, new EqpEntryInternal(entry, identifier.Slot));
|
||||
|
||||
|
||||
public bool Update(EqdpIdentifier identifier, EqdpEntryInternal entry)
|
||||
{
|
||||
if (!_eqdp.ContainsKey(identifier))
|
||||
|
|
@ -214,6 +232,50 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TryGetValue
|
||||
|
||||
public bool TryGetValue(EstIdentifier identifier, out EstEntry value)
|
||||
=> _est.TryGetValue(identifier, out value);
|
||||
|
||||
public bool TryGetValue(EqpIdentifier identifier, out EqpEntryInternal value)
|
||||
=> _eqp.TryGetValue(identifier, out value);
|
||||
|
||||
public bool TryGetValue(EqdpIdentifier identifier, out EqdpEntryInternal value)
|
||||
=> _eqdp.TryGetValue(identifier, out value);
|
||||
|
||||
public bool TryGetValue(GmpIdentifier identifier, out GmpEntry value)
|
||||
=> _gmp.TryGetValue(identifier, out value);
|
||||
|
||||
public bool TryGetValue(RspIdentifier identifier, out RspEntry value)
|
||||
=> _rsp.TryGetValue(identifier, out value);
|
||||
|
||||
public bool TryGetValue(ImcIdentifier identifier, out ImcEntry value)
|
||||
=> _imc.TryGetValue(identifier, out value);
|
||||
|
||||
#endregion
|
||||
|
||||
public bool Remove(IMetaIdentifier identifier)
|
||||
{
|
||||
var ret = identifier switch
|
||||
{
|
||||
EqdpIdentifier i => _eqdp.Remove(i),
|
||||
EqpIdentifier i => _eqp.Remove(i),
|
||||
EstIdentifier i => _est.Remove(i),
|
||||
GlobalEqpManipulation i => _globalEqp.Remove(i),
|
||||
GmpIdentifier i => _gmp.Remove(i),
|
||||
ImcIdentifier i => _imc.Remove(i),
|
||||
RspIdentifier i => _rsp.Remove(i),
|
||||
_ => false,
|
||||
};
|
||||
if (ret)
|
||||
--Count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#region Merging
|
||||
|
||||
public void UnionWith(MetaDictionary manips)
|
||||
{
|
||||
foreach (var (identifier, entry) in manips._imc)
|
||||
|
|
@ -287,24 +349,6 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetValue(EstIdentifier identifier, out EstEntry value)
|
||||
=> _est.TryGetValue(identifier, out value);
|
||||
|
||||
public bool TryGetValue(EqpIdentifier identifier, out EqpEntryInternal value)
|
||||
=> _eqp.TryGetValue(identifier, out value);
|
||||
|
||||
public bool TryGetValue(EqdpIdentifier identifier, out EqdpEntryInternal value)
|
||||
=> _eqdp.TryGetValue(identifier, out value);
|
||||
|
||||
public bool TryGetValue(GmpIdentifier identifier, out GmpEntry value)
|
||||
=> _gmp.TryGetValue(identifier, out value);
|
||||
|
||||
public bool TryGetValue(RspIdentifier identifier, out RspEntry value)
|
||||
=> _rsp.TryGetValue(identifier, out value);
|
||||
|
||||
public bool TryGetValue(ImcIdentifier identifier, out ImcEntry value)
|
||||
=> _imc.TryGetValue(identifier, out value);
|
||||
|
||||
public void SetTo(MetaDictionary other)
|
||||
{
|
||||
_imc.SetTo(other._imc);
|
||||
|
|
@ -329,6 +373,8 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
Count = _imc.Count + _eqp.Count + _eqdp.Count + _est.Count + _rsp.Count + _gmp.Count + _globalEqp.Count;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public MetaDictionary Clone()
|
||||
{
|
||||
var ret = new MetaDictionary();
|
||||
|
|
@ -336,29 +382,124 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
return ret;
|
||||
}
|
||||
|
||||
private static void WriteJson(JsonWriter writer, JsonSerializer serializer, IMetaIdentifier identifier, object entry)
|
||||
{
|
||||
var type = identifier switch
|
||||
public static JObject Serialize(EqpIdentifier identifier, EqpEntryInternal entry)
|
||||
=> Serialize(identifier, entry.ToEntry(identifier.Slot));
|
||||
|
||||
public static JObject Serialize(EqpIdentifier identifier, EqpEntry entry)
|
||||
=> new()
|
||||
{
|
||||
ImcIdentifier => "Imc",
|
||||
EqdpIdentifier => "Eqdp",
|
||||
EqpIdentifier => "Eqp",
|
||||
EstIdentifier => "Est",
|
||||
GmpIdentifier => "Gmp",
|
||||
RspIdentifier => "Rsp",
|
||||
GlobalEqpManipulation => "GlobalEqp",
|
||||
_ => string.Empty,
|
||||
["Type"] = MetaManipulationType.Eqp.ToString(),
|
||||
["Manipulation"] = identifier.AddToJson(new JObject
|
||||
{
|
||||
["Entry"] = (ulong)entry,
|
||||
}),
|
||||
};
|
||||
|
||||
if (type.Length == 0)
|
||||
return;
|
||||
public static JObject Serialize(EqdpIdentifier identifier, EqdpEntryInternal entry)
|
||||
=> Serialize(identifier, entry.ToEntry(identifier.Slot));
|
||||
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("Type");
|
||||
writer.WriteValue(type);
|
||||
writer.WritePropertyName("Manipulation");
|
||||
public static JObject Serialize(EqdpIdentifier identifier, EqdpEntry entry)
|
||||
=> new()
|
||||
{
|
||||
["Type"] = MetaManipulationType.Eqdp.ToString(),
|
||||
["Manipulation"] = identifier.AddToJson(new JObject
|
||||
{
|
||||
["Entry"] = (ushort)entry,
|
||||
}),
|
||||
};
|
||||
|
||||
writer.WriteEndObject();
|
||||
public static JObject Serialize(EstIdentifier identifier, EstEntry entry)
|
||||
=> new()
|
||||
{
|
||||
["Type"] = MetaManipulationType.Est.ToString(),
|
||||
["Manipulation"] = identifier.AddToJson(new JObject
|
||||
{
|
||||
["Entry"] = entry.Value,
|
||||
}),
|
||||
};
|
||||
|
||||
public static JObject Serialize(GmpIdentifier identifier, GmpEntry entry)
|
||||
=> new()
|
||||
{
|
||||
["Type"] = MetaManipulationType.Gmp.ToString(),
|
||||
["Manipulation"] = identifier.AddToJson(new JObject
|
||||
{
|
||||
["Entry"] = JObject.FromObject(entry),
|
||||
}),
|
||||
};
|
||||
|
||||
public static JObject Serialize(ImcIdentifier identifier, ImcEntry entry)
|
||||
=> new()
|
||||
{
|
||||
["Type"] = MetaManipulationType.Imc.ToString(),
|
||||
["Manipulation"] = identifier.AddToJson(new JObject
|
||||
{
|
||||
["Entry"] = JObject.FromObject(entry),
|
||||
}),
|
||||
};
|
||||
|
||||
public static JObject Serialize(RspIdentifier identifier, RspEntry entry)
|
||||
=> new()
|
||||
{
|
||||
["Type"] = MetaManipulationType.Rsp.ToString(),
|
||||
["Manipulation"] = identifier.AddToJson(new JObject
|
||||
{
|
||||
["Entry"] = entry.Value,
|
||||
}),
|
||||
};
|
||||
|
||||
public static JObject Serialize(GlobalEqpManipulation identifier)
|
||||
=> new()
|
||||
{
|
||||
["Type"] = MetaManipulationType.GlobalEqp.ToString(),
|
||||
["Manipulation"] = identifier.AddToJson(new JObject()),
|
||||
};
|
||||
|
||||
public static JObject? Serialize<TIdentifier, TEntry>(TIdentifier identifier, TEntry entry)
|
||||
where TIdentifier : unmanaged, IMetaIdentifier
|
||||
where TEntry : unmanaged
|
||||
{
|
||||
if (typeof(TIdentifier) == typeof(EqpIdentifier) && typeof(TEntry) == typeof(EqpEntryInternal))
|
||||
return Serialize(Unsafe.As<TIdentifier, EqpIdentifier>(ref identifier), Unsafe.As<TEntry, EqpEntryInternal>(ref entry));
|
||||
if (typeof(TIdentifier) == typeof(EqpIdentifier) && typeof(TEntry) == typeof(EqpEntry))
|
||||
return Serialize(Unsafe.As<TIdentifier, EqpIdentifier>(ref identifier), Unsafe.As<TEntry, EqpEntry>(ref entry));
|
||||
if (typeof(TIdentifier) == typeof(EqdpIdentifier) && typeof(TEntry) == typeof(EqdpEntryInternal))
|
||||
return Serialize(Unsafe.As<TIdentifier, EqdpIdentifier>(ref identifier), Unsafe.As<TEntry, EqdpEntryInternal>(ref entry));
|
||||
if (typeof(TIdentifier) == typeof(EqdpIdentifier) && typeof(TEntry) == typeof(EqdpEntry))
|
||||
return Serialize(Unsafe.As<TIdentifier, EqdpIdentifier>(ref identifier), Unsafe.As<TEntry, EqdpEntry>(ref entry));
|
||||
if (typeof(TIdentifier) == typeof(EstIdentifier) && typeof(TEntry) == typeof(EstEntry))
|
||||
return Serialize(Unsafe.As<TIdentifier, EstIdentifier>(ref identifier), Unsafe.As<TEntry, EstEntry>(ref entry));
|
||||
if (typeof(TIdentifier) == typeof(GmpIdentifier) && typeof(TEntry) == typeof(GmpEntry))
|
||||
return Serialize(Unsafe.As<TIdentifier, GmpIdentifier>(ref identifier), Unsafe.As<TEntry, GmpEntry>(ref entry));
|
||||
if (typeof(TIdentifier) == typeof(RspIdentifier) && typeof(TEntry) == typeof(RspEntry))
|
||||
return Serialize(Unsafe.As<TIdentifier, RspIdentifier>(ref identifier), Unsafe.As<TEntry, RspEntry>(ref entry));
|
||||
if (typeof(TIdentifier) == typeof(ImcIdentifier) && typeof(TEntry) == typeof(ImcEntry))
|
||||
return Serialize(Unsafe.As<TIdentifier, ImcIdentifier>(ref identifier), Unsafe.As<TEntry, ImcEntry>(ref entry));
|
||||
if (typeof(TIdentifier) == typeof(GlobalEqpManipulation))
|
||||
return Serialize(Unsafe.As<TIdentifier, GlobalEqpManipulation>(ref identifier));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static JArray SerializeTo<TIdentifier, TEntry>(JArray array, IEnumerable<KeyValuePair<TIdentifier, TEntry>> manipulations)
|
||||
where TIdentifier : unmanaged, IMetaIdentifier
|
||||
where TEntry : unmanaged
|
||||
{
|
||||
foreach (var (identifier, entry) in manipulations)
|
||||
{
|
||||
if (Serialize(identifier, entry) is { } jObj)
|
||||
array.Add(jObj);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public static JArray SerializeTo(JArray array, IEnumerable<GlobalEqpManipulation> manipulations)
|
||||
{
|
||||
foreach (var manip in manipulations)
|
||||
array.Add(Serialize(manip));
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
private class Converter : JsonConverter<MetaDictionary>
|
||||
|
|
@ -371,30 +512,27 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
return;
|
||||
}
|
||||
|
||||
writer.WriteStartArray();
|
||||
foreach (var item in value)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("Type");
|
||||
writer.WriteValue(item.ManipulationType.ToString());
|
||||
writer.WritePropertyName("Manipulation");
|
||||
serializer.Serialize(writer, item.Manipulation);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
writer.WriteEndArray();
|
||||
var array = new JArray();
|
||||
SerializeTo(array, value._imc);
|
||||
SerializeTo(array, value._eqp);
|
||||
SerializeTo(array, value._eqdp);
|
||||
SerializeTo(array, value._est);
|
||||
SerializeTo(array, value._rsp);
|
||||
SerializeTo(array, value._gmp);
|
||||
SerializeTo(array, value._globalEqp);
|
||||
array.WriteTo(writer);
|
||||
}
|
||||
|
||||
public override MetaDictionary ReadJson(JsonReader reader, Type objectType, MetaDictionary? existingValue, bool hasExistingValue,
|
||||
JsonSerializer serializer)
|
||||
{
|
||||
var dict = existingValue ?? [];
|
||||
var dict = existingValue ?? new MetaDictionary();
|
||||
dict.Clear();
|
||||
var jObj = JArray.Load(reader);
|
||||
foreach (var item in jObj)
|
||||
{
|
||||
var type = item["Type"]?.ToObject<MetaManipulation.Type>() ?? MetaManipulation.Type.Unknown;
|
||||
if (type is MetaManipulation.Type.Unknown)
|
||||
var type = item["Type"]?.ToObject<MetaManipulationType>() ?? MetaManipulationType.Unknown;
|
||||
if (type is MetaManipulationType.Unknown)
|
||||
{
|
||||
Penumbra.Log.Warning($"Invalid Meta Manipulation Type {type} encountered.");
|
||||
continue;
|
||||
|
|
@ -408,7 +546,7 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
|
||||
switch (type)
|
||||
{
|
||||
case MetaManipulation.Type.Imc:
|
||||
case MetaManipulationType.Imc:
|
||||
{
|
||||
var identifier = ImcIdentifier.FromJson(manip);
|
||||
var entry = manip["Entry"]?.ToObject<ImcEntry>();
|
||||
|
|
@ -418,7 +556,7 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
Penumbra.Log.Warning("Invalid IMC Manipulation encountered.");
|
||||
break;
|
||||
}
|
||||
case MetaManipulation.Type.Eqdp:
|
||||
case MetaManipulationType.Eqdp:
|
||||
{
|
||||
var identifier = EqdpIdentifier.FromJson(manip);
|
||||
var entry = (EqdpEntry?)manip["Entry"]?.ToObject<ushort>();
|
||||
|
|
@ -428,7 +566,7 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
Penumbra.Log.Warning("Invalid EQDP Manipulation encountered.");
|
||||
break;
|
||||
}
|
||||
case MetaManipulation.Type.Eqp:
|
||||
case MetaManipulationType.Eqp:
|
||||
{
|
||||
var identifier = EqpIdentifier.FromJson(manip);
|
||||
var entry = (EqpEntry?)manip["Entry"]?.ToObject<ulong>();
|
||||
|
|
@ -438,7 +576,7 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
Penumbra.Log.Warning("Invalid EQP Manipulation encountered.");
|
||||
break;
|
||||
}
|
||||
case MetaManipulation.Type.Est:
|
||||
case MetaManipulationType.Est:
|
||||
{
|
||||
var identifier = EstIdentifier.FromJson(manip);
|
||||
var entry = manip["Entry"]?.ToObject<EstEntry>();
|
||||
|
|
@ -448,7 +586,7 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
Penumbra.Log.Warning("Invalid EST Manipulation encountered.");
|
||||
break;
|
||||
}
|
||||
case MetaManipulation.Type.Gmp:
|
||||
case MetaManipulationType.Gmp:
|
||||
{
|
||||
var identifier = GmpIdentifier.FromJson(manip);
|
||||
var entry = manip["Entry"]?.ToObject<GmpEntry>();
|
||||
|
|
@ -458,7 +596,7 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
Penumbra.Log.Warning("Invalid GMP Manipulation encountered.");
|
||||
break;
|
||||
}
|
||||
case MetaManipulation.Type.Rsp:
|
||||
case MetaManipulationType.Rsp:
|
||||
{
|
||||
var identifier = RspIdentifier.FromJson(manip);
|
||||
var entry = manip["Entry"]?.ToObject<RspEntry>();
|
||||
|
|
@ -468,7 +606,7 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
Penumbra.Log.Warning("Invalid RSP Manipulation encountered.");
|
||||
break;
|
||||
}
|
||||
case MetaManipulation.Type.GlobalEqp:
|
||||
case MetaManipulationType.GlobalEqp:
|
||||
{
|
||||
var identifier = GlobalEqpManipulation.FromJson(manip);
|
||||
if (identifier.HasValue)
|
||||
|
|
@ -483,4 +621,22 @@ public class MetaDictionary : IEnumerable<MetaManipulation>
|
|||
return dict;
|
||||
}
|
||||
}
|
||||
|
||||
public MetaDictionary()
|
||||
{ }
|
||||
|
||||
public MetaDictionary(MetaCache? cache)
|
||||
{
|
||||
if (cache == null)
|
||||
return;
|
||||
|
||||
_imc = cache.Imc.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Entry);
|
||||
_eqp = cache.Eqp.ToDictionary(kvp => kvp.Key, kvp => new EqpEntryInternal(kvp.Value.Entry, kvp.Key.Slot));
|
||||
_eqdp = cache.Eqdp.ToDictionary(kvp => kvp.Key, kvp => new EqdpEntryInternal(kvp.Value.Entry, kvp.Key.Slot));
|
||||
_est = cache.Est.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Entry);
|
||||
_gmp = cache.Gmp.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Entry);
|
||||
_rsp = cache.Rsp.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Entry);
|
||||
_globalEqp = cache.GlobalEqp.Select(kvp => kvp.Key).ToHashSet();
|
||||
Count = cache.Count;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,457 +0,0 @@
|
|||
using Dalamud.Interface;
|
||||
using ImGuiNET;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using OtterGui;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Mods.Editor;
|
||||
using Penumbra.String.Functions;
|
||||
using Penumbra.UI;
|
||||
using Penumbra.UI.ModsTab;
|
||||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
#if false
|
||||
private static class ImcRow
|
||||
{
|
||||
private static ImcIdentifier _newIdentifier = ImcIdentifier.Default;
|
||||
|
||||
private static float IdWidth
|
||||
=> 80 * UiHelpers.Scale;
|
||||
|
||||
private static float SmallIdWidth
|
||||
=> 45 * UiHelpers.Scale;
|
||||
|
||||
public static void DrawNew(MetaFileManager metaFileManager, ModEditor editor, Vector2 iconSize)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
CopyToClipboardButton("Copy all current IMC manipulations to clipboard.", iconSize,
|
||||
editor.MetaEditor.Imc.Select(m => (MetaManipulation)m));
|
||||
ImGui.TableNextColumn();
|
||||
var (defaultEntry, fileExists, _) = metaFileManager.ImcChecker.GetDefaultEntry(_newIdentifier, true);
|
||||
var manip = (MetaManipulation)new ImcManipulation(_newIdentifier, defaultEntry);
|
||||
var canAdd = fileExists && editor.MetaEditor.CanAdd(manip);
|
||||
var tt = canAdd ? "Stage this edit." : !fileExists ? "This IMC file does not exist." : "This entry is already edited.";
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), iconSize, tt, !canAdd, true))
|
||||
editor.MetaEditor.Add(manip);
|
||||
|
||||
// Identifier
|
||||
ImGui.TableNextColumn();
|
||||
var change = ImcManipulationDrawer.DrawObjectType(ref _newIdentifier);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
change |= ImcManipulationDrawer.DrawPrimaryId(ref _newIdentifier);
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing,
|
||||
new Vector2(3 * UiHelpers.Scale, ImGui.GetStyle().ItemSpacing.Y));
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
// Equipment and accessories are slightly different imcs than other types.
|
||||
if (_newIdentifier.ObjectType is ObjectType.Equipment or ObjectType.Accessory)
|
||||
change |= ImcManipulationDrawer.DrawSlot(ref _newIdentifier);
|
||||
else
|
||||
change |= ImcManipulationDrawer.DrawSecondaryId(ref _newIdentifier);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
change |= ImcManipulationDrawer.DrawVariant(ref _newIdentifier);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (_newIdentifier.ObjectType is ObjectType.DemiHuman)
|
||||
change |= ImcManipulationDrawer.DrawSlot(ref _newIdentifier, 70);
|
||||
else
|
||||
ImUtf8.ScaledDummy(new Vector2(70 * UiHelpers.Scale, 0));
|
||||
|
||||
if (change)
|
||||
defaultEntry = metaFileManager.ImcChecker.GetDefaultEntry(_newIdentifier, true).Entry;
|
||||
// Values
|
||||
using var disabled = ImRaii.Disabled();
|
||||
ImGui.TableNextColumn();
|
||||
ImcManipulationDrawer.DrawMaterialId(defaultEntry, ref defaultEntry, false);
|
||||
ImGui.SameLine();
|
||||
ImcManipulationDrawer.DrawMaterialAnimationId(defaultEntry, ref defaultEntry, false);
|
||||
ImGui.TableNextColumn();
|
||||
ImcManipulationDrawer.DrawDecalId(defaultEntry, ref defaultEntry, false);
|
||||
ImGui.SameLine();
|
||||
ImcManipulationDrawer.DrawVfxId(defaultEntry, ref defaultEntry, false);
|
||||
ImGui.SameLine();
|
||||
ImcManipulationDrawer.DrawSoundId(defaultEntry, ref defaultEntry, false);
|
||||
ImGui.TableNextColumn();
|
||||
ImcManipulationDrawer.DrawAttributes(defaultEntry, ref defaultEntry);
|
||||
}
|
||||
|
||||
public static void Draw(MetaFileManager metaFileManager, ImcManipulation meta, ModEditor editor, Vector2 iconSize)
|
||||
{
|
||||
DrawMetaButtons(meta, editor, iconSize);
|
||||
|
||||
// Identifier
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
|
||||
ImGui.TextUnformatted(meta.ObjectType.ToName());
|
||||
ImGuiUtil.HoverTooltip(ObjectTypeTooltip);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
|
||||
ImGui.TextUnformatted(meta.PrimaryId.ToString());
|
||||
ImGuiUtil.HoverTooltip(PrimaryIdTooltipShort);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
|
||||
if (meta.ObjectType is ObjectType.Equipment or ObjectType.Accessory)
|
||||
{
|
||||
ImGui.TextUnformatted(meta.EquipSlot.ToName());
|
||||
ImGuiUtil.HoverTooltip(EquipSlotTooltip);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextUnformatted(meta.SecondaryId.ToString());
|
||||
ImGuiUtil.HoverTooltip(SecondaryIdTooltip);
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
|
||||
ImGui.TextUnformatted(meta.Variant.ToString());
|
||||
ImGuiUtil.HoverTooltip(VariantIdTooltip);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
|
||||
if (meta.ObjectType is ObjectType.DemiHuman)
|
||||
{
|
||||
ImGui.TextUnformatted(meta.EquipSlot.ToName());
|
||||
ImGuiUtil.HoverTooltip(EquipSlotTooltip);
|
||||
}
|
||||
|
||||
// Values
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing,
|
||||
new Vector2(3 * UiHelpers.Scale, ImGui.GetStyle().ItemSpacing.Y));
|
||||
ImGui.TableNextColumn();
|
||||
var defaultEntry = metaFileManager.ImcChecker.GetDefaultEntry(meta.Identifier, true).Entry;
|
||||
var newEntry = meta.Entry;
|
||||
var changes = ImcManipulationDrawer.DrawMaterialId(defaultEntry, ref newEntry, true);
|
||||
ImGui.SameLine();
|
||||
changes |= ImcManipulationDrawer.DrawMaterialAnimationId(defaultEntry, ref newEntry, true);
|
||||
ImGui.TableNextColumn();
|
||||
changes |= ImcManipulationDrawer.DrawDecalId(defaultEntry, ref newEntry, true);
|
||||
ImGui.SameLine();
|
||||
changes |= ImcManipulationDrawer.DrawVfxId(defaultEntry, ref newEntry, true);
|
||||
ImGui.SameLine();
|
||||
changes |= ImcManipulationDrawer.DrawSoundId(defaultEntry, ref newEntry, true);
|
||||
ImGui.TableNextColumn();
|
||||
changes |= ImcManipulationDrawer.DrawAttributes(defaultEntry, ref newEntry);
|
||||
|
||||
if (changes)
|
||||
editor.MetaEditor.Change(meta.Copy(newEntry));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public interface IMetaManipulation
|
||||
{
|
||||
public MetaIndex FileIndex();
|
||||
}
|
||||
|
||||
public interface IMetaManipulation<T>
|
||||
: IMetaManipulation, IComparable<T>, IEquatable<T> where T : struct
|
||||
{ }
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 16)]
|
||||
public readonly struct MetaManipulation : IEquatable<MetaManipulation>, IComparable<MetaManipulation>
|
||||
{
|
||||
public const int CurrentVersion = 0;
|
||||
|
||||
public enum Type : byte
|
||||
{
|
||||
Unknown = 0,
|
||||
Imc = 1,
|
||||
Eqdp = 2,
|
||||
Eqp = 3,
|
||||
Est = 4,
|
||||
Gmp = 5,
|
||||
Rsp = 6,
|
||||
GlobalEqp = 7,
|
||||
}
|
||||
|
||||
[FieldOffset(0)]
|
||||
[JsonIgnore]
|
||||
public readonly EqpManipulation Eqp = default;
|
||||
|
||||
[FieldOffset(0)]
|
||||
[JsonIgnore]
|
||||
public readonly GmpManipulation Gmp = default;
|
||||
|
||||
[FieldOffset(0)]
|
||||
[JsonIgnore]
|
||||
public readonly EqdpManipulation Eqdp = default;
|
||||
|
||||
[FieldOffset(0)]
|
||||
[JsonIgnore]
|
||||
public readonly EstManipulation Est = default;
|
||||
|
||||
[FieldOffset(0)]
|
||||
[JsonIgnore]
|
||||
public readonly RspManipulation Rsp = default;
|
||||
|
||||
[FieldOffset(0)]
|
||||
[JsonIgnore]
|
||||
public readonly ImcManipulation Imc = default;
|
||||
|
||||
[FieldOffset(0)]
|
||||
[JsonIgnore]
|
||||
public readonly GlobalEqpManipulation GlobalEqp = default;
|
||||
|
||||
[FieldOffset(15)]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonProperty("Type")]
|
||||
public readonly Type ManipulationType;
|
||||
|
||||
public object? Manipulation
|
||||
{
|
||||
get => ManipulationType switch
|
||||
{
|
||||
Type.Unknown => null,
|
||||
Type.Imc => Imc,
|
||||
Type.Eqdp => Eqdp,
|
||||
Type.Eqp => Eqp,
|
||||
Type.Est => Est,
|
||||
Type.Gmp => Gmp,
|
||||
Type.Rsp => Rsp,
|
||||
Type.GlobalEqp => GlobalEqp,
|
||||
_ => null,
|
||||
};
|
||||
init
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case EqpManipulation m:
|
||||
Eqp = m;
|
||||
ManipulationType = m.Validate() ? Type.Eqp : Type.Unknown;
|
||||
return;
|
||||
case EqdpManipulation m:
|
||||
Eqdp = m;
|
||||
ManipulationType = m.Validate() ? Type.Eqdp : Type.Unknown;
|
||||
return;
|
||||
case GmpManipulation m:
|
||||
Gmp = m;
|
||||
ManipulationType = m.Validate() ? Type.Gmp : Type.Unknown;
|
||||
return;
|
||||
case EstManipulation m:
|
||||
Est = m;
|
||||
ManipulationType = m.Validate() ? Type.Est : Type.Unknown;
|
||||
return;
|
||||
case RspManipulation m:
|
||||
Rsp = m;
|
||||
ManipulationType = m.Validate() ? Type.Rsp : Type.Unknown;
|
||||
return;
|
||||
case ImcManipulation m:
|
||||
Imc = m;
|
||||
ManipulationType = m.Validate(true) ? Type.Imc : Type.Unknown;
|
||||
return;
|
||||
case GlobalEqpManipulation m:
|
||||
GlobalEqp = m;
|
||||
ManipulationType = m.Validate() ? Type.GlobalEqp : Type.Unknown;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Validate()
|
||||
{
|
||||
return ManipulationType switch
|
||||
{
|
||||
Type.Imc => Imc.Validate(true),
|
||||
Type.Eqdp => Eqdp.Validate(),
|
||||
Type.Eqp => Eqp.Validate(),
|
||||
Type.Est => Est.Validate(),
|
||||
Type.Gmp => Gmp.Validate(),
|
||||
Type.Rsp => Rsp.Validate(),
|
||||
Type.GlobalEqp => GlobalEqp.Validate(),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
public MetaManipulation(EqpManipulation eqp)
|
||||
{
|
||||
Eqp = eqp;
|
||||
ManipulationType = Type.Eqp;
|
||||
}
|
||||
|
||||
public MetaManipulation(GmpManipulation gmp)
|
||||
{
|
||||
Gmp = gmp;
|
||||
ManipulationType = Type.Gmp;
|
||||
}
|
||||
|
||||
public MetaManipulation(EqdpManipulation eqdp)
|
||||
{
|
||||
Eqdp = eqdp;
|
||||
ManipulationType = Type.Eqdp;
|
||||
}
|
||||
|
||||
public MetaManipulation(EstManipulation est)
|
||||
{
|
||||
Est = est;
|
||||
ManipulationType = Type.Est;
|
||||
}
|
||||
|
||||
public MetaManipulation(RspManipulation rsp)
|
||||
{
|
||||
Rsp = rsp;
|
||||
ManipulationType = Type.Rsp;
|
||||
}
|
||||
|
||||
public MetaManipulation(ImcManipulation imc)
|
||||
{
|
||||
Imc = imc;
|
||||
ManipulationType = Type.Imc;
|
||||
}
|
||||
|
||||
public MetaManipulation(GlobalEqpManipulation eqp)
|
||||
{
|
||||
GlobalEqp = eqp;
|
||||
ManipulationType = Type.GlobalEqp;
|
||||
}
|
||||
|
||||
public static implicit operator MetaManipulation(EqpManipulation eqp)
|
||||
=> new(eqp);
|
||||
|
||||
public static implicit operator MetaManipulation(GmpManipulation gmp)
|
||||
=> new(gmp);
|
||||
|
||||
public static implicit operator MetaManipulation(EqdpManipulation eqdp)
|
||||
=> new(eqdp);
|
||||
|
||||
public static implicit operator MetaManipulation(EstManipulation est)
|
||||
=> new(est);
|
||||
|
||||
public static implicit operator MetaManipulation(RspManipulation rsp)
|
||||
=> new(rsp);
|
||||
|
||||
public static implicit operator MetaManipulation(ImcManipulation imc)
|
||||
=> new(imc);
|
||||
|
||||
public static implicit operator MetaManipulation(GlobalEqpManipulation eqp)
|
||||
=> new(eqp);
|
||||
|
||||
public bool EntryEquals(MetaManipulation other)
|
||||
{
|
||||
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.GlobalEqp => true,
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
}
|
||||
|
||||
public bool Equals(MetaManipulation other)
|
||||
{
|
||||
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.GlobalEqp => GlobalEqp.Equals(other.GlobalEqp),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
public MetaManipulation WithEntryOf(MetaManipulation other)
|
||||
{
|
||||
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.GlobalEqp => GlobalEqp,
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is MetaManipulation other && Equals(other);
|
||||
|
||||
public override int GetHashCode()
|
||||
=> ManipulationType switch
|
||||
{
|
||||
Type.Eqp => Eqp.GetHashCode(),
|
||||
Type.Gmp => Gmp.GetHashCode(),
|
||||
Type.Eqdp => Eqdp.GetHashCode(),
|
||||
Type.Est => Est.GetHashCode(),
|
||||
Type.Rsp => Rsp.GetHashCode(),
|
||||
Type.Imc => Imc.GetHashCode(),
|
||||
Type.GlobalEqp => GlobalEqp.GetHashCode(),
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
public unsafe int CompareTo(MetaManipulation other)
|
||||
{
|
||||
fixed (MetaManipulation* lhs = &this)
|
||||
{
|
||||
return MemoryUtility.MemCmpUnchecked(lhs, &other, sizeof(MetaManipulation));
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> ManipulationType switch
|
||||
{
|
||||
Type.Eqp => Eqp.ToString(),
|
||||
Type.Gmp => Gmp.ToString(),
|
||||
Type.Eqdp => Eqdp.ToString(),
|
||||
Type.Est => Est.ToString(),
|
||||
Type.Rsp => Rsp.ToString(),
|
||||
Type.Imc => Imc.ToString(),
|
||||
Type.GlobalEqp => GlobalEqp.ToString(),
|
||||
_ => "Invalid",
|
||||
};
|
||||
|
||||
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.Est => $"{Est.Entry}",
|
||||
Type.Gmp => $"{Gmp.Entry.Value}",
|
||||
Type.Rsp => $"{Rsp.Entry}",
|
||||
Type.GlobalEqp => string.Empty,
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
public static bool operator ==(MetaManipulation left, MetaManipulation right)
|
||||
=> left.Equals(right);
|
||||
|
||||
public static bool operator !=(MetaManipulation left, MetaManipulation right)
|
||||
=> !(left == right);
|
||||
|
||||
public static bool operator <(MetaManipulation left, MetaManipulation right)
|
||||
=> left.CompareTo(right) < 0;
|
||||
|
||||
public static bool operator <=(MetaManipulation left, MetaManipulation right)
|
||||
=> left.CompareTo(right) <= 0;
|
||||
|
||||
public static bool operator >(MetaManipulation left, MetaManipulation right)
|
||||
=> left.CompareTo(right) > 0;
|
||||
|
||||
public static bool operator >=(MetaManipulation left, MetaManipulation right)
|
||||
=> left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
|
|
@ -38,6 +38,9 @@ public readonly record struct RspIdentifier(SubRace SubRace, RspAttribute Attrib
|
|||
var ret = new RspIdentifier(subRace, attribute);
|
||||
return ret.Validate() ? ret : null;
|
||||
}
|
||||
|
||||
public MetaManipulationType Type
|
||||
=> MetaManipulationType.Rsp;
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(Converter))]
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta.Files;
|
||||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public readonly struct RspManipulation(RspIdentifier identifier, RspEntry entry) : IMetaManipulation<RspManipulation>
|
||||
{
|
||||
[JsonIgnore]
|
||||
public RspIdentifier Identifier { get; } = identifier;
|
||||
|
||||
public RspEntry Entry { get; } = entry;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public SubRace SubRace
|
||||
=> Identifier.SubRace;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public RspAttribute Attribute
|
||||
=> Identifier.Attribute;
|
||||
|
||||
[JsonConstructor]
|
||||
public RspManipulation(SubRace subRace, RspAttribute attribute, RspEntry entry)
|
||||
: this(new RspIdentifier(subRace, attribute), entry)
|
||||
{ }
|
||||
|
||||
public RspManipulation Copy(RspEntry entry)
|
||||
=> new(Identifier, entry);
|
||||
|
||||
public override string ToString()
|
||||
=> $"Rsp - {SubRace.ToName()} - {Attribute.ToFullString()}";
|
||||
|
||||
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 int GetHashCode()
|
||||
=> HashCode.Combine((int)SubRace, (int)Attribute);
|
||||
|
||||
public int CompareTo(RspManipulation other)
|
||||
{
|
||||
var s = SubRace.CompareTo(other.SubRace);
|
||||
return s != 0 ? s : Attribute.CompareTo(other.Attribute);
|
||||
}
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
=> MetaIndex.HumanCmp;
|
||||
|
||||
public bool Apply(CmpFile file)
|
||||
{
|
||||
var value = file[SubRace, Attribute];
|
||||
if (value == Entry)
|
||||
return false;
|
||||
|
||||
file[SubRace, Attribute] = Entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Validate()
|
||||
=> Identifier.Validate() && Entry.Validate();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue