diff --git a/OtterGui b/OtterGui index 3e6b0857..8ba88eff 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit 3e6b085749741f35dd6732c33d0720c6a51ebb97 +Subproject commit 8ba88eff15326bb28ed5e6157f5252c114d40b5f diff --git a/Penumbra.GameData b/Penumbra.GameData index e39a04c8..fb81a0b5 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit e39a04c83b67246580492677414888357b5ebed8 +Subproject commit fb81a0b55d3c68f2b26357fac3049c79fb0c22fb diff --git a/Penumbra/Interop/Hooks/Animation/ApricotListenerSoundPlay.cs b/Penumbra/Interop/Hooks/Animation/ApricotListenerSoundPlay.cs index 44eb7ebb..8838971c 100644 --- a/Penumbra/Interop/Hooks/Animation/ApricotListenerSoundPlay.cs +++ b/Penumbra/Interop/Hooks/Animation/ApricotListenerSoundPlay.cs @@ -33,25 +33,27 @@ public sealed unsafe class ApricotListenerSoundPlayCaller : FastHook> 13) & 1) == 0) + var someIntermediate = *(nint*)(a1 + VolatileOffsets.ApricotListenerSoundPlayCaller.SomeIntermediate); + var flags = someIntermediate == nint.Zero + ? (ushort)0 + : *(ushort*)(someIntermediate + VolatileOffsets.ApricotListenerSoundPlayCaller.Flags); + if (((flags >> VolatileOffsets.ApricotListenerSoundPlayCaller.BitShift) & 1) == 0) return Task.Result.Original(a1, unused, timeOffset); Penumbra.Log.Excessive( $"[Apricot Listener Sound Play Caller] Invoked on 0x{a1:X} with {unused}, {timeOffset}."); // Fetch the IInstanceListenner (sixth argument to inlined call of SoundPlay) - var apricotIInstanceListenner = *(nint*)(someIntermediate + 0x270); + var apricotIInstanceListenner = *(nint*)(someIntermediate + VolatileOffsets.ApricotListenerSoundPlayCaller.IInstanceListenner); if (apricotIInstanceListenner == nint.Zero) return Task.Result.Original(a1, unused, timeOffset); // In some cases we can obtain the associated caster via vfunc 1. var newData = ResolveData.Invalid; - var gameObject = (*(delegate* unmanaged**)apricotIInstanceListenner)[1](apricotIInstanceListenner); + var gameObject = (*(delegate* unmanaged**)apricotIInstanceListenner)[VolatileOffsets.ApricotListenerSoundPlayCaller.CasterVFunc](apricotIInstanceListenner); if (gameObject != null) { newData = _collectionResolver.IdentifyCollection(gameObject, true); diff --git a/Penumbra/Interop/Hooks/Animation/SomePapLoad.cs b/Penumbra/Interop/Hooks/Animation/SomePapLoad.cs index 7339c397..f19e4ce2 100644 --- a/Penumbra/Interop/Hooks/Animation/SomePapLoad.cs +++ b/Penumbra/Interop/Hooks/Animation/SomePapLoad.cs @@ -31,7 +31,7 @@ public sealed unsafe class SomePapLoad : FastHook private void Detour(nint a1, int a2, nint a3, int a4) { Penumbra.Log.Excessive($"[Some PAP Load] Invoked on 0x{a1:X} with {a2}, {a3}, {a4}."); - var timelinePtr = a1 + Offsets.TimeLinePtr; + var timelinePtr = a1 + VolatileOffsets.AnimationState.TimeLinePtr; if (timelinePtr != nint.Zero) { var actorIdx = (int)(*(*(ulong**)timelinePtr + 1) >> 3); diff --git a/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs b/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs index e71d07dd..327e3d1e 100644 --- a/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs +++ b/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs @@ -1,7 +1,7 @@ +using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Object; using OtterGui.Services; using Penumbra.Interop.PathResolving; -using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character; namespace Penumbra.Interop.Hooks.Meta; @@ -13,19 +13,20 @@ public sealed unsafe class CalculateHeight : FastHook public CalculateHeight(HookManager hooks, CollectionResolver collectionResolver, MetaState metaState) { _collectionResolver = collectionResolver; - _metaState = metaState; - Task = hooks.CreateHook("Calculate Height", (nint)Character.MemberFunctionPointers.CalculateHeight, Detour, !HookOverrides.Instance.Meta.CalculateHeight); + _metaState = metaState; + Task = hooks.CreateHook("Calculate Height", (nint)HeightContainer.MemberFunctionPointers.CalculateHeight, Detour, + !HookOverrides.Instance.Meta.CalculateHeight); } - public delegate ulong Delegate(Character* character); + public delegate ulong Delegate(HeightContainer* character); [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - private ulong Detour(Character* character) + private ulong Detour(HeightContainer* container) { - var collection = _collectionResolver.IdentifyCollection((GameObject*)character, true); + var collection = _collectionResolver.IdentifyCollection((GameObject*)container->OwnerObject, true); _metaState.RspCollection.Push(collection); - var ret = Task.Result.Original.Invoke(character); - Penumbra.Log.Excessive($"[Calculate Height] Invoked on {(nint)character:X} -> {ret}."); + var ret = Task.Result.Original.Invoke(container); + Penumbra.Log.Excessive($"[Calculate Height] Invoked on {(nint)container:X} -> {ret}."); _metaState.RspCollection.Pop(); return ret; } diff --git a/Penumbra/Interop/Hooks/Meta/UpdateModel.cs b/Penumbra/Interop/Hooks/Meta/UpdateModel.cs index 72beea0e..9189ce3b 100644 --- a/Penumbra/Interop/Hooks/Meta/UpdateModel.cs +++ b/Penumbra/Interop/Hooks/Meta/UpdateModel.cs @@ -25,7 +25,7 @@ public sealed unsafe class UpdateModel : FastHook { // Shortcut because this is called all the time. // Same thing is checked at the beginning of the original function. - if (*(int*)((nint)drawObject + Offsets.UpdateModelSkip) == 0) + if (*(int*)((nint)drawObject + VolatileOffsets.UpdateModel.ShortCircuit) == 0) return; Penumbra.Log.Excessive($"[Update Model] Invoked on {(nint)drawObject:X}."); diff --git a/Penumbra/Interop/Hooks/PostProcessing/ShaderReplacementFixer.cs b/Penumbra/Interop/Hooks/PostProcessing/ShaderReplacementFixer.cs index 80892b0f..40958eb4 100644 --- a/Penumbra/Interop/Hooks/PostProcessing/ShaderReplacementFixer.cs +++ b/Penumbra/Interop/Hooks/PostProcessing/ShaderReplacementFixer.cs @@ -401,7 +401,6 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic private void ModelRendererUnkFuncDetour(CSModelRenderer* modelRenderer, ModelRendererStructs.UnkPayload* unkPayload, uint unk2, uint unk3, uint unk4, uint unk5) { - // If we don't have any on-screen instances of modded iris.shpk or others, we don't need the slow path at all. if (!Enabled || GetTotalMaterialCountForModelRendererUnk() == 0) { _modelRendererUnkFuncHook.Original(modelRenderer, unkPayload, unk2, unk3, unk4, unk5); diff --git a/Penumbra/Interop/PathResolving/CollectionResolver.cs b/Penumbra/Interop/PathResolving/CollectionResolver.cs index 36c31af3..50088008 100644 --- a/Penumbra/Interop/PathResolving/CollectionResolver.cs +++ b/Penumbra/Interop/PathResolving/CollectionResolver.cs @@ -240,7 +240,7 @@ public sealed unsafe class CollectionResolver( } // Only handle human models. - if (!IsModelHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId)) + if (!IsModelHuman((uint)actor.AsCharacter->ModelCharaId)) return null; if (actor.Customize->Data[0] == 0) diff --git a/Penumbra/Interop/ResourceTree/ResourceTree.cs b/Penumbra/Interop/ResourceTree/ResourceTree.cs index 89e0c62b..62f4febe 100644 --- a/Penumbra/Interop/ResourceTree/ResourceTree.cs +++ b/Penumbra/Interop/ResourceTree/ResourceTree.cs @@ -68,7 +68,7 @@ public class ResourceTree Unsafe.AsPointer(ref character->DrawData.EquipmentModelIds[0]), 10), _ => [], }; - ModelId = character->CharacterData.ModelCharaId; + ModelId = character->ModelCharaId; CustomizeData = character->DrawData.CustomizeData; RaceCode = human != null ? (GenderRace)human->RaceSexId : GenderRace.Unknown; diff --git a/Penumbra/Interop/Services/FontReloader.cs b/Penumbra/Interop/Services/FontReloader.cs index 4f48f08f..3a2c7022 100644 --- a/Penumbra/Interop/Services/FontReloader.cs +++ b/Penumbra/Interop/Services/FontReloader.cs @@ -43,7 +43,7 @@ public unsafe class FontReloader : IService return; _atkModule = &atkModule->AtkModule; - _reloadFontsFunc = ((delegate* unmanaged*)_atkModule->VirtualTable)[Offsets.ReloadFontsVfunc]; + _reloadFontsFunc = ((delegate* unmanaged*)_atkModule->VirtualTable)[VolatileOffsets.FontReloader.ReloadFontsVFunc]; }); } } diff --git a/Penumbra/Interop/Services/RedrawService.cs b/Penumbra/Interop/Services/RedrawService.cs index 2cdc1137..8f20ca5e 100644 --- a/Penumbra/Interop/Services/RedrawService.cs +++ b/Penumbra/Interop/Services/RedrawService.cs @@ -38,10 +38,10 @@ public unsafe partial class RedrawService : IService // VFuncs that disable and enable draw, used only for GPose actors. private static void DisableDraw(IGameObject actor) - => ((delegate* unmanaged**)actor.Address)[0][Offsets.DisableDrawVfunc](actor.Address); + => ((delegate* unmanaged**)actor.Address)[0][VolatileOffsets.RedrawService.DisableDrawVFunc](actor.Address); private static void EnableDraw(IGameObject actor) - => ((delegate* unmanaged**)actor.Address)[0][Offsets.EnableDrawVfunc](actor.Address); + => ((delegate* unmanaged**)actor.Address)[0][VolatileOffsets.RedrawService.EnableDrawVFunc](actor.Address); // Check whether we currently are in GPose. // Also clear the name list. diff --git a/Penumbra/Interop/VolatileOffsets.cs b/Penumbra/Interop/VolatileOffsets.cs new file mode 100644 index 00000000..2c6e3180 --- /dev/null +++ b/Penumbra/Interop/VolatileOffsets.cs @@ -0,0 +1,35 @@ +namespace Penumbra.Interop; + +public static class VolatileOffsets +{ + public static class ApricotListenerSoundPlayCaller + { + public const int PlayTimeOffset = 0x254; + public const int SomeIntermediate = 0x1F8; + public const int Flags = 0x4A4; + public const int IInstanceListenner = 0x270; + public const int BitShift = 13; + public const int CasterVFunc = 1; + } + + public static class AnimationState + { + public const int TimeLinePtr = 0x50; + } + + public static class UpdateModel + { + public const int ShortCircuit = 0xA2C; + } + + public static class FontReloader + { + public const int ReloadFontsVFunc = 43; + } + + public static class RedrawService + { + public const int EnableDrawVFunc = 12; + public const int DisableDrawVFunc = 13; + } +} diff --git a/Penumbra/Mods/Manager/OptionEditor/ImcModGroupEditor.cs b/Penumbra/Mods/Manager/OptionEditor/ImcModGroupEditor.cs index dc94c881..f8760625 100644 --- a/Penumbra/Mods/Manager/OptionEditor/ImcModGroupEditor.cs +++ b/Penumbra/Mods/Manager/OptionEditor/ImcModGroupEditor.cs @@ -138,7 +138,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ protected override bool MoveOption(ImcModGroup group, int optionIdxFrom, int optionIdxTo) { - if (!group.OptionData.Move(optionIdxFrom, optionIdxTo)) + if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo)) return false; group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo); diff --git a/Penumbra/Mods/Manager/OptionEditor/ModGroupEditor.cs b/Penumbra/Mods/Manager/OptionEditor/ModGroupEditor.cs index 7f18852d..d01297db 100644 --- a/Penumbra/Mods/Manager/OptionEditor/ModGroupEditor.cs +++ b/Penumbra/Mods/Manager/OptionEditor/ModGroupEditor.cs @@ -90,7 +90,7 @@ public class ModGroupEditor( { var mod = group.Mod; var idxFrom = group.GetIndex(); - if (!mod.Groups.Move(idxFrom, groupIdxTo)) + if (!mod.Groups.Move(ref idxFrom, ref groupIdxTo)) return; saveService.SaveAllOptionGroups(mod, false, config.ReplaceNonAsciiOnImport); diff --git a/Penumbra/Mods/Manager/OptionEditor/MultiModGroupEditor.cs b/Penumbra/Mods/Manager/OptionEditor/MultiModGroupEditor.cs index 74362325..2446ae80 100644 --- a/Penumbra/Mods/Manager/OptionEditor/MultiModGroupEditor.cs +++ b/Penumbra/Mods/Manager/OptionEditor/MultiModGroupEditor.cs @@ -75,7 +75,7 @@ public sealed class MultiModGroupEditor(CommunicatorService communicator, SaveSe protected override bool MoveOption(MultiModGroup group, int optionIdxFrom, int optionIdxTo) { - if (!group.OptionData.Move(optionIdxFrom, optionIdxTo)) + if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo)) return false; group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo); diff --git a/Penumbra/Mods/Manager/OptionEditor/SingleModGroupEditor.cs b/Penumbra/Mods/Manager/OptionEditor/SingleModGroupEditor.cs index 15a899a0..5fd785cf 100644 --- a/Penumbra/Mods/Manager/OptionEditor/SingleModGroupEditor.cs +++ b/Penumbra/Mods/Manager/OptionEditor/SingleModGroupEditor.cs @@ -48,7 +48,7 @@ public sealed class SingleModGroupEditor(CommunicatorService communicator, SaveS protected override bool MoveOption(SingleModGroup group, int optionIdxFrom, int optionIdxTo) { - if (!group.OptionData.Move(optionIdxFrom, optionIdxTo)) + if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo)) return false; group.DefaultSettings = group.DefaultSettings.MoveSingle(optionIdxFrom, optionIdxTo); diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index b6b19ef2..41d8f668 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -1,6 +1,5 @@ using Dalamud.Plugin; using ImGuiNET; -using Lumina.Excel.GeneratedSheets; using OtterGui; using OtterGui.Log; using OtterGui.Services; @@ -20,6 +19,7 @@ using OtterGui.Tasks; using Penumbra.UI; using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager; using Dalamud.Plugin.Services; +using Lumina.Excel.Sheets; using Penumbra.GameData.Data; using Penumbra.Interop.Hooks; using Penumbra.Interop.Hooks.ResourceLoading; @@ -111,7 +111,7 @@ public class Penumbra : IDalamudPlugin private void SetupApi() { _services.GetService(); - var itemSheet = _services.GetService().GetExcelSheet()!; + var itemSheet = _services.GetService().GetExcelSheet(); _communicatorService.ChangedItemHover.Subscribe(it => { if (it is IdentifiedItem { Item.Id.IsItem: true }) diff --git a/Penumbra/Services/MessageService.cs b/Penumbra/Services/MessageService.cs index a35a67f1..e610cb6a 100644 --- a/Penumbra/Services/MessageService.cs +++ b/Penumbra/Services/MessageService.cs @@ -4,7 +4,7 @@ using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Interface; using Dalamud.Interface.ImGuiNotification; using Dalamud.Plugin.Services; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel.Sheets; using OtterGui.Log; using OtterGui.Services; using Penumbra.Mods.Manager; @@ -16,7 +16,7 @@ namespace Penumbra.Services; public class MessageService(Logger log, IUiBuilder builder, IChatGui chat, INotificationManager notificationManager) : OtterGui.Classes.MessageService(log, builder, chat, notificationManager), IService { - public void LinkItem(Item item) + public void LinkItem(in Item item) { // @formatter:off var payloadList = new List @@ -29,7 +29,7 @@ public class MessageService(Logger log, IUiBuilder builder, IChatGui chat, INoti new TextPayload($"{(char)SeIconChar.LinkMarker}"), new UIForegroundPayload(0), new UIGlowPayload(0), - new TextPayload(item.Name), + new TextPayload(item.Name.ExtractText()), new RawPayload([0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03]), new RawPayload([0x02, 0x13, 0x02, 0xEC, 0x03]), }; diff --git a/Penumbra/UI/Tabs/Debug/DebugTab.cs b/Penumbra/UI/Tabs/Debug/DebugTab.cs index 7c6cd01e..47c2c16c 100644 --- a/Penumbra/UI/Tabs/Debug/DebugTab.cs +++ b/Penumbra/UI/Tabs/Debug/DebugTab.cs @@ -54,6 +54,9 @@ public class Diagnostics(ServiceManager provider) : IUiService return; using var table = ImRaii.Table("##data", 4, ImGuiTableFlags.RowBg); + if (!table) + return; + foreach (var type in typeof(ActorManager).Assembly.GetTypes() .Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IAsyncDataContainer)))) { diff --git a/Penumbra/UI/Tabs/Debug/HookOverrideDrawer.cs b/Penumbra/UI/Tabs/Debug/HookOverrideDrawer.cs index 7af1f884..e8ff9b9c 100644 --- a/Penumbra/UI/Tabs/Debug/HookOverrideDrawer.cs +++ b/Penumbra/UI/Tabs/Debug/HookOverrideDrawer.cs @@ -34,28 +34,49 @@ public class HookOverrideDrawer(IDalamudPluginInterface pluginInterface) : IUiSe Penumbra.Log.Error($"Could not delete hook override file at {path}:\n{ex}"); } + bool? allVisible = null; + ImGui.SameLine(); + if (ImUtf8.Button("Disable All Visible Hooks"u8)) + allVisible = true; + ImGui.SameLine(); + if (ImUtf8.Button("Enable All VisibleHooks"u8)) + allVisible = false; + bool? all = null; ImGui.SameLine(); - if (ImUtf8.Button("Disable All Hooks"u8)) + if (ImUtf8.Button("Disable All Hooks")) all = true; ImGui.SameLine(); - if (ImUtf8.Button("Enable All Hooks"u8)) + if (ImUtf8.Button("Enable All Hooks")) all = false; foreach (var propertyField in typeof(HookOverrides).GetFields().Where(f => f is { IsStatic: false, FieldType.IsValueType: true })) { using var tree = ImUtf8.TreeNode(propertyField.Name); if (!tree) - continue; - - var property = propertyField.GetValue(_overrides); - foreach (var valueField in propertyField.FieldType.GetFields()) { - var value = valueField.GetValue(property) as bool? ?? false; - if (ImUtf8.Checkbox($"Disable {valueField.Name}", ref value) || all.HasValue) + if (all.HasValue) { - valueField.SetValue(property, all ?? value); - propertyField.SetValue(_overrides, property); + var property = propertyField.GetValue(_overrides); + foreach (var valueField in propertyField.FieldType.GetFields()) + { + valueField.SetValue(property, all.Value); + propertyField.SetValue(_overrides, property); + } + } + } + else + { + allVisible ??= all; + var property = propertyField.GetValue(_overrides); + foreach (var valueField in propertyField.FieldType.GetFields()) + { + var value = valueField.GetValue(property) as bool? ?? false; + if (ImUtf8.Checkbox($"Disable {valueField.Name}", ref value) || allVisible.HasValue) + { + valueField.SetValue(property, allVisible ?? value); + propertyField.SetValue(_overrides, property); + } } } }