mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Update for GameData changes.
This commit is contained in:
parent
6dc5916f2b
commit
5d28904bdf
24 changed files with 160 additions and 123 deletions
|
|
@ -1 +1 @@
|
||||||
Subproject commit c53e578e750d26f83a4e81aca1681c5b01d25a5a
|
Subproject commit bee73fbe1e263d81067029ad97c8e4a263c88147
|
||||||
|
|
@ -45,7 +45,7 @@ public readonly struct EqdpCache : IDisposable
|
||||||
foreach (var file in _eqdpFiles.OfType<ExpandedEqdpFile>())
|
foreach (var file in _eqdpFiles.OfType<ExpandedEqdpFile>())
|
||||||
{
|
{
|
||||||
var relevant = CharacterUtility.RelevantIndices[file.Index.Value];
|
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();
|
_eqdpManipulations.Clear();
|
||||||
|
|
|
||||||
|
|
@ -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);
|
var file = GetEstFile(type);
|
||||||
return file != null
|
return file != null
|
||||||
? file[genderRace, setId.Id]
|
? file[genderRace, primaryId.Id]
|
||||||
: EstFile.GetDefault(manager, type, genderRace, setId);
|
: EstFile.GetDefault(manager, type, genderRace, primaryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
|
|
|
||||||
|
|
@ -187,17 +187,17 @@ public class MetaCache : IDisposable, IEnumerable<KeyValuePair<MetaManipulation,
|
||||||
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out Meta.Files.ImcFile? file)
|
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out Meta.Files.ImcFile? file)
|
||||||
=> _imcCache.GetImcFile(path, out 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);
|
var eqdpFile = _eqdpCache.EqdpFile(race, accessory);
|
||||||
if (eqdpFile != null)
|
if (eqdpFile != null)
|
||||||
return setId.Id < eqdpFile.Count ? eqdpFile[setId] : default;
|
return primaryId.Id < eqdpFile.Count ? eqdpFile[primaryId] : default;
|
||||||
else
|
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)
|
internal ushort GetEstEntry(EstManipulation.EstType type, GenderRace genderRace, PrimaryId primaryId)
|
||||||
=> _estCache.GetEstEntry(_manager, type, genderRace, setId);
|
=> _estCache.GetEstEntry(_manager, type, genderRace, primaryId);
|
||||||
|
|
||||||
/// <summary> Use this when CharacterUtility becomes ready. </summary>
|
/// <summary> Use this when CharacterUtility becomes ready. </summary>
|
||||||
private void ApplyStoredManipulations()
|
private void ApplyStoredManipulations()
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ internal partial record ResolveContext
|
||||||
private unsafe GenderRace ResolveModelRaceCode()
|
private unsafe GenderRace ResolveModelRaceCode()
|
||||||
=> ResolveEqdpRaceCode(Slot, Equipment.Set);
|
=> ResolveEqdpRaceCode(Slot, Equipment.Set);
|
||||||
|
|
||||||
private unsafe GenderRace ResolveEqdpRaceCode(EquipSlot slot, SetId setId)
|
private unsafe GenderRace ResolveEqdpRaceCode(EquipSlot slot, PrimaryId primaryId)
|
||||||
{
|
{
|
||||||
var slotIndex = slot.ToIndex();
|
var slotIndex = slot.ToIndex();
|
||||||
if (slotIndex >= 10 || ModelType != ModelType.Human)
|
if (slotIndex >= 10 || ModelType != ModelType.Human)
|
||||||
|
|
@ -55,7 +55,7 @@ internal partial record ResolveContext
|
||||||
if (metaCache == null)
|
if (metaCache == null)
|
||||||
return GenderRace.MidlanderMale;
|
return GenderRace.MidlanderMale;
|
||||||
|
|
||||||
var entry = metaCache.GetEqdpEntry(characterRaceCode, accessory, setId);
|
var entry = metaCache.GetEqdpEntry(characterRaceCode, accessory, primaryId);
|
||||||
if (entry.ToBits(slot).Item2)
|
if (entry.ToBits(slot).Item2)
|
||||||
return characterRaceCode;
|
return characterRaceCode;
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ internal partial record ResolveContext
|
||||||
if (fallbackRaceCode == GenderRace.MidlanderMale)
|
if (fallbackRaceCode == GenderRace.MidlanderMale)
|
||||||
return GenderRace.MidlanderMale;
|
return GenderRace.MidlanderMale;
|
||||||
|
|
||||||
entry = metaCache.GetEqdpEntry(fallbackRaceCode, accessory, setId);
|
entry = metaCache.GetEqdpEntry(fallbackRaceCode, accessory, primaryId);
|
||||||
if (entry.ToBits(slot).Item2)
|
if (entry.ToBits(slot).Item2)
|
||||||
return fallbackRaceCode;
|
return fallbackRaceCode;
|
||||||
|
|
||||||
|
|
@ -229,7 +229,7 @@ internal partial record ResolveContext
|
||||||
return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
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 human = (Human*)CharacterBase.Value;
|
||||||
var characterRaceCode = (GenderRace)human->RaceSexId;
|
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 human = (Human*)CharacterBase.Value;
|
||||||
var equipment = ((CharacterArmor*)&human->Head)[slot.ToIndex()];
|
var equipment = ((CharacterArmor*)&human->Head)[slot.ToIndex()];
|
||||||
return ResolveHumanExtraSkeletonData(ResolveEqdpRaceCode(slot, equipment.Set), type, equipment.Set);
|
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 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);
|
return (raceCode, EstManipulation.ToName(type), skeletonSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ internal record GlobalResolveContext(ObjectIdentification Identifier, ModCollect
|
||||||
public readonly Dictionary<(Utf8GamePath, nint), ResourceNode> Nodes = new(128);
|
public readonly Dictionary<(Utf8GamePath, nint), ResourceNode> Nodes = new(128);
|
||||||
|
|
||||||
public unsafe ResolveContext CreateContext(CharacterBase* characterBase, uint slotIndex = 0xFFFFFFFFu,
|
public unsafe ResolveContext CreateContext(CharacterBase* characterBase, uint slotIndex = 0xFFFFFFFFu,
|
||||||
EquipSlot slot = EquipSlot.Unknown, CharacterArmor equipment = default, WeaponType weaponType = default)
|
EquipSlot slot = EquipSlot.Unknown, CharacterArmor equipment = default, SecondaryId secondaryId = default)
|
||||||
=> new(this, characterBase, slotIndex, slot, equipment, weaponType);
|
=> new(this, characterBase, slotIndex, slot, equipment, secondaryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal partial record ResolveContext(
|
internal partial record ResolveContext(
|
||||||
|
|
@ -35,7 +35,7 @@ internal partial record ResolveContext(
|
||||||
uint SlotIndex,
|
uint SlotIndex,
|
||||||
EquipSlot Slot,
|
EquipSlot Slot,
|
||||||
CharacterArmor Equipment,
|
CharacterArmor Equipment,
|
||||||
WeaponType WeaponType)
|
SecondaryId SecondaryId)
|
||||||
{
|
{
|
||||||
private static readonly ByteString ShpkPrefix = ByteString.FromSpanUnsafe("shader/sm5/shpk"u8, true, true, true);
|
private static readonly ByteString ShpkPrefix = ByteString.FromSpanUnsafe("shader/sm5/shpk"u8, true, true, true);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
|
||||||
public int Count
|
public int Count
|
||||||
=> (Length - DataOffset) / EqdpEntrySize;
|
=> (Length - DataOffset) / EqdpEntrySize;
|
||||||
|
|
||||||
public EqdpEntry this[SetId id]
|
public EqdpEntry this[PrimaryId id]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|
@ -79,7 +79,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
|
||||||
MemoryUtility.MemSet(myDataPtr, 0, Length - (int)((byte*)myDataPtr - Data));
|
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)
|
foreach (var entry in entries)
|
||||||
this[entry] = GetDefault(entry);
|
this[entry] = GetDefault(entry);
|
||||||
|
|
@ -101,18 +101,18 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public EqdpEntry GetDefault(SetId setId)
|
public EqdpEntry GetDefault(PrimaryId primaryId)
|
||||||
=> GetDefault(Manager, Index, setId);
|
=> GetDefault(Manager, Index, primaryId);
|
||||||
|
|
||||||
public static EqdpEntry GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex idx, SetId setId)
|
public static EqdpEntry GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex idx, PrimaryId primaryId)
|
||||||
=> GetDefault((byte*)manager.CharacterUtility.DefaultResource(idx).Address, setId);
|
=> 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 blockSize = *(ushort*)(data + IdentifierSize);
|
||||||
var totalBlockCount = *(ushort*)(data + IdentifierSize + 2);
|
var totalBlockCount = *(ushort*)(data + IdentifierSize + 2);
|
||||||
|
|
||||||
var blockIdx = setId.Id / blockSize;
|
var blockIdx = primaryId.Id / blockSize;
|
||||||
if (blockIdx >= totalBlockCount)
|
if (blockIdx >= totalBlockCount)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -121,9 +121,9 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var blockData = (ushort*)(data + IdentifierSize + PreambleSize + totalBlockCount * 2 + block * 2);
|
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)
|
public static EqdpEntry GetDefault(MetaFileManager manager, GenderRace raceCode, bool accessory, PrimaryId primaryId)
|
||||||
=> GetDefault(manager, CharacterUtility.ReverseIndices[(int)CharacterUtilityData.EqdpIdx(raceCode, accessory)], setId);
|
=> GetDefault(manager, CharacterUtility.ReverseIndices[(int)CharacterUtilityData.EqdpIdx(raceCode, accessory)], primaryId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
|
||||||
public ulong ControlBlock
|
public ulong ControlBlock
|
||||||
=> *(ulong*)Data;
|
=> *(ulong*)Data;
|
||||||
|
|
||||||
protected ulong GetInternal(SetId idx)
|
protected ulong GetInternal(PrimaryId idx)
|
||||||
{
|
{
|
||||||
return idx.Id switch
|
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
|
idx = idx.Id switch
|
||||||
{
|
{
|
||||||
|
|
@ -81,13 +81,13 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
|
||||||
Reset();
|
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;
|
var data = (byte*)manager.CharacterUtility.DefaultResource(fileIndex).Address;
|
||||||
if (setId == 0)
|
if (primaryId == 0)
|
||||||
setId = 1;
|
primaryId = 1;
|
||||||
|
|
||||||
var blockIdx = setId.Id / BlockSize;
|
var blockIdx = primaryId.Id / BlockSize;
|
||||||
if (blockIdx >= NumBlocks)
|
if (blockIdx >= NumBlocks)
|
||||||
return def;
|
return def;
|
||||||
|
|
||||||
|
|
@ -97,7 +97,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
|
||||||
return def;
|
return def;
|
||||||
|
|
||||||
var count = BitOperations.PopCount(control & (blockBit - 1));
|
var count = BitOperations.PopCount(control & (blockBit - 1));
|
||||||
var idx = setId.Id % BlockSize;
|
var idx = primaryId.Id % BlockSize;
|
||||||
var ptr = (ulong*)data + BlockSize * count + idx;
|
var ptr = (ulong*)data + BlockSize * count + idx;
|
||||||
return *ptr;
|
return *ptr;
|
||||||
}
|
}
|
||||||
|
|
@ -112,15 +112,15 @@ public sealed class ExpandedEqpFile : ExpandedEqpGmpBase, IEnumerable<EqpEntry>
|
||||||
: base(manager, false)
|
: base(manager, false)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public EqpEntry this[SetId idx]
|
public EqpEntry this[PrimaryId idx]
|
||||||
{
|
{
|
||||||
get => (EqpEntry)GetInternal(idx);
|
get => (EqpEntry)GetInternal(idx);
|
||||||
set => SetInternal(idx, (ulong)value);
|
set => SetInternal(idx, (ulong)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static EqpEntry GetDefault(MetaFileManager manager, SetId setIdx)
|
public static EqpEntry GetDefault(MetaFileManager manager, PrimaryId primaryIdx)
|
||||||
=> (EqpEntry)GetDefaultInternal(manager, InternalIndex, setIdx, (ulong)Eqp.DefaultEntry);
|
=> (EqpEntry)GetDefaultInternal(manager, InternalIndex, primaryIdx, (ulong)Eqp.DefaultEntry);
|
||||||
|
|
||||||
protected override unsafe void SetEmptyBlock(int idx)
|
protected override unsafe void SetEmptyBlock(int idx)
|
||||||
{
|
{
|
||||||
|
|
@ -130,7 +130,7 @@ public sealed class ExpandedEqpFile : ExpandedEqpGmpBase, IEnumerable<EqpEntry>
|
||||||
*ptr = (ulong)Eqp.DefaultEntry;
|
*ptr = (ulong)Eqp.DefaultEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset(IEnumerable<SetId> entries)
|
public void Reset(IEnumerable<PrimaryId> entries)
|
||||||
{
|
{
|
||||||
foreach (var entry in entries)
|
foreach (var entry in entries)
|
||||||
this[entry] = GetDefault(Manager, entry);
|
this[entry] = GetDefault(Manager, entry);
|
||||||
|
|
@ -155,16 +155,16 @@ public sealed class ExpandedGmpFile : ExpandedEqpGmpBase, IEnumerable<GmpEntry>
|
||||||
: base(manager, true)
|
: base(manager, true)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public GmpEntry this[SetId idx]
|
public GmpEntry this[PrimaryId idx]
|
||||||
{
|
{
|
||||||
get => (GmpEntry)GetInternal(idx);
|
get => (GmpEntry)GetInternal(idx);
|
||||||
set => SetInternal(idx, (ulong)value);
|
set => SetInternal(idx, (ulong)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GmpEntry GetDefault(MetaFileManager manager, SetId setIdx)
|
public static GmpEntry GetDefault(MetaFileManager manager, PrimaryId primaryIdx)
|
||||||
=> (GmpEntry)GetDefaultInternal(manager, InternalIndex, setIdx, (ulong)GmpEntry.Default);
|
=> (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)
|
foreach (var entry in entries)
|
||||||
this[entry] = GetDefault(Manager, entry);
|
this[entry] = GetDefault(Manager, entry);
|
||||||
|
|
|
||||||
|
|
@ -167,21 +167,21 @@ public sealed unsafe class EstFile : MetaBaseFile
|
||||||
public ushort GetDefault(GenderRace genderRace, ushort setId)
|
public ushort GetDefault(GenderRace genderRace, ushort setId)
|
||||||
=> GetDefault(Manager, Index, genderRace, 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 data = (byte*)manager.CharacterUtility.DefaultResource(index).Address;
|
||||||
var count = *(int*)data;
|
var count = *(int*)data;
|
||||||
var span = new ReadOnlySpan<Info>(data + 4, count);
|
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)
|
if (!found)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return *(ushort*)(data + 4 + count * EntryDescSize + idx * EntrySize);
|
return *(ushort*)(data + 4 + count * EntryDescSize + idx * EntrySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ushort GetDefault(MetaFileManager manager, MetaIndex metaIndex, GenderRace genderRace, SetId setId)
|
public static ushort GetDefault(MetaFileManager manager, MetaIndex metaIndex, GenderRace genderRace, PrimaryId primaryId)
|
||||||
=> GetDefault(manager, CharacterUtility.ReverseIndices[(int)metaIndex], genderRace, setId);
|
=> GetDefault(manager, CharacterUtility.ReverseIndices[(int)metaIndex], genderRace, primaryId);
|
||||||
|
|
||||||
public static ushort GetDefault(MetaFileManager manager, EstManipulation.EstType estType, GenderRace genderRace, SetId setId)
|
public static ushort GetDefault(MetaFileManager manager, EstManipulation.EstType estType, GenderRace genderRace, PrimaryId primaryId)
|
||||||
=> GetDefault(manager, (MetaIndex)estType, genderRace, setId);
|
=> GetDefault(manager, (MetaIndex)estType, genderRace, primaryId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,13 @@ public readonly struct EqdpManipulation : IMetaManipulation<EqdpManipulation>
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
public ModelRace Race { get; private init; }
|
public ModelRace Race { get; private init; }
|
||||||
|
|
||||||
public SetId SetId { get; private init; }
|
public PrimaryId SetId { get; private init; }
|
||||||
|
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
public EquipSlot Slot { get; private init; }
|
public EquipSlot Slot { get; private init; }
|
||||||
|
|
||||||
[JsonConstructor]
|
[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;
|
Gender = gender;
|
||||||
Race = race;
|
Race = race;
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,13 @@ public readonly struct EqpManipulation : IMetaManipulation<EqpManipulation>
|
||||||
[JsonConverter(typeof(ForceNumericFlagEnumConverter))]
|
[JsonConverter(typeof(ForceNumericFlagEnumConverter))]
|
||||||
public EqpEntry Entry { get; private init; }
|
public EqpEntry Entry { get; private init; }
|
||||||
|
|
||||||
public SetId SetId { get; private init; }
|
public PrimaryId SetId { get; private init; }
|
||||||
|
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
public EquipSlot Slot { get; private init; }
|
public EquipSlot Slot { get; private init; }
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public EqpManipulation(EqpEntry entry, EquipSlot slot, SetId setId)
|
public EqpManipulation(EqpEntry entry, EquipSlot slot, PrimaryId setId)
|
||||||
{
|
{
|
||||||
Slot = slot;
|
Slot = slot;
|
||||||
SetId = setId;
|
SetId = setId;
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,14 @@ public readonly struct EstManipulation : IMetaManipulation<EstManipulation>
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
public ModelRace Race { get; private init; }
|
public ModelRace Race { get; private init; }
|
||||||
|
|
||||||
public SetId SetId { get; private init; }
|
public PrimaryId SetId { get; private init; }
|
||||||
|
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
public EstType Slot { get; private init; }
|
public EstType Slot { get; private init; }
|
||||||
|
|
||||||
|
|
||||||
[JsonConstructor]
|
[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;
|
Entry = entry;
|
||||||
Gender = gender;
|
Gender = gender;
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@ namespace Penumbra.Meta.Manipulations;
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
public readonly struct GmpManipulation : IMetaManipulation<GmpManipulation>
|
public readonly struct GmpManipulation : IMetaManipulation<GmpManipulation>
|
||||||
{
|
{
|
||||||
public GmpEntry Entry { get; private init; }
|
public GmpEntry Entry { get; private init; }
|
||||||
public SetId SetId { get; private init; }
|
public PrimaryId SetId { get; private init; }
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public GmpManipulation(GmpEntry entry, SetId setId)
|
public GmpManipulation(GmpEntry entry, PrimaryId setId)
|
||||||
{
|
{
|
||||||
Entry = entry;
|
Entry = entry;
|
||||||
SetId = setId;
|
SetId = setId;
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ namespace Penumbra.Meta.Manipulations;
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
public readonly struct ImcManipulation : IMetaManipulation<ImcManipulation>
|
public readonly struct ImcManipulation : IMetaManipulation<ImcManipulation>
|
||||||
{
|
{
|
||||||
public ImcEntry Entry { get; private init; }
|
public ImcEntry Entry { get; private init; }
|
||||||
public SetId PrimaryId { get; private init; }
|
public PrimaryId PrimaryId { get; private init; }
|
||||||
public SetId SecondaryId { get; private init; }
|
public PrimaryId SecondaryId { get; private init; }
|
||||||
public Variant Variant { get; private init; }
|
public Variant Variant { get; private init; }
|
||||||
|
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
public ObjectType ObjectType { get; private init; }
|
public ObjectType ObjectType { get; private init; }
|
||||||
|
|
@ -26,7 +26,7 @@ public readonly struct ImcManipulation : IMetaManipulation<ImcManipulation>
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
public BodySlot BodySlot { get; private init; }
|
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;
|
Entry = entry;
|
||||||
PrimaryId = primaryId;
|
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,
|
// 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.
|
// and clamp the variant to 255.
|
||||||
[JsonConstructor]
|
[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)
|
EquipSlot equipSlot, ImcEntry entry)
|
||||||
{
|
{
|
||||||
Entry = entry;
|
Entry = entry;
|
||||||
|
|
|
||||||
|
|
@ -269,5 +269,23 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
||||||
Type.Gmp => $"{Gmp.Entry.Value}",
|
Type.Gmp => $"{Gmp.Entry.Value}",
|
||||||
Type.Rsp => $"{Rsp.Entry}",
|
Type.Rsp => $"{Rsp.Entry}",
|
||||||
_ => 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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
/// 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,
|
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)
|
if (idFrom.Id > byte.MaxValue)
|
||||||
throw new Exception($"The Customization ID {idFrom} is too large for {slot}.");
|
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,
|
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)
|
ref string fileName, ref bool dataWasChanged)
|
||||||
{
|
{
|
||||||
variant = slot is BodySlot.Face or BodySlot.Ear ? byte.MaxValue : variant;
|
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,
|
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)
|
ref bool dataWasChanged)
|
||||||
{
|
{
|
||||||
var path = texture.Path;
|
var path = texture.Path;
|
||||||
|
|
|
||||||
|
|
@ -185,13 +185,13 @@ public static class EquipmentSwap
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MetaSwap? CreateEqdp(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
public static MetaSwap? CreateEqdp(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
||||||
Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, GenderRace gr, SetId idFrom,
|
Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, GenderRace gr, PrimaryId idFrom,
|
||||||
SetId idTo, byte mtrlTo)
|
PrimaryId idTo, byte mtrlTo)
|
||||||
=> CreateEqdp(manager, redirections, manips, slot, slot, gr, idFrom, idTo, mtrlTo);
|
=> CreateEqdp(manager, redirections, manips, slot, slot, gr, idFrom, idTo, mtrlTo);
|
||||||
|
|
||||||
public static MetaSwap? CreateEqdp(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
public static MetaSwap? CreateEqdp(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
||||||
Func<MetaManipulation, MetaManipulation> manips, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, SetId idFrom,
|
Func<MetaManipulation, MetaManipulation> manips, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, PrimaryId idFrom,
|
||||||
SetId idTo, byte mtrlTo)
|
PrimaryId idTo, byte mtrlTo)
|
||||||
{
|
{
|
||||||
var (gender, race) = gr.Split();
|
var (gender, race) = gr.Split();
|
||||||
var eqdpFrom = new EqdpManipulation(ExpandedEqdpFile.GetDefault(manager, gr, slotFrom.IsAccessory(), idFrom), slotFrom, gender,
|
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,
|
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);
|
=> CreateMdl(manager, redirections, slot, slot, gr, idFrom, idTo, mtrlTo);
|
||||||
|
|
||||||
public static FileSwap CreateMdl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slotFrom, EquipSlot slotTo,
|
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()
|
var mdlPathFrom = slotFrom.IsAccessory()
|
||||||
? GamePaths.Accessory.Mdl.Path(idFrom, gr, slotFrom)
|
? GamePaths.Accessory.Mdl.Path(idFrom, gr, slotFrom)
|
||||||
|
|
@ -236,7 +236,7 @@ public static class EquipmentSwap
|
||||||
return mdl;
|
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();
|
slot = i.Type.ToSlot();
|
||||||
if (!slot.IsEquipmentPiece())
|
if (!slot.IsEquipmentPiece())
|
||||||
|
|
@ -247,7 +247,7 @@ public static class EquipmentSwap
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (ImcFile, Variant[], EquipItem[]) GetVariants(MetaFileManager manager, ObjectIdentification identifier, EquipSlot slotFrom,
|
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 entry = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, default);
|
||||||
var imc = new ImcFile(manager, entry);
|
var imc = new ImcFile(manager, entry);
|
||||||
|
|
@ -270,8 +270,8 @@ public static class EquipmentSwap
|
||||||
return (imc, variants, items);
|
return (imc, variants, items);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MetaSwap? CreateGmp(MetaFileManager manager, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, SetId idFrom,
|
public static MetaSwap? CreateGmp(MetaFileManager manager, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, PrimaryId idFrom,
|
||||||
SetId idTo)
|
PrimaryId idTo)
|
||||||
{
|
{
|
||||||
if (slot is not EquipSlot.Head)
|
if (slot is not EquipSlot.Head)
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -283,12 +283,12 @@ public static class EquipmentSwap
|
||||||
|
|
||||||
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
||||||
Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot,
|
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);
|
=> CreateImc(manager, redirections, manips, slot, slot, idFrom, idTo, variantFrom, variantTo, imcFileFrom, imcFileTo);
|
||||||
|
|
||||||
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
||||||
Func<MetaManipulation, MetaManipulation> manips,
|
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)
|
Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo)
|
||||||
{
|
{
|
||||||
var entryFrom = imcFileFrom.GetEntry(ImcFile.PartIndex(slotFrom), variantFrom);
|
var entryFrom = imcFileFrom.GetEntry(ImcFile.PartIndex(slotFrom), variantFrom);
|
||||||
|
|
@ -322,7 +322,7 @@ public static class EquipmentSwap
|
||||||
|
|
||||||
|
|
||||||
// Example: Abyssos Helm / Body
|
// 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)
|
if (vfxId == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -340,8 +340,8 @@ public static class EquipmentSwap
|
||||||
return avfx;
|
return avfx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MetaSwap? CreateEqp(MetaFileManager manager, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, SetId idFrom,
|
public static MetaSwap? CreateEqp(MetaFileManager manager, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, PrimaryId idFrom,
|
||||||
SetId idTo)
|
PrimaryId idTo)
|
||||||
{
|
{
|
||||||
if (slot.IsAccessory())
|
if (slot.IsAccessory())
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -353,13 +353,13 @@ public static class EquipmentSwap
|
||||||
return new MetaSwap(manips, eqpFrom, eqpTo);
|
return new MetaSwap(manips, eqpFrom, eqpTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FileSwap? CreateMtrl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slot, SetId idFrom,
|
public static FileSwap? CreateMtrl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slot, PrimaryId idFrom,
|
||||||
SetId idTo, byte variantTo, ref string fileName,
|
PrimaryId idTo, byte variantTo, ref string fileName,
|
||||||
ref bool dataWasChanged)
|
ref bool dataWasChanged)
|
||||||
=> CreateMtrl(manager, redirections, slot, slot, idFrom, idTo, variantTo, ref fileName, ref 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,
|
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)
|
ref bool dataWasChanged)
|
||||||
{
|
{
|
||||||
var prefix = slotTo.IsAccessory() ? 'a' : 'e';
|
var prefix = slotTo.IsAccessory() ? 'a' : 'e';
|
||||||
|
|
@ -397,13 +397,13 @@ public static class EquipmentSwap
|
||||||
return mtrl;
|
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)
|
ref MtrlFile.Texture texture, ref bool dataWasChanged)
|
||||||
=> CreateTex(manager, redirections, prefix, EquipSlot.Unknown, EquipSlot.Unknown, idFrom, idTo, ref texture, ref 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,
|
public static FileSwap CreateTex(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, char prefix, EquipSlot slotFrom,
|
||||||
EquipSlot slotTo, SetId idFrom,
|
EquipSlot slotTo, PrimaryId idFrom,
|
||||||
SetId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged)
|
PrimaryId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged)
|
||||||
{
|
{
|
||||||
var path = texture.Path;
|
var path = texture.Path;
|
||||||
var addedDashes = false;
|
var addedDashes = false;
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ public static class ItemSwap
|
||||||
/// <remarks> metaChanges is not manipulated, but IReadOnlySet does not support TryGetValue. </remarks>
|
/// <remarks> metaChanges is not manipulated, but IReadOnlySet does not support TryGetValue. </remarks>
|
||||||
public static MetaSwap? CreateEst(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
public static MetaSwap? CreateEst(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
||||||
Func<MetaManipulation, MetaManipulation> manips, EstManipulation.EstType type,
|
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)
|
if (type == 0)
|
||||||
return null;
|
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
|
=> condition
|
||||||
? Regex.Replace(path, $"{idType}\\d{{4}}", $"{idType}{id.Id:D4}")
|
? Regex.Replace(path, $"{idType}\\d{{4}}", $"{idType}{id.Id:D4}")
|
||||||
: path;
|
: path;
|
||||||
|
|
@ -203,10 +203,10 @@ public static class ItemSwap
|
||||||
public static string ReplaceAnyRace(string path, GenderRace to, bool condition = true)
|
public static string ReplaceAnyRace(string path, GenderRace to, bool condition = true)
|
||||||
=> ReplaceAnyId(path, 'c', (ushort)to, condition);
|
=> 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);
|
=> 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
|
=> condition
|
||||||
? path.Replace($"{type}{idFrom.Id:D4}", $"{type}{idTo.Id:D4}")
|
? path.Replace($"{type}{idFrom.Id:D4}", $"{type}{idTo.Id:D4}")
|
||||||
: path;
|
: path;
|
||||||
|
|
@ -219,7 +219,7 @@ public static class ItemSwap
|
||||||
public static string ReplaceRace(string path, GenderRace from, GenderRace to, bool condition = true)
|
public static string ReplaceRace(string path, GenderRace from, GenderRace to, bool condition = true)
|
||||||
=> ReplaceId(path, 'c', (ushort)from, (ushort)to, condition);
|
=> 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);
|
=> ReplaceId(path, slot.ToAbbreviation(), idFrom, idTo, condition);
|
||||||
|
|
||||||
public static string AddSuffix(string path, string ext, string suffix, bool condition = true)
|
public static string AddSuffix(string path, string ext, string suffix, bool condition = true)
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ public class ItemSwapContainer
|
||||||
return ret;
|
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)
|
ModCollection? collection = null)
|
||||||
{
|
{
|
||||||
var pathResolver = PathResolver(collection);
|
var pathResolver = PathResolver(collection);
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ public class ModCacheManager : IDisposable
|
||||||
{
|
{
|
||||||
case ModPathChangeType.Added:
|
case ModPathChangeType.Added:
|
||||||
case ModPathChangeType.Reloaded:
|
case ModPathChangeType.Reloaded:
|
||||||
Refresh(mod);
|
RefreshWithChangedItems(mod);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -151,10 +151,28 @@ public class ModCacheManager : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnModDiscoveryFinished()
|
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()
|
private void OnIdentifierCreation()
|
||||||
=> Parallel.ForEach(_modManager, UpdateChangedItems);
|
{
|
||||||
|
if (_updatingItems)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_updatingItems = true;
|
||||||
|
Parallel.ForEach(_modManager, UpdateChangedItems);
|
||||||
|
_updatingItems = false;
|
||||||
|
}
|
||||||
|
|
||||||
private static void UpdateFileCount(Mod mod)
|
private static void UpdateFileCount(Mod mod)
|
||||||
=> mod.TotalFileCount = mod.AllSubMods.Sum(s => s.Files.Count);
|
=> mod.TotalFileCount = mod.AllSubMods.Sum(s => s.Files.Count);
|
||||||
|
|
@ -173,15 +191,8 @@ public class ModCacheManager : IDisposable
|
||||||
|
|
||||||
private void UpdateChangedItems(Mod mod)
|
private void UpdateChangedItems(Mod mod)
|
||||||
{
|
{
|
||||||
if (_updatingItems)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_updatingItems = true;
|
|
||||||
var changedItems = (SortedList<string, object?>)mod.ChangedItems;
|
var changedItems = (SortedList<string, object?>)mod.ChangedItems;
|
||||||
changedItems.Clear();
|
changedItems.Clear();
|
||||||
if (!_identifier.Awaiter.IsCompletedSuccessfully)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var gamePath in mod.AllSubMods.SelectMany(m => m.Files.Keys.Concat(m.FileSwaps.Keys)))
|
foreach (var gamePath in mod.AllSubMods.SelectMany(m => m.Files.Keys.Concat(m.FileSwaps.Keys)))
|
||||||
_identifier.Identify(changedItems, gamePath.ToString());
|
_identifier.Identify(changedItems, gamePath.ToString());
|
||||||
|
|
||||||
|
|
@ -189,7 +200,6 @@ public class ModCacheManager : IDisposable
|
||||||
ComputeChangedItems(_identifier, changedItems, manip);
|
ComputeChangedItems(_identifier, changedItems, manip);
|
||||||
|
|
||||||
mod.LowerChangedItemsString = string.Join("\0", mod.ChangedItems.Keys.Select(k => k.ToLowerInvariant()));
|
mod.LowerChangedItemsString = string.Join("\0", mod.ChangedItems.Keys.Select(k => k.ToLowerInvariant()));
|
||||||
_updatingItems = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdateCounts(Mod mod)
|
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);
|
UpdateTags(mod);
|
||||||
UpdateCounts(mod);
|
UpdateCounts(mod);
|
||||||
UpdateChangedItems(mod);
|
UpdateChangedItems(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RefreshWithoutChangedItems(Mod mod)
|
||||||
|
{
|
||||||
|
UpdateTags(mod);
|
||||||
|
UpdateCounts(mod);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,9 @@ public sealed class SubMod : ISubMod
|
||||||
public bool IsDefault
|
public bool IsDefault
|
||||||
=> GroupIdx < 0;
|
=> GroupIdx < 0;
|
||||||
|
|
||||||
public Dictionary<Utf8GamePath, FullPath> FileData = new();
|
public Dictionary<Utf8GamePath, FullPath> FileData = [];
|
||||||
public Dictionary<Utf8GamePath, FullPath> FileSwapData = new();
|
public Dictionary<Utf8GamePath, FullPath> FileSwapData = [];
|
||||||
public HashSet<MetaManipulation> ManipulationData = new();
|
public HashSet<MetaManipulation> ManipulationData = [];
|
||||||
|
|
||||||
public SubMod(IMod parentMod)
|
public SubMod(IMod parentMod)
|
||||||
=> ParentMod = parentMod;
|
=> ParentMod = parentMod;
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,9 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="GetGitHash" BeforeTargets="GetAssemblyVersion" Returns="InformationalVersion">
|
<Target Name="GetGitHash" BeforeTargets="GetAssemblyVersion" Returns="InformationalVersion">
|
||||||
<Exec Command="git rev-parse --short HEAD" ConsoleToMSBuild="true" StandardOutputImportance="low">
|
<Exec Command="git rev-parse --short HEAD" ConsoleToMSBuild="true" StandardOutputImportance="low" ContinueOnError="true">
|
||||||
<Output TaskParameter="ConsoleOutput" PropertyName="GitCommitHash" />
|
<Output TaskParameter="ExitCode" PropertyName="GitCommitHashSuccess"/>
|
||||||
|
<Output TaskParameter="ConsoleOutput" PropertyName="GitCommitHash" Condition="$(GitCommitHashSuccess) == 0"/>
|
||||||
</Exec>
|
</Exec>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -210,26 +210,26 @@ public class ItemSwapTab : IDisposable, ITab
|
||||||
break;
|
break;
|
||||||
case SwapType.Hair when _targetId > 0 && _sourceId > 0:
|
case SwapType.Hair when _targetId > 0 && _sourceId > 0:
|
||||||
_swapData.LoadCustomization(_metaFileManager, BodySlot.Hair, Names.CombinedRace(_currentGender, _currentRace),
|
_swapData.LoadCustomization(_metaFileManager, BodySlot.Hair, Names.CombinedRace(_currentGender, _currentRace),
|
||||||
(SetId)_sourceId,
|
(PrimaryId)_sourceId,
|
||||||
(SetId)_targetId,
|
(PrimaryId)_targetId,
|
||||||
_useCurrentCollection ? _collectionManager.Active.Current : null);
|
_useCurrentCollection ? _collectionManager.Active.Current : null);
|
||||||
break;
|
break;
|
||||||
case SwapType.Face when _targetId > 0 && _sourceId > 0:
|
case SwapType.Face when _targetId > 0 && _sourceId > 0:
|
||||||
_swapData.LoadCustomization(_metaFileManager, BodySlot.Face, Names.CombinedRace(_currentGender, _currentRace),
|
_swapData.LoadCustomization(_metaFileManager, BodySlot.Face, Names.CombinedRace(_currentGender, _currentRace),
|
||||||
(SetId)_sourceId,
|
(PrimaryId)_sourceId,
|
||||||
(SetId)_targetId,
|
(PrimaryId)_targetId,
|
||||||
_useCurrentCollection ? _collectionManager.Active.Current : null);
|
_useCurrentCollection ? _collectionManager.Active.Current : null);
|
||||||
break;
|
break;
|
||||||
case SwapType.Ears when _targetId > 0 && _sourceId > 0:
|
case SwapType.Ears when _targetId > 0 && _sourceId > 0:
|
||||||
_swapData.LoadCustomization(_metaFileManager, BodySlot.Ear, Names.CombinedRace(_currentGender, ModelRace.Viera),
|
_swapData.LoadCustomization(_metaFileManager, BodySlot.Ear, Names.CombinedRace(_currentGender, ModelRace.Viera),
|
||||||
(SetId)_sourceId,
|
(PrimaryId)_sourceId,
|
||||||
(SetId)_targetId,
|
(PrimaryId)_targetId,
|
||||||
_useCurrentCollection ? _collectionManager.Active.Current : null);
|
_useCurrentCollection ? _collectionManager.Active.Current : null);
|
||||||
break;
|
break;
|
||||||
case SwapType.Tail when _targetId > 0 && _sourceId > 0:
|
case SwapType.Tail when _targetId > 0 && _sourceId > 0:
|
||||||
_swapData.LoadCustomization(_metaFileManager, BodySlot.Tail, Names.CombinedRace(_currentGender, _currentRace),
|
_swapData.LoadCustomization(_metaFileManager, BodySlot.Tail, Names.CombinedRace(_currentGender, _currentRace),
|
||||||
(SetId)_sourceId,
|
(PrimaryId)_sourceId,
|
||||||
(SetId)_targetId,
|
(PrimaryId)_targetId,
|
||||||
_useCurrentCollection ? _collectionManager.Active.Current : null);
|
_useCurrentCollection ? _collectionManager.Active.Current : null);
|
||||||
break;
|
break;
|
||||||
case SwapType.Weapon: break;
|
case SwapType.Weapon: break;
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
|
||||||
ImRaii.TreeNode(leaf.Value.Name, flags).Dispose();
|
ImRaii.TreeNode(leaf.Value.Name, flags).Dispose();
|
||||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Middle))
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Middle))
|
||||||
{
|
{
|
||||||
|
_modManager.SetKnown(leaf.Value);
|
||||||
var (setting, collection) = _collectionManager.Active.Current[leaf.Value.Index];
|
var (setting, collection) = _collectionManager.Active.Current[leaf.Value.Index];
|
||||||
if (_config.DeleteModModifier.ForcedModifier(new DoubleModifier(ModifierHotkey.Control, ModifierHotkey.Shift)).IsActive())
|
if (_config.DeleteModModifier.ForcedModifier(new DoubleModifier(ModifierHotkey.Control, ModifierHotkey.Shift)).IsActive())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue