diff --git a/Penumbra/Collections/Cache/MetaCache.cs b/Penumbra/Collections/Cache/MetaCache.cs
index 014c7552..e6083351 100644
--- a/Penumbra/Collections/Cache/MetaCache.cs
+++ b/Penumbra/Collections/Cache/MetaCache.cs
@@ -145,9 +145,6 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
public MetaList.MetaReverter? TemporarilySetEqdpFile(GenderRace genderRace, bool accessory)
=> Eqdp.TemporarilySetFile(genderRace, accessory);
- public MetaList.MetaReverter TemporarilySetCmpFile()
- => Rsp.TemporarilySetFile();
-
/// Try to obtain a manipulated IMC file.
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out Meta.Files.ImcFile? file)
=> Imc.GetFile(path, out file);
diff --git a/Penumbra/Collections/Cache/RspCache.cs b/Penumbra/Collections/Cache/RspCache.cs
index 8a5fe97d..8a983c6c 100644
--- a/Penumbra/Collections/Cache/RspCache.cs
+++ b/Penumbra/Collections/Cache/RspCache.cs
@@ -1,78 +1,26 @@
-using Penumbra.Interop.Services;
-using Penumbra.Interop.Structs;
using Penumbra.Meta;
-using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
namespace Penumbra.Collections.Cache;
public sealed class RspCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase(manager, collection)
{
- private CmpFile? _cmpFile;
-
public override void SetFiles()
- => Manager.SetFile(_cmpFile, MetaIndex.HumanCmp);
+ { }
protected override void IncorporateChangesInternal()
- {
- if (GetFile() is not { } file)
- return;
-
- foreach (var (identifier, (_, entry)) in this)
- Apply(file, identifier, entry);
-
- Penumbra.Log.Verbose($"{Collection.AnonymizedName}: Loaded {Count} delayed RSP manipulations.");
- }
-
- public MetaList.MetaReverter TemporarilySetFile()
- => Manager.TemporarilySetFile(_cmpFile, MetaIndex.HumanCmp);
+ { }
public void Reset()
- {
- if (_cmpFile == null)
- return;
-
- _cmpFile.Reset(Keys.Select(identifier => (identifier.SubRace, identifier.Attribute)));
- Clear();
- }
+ => Clear();
protected override void ApplyModInternal(RspIdentifier identifier, RspEntry entry)
- {
- if (GetFile() is { } file)
- Apply(file, identifier, entry);
- }
+ { }
protected override void RevertModInternal(RspIdentifier identifier)
- {
- if (GetFile() is { } file)
- Apply(file, identifier, CmpFile.GetDefault(Manager, identifier.SubRace, identifier.Attribute));
- }
+ { }
- public static bool Apply(CmpFile file, RspIdentifier identifier, RspEntry entry)
- {
- var value = file[identifier.SubRace, identifier.Attribute];
- if (value == entry)
- return false;
-
- file[identifier.SubRace, identifier.Attribute] = entry;
- return true;
- }
protected override void Dispose(bool _)
- {
- _cmpFile?.Dispose();
- _cmpFile = null;
- Clear();
- }
-
- private CmpFile? GetFile()
- {
- if (_cmpFile != null)
- return _cmpFile;
-
- if (!Manager.CharacterUtility.Ready)
- return null;
-
- return _cmpFile = new CmpFile(Manager);
- }
+ => Clear();
}
diff --git a/Penumbra/Collections/ModCollection.Cache.Access.cs b/Penumbra/Collections/ModCollection.Cache.Access.cs
index dba971c6..d93a0f53 100644
--- a/Penumbra/Collections/ModCollection.Cache.Access.cs
+++ b/Penumbra/Collections/ModCollection.Cache.Access.cs
@@ -99,8 +99,4 @@ public partial class ModCollection
var idx = CharacterUtilityData.EqdpIdx(genderRace, accessory);
return idx >= 0 ? utility.TemporarilyResetResource(idx) : null;
}
-
- public MetaList.MetaReverter TemporarilySetCmpFile(CharacterUtility utility)
- => _cache?.Meta.TemporarilySetCmpFile()
- ?? utility.TemporarilyResetResource(MetaIndex.HumanCmp);
}
diff --git a/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs b/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs
index 2fd87f6e..5a207491 100644
--- a/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs
+++ b/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs
@@ -1,5 +1,6 @@
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using OtterGui.Services;
+using Penumbra.Collections;
using Penumbra.Interop.PathResolving;
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
@@ -22,10 +23,10 @@ public sealed unsafe class CalculateHeight : FastHook
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private ulong Detour(Character* character)
{
- var collection = _collectionResolver.IdentifyCollection((GameObject*)character, true);
- using var cmp = _metaState.ResolveRspData(collection.ModCollection);
- var ret = Task.Result.Original.Invoke(character);
+ _metaState.RspCollection = _collectionResolver.IdentifyCollection((GameObject*)character, true);
+ var ret = Task.Result.Original.Invoke(character);
Penumbra.Log.Excessive($"[Calculate Height] Invoked on {(nint)character:X} -> {ret}.");
+ _metaState.RspCollection = ResolveData.Invalid;
return ret;
}
}
diff --git a/Penumbra/Interop/Hooks/Meta/ChangeCustomize.cs b/Penumbra/Interop/Hooks/Meta/ChangeCustomize.cs
index 2f717491..4e0a5744 100644
--- a/Penumbra/Interop/Hooks/Meta/ChangeCustomize.cs
+++ b/Penumbra/Interop/Hooks/Meta/ChangeCustomize.cs
@@ -25,12 +25,13 @@ public sealed unsafe class ChangeCustomize : FastHook
private bool Detour(Human* human, CustomizeArray* data, byte skipEquipment)
{
_metaState.CustomizeChangeCollection = _collectionResolver.IdentifyCollection((DrawObject*)human, true);
- using var cmp = _metaState.ResolveRspData(_metaState.CustomizeChangeCollection.ModCollection);
+ _metaState.RspCollection = _metaState.CustomizeChangeCollection;
using var decal1 = _metaState.ResolveDecal(_metaState.CustomizeChangeCollection, true);
using var decal2 = _metaState.ResolveDecal(_metaState.CustomizeChangeCollection, false);
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}.");
_metaState.CustomizeChangeCollection = ResolveData.Invalid;
+ _metaState.RspCollection = ResolveData.Invalid;
return ret;
}
}
diff --git a/Penumbra/Interop/Hooks/Meta/EstHook.cs b/Penumbra/Interop/Hooks/Meta/EstHook.cs
index 3fab1434..34935edb 100644
--- a/Penumbra/Interop/Hooks/Meta/EstHook.cs
+++ b/Penumbra/Interop/Hooks/Meta/EstHook.cs
@@ -27,7 +27,7 @@ public class EstHook : FastHook
else
ret = Task.Result.Original(genderRace, estType, id);
- Penumbra.Log.Information($"[GetEstEntry] Invoked with {genderRace}, {estType}, {id}, returned {ret.Value}.");
+ Penumbra.Log.Excessive($"[GetEstEntry] Invoked with {genderRace}, {estType}, {id}, returned {ret.Value}.");
return ret;
}
diff --git a/Penumbra/Interop/Hooks/Meta/RspBustHook.cs b/Penumbra/Interop/Hooks/Meta/RspBustHook.cs
new file mode 100644
index 00000000..fc1d743a
--- /dev/null
+++ b/Penumbra/Interop/Hooks/Meta/RspBustHook.cs
@@ -0,0 +1,66 @@
+using OtterGui.Services;
+using Penumbra.GameData.Enums;
+using Penumbra.Interop.PathResolving;
+using Penumbra.Meta;
+using Penumbra.Meta.Files;
+using Penumbra.Meta.Manipulations;
+
+namespace Penumbra.Interop.Hooks.Meta;
+
+public unsafe class RspBustHook : FastHook
+{
+ public delegate float* Delegate(nint cmpResource, float* storage, Race race, byte gender, byte isSecondSubRace, byte bodyType,
+ byte bustSize);
+
+ private readonly MetaState _metaState;
+ private readonly MetaFileManager _metaFileManager;
+
+ public RspBustHook(HookManager hooks, MetaState metaState, MetaFileManager metaFileManager)
+ {
+ _metaState = metaState;
+ _metaFileManager = metaFileManager;
+ Task = hooks.CreateHook("GetRspBust", "E8 ?? ?? ?? ?? F2 0F 10 44 24 ?? 8B 44 24", Detour, true);
+ }
+
+ private float* Detour(nint cmpResource, float* storage, Race race, byte gender, byte isSecondSubRace, byte bodyType, byte bustSize)
+ {
+ if (gender == 0)
+ {
+ storage[0] = 1f;
+ storage[1] = 1f;
+ storage[2] = 1f;
+ return storage;
+ }
+
+ var ret = storage;
+ if (bodyType < 2 && _metaState.RspCollection is { Valid: true, ModCollection.MetaCache: { } cache })
+ {
+ var bustScale = bustSize / 100f;
+ var clan = (SubRace)(((int)race - 1) * 2 + 1 + isSecondSubRace);
+ var ptr = CmpFile.GetDefaults(_metaFileManager, clan, RspAttribute.BustMinX);
+ storage[0] = GetValue(0, RspAttribute.BustMinX, RspAttribute.BustMaxX);
+ storage[1] = GetValue(1, RspAttribute.BustMinY, RspAttribute.BustMaxY);
+ storage[2] = GetValue(2, RspAttribute.BustMinZ, RspAttribute.BustMaxZ);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ float GetValue(int dimension, RspAttribute min, RspAttribute max)
+ {
+ var minValue = cache.Rsp.TryGetValue(new RspIdentifier(clan, min), out var minEntry)
+ ? minEntry.Entry.Value
+ : (ptr + dimension)->Value;
+ var maxValue = cache.Rsp.TryGetValue(new RspIdentifier(clan, max), out var maxEntry)
+ ? maxEntry.Entry.Value
+ : (ptr + 3 + dimension)->Value;
+ return (maxValue - minValue) * bustScale + minValue;
+ }
+ }
+ else
+ {
+ ret = Task.Result.Original(cmpResource, storage, race, gender, isSecondSubRace, bodyType, bustSize);
+ }
+
+ Penumbra.Log.Information(
+ $"[GetRspBust] Invoked on 0x{cmpResource:X} with {race}, {(Gender)(gender + 1)}, {isSecondSubRace == 1}, {bodyType}, {bustSize}, returned {storage[0]}, {storage[1]}, {storage[2]}.");
+ return ret;
+ }
+}
diff --git a/Penumbra/Interop/Hooks/Meta/RspHeightHook.cs b/Penumbra/Interop/Hooks/Meta/RspHeightHook.cs
new file mode 100644
index 00000000..883f5fc6
--- /dev/null
+++ b/Penumbra/Interop/Hooks/Meta/RspHeightHook.cs
@@ -0,0 +1,68 @@
+using OtterGui.Services;
+using Penumbra.GameData.Enums;
+using Penumbra.Interop.PathResolving;
+using Penumbra.Meta;
+using Penumbra.Meta.Files;
+using Penumbra.Meta.Manipulations;
+
+namespace Penumbra.Interop.Hooks.Meta;
+
+public class RspHeightHook : FastHook
+{
+ public delegate float Delegate(nint cmpResource, Race clan, byte gender, byte isSecondSubRace, byte bodyType, byte height);
+
+ private readonly MetaState _metaState;
+ private readonly MetaFileManager _metaFileManager;
+
+ public RspHeightHook(HookManager hooks, MetaState metaState, MetaFileManager metaFileManager)
+ {
+ _metaState = metaState;
+ _metaFileManager = metaFileManager;
+ Task = hooks.CreateHook("GetRspHeight", "E8 ?? ?? ?? ?? 48 8B 8E ?? ?? ?? ?? 44 8B CF", Detour, true);
+ }
+
+ private unsafe float Detour(nint cmpResource, Race race, byte gender, byte isSecondSubRace, byte bodyType, byte height)
+ {
+ float scale;
+ if (bodyType < 2 && _metaState.RspCollection is { Valid: true, ModCollection.MetaCache: { } cache })
+ {
+ var clan = (SubRace)(((int)race - 1) * 2 + 1 + isSecondSubRace);
+ var (minIdent, maxIdent) = gender == 0
+ ? (new RspIdentifier(clan, RspAttribute.MaleMinSize), new RspIdentifier(clan, RspAttribute.MaleMaxSize))
+ : (new RspIdentifier(clan, RspAttribute.FemaleMinSize), new RspIdentifier(clan, RspAttribute.FemaleMaxSize));
+
+ float minEntry, maxEntry;
+ if (cache.Rsp.TryGetValue(minIdent, out var min))
+ {
+ minEntry = min.Entry.Value;
+ maxEntry = cache.Rsp.TryGetValue(maxIdent, out var max)
+ ? max.Entry.Value
+ : CmpFile.GetDefault(_metaFileManager, minIdent.SubRace, maxIdent.Attribute).Value;
+ }
+ else
+ {
+ var ptr = CmpFile.GetDefaults(_metaFileManager, minIdent.SubRace, minIdent.Attribute);
+ if (cache.Rsp.TryGetValue(maxIdent, out var max))
+ {
+ minEntry = ptr->Value;
+ maxEntry = max.Entry.Value;
+ }
+ else
+ {
+ minEntry = ptr[0].Value;
+ maxEntry = ptr[1].Value;
+ }
+ }
+
+ scale = (maxEntry - minEntry) * height / 100f + minEntry;
+ }
+ else
+ {
+ scale = Task.Result.Original(cmpResource, race, gender, isSecondSubRace, bodyType, height);
+ }
+
+ Penumbra.Log.Excessive(
+ $"[GetRspHeight] Invoked on 0x{cmpResource:X} with {race}, {(Gender)(gender + 1)}, {isSecondSubRace == 1}, {bodyType}, {height}, returned {scale}.");
+ return scale;
+ }
+}
diff --git a/Penumbra/Interop/Hooks/Meta/RspSetupCharacter.cs b/Penumbra/Interop/Hooks/Meta/RspSetupCharacter.cs
index 8f8f1d78..831c99bb 100644
--- a/Penumbra/Interop/Hooks/Meta/RspSetupCharacter.cs
+++ b/Penumbra/Interop/Hooks/Meta/RspSetupCharacter.cs
@@ -1,5 +1,6 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using OtterGui.Services;
+using Penumbra.Collections;
using Penumbra.GameData;
using Penumbra.Interop.PathResolving;
@@ -30,8 +31,8 @@ public sealed unsafe class RspSetupCharacter : FastHook
+{
+ public delegate float Delegate(nint cmpResource, Race clan, byte gender, byte isSecondSubRace, byte bodyType, byte height);
+
+ private readonly MetaState _metaState;
+ private readonly MetaFileManager _metaFileManager;
+
+ public RspTailHook(HookManager hooks, MetaState metaState, MetaFileManager metaFileManager)
+ {
+ _metaState = metaState;
+ _metaFileManager = metaFileManager;
+ Task = hooks.CreateHook("GetRspTail", "E8 ?? ?? ?? ?? 0F 28 F0 48 8B 05", Detour, true);
+ }
+
+ private unsafe float Detour(nint cmpResource, Race race, byte gender, byte isSecondSubRace, byte bodyType, byte tailLength)
+ {
+ float scale;
+ if (bodyType < 2 && _metaState.RspCollection is { Valid: true, ModCollection.MetaCache: { } cache })
+ {
+ var clan = (SubRace)(((int)race - 1) * 2 + 1 + isSecondSubRace);
+ var (minIdent, maxIdent) = gender == 0
+ ? (new RspIdentifier(clan, RspAttribute.MaleMinTail), new RspIdentifier(clan, RspAttribute.MaleMaxTail))
+ : (new RspIdentifier(clan, RspAttribute.FemaleMinTail), new RspIdentifier(clan, RspAttribute.FemaleMaxTail));
+
+ float minEntry, maxEntry;
+ if (cache.Rsp.TryGetValue(minIdent, out var min))
+ {
+ minEntry = min.Entry.Value;
+ maxEntry = cache.Rsp.TryGetValue(maxIdent, out var max)
+ ? max.Entry.Value
+ : CmpFile.GetDefault(_metaFileManager, minIdent.SubRace, maxIdent.Attribute).Value;
+ }
+ else
+ {
+ var ptr = CmpFile.GetDefaults(_metaFileManager, minIdent.SubRace, minIdent.Attribute);
+ if (cache.Rsp.TryGetValue(maxIdent, out var max))
+ {
+ minEntry = ptr->Value;
+ maxEntry = max.Entry.Value;
+ }
+ else
+ {
+ minEntry = ptr[0].Value;
+ maxEntry = ptr[1].Value;
+ }
+ }
+
+ scale = (maxEntry - minEntry) * tailLength / 100f + minEntry;
+ }
+ else
+ {
+ scale = Task.Result.Original(cmpResource, race, gender, isSecondSubRace, bodyType, tailLength);
+ }
+
+ Penumbra.Log.Excessive(
+ $"[GetRspTail] Invoked on 0x{cmpResource:X} with {race}, {(Gender)(gender + 1)}, {isSecondSubRace == 1}, {bodyType}, {tailLength}, returned {scale}.");
+ return scale;
+ }
+}
diff --git a/Penumbra/Interop/Hooks/Meta/SetupVisor.cs b/Penumbra/Interop/Hooks/Meta/SetupVisor.cs
index a3e56d7f..8479968f 100644
--- a/Penumbra/Interop/Hooks/Meta/SetupVisor.cs
+++ b/Penumbra/Interop/Hooks/Meta/SetupVisor.cs
@@ -30,7 +30,7 @@ public sealed unsafe class SetupVisor : FastHook
_metaState.GmpCollection = _collectionResolver.IdentifyCollection(drawObject, true);
_metaState.UndividedGmpId = modelId;
var ret = Task.Result.Original.Invoke(drawObject, modelId, visorState);
- Penumbra.Log.Information($"[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;
return ret;
}
diff --git a/Penumbra/Interop/Hooks/Resources/ResolvePathHooksBase.cs b/Penumbra/Interop/Hooks/Resources/ResolvePathHooksBase.cs
index 6c9c1b7d..17cfa3f6 100644
--- a/Penumbra/Interop/Hooks/Resources/ResolvePathHooksBase.cs
+++ b/Penumbra/Interop/Hooks/Resources/ResolvePathHooksBase.cs
@@ -159,25 +159,33 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
private nint ResolvePapHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint unkAnimationIndex, nint animationName)
{
_parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
- return ResolvePath(_parent.MetaState.EstCollection, _resolvePapPathHook.Original(drawObject, pathBuffer, pathBufferSize, unkAnimationIndex, animationName));
+ var ret = ResolvePath(_parent.MetaState.EstCollection, _resolvePapPathHook.Original(drawObject, pathBuffer, pathBufferSize, unkAnimationIndex, animationName));
+ _parent.MetaState.EstCollection = ResolveData.Invalid;
+ return ret;
}
private nint ResolvePhybHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex)
{
_parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
- return ResolvePath(_parent.MetaState.EstCollection, _resolvePhybPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
+ var ret = ResolvePath(_parent.MetaState.EstCollection, _resolvePhybPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
+ _parent.MetaState.EstCollection = ResolveData.Invalid;
+ return ret;
}
private nint ResolveSklbHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex)
{
_parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
- return ResolvePath(_parent.MetaState.EstCollection, _resolveSklbPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
+ var ret = ResolvePath(_parent.MetaState.EstCollection, _resolveSklbPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
+ _parent.MetaState.EstCollection = ResolveData.Invalid;
+ return ret;
}
private nint ResolveSkpHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex)
{
_parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
- return ResolvePath(_parent.MetaState.EstCollection, _resolveSkpPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
+ var ret = ResolvePath(_parent.MetaState.EstCollection, _resolveSkpPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
+ _parent.MetaState.EstCollection = ResolveData.Invalid;
+ return ret;
}
private nint ResolveVfxHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex, nint unkOutParam)
diff --git a/Penumbra/Interop/PathResolving/MetaState.cs b/Penumbra/Interop/PathResolving/MetaState.cs
index 8fa09232..3da94ce3 100644
--- a/Penumbra/Interop/PathResolving/MetaState.cs
+++ b/Penumbra/Interop/PathResolving/MetaState.cs
@@ -49,6 +49,7 @@ public sealed unsafe class MetaState : IDisposable
public ResolveData EqpCollection = ResolveData.Invalid;
public ResolveData GmpCollection = ResolveData.Invalid;
public ResolveData EstCollection = ResolveData.Invalid;
+ public ResolveData RspCollection = ResolveData.Invalid;
public PrimaryId UndividedGmpId = 0;
private ResolveData _lastCreatedCollection = ResolveData.Invalid;
@@ -96,9 +97,6 @@ public sealed unsafe class MetaState : IDisposable
_ => DisposableContainer.Empty,
};
- public MetaList.MetaReverter ResolveRspData(ModCollection collection)
- => collection.TemporarilySetCmpFile(_characterUtility);
-
public DecalReverter ResolveDecal(ResolveData resolve, bool which)
=> new(_config, _characterUtility, _resources, resolve, which);
@@ -132,9 +130,9 @@ public sealed unsafe class MetaState : IDisposable
var decal = new DecalReverter(_config, _characterUtility, _resources, _lastCreatedCollection,
UsesDecal(*(uint*)modelCharaId, (nint)customize));
- var cmp = _lastCreatedCollection.ModCollection.TemporarilySetCmpFile(_characterUtility);
+ RspCollection = _lastCreatedCollection;
_characterBaseCreateMetaChanges.Dispose(); // Should always be empty.
- _characterBaseCreateMetaChanges = new DisposableContainer(decal, cmp);
+ _characterBaseCreateMetaChanges = new DisposableContainer(decal);
}
private void OnCharacterBaseCreated(ModelCharaId _1, CustomizeArray* _2, CharacterArmor* _3, CharacterBase* drawObject)
@@ -144,6 +142,7 @@ public sealed unsafe class MetaState : IDisposable
if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero && drawObject != null)
_communicator.CreatedCharacterBase.Invoke(_lastCreatedCollection.AssociatedGameObject,
_lastCreatedCollection.ModCollection, (nint)drawObject);
+ RspCollection = ResolveData.Invalid;
_lastCreatedCollection = ResolveData.Invalid;
}
diff --git a/Penumbra/Meta/Files/CmpFile.cs b/Penumbra/Meta/Files/CmpFile.cs
index 96cda496..8ca7cb80 100644
--- a/Penumbra/Meta/Files/CmpFile.cs
+++ b/Penumbra/Meta/Files/CmpFile.cs
@@ -46,6 +46,14 @@ public sealed unsafe class CmpFile : MetaBaseFile
return *(RspEntry*)(data + RacialScalingStart + ToRspIndex(subRace) * RspData.ByteSize + (int)attribute * 4);
}
+ public static RspEntry* GetDefaults(MetaFileManager manager, SubRace subRace, RspAttribute attribute)
+ {
+ {
+ var data = (byte*)manager.CharacterUtility.DefaultResource(InternalIndex).Address;
+ return (RspEntry*)(data + RacialScalingStart + ToRspIndex(subRace) * RspData.ByteSize + (int)attribute * 4);
+ }
+ }
+
private static int ToRspIndex(SubRace subRace)
=> subRace switch
{
diff --git a/Penumbra/UI/AdvancedWindow/Meta/RspMetaDrawer.cs b/Penumbra/UI/AdvancedWindow/Meta/RspMetaDrawer.cs
index 2b7904ce..be02e321 100644
--- a/Penumbra/UI/AdvancedWindow/Meta/RspMetaDrawer.cs
+++ b/Penumbra/UI/AdvancedWindow/Meta/RspMetaDrawer.cs
@@ -85,7 +85,7 @@ public sealed class RspMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
{
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
- var ret = DragInput("##rspValue"u8, [], ImUtf8.GlobalScale * 150, defaultEntry.Value, entry.Value, out var newValue,
+ var ret = DragInput("##rspValue"u8, [], ImUtf8.GlobalScale * 150, entry.Value, defaultEntry.Value, out var newValue,
RspEntry.MinValue, RspEntry.MaxValue, 0.001f, !disabled);
if (ret)
entry = new RspEntry(newValue);