diff --git a/Penumbra/Collections/Manager/CollectionEditor.cs b/Penumbra/Collections/Manager/CollectionEditor.cs index c2b84256..4000f504 100644 --- a/Penumbra/Collections/Manager/CollectionEditor.cs +++ b/Penumbra/Collections/Manager/CollectionEditor.cs @@ -205,7 +205,7 @@ public class CollectionEditor [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private void InvokeChange(ModCollection changedCollection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx) { - _saveService.DelaySave(new ModCollectionSave(_modStorage, changedCollection)); + _saveService.QueueSave(new ModCollectionSave(_modStorage, changedCollection)); _communicator.ModSettingChanged.Invoke(changedCollection, type, mod, oldValue, groupIdx, false); RecurseInheritors(changedCollection, type, mod, oldValue, groupIdx); } diff --git a/Penumbra/Collections/Manager/CollectionStorage.cs b/Penumbra/Collections/Manager/CollectionStorage.cs index 7a60aaec..2c2b9f24 100644 --- a/Penumbra/Collections/Manager/CollectionStorage.cs +++ b/Penumbra/Collections/Manager/CollectionStorage.cs @@ -194,14 +194,14 @@ public class CollectionStorage : IReadOnlyList, IDisposable var any = collection.UnusedSettings.Count > 0; ((Dictionary)collection.UnusedSettings).Clear(); if (any) - _saveService.DelaySave(new ModCollectionSave(_modStorage, collection)); + _saveService.QueueSave(new ModCollectionSave(_modStorage, collection)); } /// Remove a specific setting for not currently-installed mods from the given collection. public void CleanUnavailableSetting(ModCollection collection, string? setting) { if (setting != null && ((Dictionary)collection.UnusedSettings).Remove(setting)) - _saveService.DelaySave(new ModCollectionSave(_modStorage, collection)); + _saveService.QueueSave(new ModCollectionSave(_modStorage, collection)); } /// @@ -304,7 +304,7 @@ public class CollectionStorage : IReadOnlyList, IDisposable break; case ModPathChangeType.Moved: foreach (var collection in this.Where(collection => collection.Settings[mod.Index] != null)) - _saveService.DelaySave(new ModCollectionSave(_modStorage, collection)); + _saveService.QueueSave(new ModCollectionSave(_modStorage, collection)); break; } } @@ -319,7 +319,7 @@ public class CollectionStorage : IReadOnlyList, IDisposable foreach (var collection in this) { if (collection.Settings[mod.Index]?.HandleChanges(type, mod, groupIdx, optionIdx, movedToIdx) ?? false) - _saveService.DelaySave(new ModCollectionSave(_modStorage, collection)); + _saveService.QueueSave(new ModCollectionSave(_modStorage, collection)); } } } diff --git a/Penumbra/Collections/Manager/InheritanceManager.cs b/Penumbra/Collections/Manager/InheritanceManager.cs index 0d8bfe3c..93dee89f 100644 --- a/Penumbra/Collections/Manager/InheritanceManager.cs +++ b/Penumbra/Collections/Manager/InheritanceManager.cs @@ -88,7 +88,7 @@ public class InheritanceManager : IDisposable var parent = inheritor.DirectlyInheritsFrom[idx]; ((List)inheritor.DirectlyInheritsFrom).RemoveAt(idx); ((List)parent.DirectParentOf).Remove(inheritor); - _saveService.DelaySave(new ModCollectionSave(_modStorage, inheritor)); + _saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor)); _communicator.CollectionInheritanceChanged.Invoke(inheritor, false); RecurseInheritanceChanges(inheritor); Penumbra.Log.Debug($"Removed {parent.AnonymizedName} from {inheritor.AnonymizedName} inheritances."); @@ -100,7 +100,7 @@ public class InheritanceManager : IDisposable if (!((List)inheritor.DirectlyInheritsFrom).Move(from, to)) return; - _saveService.DelaySave(new ModCollectionSave(_modStorage, inheritor)); + _saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor)); _communicator.CollectionInheritanceChanged.Invoke(inheritor, false); RecurseInheritanceChanges(inheritor); Penumbra.Log.Debug($"Moved {inheritor.AnonymizedName}s inheritance {from} to {to}."); @@ -116,7 +116,7 @@ public class InheritanceManager : IDisposable ((List)parent.DirectParentOf).Add(inheritor); if (invokeEvent) { - _saveService.DelaySave(new ModCollectionSave(_modStorage, inheritor)); + _saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor)); _communicator.CollectionInheritanceChanged.Invoke(inheritor, false); RecurseInheritanceChanges(inheritor); } diff --git a/Penumbra/Interop/PathResolving/AnimationHookService.cs b/Penumbra/Interop/PathResolving/AnimationHookService.cs index 8447dfa7..19d1ede8 100644 --- a/Penumbra/Interop/PathResolving/AnimationHookService.cs +++ b/Penumbra/Interop/PathResolving/AnimationHookService.cs @@ -48,7 +48,7 @@ public unsafe class AnimationHookService : IDisposable _unkMountAnimationHook.Enable(); _unkParasolAnimationHook.Enable(); _dismountHook.Enable(); - _vfxWeaponHook.Enable(); + _apricotListenerSoundPlayHook.Enable(); } public bool HandleFiles(ResourceType type, Utf8GamePath _, out ResolveData resolveData) @@ -106,7 +106,7 @@ public unsafe class AnimationHookService : IDisposable _unkMountAnimationHook.Dispose(); _unkParasolAnimationHook.Dispose(); _dismountHook.Dispose(); - _vfxWeaponHook.Dispose(); + _apricotListenerSoundPlayHook.Dispose(); } /// Characters load some of their voice lines or whatever with this function. @@ -364,23 +364,34 @@ public unsafe class AnimationHookService : IDisposable _animationLoadData.Value = last; } - [Signature("48 89 6C 24 ?? 41 54 41 56 41 57 48 81 EC", DetourName = nameof(VfxWeaponDetour))] - private readonly Hook _vfxWeaponHook = null!; + [Signature("48 89 6C 24 ?? 41 54 41 56 41 57 48 81 EC", DetourName = nameof(ApricotListenerSoundPlayDetour))] + private readonly Hook _apricotListenerSoundPlayHook = null!; - private delegate nint VfxWeaponDelegate(nint a1, nint a2, nint a3, nint a4, nint a5, nint a6); + private delegate nint ApricotListenerSoundPlayDelegate(nint a1, nint a2, nint a3, nint a4, nint a5, nint a6); - private nint VfxWeaponDetour(nint a1, nint a2, nint a3, nint a4, nint a5, nint a6) + private nint ApricotListenerSoundPlayDetour(nint a1, nint a2, nint a3, nint a4, nint a5, nint a6) { if (a6 == nint.Zero) - return _vfxWeaponHook!.Original(a1, a2, a3, a4, a5, a6); + return _apricotListenerSoundPlayHook!.Original(a1, a2, a3, a4, a5, a6); - var drawObject = ((DrawObject**)a6)[1]; - if (drawObject == null) - return _vfxWeaponHook!.Original(a1, a2, a3, a4, a5, a6); + var last = _animationLoadData.Value; + // a6 is some instance of Apricot.IInstanceListenner, in some cases we can obtain the associated caster via vfunc 1. + var gameObject = (*(delegate* unmanaged**)a6)[1](a6); + if (gameObject != null) + { + _animationLoadData.Value = _collectionResolver.IdentifyCollection(gameObject, true); + } + else + { + // for VfxListenner we can obtain the associated draw object as its first member, + // if the object has different type, drawObject will contain other values or garbage, + // but only be used in a dictionary pointer lookup, so this does not hurt. + var drawObject = ((DrawObject**)a6)[1]; + if (drawObject != null) + _animationLoadData.Value = _collectionResolver.IdentifyCollection(drawObject, true); + } - var last = _animationLoadData.Value; - _animationLoadData.Value = _collectionResolver.IdentifyCollection(drawObject, true); - var ret = _vfxWeaponHook!.Original(a1, a2, a3, a4, a5, a6); + var ret = _apricotListenerSoundPlayHook!.Original(a1, a2, a3, a4, a5, a6); _animationLoadData.Value = last; return ret; }