mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Use strongly typed ids in most places.
This commit is contained in:
parent
dccd347432
commit
18b6b87e6b
26 changed files with 192 additions and 193 deletions
|
|
@ -1 +1 @@
|
||||||
Subproject commit 98bd4e9946ded20cb5d54182883e73f344fe2d26
|
Subproject commit 263bfb49c998700197a18ad99fa1daadc8736f5d
|
||||||
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Filesystem;
|
using OtterGui.Filesystem;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Interop.Services;
|
using Penumbra.Interop.Services;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
using Penumbra.Meta;
|
using Penumbra.Meta;
|
||||||
|
|
@ -48,7 +49,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 => (int)m.SetId));
|
file.Reset(_eqdpManipulations.Where(m => m.FileIndex() == relevant).Select(m => (SetId) m.SetId));
|
||||||
}
|
}
|
||||||
|
|
||||||
_eqdpManipulations.Clear();
|
_eqdpManipulations.Clear();
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ public struct EqpCache : IDisposable
|
||||||
if (_eqpFile == null)
|
if (_eqpFile == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_eqpFile.Reset(_eqpManipulations.Select(m => (int)m.SetId));
|
_eqpFile.Reset(_eqpManipulations.Select(m => m.SetId));
|
||||||
_eqpManipulations.Clear();
|
_eqpManipulations.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ public struct GmpCache : IDisposable
|
||||||
if( _gmpFile == null )
|
if( _gmpFile == null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_gmpFile.Reset( _gmpManipulations.Select( m => ( int )m.SetId ) );
|
_gmpFile.Reset( _gmpManipulations.Select( m => m.SetId ) );
|
||||||
_gmpManipulations.Clear();
|
_gmpManipulations.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ public readonly struct ImcCache : IDisposable
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var def = ImcFile.GetDefault(manager, file.Path, m.EquipSlot, m.Variant, out _);
|
var def = ImcFile.GetDefault(manager, file.Path, m.EquipSlot, m.Variant.Id, out _);
|
||||||
var manip = m.Copy(def);
|
var manip = m.Copy(def);
|
||||||
if (!manip.Apply(file))
|
if (!manip.Apply(file))
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa
|
||||||
if (!_config.UseOwnerNameForCharacterCollection)
|
if (!_config.UseOwnerNameForCharacterCollection)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
identifier = _actorService.AwaitedService.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName, identifier.HomeWorld,
|
identifier = _actorService.AwaitedService.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName, identifier.HomeWorld.Id,
|
||||||
ObjectKind.None, uint.MaxValue);
|
ObjectKind.None, uint.MaxValue);
|
||||||
return CheckWorlds(identifier, out collection);
|
return CheckWorlds(identifier, out collection);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ public sealed partial class IndividualCollections
|
||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
return table.Where(kvp => kvp.Value == name)
|
return table.Where(kvp => kvp.Value == name)
|
||||||
.Select(kvp => manager.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, identifier.HomeWorld, identifier.Kind,
|
.Select(kvp => manager.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, identifier.HomeWorld.Id, identifier.Kind,
|
||||||
kvp.Key)).ToArray();
|
kvp.Key)).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ public partial class TexToolsMeta
|
||||||
var partIdx = ImcFile.PartIndex(manip.EquipSlot); // Gets turned to unknown for things without equip, and unknown turns to 0.
|
var partIdx = ImcFile.PartIndex(manip.EquipSlot); // Gets turned to unknown for things without equip, and unknown turns to 0.
|
||||||
foreach (var value in values)
|
foreach (var value in values)
|
||||||
{
|
{
|
||||||
if (_keepDefault || !value.Equals(def.GetEntry(partIdx, i)))
|
if (_keepDefault || !value.Equals(def.GetEntry(partIdx, (Variant) i)))
|
||||||
{
|
{
|
||||||
var imc = new ImcManipulation(manip.ObjectType, manip.BodySlot, manip.PrimaryId, manip.SecondaryId, i, manip.EquipSlot,
|
var imc = new ImcManipulation(manip.ObjectType, manip.BodySlot, manip.PrimaryId, manip.SecondaryId, i, manip.EquipSlot,
|
||||||
value);
|
value);
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ public partial class TexToolsMeta
|
||||||
|
|
||||||
for( var i = 0; i <= baseFile.Count; ++i )
|
for( var i = 0; i <= baseFile.Count; ++i )
|
||||||
{
|
{
|
||||||
var entry = baseFile.GetEntry( partIdx, i );
|
var entry = baseFile.GetEntry( partIdx, (Variant)i );
|
||||||
b.Write( entry.MaterialId );
|
b.Write( entry.MaterialId );
|
||||||
b.Write( entry.DecalId );
|
b.Write( entry.DecalId );
|
||||||
b.Write( entry.AttributeAndSound );
|
b.Write( entry.AttributeAndSound );
|
||||||
|
|
@ -184,7 +184,7 @@ public partial class TexToolsMeta
|
||||||
foreach( var manip in manips )
|
foreach( var manip in manips )
|
||||||
{
|
{
|
||||||
b.Write( ( ushort )Names.CombinedRace( manip.Est.Gender, manip.Est.Race ) );
|
b.Write( ( ushort )Names.CombinedRace( manip.Est.Gender, manip.Est.Race ) );
|
||||||
b.Write( manip.Est.SetId );
|
b.Write( manip.Est.SetId.Id );
|
||||||
b.Write( manip.Est.Entry );
|
b.Write( manip.Est.Entry );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ public unsafe class CollectionResolver
|
||||||
if (identifier.Type != IdentifierType.Owned || !_config.UseOwnerNameForCharacterCollection || owner == null)
|
if (identifier.Type != IdentifierType.Owned || !_config.UseOwnerNameForCharacterCollection || owner == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var id = _actors.AwaitedService.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName, identifier.HomeWorld,
|
var id = _actors.AwaitedService.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName, identifier.HomeWorld.Id,
|
||||||
ObjectKind.None,
|
ObjectKind.None,
|
||||||
uint.MaxValue);
|
uint.MaxValue);
|
||||||
return CheckYourself(id, owner)
|
return CheckYourself(id, owner)
|
||||||
|
|
|
||||||
|
|
@ -225,10 +225,10 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
|
||||||
{
|
{
|
||||||
"chara" => SafeGet(path, 1) switch
|
"chara" => SafeGet(path, 1) switch
|
||||||
{
|
{
|
||||||
"accessory" => IsMatchEquipment(path[2..], $"a{Equipment.Set.Value:D4}"),
|
"accessory" => IsMatchEquipment(path[2..], $"a{Equipment.Set.Id:D4}"),
|
||||||
"equipment" => IsMatchEquipment(path[2..], $"e{Equipment.Set.Value:D4}"),
|
"equipment" => IsMatchEquipment(path[2..], $"e{Equipment.Set.Id:D4}"),
|
||||||
"monster" => SafeGet(path, 2) == $"m{Skeleton:D4}",
|
"monster" => SafeGet(path, 2) == $"m{Skeleton:D4}",
|
||||||
"weapon" => IsMatchEquipment(path[2..], $"w{Equipment.Set.Value:D4}"),
|
"weapon" => IsMatchEquipment(path[2..], $"w{Equipment.Set.Id:D4}"),
|
||||||
_ => null,
|
_ => null,
|
||||||
},
|
},
|
||||||
_ => null,
|
_ => null,
|
||||||
|
|
@ -238,7 +238,7 @@ internal record class ResolveContext(Configuration Config, IObjectIdentifier Ide
|
||||||
=> SafeGet(path, 0) == equipmentDir
|
=> SafeGet(path, 0) == equipmentDir
|
||||||
? SafeGet(path, 1) switch
|
? SafeGet(path, 1) switch
|
||||||
{
|
{
|
||||||
"material" => SafeGet(path, 2) == $"v{Equipment.Variant:D4}",
|
"material" => SafeGet(path, 2) == $"v{Equipment.Variant.Id:D4}",
|
||||||
_ => null,
|
_ => null,
|
||||||
}
|
}
|
||||||
: false;
|
: false;
|
||||||
|
|
|
||||||
|
|
@ -40,21 +40,21 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
|
||||||
public int Count
|
public int Count
|
||||||
=> (Length - DataOffset) / EqdpEntrySize;
|
=> (Length - DataOffset) / EqdpEntrySize;
|
||||||
|
|
||||||
public EqdpEntry this[int idx]
|
public EqdpEntry this[SetId id]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (idx >= Count || idx < 0)
|
if (id.Id >= Count)
|
||||||
throw new IndexOutOfRangeException();
|
throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
return (EqdpEntry)(*(ushort*)(Data + DataOffset + EqdpEntrySize * idx));
|
return (EqdpEntry)(*(ushort*)(Data + DataOffset + EqdpEntrySize * id.Id));
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (idx >= Count || idx < 0)
|
if (id.Id >= Count)
|
||||||
throw new IndexOutOfRangeException();
|
throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
*(ushort*)(Data + DataOffset + EqdpEntrySize * idx) = (ushort)value;
|
*(ushort*)(Data + DataOffset + EqdpEntrySize * id.Id) = (ushort)value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,7 +81,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<int> entries)
|
public void Reset(IEnumerable<SetId> entries)
|
||||||
{
|
{
|
||||||
foreach (var entry in entries)
|
foreach (var entry in entries)
|
||||||
this[entry] = GetDefault(entry);
|
this[entry] = GetDefault(entry);
|
||||||
|
|
@ -103,18 +103,18 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public EqdpEntry GetDefault(int setIdx)
|
public EqdpEntry GetDefault(SetId setId)
|
||||||
=> GetDefault(Manager, Index, setIdx);
|
=> GetDefault(Manager, Index, setId);
|
||||||
|
|
||||||
public static EqdpEntry GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex idx, int setIdx)
|
public static EqdpEntry GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex idx, SetId setId)
|
||||||
=> GetDefault((byte*)manager.CharacterUtility.DefaultResource(idx).Address, setIdx);
|
=> GetDefault((byte*)manager.CharacterUtility.DefaultResource(idx).Address, setId);
|
||||||
|
|
||||||
public static EqdpEntry GetDefault(byte* data, int setIdx)
|
public static EqdpEntry GetDefault(byte* data, SetId setId)
|
||||||
{
|
{
|
||||||
var blockSize = *(ushort*)(data + IdentifierSize);
|
var blockSize = *(ushort*)(data + IdentifierSize);
|
||||||
var totalBlockCount = *(ushort*)(data + IdentifierSize + 2);
|
var totalBlockCount = *(ushort*)(data + IdentifierSize + 2);
|
||||||
|
|
||||||
var blockIdx = setIdx / blockSize;
|
var blockIdx = setId.Id / blockSize;
|
||||||
if (blockIdx >= totalBlockCount)
|
if (blockIdx >= totalBlockCount)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -123,9 +123,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 + setIdx % blockSize));
|
return (EqdpEntry)(*(blockData + setId.Id % blockSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EqdpEntry GetDefault(MetaFileManager manager, GenderRace raceCode, bool accessory, int setIdx)
|
public static EqdpEntry GetDefault(MetaFileManager manager, GenderRace raceCode, bool accessory, SetId setId)
|
||||||
=> GetDefault(manager, CharacterUtility.ReverseIndices[(int)CharacterUtilityData.EqdpIdx(raceCode, accessory)], setIdx);
|
=> GetDefault(manager, CharacterUtility.ReverseIndices[(int)CharacterUtilityData.EqdpIdx(raceCode, accessory)], setId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,26 +28,26 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
|
||||||
public ulong ControlBlock
|
public ulong ControlBlock
|
||||||
=> *(ulong*)Data;
|
=> *(ulong*)Data;
|
||||||
|
|
||||||
protected ulong GetInternal(int idx)
|
protected ulong GetInternal(SetId idx)
|
||||||
{
|
{
|
||||||
return idx switch
|
return idx.Id switch
|
||||||
{
|
{
|
||||||
>= Count => throw new IndexOutOfRangeException(),
|
>= Count => throw new IndexOutOfRangeException(),
|
||||||
<= 1 => *((ulong*)Data + 1),
|
<= 1 => *((ulong*)Data + 1),
|
||||||
_ => *((ulong*)Data + idx),
|
_ => *((ulong*)Data + idx.Id),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SetInternal(int idx, ulong value)
|
protected void SetInternal(SetId idx, ulong value)
|
||||||
{
|
{
|
||||||
idx = idx switch
|
idx = idx.Id switch
|
||||||
{
|
{
|
||||||
>= Count => throw new IndexOutOfRangeException(),
|
>= Count => throw new IndexOutOfRangeException(),
|
||||||
<= 0 => 1,
|
<= 0 => 1,
|
||||||
_ => idx,
|
_ => idx,
|
||||||
};
|
};
|
||||||
|
|
||||||
*((ulong*)Data + idx) = value;
|
*((ulong*)Data + idx.Id) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void SetEmptyBlock(int idx)
|
protected virtual void SetEmptyBlock(int idx)
|
||||||
|
|
@ -85,13 +85,13 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static ulong GetDefaultInternal(MetaFileManager manager, CharacterUtility.InternalIndex fileIndex, int setIdx, ulong def)
|
protected static ulong GetDefaultInternal(MetaFileManager manager, CharacterUtility.InternalIndex fileIndex, SetId setId, ulong def)
|
||||||
{
|
{
|
||||||
var data = (byte*)manager.CharacterUtility.DefaultResource(fileIndex).Address;
|
var data = (byte*)manager.CharacterUtility.DefaultResource(fileIndex).Address;
|
||||||
if (setIdx == 0)
|
if (setId == 0)
|
||||||
setIdx = 1;
|
setId = 1;
|
||||||
|
|
||||||
var blockIdx = setIdx / BlockSize;
|
var blockIdx = setId.Id / BlockSize;
|
||||||
if (blockIdx >= NumBlocks)
|
if (blockIdx >= NumBlocks)
|
||||||
return def;
|
return def;
|
||||||
|
|
||||||
|
|
@ -101,7 +101,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 = setIdx % BlockSize;
|
var idx = setId.Id % BlockSize;
|
||||||
var ptr = (ulong*)data + BlockSize * count + idx;
|
var ptr = (ulong*)data + BlockSize * count + idx;
|
||||||
return *ptr;
|
return *ptr;
|
||||||
}
|
}
|
||||||
|
|
@ -116,14 +116,14 @@ public sealed class ExpandedEqpFile : ExpandedEqpGmpBase, IEnumerable<EqpEntry>
|
||||||
: base(manager, false)
|
: base(manager, false)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public EqpEntry this[int idx]
|
public EqpEntry this[SetId 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, int setIdx)
|
public static EqpEntry GetDefault(MetaFileManager manager, SetId setIdx)
|
||||||
=> (EqpEntry)GetDefaultInternal(manager, InternalIndex, setIdx, (ulong)Eqp.DefaultEntry);
|
=> (EqpEntry)GetDefaultInternal(manager, InternalIndex, setIdx, (ulong)Eqp.DefaultEntry);
|
||||||
|
|
||||||
protected override unsafe void SetEmptyBlock(int idx)
|
protected override unsafe void SetEmptyBlock(int idx)
|
||||||
|
|
@ -134,7 +134,7 @@ public sealed class ExpandedEqpFile : ExpandedEqpGmpBase, IEnumerable<EqpEntry>
|
||||||
*ptr = (ulong)Eqp.DefaultEntry;
|
*ptr = (ulong)Eqp.DefaultEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset(IEnumerable<int> entries)
|
public void Reset(IEnumerable<SetId> entries)
|
||||||
{
|
{
|
||||||
foreach (var entry in entries)
|
foreach (var entry in entries)
|
||||||
this[entry] = GetDefault(Manager, entry);
|
this[entry] = GetDefault(Manager, entry);
|
||||||
|
|
@ -142,7 +142,7 @@ public sealed class ExpandedEqpFile : ExpandedEqpGmpBase, IEnumerable<EqpEntry>
|
||||||
|
|
||||||
public IEnumerator<EqpEntry> GetEnumerator()
|
public IEnumerator<EqpEntry> GetEnumerator()
|
||||||
{
|
{
|
||||||
for (var idx = 1; idx < Count; ++idx)
|
for (ushort idx = 1; idx < Count; ++idx)
|
||||||
yield return this[idx];
|
yield return this[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,16 +159,16 @@ public sealed class ExpandedGmpFile : ExpandedEqpGmpBase, IEnumerable<GmpEntry>
|
||||||
: base(manager, true)
|
: base(manager, true)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public GmpEntry this[int idx]
|
public GmpEntry this[SetId 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, int setIdx)
|
public static GmpEntry GetDefault(MetaFileManager manager, SetId setIdx)
|
||||||
=> (GmpEntry)GetDefaultInternal(manager, InternalIndex, setIdx, (ulong)GmpEntry.Default);
|
=> (GmpEntry)GetDefaultInternal(manager, InternalIndex, setIdx, (ulong)GmpEntry.Default);
|
||||||
|
|
||||||
public void Reset(IEnumerable<int> entries)
|
public void Reset(IEnumerable<SetId> entries)
|
||||||
{
|
{
|
||||||
foreach (var entry in entries)
|
foreach (var entry in entries)
|
||||||
this[entry] = GetDefault(Manager, entry);
|
this[entry] = GetDefault(Manager, entry);
|
||||||
|
|
@ -176,7 +176,7 @@ public sealed class ExpandedGmpFile : ExpandedEqpGmpBase, IEnumerable<GmpEntry>
|
||||||
|
|
||||||
public IEnumerator<GmpEntry> GetEnumerator()
|
public IEnumerator<GmpEntry> GetEnumerator()
|
||||||
{
|
{
|
||||||
for (var idx = 1; idx < Count; ++idx)
|
for (ushort idx = 1; idx < Count; ++idx)
|
||||||
yield return this[idx];
|
yield return this[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Interop.Services;
|
using Penumbra.Interop.Services;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
using Penumbra.Meta.Manipulations;
|
using Penumbra.Meta.Manipulations;
|
||||||
|
|
@ -168,21 +169,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, ushort setId)
|
public static ushort GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex index, GenderRace genderRace, SetId setId)
|
||||||
{
|
{
|
||||||
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);
|
var (idx, found) = FindEntry(span, genderRace, setId.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, ushort setId)
|
public static ushort GetDefault(MetaFileManager manager, MetaIndex metaIndex, GenderRace genderRace, SetId setId)
|
||||||
=> GetDefault(manager, CharacterUtility.ReverseIndices[(int)metaIndex], genderRace, setId);
|
=> GetDefault(manager, CharacterUtility.ReverseIndices[(int)metaIndex], genderRace, setId);
|
||||||
|
|
||||||
public static ushort GetDefault(MetaFileManager manager, EstManipulation.EstType estType, GenderRace genderRace, ushort setId)
|
public static ushort GetDefault(MetaFileManager manager, EstManipulation.EstType estType, GenderRace genderRace, SetId setId)
|
||||||
=> GetDefault(manager, (MetaIndex)estType, genderRace, setId);
|
=> GetDefault(manager, (MetaIndex)estType, genderRace, setId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
using Penumbra.Meta.Manipulations;
|
using Penumbra.Meta.Manipulations;
|
||||||
using Penumbra.Services;
|
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
using Penumbra.String.Functions;
|
using Penumbra.String.Functions;
|
||||||
|
|
||||||
|
|
@ -50,19 +49,19 @@ public unsafe class ImcFile : MetaBaseFile
|
||||||
private static ushort PartMask(byte* data)
|
private static ushort PartMask(byte* data)
|
||||||
=> *(ushort*)(data + 2);
|
=> *(ushort*)(data + 2);
|
||||||
|
|
||||||
private static ImcEntry* VariantPtr(byte* data, int partIdx, int variantIdx)
|
private static ImcEntry* VariantPtr(byte* data, int partIdx, Variant variantIdx)
|
||||||
{
|
{
|
||||||
var flag = 1 << partIdx;
|
var flag = 1 << partIdx;
|
||||||
if ((PartMask(data) & flag) == 0 || variantIdx > CountInternal(data))
|
if ((PartMask(data) & flag) == 0 || variantIdx.Id > CountInternal(data))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var numParts = BitOperations.PopCount(PartMask(data));
|
var numParts = BitOperations.PopCount(PartMask(data));
|
||||||
var ptr = (ImcEntry*)(data + PreambleSize);
|
var ptr = (ImcEntry*)(data + PreambleSize);
|
||||||
ptr += variantIdx * numParts + partIdx;
|
ptr += variantIdx.Id * numParts + partIdx;
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImcEntry GetEntry(int partIdx, int variantIdx)
|
public ImcEntry GetEntry(int partIdx, Variant variantIdx)
|
||||||
{
|
{
|
||||||
var ptr = VariantPtr(Data, partIdx, variantIdx);
|
var ptr = VariantPtr(Data, partIdx, variantIdx);
|
||||||
return ptr == null ? new ImcEntry() : *ptr;
|
return ptr == null ? new ImcEntry() : *ptr;
|
||||||
|
|
@ -106,12 +105,12 @@ public unsafe class ImcFile : MetaBaseFile
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SetEntry(int partIdx, int variantIdx, ImcEntry entry)
|
public bool SetEntry(int partIdx, Variant variantIdx, ImcEntry entry)
|
||||||
{
|
{
|
||||||
if (partIdx >= NumParts)
|
if (partIdx >= NumParts)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
EnsureVariantCount(variantIdx);
|
EnsureVariantCount(variantIdx.Id);
|
||||||
|
|
||||||
var variantPtr = VariantPtr(Data, partIdx, variantIdx);
|
var variantPtr = VariantPtr(Data, partIdx, variantIdx);
|
||||||
if (variantPtr == null)
|
if (variantPtr == null)
|
||||||
|
|
@ -154,10 +153,10 @@ public unsafe class ImcFile : MetaBaseFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImcEntry GetDefault(MetaFileManager manager, Utf8GamePath path, EquipSlot slot, int variantIdx, out bool exists)
|
public static ImcEntry GetDefault(MetaFileManager manager, Utf8GamePath path, EquipSlot slot, Variant variantIdx, out bool exists)
|
||||||
=> GetDefault(manager, path.ToString(), slot, variantIdx, out exists);
|
=> GetDefault(manager, path.ToString(), slot, variantIdx, out exists);
|
||||||
|
|
||||||
public static ImcEntry GetDefault(MetaFileManager manager, string path, EquipSlot slot, int variantIdx, out bool exists)
|
public static ImcEntry GetDefault(MetaFileManager manager, string path, EquipSlot slot, Variant variantIdx, out bool exists)
|
||||||
{
|
{
|
||||||
var file = manager.GameData.GetFile(path);
|
var file = manager.GameData.GetFile(path);
|
||||||
exists = false;
|
exists = false;
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,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 ushort SetId { get; private init; }
|
public SetId 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, ushort setId)
|
public EqdpManipulation(EqdpEntry entry, EquipSlot slot, Gender gender, ModelRace race, SetId setId)
|
||||||
{
|
{
|
||||||
Gender = gender;
|
Gender = gender;
|
||||||
Race = race;
|
Race = race;
|
||||||
|
|
@ -74,7 +74,7 @@ public readonly struct EqdpManipulation : IMetaManipulation<EqdpManipulation>
|
||||||
if (g != 0)
|
if (g != 0)
|
||||||
return g;
|
return g;
|
||||||
|
|
||||||
var set = SetId.CompareTo(other.SetId);
|
var set = SetId.Id.CompareTo(other.SetId.Id);
|
||||||
return set != 0 ? set : Slot.CompareTo(other.Slot);
|
return set != 0 ? set : Slot.CompareTo(other.Slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,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 ushort SetId { get; private init; }
|
public SetId 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, ushort setId )
|
public EqpManipulation( EqpEntry entry, EquipSlot slot, SetId setId )
|
||||||
{
|
{
|
||||||
Slot = slot;
|
Slot = slot;
|
||||||
SetId = setId;
|
SetId = setId;
|
||||||
|
|
@ -48,7 +48,7 @@ public readonly struct EqpManipulation : IMetaManipulation< EqpManipulation >
|
||||||
|
|
||||||
public int CompareTo( EqpManipulation other )
|
public int CompareTo( EqpManipulation other )
|
||||||
{
|
{
|
||||||
var set = SetId.CompareTo( other.SetId );
|
var set = SetId.Id.CompareTo( other.SetId.Id );
|
||||||
return set != 0 ? set : Slot.CompareTo( other.Slot );
|
return set != 0 ? set : Slot.CompareTo( other.Slot );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using System.Runtime.InteropServices;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
using Penumbra.Meta.Files;
|
using Penumbra.Meta.Files;
|
||||||
|
|
||||||
|
|
@ -37,13 +38,13 @@ 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 ushort SetId { get; private init; }
|
public SetId 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, ushort setId, ushort entry )
|
public EstManipulation( Gender gender, ModelRace race, EstType slot, SetId setId, ushort entry )
|
||||||
{
|
{
|
||||||
Entry = entry;
|
Entry = entry;
|
||||||
Gender = gender;
|
Gender = gender;
|
||||||
|
|
@ -86,7 +87,7 @@ public readonly struct EstManipulation : IMetaManipulation< EstManipulation >
|
||||||
}
|
}
|
||||||
|
|
||||||
var s = Slot.CompareTo( other.Slot );
|
var s = Slot.CompareTo( other.Slot );
|
||||||
return s != 0 ? s : SetId.CompareTo( other.SetId );
|
return s != 0 ? s : SetId.Id.CompareTo( other.SetId.Id );
|
||||||
}
|
}
|
||||||
|
|
||||||
public MetaIndex FileIndex()
|
public MetaIndex FileIndex()
|
||||||
|
|
@ -94,7 +95,7 @@ public readonly struct EstManipulation : IMetaManipulation< EstManipulation >
|
||||||
|
|
||||||
public bool Apply( EstFile file )
|
public bool Apply( EstFile file )
|
||||||
{
|
{
|
||||||
return file.SetEntry( Names.CombinedRace( Gender, Race ), SetId, Entry ) switch
|
return file.SetEntry( Names.CombinedRace( Gender, Race ), SetId.Id, Entry ) switch
|
||||||
{
|
{
|
||||||
EstFile.EstEntryChange.Unchanged => false,
|
EstFile.EstEntryChange.Unchanged => false,
|
||||||
EstFile.EstEntryChange.Changed => true,
|
EstFile.EstEntryChange.Changed => true,
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ namespace Penumbra.Meta.Manipulations;
|
||||||
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 ushort SetId { get; private init; }
|
public SetId SetId { get; private init; }
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public GmpManipulation( GmpEntry entry, ushort setId )
|
public GmpManipulation( GmpEntry entry, SetId setId )
|
||||||
{
|
{
|
||||||
Entry = entry;
|
Entry = entry;
|
||||||
SetId = setId;
|
SetId = setId;
|
||||||
|
|
@ -35,7 +35,7 @@ public readonly struct GmpManipulation : IMetaManipulation< GmpManipulation >
|
||||||
=> SetId.GetHashCode();
|
=> SetId.GetHashCode();
|
||||||
|
|
||||||
public int CompareTo( GmpManipulation other )
|
public int CompareTo( GmpManipulation other )
|
||||||
=> SetId.CompareTo( other.SetId );
|
=> SetId.Id.CompareTo( other.SetId.Id );
|
||||||
|
|
||||||
public MetaIndex FileIndex()
|
public MetaIndex FileIndex()
|
||||||
=> MetaIndex.Gmp;
|
=> MetaIndex.Gmp;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Reflection.Metadata;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
|
|
@ -12,28 +11,28 @@ using Penumbra.String.Classes;
|
||||||
|
|
||||||
namespace Penumbra.Meta.Manipulations;
|
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 ushort PrimaryId { get; private init; }
|
public SetId PrimaryId { get; private init; }
|
||||||
public ushort SecondaryId { get; private init; }
|
public SetId SecondaryId { get; private init; }
|
||||||
public byte 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; }
|
||||||
|
|
||||||
[JsonConverter( typeof( StringEnumConverter ) )]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
public EquipSlot EquipSlot { get; private init; }
|
public EquipSlot EquipSlot { get; private init; }
|
||||||
|
|
||||||
[JsonConverter( typeof( StringEnumConverter ) )]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
public BodySlot BodySlot { get; private init; }
|
public BodySlot BodySlot { get; private init; }
|
||||||
|
|
||||||
public ImcManipulation( EquipSlot equipSlot, ushort variant, ushort primaryId, ImcEntry entry )
|
public ImcManipulation(EquipSlot equipSlot, ushort variant, SetId primaryId, ImcEntry entry)
|
||||||
{
|
{
|
||||||
Entry = entry;
|
Entry = entry;
|
||||||
PrimaryId = primaryId;
|
PrimaryId = primaryId;
|
||||||
Variant = ( byte )Math.Clamp( variant, ( ushort )0, byte.MaxValue );
|
Variant = (Variant)Math.Clamp(variant, (ushort)0, byte.MaxValue);
|
||||||
SecondaryId = 0;
|
SecondaryId = 0;
|
||||||
ObjectType = equipSlot.IsAccessory() ? ObjectType.Accessory : ObjectType.Equipment;
|
ObjectType = equipSlot.IsAccessory() ? ObjectType.Accessory : ObjectType.Equipment;
|
||||||
EquipSlot = equipSlot;
|
EquipSlot = equipSlot;
|
||||||
|
|
@ -45,21 +44,21 @@ 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, ushort primaryId, ushort secondaryId, ushort variant,
|
internal ImcManipulation(ObjectType objectType, BodySlot bodySlot, SetId primaryId, SetId secondaryId, ushort variant,
|
||||||
EquipSlot equipSlot, ImcEntry entry )
|
EquipSlot equipSlot, ImcEntry entry)
|
||||||
{
|
{
|
||||||
Entry = entry;
|
Entry = entry;
|
||||||
ObjectType = objectType;
|
ObjectType = objectType;
|
||||||
PrimaryId = primaryId;
|
PrimaryId = primaryId;
|
||||||
Variant = ( byte )Math.Clamp( variant, ( ushort )0, byte.MaxValue );
|
Variant = (Variant)Math.Clamp(variant, (ushort)0, byte.MaxValue);
|
||||||
|
|
||||||
if( objectType is ObjectType.Accessory or ObjectType.Equipment )
|
if (objectType is ObjectType.Accessory or ObjectType.Equipment)
|
||||||
{
|
{
|
||||||
BodySlot = variant > byte.MaxValue ? BodySlot.Body : BodySlot.Unknown;
|
BodySlot = variant > byte.MaxValue ? BodySlot.Body : BodySlot.Unknown;
|
||||||
SecondaryId = 0;
|
SecondaryId = 0;
|
||||||
EquipSlot = equipSlot;
|
EquipSlot = equipSlot;
|
||||||
}
|
}
|
||||||
else if( objectType is ObjectType.DemiHuman )
|
else if (objectType is ObjectType.DemiHuman)
|
||||||
{
|
{
|
||||||
BodySlot = variant > byte.MaxValue ? BodySlot.Body : BodySlot.Unknown;
|
BodySlot = variant > byte.MaxValue ? BodySlot.Body : BodySlot.Unknown;
|
||||||
SecondaryId = secondaryId;
|
SecondaryId = secondaryId;
|
||||||
|
|
@ -73,85 +72,81 @@ public readonly struct ImcManipulation : IMetaManipulation< ImcManipulation >
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImcManipulation Copy( ImcEntry entry )
|
public ImcManipulation Copy(ImcEntry entry)
|
||||||
=> new(ObjectType, BodySlot, PrimaryId, SecondaryId, Variant, EquipSlot, entry);
|
=> new(ObjectType, BodySlot, PrimaryId, SecondaryId, Variant.Id, EquipSlot, entry);
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
=> ObjectType is ObjectType.Equipment or ObjectType.Accessory
|
=> ObjectType is ObjectType.Equipment or ObjectType.Accessory
|
||||||
? $"Imc - {PrimaryId} - {EquipSlot} - {Variant}"
|
? $"Imc - {PrimaryId} - {EquipSlot} - {Variant}"
|
||||||
: $"Imc - {PrimaryId} - {ObjectType} - {SecondaryId} - {BodySlot} - {Variant}";
|
: $"Imc - {PrimaryId} - {ObjectType} - {SecondaryId} - {BodySlot} - {Variant}";
|
||||||
|
|
||||||
public bool Equals( ImcManipulation other )
|
public bool Equals(ImcManipulation other)
|
||||||
=> PrimaryId == other.PrimaryId
|
=> PrimaryId == other.PrimaryId
|
||||||
&& Variant == other.Variant
|
&& Variant == other.Variant
|
||||||
&& SecondaryId == other.SecondaryId
|
&& SecondaryId == other.SecondaryId
|
||||||
&& ObjectType == other.ObjectType
|
&& ObjectType == other.ObjectType
|
||||||
&& EquipSlot == other.EquipSlot
|
&& EquipSlot == other.EquipSlot
|
||||||
&& BodySlot == other.BodySlot;
|
&& BodySlot == other.BodySlot;
|
||||||
|
|
||||||
public override bool Equals( object? obj )
|
public override bool Equals(object? obj)
|
||||||
=> obj is ImcManipulation other && Equals( other );
|
=> obj is ImcManipulation other && Equals(other);
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
=> HashCode.Combine( PrimaryId, Variant, SecondaryId, ( int )ObjectType, ( int )EquipSlot, ( int )BodySlot );
|
=> HashCode.Combine(PrimaryId, Variant, SecondaryId, (int)ObjectType, (int)EquipSlot, (int)BodySlot);
|
||||||
|
|
||||||
public int CompareTo( ImcManipulation other )
|
public int CompareTo(ImcManipulation other)
|
||||||
{
|
{
|
||||||
var o = ObjectType.CompareTo( other.ObjectType );
|
var o = ObjectType.CompareTo(other.ObjectType);
|
||||||
if( o != 0 )
|
if (o != 0)
|
||||||
{
|
|
||||||
return o;
|
return o;
|
||||||
}
|
|
||||||
|
|
||||||
var i = PrimaryId.CompareTo( other.PrimaryId );
|
var i = PrimaryId.Id.CompareTo(other.PrimaryId.Id);
|
||||||
if( i != 0 )
|
if (i != 0)
|
||||||
{
|
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
|
if (ObjectType is ObjectType.Equipment or ObjectType.Accessory)
|
||||||
|
{
|
||||||
|
var e = EquipSlot.CompareTo(other.EquipSlot);
|
||||||
|
return e != 0 ? e : Variant.Id.CompareTo(other.Variant.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ObjectType is ObjectType.Equipment or ObjectType.Accessory )
|
if (ObjectType is ObjectType.DemiHuman)
|
||||||
{
|
{
|
||||||
var e = EquipSlot.CompareTo( other.EquipSlot );
|
var e = EquipSlot.CompareTo(other.EquipSlot);
|
||||||
return e != 0 ? e : Variant.CompareTo( other.Variant );
|
if (e != 0)
|
||||||
}
|
|
||||||
|
|
||||||
if( ObjectType is ObjectType.DemiHuman )
|
|
||||||
{
|
|
||||||
var e = EquipSlot.CompareTo( other.EquipSlot );
|
|
||||||
if( e != 0 )
|
|
||||||
{
|
|
||||||
return e;
|
return e;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var s = SecondaryId.CompareTo( other.SecondaryId );
|
var s = SecondaryId.Id.CompareTo(other.SecondaryId.Id);
|
||||||
if( s != 0 )
|
if (s != 0)
|
||||||
{
|
|
||||||
return s;
|
return s;
|
||||||
}
|
|
||||||
|
|
||||||
var b = BodySlot.CompareTo( other.BodySlot );
|
var b = BodySlot.CompareTo(other.BodySlot);
|
||||||
return b != 0 ? b : Variant.CompareTo( other.Variant );
|
return b != 0 ? b : Variant.Id.CompareTo(other.Variant.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MetaIndex FileIndex()
|
public MetaIndex FileIndex()
|
||||||
=> ( MetaIndex )( -1 );
|
=> (MetaIndex)(-1);
|
||||||
|
|
||||||
public Utf8GamePath GamePath()
|
public Utf8GamePath GamePath()
|
||||||
{
|
{
|
||||||
return ObjectType switch
|
return ObjectType switch
|
||||||
{
|
{
|
||||||
ObjectType.Accessory => Utf8GamePath.FromString( GamePaths.Accessory.Imc.Path( PrimaryId ), out var p ) ? p : Utf8GamePath.Empty,
|
ObjectType.Accessory => Utf8GamePath.FromString(GamePaths.Accessory.Imc.Path(PrimaryId), out var p) ? p : Utf8GamePath.Empty,
|
||||||
ObjectType.Equipment => Utf8GamePath.FromString( GamePaths.Equipment.Imc.Path( PrimaryId ), out var p ) ? p : Utf8GamePath.Empty,
|
ObjectType.Equipment => Utf8GamePath.FromString(GamePaths.Equipment.Imc.Path(PrimaryId), out var p) ? p : Utf8GamePath.Empty,
|
||||||
ObjectType.DemiHuman => Utf8GamePath.FromString( GamePaths.DemiHuman.Imc.Path( PrimaryId, SecondaryId ), out var p ) ? p : Utf8GamePath.Empty,
|
ObjectType.DemiHuman => Utf8GamePath.FromString(GamePaths.DemiHuman.Imc.Path(PrimaryId, SecondaryId), out var p)
|
||||||
ObjectType.Monster => Utf8GamePath.FromString( GamePaths.Monster.Imc.Path( PrimaryId, SecondaryId ), out var p ) ? p : Utf8GamePath.Empty,
|
? p
|
||||||
ObjectType.Weapon => Utf8GamePath.FromString( GamePaths.Weapon.Imc.Path( PrimaryId, SecondaryId ), out var p ) ? p : Utf8GamePath.Empty,
|
: Utf8GamePath.Empty,
|
||||||
_ => throw new NotImplementedException(),
|
ObjectType.Monster => Utf8GamePath.FromString(GamePaths.Monster.Imc.Path(PrimaryId, SecondaryId), out var p)
|
||||||
|
? p
|
||||||
|
: Utf8GamePath.Empty,
|
||||||
|
ObjectType.Weapon => Utf8GamePath.FromString(GamePaths.Weapon.Imc.Path(PrimaryId, SecondaryId), out var p) ? p : Utf8GamePath.Empty,
|
||||||
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Apply( ImcFile file )
|
public bool Apply(ImcFile file)
|
||||||
=> file.SetEntry( ImcFile.PartIndex( EquipSlot ), Variant, Entry );
|
=> file.SetEntry(ImcFile.PartIndex(EquipSlot), Variant.Id, Entry);
|
||||||
|
|
||||||
public bool Validate()
|
public bool Validate()
|
||||||
{
|
{
|
||||||
|
|
@ -165,12 +160,14 @@ public readonly struct ImcManipulation : IMetaManipulation< ImcManipulation >
|
||||||
return false;
|
return false;
|
||||||
if (SecondaryId != 0)
|
if (SecondaryId != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ObjectType.DemiHuman:
|
case ObjectType.DemiHuman:
|
||||||
if (BodySlot is not BodySlot.Unknown)
|
if (BodySlot is not BodySlot.Unknown)
|
||||||
return false;
|
return false;
|
||||||
if (!EquipSlot.IsEquipment() && !EquipSlot.IsAccessory())
|
if (!EquipSlot.IsEquipment() && !EquipSlot.IsAccessory())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (!Enum.IsDefined(BodySlot))
|
if (!Enum.IsDefined(BodySlot))
|
||||||
|
|
@ -179,6 +176,7 @@ public readonly struct ImcManipulation : IMetaManipulation< ImcManipulation >
|
||||||
return false;
|
return false;
|
||||||
if (!Enum.IsDefined(ObjectType))
|
if (!Enum.IsDefined(ObjectType))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,4 +185,4 @@ public readonly struct ImcManipulation : IMetaManipulation< ImcManipulation >
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,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, SetId idFrom, SetId idTo )
|
public static FileSwap CreateMdl( MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, BodySlot slot, GenderRace race, SetId idFrom, SetId idTo )
|
||||||
{
|
{
|
||||||
if( idFrom.Value > 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}." );
|
||||||
}
|
}
|
||||||
|
|
@ -51,9 +51,9 @@ public static class CustomizationSwap
|
||||||
|
|
||||||
var newFileName = fileName;
|
var newFileName = fileName;
|
||||||
newFileName = ItemSwap.ReplaceRace( newFileName, gameRaceTo, race, gameRaceTo != race );
|
newFileName = ItemSwap.ReplaceRace( newFileName, gameRaceTo, race, gameRaceTo != race );
|
||||||
newFileName = ItemSwap.ReplaceBody( newFileName, slot, idTo, idFrom, idFrom.Value != idTo.Value );
|
newFileName = ItemSwap.ReplaceBody( newFileName, slot, idTo, idFrom, idFrom != idTo );
|
||||||
newFileName = ItemSwap.AddSuffix( newFileName, ".mtrl", $"_c{race.ToRaceCode()}", gameRaceFrom != race || MaterialHandling.IsSpecialCase( race, idFrom ) );
|
newFileName = ItemSwap.AddSuffix( newFileName, ".mtrl", $"_c{race.ToRaceCode()}", gameRaceFrom != race || MaterialHandling.IsSpecialCase( race, idFrom ) );
|
||||||
newFileName = ItemSwap.AddSuffix( newFileName, ".mtrl", $"_{slot.ToAbbreviation()}{idFrom.Value:D4}", gameSetIdFrom.Value != idFrom.Value );
|
newFileName = ItemSwap.AddSuffix( newFileName, ".mtrl", $"_{slot.ToAbbreviation()}{idFrom.Id:D4}", gameSetIdFrom != idFrom );
|
||||||
|
|
||||||
var actualMtrlFromPath = mtrlFromPath;
|
var actualMtrlFromPath = mtrlFromPath;
|
||||||
if( newFileName != fileName )
|
if( newFileName != fileName )
|
||||||
|
|
|
||||||
|
|
@ -54,11 +54,11 @@ public static class EquipmentSwap
|
||||||
throw new ItemSwap.InvalidItemTypeException();
|
throw new ItemSwap.InvalidItemTypeException();
|
||||||
|
|
||||||
var (imcFileFrom, variants, affectedItems) = GetVariants(manager, identifier, slotFrom, idFrom, idTo, variantFrom);
|
var (imcFileFrom, variants, affectedItems) = GetVariants(manager, identifier, slotFrom, idFrom, idTo, variantFrom);
|
||||||
var imcManip = new ImcManipulation(slotTo, variantTo, idTo.Value, default);
|
var imcManip = new ImcManipulation(slotTo, variantTo.Id, idTo.Id, default);
|
||||||
var imcFileTo = new ImcFile(manager, imcManip);
|
var imcFileTo = new ImcFile(manager, imcManip);
|
||||||
var skipFemale = false;
|
var skipFemale = false;
|
||||||
var skipMale = false;
|
var skipMale = false;
|
||||||
var mtrlVariantTo = manips(imcManip.Copy(imcFileTo.GetEntry(ImcFile.PartIndex(slotTo), variantTo))).Imc.Entry.MaterialId;
|
var mtrlVariantTo = manips(imcManip.Copy(imcFileTo.GetEntry(ImcFile.PartIndex(slotTo), variantTo.Id))).Imc.Entry.MaterialId;
|
||||||
foreach (var gr in Enum.GetValues<GenderRace>())
|
foreach (var gr in Enum.GetValues<GenderRace>())
|
||||||
{
|
{
|
||||||
switch (gr.Split().Item1)
|
switch (gr.Split().Item1)
|
||||||
|
|
@ -124,7 +124,7 @@ public static class EquipmentSwap
|
||||||
foreach (var slot in ConvertSlots(slotFrom, rFinger, lFinger))
|
foreach (var slot in ConvertSlots(slotFrom, rFinger, lFinger))
|
||||||
{
|
{
|
||||||
(var imcFileFrom, var variants, affectedItems) = GetVariants(manager, identifier, slot, idFrom, idTo, variantFrom);
|
(var imcFileFrom, var variants, affectedItems) = GetVariants(manager, identifier, slot, idFrom, idTo, variantFrom);
|
||||||
var imcManip = new ImcManipulation(slot, variantTo, idTo.Value, default);
|
var imcManip = new ImcManipulation(slot, variantTo.Id, idTo, default);
|
||||||
var imcFileTo = new ImcFile(manager, imcManip);
|
var imcFileTo = new ImcFile(manager, imcManip);
|
||||||
|
|
||||||
var isAccessory = slot.IsAccessory();
|
var isAccessory = slot.IsAccessory();
|
||||||
|
|
@ -198,10 +198,10 @@ public static class EquipmentSwap
|
||||||
SetId idTo, byte mtrlTo)
|
SetId idTo, byte mtrlTo)
|
||||||
{
|
{
|
||||||
var (gender, race) = gr.Split();
|
var (gender, race) = gr.Split();
|
||||||
var eqdpFrom = new EqdpManipulation(ExpandedEqdpFile.GetDefault(manager, gr, slotFrom.IsAccessory(), idFrom.Value), slotFrom, gender,
|
var eqdpFrom = new EqdpManipulation(ExpandedEqdpFile.GetDefault(manager, gr, slotFrom.IsAccessory(), idFrom), slotFrom, gender,
|
||||||
race, idFrom.Value);
|
race, idFrom);
|
||||||
var eqdpTo = new EqdpManipulation(ExpandedEqdpFile.GetDefault(manager, gr, slotTo.IsAccessory(), idTo.Value), slotTo, gender, race,
|
var eqdpTo = new EqdpManipulation(ExpandedEqdpFile.GetDefault(manager, gr, slotTo.IsAccessory(), idTo), slotTo, gender, race,
|
||||||
idTo.Value);
|
idTo);
|
||||||
var meta = new MetaSwap(manips, eqdpFrom, eqdpTo);
|
var meta = new MetaSwap(manips, eqdpFrom, eqdpTo);
|
||||||
var (ownMtrl, ownMdl) = meta.SwapApplied.Eqdp.Entry.ToBits(slotFrom);
|
var (ownMtrl, ownMdl) = meta.SwapApplied.Eqdp.Entry.ToBits(slotFrom);
|
||||||
if (ownMdl)
|
if (ownMdl)
|
||||||
|
|
@ -240,7 +240,7 @@ public static class EquipmentSwap
|
||||||
return mdl;
|
return mdl;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void LookupItem(EquipItem i, out EquipSlot slot, out SetId modelId, out byte variant)
|
private static void LookupItem(EquipItem i, out EquipSlot slot, out SetId modelId, out Variant variant)
|
||||||
{
|
{
|
||||||
slot = i.Type.ToSlot();
|
slot = i.Type.ToSlot();
|
||||||
if (!slot.IsEquipmentPiece())
|
if (!slot.IsEquipmentPiece())
|
||||||
|
|
@ -250,14 +250,14 @@ public static class EquipmentSwap
|
||||||
variant = i.Variant;
|
variant = i.Variant;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (ImcFile, byte[], EquipItem[]) GetVariants(MetaFileManager manager, IObjectIdentifier identifier, EquipSlot slotFrom,
|
private static (ImcFile, Variant[], EquipItem[]) GetVariants(MetaFileManager manager, IObjectIdentifier identifier, EquipSlot slotFrom,
|
||||||
SetId idFrom, SetId idTo, byte variantFrom)
|
SetId idFrom, SetId idTo, Variant variantFrom)
|
||||||
{
|
{
|
||||||
var entry = new ImcManipulation(slotFrom, variantFrom, idFrom.Value, default);
|
var entry = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, default);
|
||||||
var imc = new ImcFile(manager, entry);
|
var imc = new ImcFile(manager, entry);
|
||||||
EquipItem[] items;
|
EquipItem[] items;
|
||||||
byte[] variants;
|
Variant[] variants;
|
||||||
if (idFrom.Value == idTo.Value)
|
if (idFrom == idTo)
|
||||||
{
|
{
|
||||||
items = identifier.Identify(idFrom, variantFrom, slotFrom).ToArray();
|
items = identifier.Identify(idFrom, variantFrom, slotFrom).ToArray();
|
||||||
variants = new[]
|
variants = new[]
|
||||||
|
|
@ -270,7 +270,7 @@ public static class EquipmentSwap
|
||||||
items = identifier.Identify(slotFrom.IsEquipment()
|
items = identifier.Identify(slotFrom.IsEquipment()
|
||||||
? GamePaths.Equipment.Mdl.Path(idFrom, GenderRace.MidlanderMale, slotFrom)
|
? GamePaths.Equipment.Mdl.Path(idFrom, GenderRace.MidlanderMale, slotFrom)
|
||||||
: GamePaths.Accessory.Mdl.Path(idFrom, GenderRace.MidlanderMale, slotFrom)).Select(kvp => kvp.Value).OfType<EquipItem>().ToArray();
|
: GamePaths.Accessory.Mdl.Path(idFrom, GenderRace.MidlanderMale, slotFrom)).Select(kvp => kvp.Value).OfType<EquipItem>().ToArray();
|
||||||
variants = Enumerable.Range(0, imc.Count + 1).Select(i => (byte)i).ToArray();
|
variants = Enumerable.Range(0, imc.Count + 1).Select(i => (Variant)i).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (imc, variants, items);
|
return (imc, variants, items);
|
||||||
|
|
@ -282,24 +282,23 @@ public static class EquipmentSwap
|
||||||
if (slot is not EquipSlot.Head)
|
if (slot is not EquipSlot.Head)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var manipFrom = new GmpManipulation(ExpandedGmpFile.GetDefault(manager, idFrom.Value), idFrom.Value);
|
var manipFrom = new GmpManipulation(ExpandedGmpFile.GetDefault(manager, idFrom), idFrom);
|
||||||
var manipTo = new GmpManipulation(ExpandedGmpFile.GetDefault(manager, idTo.Value), idTo.Value);
|
var manipTo = new GmpManipulation(ExpandedGmpFile.GetDefault(manager, idTo), idTo);
|
||||||
return new MetaSwap(manips, manipFrom, manipTo);
|
return new MetaSwap(manips, manipFrom, manipTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot,
|
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot,
|
||||||
SetId idFrom, SetId idTo,
|
SetId idFrom, SetId idTo, Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo)
|
||||||
byte variantFrom, byte 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, Func<MetaManipulation, MetaManipulation> manips,
|
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, SetId idFrom, SetId idTo,
|
||||||
byte variantFrom, byte 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);
|
||||||
var entryTo = imcFileTo.GetEntry(ImcFile.PartIndex(slotTo), variantTo);
|
var entryTo = imcFileTo.GetEntry(ImcFile.PartIndex(slotTo), variantTo);
|
||||||
var manipulationFrom = new ImcManipulation(slotFrom, variantFrom, idFrom.Value, entryFrom);
|
var manipulationFrom = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, entryFrom);
|
||||||
var manipulationTo = new ImcManipulation(slotTo, variantTo, idTo.Value, entryTo);
|
var manipulationTo = new ImcManipulation(slotTo, variantTo.Id, idTo, entryTo);
|
||||||
var imc = new MetaSwap(manips, manipulationFrom, manipulationTo);
|
var imc = new MetaSwap(manips, manipulationFrom, manipulationTo);
|
||||||
|
|
||||||
var decal = CreateDecal(manager, redirections, imc.SwapToModded.Imc.Entry.DecalId);
|
var decal = CreateDecal(manager, redirections, imc.SwapToModded.Imc.Entry.DecalId);
|
||||||
|
|
@ -332,8 +331,8 @@ public static class EquipmentSwap
|
||||||
if (vfxId == 0)
|
if (vfxId == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var vfxPathFrom = GamePaths.Equipment.Avfx.Path(idFrom.Value, vfxId);
|
var vfxPathFrom = GamePaths.Equipment.Avfx.Path(idFrom, vfxId);
|
||||||
var vfxPathTo = GamePaths.Equipment.Avfx.Path(idTo.Value, vfxId);
|
var vfxPathTo = GamePaths.Equipment.Avfx.Path(idTo, vfxId);
|
||||||
var avfx = FileSwap.CreateSwap(manager, ResourceType.Avfx, redirections, vfxPathFrom, vfxPathTo);
|
var avfx = FileSwap.CreateSwap(manager, ResourceType.Avfx, redirections, vfxPathFrom, vfxPathTo);
|
||||||
|
|
||||||
foreach (ref var filePath in avfx.AsAvfx()!.Textures.AsSpan())
|
foreach (ref var filePath in avfx.AsAvfx()!.Textures.AsSpan())
|
||||||
|
|
@ -351,10 +350,10 @@ public static class EquipmentSwap
|
||||||
if (slot.IsAccessory())
|
if (slot.IsAccessory())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var eqpValueFrom = ExpandedEqpFile.GetDefault(manager, idFrom.Value);
|
var eqpValueFrom = ExpandedEqpFile.GetDefault(manager, idFrom);
|
||||||
var eqpValueTo = ExpandedEqpFile.GetDefault(manager, idTo.Value);
|
var eqpValueTo = ExpandedEqpFile.GetDefault(manager, idTo);
|
||||||
var eqpFrom = new EqpManipulation(eqpValueFrom, slot, idFrom.Value);
|
var eqpFrom = new EqpManipulation(eqpValueFrom, slot, idFrom);
|
||||||
var eqpTo = new EqpManipulation(eqpValueTo, slot, idFrom.Value);
|
var eqpTo = new EqpManipulation(eqpValueTo, slot, idFrom);
|
||||||
return new MetaSwap(manips, eqpFrom, eqpTo);
|
return new MetaSwap(manips, eqpFrom, eqpTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -368,7 +367,7 @@ public static class EquipmentSwap
|
||||||
ref bool dataWasChanged)
|
ref bool dataWasChanged)
|
||||||
{
|
{
|
||||||
var prefix = slotTo.IsAccessory() ? 'a' : 'e';
|
var prefix = slotTo.IsAccessory() ? 'a' : 'e';
|
||||||
if (!fileName.Contains($"{prefix}{idTo.Value:D4}"))
|
if (!fileName.Contains($"{prefix}{idTo.Id:D4}"))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var folderTo = slotTo.IsAccessory()
|
var folderTo = slotTo.IsAccessory()
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Meta;
|
using Penumbra.Meta;
|
||||||
using Penumbra.Meta.Files;
|
using Penumbra.Meta.Files;
|
||||||
using Penumbra.Meta.Manipulations;
|
using Penumbra.Meta.Manipulations;
|
||||||
using Penumbra.Services;
|
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
|
|
||||||
namespace Penumbra.Mods.ItemSwap;
|
namespace Penumbra.Mods.ItemSwap;
|
||||||
|
|
@ -163,8 +162,8 @@ public static class ItemSwap
|
||||||
}
|
}
|
||||||
|
|
||||||
var (gender, race) = genderRace.Split();
|
var (gender, race) = genderRace.Split();
|
||||||
var fromDefault = new EstManipulation( gender, race, type, idFrom.Value, EstFile.GetDefault( manager, type, genderRace, idFrom.Value ) );
|
var fromDefault = new EstManipulation( gender, race, type, idFrom, EstFile.GetDefault( manager, type, genderRace, idFrom ) );
|
||||||
var toDefault = new EstManipulation( gender, race, type, idTo.Value, EstFile.GetDefault( manager, type, genderRace, idTo.Value ) );
|
var toDefault = new EstManipulation( gender, race, type, idTo, EstFile.GetDefault( manager, type, genderRace, idTo ) );
|
||||||
var est = new MetaSwap( manips, fromDefault, toDefault );
|
var est = new MetaSwap( manips, fromDefault, toDefault );
|
||||||
|
|
||||||
if( ownMdl && est.SwapApplied.Est.Entry >= 2 )
|
if( ownMdl && est.SwapApplied.Est.Entry >= 2 )
|
||||||
|
|
@ -206,7 +205,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, SetId id, bool condition = true )
|
||||||
=> condition
|
=> condition
|
||||||
? Regex.Replace( path, $"{idType}\\d{{4}}", $"{idType}{id.Value:D4}" )
|
? Regex.Replace( path, $"{idType}\\d{{4}}", $"{idType}{id.Id:D4}" )
|
||||||
: path;
|
: path;
|
||||||
|
|
||||||
public static string ReplaceAnyRace( string path, GenderRace to, bool condition = true )
|
public static string ReplaceAnyRace( string path, GenderRace to, bool condition = true )
|
||||||
|
|
@ -217,7 +216,7 @@ public static class ItemSwap
|
||||||
|
|
||||||
public static string ReplaceId( string path, char type, SetId idFrom, SetId idTo, bool condition = true )
|
public static string ReplaceId( string path, char type, SetId idFrom, SetId idTo, bool condition = true )
|
||||||
=> condition
|
=> condition
|
||||||
? path.Replace( $"{type}{idFrom.Value:D4}", $"{type}{idTo.Value:D4}" )
|
? path.Replace( $"{type}{idFrom.Id:D4}", $"{type}{idTo.Id:D4}" )
|
||||||
: path;
|
: path;
|
||||||
|
|
||||||
public static string ReplaceSlot( string path, EquipSlot from, EquipSlot to, bool condition = true )
|
public static string ReplaceSlot( string path, EquipSlot from, EquipSlot to, bool condition = true )
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ public partial class ModEditWindow
|
||||||
|
|
||||||
// Identifier
|
// Identifier
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
if (IdInput("##eqpId", IdWidth, _new.SetId, out var setId, 1, ExpandedEqpGmpBase.Count - 1, _new.SetId <= 1))
|
if (IdInput("##eqpId", IdWidth, _new.SetId.Id, out var setId, 1, ExpandedEqpGmpBase.Count - 1, _new.SetId <= 1))
|
||||||
_new = new EqpManipulation(ExpandedEqpFile.GetDefault(metaFileManager, setId), _new.Slot, setId);
|
_new = new EqpManipulation(ExpandedEqpFile.GetDefault(metaFileManager, setId), _new.Slot, setId);
|
||||||
|
|
||||||
ImGuiUtil.HoverTooltip(ModelSetIdTooltip);
|
ImGuiUtil.HoverTooltip(ModelSetIdTooltip);
|
||||||
|
|
@ -224,7 +224,7 @@ public partial class ModEditWindow
|
||||||
|
|
||||||
// Identifier
|
// Identifier
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
if (IdInput("##eqdpId", IdWidth, _new.SetId, out var setId, 0, ExpandedEqpGmpBase.Count - 1, _new.SetId <= 1))
|
if (IdInput("##eqdpId", IdWidth, _new.SetId.Id, out var setId, 0, ExpandedEqpGmpBase.Count - 1, _new.SetId <= 1))
|
||||||
{
|
{
|
||||||
var newDefaultEntry = ExpandedEqdpFile.GetDefault(metaFileManager, Names.CombinedRace(_new.Gender, _new.Race), _new.Slot.IsAccessory(), setId);
|
var newDefaultEntry = ExpandedEqdpFile.GetDefault(metaFileManager, Names.CombinedRace(_new.Gender, _new.Race), _new.Slot.IsAccessory(), setId);
|
||||||
_new = new EqdpManipulation(newDefaultEntry, _new.Slot, _new.Gender, _new.Race, setId);
|
_new = new EqdpManipulation(newDefaultEntry, _new.Slot, _new.Gender, _new.Race, setId);
|
||||||
|
|
@ -352,14 +352,14 @@ public partial class ModEditWindow
|
||||||
_ => EquipSlot.Unknown,
|
_ => EquipSlot.Unknown,
|
||||||
};
|
};
|
||||||
_new = new ImcManipulation(type, _new.BodySlot, _new.PrimaryId, _new.SecondaryId == 0 ? (ushort)1 : _new.SecondaryId,
|
_new = new ImcManipulation(type, _new.BodySlot, _new.PrimaryId, _new.SecondaryId == 0 ? (ushort)1 : _new.SecondaryId,
|
||||||
_new.Variant, equipSlot, _new.Entry);
|
_new.Variant.Id, equipSlot, _new.Entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiUtil.HoverTooltip(ObjectTypeTooltip);
|
ImGuiUtil.HoverTooltip(ObjectTypeTooltip);
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
if (IdInput("##imcId", IdWidth, _new.PrimaryId, out var setId, 0, ushort.MaxValue, _new.PrimaryId <= 1))
|
if (IdInput("##imcId", IdWidth, _new.PrimaryId.Id, out var setId, 0, ushort.MaxValue, _new.PrimaryId <= 1))
|
||||||
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, setId, _new.SecondaryId, _new.Variant, _new.EquipSlot, _new.Entry)
|
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, setId, _new.SecondaryId, _new.Variant.Id, _new.EquipSlot, _new.Entry)
|
||||||
.Copy(GetDefault(metaFileManager, _new)
|
.Copy(GetDefault(metaFileManager, _new)
|
||||||
?? new ImcEntry());
|
?? new ImcEntry());
|
||||||
|
|
||||||
|
|
@ -373,7 +373,7 @@ public partial class ModEditWindow
|
||||||
if (_new.ObjectType is ObjectType.Equipment)
|
if (_new.ObjectType is ObjectType.Equipment)
|
||||||
{
|
{
|
||||||
if (Combos.EqpEquipSlot("##imcSlot", 100, _new.EquipSlot, out var slot))
|
if (Combos.EqpEquipSlot("##imcSlot", 100, _new.EquipSlot, out var slot))
|
||||||
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, _new.SecondaryId, _new.Variant, slot, _new.Entry)
|
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, _new.SecondaryId, _new.Variant.Id, slot, _new.Entry)
|
||||||
.Copy(GetDefault(metaFileManager, _new)
|
.Copy(GetDefault(metaFileManager, _new)
|
||||||
?? new ImcEntry());
|
?? new ImcEntry());
|
||||||
|
|
||||||
|
|
@ -382,7 +382,7 @@ public partial class ModEditWindow
|
||||||
else if (_new.ObjectType is ObjectType.Accessory)
|
else if (_new.ObjectType is ObjectType.Accessory)
|
||||||
{
|
{
|
||||||
if (Combos.AccessorySlot("##imcSlot", _new.EquipSlot, out var slot))
|
if (Combos.AccessorySlot("##imcSlot", _new.EquipSlot, out var slot))
|
||||||
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, _new.SecondaryId, _new.Variant, slot, _new.Entry)
|
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, _new.SecondaryId, _new.Variant.Id, slot, _new.Entry)
|
||||||
.Copy(GetDefault(metaFileManager, _new)
|
.Copy(GetDefault(metaFileManager, _new)
|
||||||
?? new ImcEntry());
|
?? new ImcEntry());
|
||||||
|
|
||||||
|
|
@ -390,8 +390,8 @@ public partial class ModEditWindow
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (IdInput("##imcId2", 100 * UiHelpers.Scale, _new.SecondaryId, out var setId2, 0, ushort.MaxValue, false))
|
if (IdInput("##imcId2", 100 * UiHelpers.Scale, _new.SecondaryId.Id, out var setId2, 0, ushort.MaxValue, false))
|
||||||
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, setId2, _new.Variant, _new.EquipSlot, _new.Entry)
|
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, setId2, _new.Variant.Id, _new.EquipSlot, _new.Entry)
|
||||||
.Copy(GetDefault(metaFileManager, _new)
|
.Copy(GetDefault(metaFileManager, _new)
|
||||||
?? new ImcEntry());
|
?? new ImcEntry());
|
||||||
|
|
||||||
|
|
@ -399,7 +399,7 @@ public partial class ModEditWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
if (IdInput("##imcVariant", SmallIdWidth, _new.Variant, out var variant, 0, byte.MaxValue, false))
|
if (IdInput("##imcVariant", SmallIdWidth, _new.Variant.Id, out var variant, 0, byte.MaxValue, false))
|
||||||
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, _new.SecondaryId, variant, _new.EquipSlot,
|
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, _new.SecondaryId, variant, _new.EquipSlot,
|
||||||
_new.Entry).Copy(GetDefault(metaFileManager, _new)
|
_new.Entry).Copy(GetDefault(metaFileManager, _new)
|
||||||
?? new ImcEntry());
|
?? new ImcEntry());
|
||||||
|
|
@ -408,7 +408,7 @@ public partial class ModEditWindow
|
||||||
if (_new.ObjectType is ObjectType.DemiHuman)
|
if (_new.ObjectType is ObjectType.DemiHuman)
|
||||||
{
|
{
|
||||||
if (Combos.EqpEquipSlot("##imcSlot", 70, _new.EquipSlot, out var slot))
|
if (Combos.EqpEquipSlot("##imcSlot", 70, _new.EquipSlot, out var slot))
|
||||||
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, _new.SecondaryId, _new.Variant, slot, _new.Entry)
|
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, _new.SecondaryId, _new.Variant.Id, slot, _new.Entry)
|
||||||
.Copy(GetDefault(metaFileManager, _new)
|
.Copy(GetDefault(metaFileManager, _new)
|
||||||
?? new ImcEntry());
|
?? new ImcEntry());
|
||||||
|
|
||||||
|
|
@ -557,7 +557,7 @@ public partial class ModEditWindow
|
||||||
|
|
||||||
// Identifier
|
// Identifier
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
if (IdInput("##estId", IdWidth, _new.SetId, out var setId, 0, ExpandedEqpGmpBase.Count - 1, _new.SetId <= 1))
|
if (IdInput("##estId", IdWidth, _new.SetId.Id, out var setId, 0, ExpandedEqpGmpBase.Count - 1, _new.SetId <= 1))
|
||||||
{
|
{
|
||||||
var newDefaultEntry = EstFile.GetDefault(metaFileManager, _new.Slot, Names.CombinedRace(_new.Gender, _new.Race), setId);
|
var newDefaultEntry = EstFile.GetDefault(metaFileManager, _new.Slot, Names.CombinedRace(_new.Gender, _new.Race), setId);
|
||||||
_new = new EstManipulation(_new.Gender, _new.Race, _new.Slot, setId, newDefaultEntry);
|
_new = new EstManipulation(_new.Gender, _new.Race, _new.Slot, setId, newDefaultEntry);
|
||||||
|
|
@ -656,7 +656,7 @@ public partial class ModEditWindow
|
||||||
|
|
||||||
// Identifier
|
// Identifier
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
if (IdInput("##gmpId", IdWidth, _new.SetId, out var setId, 1, ExpandedEqpGmpBase.Count - 1, _new.SetId <= 1))
|
if (IdInput("##gmpId", IdWidth, _new.SetId.Id, out var setId, 1, ExpandedEqpGmpBase.Count - 1, _new.SetId <= 1))
|
||||||
_new = new GmpManipulation(ExpandedGmpFile.GetDefault(metaFileManager, setId), setId);
|
_new = new GmpManipulation(ExpandedGmpFile.GetDefault(metaFileManager, setId), setId);
|
||||||
|
|
||||||
ImGuiUtil.HoverTooltip(ModelSetIdTooltip);
|
ImGuiUtil.HoverTooltip(ModelSetIdTooltip);
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ public class ChangedItemDrawer : IDisposable
|
||||||
private readonly ExcelSheet<Item> _items;
|
private readonly ExcelSheet<Item> _items;
|
||||||
private readonly CommunicatorService _communicator;
|
private readonly CommunicatorService _communicator;
|
||||||
private readonly Dictionary<ChangedItemIcon, TextureWrap> _icons = new(16);
|
private readonly Dictionary<ChangedItemIcon, TextureWrap> _icons = new(16);
|
||||||
private float _smallestIconWidth = 0;
|
private float _smallestIconWidth;
|
||||||
|
|
||||||
public ChangedItemDrawer(UiBuilder uiBuilder, DataManager gameData, CommunicatorService communicator, Configuration config)
|
public ChangedItemDrawer(UiBuilder uiBuilder, DataManager gameData, CommunicatorService communicator, Configuration config)
|
||||||
{
|
{
|
||||||
|
|
@ -265,7 +265,7 @@ public class ChangedItemDrawer : IDisposable
|
||||||
switch (obj)
|
switch (obj)
|
||||||
{
|
{
|
||||||
case EquipItem it:
|
case EquipItem it:
|
||||||
text = it.WeaponType == 0 ? $"({it.ModelId.Value}-{it.Variant})" : $"({it.ModelId.Value}-{it.WeaponType.Value}-{it.Variant})";
|
text = it.ModelString;
|
||||||
return true;
|
return true;
|
||||||
case ModelChara m:
|
case ModelChara m:
|
||||||
text = $"({((CharacterBase.ModelType)m.Type).ToName()} {m.Model}-{m.Base}-{m.Variant})";
|
text = $"({((CharacterBase.ModelType)m.Type).ToName()} {m.Model}-{m.Base}-{m.Variant})";
|
||||||
|
|
@ -280,7 +280,7 @@ public class ChangedItemDrawer : IDisposable
|
||||||
private object? Convert(object? data)
|
private object? Convert(object? data)
|
||||||
{
|
{
|
||||||
if (data is EquipItem it)
|
if (data is EquipItem it)
|
||||||
return _items.GetRow(it.ItemId);
|
return _items.GetRow(it.ItemId.Id);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Group;
|
using FFXIVClientStructs.FFXIV.Client.Game.Group;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
|
|
@ -503,10 +504,10 @@ public class DebugTab : Window, ITab
|
||||||
if (table)
|
if (table)
|
||||||
for (var i = 0; i < 8; ++i)
|
for (var i = 0; i < 8; ++i)
|
||||||
{
|
{
|
||||||
var c = agent->Character(i);
|
ref var c = ref agent->Data->CharacterArraySpan[i];
|
||||||
ImGuiUtil.DrawTableColumn($"Character {i}");
|
ImGuiUtil.DrawTableColumn($"Character {i}");
|
||||||
var name = c->Name1.ToString();
|
var name = c.Name1.ToString();
|
||||||
ImGuiUtil.DrawTableColumn(name.Length == 0 ? "NULL" : $"{name} ({c->WorldId})");
|
ImGuiUtil.DrawTableColumn(name.Length == 0 ? "NULL" : $"{name} ({c.WorldId})");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue