From 3c8cef06dd25bf189e136c3649d81f2f455c6551 Mon Sep 17 00:00:00 2001 From: Loskh <1020612624@qq.com> Date: Thu, 18 Dec 2025 21:54:28 +0800 Subject: [PATCH 1/5] fix: EventItem name for Japanese client. --- Dalamud/Utility/ItemUtil.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Dalamud/Utility/ItemUtil.cs b/Dalamud/Utility/ItemUtil.cs index 5f718bcee..b632d14d7 100644 --- a/Dalamud/Utility/ItemUtil.cs +++ b/Dalamud/Utility/ItemUtil.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using Dalamud.Data; using Dalamud.Game; using Dalamud.Game.Text; + using Lumina.Excel.Sheets; using Lumina.Text; using Lumina.Text.ReadOnly; @@ -125,10 +126,15 @@ public static class ItemUtil if (IsEventItem(itemId)) { + // Only English, German, and French have a Name field. + // For other languages, the Name is an empty string, and the Singular field should be used instead. + language ??= dataManager.Language; + var useSingular = language is not (ClientLanguage.English or ClientLanguage.German or ClientLanguage.French); + return dataManager .GetExcelSheet(language) .TryGetRow(itemId, out var eventItem) - ? eventItem.Name + ? (useSingular ? eventItem.Singular : eventItem.Name) : default; } From 0b1a697d4df81fc996bfc791c9ac8464fec01512 Mon Sep 17 00:00:00 2001 From: Infi Date: Thu, 18 Dec 2025 15:38:57 +0100 Subject: [PATCH 2/5] - Comment out erroring unknown prints --- Dalamud/Interface/Internal/UiDebug.cs | 14 ++++++------ .../UiDebug2/Browsing/NodeTree.Component.cs | 22 +++++++------------ 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/Dalamud/Interface/Internal/UiDebug.cs b/Dalamud/Interface/Internal/UiDebug.cs index 1211b505d..82554995b 100644 --- a/Dalamud/Interface/Internal/UiDebug.cs +++ b/Dalamud/Interface/Internal/UiDebug.cs @@ -420,13 +420,13 @@ internal unsafe class UiDebug ImGui.SameLine(); Service.Get().Draw(textInputComponent->AtkComponentInputBase.RawString); - ImGui.Text("Text1: "u8); - ImGui.SameLine(); - Service.Get().Draw(textInputComponent->UnkText01); - - ImGui.Text("Text2: "u8); - ImGui.SameLine(); - Service.Get().Draw(textInputComponent->UnkText02); + // ImGui.Text("Text1: "u8); + // ImGui.SameLine(); + // Service.Get().Draw(textInputComponent->UnkText01); + // + // ImGui.Text("Text2: "u8); + // ImGui.SameLine(); + // Service.Get().Draw(textInputComponent->UnkText02); ImGui.Text("AvailableLines: "u8); ImGui.SameLine(); diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs index a35195498..922d226b6 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs @@ -89,20 +89,14 @@ internal unsafe class ComponentNodeTree : ResNodeTree { case TextInput: var textInputComponent = (AtkComponentTextInput*)this.Component; - ImGui.Text( - $"InputBase Text1: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.EvaluatedString.StringPtr))}"); - ImGui.Text( - $"InputBase Text2: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.RawString.StringPtr))}"); - ImGui.Text( - $"Text1: {Marshal.PtrToStringAnsi(new(textInputComponent->UnkText01.StringPtr))}"); - ImGui.Text( - $"Text2: {Marshal.PtrToStringAnsi(new(textInputComponent->UnkText02.StringPtr))}"); - ImGui.Text( - $"AvailableLines: {Marshal.PtrToStringAnsi(new(textInputComponent->AvailableLines.StringPtr))}"); - ImGui.Text( - $"HighlightedAutoTranslateOptionColorPrefix: {Marshal.PtrToStringAnsi(new(textInputComponent->HighlightedAutoTranslateOptionColorPrefix.StringPtr))}"); - ImGui.Text( - $"HighlightedAutoTranslateOptionColorSuffix: {Marshal.PtrToStringAnsi(new(textInputComponent->HighlightedAutoTranslateOptionColorSuffix.StringPtr))}"); + ImGui.Text($"InputBase Text1: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.EvaluatedString.StringPtr))}"); + ImGui.Text($"InputBase Text2: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.RawString.StringPtr))}"); + // TODO: Reenable when unknowns have been unprivated / named + // ImGui.Text($"Text1: {Marshal.PtrToStringAnsi(new(textInputComponent->UnkText01.StringPtr))}"); + // ImGui.Text($"Text2: {Marshal.PtrToStringAnsi(new(textInputComponent->UnkText02.StringPtr))}"); + ImGui.Text($"AvailableLines: {Marshal.PtrToStringAnsi(new(textInputComponent->AvailableLines.StringPtr))}"); + ImGui.Text($"HighlightedAutoTranslateOptionColorPrefix: {Marshal.PtrToStringAnsi(new(textInputComponent->HighlightedAutoTranslateOptionColorPrefix.StringPtr))}"); + ImGui.Text($"HighlightedAutoTranslateOptionColorSuffix: {Marshal.PtrToStringAnsi(new(textInputComponent->HighlightedAutoTranslateOptionColorSuffix.StringPtr))}"); break; case List: case TreeList: From 3eb65c85c06c357c3af89725f75213caa4825cbf Mon Sep 17 00:00:00 2001 From: bleatbot <106497096+bleatbot@users.noreply.github.com> Date: Thu, 18 Dec 2025 19:08:27 +0100 Subject: [PATCH 3/5] Update ClientStructs (#2524) Co-authored-by: github-actions[bot] --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index df206b5f6..a88271426 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit df206b5f61855e3ba73f93fd57bc07056698ac4a +Subproject commit a8827142678d35e62ab0c1bafe94d607271af010 From a56d2cf40be3a09c3d9105e722c2c84492e90887 Mon Sep 17 00:00:00 2001 From: goaaats Date: Thu, 18 Dec 2025 20:28:03 +0100 Subject: [PATCH 4/5] Add verifier for hook signatures This one is real bad, so we should make sure everyone using a canonical signature --- Dalamud/Dalamud.cs | 6 + Dalamud/Hooking/Hook.cs | 3 + .../Verification/HookVerificationException.cs | 41 +++++++ .../Internal/Verification/HookVerifier.cs | 107 ++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 Dalamud/Hooking/Internal/Verification/HookVerificationException.cs create mode 100644 Dalamud/Hooking/Internal/Verification/HookVerifier.cs diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index a411883d5..2d32b8e8a 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Dalamud.Common; using Dalamud.Configuration.Internal; using Dalamud.Game; +using Dalamud.Hooking.Internal.Verification; using Dalamud.Plugin.Internal; using Dalamud.Storage; using Dalamud.Utility; @@ -73,6 +74,11 @@ internal sealed unsafe class Dalamud : IServiceType scanner, Localization.FromAssets(info.AssetDirectory!, configuration.LanguageOverride)); + using (Timings.Start("HookVerifier Init")) + { + HookVerifier.Initialize(scanner); + } + // Set up FFXIVClientStructs this.SetupClientStructsResolver(cacheDir); diff --git a/Dalamud/Hooking/Hook.cs b/Dalamud/Hooking/Hook.cs index 1cd3ef91d..b8fd78b4f 100644 --- a/Dalamud/Hooking/Hook.cs +++ b/Dalamud/Hooking/Hook.cs @@ -4,6 +4,7 @@ using System.Runtime.InteropServices; using Dalamud.Configuration.Internal; using Dalamud.Hooking.Internal; +using Dalamud.Hooking.Internal.Verification; namespace Dalamud.Hooking; @@ -230,6 +231,8 @@ public abstract class Hook : IDalamudHook where T : Delegate if (EnvironmentConfiguration.DalamudForceMinHook) useMinHook = true; + HookVerifier.Verify(procAddress); + procAddress = HookManager.FollowJmp(procAddress); if (useMinHook) return new MinHookHook(procAddress, detour, Assembly.GetCallingAssembly()); diff --git a/Dalamud/Hooking/Internal/Verification/HookVerificationException.cs b/Dalamud/Hooking/Internal/Verification/HookVerificationException.cs new file mode 100644 index 000000000..c43b5d540 --- /dev/null +++ b/Dalamud/Hooking/Internal/Verification/HookVerificationException.cs @@ -0,0 +1,41 @@ +using System.Linq; + +namespace Dalamud.Hooking.Internal.Verification; + +/// +/// Exception thrown when a provided delegate for a hook does not match a known delegate. +/// +public class HookVerificationException : Exception +{ + private HookVerificationException(string message) + : base(message) + { + } + + /// + /// Create a new exception. + /// + /// The address of the function that is being hooked. + /// The delegate passed by the user. + /// The delegate we think is correct. + /// Additional context to show to the user. + /// The created exception. + internal static HookVerificationException Create(IntPtr address, Type passed, Type enforced, string message) + { + return new HookVerificationException( + $"Hook verification failed for address 0x{address.ToInt64():X}\n\n" + + $"Why: {message}\n" + + $"Passed Delegate: {GetSignature(passed)}\n" + + $"Correct Delegate: {GetSignature(enforced)}\n\n" + + "The hook delegate must exactly match the provided signature to prevent memory corruption and wrong data passed to originals."); + } + + private static string GetSignature(Type delegateType) + { + var method = delegateType.GetMethod("Invoke"); + if (method == null) return delegateType.Name; + + var parameters = string.Join(", ", method.GetParameters().Select(p => p.ParameterType.Name)); + return $"{method.ReturnType.Name} ({parameters})"; + } +} diff --git a/Dalamud/Hooking/Internal/Verification/HookVerifier.cs b/Dalamud/Hooking/Internal/Verification/HookVerifier.cs new file mode 100644 index 000000000..ad68ae38e --- /dev/null +++ b/Dalamud/Hooking/Internal/Verification/HookVerifier.cs @@ -0,0 +1,107 @@ +using System.Linq; + +using Dalamud.Game; +using Dalamud.Logging.Internal; + +namespace Dalamud.Hooking.Internal.Verification; + +/// +/// Global utility that can verify whether hook delegates are correctly declared. +/// Initialized out-of-band, since Hook is instantiated all over the place without a service, so this cannot be +/// a service either. +/// +internal static class HookVerifier +{ + private static readonly ModuleLog Log = new("HookVerifier"); + + private static readonly VerificationEntry[] ToVerify = + [ + new( + "ActorControlSelf", + "E8 ?? ?? ?? ?? 0F B7 0B 83 E9 64", + typeof(ActorControlSelfDelegate), + "Signature changed in Patch 7.4") // 7.4 (new parameters) + ]; + + private delegate void ActorControlSelfDelegate(uint category, uint eventId, uint param1, uint param2, uint param3, uint param4, uint param5, uint param6, uint param7, uint param8, ulong targetId, byte param9); + + /// + /// Initializes a new instance of the class. + /// + /// Process to scan in. + public static void Initialize(TargetSigScanner scanner) + { + foreach (var entry in ToVerify) + { + if (!scanner.TryScanText(entry.Signature, out var address)) + { + Log.Error("Could not resolve signature for hook {Name} ({Sig})", entry.Name, entry.Signature); + continue; + } + + entry.Address = address; + } + } + + /// + /// Verify the hook with the provided address and exception. + /// + /// The address of the function we are hooking. + /// The delegate type passed by the creator of the hook. + /// Exception thrown when we think the hook is not correctly declared. + public static void Verify(IntPtr address) where T : Delegate + { + var entry = ToVerify.FirstOrDefault(x => x.Address == address); + + // Nothing to verify for this hook? + if (entry == null) + { + return; + } + + var passedType = typeof(T); + + // Directly compare delegates + if (passedType == entry.TargetDelegateType) + { + return; + } + + var passedInvoke = passedType.GetMethod("Invoke")!; + var enforcedInvoke = entry.TargetDelegateType.GetMethod("Invoke")!; + + // Compare Return Type + var mismatch = passedInvoke.ReturnType != enforcedInvoke.ReturnType; + + // Compare Parameter Count + var passedParams = passedInvoke.GetParameters(); + var enforcedParams = enforcedInvoke.GetParameters(); + + if (passedParams.Length != enforcedParams.Length) + { + mismatch = true; + } + else + { + // Compare Parameter Types + for (var i = 0; i < passedParams.Length; i++) + { + if (passedParams[i].ParameterType != enforcedParams[i].ParameterType) + { + mismatch = true; + break; + } + } + } + + if (mismatch) + { + throw HookVerificationException.Create(address, passedType, entry.TargetDelegateType, entry.Message); + } + } + + private record VerificationEntry(string Name, string Signature, Type TargetDelegateType, string Message) + { + public nint Address { get; set; } + } +} From a2add0369ee3f700c7ab14ee4eace522a255f8aa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 18 Dec 2025 19:41:52 +0000 Subject: [PATCH 5/5] Update ClientStructs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index a88271426..b309d1ee1 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit a8827142678d35e62ab0c1bafe94d607271af010 +Subproject commit b309d1ee1f5445678a9ffffc51555a3a089f72d8