diff --git a/Dalamud.Injector/Program.cs b/Dalamud.Injector/Program.cs index d25243c8e..ffd612034 100644 --- a/Dalamud.Injector/Program.cs +++ b/Dalamud.Injector/Program.cs @@ -13,6 +13,8 @@ using Newtonsoft.Json; namespace Dalamud.Injector { internal static class Program { + static private Process process = null; + private static void Main(string[] args) { #if !DEBUG AppDomain.CurrentDomain.UnhandledException += delegate(object sender, UnhandledExceptionEventArgs eventArgs) @@ -25,9 +27,10 @@ namespace Dalamud.Injector { }; #endif - var pid = int.Parse(args[0]); - - Process process = null; + var pid = -1; + if (args.Length == 1) { + pid = int.Parse(args[0]); + } switch (pid) { case -1: @@ -45,10 +48,12 @@ namespace Dalamud.Injector { } DalamudStartInfo startInfo; - if (args.Length == 1) { + if (args.Length <= 1) { startInfo = GetDefaultStartInfo(); Console.WriteLine("\nA Dalamud start info was not found in the program arguments. One has been generated for you."); Console.WriteLine("\nCopy the following contents into the program arguments:"); + Console.WriteLine(); + Console.WriteLine(Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(startInfo)))); } else { startInfo = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(Convert.FromBase64String(args[1]))); } @@ -80,6 +85,7 @@ namespace Dalamud.Injector { } private static DalamudStartInfo GetDefaultStartInfo() { + var ffxivDir = Path.GetDirectoryName(process.MainModule.FileName); var startInfo = new DalamudStartInfo { WorkingDirectory = null, ConfigurationPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @@ -89,7 +95,7 @@ namespace Dalamud.Injector { DefaultPluginDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\XIVLauncher\devPlugins", - GameVersion = File.ReadAllText(@"C:\Program Files (x86)\SquareEnix\FINAL FANTASY XIV - A Realm Reborn\game\ffxivgame.ver"), + GameVersion = File.ReadAllText(Path.Combine(ffxivDir, "ffxivgame.ver")), Language = ClientLanguage.English }; diff --git a/Dalamud/Game/Internal/BaseAddressResolver.cs b/Dalamud/Game/Internal/BaseAddressResolver.cs index 8cbb83be5..6c465b931 100644 --- a/Dalamud/Game/Internal/BaseAddressResolver.cs +++ b/Dalamud/Game/Internal/BaseAddressResolver.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.InteropServices; namespace Dalamud.Game.Internal { @@ -35,7 +35,7 @@ namespace Dalamud.Game.Internal { // Do nothing } - protected T GetVirtualFunction(IntPtr address, int vtableOffset, int count) where T : class { + public T GetVirtualFunction(IntPtr address, int vtableOffset, int count) where T : class { // Get vtable var vtable = Marshal.ReadIntPtr(address, vtableOffset); diff --git a/Dalamud/Game/Internal/Gui/GameGui.cs b/Dalamud/Game/Internal/Gui/GameGui.cs index 86de67e7a..89f64eb2e 100644 --- a/Dalamud/Game/Internal/Gui/GameGui.cs +++ b/Dalamud/Game/Internal/Gui/GameGui.cs @@ -1,6 +1,6 @@ using System; using System.Runtime.InteropServices; -using Dalamud.Game.Chat; +using Dalamud.Game.Chat.SeStringHandling.Payloads; using Dalamud.Hooking; using Serilog; @@ -22,6 +22,18 @@ namespace Dalamud.Game.Internal.Gui { private delegate IntPtr HandleItemOutDelegate(IntPtr hoverState, IntPtr a2, IntPtr a3, ulong a4); private readonly Hook handleItemOutHook; + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate IntPtr GetUIObjectDelegate(); + private readonly GetUIObjectDelegate getUIObject; + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + private delegate IntPtr GetUIMapObjectDelegate(IntPtr UIObject); + private GetUIMapObjectDelegate getUIMapObject; + + [UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Ansi)] + private delegate bool OpenMapWithFlagDelegate(IntPtr UIMapObject, string flag); + private OpenMapWithFlagDelegate openMapWithFlag; + /// /// The item ID that is currently hovered by the player. 0 when no item is hovered. /// If > 1.000.000, subtract 1.000.000 and treat it as HQ @@ -43,6 +55,7 @@ namespace Dalamud.Game.Internal.Gui { Log.Verbose("SetGlobalBgm address {Address}", Address.SetGlobalBgm); Log.Verbose("HandleItemHover address {Address}", Address.HandleItemHover); Log.Verbose("HandleItemOut address {Address}", Address.HandleItemOut); + Log.Verbose("GetUIObject address {Address}", Address.GetUIObject); Chat = new ChatGui(Address.ChatManager, scanner, dalamud); @@ -59,6 +72,8 @@ namespace Dalamud.Game.Internal.Gui { new Hook(Address.HandleItemOut, new HandleItemOutDelegate(HandleItemOutDetour), this); + + this.getUIObject = Marshal.GetDelegateForFunctionPointer(Address.GetUIObject); } private IntPtr HandleSetGlobalBgmDetour(UInt16 bgmKey, byte a2, UInt32 a3, UInt32 a4, UInt32 a5, byte a6) { @@ -111,6 +126,39 @@ namespace Dalamud.Game.Internal.Gui { return retVal; } + public bool OpenMapWithMapLink(MapLinkPayload mapLink) + { + var uiObjectPtr = getUIObject(); + + if (uiObjectPtr.Equals(IntPtr.Zero)) + { + Log.Error("OpenMapWithMapLink: Null pointer returned from getUIObject()"); + return false; + } + + getUIMapObject = + Address.GetVirtualFunction(uiObjectPtr, 0, 8); + + + var uiMapObjectPtr = this.getUIMapObject(uiObjectPtr); + + if (uiMapObjectPtr.Equals(IntPtr.Zero)) + { + Log.Error("OpenMapWithMapLink: Null pointer returned from GetUIMapObject()"); + return false; + } + + openMapWithFlag = + Address.GetVirtualFunction(uiMapObjectPtr, 0, 63); + + var mapLinkString = + $"m:{mapLink.TerritoryTypeId},{mapLink.MapId},{unchecked((int)mapLink.RawX)},{unchecked((int)mapLink.RawY)}"; + + Log.Debug($"OpenMapWithMapLink: Opening Map Link: {mapLinkString}"); + + return this.openMapWithFlag(uiMapObjectPtr, mapLinkString); + } + public void SetBgm(ushort bgmKey) => this.setGlobalBgmHook.Original(bgmKey, 0, 0, 0, 0, 0); public void Enable() { diff --git a/Dalamud/Game/Internal/Gui/GameGuiAddressResolver.cs b/Dalamud/Game/Internal/Gui/GameGuiAddressResolver.cs index 8fbda8a5a..5142d0d41 100644 --- a/Dalamud/Game/Internal/Gui/GameGuiAddressResolver.cs +++ b/Dalamud/Game/Internal/Gui/GameGuiAddressResolver.cs @@ -11,6 +11,7 @@ namespace Dalamud.Game.Internal.Gui { public IntPtr SetGlobalBgm { get; private set; } public IntPtr HandleItemHover { get; set; } public IntPtr HandleItemOut { get; set; } + public IntPtr GetUIObject { get; private set; } public GameGuiAddressResolver(IntPtr baseAddress) { BaseAddress = baseAddress; @@ -29,6 +30,7 @@ namespace Dalamud.Game.Internal.Gui { SetGlobalBgm = sig.ScanText("4C 8B 15 ?? ?? ?? ?? 4D 85 D2 74 58"); HandleItemHover = sig.ScanText("E8 ?? ?? ?? ?? 48 8B 5C 24 ?? 48 89 AE ?? ?? ?? ??"); HandleItemOut = sig.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 4D"); + GetUIObject = sig.ScanText("E8 ?? ?? ?? ?? 48 8B C8 48 8B 10 FF 52 40 80 88 ?? ?? ?? ?? 01 E9"); } } }