mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
Merge branch 'master' of github.com:goatcorp/Dalamud into profiles
This commit is contained in:
commit
e4724c36a8
18 changed files with 224 additions and 118 deletions
|
|
@ -140,3 +140,8 @@ indent_style = space
|
|||
indent_size = 4
|
||||
tab_width = 4
|
||||
dotnet_style_parentheses_in_other_operators=always_for_clarity:silent
|
||||
|
||||
[*.{yaml,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
|
|
|
|||
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
* @goatcorp/dalamud-maintainers
|
||||
7
.github/workflows/tag-build.yml
vendored
7
.github/workflows/tag-build.yml
vendored
|
|
@ -1,5 +1,10 @@
|
|||
name: Tag Build
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags-ignore:
|
||||
- '*' # don't needlessly execute on tags
|
||||
|
||||
jobs:
|
||||
tag:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Feature">
|
||||
<DalamudVersion>7.6.0.0</DalamudVersion>
|
||||
<DalamudVersion>7.6.5.0</DalamudVersion>
|
||||
<Description>XIV Launcher addon framework</Description>
|
||||
<AssemblyVersion>$(DalamudVersion)</AssemblyVersion>
|
||||
<Version>$(DalamudVersion)</Version>
|
||||
|
|
|
|||
|
|
@ -35,12 +35,8 @@ public sealed class AsmHook : IDisposable, IDalamudHook
|
|||
{
|
||||
address = HookManager.FollowJmp(address);
|
||||
|
||||
var hasOtherHooks = HookManager.Originals.ContainsKey(address);
|
||||
if (!hasOtherHooks)
|
||||
{
|
||||
MemoryHelper.ReadRaw(address, 0x32, out var original);
|
||||
HookManager.Originals[address] = original;
|
||||
}
|
||||
// We cannot call TrimAfterHook here because the hook is activated by the caller.
|
||||
HookManager.RegisterUnhooker(address);
|
||||
|
||||
this.address = address;
|
||||
this.hookImpl = ReloadedHooks.Instance.CreateAsmHook(assembly, address.ToInt64(), (Reloaded.Hooks.Definitions.Enums.AsmHookBehaviour)asmHookBehaviour);
|
||||
|
|
@ -65,12 +61,8 @@ public sealed class AsmHook : IDisposable, IDalamudHook
|
|||
{
|
||||
address = HookManager.FollowJmp(address);
|
||||
|
||||
var hasOtherHooks = HookManager.Originals.ContainsKey(address);
|
||||
if (!hasOtherHooks)
|
||||
{
|
||||
MemoryHelper.ReadRaw(address, 0x32, out var original);
|
||||
HookManager.Originals[address] = original;
|
||||
}
|
||||
// We cannot call TrimAfterHook here because the hook is activated by the caller.
|
||||
HookManager.RegisterUnhooker(address);
|
||||
|
||||
this.address = address;
|
||||
this.hookImpl = ReloadedHooks.Instance.CreateAsmHook(assembly, address.ToInt64(), (Reloaded.Hooks.Definitions.Enums.AsmHookBehaviour)asmHookBehaviour);
|
||||
|
|
|
|||
|
|
@ -41,12 +41,7 @@ internal class FunctionPointerVariableHook<T> : Hook<T>
|
|||
{
|
||||
lock (HookManager.HookEnableSyncRoot)
|
||||
{
|
||||
var hasOtherHooks = HookManager.Originals.ContainsKey(this.Address);
|
||||
if (!hasOtherHooks)
|
||||
{
|
||||
MemoryHelper.ReadRaw(this.Address, 0x32, out var original);
|
||||
HookManager.Originals[this.Address] = original;
|
||||
}
|
||||
var unhooker = HookManager.RegisterUnhooker(this.Address, 8, 8);
|
||||
|
||||
if (!HookManager.MultiHookTracker.TryGetValue(this.Address, out var indexList))
|
||||
{
|
||||
|
|
@ -104,6 +99,8 @@ internal class FunctionPointerVariableHook<T> : Hook<T>
|
|||
// Add afterwards, so the hookIdent starts at 0.
|
||||
indexList.Add(this);
|
||||
|
||||
unhooker.TrimAfterHook();
|
||||
|
||||
HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ namespace Dalamud.Hooking.Internal;
|
|||
[ServiceManager.EarlyLoadedService]
|
||||
internal class HookManager : IDisposable, IServiceType
|
||||
{
|
||||
private static readonly ModuleLog Log = new("HM");
|
||||
/// <summary>
|
||||
/// Logger shared with <see cref="Unhooker"/>
|
||||
/// </summary>
|
||||
internal static readonly ModuleLog Log = new("HM");
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private HookManager()
|
||||
|
|
@ -34,9 +37,36 @@ internal class HookManager : IDisposable, IServiceType
|
|||
internal static ConcurrentDictionary<Guid, HookInfo> TrackedHooks { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a static dictionary of original code for a hooked address.
|
||||
/// Gets a static dictionary of unhookers for a hooked address.
|
||||
/// </summary>
|
||||
internal static ConcurrentDictionary<IntPtr, byte[]> Originals { get; } = new();
|
||||
internal static ConcurrentDictionary<IntPtr, Unhooker> Unhookers { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Unhooker instance for the provided address if no such unhooker was already registered, or returns
|
||||
/// an existing instance if the address was registered previously. By default, the unhooker will restore between 0
|
||||
/// and 0x32 bytes depending on the detected size of the hook. To specify the minimum and maximum bytes restored
|
||||
/// manually, use <see cref="RegisterUnhooker(System.IntPtr, int, int)"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">The address of the instruction.</param>
|
||||
/// <returns>A new Unhooker instance.</returns>
|
||||
public static Unhooker RegisterUnhooker(IntPtr address)
|
||||
{
|
||||
return RegisterUnhooker(address, 0, 0x32);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Unhooker instance for the provided address if no such unhooker was already registered, or returns
|
||||
/// an existing instance if the address was registered previously.
|
||||
/// </summary>
|
||||
/// <param name="address">The address of the instruction.</param>
|
||||
/// <param name="minBytes">The minimum amount of bytes to restore when unhooking.</param>
|
||||
/// <param name="maxBytes">The maximum amount of bytes to restore when unhooking.</param>
|
||||
/// <returns>A new Unhooker instance.</returns>
|
||||
public static Unhooker RegisterUnhooker(IntPtr address, int minBytes, int maxBytes)
|
||||
{
|
||||
Log.Verbose($"Registering hook at 0x{address.ToInt64():X} (minBytes=0x{minBytes:X}, maxBytes=0x{maxBytes:X})");
|
||||
return Unhookers.GetOrAdd(address, _ => new Unhooker(address, minBytes, maxBytes));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a static dictionary of the number of hooks on a given address.
|
||||
|
|
@ -48,7 +78,7 @@ internal class HookManager : IDisposable, IServiceType
|
|||
{
|
||||
RevertHooks();
|
||||
TrackedHooks.Clear();
|
||||
Originals.Clear();
|
||||
Unhookers.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -60,7 +90,7 @@ internal class HookManager : IDisposable, IServiceType
|
|||
{
|
||||
while (true)
|
||||
{
|
||||
var hasOtherHooks = HookManager.Originals.ContainsKey(address);
|
||||
var hasOtherHooks = HookManager.Unhookers.ContainsKey(address);
|
||||
if (hasOtherHooks)
|
||||
{
|
||||
// This address has been hooked already. Do not follow a jmp into a trampoline of our own making.
|
||||
|
|
@ -124,28 +154,11 @@ internal class HookManager : IDisposable, IServiceType
|
|||
return address;
|
||||
}
|
||||
|
||||
private static unsafe void RevertHooks()
|
||||
private static void RevertHooks()
|
||||
{
|
||||
foreach (var (address, originalBytes) in Originals)
|
||||
foreach (var unhooker in Unhookers.Values)
|
||||
{
|
||||
var i = 0;
|
||||
var current = (byte*)address;
|
||||
// Find how many bytes have been modified by comparing to the saved original
|
||||
for (; i < originalBytes.Length; i++)
|
||||
{
|
||||
if (current[i] == originalBytes[i])
|
||||
break;
|
||||
}
|
||||
|
||||
var snippet = originalBytes[0..i];
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
Log.Verbose($"Reverting hook at 0x{address.ToInt64():X} ({snippet.Length} bytes)");
|
||||
MemoryHelper.ChangePermission(address, i, MemoryProtection.ExecuteReadWrite, out var oldPermissions);
|
||||
MemoryHelper.WriteRaw(address, snippet);
|
||||
MemoryHelper.ChangePermission(address, i, oldPermissions);
|
||||
}
|
||||
unhooker.Unhook();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using Dalamud.Memory;
|
||||
|
||||
namespace Dalamud.Hooking.Internal;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -24,12 +22,7 @@ internal class MinHookHook<T> : Hook<T> where T : Delegate
|
|||
{
|
||||
lock (HookManager.HookEnableSyncRoot)
|
||||
{
|
||||
var hasOtherHooks = HookManager.Originals.ContainsKey(this.Address);
|
||||
if (!hasOtherHooks)
|
||||
{
|
||||
MemoryHelper.ReadRaw(this.Address, 0x32, out var original);
|
||||
HookManager.Originals[this.Address] = original;
|
||||
}
|
||||
var unhooker = HookManager.RegisterUnhooker(this.Address);
|
||||
|
||||
if (!HookManager.MultiHookTracker.TryGetValue(this.Address, out var indexList))
|
||||
indexList = HookManager.MultiHookTracker[this.Address] = new();
|
||||
|
|
@ -41,6 +34,8 @@ internal class MinHookHook<T> : Hook<T> where T : Delegate
|
|||
// Add afterwards, so the hookIdent starts at 0.
|
||||
indexList.Add(this);
|
||||
|
||||
unhooker.TrimAfterHook();
|
||||
|
||||
HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using Dalamud.Memory;
|
||||
using Reloaded.Hooks;
|
||||
|
||||
namespace Dalamud.Hooking.Internal;
|
||||
|
|
@ -25,17 +24,13 @@ internal class ReloadedHook<T> : Hook<T> where T : Delegate
|
|||
{
|
||||
lock (HookManager.HookEnableSyncRoot)
|
||||
{
|
||||
var hasOtherHooks = HookManager.Originals.ContainsKey(this.Address);
|
||||
if (!hasOtherHooks)
|
||||
{
|
||||
MemoryHelper.ReadRaw(this.Address, 0x32, out var original);
|
||||
HookManager.Originals[this.Address] = original;
|
||||
}
|
||||
|
||||
var unhooker = HookManager.RegisterUnhooker(address);
|
||||
this.hookImpl = ReloadedHooks.Instance.CreateHook<T>(detour, address.ToInt64());
|
||||
this.hookImpl.Activate();
|
||||
this.hookImpl.Disable();
|
||||
|
||||
unhooker.TrimAfterHook();
|
||||
|
||||
HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
102
Dalamud/Hooking/Internal/Unhooker.cs
Normal file
102
Dalamud/Hooking/Internal/Unhooker.cs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
using System;
|
||||
|
||||
using Dalamud.Memory;
|
||||
|
||||
namespace Dalamud.Hooking.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// A class which stores a copy of the bytes at a location which will be hooked in the future, such that those bytes can
|
||||
/// be restored later to "unhook" the function.
|
||||
/// </summary>
|
||||
public class Unhooker
|
||||
{
|
||||
private readonly IntPtr address;
|
||||
private readonly int minBytes;
|
||||
private byte[] originalBytes;
|
||||
private bool trimmed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Unhooker"/> class. Upon creation, the Unhooker stores a copy of
|
||||
/// the bytes stored at the provided address, and can be used to restore these bytes when the hook should be
|
||||
/// removed. As such this class should be instantiated before the function is actually hooked.
|
||||
/// </summary>
|
||||
/// <param name="address">The address which will be hooked.</param>
|
||||
/// <param name="minBytes">The minimum amount of bytes to restore when unhooking.</param>
|
||||
/// <param name="maxBytes">The maximum amount of bytes to restore when unhooking.</param>
|
||||
internal Unhooker(IntPtr address, int minBytes, int maxBytes)
|
||||
{
|
||||
if (minBytes < 0 || minBytes > maxBytes)
|
||||
{
|
||||
throw new ArgumentException($"minBytes ({minBytes}) must be <= maxBytes ({maxBytes}) and nonnegative.");
|
||||
}
|
||||
|
||||
this.address = address;
|
||||
this.minBytes = minBytes;
|
||||
MemoryHelper.ReadRaw(address, maxBytes, out this.originalBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When called after a hook is created, checks the pre-hook original bytes and post-hook modified bytes, trimming
|
||||
/// the original bytes stored and removing unmodified bytes from the end of the byte sequence. Assuming no
|
||||
/// concurrent actions modified the same address space, this should result in storing only the minimum bytes
|
||||
/// required to unhook the function.
|
||||
/// </summary>
|
||||
public void TrimAfterHook()
|
||||
{
|
||||
if (this.trimmed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var len = int.Max(this.GetFullHookLength(), this.minBytes);
|
||||
|
||||
this.originalBytes = this.originalBytes[..len];
|
||||
this.trimmed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to unhook the function by replacing the hooked bytes with the original bytes. If
|
||||
/// <see cref="TrimAfterHook"/> was called, the trimmed original bytes stored at that time will be used for
|
||||
/// unhooking. Otherwise, a naive algorithm which only restores bytes until the first unchanged byte will be used in
|
||||
/// order to avoid overwriting adjacent data.
|
||||
/// </summary>
|
||||
public void Unhook()
|
||||
{
|
||||
var len = this.trimmed ? this.originalBytes.Length : int.Max(this.GetNaiveHookLength(), this.minBytes);
|
||||
if (len > 0)
|
||||
{
|
||||
HookManager.Log.Verbose($"Reverting hook at 0x{this.address.ToInt64():X} ({len} bytes, trimmed={this.trimmed})");
|
||||
MemoryHelper.ChangePermission(this.address, len, MemoryProtection.ExecuteReadWrite, out var oldPermissions);
|
||||
MemoryHelper.WriteRaw(this.address, this.originalBytes[..len]);
|
||||
MemoryHelper.ChangePermission(this.address, len, oldPermissions);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe int GetNaiveHookLength()
|
||||
{
|
||||
var current = (byte*)this.address;
|
||||
for (var i = 0; i < this.originalBytes.Length; i++)
|
||||
{
|
||||
if (current[i] == this.originalBytes[i])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private unsafe int GetFullHookLength()
|
||||
{
|
||||
var current = (byte*)this.address;
|
||||
for (var i = this.originalBytes.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (current[i] != this.originalBytes[i])
|
||||
{
|
||||
return int.Max(i + 1, this.minBytes);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -802,6 +802,11 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
ImGui.SetWindowFocus(null);
|
||||
}
|
||||
|
||||
if (ImGui.MenuItem("Clear stacks"))
|
||||
{
|
||||
Service<InterfaceManager>.Get().ClearStacks();
|
||||
}
|
||||
|
||||
if (ImGui.MenuItem("Dump style"))
|
||||
{
|
||||
var info = string.Empty;
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ using Dalamud.Interface.Internal.ManagedAsserts;
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.Style;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Utility;
|
||||
using Dalamud.Utility.Timing;
|
||||
using ImGuiNET;
|
||||
using ImGuiScene;
|
||||
using PInvoke;
|
||||
using Serilog;
|
||||
|
||||
// general dev notes, here because it's easiest
|
||||
|
||||
|
|
@ -47,8 +47,6 @@ namespace Dalamud.Interface.Internal;
|
|||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
internal class InterfaceManager : IDisposable, IServiceType
|
||||
{
|
||||
private static ModuleLog Log = new ModuleLog("IM");
|
||||
|
||||
private const float DefaultFontSizePt = 12.0f;
|
||||
private const float DefaultFontSizePx = DefaultFontSizePt * 4.0f / 3.0f;
|
||||
private const ushort Fallback1Codepoint = 0x3013; // Geta mark; FFXIV uses this to indicate that a glyph is missing.
|
||||
|
|
@ -78,41 +76,16 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
private bool isOverrideGameCursor = true;
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private InterfaceManager(SigScanner sigScanner)
|
||||
private InterfaceManager()
|
||||
{
|
||||
Log.Information("ctor called");
|
||||
|
||||
this.dispatchMessageWHook = Hook<DispatchMessageWDelegate>.FromImport(
|
||||
null, "user32.dll", "DispatchMessageW", 0, this.DispatchMessageWDetour);
|
||||
this.setCursorHook = Hook<SetCursorDelegate>.FromImport(
|
||||
null, "user32.dll", "SetCursor", 0, this.SetCursorDetour);
|
||||
Log.Information("Import hooks applied");
|
||||
|
||||
this.fontBuildSignal = new ManualResetEvent(false);
|
||||
|
||||
this.address = new SwapChainVtableResolver();
|
||||
this.address.Setup();
|
||||
Log.Information("Resolver setup complete");
|
||||
|
||||
Log.Information("===== S W A P C H A I N =====");
|
||||
Log.Information($"Is ReShade: {this.address.IsReshade}");
|
||||
Log.Information($"Present address 0x{this.address.Present.ToInt64():X}");
|
||||
Log.Information($"ResizeBuffers address 0x{this.address.ResizeBuffers.ToInt64():X}");
|
||||
|
||||
this.presentHook = Hook<PresentDelegate>.FromAddress(this.address.Present, this.PresentDetour);
|
||||
this.resizeBuffersHook = Hook<ResizeBuffersDelegate>.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);
|
||||
Log.Information("Present and ResizeBuffers hooked");
|
||||
|
||||
var wndProcAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? 80 7C 24 ?? ?? 74 ?? B8");
|
||||
Log.Information($"WndProc address 0x{wndProcAddress.ToInt64():X}");
|
||||
this.processMessageHook = Hook<ProcessMessageDelegate>.FromAddress(wndProcAddress, this.ProcessMessageDetour);
|
||||
|
||||
this.setCursorHook.Enable();
|
||||
this.presentHook.Enable();
|
||||
this.resizeBuffersHook.Enable();
|
||||
this.dispatchMessageWHook.Enable();
|
||||
this.processMessageHook.Enable();
|
||||
Log.Information("Hooks enabled");
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
|
|
@ -462,6 +435,15 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear font, style, and color stack. Dangerous, only use when you know
|
||||
/// no one else has something pushed they may try to pop.
|
||||
/// </summary>
|
||||
public void ClearStacks()
|
||||
{
|
||||
this.scene?.ClearStacksOnContext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle Windows 11 immersive mode on the game window.
|
||||
/// </summary>
|
||||
|
|
@ -919,6 +901,7 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
Log.Verbose("[FONT] ImGui.IO.Build will be called.");
|
||||
ioFonts.Build();
|
||||
gameFontManager.AfterIoFontsBuild();
|
||||
this.ClearStacks();
|
||||
Log.Verbose("[FONT] ImGui.IO.Build OK!");
|
||||
|
||||
gameFontManager.AfterBuildFonts();
|
||||
|
|
@ -1009,9 +992,10 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
}
|
||||
|
||||
[ServiceManager.CallWhenServicesReady]
|
||||
private void ContinueConstruction()
|
||||
private void ContinueConstruction(SigScanner sigScanner, Framework framework)
|
||||
{
|
||||
this.framework.RunOnFrameworkThread(() =>
|
||||
this.address.Setup(sigScanner);
|
||||
framework.RunOnFrameworkThread(() =>
|
||||
{
|
||||
while ((this.GameWindowHandle = NativeFunctions.FindWindowEx(IntPtr.Zero, this.GameWindowHandle, "FFXIVGAME", IntPtr.Zero)) != IntPtr.Zero)
|
||||
{
|
||||
|
|
@ -1030,6 +1014,23 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
{
|
||||
Log.Error(ex, "Could not enable immersive mode");
|
||||
}
|
||||
|
||||
this.presentHook = Hook<PresentDelegate>.FromAddress(this.address.Present, this.PresentDetour);
|
||||
this.resizeBuffersHook = Hook<ResizeBuffersDelegate>.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);
|
||||
|
||||
Log.Verbose("===== S W A P C H A I N =====");
|
||||
Log.Verbose($"Present address 0x{this.presentHook!.Address.ToInt64():X}");
|
||||
Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook!.Address.ToInt64():X}");
|
||||
|
||||
var wndProcAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? 80 7C 24 ?? ?? 74 ?? B8");
|
||||
Log.Verbose($"WndProc address 0x{wndProcAddress.ToInt64():X}");
|
||||
this.processMessageHook = Hook<ProcessMessageDelegate>.FromAddress(wndProcAddress, this.ProcessMessageDetour);
|
||||
|
||||
this.setCursorHook.Enable();
|
||||
this.presentHook.Enable();
|
||||
this.resizeBuffersHook.Enable();
|
||||
this.dispatchMessageWHook.Enable();
|
||||
this.processMessageHook.Enable();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,11 +104,11 @@ internal class PluginStatWindow : Window
|
|||
? loadedPlugins.OrderBy(plugin => plugin.Name)
|
||||
: loadedPlugins.OrderByDescending(plugin => plugin.Name),
|
||||
2 => sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending
|
||||
? loadedPlugins.OrderBy(plugin => plugin.DalamudInterface?.UiBuilder.MaxDrawTime)
|
||||
: loadedPlugins.OrderByDescending(plugin => plugin.DalamudInterface?.UiBuilder.MaxDrawTime),
|
||||
? loadedPlugins.OrderBy(plugin => plugin.DalamudInterface?.UiBuilder.MaxDrawTime ?? 0)
|
||||
: loadedPlugins.OrderByDescending(plugin => plugin.DalamudInterface?.UiBuilder.MaxDrawTime ?? 0),
|
||||
3 => sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending
|
||||
? loadedPlugins.OrderBy(plugin => plugin.DalamudInterface?.UiBuilder.DrawTimeHistory.Average())
|
||||
: loadedPlugins.OrderByDescending(plugin => plugin.DalamudInterface?.UiBuilder.DrawTimeHistory.Average()),
|
||||
? loadedPlugins.OrderBy(plugin => plugin.DalamudInterface?.UiBuilder.DrawTimeHistory.DefaultIfEmpty().Average() ?? 0)
|
||||
: loadedPlugins.OrderByDescending(plugin => plugin.DalamudInterface?.UiBuilder.DrawTimeHistory.DefaultIfEmpty().Average() ?? 0),
|
||||
_ => loadedPlugins,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -91,11 +91,7 @@ public class HappyEyeballsCallback : IDisposable
|
|||
private async Task<NetworkStream> AttemptConnection(IPAddress address, int port, CancellationToken token, CancellationToken delayToken)
|
||||
{
|
||||
await AsyncUtils.CancellableDelay(-1, delayToken).ConfigureAwait(false);
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return Task.FromCanceled<NetworkStream>(token).Result;
|
||||
}
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
|
||||
{
|
||||
|
|
@ -116,9 +112,9 @@ public class HappyEyeballsCallback : IDisposable
|
|||
|
||||
private async Task<List<IPAddress>> GetSortedAddresses(string hostname, CancellationToken token)
|
||||
{
|
||||
// This method abuses DNS ordering and LINQ a bit. We can normally assume that "addresses" will be provided in
|
||||
// This method abuses DNS ordering and LINQ a bit. We can normally assume that addresses will be provided in
|
||||
// the order the system wants to use. GroupBy will return its groups *in the order they're discovered*. Meaning,
|
||||
// the first group created will always be the "preferred" group, and all other groups are in preference order.
|
||||
// the first group created will always be the preferred group, and all other groups are in preference order.
|
||||
// This means a straight zipper merge is nice and clean and gives us most -> least preferred, repeating.
|
||||
var dnsRecords = await Dns.GetHostAddressesAsync(hostname, this.forcedAddressFamily, token);
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public static class AsyncUtils
|
|||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(millisecondsDelay, cancellationToken);
|
||||
await Task.Delay(millisecondsDelay, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
|
|
|||
23
README.md
23
README.md
|
|
@ -26,23 +26,22 @@ Thanks to Mino, whose work has made this possible!
|
|||
These components are used in order to load Dalamud into a target process.
|
||||
Dalamud can be loaded via DLL injection, or by rewriting a process' entrypoint.
|
||||
|
||||
| Name | Purpose |
|
||||
|---|---|
|
||||
| *Dalamud.Injector.Boot* (C++) | Loads the .NET Core runtime into a process via hostfxr and kicks off Dalamud.Injector |
|
||||
| *Dalamud.Injector* (C#) | Performs DLL injection on the target process |
|
||||
| *Dalamud.Boot* (C++) | Loads the .NET Core runtime into the active process and kicks off Dalamud, or rewrites a target process' entrypoint to do so |
|
||||
| *Dalamud* (C#) | Core API, game bindings, plugin framework |
|
||||
| *Dalamud.CorePlugin* (C#) | Testbed plugin that can access Dalamud internals, to prototype new Dalamud features |
|
||||
| Name | Purpose |
|
||||
|-------------------------------|------------------------------------------------------------------------------------------------------------------------------|
|
||||
| *Dalamud.Injector.Boot* (C++) | Loads the .NET Core runtime into a process via hostfxr and kicks off Dalamud.Injector |
|
||||
| *Dalamud.Injector* (C#) | Performs DLL injection on the target process |
|
||||
| *Dalamud.Boot* (C++) | Loads the .NET Core runtime into the active process and kicks off Dalamud, or rewrites a target process' entrypoint to do so |
|
||||
| *Dalamud* (C#) | Core API, game bindings, plugin framework |
|
||||
| *Dalamud.CorePlugin* (C#) | Testbed plugin that can access Dalamud internals, to prototype new Dalamud features |
|
||||
|
||||
## Branches
|
||||
|
||||
We are currently working from the following branches.
|
||||
|
||||
| Name | Purpose | .NET Version | Track |
|
||||
|---|---|---|---|
|
||||
| *master* | Current release branch | .NET 6.0.3 (March 2022) | Release & Staging |
|
||||
| *net7* | Upgrade to .NET 7 | .NET 7.0.0 (November 2022) | net7 |
|
||||
| *api3* | Legacy version, no longer in active use | .NET Framework 4.7.2 (April 2017) | - |
|
||||
| Name | API Level | Purpose | .NET Version | Track |
|
||||
|----------|-----------|------------------------------------------------------------|----------------------------|-------------------|
|
||||
| *master* | **8** | Current release branch | .NET 7.0.0 (November 2022) | Release & Staging |
|
||||
| *v9* | **9** | Next major version, slated for release alongside Patch 6.5 | .NET 7.0.0 (November 2022) | v9 |
|
||||
|
||||
<br>
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 8e25367e5f7d3acbe8e2b2a81121852b1ea1291c
|
||||
Subproject commit 3595efd84f467e2c388f6e6b006a4e0764c46fdf
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 262d3b0668196fb236e2191c4a37e9be94e5a7a3
|
||||
Subproject commit 2f37349ffd778561a1103a650683116c43edc86c
|
||||
Loading…
Add table
Add a link
Reference in a new issue