Get rid off all MetaManipulation things.

This commit is contained in:
Ottermandias 2024-06-14 13:38:36 +02:00
parent 361082813b
commit 3170edfeb6
63 changed files with 2422 additions and 2847 deletions

View file

@ -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)

View file

@ -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}";
}

View file

@ -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;
}
}

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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",
};
}

View file

@ -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;
}
}

View file

@ -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,
};
}

View file

@ -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;
}

View file

@ -36,4 +36,7 @@ public readonly record struct GmpIdentifier(PrimaryId SetId) : IMetaIdentifier,
jObj["SetId"] = SetId.Id.ToString();
return jObj;
}
public MetaManipulationType Type
=> MetaManipulationType.Gmp;
}

View file

@ -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;
}

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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))]

View file

@ -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();
}