Remove TryGetValue(MetaManipulation) from MetaDictionary.

This commit is contained in:
Ottermandias 2024-06-08 19:09:46 +02:00
parent 5ca9e63a2a
commit 0445ed0ef9
11 changed files with 182 additions and 183 deletions

View file

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

View file

@ -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<GmpEntry>
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<PrimaryId> entries)
{
foreach (var entry in entries)

View file

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

View file

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

View file

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

View file

@ -52,6 +52,19 @@ public sealed class MetaDictionary : IEnumerable<MetaManipulation>
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<MetaManipulation>
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)
{

View file

@ -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>();
? [EquipSlot.LFinger]
: [];
}
public static EquipItem[] CreateTypeSwap(MetaFileManager manager, ObjectIdentification identifier, List<Swap> swaps,
Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips,
Func<Utf8GamePath, FullPath> 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<GenderRace>())
{
switch (gr.Split().Item1)
@ -99,7 +89,7 @@ public static class EquipmentSwap
}
public static EquipItem[] CreateItemSwap(MetaFileManager manager, ObjectIdentification identifier, List<Swap> swaps,
Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips, EquipItem itemFrom,
Func<Utf8GamePath, FullPath> 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<GenderRace>())
{
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<Utf8GamePath, FullPath> redirections,
Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, GenderRace gr, PrimaryId idFrom,
PrimaryId idTo, byte mtrlTo)
public static MetaSwap<EqdpIdentifier, EqdpEntryInternal>? CreateEqdp(MetaFileManager manager, Func<Utf8GamePath, FullPath> 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<Utf8GamePath, FullPath> redirections,
Func<MetaManipulation, MetaManipulation> manips, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, PrimaryId idFrom,
public static MetaSwap<EqdpIdentifier, EqdpEntryInternal>? CreateEqdp(MetaFileManager manager, Func<Utf8GamePath, FullPath> 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<EqdpIdentifier, EqdpEntryInternal>(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<MetaManipulation, MetaManipulation> manips, EquipSlot slot, PrimaryId idFrom,
PrimaryId idTo)
public static MetaSwap<GmpIdentifier, GmpEntry>? 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<GmpIdentifier, GmpEntry>(i => manips.TryGetValue(i, out var e) ? e : null, manipFromIdentifier, manipFromDefault, manipToIdentifier, manipToDefault);
}
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot,
PrimaryId idFrom, PrimaryId idTo, Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo)
public static MetaSwap<ImcIdentifier, ImcEntry> CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> 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<Utf8GamePath, FullPath> redirections,
Func<MetaManipulation, MetaManipulation> manips,
EquipSlot slotFrom, EquipSlot slotTo, PrimaryId idFrom, PrimaryId idTo,
public static MetaSwap<ImcIdentifier, ImcEntry> CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> 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<ImcIdentifier, ImcEntry>(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<Utf8GamePath, FullPath> redirections, PrimaryId idFrom, PrimaryId idTo, byte vfxId)
public static FileSwap? CreateAvfx(MetaFileManager manager, Func<Utf8GamePath, FullPath> 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<MetaManipulation, MetaManipulation> manips, EquipSlot slot, PrimaryId idFrom,
PrimaryId idTo)
public static MetaSwap<EqpIdentifier, EqpEntryInternal>? 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<EqpIdentifier, EqpEntryInternal>(i => manips.TryGetValue(i, out var e) ? e : null, manipFromIdentifier,
manipFromDefault, manipToIdentifier, manipToDefault);
}
public static FileSwap? CreateMtrl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slot, PrimaryId idFrom,
@ -397,7 +393,8 @@ public static class EquipmentSwap
return mtrl;
}
public static FileSwap CreateTex(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, char prefix, PrimaryId idFrom, PrimaryId idTo,
public static FileSwap CreateTex(MetaFileManager manager, Func<Utf8GamePath, FullPath> 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);

View file

@ -147,24 +147,23 @@ public static class ItemSwap
return FileSwap.CreateSwap(manager, ResourceType.Sklb, redirections, sklbPath, sklbPath);
}
/// <remarks> metaChanges is not manipulated, but IReadOnlySet does not support TryGetValue. </remarks>
public static MetaSwap? CreateEst(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
Func<MetaManipulation, MetaManipulation> manips, EstType type,
GenderRace genderRace, PrimaryId idFrom, PrimaryId idTo, bool ownMdl)
public static MetaSwap<EstIdentifier, EstEntry>? CreateEst(MetaFileManager manager, Func<Utf8GamePath, FullPath> 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<EstIdentifier, EstEntry>(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)

View file

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

View file

@ -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
{
/// <summary> Any further swaps belonging specifically to this tree of changes. </summary>
public readonly List<Swap> ChildSwaps = new();
public readonly List<Swap> ChildSwaps = [];
public IEnumerable<Swap> 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<TIdentifier, TEntry> : Swap, IMetaSwap
where TIdentifier : unmanaged, IMetaIdentifier
where TEntry : unmanaged, IEquatable<TEntry>
{
public TIdentifier SwapFromIdentifier;
public TIdentifier SwapToIdentifier;
/// <summary> The default value of a specific meta manipulation that needs to be redirected. </summary>
public MetaManipulation SwapFrom;
public TEntry SwapFromDefaultEntry;
/// <summary> The default value of the same Meta entry of the redirected item. </summary>
public MetaManipulation SwapToDefault;
public TEntry SwapToDefaultEntry;
/// <summary> The modded value of the same Meta entry of the redirected item, or the same as SwapToDefault if unmodded. </summary>
public MetaManipulation SwapToModded;
public TEntry SwapToModdedEntry;
/// <summary> The modded value applied to the specific meta manipulation target before redirection. </summary>
public MetaManipulation SwapApplied;
/// <summary> Whether SwapToModded equals SwapToDefault. </summary>
public bool SwapToIsDefault;
/// <summary> Whether SwapToModdedEntry equals SwapToDefaultEntry. </summary>
public bool SwapToIsDefault { get; }
/// <summary> Whether the applied meta manipulation does not change anything against the default. </summary>
public bool SwapAppliedIsDefault;
public bool SwapAppliedIsDefault { get; }
/// <summary>
/// Create a new MetaSwap from the original meta identifier and the target meta identifier.
/// </summary>
/// <param name="manipulations">A function that converts the given manipulation to the modded one.</param>
/// <param name="manipFrom">The original meta identifier with its default value.</param>
/// <param name="manipTo">The target meta identifier with its default value.</param>
public MetaSwap(Func<MetaManipulation, MetaManipulation> manipulations, MetaManipulation manipFrom, MetaManipulation manipTo)
/// <param name="manipulations">A function that obtains a modded meta entry if it exists. </param>
/// <param name="manipFromIdentifier"> The original meta identifier. </param>
/// <param name="manipFromEntry"> The default value for the original meta identifier. </param>
/// <param name="manipToIdentifier"> The target meta identifier. </param>
/// <param name="manipToEntry"> The default value for the target meta identifier. </param>
public MetaSwap(Func<TIdentifier, TEntry?> 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
/// <param name="swap">A full swap container with the actual file in memory.</param>
/// <returns>True if everything could be read correctly, false otherwise.</returns>
public static FileSwap CreateSwap(MetaFileManager manager, ResourceType type, Func<Utf8GamePath, FullPath> redirections,
string swapFromRequest, string swapToRequest,
string? swapFromPreChange = null)
string swapFromRequest, string swapToRequest, string? swapFromPreChange = null)
{
var swap = new FileSwap
{

View file

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