diff --git a/Penumbra/Meta/Files/EqdpFile.cs b/Penumbra/Meta/Files/EqdpFile.cs index c76c4efd..e46e82e9 100644 --- a/Penumbra/Meta/Files/EqdpFile.cs +++ b/Penumbra/Meta/Files/EqdpFile.cs @@ -2,6 +2,7 @@ using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using Penumbra.Interop.Services; using Penumbra.Interop.Structs; +using Penumbra.Meta.Manipulations; using Penumbra.String.Functions; namespace Penumbra.Meta.Files; @@ -126,4 +127,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile public static EqdpEntry GetDefault(MetaFileManager manager, GenderRace raceCode, bool accessory, PrimaryId primaryId) => GetDefault(manager, CharacterUtility.ReverseIndices[(int)CharacterUtilityData.EqdpIdx(raceCode, accessory)], primaryId); + + public static EqdpEntry GetDefault(MetaFileManager manager, EqdpIdentifier identifier) + => GetDefault(manager, CharacterUtility.ReverseIndices[(int)identifier.FileIndex()], identifier.SetId); } diff --git a/Penumbra/Meta/Files/EqpGmpFile.cs b/Penumbra/Meta/Files/EqpGmpFile.cs index 70067c2b..17541c4f 100644 --- a/Penumbra/Meta/Files/EqpGmpFile.cs +++ b/Penumbra/Meta/Files/EqpGmpFile.cs @@ -1,6 +1,7 @@ using Penumbra.GameData.Structs; using Penumbra.Interop.Services; using Penumbra.Interop.Structs; +using Penumbra.Meta.Manipulations; using Penumbra.String.Functions; namespace Penumbra.Meta.Files; @@ -164,6 +165,9 @@ public sealed class ExpandedGmpFile : ExpandedEqpGmpBase, IEnumerable public static GmpEntry GetDefault(MetaFileManager manager, PrimaryId primaryIdx) => new() { Value = GetDefaultInternal(manager, InternalIndex, primaryIdx, GmpEntry.Default.Value) }; + public static GmpEntry GetDefault(MetaFileManager manager, GmpIdentifier identifier) + => new() { Value = GetDefaultInternal(manager, InternalIndex, identifier.SetId, GmpEntry.Default.Value) }; + public void Reset(IEnumerable entries) { foreach (var entry in entries) diff --git a/Penumbra/Meta/Files/EstFile.cs b/Penumbra/Meta/Files/EstFile.cs index ee38ea1e..f3860416 100644 --- a/Penumbra/Meta/Files/EstFile.cs +++ b/Penumbra/Meta/Files/EstFile.cs @@ -184,4 +184,7 @@ public sealed unsafe class EstFile : MetaBaseFile public static EstEntry GetDefault(MetaFileManager manager, EstType estType, GenderRace genderRace, PrimaryId primaryId) => GetDefault(manager, (MetaIndex)estType, genderRace, primaryId); + + public static EstEntry GetDefault(MetaFileManager manager, EstIdentifier identifier) + => GetDefault(manager, identifier.FileIndex(), identifier.GenderRace, identifier.SetId); } diff --git a/Penumbra/Meta/Files/ImcFile.cs b/Penumbra/Meta/Files/ImcFile.cs index 5d704cf8..892f5b44 100644 --- a/Penumbra/Meta/Files/ImcFile.cs +++ b/Penumbra/Meta/Files/ImcFile.cs @@ -65,6 +65,9 @@ public unsafe class ImcFile : MetaBaseFile return ptr == null ? new ImcEntry() : *ptr; } + public ImcEntry GetEntry(EquipSlot slot, Variant variantIdx) + => GetEntry(PartIndex(slot), variantIdx); + public ImcEntry GetEntry(int partIdx, Variant variantIdx, out bool exists) { var ptr = VariantPtr(Data, partIdx, variantIdx); diff --git a/Penumbra/Meta/Manipulations/Eqdp.cs b/Penumbra/Meta/Manipulations/Eqdp.cs index a986d475..6306f419 100644 --- a/Penumbra/Meta/Manipulations/Eqdp.cs +++ b/Penumbra/Meta/Manipulations/Eqdp.cs @@ -71,7 +71,7 @@ public readonly record struct EqdpIdentifier(PrimaryId SetId, EquipSlot Slot, Ge } } -public readonly record struct EqdpEntryInternal(bool Model, bool Material) +public readonly record struct EqdpEntryInternal(bool Material, bool Model) { private EqdpEntryInternal((bool, bool) val) : this(val.Item1, val.Item2) @@ -81,7 +81,6 @@ public readonly record struct EqdpEntryInternal(bool Model, bool Material) : this(entry.ToBits(slot)) { } - public EqdpEntry ToEntry(EquipSlot slot) - => Eqdp.FromSlotAndBits(slot, Model, Material); + => Eqdp.FromSlotAndBits(slot, Material, Model); } diff --git a/Penumbra/Meta/Manipulations/MetaDictionary.cs b/Penumbra/Meta/Manipulations/MetaDictionary.cs index 941cdf34..51149e3b 100644 --- a/Penumbra/Meta/Manipulations/MetaDictionary.cs +++ b/Penumbra/Meta/Manipulations/MetaDictionary.cs @@ -52,6 +52,19 @@ public sealed class MetaDictionary : IEnumerable IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public bool Add(IMetaIdentifier identifier, object entry) + => identifier switch + { + EqdpIdentifier eqdpIdentifier => entry is EqdpEntryInternal e && Add(eqdpIdentifier, e), + EqpIdentifier eqpIdentifier => entry is EqpEntryInternal e && Add(eqpIdentifier, e), + EstIdentifier estIdentifier => entry is EstEntry e && Add(estIdentifier, e), + GlobalEqpManipulation globalEqpManipulation => Add(globalEqpManipulation), + GmpIdentifier gmpIdentifier => entry is GmpEntry e && Add(gmpIdentifier, e), + ImcIdentifier imcIdentifier => entry is ImcEntry e && Add(imcIdentifier, e), + RspIdentifier rspIdentifier => entry is RspEntry e && Add(rspIdentifier, e), + _ => false, + }; + public bool Add(MetaManipulation manip) { var ret = manip.ManipulationType switch @@ -146,71 +159,23 @@ public sealed class MetaDictionary : IEnumerable TryAdd(identifier); } - public bool TryGetValue(MetaManipulation identifier, out MetaManipulation oldValue) - { - switch (identifier.ManipulationType) - { - case MetaManipulation.Type.Imc: - if (_imc.TryGetValue(identifier.Imc.Identifier, out var oldImc)) - { - oldValue = new MetaManipulation(new ImcManipulation(identifier.Imc.Identifier, oldImc)); - return true; - } + public bool TryGetValue(EstIdentifier identifier, out EstEntry value) + => _est.TryGetValue(identifier, out value); - break; - case MetaManipulation.Type.Eqdp: - if (_eqp.TryGetValue(identifier.Eqp.Identifier, out var oldEqdp)) - { - oldValue = new MetaManipulation(new EqpManipulation(identifier.Eqp.Identifier, oldEqdp.ToEntry(identifier.Eqp.Slot))); - return true; - } + public bool TryGetValue(EqpIdentifier identifier, out EqpEntryInternal value) + => _eqp.TryGetValue(identifier, out value); - break; - case MetaManipulation.Type.Eqp: - if (_eqdp.TryGetValue(identifier.Eqdp.Identifier, out var oldEqp)) - { - oldValue = new MetaManipulation(new EqdpManipulation(identifier.Eqdp.Identifier, oldEqp.ToEntry(identifier.Eqdp.Slot))); - return true; - } + public bool TryGetValue(EqdpIdentifier identifier, out EqdpEntryInternal value) + => _eqdp.TryGetValue(identifier, out value); - break; - case MetaManipulation.Type.Est: - if (_est.TryGetValue(identifier.Est.Identifier, out var oldEst)) - { - oldValue = new MetaManipulation(new EstManipulation(identifier.Est.Identifier, oldEst)); - return true; - } + public bool TryGetValue(GmpIdentifier identifier, out GmpEntry value) + => _gmp.TryGetValue(identifier, out value); - break; - case MetaManipulation.Type.Gmp: - if (_gmp.TryGetValue(identifier.Gmp.Identifier, out var oldGmp)) - { - oldValue = new MetaManipulation(new GmpManipulation(identifier.Gmp.Identifier, oldGmp)); - return true; - } + public bool TryGetValue(RspIdentifier identifier, out RspEntry value) + => _rsp.TryGetValue(identifier, out value); - break; - case MetaManipulation.Type.Rsp: - if (_rsp.TryGetValue(identifier.Rsp.Identifier, out var oldRsp)) - { - oldValue = new MetaManipulation(new RspManipulation(identifier.Rsp.Identifier, oldRsp)); - return true; - } - - break; - case MetaManipulation.Type.GlobalEqp: - if (_globalEqp.TryGetValue(identifier.GlobalEqp, out var oldGlobalEqp)) - { - oldValue = new MetaManipulation(oldGlobalEqp); - return true; - } - - break; - } - - oldValue = default; - return false; - } + public bool TryGetValue(ImcIdentifier identifier, out ImcEntry value) + => _imc.TryGetValue(identifier, out value); public void SetTo(MetaDictionary other) { diff --git a/Penumbra/Mods/ItemSwap/EquipmentSwap.cs b/Penumbra/Mods/ItemSwap/EquipmentSwap.cs index 3efee857..e42a1d31 100644 --- a/Penumbra/Mods/ItemSwap/EquipmentSwap.cs +++ b/Penumbra/Mods/ItemSwap/EquipmentSwap.cs @@ -1,4 +1,4 @@ -using Penumbra.Api.Enums; +using Penumbra.Api.Enums; using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.GameData.Files; @@ -16,32 +16,19 @@ public static class EquipmentSwap private static EquipSlot[] ConvertSlots(EquipSlot slot, bool rFinger, bool lFinger) { if (slot != EquipSlot.RFinger) - return new[] - { - slot, - }; + return [slot]; return rFinger ? lFinger - ? new[] - { - EquipSlot.RFinger, - EquipSlot.LFinger, - } - : new[] - { - EquipSlot.RFinger, - } + ? [EquipSlot.RFinger, EquipSlot.LFinger] + : [EquipSlot.RFinger] : lFinger - ? new[] - { - EquipSlot.LFinger, - } - : Array.Empty(); + ? [EquipSlot.LFinger] + : []; } public static EquipItem[] CreateTypeSwap(MetaFileManager manager, ObjectIdentification identifier, List swaps, - Func redirections, Func manips, + Func redirections, MetaDictionary manips, EquipSlot slotFrom, EquipItem itemFrom, EquipSlot slotTo, EquipItem itemTo) { LookupItem(itemFrom, out var actualSlotFrom, out var idFrom, out var variantFrom); @@ -50,11 +37,14 @@ public static class EquipmentSwap throw new ItemSwap.InvalidItemTypeException(); var (imcFileFrom, variants, affectedItems) = GetVariants(manager, identifier, slotFrom, idFrom, idTo, variantFrom); - var imcManip = new ImcManipulation(slotTo, variantTo.Id, idTo.Id, default); - var imcFileTo = new ImcFile(manager, imcManip.Identifier); + var imcIdentifierTo = new ImcIdentifier(slotTo, idTo, variantTo); + var imcFileTo = new ImcFile(manager, imcIdentifierTo); + var imcEntry = manips.TryGetValue(imcIdentifierTo, out var entry) + ? entry + : imcFileTo.GetEntry(imcIdentifierTo.EquipSlot, imcIdentifierTo.Variant); + var mtrlVariantTo = imcEntry.MaterialId; var skipFemale = false; var skipMale = false; - var mtrlVariantTo = manips(imcManip.Copy(imcFileTo.GetEntry(ImcFile.PartIndex(slotTo), variantTo.Id))).Imc.Entry.MaterialId; foreach (var gr in Enum.GetValues()) { switch (gr.Split().Item1) @@ -99,7 +89,7 @@ public static class EquipmentSwap } public static EquipItem[] CreateItemSwap(MetaFileManager manager, ObjectIdentification identifier, List swaps, - Func redirections, Func manips, EquipItem itemFrom, + Func redirections, MetaDictionary manips, EquipItem itemFrom, EquipItem itemTo, bool rFinger = true, bool lFinger = true) { // Check actual ids, variants and slots. We only support using the same slot. @@ -120,8 +110,12 @@ public static class EquipmentSwap foreach (var slot in ConvertSlots(slotFrom, rFinger, lFinger)) { (var imcFileFrom, var variants, affectedItems) = GetVariants(manager, identifier, slot, idFrom, idTo, variantFrom); - var imcManip = new ImcManipulation(slot, variantTo.Id, idTo, default); - var imcFileTo = new ImcFile(manager, imcManip.Identifier); + var imcIdentifierTo = new ImcIdentifier(slotTo, idTo, variantTo); + var imcFileTo = new ImcFile(manager, imcIdentifierTo); + var imcEntry = manips.TryGetValue(imcIdentifierTo, out var entry) + ? entry + : imcFileTo.GetEntry(imcIdentifierTo.EquipSlot, imcIdentifierTo.Variant); + var mtrlVariantTo = imcEntry.MaterialId; var isAccessory = slot.IsAccessory(); var estType = slot switch @@ -133,7 +127,6 @@ public static class EquipmentSwap var skipFemale = false; var skipMale = false; - var mtrlVariantTo = manips(imcManip.Copy(imcFileTo.GetEntry(ImcFile.PartIndex(slot), variantTo))).Imc.Entry.MaterialId; foreach (var gr in Enum.GetValues()) { switch (gr.Split().Item1) @@ -154,7 +147,7 @@ public static class EquipmentSwap if (eqdp != null) swaps.Add(eqdp); - var ownMdl = eqdp?.SwapApplied.Eqdp.Entry.ToBits(slot).Item2 ?? false; + var ownMdl = eqdp?.SwapToModdedEntry.Model ?? false; var est = ItemSwap.CreateEst(manager, redirections, manips, estType, gr, idFrom, idTo, ownMdl); if (est != null) swaps.Add(est); @@ -184,22 +177,22 @@ public static class EquipmentSwap return affectedItems; } - public static MetaSwap? CreateEqdp(MetaFileManager manager, Func redirections, - Func manips, EquipSlot slot, GenderRace gr, PrimaryId idFrom, - PrimaryId idTo, byte mtrlTo) + public static MetaSwap? CreateEqdp(MetaFileManager manager, Func redirections, + MetaDictionary manips, EquipSlot slot, GenderRace gr, PrimaryId idFrom, PrimaryId idTo, byte mtrlTo) => CreateEqdp(manager, redirections, manips, slot, slot, gr, idFrom, idTo, mtrlTo); - public static MetaSwap? CreateEqdp(MetaFileManager manager, Func redirections, - Func manips, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, PrimaryId idFrom, + public static MetaSwap? CreateEqdp(MetaFileManager manager, Func redirections, + MetaDictionary manips, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, PrimaryId idFrom, PrimaryId idTo, byte mtrlTo) { - var (gender, race) = gr.Split(); - var eqdpFrom = new EqdpManipulation(ExpandedEqdpFile.GetDefault(manager, gr, slotFrom.IsAccessory(), idFrom), slotFrom, gender, - race, idFrom); - var eqdpTo = new EqdpManipulation(ExpandedEqdpFile.GetDefault(manager, gr, slotTo.IsAccessory(), idTo), slotTo, gender, race, - idTo); - var meta = new MetaSwap(manips, eqdpFrom, eqdpTo); - var (ownMtrl, ownMdl) = meta.SwapApplied.Eqdp.Entry.ToBits(slotFrom); + var eqdpFromIdentifier = new EqdpIdentifier(idFrom, slotFrom, gr); + var eqdpToIdentifier = new EqdpIdentifier(idFrom, slotFrom, gr); + var eqdpFromDefault = new EqdpEntryInternal(ExpandedEqdpFile.GetDefault(manager, eqdpFromIdentifier), slotFrom); + var eqdpToDefault = new EqdpEntryInternal(ExpandedEqdpFile.GetDefault(manager, eqdpToIdentifier), slotTo); + var meta = new MetaSwap(i => manips.TryGetValue(i, out var e) ? e : null, eqdpFromIdentifier, + eqdpFromDefault, eqdpToIdentifier, + eqdpToDefault); + var (ownMtrl, ownMdl) = meta.SwapToModdedEntry; if (ownMdl) { var mdl = CreateMdl(manager, redirections, slotFrom, slotTo, gr, idFrom, idTo, mtrlTo); @@ -270,38 +263,39 @@ public static class EquipmentSwap return (imc, variants, items); } - public static MetaSwap? CreateGmp(MetaFileManager manager, Func manips, EquipSlot slot, PrimaryId idFrom, - PrimaryId idTo) + public static MetaSwap? CreateGmp(MetaFileManager manager, MetaDictionary manips, + EquipSlot slot, PrimaryId idFrom, PrimaryId idTo) { if (slot is not EquipSlot.Head) return null; - var manipFrom = new GmpManipulation(ExpandedGmpFile.GetDefault(manager, idFrom), idFrom); - var manipTo = new GmpManipulation(ExpandedGmpFile.GetDefault(manager, idTo), idTo); - return new MetaSwap(manips, manipFrom, manipTo); + var manipFromIdentifier = new GmpIdentifier(idFrom); + var manipToIdentifier = new GmpIdentifier(idTo); + var manipFromDefault = ExpandedGmpFile.GetDefault(manager, manipFromIdentifier); + var manipToDefault = ExpandedGmpFile.GetDefault(manager, manipToIdentifier); + return new MetaSwap(i => manips.TryGetValue(i, out var e) ? e : null, manipFromIdentifier, manipFromDefault, manipToIdentifier, manipToDefault); } - public static MetaSwap CreateImc(MetaFileManager manager, Func redirections, - Func manips, EquipSlot slot, - PrimaryId idFrom, PrimaryId idTo, Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo) + public static MetaSwap CreateImc(MetaFileManager manager, Func redirections, + MetaDictionary manips, EquipSlot slot, PrimaryId idFrom, PrimaryId idTo, Variant variantFrom, Variant variantTo, + ImcFile imcFileFrom, ImcFile imcFileTo) => CreateImc(manager, redirections, manips, slot, slot, idFrom, idTo, variantFrom, variantTo, imcFileFrom, imcFileTo); - public static MetaSwap CreateImc(MetaFileManager manager, Func redirections, - Func manips, - EquipSlot slotFrom, EquipSlot slotTo, PrimaryId idFrom, PrimaryId idTo, + public static MetaSwap CreateImc(MetaFileManager manager, Func redirections, + MetaDictionary manips, EquipSlot slotFrom, EquipSlot slotTo, PrimaryId idFrom, PrimaryId idTo, Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo) { - var entryFrom = imcFileFrom.GetEntry(ImcFile.PartIndex(slotFrom), variantFrom); - var entryTo = imcFileTo.GetEntry(ImcFile.PartIndex(slotTo), variantTo); - var manipulationFrom = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, entryFrom); - var manipulationTo = new ImcManipulation(slotTo, variantTo.Id, idTo, entryTo); - var imc = new MetaSwap(manips, manipulationFrom, manipulationTo); + var manipFromIdentifier = new ImcIdentifier(slotFrom, idFrom, variantFrom); + var manipToIdentifier = new ImcIdentifier(slotTo, idTo, variantTo); + var manipFromDefault = imcFileFrom.GetEntry(ImcFile.PartIndex(slotFrom), variantFrom); + var manipToDefault = imcFileTo.GetEntry(ImcFile.PartIndex(slotTo), variantTo); + var imc = new MetaSwap(i => manips.TryGetValue(i, out var e) ? e : null, manipFromIdentifier, manipFromDefault, manipToIdentifier, manipToDefault); - var decal = CreateDecal(manager, redirections, imc.SwapToModded.Imc.Entry.DecalId); + var decal = CreateDecal(manager, redirections, imc.SwapToModdedEntry.DecalId); if (decal != null) imc.ChildSwaps.Add(decal); - var avfx = CreateAvfx(manager, redirections, idFrom, idTo, imc.SwapToModded.Imc.Entry.VfxId); + var avfx = CreateAvfx(manager, redirections, idFrom, idTo, imc.SwapToModdedEntry.VfxId); if (avfx != null) imc.ChildSwaps.Add(avfx); @@ -322,7 +316,8 @@ public static class EquipmentSwap // Example: Abyssos Helm / Body - public static FileSwap? CreateAvfx(MetaFileManager manager, Func redirections, PrimaryId idFrom, PrimaryId idTo, byte vfxId) + public static FileSwap? CreateAvfx(MetaFileManager manager, Func redirections, PrimaryId idFrom, PrimaryId idTo, + byte vfxId) { if (vfxId == 0) return null; @@ -340,17 +335,18 @@ public static class EquipmentSwap return avfx; } - public static MetaSwap? CreateEqp(MetaFileManager manager, Func manips, EquipSlot slot, PrimaryId idFrom, - PrimaryId idTo) + public static MetaSwap? CreateEqp(MetaFileManager manager, MetaDictionary manips, + EquipSlot slot, PrimaryId idFrom, PrimaryId idTo) { if (slot.IsAccessory()) return null; - var eqpValueFrom = ExpandedEqpFile.GetDefault(manager, idFrom); - var eqpValueTo = ExpandedEqpFile.GetDefault(manager, idTo); - var eqpFrom = new EqpManipulation(eqpValueFrom, slot, idFrom); - var eqpTo = new EqpManipulation(eqpValueTo, slot, idFrom); - return new MetaSwap(manips, eqpFrom, eqpTo); + var manipFromIdentifier = new EqpIdentifier(idFrom, slot); + var manipToIdentifier = new EqpIdentifier(idTo, slot); + var manipFromDefault = new EqpEntryInternal(ExpandedEqpFile.GetDefault(manager, idFrom), slot); + var manipToDefault = new EqpEntryInternal(ExpandedEqpFile.GetDefault(manager, idTo), slot); + return new MetaSwap(i => manips.TryGetValue(i, out var e) ? e : null, manipFromIdentifier, + manipFromDefault, manipToIdentifier, manipToDefault); } public static FileSwap? CreateMtrl(MetaFileManager manager, Func redirections, EquipSlot slot, PrimaryId idFrom, @@ -397,7 +393,8 @@ public static class EquipmentSwap return mtrl; } - public static FileSwap CreateTex(MetaFileManager manager, Func redirections, char prefix, PrimaryId idFrom, PrimaryId idTo, + public static FileSwap CreateTex(MetaFileManager manager, Func redirections, char prefix, PrimaryId idFrom, + PrimaryId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged) => CreateTex(manager, redirections, prefix, EquipSlot.Unknown, EquipSlot.Unknown, idFrom, idTo, ref texture, ref dataWasChanged); diff --git a/Penumbra/Mods/ItemSwap/ItemSwap.cs b/Penumbra/Mods/ItemSwap/ItemSwap.cs index 7fac52c1..efd8080c 100644 --- a/Penumbra/Mods/ItemSwap/ItemSwap.cs +++ b/Penumbra/Mods/ItemSwap/ItemSwap.cs @@ -147,24 +147,23 @@ public static class ItemSwap return FileSwap.CreateSwap(manager, ResourceType.Sklb, redirections, sklbPath, sklbPath); } - /// metaChanges is not manipulated, but IReadOnlySet does not support TryGetValue. - public static MetaSwap? CreateEst(MetaFileManager manager, Func redirections, - Func manips, EstType type, - GenderRace genderRace, PrimaryId idFrom, PrimaryId idTo, bool ownMdl) + public static MetaSwap? CreateEst(MetaFileManager manager, Func redirections, + MetaDictionary manips, EstType type, GenderRace genderRace, PrimaryId idFrom, PrimaryId idTo, bool ownMdl) { if (type == 0) return null; - var (gender, race) = genderRace.Split(); - var fromDefault = new EstManipulation(gender, race, type, idFrom, EstFile.GetDefault(manager, type, genderRace, idFrom)); - var toDefault = new EstManipulation(gender, race, type, idTo, EstFile.GetDefault(manager, type, genderRace, idTo)); - var est = new MetaSwap(manips, fromDefault, toDefault); + var manipFromIdentifier = new EstIdentifier(idFrom, type, genderRace); + var manipToIdentifier = new EstIdentifier(idTo, type, genderRace); + var manipFromDefault = EstFile.GetDefault(manager, manipFromIdentifier); + var manipToDefault = EstFile.GetDefault(manager, manipToIdentifier); + var est = new MetaSwap(i => manips.TryGetValue(i, out var e) ? e : null, manipFromIdentifier, manipFromDefault, manipToIdentifier, manipToDefault); - if (ownMdl && est.SwapApplied.Est.Entry.Value >= 2) + if (ownMdl && est.SwapToModdedEntry.Value >= 2) { - var phyb = CreatePhyb(manager, redirections, type, genderRace, est.SwapApplied.Est.Entry); + var phyb = CreatePhyb(manager, redirections, type, genderRace, est.SwapToModdedEntry); est.ChildSwaps.Add(phyb); - var sklb = CreateSklb(manager, redirections, type, genderRace, est.SwapApplied.Est.Entry); + var sklb = CreateSklb(manager, redirections, type, genderRace, est.SwapToModdedEntry); est.ChildSwaps.Add(sklb); } else if (est.SwapAppliedIsDefault) diff --git a/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs b/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs index 67a5d007..021ee665 100644 --- a/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs +++ b/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs @@ -74,9 +74,9 @@ public class ItemSwapContainer } break; - case MetaSwap meta: + case IMetaSwap meta: if (!meta.SwapAppliedIsDefault) - convertedManips.Add(meta.SwapApplied); + convertedManips.Add(meta.SwapFromIdentifier, meta.SwapToModdedEntry); break; } @@ -116,17 +116,10 @@ public class ItemSwapContainer ? p => collection.ResolvePath(p) ?? new FullPath(p) : p => ModRedirections.TryGetValue(p, out var path) ? path : new FullPath(p); - private Func MetaResolver(ModCollection? collection) - { - if (collection?.MetaCache?.Manipulations is { } cache) - { - MetaDictionary dict = [.. cache]; - return m => dict.TryGetValue(m, out var a) ? a : m; - } - - var set = _appliedModData.Manipulations; - return m => set.TryGetValue(m, out var a) ? a : m; - } + private MetaDictionary MetaResolver(ModCollection? collection) + => collection?.MetaCache?.Manipulations is { } cache + ? [.. cache] + : _appliedModData.Manipulations; public EquipItem[] LoadEquipment(EquipItem from, EquipItem to, ModCollection? collection = null, bool useRightRing = true, bool useLeftRing = true) @@ -161,8 +154,8 @@ public class ItemSwapContainer _ => (EstType)0, }; - var metaResolver = MetaResolver(collection); - var est = ItemSwap.CreateEst(manager, pathResolver, metaResolver, type, race, from, to, true); + var estResolver = MetaResolver(collection); + var est = ItemSwap.CreateEst(manager, pathResolver, estResolver, type, race, from, to, true); Swaps.Add(mdl); if (est != null) diff --git a/Penumbra/Mods/ItemSwap/Swaps.cs b/Penumbra/Mods/ItemSwap/Swaps.cs index 27935ffb..36c54203 100644 --- a/Penumbra/Mods/ItemSwap/Swaps.cs +++ b/Penumbra/Mods/ItemSwap/Swaps.cs @@ -1,58 +1,91 @@ -using Penumbra.Api.Enums; +using Penumbra.Api.Enums; using Penumbra.GameData.Files; using Penumbra.Meta.Manipulations; using Penumbra.String.Classes; using Penumbra.Meta; using static Penumbra.Mods.ItemSwap.ItemSwap; -using Penumbra.Services; namespace Penumbra.Mods.ItemSwap; public class Swap { /// Any further swaps belonging specifically to this tree of changes. - public readonly List ChildSwaps = new(); + public readonly List ChildSwaps = []; public IEnumerable WithChildren() => ChildSwaps.SelectMany(c => c.WithChildren()).Prepend(this); } -public sealed class MetaSwap : Swap +public interface IMetaSwap { + public IMetaIdentifier SwapFromIdentifier { get; } + public IMetaIdentifier SwapToIdentifier { get; } + + public object SwapFromDefaultEntry { get; } + public object SwapToDefaultEntry { get; } + public object SwapToModdedEntry { get; } + + public bool SwapToIsDefault { get; } + public bool SwapAppliedIsDefault { get; } +} + +public sealed class MetaSwap : Swap, IMetaSwap + where TIdentifier : unmanaged, IMetaIdentifier + where TEntry : unmanaged, IEquatable +{ + public TIdentifier SwapFromIdentifier; + public TIdentifier SwapToIdentifier; + /// The default value of a specific meta manipulation that needs to be redirected. - public MetaManipulation SwapFrom; + public TEntry SwapFromDefaultEntry; /// The default value of the same Meta entry of the redirected item. - public MetaManipulation SwapToDefault; + public TEntry SwapToDefaultEntry; /// The modded value of the same Meta entry of the redirected item, or the same as SwapToDefault if unmodded. - public MetaManipulation SwapToModded; + public TEntry SwapToModdedEntry; - /// The modded value applied to the specific meta manipulation target before redirection. - public MetaManipulation SwapApplied; - - /// Whether SwapToModded equals SwapToDefault. - public bool SwapToIsDefault; + /// Whether SwapToModdedEntry equals SwapToDefaultEntry. + public bool SwapToIsDefault { get; } /// Whether the applied meta manipulation does not change anything against the default. - public bool SwapAppliedIsDefault; + public bool SwapAppliedIsDefault { get; } /// /// Create a new MetaSwap from the original meta identifier and the target meta identifier. /// - /// A function that converts the given manipulation to the modded one. - /// The original meta identifier with its default value. - /// The target meta identifier with its default value. - public MetaSwap(Func manipulations, MetaManipulation manipFrom, MetaManipulation manipTo) + /// A function that obtains a modded meta entry if it exists. + /// The original meta identifier. + /// The default value for the original meta identifier. + /// The target meta identifier. + /// The default value for the target meta identifier. + public MetaSwap(Func manipulations, TIdentifier manipFromIdentifier, TEntry manipFromEntry, + TIdentifier manipToIdentifier, TEntry manipToEntry) { - SwapFrom = manipFrom; - SwapToDefault = manipTo; + SwapFromIdentifier = manipFromIdentifier; + SwapToIdentifier = manipToIdentifier; + SwapFromDefaultEntry = manipFromEntry; + SwapToDefaultEntry = manipToEntry; - SwapToModded = manipulations(manipTo); - SwapToIsDefault = manipTo.EntryEquals(SwapToModded); - SwapApplied = SwapFrom.WithEntryOf(SwapToModded); - SwapAppliedIsDefault = SwapApplied.EntryEquals(SwapFrom); + SwapToModdedEntry = manipulations(SwapToIdentifier) ?? SwapToDefaultEntry; + SwapToIsDefault = SwapToModdedEntry.Equals(SwapToDefaultEntry); + SwapAppliedIsDefault = SwapToModdedEntry.Equals(SwapFromDefaultEntry); } + + IMetaIdentifier IMetaSwap.SwapFromIdentifier + => SwapFromIdentifier; + + IMetaIdentifier IMetaSwap.SwapToIdentifier + => SwapToIdentifier; + + object IMetaSwap.SwapFromDefaultEntry + => SwapFromDefaultEntry; + + object IMetaSwap.SwapToDefaultEntry + => SwapToDefaultEntry; + + object IMetaSwap.SwapToModdedEntry + => SwapToModdedEntry; } public sealed class FileSwap : Swap @@ -113,8 +146,7 @@ public sealed class FileSwap : Swap /// A full swap container with the actual file in memory. /// True if everything could be read correctly, false otherwise. public static FileSwap CreateSwap(MetaFileManager manager, ResourceType type, Func redirections, - string swapFromRequest, string swapToRequest, - string? swapFromPreChange = null) + string swapFromRequest, string swapToRequest, string? swapFromPreChange = null) { var swap = new FileSwap { diff --git a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs index 6010cdaf..62115dd6 100644 --- a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs +++ b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs @@ -240,7 +240,7 @@ public class ItemSwapTab : IDisposable, ITab { return swap switch { - MetaSwap meta => $"{meta.SwapFrom}: {meta.SwapFrom.EntryToString()} -> {meta.SwapApplied.EntryToString()}", + IMetaSwap meta => $"{meta.SwapFromIdentifier}: {meta.SwapFromDefaultEntry} -> {meta.SwapToModdedEntry}", FileSwap file => $"{file.Type}: {file.SwapFromRequestPath} -> {file.SwapToModded.FullName}{(file.DataWasChanged ? " (EDITED)" : string.Empty)}", _ => string.Empty, @@ -410,7 +410,7 @@ public class ItemSwapTab : IDisposable, ITab private ImRaii.IEndObject DrawTab(SwapType newTab) { - using var tab = ImRaii.TabItem(newTab is SwapType.BetweenSlots ? "Between Slots" : newTab.ToString()); + var tab = ImRaii.TabItem(newTab is SwapType.BetweenSlots ? "Between Slots" : newTab.ToString()); if (tab) { _dirty |= _lastTab != newTab;