mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Get rid off EQDP files
This commit is contained in:
parent
9ecc4ab46d
commit
600fd2ecd3
23 changed files with 192 additions and 246 deletions
|
|
@ -1,8 +1,5 @@
|
||||||
using OtterGui;
|
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Interop.Services;
|
|
||||||
using Penumbra.Interop.Structs;
|
|
||||||
using Penumbra.Meta;
|
using Penumbra.Meta;
|
||||||
using Penumbra.Meta.Files;
|
using Penumbra.Meta.Files;
|
||||||
using Penumbra.Meta.Manipulations;
|
using Penumbra.Meta.Manipulations;
|
||||||
|
|
@ -11,108 +8,60 @@ namespace Penumbra.Collections.Cache;
|
||||||
|
|
||||||
public sealed class EqdpCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase<EqdpIdentifier, EqdpEntry>(manager, collection)
|
public sealed class EqdpCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase<EqdpIdentifier, EqdpEntry>(manager, collection)
|
||||||
{
|
{
|
||||||
private readonly ExpandedEqdpFile?[] _eqdpFiles = new ExpandedEqdpFile[CharacterUtilityData.EqdpIndices.Length]; // TODO: female Hrothgar
|
private readonly Dictionary<(PrimaryId Id, GenderRace GenderRace, bool Accessory), EqdpEntry> _fullEntries = [];
|
||||||
|
|
||||||
public override void SetFiles()
|
public override void SetFiles()
|
||||||
{
|
{ }
|
||||||
for (var i = 0; i < CharacterUtilityData.EqdpIndices.Length; ++i)
|
|
||||||
Manager.SetFile(_eqdpFiles[i], CharacterUtilityData.EqdpIndices[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetFile(MetaIndex index)
|
public bool TryGetFullEntry(PrimaryId id, GenderRace genderRace, bool accessory, out EqdpEntry entry)
|
||||||
{
|
=> _fullEntries.TryGetValue((id, genderRace, accessory), out entry);
|
||||||
var i = CharacterUtilityData.EqdpIndices.IndexOf(index);
|
|
||||||
if (i != -1)
|
|
||||||
Manager.SetFile(_eqdpFiles[i], index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetFiles()
|
|
||||||
{
|
|
||||||
foreach (var t in CharacterUtilityData.EqdpIndices)
|
|
||||||
Manager.SetFile(null, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void IncorporateChangesInternal()
|
protected override void IncorporateChangesInternal()
|
||||||
{
|
{ }
|
||||||
foreach (var (identifier, (_, entry)) in this)
|
|
||||||
Apply(GetFile(identifier)!, identifier, entry);
|
|
||||||
|
|
||||||
Penumbra.Log.Verbose($"{Collection.AnonymizedName}: Loaded {Count} delayed EQDP manipulations.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExpandedEqdpFile? EqdpFile(GenderRace race, bool accessory)
|
|
||||||
=> _eqdpFiles[Array.IndexOf(CharacterUtilityData.EqdpIndices, CharacterUtilityData.EqdpIdx(race, accessory))]; // TODO: female Hrothgar
|
|
||||||
|
|
||||||
public MetaList.MetaReverter? TemporarilySetFile(GenderRace genderRace, bool accessory)
|
|
||||||
{
|
|
||||||
var idx = CharacterUtilityData.EqdpIdx(genderRace, accessory);
|
|
||||||
if (idx < 0)
|
|
||||||
{
|
|
||||||
Penumbra.Log.Warning($"Invalid Gender, Race or Accessory for EQDP file {genderRace}, {accessory}.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var i = CharacterUtilityData.EqdpIndices.IndexOf(idx);
|
|
||||||
if (i < 0)
|
|
||||||
{
|
|
||||||
Penumbra.Log.Warning($"Invalid Gender, Race or Accessory for EQDP file {genderRace}, {accessory}.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Manager.TemporarilySetFile(_eqdpFiles[i], idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
foreach (var file in _eqdpFiles.OfType<ExpandedEqdpFile>())
|
|
||||||
{
|
|
||||||
var relevant = CharacterUtility.RelevantIndices[file.Index.Value];
|
|
||||||
file.Reset(Keys.Where(m => m.FileIndex() == relevant).Select(m => m.SetId));
|
|
||||||
}
|
|
||||||
|
|
||||||
Clear();
|
Clear();
|
||||||
|
_fullEntries.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyModInternal(EqdpIdentifier identifier, EqdpEntry entry)
|
protected override void ApplyModInternal(EqdpIdentifier identifier, EqdpEntry entry)
|
||||||
{
|
{
|
||||||
if (GetFile(identifier) is { } file)
|
var tuple = (identifier.SetId, identifier.GenderRace, identifier.Slot.IsAccessory());
|
||||||
Apply(file, identifier, entry);
|
var mask = Eqdp.Mask(identifier.Slot);
|
||||||
|
if (!_fullEntries.TryGetValue(tuple, out var currentEntry))
|
||||||
|
currentEntry = ExpandedEqdpFile.GetDefault(Manager, identifier);
|
||||||
|
|
||||||
|
_fullEntries[tuple] = (currentEntry & ~mask) | (entry & mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void RevertModInternal(EqdpIdentifier identifier)
|
protected override void RevertModInternal(EqdpIdentifier identifier)
|
||||||
{
|
{
|
||||||
if (GetFile(identifier) is { } file)
|
var tuple = (identifier.SetId, identifier.GenderRace, identifier.Slot.IsAccessory());
|
||||||
Apply(file, identifier, ExpandedEqdpFile.GetDefault(Manager, identifier));
|
var mask = Eqdp.Mask(identifier.Slot);
|
||||||
}
|
|
||||||
|
|
||||||
public static bool Apply(ExpandedEqdpFile file, EqdpIdentifier identifier, EqdpEntry entry)
|
if (_fullEntries.TryGetValue(tuple, out var currentEntry))
|
||||||
{
|
{
|
||||||
var origEntry = file[identifier.SetId];
|
var def = ExpandedEqdpFile.GetDefault(Manager, identifier);
|
||||||
var mask = Eqdp.Mask(identifier.Slot);
|
var newEntry = (currentEntry & ~mask) | (def & mask);
|
||||||
if ((origEntry & mask) == entry)
|
if (currentEntry != newEntry)
|
||||||
return false;
|
{
|
||||||
|
_fullEntries[tuple] = newEntry;
|
||||||
file[identifier.SetId] = (origEntry & ~mask) | entry;
|
}
|
||||||
return true;
|
else
|
||||||
|
{
|
||||||
|
var slots = tuple.Item3 ? EquipSlotExtensions.AccessorySlots : EquipSlotExtensions.EquipmentSlots;
|
||||||
|
if (slots.All(s => !ContainsKey(identifier with { Slot = s })))
|
||||||
|
_fullEntries.Remove(tuple);
|
||||||
|
else
|
||||||
|
_fullEntries[tuple] = newEntry;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool _)
|
protected override void Dispose(bool _)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < _eqdpFiles.Length; ++i)
|
|
||||||
{
|
|
||||||
_eqdpFiles[i]?.Dispose();
|
|
||||||
_eqdpFiles[i] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Clear();
|
Clear();
|
||||||
}
|
_fullEntries.Clear();
|
||||||
|
|
||||||
private ExpandedEqdpFile? GetFile(EqdpIdentifier identifier)
|
|
||||||
{
|
|
||||||
if (!Manager.CharacterUtility.Ready)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var index = Array.IndexOf(CharacterUtilityData.EqdpIndices, identifier.FileIndex());
|
|
||||||
return _eqdpFiles[index] ??= new ExpandedEqdpFile(Manager, identifier.GenderRace, identifier.Slot.IsAccessory());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Interop.Services;
|
|
||||||
using Penumbra.Interop.Structs;
|
|
||||||
using Penumbra.Meta;
|
using Penumbra.Meta;
|
||||||
using Penumbra.Meta.Manipulations;
|
using Penumbra.Meta.Manipulations;
|
||||||
using Penumbra.Mods.Editor;
|
using Penumbra.Mods.Editor;
|
||||||
|
|
@ -115,48 +113,18 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
|
||||||
~MetaCache()
|
~MetaCache()
|
||||||
=> Dispose();
|
=> Dispose();
|
||||||
|
|
||||||
/// <summary> Set a single file. </summary>
|
|
||||||
public void SetFile(MetaIndex metaIndex)
|
|
||||||
{
|
|
||||||
switch (metaIndex)
|
|
||||||
{
|
|
||||||
case MetaIndex.Eqp:
|
|
||||||
break;
|
|
||||||
case MetaIndex.Gmp:
|
|
||||||
break;
|
|
||||||
case MetaIndex.HumanCmp:
|
|
||||||
Rsp.SetFiles();
|
|
||||||
break;
|
|
||||||
case MetaIndex.FaceEst:
|
|
||||||
case MetaIndex.HairEst:
|
|
||||||
case MetaIndex.HeadEst:
|
|
||||||
case MetaIndex.BodyEst:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Eqdp.SetFile(metaIndex);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Set the currently relevant IMC files for the collection cache. </summary>
|
/// <summary> Set the currently relevant IMC files for the collection cache. </summary>
|
||||||
public void SetImcFiles(bool fromFullCompute)
|
public void SetImcFiles(bool fromFullCompute)
|
||||||
=> Imc.SetFiles(fromFullCompute);
|
=> Imc.SetFiles(fromFullCompute);
|
||||||
|
|
||||||
public MetaList.MetaReverter? TemporarilySetEqdpFile(GenderRace genderRace, bool accessory)
|
|
||||||
=> Eqdp.TemporarilySetFile(genderRace, accessory);
|
|
||||||
|
|
||||||
/// <summary> Try to obtain a manipulated IMC file. </summary>
|
/// <summary> Try to obtain a manipulated IMC file. </summary>
|
||||||
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out Meta.Files.ImcFile? file)
|
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out Meta.Files.ImcFile? file)
|
||||||
=> Imc.GetFile(path, out file);
|
=> Imc.GetFile(path, out file);
|
||||||
|
|
||||||
internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, PrimaryId primaryId)
|
internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, PrimaryId primaryId)
|
||||||
{
|
=> Eqdp.TryGetFullEntry(primaryId, race, accessory, out var entry)
|
||||||
var eqdpFile = Eqdp.EqdpFile(race, accessory);
|
? entry
|
||||||
if (eqdpFile != null)
|
: Meta.Files.ExpandedEqdpFile.GetDefault(manager, race, accessory, primaryId);
|
||||||
return primaryId.Id < eqdpFile.Count ? eqdpFile[primaryId] : default;
|
|
||||||
|
|
||||||
return Meta.Files.ExpandedEqdpFile.GetDefault(manager, race, accessory, primaryId);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal EstEntry GetEstEntry(EstType type, GenderRace genderRace, PrimaryId primaryId)
|
internal EstEntry GetEstEntry(EstType type, GenderRace genderRace, PrimaryId primaryId)
|
||||||
=> Est.GetEstEntry(new EstIdentifier(primaryId, type, genderRace));
|
=> Est.GetEstEntry(new EstIdentifier(primaryId, type, genderRace));
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,9 @@
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
using Penumbra.GameData.Enums;
|
|
||||||
using Penumbra.Mods;
|
using Penumbra.Mods;
|
||||||
using Penumbra.Interop.Structs;
|
|
||||||
using Penumbra.Meta.Files;
|
using Penumbra.Meta.Files;
|
||||||
using Penumbra.Meta.Manipulations;
|
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
using Penumbra.Collections.Cache;
|
using Penumbra.Collections.Cache;
|
||||||
using Penumbra.Interop.Services;
|
|
||||||
using Penumbra.Mods.Editor;
|
using Penumbra.Mods.Editor;
|
||||||
using Penumbra.GameData.Structs;
|
|
||||||
|
|
||||||
namespace Penumbra.Collections;
|
namespace Penumbra.Collections;
|
||||||
|
|
||||||
|
|
@ -68,35 +63,4 @@ public partial class ModCollection
|
||||||
|
|
||||||
internal SingleArray<ModConflicts> Conflicts(Mod mod)
|
internal SingleArray<ModConflicts> Conflicts(Mod mod)
|
||||||
=> _cache?.Conflicts(mod) ?? new SingleArray<ModConflicts>();
|
=> _cache?.Conflicts(mod) ?? new SingleArray<ModConflicts>();
|
||||||
|
|
||||||
public void SetFiles(CharacterUtility utility)
|
|
||||||
{
|
|
||||||
if (_cache == null)
|
|
||||||
{
|
|
||||||
utility.ResetAll();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_cache.Meta.SetFiles();
|
|
||||||
Penumbra.Log.Debug($"Set CharacterUtility resources for collection {Identifier}.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetMetaFile(CharacterUtility utility, MetaIndex idx)
|
|
||||||
{
|
|
||||||
if (_cache == null)
|
|
||||||
utility.ResetResource(idx);
|
|
||||||
else
|
|
||||||
_cache.Meta.SetFile(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used for short periods of changed files.
|
|
||||||
public MetaList.MetaReverter? TemporarilySetEqdpFile(CharacterUtility utility, GenderRace genderRace, bool accessory)
|
|
||||||
{
|
|
||||||
if (_cache != null)
|
|
||||||
return _cache?.Meta.TemporarilySetEqdpFile(genderRace, accessory);
|
|
||||||
|
|
||||||
var idx = CharacterUtilityData.EqdpIdx(genderRace, accessory);
|
|
||||||
return idx >= 0 ? utility.TemporarilyResetResource(idx) : null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,11 @@ public sealed unsafe class CalculateHeight : FastHook<CalculateHeight.Delegate>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private ulong Detour(Character* character)
|
private ulong Detour(Character* character)
|
||||||
{
|
{
|
||||||
_metaState.RspCollection = _collectionResolver.IdentifyCollection((GameObject*)character, true);
|
var collection = _collectionResolver.IdentifyCollection((GameObject*)character, true);
|
||||||
|
_metaState.RspCollection.Push(collection);
|
||||||
var ret = Task.Result.Original.Invoke(character);
|
var ret = Task.Result.Original.Invoke(character);
|
||||||
Penumbra.Log.Excessive($"[Calculate Height] Invoked on {(nint)character:X} -> {ret}.");
|
Penumbra.Log.Excessive($"[Calculate Height] Invoked on {(nint)character:X} -> {ret}.");
|
||||||
_metaState.RspCollection = ResolveData.Invalid;
|
_metaState.RspCollection.Pop();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
using Penumbra.Collections;
|
using Penumbra.Collections;
|
||||||
using Penumbra.GameData;
|
using Penumbra.GameData;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Interop.PathResolving;
|
using Penumbra.Interop.PathResolving;
|
||||||
|
|
||||||
namespace Penumbra.Interop.Hooks.Meta;
|
namespace Penumbra.Interop.Hooks.Meta;
|
||||||
|
|
||||||
public sealed unsafe class ChangeCustomize : FastHook<ChangeCustomize.Delegate>
|
public sealed unsafe class ChangeCustomize : FastHook<ChangeCustomize.Delegate>
|
||||||
{
|
{
|
||||||
private readonly CollectionResolver _collectionResolver;
|
private readonly CollectionResolver _collectionResolver;
|
||||||
|
|
@ -24,14 +24,15 @@ public sealed unsafe class ChangeCustomize : FastHook<ChangeCustomize.Delegate>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private bool Detour(Human* human, CustomizeArray* data, byte skipEquipment)
|
private bool Detour(Human* human, CustomizeArray* data, byte skipEquipment)
|
||||||
{
|
{
|
||||||
_metaState.CustomizeChangeCollection = _collectionResolver.IdentifyCollection((DrawObject*)human, true);
|
var collection = _collectionResolver.IdentifyCollection((DrawObject*)human, true);
|
||||||
_metaState.RspCollection = _metaState.CustomizeChangeCollection;
|
_metaState.CustomizeChangeCollection = collection;
|
||||||
|
_metaState.RspCollection.Push(collection);
|
||||||
using var decal1 = _metaState.ResolveDecal(_metaState.CustomizeChangeCollection, true);
|
using var decal1 = _metaState.ResolveDecal(_metaState.CustomizeChangeCollection, true);
|
||||||
using var decal2 = _metaState.ResolveDecal(_metaState.CustomizeChangeCollection, false);
|
using var decal2 = _metaState.ResolveDecal(_metaState.CustomizeChangeCollection, false);
|
||||||
var ret = Task.Result.Original.Invoke(human, data, skipEquipment);
|
var ret = Task.Result.Original.Invoke(human, data, skipEquipment);
|
||||||
Penumbra.Log.Excessive($"[Change Customize] Invoked on {(nint)human:X} with {(nint)data:X}, {skipEquipment} -> {ret}.");
|
Penumbra.Log.Excessive($"[Change Customize] Invoked on {(nint)human:X} with {(nint)data:X}, {skipEquipment} -> {ret}.");
|
||||||
_metaState.CustomizeChangeCollection = ResolveData.Invalid;
|
_metaState.CustomizeChangeCollection = ResolveData.Invalid;
|
||||||
_metaState.RspCollection = ResolveData.Invalid;
|
_metaState.RspCollection.Pop();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
33
Penumbra/Interop/Hooks/Meta/EqdpAccessoryHook.cs
Normal file
33
Penumbra/Interop/Hooks/Meta/EqdpAccessoryHook.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
|
using OtterGui.Services;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
|
using Penumbra.Interop.PathResolving;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Hooks.Meta;
|
||||||
|
|
||||||
|
public unsafe class EqdpAccessoryHook : FastHook<EqdpAccessoryHook.Delegate>
|
||||||
|
{
|
||||||
|
public delegate void Delegate(CharacterUtility* utility, EqdpEntry* entry, uint id, uint raceCode);
|
||||||
|
|
||||||
|
private readonly MetaState _metaState;
|
||||||
|
|
||||||
|
public EqdpAccessoryHook(HookManager hooks, MetaState metaState)
|
||||||
|
{
|
||||||
|
_metaState = metaState;
|
||||||
|
Task = hooks.CreateHook<Delegate>("GetEqdpAccessoryEntry", "E8 ?? ?? ?? ?? 41 BF ?? ?? ?? ?? 83 FB", Detour, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Detour(CharacterUtility* utility, EqdpEntry* entry, uint setId, uint raceCode)
|
||||||
|
{
|
||||||
|
if (_metaState.EqdpCollection.TryPeek(out var collection)
|
||||||
|
&& collection is { Valid: true, ModCollection.MetaCache: { } cache }
|
||||||
|
&& cache.Eqdp.TryGetFullEntry(new PrimaryId((ushort)setId), (GenderRace)raceCode, true, out var newEntry))
|
||||||
|
*entry = newEntry;
|
||||||
|
else
|
||||||
|
Task.Result.Original(utility, entry, setId, raceCode);
|
||||||
|
Penumbra.Log.Information(
|
||||||
|
$"[GetEqdpAccessoryEntry] Invoked on 0x{(ulong)utility:X} with {setId}, {(GenderRace)raceCode}, returned {(ushort)*entry:B10}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
32
Penumbra/Interop/Hooks/Meta/EqdpEquipHook.cs
Normal file
32
Penumbra/Interop/Hooks/Meta/EqdpEquipHook.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
|
using OtterGui.Services;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
|
using Penumbra.Interop.PathResolving;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Hooks.Meta;
|
||||||
|
|
||||||
|
public unsafe class EqdpEquipHook : FastHook<EqdpEquipHook.Delegate>
|
||||||
|
{
|
||||||
|
public delegate void Delegate(CharacterUtility* utility, EqdpEntry* entry, uint id, uint raceCode);
|
||||||
|
|
||||||
|
private readonly MetaState _metaState;
|
||||||
|
|
||||||
|
public EqdpEquipHook(HookManager hooks, MetaState metaState)
|
||||||
|
{
|
||||||
|
_metaState = metaState;
|
||||||
|
Task = hooks.CreateHook<Delegate>("GetEqdpEquipEntry", "E8 ?? ?? ?? ?? 85 DB 75 ?? F6 45", Detour, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Detour(CharacterUtility* utility, EqdpEntry* entry, uint setId, uint raceCode)
|
||||||
|
{
|
||||||
|
if (_metaState.EqdpCollection.TryPeek(out var collection)
|
||||||
|
&& collection is { Valid: true, ModCollection.MetaCache: { } cache }
|
||||||
|
&& cache.Eqdp.TryGetFullEntry(new PrimaryId((ushort)setId), (GenderRace)raceCode, false, out var newEntry))
|
||||||
|
*entry = newEntry;
|
||||||
|
else
|
||||||
|
Task.Result.Original(utility, entry, setId, raceCode);
|
||||||
|
Penumbra.Log.Information(
|
||||||
|
$"[GetEqdpEquipEntry] Invoked on 0x{(ulong)utility:X} with {setId}, {(GenderRace)raceCode}, returned {(ushort)*entry:B10}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,7 @@ public unsafe class EqpHook : FastHook<EqpHook.Delegate>
|
||||||
|
|
||||||
private void Detour(CharacterUtility* utility, EqpEntry* flags, CharacterArmor* armor)
|
private void Detour(CharacterUtility* utility, EqpEntry* flags, CharacterArmor* armor)
|
||||||
{
|
{
|
||||||
if (_metaState.EqpCollection is { Valid: true, ModCollection.MetaCache: { } cache })
|
if (_metaState.EqpCollection.TryPeek(out var collection) && collection is { Valid: true, ModCollection.MetaCache: { } cache })
|
||||||
{
|
{
|
||||||
*flags = cache.Eqp.GetValues(armor);
|
*flags = cache.Eqp.GetValues(armor);
|
||||||
*flags = cache.GlobalEqp.Apply(*flags, armor);
|
*flags = cache.GlobalEqp.Apply(*flags, armor);
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ public class EstHook : FastHook<EstHook.Delegate>
|
||||||
private EstEntry Detour(uint genderRace, int estType, uint id)
|
private EstEntry Detour(uint genderRace, int estType, uint id)
|
||||||
{
|
{
|
||||||
EstEntry ret;
|
EstEntry ret;
|
||||||
if (_metaState.EstCollection is { Valid: true, ModCollection.MetaCache: { } cache }
|
if (_metaState.EstCollection.TryPeek(out var collection) && collection is { Valid: true, ModCollection.MetaCache: { } cache }
|
||||||
&& cache.Est.TryGetValue(Convert(genderRace, estType, id), out var entry))
|
&& cache.Est.TryGetValue(Convert(genderRace, estType, id), out var entry))
|
||||||
ret = entry.Entry;
|
ret = entry.Entry;
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,9 @@ public sealed unsafe class GetEqpIndirect : FastHook<GetEqpIndirect.Delegate>
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Penumbra.Log.Excessive($"[Get EQP Indirect] Invoked on {(nint)drawObject:X}.");
|
Penumbra.Log.Excessive($"[Get EQP Indirect] Invoked on {(nint)drawObject:X}.");
|
||||||
_metaState.EqpCollection = _collectionResolver.IdentifyCollection(drawObject, true);
|
var collection = _collectionResolver.IdentifyCollection(drawObject, true);
|
||||||
|
_metaState.EqpCollection.Push(collection);
|
||||||
Task.Result.Original(drawObject);
|
Task.Result.Original(drawObject);
|
||||||
_metaState.EqpCollection = ResolveData.Invalid;
|
_metaState.EqpCollection.Pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
using Penumbra.Collections;
|
using Penumbra.Collections;
|
||||||
using Penumbra.GameData;
|
using Penumbra.GameData;
|
||||||
using Penumbra.Interop.PathResolving;
|
using Penumbra.Interop.PathResolving;
|
||||||
|
|
||||||
namespace Penumbra.Interop.Hooks.Meta;
|
namespace Penumbra.Interop.Hooks.Meta;
|
||||||
|
|
||||||
public sealed unsafe class GetEqpIndirect2 : FastHook<GetEqpIndirect2.Delegate>
|
public sealed unsafe class GetEqpIndirect2 : FastHook<GetEqpIndirect2.Delegate>
|
||||||
{
|
{
|
||||||
private readonly CollectionResolver _collectionResolver;
|
private readonly CollectionResolver _collectionResolver;
|
||||||
|
|
@ -29,8 +29,9 @@ public sealed unsafe class GetEqpIndirect2 : FastHook<GetEqpIndirect2.Delegate>
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Penumbra.Log.Excessive($"[Get EQP Indirect 2] Invoked on {(nint)drawObject:X}.");
|
Penumbra.Log.Excessive($"[Get EQP Indirect 2] Invoked on {(nint)drawObject:X}.");
|
||||||
_metaState.EqpCollection = _collectionResolver.IdentifyCollection(drawObject, true);
|
var collection = _collectionResolver.IdentifyCollection(drawObject, true);
|
||||||
|
_metaState.EqpCollection.Push(collection);
|
||||||
Task.Result.Original(drawObject);
|
Task.Result.Original(drawObject);
|
||||||
_metaState.EqpCollection = ResolveData.Invalid;
|
_metaState.EqpCollection.Pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,15 +27,15 @@ public unsafe class GmpHook : FastHook<GmpHook.Delegate>
|
||||||
private nint Detour(nint gmpResource, uint dividedHeadId)
|
private nint Detour(nint gmpResource, uint dividedHeadId)
|
||||||
{
|
{
|
||||||
nint ret;
|
nint ret;
|
||||||
if (_metaState.GmpCollection is { Valid: true, ModCollection.MetaCache: { } cache }
|
if (_metaState.GmpCollection.TryPeek(out var collection) && collection.Collection is { Valid: true, ModCollection.MetaCache: { } cache }
|
||||||
&& cache.Gmp.TryGetValue(new GmpIdentifier(_metaState.UndividedGmpId), out var entry))
|
&& cache.Gmp.TryGetValue(new GmpIdentifier(collection.Id), out var entry))
|
||||||
{
|
{
|
||||||
if (entry.Entry.Enabled)
|
if (entry.Entry.Enabled)
|
||||||
{
|
{
|
||||||
*StablePointer.Pointer = entry.Entry.Value;
|
*StablePointer.Pointer = entry.Entry.Value;
|
||||||
// This function already gets the original ID divided by the block size, so we can compute the modulo with a single multiplication and addition.
|
// This function already gets the original ID divided by the block size, so we can compute the modulo with a single multiplication and addition.
|
||||||
// We then go backwards from our pointer because this gets added by the calling functions.
|
// We then go backwards from our pointer because this gets added by the calling functions.
|
||||||
ret = (nint)(StablePointer.Pointer - (_metaState.UndividedGmpId.Id - dividedHeadId * ExpandedEqpGmpBase.BlockSize));
|
ret = (nint)(StablePointer.Pointer - (collection.Id.Id - dividedHeadId * ExpandedEqpGmpBase.BlockSize));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,11 @@ public sealed unsafe class ModelLoadComplete : FastHook<ModelLoadComplete.Delega
|
||||||
private void Detour(DrawObject* drawObject)
|
private void Detour(DrawObject* drawObject)
|
||||||
{
|
{
|
||||||
Penumbra.Log.Excessive($"[Model Load Complete] Invoked on {(nint)drawObject:X}.");
|
Penumbra.Log.Excessive($"[Model Load Complete] Invoked on {(nint)drawObject:X}.");
|
||||||
var collection = _collectionResolver.IdentifyCollection(drawObject, true);
|
var collection = _collectionResolver.IdentifyCollection(drawObject, true);
|
||||||
using var eqdp = _metaState.ResolveEqdpData(collection.ModCollection, MetaState.GetDrawObjectGenderRace((nint)drawObject), true, true);
|
_metaState.EqpCollection.Push(collection);
|
||||||
_metaState.EqpCollection = collection;
|
_metaState.EqdpCollection.Push(collection);
|
||||||
Task.Result.Original(drawObject);
|
Task.Result.Original(drawObject);
|
||||||
_metaState.EqpCollection = ResolveData.Invalid;
|
_metaState.EqpCollection.Pop();
|
||||||
|
_metaState.EqdpCollection.Pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ public unsafe class RspBustHook : FastHook<RspBustHook.Delegate>
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = storage;
|
var ret = storage;
|
||||||
if (bodyType < 2 && _metaState.RspCollection is { Valid: true, ModCollection.MetaCache: { } cache })
|
if (bodyType < 2 && _metaState.RspCollection.TryPeek(out var collection) && collection is { Valid: true, ModCollection.MetaCache: { } cache })
|
||||||
{
|
{
|
||||||
var bustScale = bustSize / 100f;
|
var bustScale = bustSize / 100f;
|
||||||
var clan = (SubRace)(((int)race - 1) * 2 + 1 + isSecondSubRace);
|
var clan = (SubRace)(((int)race - 1) * 2 + 1 + isSecondSubRace);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.Interop.PathResolving;
|
using Penumbra.Interop.PathResolving;
|
||||||
|
|
@ -24,7 +25,7 @@ public class RspHeightHook : FastHook<RspHeightHook.Delegate>
|
||||||
private unsafe float Detour(nint cmpResource, Race race, byte gender, byte isSecondSubRace, byte bodyType, byte height)
|
private unsafe float Detour(nint cmpResource, Race race, byte gender, byte isSecondSubRace, byte bodyType, byte height)
|
||||||
{
|
{
|
||||||
float scale;
|
float scale;
|
||||||
if (bodyType < 2 && _metaState.RspCollection is { Valid: true, ModCollection.MetaCache: { } cache })
|
if (bodyType < 2 && _metaState.RspCollection.TryPeek(out var collection) && collection is { Valid: true, ModCollection.MetaCache: { } cache })
|
||||||
{
|
{
|
||||||
var clan = (SubRace)(((int)race - 1) * 2 + 1 + isSecondSubRace);
|
var clan = (SubRace)(((int)race - 1) * 2 + 1 + isSecondSubRace);
|
||||||
var (minIdent, maxIdent) = gender == 0
|
var (minIdent, maxIdent) = gender == 0
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,9 @@ public sealed unsafe class RspSetupCharacter : FastHook<RspSetupCharacter.Delega
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_metaState.RspCollection = _collectionResolver.IdentifyCollection(drawObject, true);
|
var collection = _collectionResolver.IdentifyCollection(drawObject, true);
|
||||||
|
_metaState.RspCollection.Push(collection);
|
||||||
Task.Result.Original.Invoke(drawObject, unk2, unk3, unk4, unk5);
|
Task.Result.Original.Invoke(drawObject, unk2, unk3, unk4, unk5);
|
||||||
_metaState.RspCollection = ResolveData.Invalid;
|
_metaState.RspCollection.Pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ public class RspTailHook : FastHook<RspTailHook.Delegate>
|
||||||
private unsafe float Detour(nint cmpResource, Race race, byte gender, byte isSecondSubRace, byte bodyType, byte tailLength)
|
private unsafe float Detour(nint cmpResource, Race race, byte gender, byte isSecondSubRace, byte bodyType, byte tailLength)
|
||||||
{
|
{
|
||||||
float scale;
|
float scale;
|
||||||
if (bodyType < 2 && _metaState.RspCollection is { Valid: true, ModCollection.MetaCache: { } cache })
|
if (bodyType < 2 && _metaState.RspCollection.TryPeek(out var collection) && collection is { Valid: true, ModCollection.MetaCache: { } cache })
|
||||||
{
|
{
|
||||||
var clan = (SubRace)(((int)race - 1) * 2 + 1 + isSecondSubRace);
|
var clan = (SubRace)(((int)race - 1) * 2 + 1 + isSecondSubRace);
|
||||||
var (minIdent, maxIdent) = gender == 0
|
var (minIdent, maxIdent) = gender == 0
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@ public sealed unsafe class SetupVisor : FastHook<SetupVisor.Delegate>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private byte Detour(DrawObject* drawObject, ushort modelId, byte visorState)
|
private byte Detour(DrawObject* drawObject, ushort modelId, byte visorState)
|
||||||
{
|
{
|
||||||
_metaState.GmpCollection = _collectionResolver.IdentifyCollection(drawObject, true);
|
var collection = _collectionResolver.IdentifyCollection(drawObject, true);
|
||||||
_metaState.UndividedGmpId = modelId;
|
_metaState.GmpCollection.Push((collection, modelId));
|
||||||
var ret = Task.Result.Original.Invoke(drawObject, modelId, visorState);
|
var ret = Task.Result.Original.Invoke(drawObject, modelId, visorState);
|
||||||
Penumbra.Log.Excessive($"[Setup Visor] Invoked on {(nint)drawObject:X} with {modelId}, {visorState} -> {ret}.");
|
Penumbra.Log.Excessive($"[Setup Visor] Invoked on {(nint)drawObject:X} with {modelId}, {visorState} -> {ret}.");
|
||||||
_metaState.GmpCollection = ResolveData.Invalid;
|
_metaState.GmpCollection.Pop();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,11 @@ public sealed unsafe class UpdateModel : FastHook<UpdateModel.Delegate>
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Penumbra.Log.Excessive($"[Update Model] Invoked on {(nint)drawObject:X}.");
|
Penumbra.Log.Excessive($"[Update Model] Invoked on {(nint)drawObject:X}.");
|
||||||
var collection = _collectionResolver.IdentifyCollection(drawObject, true);
|
var collection = _collectionResolver.IdentifyCollection(drawObject, true);
|
||||||
using var eqdp = _metaState.ResolveEqdpData(collection.ModCollection, MetaState.GetDrawObjectGenderRace((nint)drawObject), true, true);
|
_metaState.EqpCollection.Push(collection);
|
||||||
_metaState.EqpCollection = collection;
|
_metaState.EqdpCollection.Push(collection);
|
||||||
Task.Result.Original(drawObject);
|
Task.Result.Original(drawObject);
|
||||||
_metaState.EqpCollection = ResolveData.Invalid;
|
_metaState.EqpCollection.Pop();
|
||||||
|
_metaState.EqdpCollection.Pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
using System.Text.Unicode;
|
using System.Text.Unicode;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
using OtterGui.Classes;
|
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
using Penumbra.Collections;
|
using Penumbra.Collections;
|
||||||
using Penumbra.Interop.PathResolving;
|
using Penumbra.Interop.PathResolving;
|
||||||
using Penumbra.Meta.Manipulations;
|
|
||||||
|
|
||||||
namespace Penumbra.Interop.Hooks.Resources;
|
namespace Penumbra.Interop.Hooks.Resources;
|
||||||
|
|
||||||
|
|
@ -149,42 +147,51 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
|
||||||
|
|
||||||
private nint ResolveMdlHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex)
|
private nint ResolveMdlHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex)
|
||||||
{
|
{
|
||||||
var data = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
var collection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
||||||
using var eqdp = slotIndex > 9 || _parent.InInternalResolve
|
if (slotIndex < 10)
|
||||||
? DisposableContainer.Empty
|
_parent.MetaState.EqdpCollection.Push(collection);
|
||||||
: _parent.MetaState.ResolveEqdpData(data.ModCollection, MetaState.GetHumanGenderRace(drawObject), slotIndex < 5, slotIndex > 4);
|
|
||||||
return ResolvePath(data, _resolveMdlPathHook.Original(drawObject, pathBuffer, pathBufferSize, slotIndex));
|
var ret = ResolvePath(collection, _resolveMdlPathHook.Original(drawObject, pathBuffer, pathBufferSize, slotIndex));
|
||||||
|
if (slotIndex < 10)
|
||||||
|
_parent.MetaState.EqdpCollection.Pop();
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private nint ResolvePapHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint unkAnimationIndex, nint animationName)
|
private nint ResolvePapHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint unkAnimationIndex, nint animationName)
|
||||||
{
|
{
|
||||||
_parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
var collection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
||||||
var ret = ResolvePath(_parent.MetaState.EstCollection, _resolvePapPathHook.Original(drawObject, pathBuffer, pathBufferSize, unkAnimationIndex, animationName));
|
_parent.MetaState.EstCollection.Push(collection);
|
||||||
_parent.MetaState.EstCollection = ResolveData.Invalid;
|
var ret = ResolvePath(collection,
|
||||||
|
_resolvePapPathHook.Original(drawObject, pathBuffer, pathBufferSize, unkAnimationIndex, animationName));
|
||||||
|
_parent.MetaState.EstCollection.Pop();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private nint ResolvePhybHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex)
|
private nint ResolvePhybHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex)
|
||||||
{
|
{
|
||||||
_parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
var collection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
||||||
var ret = ResolvePath(_parent.MetaState.EstCollection, _resolvePhybPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
|
_parent.MetaState.EstCollection.Push(collection);
|
||||||
_parent.MetaState.EstCollection = ResolveData.Invalid;
|
var ret = ResolvePath(collection, _resolvePhybPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
|
||||||
|
_parent.MetaState.EstCollection.Pop();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private nint ResolveSklbHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex)
|
private nint ResolveSklbHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex)
|
||||||
{
|
{
|
||||||
_parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
var collection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
||||||
var ret = ResolvePath(_parent.MetaState.EstCollection, _resolveSklbPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
|
_parent.MetaState.EstCollection.Push(collection);
|
||||||
_parent.MetaState.EstCollection = ResolveData.Invalid;
|
var ret = ResolvePath(collection, _resolveSklbPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
|
||||||
|
_parent.MetaState.EstCollection.Pop();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private nint ResolveSkpHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex)
|
private nint ResolveSkpHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex)
|
||||||
{
|
{
|
||||||
_parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
var collection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
||||||
var ret = ResolvePath(_parent.MetaState.EstCollection, _resolveSkpPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
|
_parent.MetaState.EstCollection.Push(collection);
|
||||||
_parent.MetaState.EstCollection = ResolveData.Invalid;
|
var ret = ResolvePath(collection, _resolveSkpPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
|
||||||
|
_parent.MetaState.EstCollection.Pop();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,12 +45,14 @@ public sealed unsafe class MetaState : IDisposable
|
||||||
private readonly CharacterUtility _characterUtility;
|
private readonly CharacterUtility _characterUtility;
|
||||||
private readonly CreateCharacterBase _createCharacterBase;
|
private readonly CreateCharacterBase _createCharacterBase;
|
||||||
|
|
||||||
public ResolveData CustomizeChangeCollection = ResolveData.Invalid;
|
public ResolveData CustomizeChangeCollection = ResolveData.Invalid;
|
||||||
public ResolveData EqpCollection = ResolveData.Invalid;
|
public readonly Stack<ResolveData> EqpCollection = [];
|
||||||
public ResolveData GmpCollection = ResolveData.Invalid;
|
public readonly Stack<ResolveData> EqdpCollection = [];
|
||||||
public ResolveData EstCollection = ResolveData.Invalid;
|
public readonly Stack<ResolveData> EstCollection = [];
|
||||||
public ResolveData RspCollection = ResolveData.Invalid;
|
public readonly Stack<ResolveData> RspCollection = [];
|
||||||
public PrimaryId UndividedGmpId = 0;
|
|
||||||
|
public readonly Stack<(ResolveData Collection, PrimaryId Id)> GmpCollection = [];
|
||||||
|
|
||||||
|
|
||||||
private ResolveData _lastCreatedCollection = ResolveData.Invalid;
|
private ResolveData _lastCreatedCollection = ResolveData.Invalid;
|
||||||
private DisposableContainer _characterBaseCreateMetaChanges = DisposableContainer.Empty;
|
private DisposableContainer _characterBaseCreateMetaChanges = DisposableContainer.Empty;
|
||||||
|
|
@ -82,21 +84,6 @@ public sealed unsafe class MetaState : IDisposable
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisposableContainer ResolveEqdpData(ModCollection collection, GenderRace race, bool equipment, bool accessory)
|
|
||||||
=> (equipment, accessory) switch
|
|
||||||
{
|
|
||||||
(true, true) => new DisposableContainer(race.Dependencies().SelectMany(r => new[]
|
|
||||||
{
|
|
||||||
collection.TemporarilySetEqdpFile(_characterUtility, r, false),
|
|
||||||
collection.TemporarilySetEqdpFile(_characterUtility, r, true),
|
|
||||||
})),
|
|
||||||
(true, false) => new DisposableContainer(race.Dependencies()
|
|
||||||
.Select(r => collection.TemporarilySetEqdpFile(_characterUtility, r, false))),
|
|
||||||
(false, true) => new DisposableContainer(race.Dependencies()
|
|
||||||
.Select(r => collection.TemporarilySetEqdpFile(_characterUtility, r, true))),
|
|
||||||
_ => DisposableContainer.Empty,
|
|
||||||
};
|
|
||||||
|
|
||||||
public DecalReverter ResolveDecal(ResolveData resolve, bool which)
|
public DecalReverter ResolveDecal(ResolveData resolve, bool which)
|
||||||
=> new(_config, _characterUtility, _resources, resolve, which);
|
=> new(_config, _characterUtility, _resources, resolve, which);
|
||||||
|
|
||||||
|
|
@ -130,7 +117,7 @@ public sealed unsafe class MetaState : IDisposable
|
||||||
|
|
||||||
var decal = new DecalReverter(_config, _characterUtility, _resources, _lastCreatedCollection,
|
var decal = new DecalReverter(_config, _characterUtility, _resources, _lastCreatedCollection,
|
||||||
UsesDecal(*(uint*)modelCharaId, (nint)customize));
|
UsesDecal(*(uint*)modelCharaId, (nint)customize));
|
||||||
RspCollection = _lastCreatedCollection;
|
RspCollection.Push(_lastCreatedCollection);
|
||||||
_characterBaseCreateMetaChanges.Dispose(); // Should always be empty.
|
_characterBaseCreateMetaChanges.Dispose(); // Should always be empty.
|
||||||
_characterBaseCreateMetaChanges = new DisposableContainer(decal);
|
_characterBaseCreateMetaChanges = new DisposableContainer(decal);
|
||||||
}
|
}
|
||||||
|
|
@ -142,7 +129,7 @@ public sealed unsafe class MetaState : IDisposable
|
||||||
if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero && drawObject != null)
|
if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero && drawObject != null)
|
||||||
_communicator.CreatedCharacterBase.Invoke(_lastCreatedCollection.AssociatedGameObject,
|
_communicator.CreatedCharacterBase.Invoke(_lastCreatedCollection.AssociatedGameObject,
|
||||||
_lastCreatedCollection.ModCollection, (nint)drawObject);
|
_lastCreatedCollection.ModCollection, (nint)drawObject);
|
||||||
RspCollection = ResolveData.Invalid;
|
RspCollection.Pop();
|
||||||
_lastCreatedCollection = ResolveData.Invalid;
|
_lastCreatedCollection = ResolveData.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ public unsafe class MetaList : IDisposable
|
||||||
=> SetResourceInternal(_defaultResourceData, _defaultResourceSize);
|
=> SetResourceInternal(_defaultResourceData, _defaultResourceSize);
|
||||||
|
|
||||||
private void SetResourceToDefaultCollection()
|
private void SetResourceToDefaultCollection()
|
||||||
=> _utility.Active.Default.SetMetaFile(_utility, GlobalMetaIndex);
|
{}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,6 @@ public class Penumbra : IDalamudPlugin
|
||||||
{
|
{
|
||||||
if (_characterUtility.Ready)
|
if (_characterUtility.Ready)
|
||||||
{
|
{
|
||||||
_collectionManager.Active.Default.SetFiles(_characterUtility);
|
|
||||||
_residentResources.Reload();
|
_residentResources.Reload();
|
||||||
_redrawService.RedrawAll(RedrawType.Redraw);
|
_redrawService.RedrawAll(RedrawType.Redraw);
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +152,6 @@ public class Penumbra : IDalamudPlugin
|
||||||
{
|
{
|
||||||
if (_characterUtility.Ready)
|
if (_characterUtility.Ready)
|
||||||
{
|
{
|
||||||
_characterUtility.ResetAll();
|
|
||||||
_residentResources.Reload();
|
_residentResources.Reload();
|
||||||
_redrawService.RedrawAll(RedrawType.Redraw);
|
_redrawService.RedrawAll(RedrawType.Redraw);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue