mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Merge pull request #289 from ascclemens/toast
This commit is contained in:
commit
a6205d8418
5 changed files with 190 additions and 0 deletions
|
|
@ -120,6 +120,7 @@ namespace Dalamud.Game.Internal {
|
|||
private bool HandleFrameworkUpdate(IntPtr framework) {
|
||||
try {
|
||||
Gui.Chat.UpdateQueue(this);
|
||||
Gui.Toast.UpdateQueue();
|
||||
Network.UpdateQueue(this);
|
||||
} catch (Exception ex) {
|
||||
Log.Error(ex, "Exception while handling Framework::Update hook.");
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace Dalamud.Game.Internal.Gui {
|
|||
|
||||
public ChatGui Chat { get; private set; }
|
||||
public PartyFinderGui PartyFinder { get; private set; }
|
||||
public ToastGui Toast { get; private set; }
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
private delegate IntPtr SetGlobalBgmDelegate(UInt16 bgmKey, byte a2, UInt32 a3, UInt32 a4, UInt32 a5, byte a6);
|
||||
|
|
@ -68,6 +69,9 @@ namespace Dalamud.Game.Internal.Gui {
|
|||
private delegate IntPtr GetUIObjectByNameDelegate(IntPtr thisPtr, string uiName, int index);
|
||||
private readonly GetUIObjectByNameDelegate getUIObjectByName;
|
||||
|
||||
private delegate IntPtr GetUiModuleDelegate(IntPtr basePtr);
|
||||
private readonly GetUiModuleDelegate getUiModule;
|
||||
|
||||
public bool GameUiHidden { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -110,6 +114,7 @@ namespace Dalamud.Game.Internal.Gui {
|
|||
|
||||
Chat = new ChatGui(Address.ChatManager, scanner, dalamud);
|
||||
PartyFinder = new PartyFinderGui(scanner, dalamud);
|
||||
Toast = new ToastGui(scanner, dalamud);
|
||||
|
||||
this.setGlobalBgmHook =
|
||||
new Hook<SetGlobalBgmDelegate>(Address.SetGlobalBgm,
|
||||
|
|
@ -146,6 +151,8 @@ namespace Dalamud.Game.Internal.Gui {
|
|||
|
||||
this.GetBaseUIObject = Marshal.GetDelegateForFunctionPointer<GetBaseUIObjectDelegate>(Address.GetBaseUIObject);
|
||||
this.getUIObjectByName = Marshal.GetDelegateForFunctionPointer<GetUIObjectByNameDelegate>(Address.GetUIObjectByName);
|
||||
|
||||
this.getUiModule = Marshal.GetDelegateForFunctionPointer<GetUiModuleDelegate>(Address.GetUIModule);
|
||||
}
|
||||
|
||||
private IntPtr HandleSetGlobalBgmDetour(UInt16 bgmKey, byte a2, UInt32 a3, UInt32 a4, UInt32 a5, byte a6) {
|
||||
|
|
@ -411,6 +418,15 @@ namespace Dalamud.Game.Internal.Gui {
|
|||
return this.toggleUiHideHook.Original(thisPtr, unknownByte);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to the game's UI module.
|
||||
/// </summary>
|
||||
/// <returns>IntPtr pointing to UI module</returns>
|
||||
public IntPtr GetUIModule()
|
||||
{
|
||||
return this.getUiModule(Marshal.ReadIntPtr(Address.GetUIModulePtr));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the UI Object with the given name and index.
|
||||
/// </summary>
|
||||
|
|
@ -436,6 +452,7 @@ namespace Dalamud.Game.Internal.Gui {
|
|||
|
||||
public void Enable() {
|
||||
Chat.Enable();
|
||||
Toast.Enable();
|
||||
PartyFinder.Enable();
|
||||
this.setGlobalBgmHook.Enable();
|
||||
this.handleItemHoverHook.Enable();
|
||||
|
|
@ -447,6 +464,7 @@ namespace Dalamud.Game.Internal.Gui {
|
|||
|
||||
public void Dispose() {
|
||||
Chat.Dispose();
|
||||
Toast.Dispose();
|
||||
PartyFinder.Dispose();
|
||||
this.setGlobalBgmHook.Dispose();
|
||||
this.handleItemHoverHook.Dispose();
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ namespace Dalamud.Game.Internal.Gui {
|
|||
public IntPtr ToggleUiHide { get; set; }
|
||||
public IntPtr GetBaseUIObject { get; private set; }
|
||||
public IntPtr GetUIObjectByName { get; private set; }
|
||||
public IntPtr GetUIModule { get; private set; }
|
||||
public IntPtr GetUIModulePtr { get; private set; }
|
||||
|
||||
public GameGuiAddressResolver(IntPtr baseAddress) {
|
||||
BaseAddress = baseAddress;
|
||||
|
|
@ -45,6 +47,8 @@ namespace Dalamud.Game.Internal.Gui {
|
|||
ToggleUiHide = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 0F B6 B9 ?? ?? ?? ?? B8 ?? ?? ?? ??");
|
||||
GetBaseUIObject = sig.ScanText("E8 ?? ?? ?? ?? 41 B8 01 00 00 00 48 8D 15 ?? ?? ?? ?? 48 8B 48 20 E8 ?? ?? ?? ?? 48 8B CF");
|
||||
GetUIObjectByName = sig.ScanText("E8 ?? ?? ?? ?? 48 8B CF 48 89 87 ?? ?? 00 00 E8 ?? ?? ?? ?? 41 B8 01 00 00 00");
|
||||
GetUIModule = sig.ScanText("E8 ?? ?? ?? ?? 83 3B 01");
|
||||
GetUIModulePtr = sig.GetStaticAddressFromSig("48 8B 0D ?? ?? ?? ?? 48 8D 54 24 ?? 48 83 C1 10 E8 ?? ?? ?? ??");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
153
Dalamud/Game/Internal/Gui/ToastGui.cs
Executable file
153
Dalamud/Game/Internal/Gui/ToastGui.cs
Executable file
|
|
@ -0,0 +1,153 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Hooking;
|
||||
|
||||
namespace Dalamud.Game.Internal.Gui
|
||||
{
|
||||
public class ToastGui : IDisposable
|
||||
{
|
||||
#region Events
|
||||
|
||||
public delegate void OnToastDelegate(ref SeString message, ref bool isHandled);
|
||||
|
||||
/// <summary>
|
||||
/// Event that will be fired when a toast is sent by the game or a plugin.
|
||||
/// </summary>
|
||||
public event OnToastDelegate OnToast;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hooks
|
||||
|
||||
private readonly Hook<ShowToastDelegate> showToastHook;
|
||||
|
||||
#endregion
|
||||
|
||||
private delegate IntPtr ShowToastDelegate(IntPtr manager, IntPtr text, int layer, byte bool1, byte bool2, int logMessageId);
|
||||
|
||||
private Dalamud Dalamud { get; }
|
||||
|
||||
private ToastGuiAddressResolver Address { get; }
|
||||
|
||||
private Queue<byte[]> ToastQueue { get; } = new Queue<byte[]>();
|
||||
|
||||
|
||||
public ToastGui(SigScanner scanner, Dalamud dalamud)
|
||||
{
|
||||
this.Dalamud = dalamud;
|
||||
|
||||
this.Address = new ToastGuiAddressResolver();
|
||||
this.Address.Setup(scanner);
|
||||
|
||||
Marshal.GetDelegateForFunctionPointer<ShowToastDelegate>(this.Address.ShowToast);
|
||||
this.showToastHook = new Hook<ShowToastDelegate>(this.Address.ShowToast, new ShowToastDelegate(this.HandleToastDetour));
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
this.showToastHook.Enable();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.showToastHook.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show a toast message with the given content.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be shown</param>
|
||||
public void Show(string message)
|
||||
{
|
||||
this.ToastQueue.Enqueue(Encoding.UTF8.GetBytes(message));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show a toast message with the given content.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be shown</param>
|
||||
public void Show(SeString message)
|
||||
{
|
||||
this.ToastQueue.Enqueue(message.Encode());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the toast queue.
|
||||
/// </summary>
|
||||
internal void UpdateQueue()
|
||||
{
|
||||
while (this.ToastQueue.Count > 0)
|
||||
{
|
||||
var message = this.ToastQueue.Dequeue();
|
||||
this.Show(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void Show(byte[] bytes)
|
||||
{
|
||||
var manager = this.Dalamud.Framework.Gui.GetUIModule();
|
||||
|
||||
// terminate the string
|
||||
var terminated = new byte[bytes.Length + 1];
|
||||
Array.Copy(bytes, 0, terminated, 0, bytes.Length);
|
||||
terminated[^1] = 0;
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = terminated)
|
||||
{
|
||||
this.HandleToastDetour(manager, (IntPtr)ptr, 5, 0, 1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IntPtr HandleToastDetour(IntPtr manager, IntPtr text, int layer, byte bool1, byte bool2, int logMessageId)
|
||||
{
|
||||
if (text == IntPtr.Zero)
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
// get the message as an sestring
|
||||
var bytes = new List<byte>();
|
||||
unsafe
|
||||
{
|
||||
var ptr = (byte*)text;
|
||||
while (*ptr != 0)
|
||||
{
|
||||
bytes.Add(*ptr);
|
||||
ptr += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// call events
|
||||
var isHandled = false;
|
||||
var str = this.Dalamud.SeStringManager.Parse(bytes.ToArray());
|
||||
|
||||
this.OnToast?.Invoke(ref str, ref isHandled);
|
||||
|
||||
// do nothing if handled
|
||||
if (isHandled)
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
var encoded = str.Encode();
|
||||
var terminated = new byte[encoded.Length + 1];
|
||||
Array.Copy(encoded, 0, terminated, 0, encoded.Length);
|
||||
terminated[^1] = 0;
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* message = terminated)
|
||||
{
|
||||
return this.showToastHook.Original(manager, (IntPtr)message, layer, bool1, bool2, logMessageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
Dalamud/Game/Internal/Gui/ToastGuiAddressResolver.cs
Executable file
14
Dalamud/Game/Internal/Gui/ToastGuiAddressResolver.cs
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
|
||||
namespace Dalamud.Game.Internal.Gui
|
||||
{
|
||||
public class ToastGuiAddressResolver : BaseAddressResolver
|
||||
{
|
||||
public IntPtr ShowToast { get; private set; }
|
||||
|
||||
protected override void Setup64Bit(SigScanner sig)
|
||||
{
|
||||
ShowToast = sig.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 83 3D ?? ?? ?? ?? ??");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue