Update for GameData changes.

This commit is contained in:
Ottermandias 2023-12-20 16:39:26 +01:00
parent 6dc5916f2b
commit 5d28904bdf
24 changed files with 160 additions and 123 deletions

@ -1 +1 @@
Subproject commit c53e578e750d26f83a4e81aca1681c5b01d25a5a
Subproject commit bee73fbe1e263d81067029ad97c8e4a263c88147

View file

@ -45,7 +45,7 @@ public readonly struct EqdpCache : IDisposable
foreach (var file in _eqdpFiles.OfType<ExpandedEqdpFile>())
{
var relevant = CharacterUtility.RelevantIndices[file.Index.Value];
file.Reset(_eqdpManipulations.Where(m => m.FileIndex() == relevant).Select(m => (SetId)m.SetId));
file.Reset(_eqdpManipulations.Where(m => m.FileIndex() == relevant).Select(m => (PrimaryId)m.SetId));
}
_eqdpManipulations.Clear();

View file

@ -74,12 +74,12 @@ public struct EstCache : IDisposable
};
}
internal ushort GetEstEntry(MetaFileManager manager, EstManipulation.EstType type, GenderRace genderRace, SetId setId)
internal ushort GetEstEntry(MetaFileManager manager, EstManipulation.EstType type, GenderRace genderRace, PrimaryId primaryId)
{
var file = GetEstFile(type);
return file != null
? file[genderRace, setId.Id]
: EstFile.GetDefault(manager, type, genderRace, setId);
? file[genderRace, primaryId.Id]
: EstFile.GetDefault(manager, type, genderRace, primaryId);
}
public void Reset()

View file

@ -187,17 +187,17 @@ public class MetaCache : IDisposable, IEnumerable<KeyValuePair<MetaManipulation,
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out Meta.Files.ImcFile? file)
=> _imcCache.GetImcFile(path, out file);
internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, SetId setId)
internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, PrimaryId primaryId)
{
var eqdpFile = _eqdpCache.EqdpFile(race, accessory);
if (eqdpFile != null)
return setId.Id < eqdpFile.Count ? eqdpFile[setId] : default;
return primaryId.Id < eqdpFile.Count ? eqdpFile[primaryId] : default;
else
return Meta.Files.ExpandedEqdpFile.GetDefault(_manager, race, accessory, setId);
return Meta.Files.ExpandedEqdpFile.GetDefault(_manager, race, accessory, primaryId);
}
internal ushort GetEstEntry(EstManipulation.EstType type, GenderRace genderRace, SetId setId)
=> _estCache.GetEstEntry(_manager, type, genderRace, setId);
internal ushort GetEstEntry(EstManipulation.EstType type, GenderRace genderRace, PrimaryId primaryId)
=> _estCache.GetEstEntry(_manager, type, genderRace, primaryId);
/// <summary> Use this when CharacterUtility becomes ready. </summary>
private void ApplyStoredManipulations()

View file

@ -37,7 +37,7 @@ internal partial record ResolveContext
private unsafe GenderRace ResolveModelRaceCode()
=> ResolveEqdpRaceCode(Slot, Equipment.Set);
private unsafe GenderRace ResolveEqdpRaceCode(EquipSlot slot, SetId setId)
private unsafe GenderRace ResolveEqdpRaceCode(EquipSlot slot, PrimaryId primaryId)
{
var slotIndex = slot.ToIndex();
if (slotIndex >= 10 || ModelType != ModelType.Human)
@ -55,7 +55,7 @@ internal partial record ResolveContext
if (metaCache == null)
return GenderRace.MidlanderMale;
var entry = metaCache.GetEqdpEntry(characterRaceCode, accessory, setId);
var entry = metaCache.GetEqdpEntry(characterRaceCode, accessory, primaryId);
if (entry.ToBits(slot).Item2)
return characterRaceCode;
@ -63,7 +63,7 @@ internal partial record ResolveContext
if (fallbackRaceCode == GenderRace.MidlanderMale)
return GenderRace.MidlanderMale;
entry = metaCache.GetEqdpEntry(fallbackRaceCode, accessory, setId);
entry = metaCache.GetEqdpEntry(fallbackRaceCode, accessory, primaryId);
if (entry.ToBits(slot).Item2)
return fallbackRaceCode;
@ -229,7 +229,7 @@ internal partial record ResolveContext
return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
}
private unsafe (GenderRace RaceCode, string Slot, SetId Set) ResolveHumanSkeletonData(uint partialSkeletonIndex)
private unsafe (GenderRace RaceCode, string Slot, PrimaryId Set) ResolveHumanSkeletonData(uint partialSkeletonIndex)
{
var human = (Human*)CharacterBase.Value;
var characterRaceCode = (GenderRace)human->RaceSexId;
@ -262,17 +262,17 @@ internal partial record ResolveContext
}
}
private unsafe (GenderRace RaceCode, string Slot, SetId Set) ResolveHumanEquipmentSkeletonData(EquipSlot slot, EstManipulation.EstType type)
private unsafe (GenderRace RaceCode, string Slot, PrimaryId Set) ResolveHumanEquipmentSkeletonData(EquipSlot slot, EstManipulation.EstType type)
{
var human = (Human*)CharacterBase.Value;
var equipment = ((CharacterArmor*)&human->Head)[slot.ToIndex()];
return ResolveHumanExtraSkeletonData(ResolveEqdpRaceCode(slot, equipment.Set), type, equipment.Set);
}
private unsafe (GenderRace RaceCode, string Slot, SetId Set) ResolveHumanExtraSkeletonData(GenderRace raceCode, EstManipulation.EstType type, SetId set)
private unsafe (GenderRace RaceCode, string Slot, PrimaryId Set) ResolveHumanExtraSkeletonData(GenderRace raceCode, EstManipulation.EstType type, PrimaryId primary)
{
var metaCache = Global.Collection.MetaCache;
var skeletonSet = metaCache == null ? default : metaCache.GetEstEntry(type, raceCode, set);
var skeletonSet = metaCache == null ? default : metaCache.GetEstEntry(type, raceCode, primary);
return (raceCode, EstManipulation.ToName(type), skeletonSet);
}

View file

@ -25,8 +25,8 @@ internal record GlobalResolveContext(ObjectIdentification Identifier, ModCollect
public readonly Dictionary<(Utf8GamePath, nint), ResourceNode> Nodes = new(128);
public unsafe ResolveContext CreateContext(CharacterBase* characterBase, uint slotIndex = 0xFFFFFFFFu,
EquipSlot slot = EquipSlot.Unknown, CharacterArmor equipment = default, WeaponType weaponType = default)
=> new(this, characterBase, slotIndex, slot, equipment, weaponType);
EquipSlot slot = EquipSlot.Unknown, CharacterArmor equipment = default, SecondaryId secondaryId = default)
=> new(this, characterBase, slotIndex, slot, equipment, secondaryId);
}
internal partial record ResolveContext(
@ -35,7 +35,7 @@ internal partial record ResolveContext(
uint SlotIndex,
EquipSlot Slot,
CharacterArmor Equipment,
WeaponType WeaponType)
SecondaryId SecondaryId)
{
private static readonly ByteString ShpkPrefix = ByteString.FromSpanUnsafe("shader/sm5/shpk"u8, true, true, true);

View file

@ -38,7 +38,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
public int Count
=> (Length - DataOffset) / EqdpEntrySize;
public EqdpEntry this[SetId id]
public EqdpEntry this[PrimaryId id]
{
get
{
@ -79,7 +79,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
MemoryUtility.MemSet(myDataPtr, 0, Length - (int)((byte*)myDataPtr - Data));
}
public void Reset(IEnumerable<SetId> entries)
public void Reset(IEnumerable<PrimaryId> entries)
{
foreach (var entry in entries)
this[entry] = GetDefault(entry);
@ -101,18 +101,18 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
Reset();
}
public EqdpEntry GetDefault(SetId setId)
=> GetDefault(Manager, Index, setId);
public EqdpEntry GetDefault(PrimaryId primaryId)
=> GetDefault(Manager, Index, primaryId);
public static EqdpEntry GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex idx, SetId setId)
=> GetDefault((byte*)manager.CharacterUtility.DefaultResource(idx).Address, setId);
public static EqdpEntry GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex idx, PrimaryId primaryId)
=> GetDefault((byte*)manager.CharacterUtility.DefaultResource(idx).Address, primaryId);
public static EqdpEntry GetDefault(byte* data, SetId setId)
public static EqdpEntry GetDefault(byte* data, PrimaryId primaryId)
{
var blockSize = *(ushort*)(data + IdentifierSize);
var totalBlockCount = *(ushort*)(data + IdentifierSize + 2);
var blockIdx = setId.Id / blockSize;
var blockIdx = primaryId.Id / blockSize;
if (blockIdx >= totalBlockCount)
return 0;
@ -121,9 +121,9 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
return 0;
var blockData = (ushort*)(data + IdentifierSize + PreambleSize + totalBlockCount * 2 + block * 2);
return (EqdpEntry)(*(blockData + setId.Id % blockSize));
return (EqdpEntry)(*(blockData + primaryId.Id % blockSize));
}
public static EqdpEntry GetDefault(MetaFileManager manager, GenderRace raceCode, bool accessory, SetId setId)
=> GetDefault(manager, CharacterUtility.ReverseIndices[(int)CharacterUtilityData.EqdpIdx(raceCode, accessory)], setId);
public static EqdpEntry GetDefault(MetaFileManager manager, GenderRace raceCode, bool accessory, PrimaryId primaryId)
=> GetDefault(manager, CharacterUtility.ReverseIndices[(int)CharacterUtilityData.EqdpIdx(raceCode, accessory)], primaryId);
}

View file

@ -24,7 +24,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
public ulong ControlBlock
=> *(ulong*)Data;
protected ulong GetInternal(SetId idx)
protected ulong GetInternal(PrimaryId idx)
{
return idx.Id switch
{
@ -34,7 +34,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
};
}
protected void SetInternal(SetId idx, ulong value)
protected void SetInternal(PrimaryId idx, ulong value)
{
idx = idx.Id switch
{
@ -81,13 +81,13 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
Reset();
}
protected static ulong GetDefaultInternal(MetaFileManager manager, CharacterUtility.InternalIndex fileIndex, SetId setId, ulong def)
protected static ulong GetDefaultInternal(MetaFileManager manager, CharacterUtility.InternalIndex fileIndex, PrimaryId primaryId, ulong def)
{
var data = (byte*)manager.CharacterUtility.DefaultResource(fileIndex).Address;
if (setId == 0)
setId = 1;
if (primaryId == 0)
primaryId = 1;
var blockIdx = setId.Id / BlockSize;
var blockIdx = primaryId.Id / BlockSize;
if (blockIdx >= NumBlocks)
return def;
@ -97,7 +97,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
return def;
var count = BitOperations.PopCount(control & (blockBit - 1));
var idx = setId.Id % BlockSize;
var idx = primaryId.Id % BlockSize;
var ptr = (ulong*)data + BlockSize * count + idx;
return *ptr;
}
@ -112,15 +112,15 @@ public sealed class ExpandedEqpFile : ExpandedEqpGmpBase, IEnumerable<EqpEntry>
: base(manager, false)
{ }
public EqpEntry this[SetId idx]
public EqpEntry this[PrimaryId idx]
{
get => (EqpEntry)GetInternal(idx);
set => SetInternal(idx, (ulong)value);
}
public static EqpEntry GetDefault(MetaFileManager manager, SetId setIdx)
=> (EqpEntry)GetDefaultInternal(manager, InternalIndex, setIdx, (ulong)Eqp.DefaultEntry);
public static EqpEntry GetDefault(MetaFileManager manager, PrimaryId primaryIdx)
=> (EqpEntry)GetDefaultInternal(manager, InternalIndex, primaryIdx, (ulong)Eqp.DefaultEntry);
protected override unsafe void SetEmptyBlock(int idx)
{
@ -130,7 +130,7 @@ public sealed class ExpandedEqpFile : ExpandedEqpGmpBase, IEnumerable<EqpEntry>
*ptr = (ulong)Eqp.DefaultEntry;
}
public void Reset(IEnumerable<SetId> entries)
public void Reset(IEnumerable<PrimaryId> entries)
{
foreach (var entry in entries)
this[entry] = GetDefault(Manager, entry);
@ -155,16 +155,16 @@ public sealed class ExpandedGmpFile : ExpandedEqpGmpBase, IEnumerable<GmpEntry>
: base(manager, true)
{ }
public GmpEntry this[SetId idx]
public GmpEntry this[PrimaryId idx]
{
get => (GmpEntry)GetInternal(idx);
set => SetInternal(idx, (ulong)value);
}
public static GmpEntry GetDefault(MetaFileManager manager, SetId setIdx)
=> (GmpEntry)GetDefaultInternal(manager, InternalIndex, setIdx, (ulong)GmpEntry.Default);
public static GmpEntry GetDefault(MetaFileManager manager, PrimaryId primaryIdx)
=> (GmpEntry)GetDefaultInternal(manager, InternalIndex, primaryIdx, (ulong)GmpEntry.Default);
public void Reset(IEnumerable<SetId> entries)
public void Reset(IEnumerable<PrimaryId> entries)
{
foreach (var entry in entries)
this[entry] = GetDefault(Manager, entry);

View file

@ -167,21 +167,21 @@ public sealed unsafe class EstFile : MetaBaseFile
public ushort GetDefault(GenderRace genderRace, ushort setId)
=> GetDefault(Manager, Index, genderRace, setId);
public static ushort GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex index, GenderRace genderRace, SetId setId)
public static ushort GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex index, GenderRace genderRace, PrimaryId primaryId)
{
var data = (byte*)manager.CharacterUtility.DefaultResource(index).Address;
var count = *(int*)data;
var span = new ReadOnlySpan<Info>(data + 4, count);
var (idx, found) = FindEntry(span, genderRace, setId.Id);
var (idx, found) = FindEntry(span, genderRace, primaryId.Id);
if (!found)
return 0;
return *(ushort*)(data + 4 + count * EntryDescSize + idx * EntrySize);
}
public static ushort GetDefault(MetaFileManager manager, MetaIndex metaIndex, GenderRace genderRace, SetId setId)
=> GetDefault(manager, CharacterUtility.ReverseIndices[(int)metaIndex], genderRace, setId);
public static ushort GetDefault(MetaFileManager manager, MetaIndex metaIndex, GenderRace genderRace, PrimaryId primaryId)
=> GetDefault(manager, CharacterUtility.ReverseIndices[(int)metaIndex], genderRace, primaryId);
public static ushort GetDefault(MetaFileManager manager, EstManipulation.EstType estType, GenderRace genderRace, SetId setId)
=> GetDefault(manager, (MetaIndex)estType, genderRace, setId);
public static ushort GetDefault(MetaFileManager manager, EstManipulation.EstType estType, GenderRace genderRace, PrimaryId primaryId)
=> GetDefault(manager, (MetaIndex)estType, genderRace, primaryId);
}

View file

@ -18,13 +18,13 @@ public readonly struct EqdpManipulation : IMetaManipulation<EqdpManipulation>
[JsonConverter(typeof(StringEnumConverter))]
public ModelRace Race { get; private init; }
public SetId SetId { get; private init; }
public PrimaryId SetId { get; private init; }
[JsonConverter(typeof(StringEnumConverter))]
public EquipSlot Slot { get; private init; }
[JsonConstructor]
public EqdpManipulation(EqdpEntry entry, EquipSlot slot, Gender gender, ModelRace race, SetId setId)
public EqdpManipulation(EqdpEntry entry, EquipSlot slot, Gender gender, ModelRace race, PrimaryId setId)
{
Gender = gender;
Race = race;

View file

@ -15,13 +15,13 @@ public readonly struct EqpManipulation : IMetaManipulation<EqpManipulation>
[JsonConverter(typeof(ForceNumericFlagEnumConverter))]
public EqpEntry Entry { get; private init; }
public SetId SetId { get; private init; }
public PrimaryId SetId { get; private init; }
[JsonConverter(typeof(StringEnumConverter))]
public EquipSlot Slot { get; private init; }
[JsonConstructor]
public EqpManipulation(EqpEntry entry, EquipSlot slot, SetId setId)
public EqpManipulation(EqpEntry entry, EquipSlot slot, PrimaryId setId)
{
Slot = slot;
SetId = setId;

View file

@ -36,13 +36,14 @@ public readonly struct EstManipulation : IMetaManipulation<EstManipulation>
[JsonConverter(typeof(StringEnumConverter))]
public ModelRace Race { get; private init; }
public SetId SetId { get; private init; }
public PrimaryId SetId { get; private init; }
[JsonConverter(typeof(StringEnumConverter))]
public EstType Slot { get; private init; }
[JsonConstructor]
public EstManipulation(Gender gender, ModelRace race, EstType slot, SetId setId, ushort entry)
public EstManipulation(Gender gender, ModelRace race, EstType slot, PrimaryId setId, ushort entry)
{
Entry = entry;
Gender = gender;

View file

@ -9,10 +9,10 @@ namespace Penumbra.Meta.Manipulations;
public readonly struct GmpManipulation : IMetaManipulation<GmpManipulation>
{
public GmpEntry Entry { get; private init; }
public SetId SetId { get; private init; }
public PrimaryId SetId { get; private init; }
[JsonConstructor]
public GmpManipulation(GmpEntry entry, SetId setId)
public GmpManipulation(GmpEntry entry, PrimaryId setId)
{
Entry = entry;
SetId = setId;

View file

@ -13,8 +13,8 @@ namespace Penumbra.Meta.Manipulations;
public readonly struct ImcManipulation : IMetaManipulation<ImcManipulation>
{
public ImcEntry Entry { get; private init; }
public SetId PrimaryId { get; private init; }
public SetId SecondaryId { get; private init; }
public PrimaryId PrimaryId { get; private init; }
public PrimaryId SecondaryId { get; private init; }
public Variant Variant { get; private init; }
[JsonConverter(typeof(StringEnumConverter))]
@ -26,7 +26,7 @@ public readonly struct ImcManipulation : IMetaManipulation<ImcManipulation>
[JsonConverter(typeof(StringEnumConverter))]
public BodySlot BodySlot { get; private init; }
public ImcManipulation(EquipSlot equipSlot, ushort variant, SetId primaryId, ImcEntry entry)
public ImcManipulation(EquipSlot equipSlot, ushort variant, PrimaryId primaryId, ImcEntry entry)
{
Entry = entry;
PrimaryId = primaryId;
@ -42,7 +42,7 @@ public readonly struct ImcManipulation : IMetaManipulation<ImcManipulation>
// 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, SetId primaryId, SetId secondaryId, ushort variant,
internal ImcManipulation(ObjectType objectType, BodySlot bodySlot, PrimaryId primaryId, PrimaryId secondaryId, ushort variant,
EquipSlot equipSlot, ImcEntry entry)
{
Entry = entry;

View file

@ -270,4 +270,22 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
Type.Rsp => $"{Rsp.Entry}",
_ => 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

@ -12,7 +12,7 @@ public static class CustomizationSwap
{
/// The .mdl file for customizations is unique per racecode, slot and id, thus the .mdl redirection itself is independent of the mode.
public static FileSwap CreateMdl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, BodySlot slot, GenderRace race,
SetId idFrom, SetId idTo)
PrimaryId idFrom, PrimaryId idTo)
{
if (idFrom.Id > byte.MaxValue)
throw new Exception($"The Customization ID {idFrom} is too large for {slot}.");
@ -43,7 +43,7 @@ public static class CustomizationSwap
}
public static FileSwap CreateMtrl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, BodySlot slot, GenderRace race,
SetId idFrom, SetId idTo, byte variant,
PrimaryId idFrom, PrimaryId idTo, byte variant,
ref string fileName, ref bool dataWasChanged)
{
variant = slot is BodySlot.Face or BodySlot.Ear ? byte.MaxValue : variant;
@ -79,7 +79,7 @@ public static class CustomizationSwap
}
public static FileSwap CreateTex(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, BodySlot slot, GenderRace race,
SetId idFrom, ref MtrlFile.Texture texture,
PrimaryId idFrom, ref MtrlFile.Texture texture,
ref bool dataWasChanged)
{
var path = texture.Path;

View file

@ -185,13 +185,13 @@ public static class EquipmentSwap
}
public static MetaSwap? CreateEqdp(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, GenderRace gr, SetId idFrom,
SetId idTo, byte mtrlTo)
Func<MetaManipulation, MetaManipulation> 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, SetId idFrom,
SetId idTo, byte mtrlTo)
Func<MetaManipulation, MetaManipulation> 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,
@ -214,11 +214,11 @@ public static class EquipmentSwap
}
public static FileSwap CreateMdl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slot, GenderRace gr,
SetId idFrom, SetId idTo, byte mtrlTo)
PrimaryId idFrom, PrimaryId idTo, byte mtrlTo)
=> CreateMdl(manager, redirections, slot, slot, gr, idFrom, idTo, mtrlTo);
public static FileSwap CreateMdl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slotFrom, EquipSlot slotTo,
GenderRace gr, SetId idFrom, SetId idTo, byte mtrlTo)
GenderRace gr, PrimaryId idFrom, PrimaryId idTo, byte mtrlTo)
{
var mdlPathFrom = slotFrom.IsAccessory()
? GamePaths.Accessory.Mdl.Path(idFrom, gr, slotFrom)
@ -236,7 +236,7 @@ public static class EquipmentSwap
return mdl;
}
private static void LookupItem(EquipItem i, out EquipSlot slot, out SetId modelId, out Variant variant)
private static void LookupItem(EquipItem i, out EquipSlot slot, out PrimaryId modelId, out Variant variant)
{
slot = i.Type.ToSlot();
if (!slot.IsEquipmentPiece())
@ -247,7 +247,7 @@ public static class EquipmentSwap
}
private static (ImcFile, Variant[], EquipItem[]) GetVariants(MetaFileManager manager, ObjectIdentification identifier, EquipSlot slotFrom,
SetId idFrom, SetId idTo, Variant variantFrom)
PrimaryId idFrom, PrimaryId idTo, Variant variantFrom)
{
var entry = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, default);
var imc = new ImcFile(manager, entry);
@ -270,8 +270,8 @@ public static class EquipmentSwap
return (imc, variants, items);
}
public static MetaSwap? CreateGmp(MetaFileManager manager, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, SetId idFrom,
SetId idTo)
public static MetaSwap? CreateGmp(MetaFileManager manager, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, PrimaryId idFrom,
PrimaryId idTo)
{
if (slot is not EquipSlot.Head)
return null;
@ -283,12 +283,12 @@ public static class EquipmentSwap
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot,
SetId idFrom, SetId idTo, Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo)
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, SetId idFrom, SetId idTo,
EquipSlot slotFrom, EquipSlot slotTo, PrimaryId idFrom, PrimaryId idTo,
Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo)
{
var entryFrom = imcFileFrom.GetEntry(ImcFile.PartIndex(slotFrom), variantFrom);
@ -322,7 +322,7 @@ public static class EquipmentSwap
// Example: Abyssos Helm / Body
public static FileSwap? CreateAvfx(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, SetId idFrom, SetId 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,8 +340,8 @@ public static class EquipmentSwap
return avfx;
}
public static MetaSwap? CreateEqp(MetaFileManager manager, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, SetId idFrom,
SetId idTo)
public static MetaSwap? CreateEqp(MetaFileManager manager, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, PrimaryId idFrom,
PrimaryId idTo)
{
if (slot.IsAccessory())
return null;
@ -353,13 +353,13 @@ public static class EquipmentSwap
return new MetaSwap(manips, eqpFrom, eqpTo);
}
public static FileSwap? CreateMtrl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slot, SetId idFrom,
SetId idTo, byte variantTo, ref string fileName,
public static FileSwap? CreateMtrl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slot, PrimaryId idFrom,
PrimaryId idTo, byte variantTo, ref string fileName,
ref bool dataWasChanged)
=> CreateMtrl(manager, redirections, slot, slot, idFrom, idTo, variantTo, ref fileName, ref dataWasChanged);
public static FileSwap? CreateMtrl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slotFrom, EquipSlot slotTo,
SetId idFrom, SetId idTo, byte variantTo, ref string fileName,
PrimaryId idFrom, PrimaryId idTo, byte variantTo, ref string fileName,
ref bool dataWasChanged)
{
var prefix = slotTo.IsAccessory() ? 'a' : 'e';
@ -397,13 +397,13 @@ public static class EquipmentSwap
return mtrl;
}
public static FileSwap CreateTex(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, char prefix, SetId idFrom, SetId 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);
public static FileSwap CreateTex(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, char prefix, EquipSlot slotFrom,
EquipSlot slotTo, SetId idFrom,
SetId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged)
EquipSlot slotTo, PrimaryId idFrom,
PrimaryId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged)
{
var path = texture.Path;
var addedDashes = false;

View file

@ -150,7 +150,7 @@ public static class ItemSwap
/// <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, EstManipulation.EstType type,
GenderRace genderRace, SetId idFrom, SetId idTo, bool ownMdl)
GenderRace genderRace, PrimaryId idFrom, PrimaryId idTo, bool ownMdl)
{
if (type == 0)
return null;
@ -195,7 +195,7 @@ public static class ItemSwap
}
}
public static string ReplaceAnyId(string path, char idType, SetId id, bool condition = true)
public static string ReplaceAnyId(string path, char idType, PrimaryId id, bool condition = true)
=> condition
? Regex.Replace(path, $"{idType}\\d{{4}}", $"{idType}{id.Id:D4}")
: path;
@ -203,10 +203,10 @@ public static class ItemSwap
public static string ReplaceAnyRace(string path, GenderRace to, bool condition = true)
=> ReplaceAnyId(path, 'c', (ushort)to, condition);
public static string ReplaceAnyBody(string path, BodySlot slot, SetId to, bool condition = true)
public static string ReplaceAnyBody(string path, BodySlot slot, PrimaryId to, bool condition = true)
=> ReplaceAnyId(path, slot.ToAbbreviation(), to, condition);
public static string ReplaceId(string path, char type, SetId idFrom, SetId idTo, bool condition = true)
public static string ReplaceId(string path, char type, PrimaryId idFrom, PrimaryId idTo, bool condition = true)
=> condition
? path.Replace($"{type}{idFrom.Id:D4}", $"{type}{idTo.Id:D4}")
: path;
@ -219,7 +219,7 @@ public static class ItemSwap
public static string ReplaceRace(string path, GenderRace from, GenderRace to, bool condition = true)
=> ReplaceId(path, 'c', (ushort)from, (ushort)to, condition);
public static string ReplaceBody(string path, BodySlot slot, SetId idFrom, SetId idTo, bool condition = true)
public static string ReplaceBody(string path, BodySlot slot, PrimaryId idFrom, PrimaryId idTo, bool condition = true)
=> ReplaceId(path, slot.ToAbbreviation(), idFrom, idTo, condition);
public static string AddSuffix(string path, string ext, string suffix, bool condition = true)

View file

@ -145,7 +145,7 @@ public class ItemSwapContainer
return ret;
}
public bool LoadCustomization(MetaFileManager manager, BodySlot slot, GenderRace race, SetId from, SetId to,
public bool LoadCustomization(MetaFileManager manager, BodySlot slot, GenderRace race, PrimaryId from, PrimaryId to,
ModCollection? collection = null)
{
var pathResolver = PathResolver(collection);

View file

@ -139,7 +139,7 @@ public class ModCacheManager : IDisposable
{
case ModPathChangeType.Added:
case ModPathChangeType.Reloaded:
Refresh(mod);
RefreshWithChangedItems(mod);
break;
}
}
@ -151,10 +151,28 @@ public class ModCacheManager : IDisposable
}
private void OnModDiscoveryFinished()
=> Parallel.ForEach(_modManager, Refresh);
{
if (!_identifier.Awaiter.IsCompletedSuccessfully || _updatingItems)
{
Parallel.ForEach(_modManager, RefreshWithoutChangedItems);
}
else
{
_updatingItems = true;
Parallel.ForEach(_modManager, RefreshWithChangedItems);
_updatingItems = false;
}
}
private void OnIdentifierCreation()
=> Parallel.ForEach(_modManager, UpdateChangedItems);
{
if (_updatingItems)
return;
_updatingItems = true;
Parallel.ForEach(_modManager, UpdateChangedItems);
_updatingItems = false;
}
private static void UpdateFileCount(Mod mod)
=> mod.TotalFileCount = mod.AllSubMods.Sum(s => s.Files.Count);
@ -173,15 +191,8 @@ public class ModCacheManager : IDisposable
private void UpdateChangedItems(Mod mod)
{
if (_updatingItems)
return;
_updatingItems = true;
var changedItems = (SortedList<string, object?>)mod.ChangedItems;
changedItems.Clear();
if (!_identifier.Awaiter.IsCompletedSuccessfully)
return;
foreach (var gamePath in mod.AllSubMods.SelectMany(m => m.Files.Keys.Concat(m.FileSwaps.Keys)))
_identifier.Identify(changedItems, gamePath.ToString());
@ -189,7 +200,6 @@ public class ModCacheManager : IDisposable
ComputeChangedItems(_identifier, changedItems, manip);
mod.LowerChangedItemsString = string.Join("\0", mod.ChangedItems.Keys.Select(k => k.ToLowerInvariant()));
_updatingItems = false;
}
private static void UpdateCounts(Mod mod)
@ -210,10 +220,16 @@ public class ModCacheManager : IDisposable
}
}
private void Refresh(Mod mod)
private void RefreshWithChangedItems(Mod mod)
{
UpdateTags(mod);
UpdateCounts(mod);
UpdateChangedItems(mod);
}
private void RefreshWithoutChangedItems(Mod mod)
{
UpdateTags(mod);
UpdateCounts(mod);
}
}

View file

@ -30,9 +30,9 @@ public sealed class SubMod : ISubMod
public bool IsDefault
=> GroupIdx < 0;
public Dictionary<Utf8GamePath, FullPath> FileData = new();
public Dictionary<Utf8GamePath, FullPath> FileSwapData = new();
public HashSet<MetaManipulation> ManipulationData = new();
public Dictionary<Utf8GamePath, FullPath> FileData = [];
public Dictionary<Utf8GamePath, FullPath> FileSwapData = [];
public HashSet<MetaManipulation> ManipulationData = [];
public SubMod(IMod parentMod)
=> ParentMod = parentMod;

View file

@ -91,8 +91,9 @@
</ItemGroup>
<Target Name="GetGitHash" BeforeTargets="GetAssemblyVersion" Returns="InformationalVersion">
<Exec Command="git rev-parse --short HEAD" ConsoleToMSBuild="true" StandardOutputImportance="low">
<Output TaskParameter="ConsoleOutput" PropertyName="GitCommitHash" />
<Exec Command="git rev-parse --short HEAD" ConsoleToMSBuild="true" StandardOutputImportance="low" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="GitCommitHashSuccess"/>
<Output TaskParameter="ConsoleOutput" PropertyName="GitCommitHash" Condition="$(GitCommitHashSuccess) == 0"/>
</Exec>
<PropertyGroup>

View file

@ -210,26 +210,26 @@ public class ItemSwapTab : IDisposable, ITab
break;
case SwapType.Hair when _targetId > 0 && _sourceId > 0:
_swapData.LoadCustomization(_metaFileManager, BodySlot.Hair, Names.CombinedRace(_currentGender, _currentRace),
(SetId)_sourceId,
(SetId)_targetId,
(PrimaryId)_sourceId,
(PrimaryId)_targetId,
_useCurrentCollection ? _collectionManager.Active.Current : null);
break;
case SwapType.Face when _targetId > 0 && _sourceId > 0:
_swapData.LoadCustomization(_metaFileManager, BodySlot.Face, Names.CombinedRace(_currentGender, _currentRace),
(SetId)_sourceId,
(SetId)_targetId,
(PrimaryId)_sourceId,
(PrimaryId)_targetId,
_useCurrentCollection ? _collectionManager.Active.Current : null);
break;
case SwapType.Ears when _targetId > 0 && _sourceId > 0:
_swapData.LoadCustomization(_metaFileManager, BodySlot.Ear, Names.CombinedRace(_currentGender, ModelRace.Viera),
(SetId)_sourceId,
(SetId)_targetId,
(PrimaryId)_sourceId,
(PrimaryId)_targetId,
_useCurrentCollection ? _collectionManager.Active.Current : null);
break;
case SwapType.Tail when _targetId > 0 && _sourceId > 0:
_swapData.LoadCustomization(_metaFileManager, BodySlot.Tail, Names.CombinedRace(_currentGender, _currentRace),
(SetId)_sourceId,
(SetId)_targetId,
(PrimaryId)_sourceId,
(PrimaryId)_targetId,
_useCurrentCollection ? _collectionManager.Active.Current : null);
break;
case SwapType.Weapon: break;

View file

@ -169,6 +169,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
ImRaii.TreeNode(leaf.Value.Name, flags).Dispose();
if (ImGui.IsItemClicked(ImGuiMouseButton.Middle))
{
_modManager.SetKnown(leaf.Value);
var (setting, collection) = _collectionManager.Active.Current[leaf.Value.Index];
if (_config.DeleteModModifier.ForcedModifier(new DoubleModifier(ModifierHotkey.Control, ModifierHotkey.Shift)).IsActive())
{