From e930c9876907b2bc0cdbbf5dc179e6ff3ad4052d Mon Sep 17 00:00:00 2001
From: goat <16760685+goaaats@users.noreply.github.com>
Date: Mon, 26 Apr 2021 20:39:09 +0200
Subject: [PATCH] feat: add aers menu thingy
---
Dalamud/Dalamud.cs | 13 +-
Dalamud/Game/Addon/DalamudSystemMenu.cs | 168 ++++++++++++++++++++++++
2 files changed, 180 insertions(+), 1 deletion(-)
create mode 100644 Dalamud/Game/Addon/DalamudSystemMenu.cs
diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs
index 84b125171..122eedc03 100644
--- a/Dalamud/Dalamud.cs
+++ b/Dalamud/Dalamud.cs
@@ -6,6 +6,7 @@ using System.Threading;
using Dalamud.Configuration;
using Dalamud.Data;
using Dalamud.Game;
+using Dalamud.Game.Addon;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.ClientState;
using Dalamud.Game.Command;
@@ -164,6 +165,11 @@ namespace Dalamud
///
internal NetworkHandlers NetworkHandlers { get; private set; }
+ ///
+ /// Gets subsystem responsible for adding the Dalamud menu items to the game's system menu.
+ ///
+ internal DalamudSystemMenu SystemMenu { get; private set; }
+
#endregion
///
@@ -198,7 +204,7 @@ namespace Dalamud
this.AntiDebug = new AntiDebug(this.SigScanner);
#if DEBUG
- AntiDebug.Enable();
+ this.AntiDebug.Enable();
#endif
Log.Information("[START] AntiDebug OK!");
@@ -315,6 +321,9 @@ namespace Dalamud
Log.Information("[START] DUI OK!");
+ this.SystemMenu = new DalamudSystemMenu(this);
+ this.SystemMenu.Enable();
+
this.IsReady = true;
Troubleshooting.LogTroubleshooting(this, isInterfaceLoaded);
@@ -404,6 +413,8 @@ namespace Dalamud
this.AntiDebug?.Dispose();
+ this.SystemMenu?.Dispose();
+
Log.Debug("Dalamud::Dispose() OK!");
}
catch (Exception ex)
diff --git a/Dalamud/Game/Addon/DalamudSystemMenu.cs b/Dalamud/Game/Addon/DalamudSystemMenu.cs
new file mode 100644
index 000000000..e67fc2132
--- /dev/null
+++ b/Dalamud/Game/Addon/DalamudSystemMenu.cs
@@ -0,0 +1,168 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+using Dalamud.Hooking;
+using FFXIVClientStructs.FFXIV.Component.GUI;
+
+using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
+
+namespace Dalamud.Game.Addon
+{
+ internal unsafe class DalamudSystemMenu
+ {
+ private readonly Dalamud dalamud;
+
+ private delegate void AgentHudOpenSystemMenuProtoype(void* thisPtr, AtkValue* atkValueArgs, uint menuSize);
+
+ private Hook hookAgentHudOpenSystemMenu;
+
+ private delegate void AtkValueChangeType(AtkValue* thisPtr, ValueType type);
+
+ private AtkValueChangeType atkValueChangeType;
+
+ private delegate void AtkValueSetString(AtkValue* thisPtr, byte* bytes);
+
+ private AtkValueSetString atkValueSetString;
+
+ private delegate void UiModuleRequestMainCommand(void* thisPtr, int commandId);
+
+ // TODO: Make this into events in Framework.Gui
+ private Hook hookUiModuleRequestMainCommand;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The dalamud instance to act on.
+ public DalamudSystemMenu(Dalamud dalamud)
+ {
+ this.dalamud = dalamud;
+
+ var openSystemMenuAddress = this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 32 C0 4C 8B AC 24 ?? ?? ?? ?? 48 8B 8D ?? ?? ?? ??");
+
+ this.hookAgentHudOpenSystemMenu = new Hook(
+ openSystemMenuAddress,
+ new AgentHudOpenSystemMenuProtoype(this.AgentHudOpenSystemMenuDetour),
+ this);
+
+ var atkValueChangeTypeAddress =
+ this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 45 84 F6 48 8D 4C 24 ??");
+ this.atkValueChangeType =
+ Marshal.GetDelegateForFunctionPointer(atkValueChangeTypeAddress);
+
+ var atkValueSetStringAddress =
+ this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 41 03 ED");
+ this.atkValueSetString = Marshal.GetDelegateForFunctionPointer(atkValueSetStringAddress);
+
+ var uiModuleRequestMainCommmandAddress = this.dalamud.SigScanner.ScanText(
+ "40 53 56 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 48 8B 01 8B DA 48 8B F1 FF 90 ?? ?? ?? ??");
+ this.hookUiModuleRequestMainCommand = new Hook(
+ uiModuleRequestMainCommmandAddress,
+ new UiModuleRequestMainCommand(this.UiModuleRequestMainCommandDetour),
+ this);
+ }
+
+ public void Enable()
+ {
+ this.hookAgentHudOpenSystemMenu.Enable();
+ this.hookUiModuleRequestMainCommand.Enable();
+ }
+
+ private void AgentHudOpenSystemMenuDetour(void* thisPtr, AtkValue* atkValueArgs, uint menuSize)
+ {
+ // the max size (hardcoded) is 0xE/15, but the system menu currently uses 0xC/12
+ // this is a just in case that doesnt really matter
+ // see if we can add 2 entries
+ if (menuSize >= 0xD)
+ {
+ hookAgentHudOpenSystemMenu.Original(thisPtr, atkValueArgs, menuSize);
+ return;
+ }
+
+ // atkValueArgs is actually an array of AtkValues used as args. all their UI code works like this.
+ // in this case, menu size is stored in atkValueArgs[4], and the next 15 slots are the MainCommand
+ // the 15 slots after that, if they exist, are the entry names, but they are otherwise pulled from MainCommand EXD
+ // reference the original function for more details :)
+
+ // step 1) move all the current menu items down so we can put Dalamud at the top like it deserves
+ atkValueChangeType(&atkValueArgs[menuSize + 5], ValueType.Int); // currently this value has no type, set it to int
+ atkValueChangeType(&atkValueArgs[menuSize + 5 + 1], ValueType.Int);
+
+ for (uint i = menuSize+2; i > 1; i--)
+ {
+ var curEntry = &atkValueArgs[i + 5 - 2];
+ var nextEntry = &atkValueArgs[i + 5];
+
+ nextEntry->Int = curEntry->Int;
+ }
+
+ // step 2) set our new entries to dummy commands
+ var firstEntry = &atkValueArgs[5];
+ firstEntry->Int = 69420;
+ var secondEntry = &atkValueArgs[6];
+ secondEntry->Int = 69421;
+
+ // step 3) create strings for them
+ // since the game first checks for strings in the AtkValue argument before pulling them from the exd, if we create strings we dont have to worry
+ // about hooking the exd reader, thank god
+ var firstStringEntry = &atkValueArgs[5 + 15];
+ atkValueChangeType(firstStringEntry, ValueType.String);
+ var secondStringEntry = &atkValueArgs[6 + 15];
+ atkValueChangeType(secondStringEntry, ValueType.String);
+
+ // do this the most terrible way possible since im lazy
+ var bytes = stackalloc byte[17];
+ Marshal.Copy(System.Text.Encoding.ASCII.GetBytes("Dalamud Settings"), 0, new IntPtr(bytes), 16);
+ bytes[16] = 0x0;
+
+ atkValueSetString(firstStringEntry, bytes); // this allocs the string properly using the game's allocators and copies it, so we dont have to worry about memory fuckups
+
+ var bytes2 = stackalloc byte[16];
+ Marshal.Copy(System.Text.Encoding.ASCII.GetBytes("Dalamud Plugins"), 0, new IntPtr(bytes2), 15);
+ bytes2[15] = 0x0;
+
+ atkValueSetString(secondStringEntry, bytes2);
+
+ // open menu with new size
+ var sizeEntry = &atkValueArgs[4];
+ sizeEntry->UInt = menuSize + 2;
+
+ this.hookAgentHudOpenSystemMenu.Original(thisPtr, atkValueArgs, menuSize + 2);
+ }
+
+ private void UiModuleRequestMainCommandDetour(void* thisPtr, int commandId)
+ {
+ if (commandId == 69420)
+ {
+ this.dalamud.DalamudUi.OpenSettings();
+ }
+ else if (commandId == 69421)
+ {
+ this.dalamud.DalamudUi.OpenPluginInstaller();
+ }
+ else
+ {
+ this.hookUiModuleRequestMainCommand.Original(thisPtr, commandId);
+ }
+ }
+
+ #region IDisposable Support
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposing) return;
+
+ this.hookAgentHudOpenSystemMenu.Dispose();
+ this.hookUiModuleRequestMainCommand.Dispose();
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+ }
+}