diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index 537ffa516..b515a36d7 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -7,6 +7,7 @@ using System.Threading; using Dalamud.Configuration.Internal; using Dalamud.Data; +using Dalamud.Fixes; using Dalamud.Game; using Dalamud.Game.ClientState; using Dalamud.Game.Command; @@ -102,6 +103,10 @@ namespace Dalamud Service.Set(new SigScanner(true)); Service.Set(); + // Initialize game fixes + var gameFixes = Service.Set(); + gameFixes.Apply(); + // Initialize FFXIVClientStructs function resolver FFXIVClientStructs.Resolver.Initialize(); Log.Information("[T1] FFXIVClientStructs initialized!"); @@ -347,6 +352,8 @@ namespace Dalamud Service.GetNullable()?.ExplicitDispose(); Service.GetNullable()?.ExplicitDispose(); + Service.GetNullable()?.ExplicitDispose(); + this.unloadSignal?.Dispose(); Service.GetNullable()?.Dispose(); diff --git a/Dalamud/EntryPoint.cs b/Dalamud/EntryPoint.cs index f8e952d7b..4894032d7 100644 --- a/Dalamud/EntryPoint.cs +++ b/Dalamud/EntryPoint.cs @@ -212,7 +212,7 @@ namespace Dalamud var levelSwitch = new LoggingLevelSwitch(LogEventLevel.Verbose); Log.Logger = new LoggerConfiguration() - .WriteTo.Async(a => a.File(logPath)) + .WriteTo.Async(a => a.File(logPath, fileSizeLimitBytes: null, buffered: false, flushToDiskInterval: TimeSpan.FromSeconds(1))) .WriteTo.Sink(SerilogEventSink.Instance) .MinimumLevel.ControlledBy(levelSwitch) .CreateLogger(); diff --git a/Dalamud/Fixes/GameFixes.cs b/Dalamud/Fixes/GameFixes.cs new file mode 100644 index 000000000..fad476150 --- /dev/null +++ b/Dalamud/Fixes/GameFixes.cs @@ -0,0 +1,43 @@ +using System; +using System.Linq; +using Serilog; + +namespace Dalamud.Fixes; + +/// +/// Class responsible for executing game fixes. +/// +internal class GameFixes : IDisposable +{ + private readonly IGameFix[] fixes = + { + new WndProcNullRefFix(), + }; + + /// + /// Apply all game fixes. + /// + public void Apply() + { + foreach (var gameFix in this.fixes) + { + try + { + gameFix.Apply(); + } + catch (Exception ex) + { + Log.Error(ex, "Could not apply game fix: {FixName}", gameFix.GetType().FullName); + } + } + } + + /// + public void Dispose() + { + foreach (var disposable in this.fixes.OfType()) + { + disposable.Dispose(); + } + } +} diff --git a/Dalamud/Fixes/IGameFix.cs b/Dalamud/Fixes/IGameFix.cs new file mode 100644 index 000000000..818c09bf4 --- /dev/null +++ b/Dalamud/Fixes/IGameFix.cs @@ -0,0 +1,14 @@ +using System; + +namespace Dalamud.Fixes; + +/// +/// Base interface to be implemented by game fixes. +/// +internal interface IGameFix +{ + /// + /// Apply the patch to the game. + /// + public void Apply(); +} diff --git a/Dalamud/Fixes/WndProcNullRefFix.cs b/Dalamud/Fixes/WndProcNullRefFix.cs new file mode 100644 index 000000000..186446a85 --- /dev/null +++ b/Dalamud/Fixes/WndProcNullRefFix.cs @@ -0,0 +1,45 @@ +using System; +using Dalamud.Game; +using Dalamud.Hooking; +using Serilog; + +namespace Dalamud.Fixes; + +/// +/// This fix is for the following issue: +/// Null reference in the game's WndProc function when certain window messages arrive +/// before an object on the game's input manager is initialized. +/// +internal class WndProcNullRefFix : IGameFix, IDisposable +{ + private AsmHook? wndProcHook; + + /// + public void Apply() + { + var sigScanner = Service.Get(); + + if (!sigScanner.TryScanText("E8 ?? ?? ?? ?? 48 83 38 00 74 14", out var patchAddress)) + { + Log.Error("Failed to find WndProc patch address"); + return; + } + + Log.Information($"Applying WndProcNullRefFix at {patchAddress:X}"); + + var patchAsm = new byte[] + { + 0x48, 0x85, 0xc0, // test rax, rax + 0x74, 0x15, // jz +0x1A + }; + + this.wndProcHook = new AsmHook(patchAddress, patchAsm, "WndProcNullRefFix"); + this.wndProcHook.Enable(); + } + + /// + public void Dispose() + { + this.wndProcHook?.Dispose(); + } +} diff --git a/Dalamud/Interface/Internal/Windows/IMEWindow.cs b/Dalamud/Interface/Internal/Windows/IMEWindow.cs index e987047a1..f07c56202 100644 --- a/Dalamud/Interface/Internal/Windows/IMEWindow.cs +++ b/Dalamud/Interface/Internal/Windows/IMEWindow.cs @@ -1,7 +1,7 @@ using System.Numerics; + using Dalamud.Game.ClientState.Keys; using Dalamud.Game.Gui.Internal; -using Dalamud.Interface.Colors; using Dalamud.Interface.Windowing; using ImGuiNET; diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index 3f676cd82..b973ea296 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -140,7 +140,7 @@ namespace Dalamud.Plugin /// /// Gets a value indicating whether Dalamud is running in Debug mode or the /xldev menu is open. This can occur on release builds. /// - public bool IsDevMenuOpen => Service.GetNullable() is {IsDevMenuOpen: true}; // Can be null during boot + public bool IsDevMenuOpen => Service.GetNullable() is { IsDevMenuOpen: true }; // Can be null during boot /// /// Gets a value indicating whether a debugger is attached.