mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-02-14 20:07:43 +01:00
Compare commits
No commits in common. "master" and "14.0.2.1" have entirely different histories.
30 changed files with 559 additions and 748 deletions
|
|
@ -94,8 +94,6 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
||||||
/// <param name="listener">The listener to unregister.</param>
|
/// <param name="listener">The listener to unregister.</param>
|
||||||
internal void UnregisterListener(AddonLifecycleEventListener listener)
|
internal void UnregisterListener(AddonLifecycleEventListener listener)
|
||||||
{
|
{
|
||||||
listener.IsRequestedToClear = true;
|
|
||||||
|
|
||||||
if (this.isInvokingListeners)
|
if (this.isInvokingListeners)
|
||||||
{
|
{
|
||||||
this.framework.RunOnTick(() => this.UnregisterListenerMethod(listener));
|
this.framework.RunOnTick(() => this.UnregisterListenerMethod(listener));
|
||||||
|
|
@ -124,8 +122,6 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
||||||
{
|
{
|
||||||
foreach (var listener in globalListeners)
|
foreach (var listener in globalListeners)
|
||||||
{
|
{
|
||||||
if (listener.IsRequestedToClear) continue;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
listener.FunctionDelegate.Invoke(eventType, args);
|
listener.FunctionDelegate.Invoke(eventType, args);
|
||||||
|
|
@ -142,8 +138,6 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
||||||
{
|
{
|
||||||
foreach (var listener in addonListener)
|
foreach (var listener in addonListener)
|
||||||
{
|
{
|
||||||
if (listener.IsRequestedToClear) continue;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
listener.FunctionDelegate.Invoke(eventType, args);
|
listener.FunctionDelegate.Invoke(eventType, args);
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,4 @@ internal class AddonLifecycleEventListener
|
||||||
/// Gets the delegate this listener invokes.
|
/// Gets the delegate this listener invokes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IAddonLifecycle.AddonEventDelegate FunctionDelegate { get; init; }
|
public IAddonLifecycle.AddonEventDelegate FunctionDelegate { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets if the listener is requested to be cleared.
|
|
||||||
/// </summary>
|
|
||||||
internal bool IsRequestedToClear { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,8 +107,6 @@ internal unsafe class AgentLifecycle : IInternalDisposableService
|
||||||
/// <param name="listener">The listener to unregister.</param>
|
/// <param name="listener">The listener to unregister.</param>
|
||||||
internal void UnregisterListener(AgentLifecycleEventListener listener)
|
internal void UnregisterListener(AgentLifecycleEventListener listener)
|
||||||
{
|
{
|
||||||
listener.IsRequestedToClear = true;
|
|
||||||
|
|
||||||
if (this.isInvokingListeners)
|
if (this.isInvokingListeners)
|
||||||
{
|
{
|
||||||
this.framework.RunOnTick(() => this.UnregisterListenerMethod(listener));
|
this.framework.RunOnTick(() => this.UnregisterListenerMethod(listener));
|
||||||
|
|
@ -137,8 +135,6 @@ internal unsafe class AgentLifecycle : IInternalDisposableService
|
||||||
{
|
{
|
||||||
foreach (var listener in globalListeners)
|
foreach (var listener in globalListeners)
|
||||||
{
|
{
|
||||||
if (listener.IsRequestedToClear) continue;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
listener.FunctionDelegate.Invoke(eventType, args);
|
listener.FunctionDelegate.Invoke(eventType, args);
|
||||||
|
|
@ -155,8 +151,6 @@ internal unsafe class AgentLifecycle : IInternalDisposableService
|
||||||
{
|
{
|
||||||
foreach (var listener in agentListener)
|
foreach (var listener in agentListener)
|
||||||
{
|
{
|
||||||
if (listener.IsRequestedToClear) continue;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
listener.FunctionDelegate.Invoke(eventType, args);
|
listener.FunctionDelegate.Invoke(eventType, args);
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,4 @@ public class AgentLifecycleEventListener
|
||||||
/// Gets the delegate this listener invokes.
|
/// Gets the delegate this listener invokes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IAgentLifecycle.AgentEventDelegate FunctionDelegate { get; init; }
|
public IAgentLifecycle.AgentEventDelegate FunctionDelegate { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets if the listener is requested to be cleared.
|
|
||||||
/// </summary>
|
|
||||||
internal bool IsRequestedToClear { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,311 +0,0 @@
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
|
||||||
|
|
||||||
namespace Dalamud.Game.ClientState.Customize;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This collection represents customization data a <see cref="ICharacter"/> has.
|
|
||||||
/// </summary>
|
|
||||||
public interface ICustomizeData
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current race.
|
|
||||||
/// E.g., Miqo'te, Aura.
|
|
||||||
/// </summary>
|
|
||||||
public byte Race { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current sex.
|
|
||||||
/// </summary>
|
|
||||||
public byte Sex { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current body type.
|
|
||||||
/// </summary>
|
|
||||||
public byte BodyType { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current height (0 to 100).
|
|
||||||
/// </summary>
|
|
||||||
public byte Height { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current tribe.
|
|
||||||
/// E.g., Seeker of the Sun, Keeper of the Moon.
|
|
||||||
/// </summary>
|
|
||||||
public byte Tribe { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current face (1 to 4).
|
|
||||||
/// </summary>
|
|
||||||
public byte Face { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current hairstyle.
|
|
||||||
/// </summary>
|
|
||||||
public byte Hairstyle { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current skin color.
|
|
||||||
/// </summary>
|
|
||||||
public byte SkinColor { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current color of the left eye.
|
|
||||||
/// </summary>
|
|
||||||
public byte EyeColorLeft { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current color of the right eye.
|
|
||||||
/// </summary>
|
|
||||||
public byte EyeColorRight { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current main hair color.
|
|
||||||
/// </summary>
|
|
||||||
public byte HairColor { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current highlight hair color.
|
|
||||||
/// </summary>
|
|
||||||
public byte HighlightsColor { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current tattoo color.
|
|
||||||
/// </summary>
|
|
||||||
public byte TattooColor { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current eyebrow type.
|
|
||||||
/// </summary>
|
|
||||||
public byte Eyebrows { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current nose type.
|
|
||||||
/// </summary>
|
|
||||||
public byte Nose { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current jaw type.
|
|
||||||
/// </summary>
|
|
||||||
public byte Jaw { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current lip color fur pattern.
|
|
||||||
/// </summary>
|
|
||||||
public byte LipColorFurPattern { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current muscle mass value.
|
|
||||||
/// </summary>
|
|
||||||
public byte MuscleMass { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current tail type (1 to 4).
|
|
||||||
/// </summary>
|
|
||||||
public byte TailShape { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current bust size (0 to 100).
|
|
||||||
/// </summary>
|
|
||||||
public byte BustSize { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current color of the face paint.
|
|
||||||
/// </summary>
|
|
||||||
public byte FacePaintColor { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether highlight color is used.
|
|
||||||
/// </summary>
|
|
||||||
public bool Highlights { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether this facial feature is used.
|
|
||||||
/// </summary>
|
|
||||||
public bool FacialFeature1 { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="FacialFeature1"/>
|
|
||||||
public bool FacialFeature2 { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="FacialFeature1"/>
|
|
||||||
public bool FacialFeature3 { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="FacialFeature1"/>
|
|
||||||
public bool FacialFeature4 { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="FacialFeature1"/>
|
|
||||||
public bool FacialFeature5 { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="FacialFeature1"/>
|
|
||||||
public bool FacialFeature6 { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="FacialFeature1"/>
|
|
||||||
public bool FacialFeature7 { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the legacy tattoo is used.
|
|
||||||
/// </summary>
|
|
||||||
public bool LegacyTattoo { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current eye shape type.
|
|
||||||
/// </summary>
|
|
||||||
public byte EyeShape { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether small iris is used.
|
|
||||||
/// </summary>
|
|
||||||
public bool SmallIris { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current mouth type.
|
|
||||||
/// </summary>
|
|
||||||
public byte Mouth { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether lipstick is used.
|
|
||||||
/// </summary>
|
|
||||||
public bool Lipstick { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current face paint type.
|
|
||||||
/// </summary>
|
|
||||||
public byte FacePaint { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether face paint reversed is used.
|
|
||||||
/// </summary>
|
|
||||||
public bool FacePaintReversed { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
internal readonly unsafe struct CustomizeData : ICustomizeData
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the address of the customize data struct in memory.
|
|
||||||
/// </summary>
|
|
||||||
public readonly nint Address;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="CustomizeData"/> struct.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="address">Address of the status list.</param>
|
|
||||||
internal CustomizeData(nint address)
|
|
||||||
{
|
|
||||||
this.Address = address;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte Race => this.Struct->Race;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte Sex => this.Struct->Sex;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte BodyType => this.Struct->BodyType;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte Height => this.Struct->Height;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte Tribe => this.Struct->Tribe;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte Face => this.Struct->Face;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte Hairstyle => this.Struct->Hairstyle;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte SkinColor => this.Struct->SkinColor;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte EyeColorLeft => this.Struct->EyeColorLeft;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte EyeColorRight => this.Struct->EyeColorRight;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte HairColor => this.Struct->HairColor;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte HighlightsColor => this.Struct->HighlightsColor;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte TattooColor => this.Struct->TattooColor;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte Eyebrows => this.Struct->Eyebrows;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte Nose => this.Struct->Nose;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte Jaw => this.Struct->Jaw;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte LipColorFurPattern => this.Struct->LipColorFurPattern;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte MuscleMass => this.Struct->MuscleMass;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte TailShape => this.Struct->TailShape;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte BustSize => this.Struct->BustSize;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte FacePaintColor => this.Struct->FacePaintColor;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool Highlights => this.Struct->Highlights;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool FacialFeature1 => this.Struct->FacialFeature1;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool FacialFeature2 => this.Struct->FacialFeature2;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool FacialFeature3 => this.Struct->FacialFeature3;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool FacialFeature4 => this.Struct->FacialFeature4;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool FacialFeature5 => this.Struct->FacialFeature5;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool FacialFeature6 => this.Struct->FacialFeature6;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool FacialFeature7 => this.Struct->FacialFeature7;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool LegacyTattoo => this.Struct->LegacyTattoo;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte EyeShape => this.Struct->EyeShape;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool SmallIris => this.Struct->SmallIris;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte Mouth => this.Struct->Mouth;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool Lipstick => this.Struct->Lipstick;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public byte FacePaint => this.Struct->FacePaint;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool FacePaintReversed => this.Struct->FacePaintReversed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the underlying structure.
|
|
||||||
/// </summary>
|
|
||||||
internal FFXIVClientStructs.FFXIV.Client.Game.Character.CustomizeData* Struct =>
|
|
||||||
(FFXIVClientStructs.FFXIV.Client.Game.Character.CustomizeData*)this.Address;
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
using Dalamud.Data;
|
using Dalamud.Data;
|
||||||
using Dalamud.Game.ClientState.Customize;
|
|
||||||
using Dalamud.Game.ClientState.Objects.Enums;
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Utility;
|
|
||||||
|
|
||||||
using Lumina.Excel;
|
using Lumina.Excel;
|
||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.Sheets;
|
||||||
|
|
@ -15,73 +13,68 @@ namespace Dalamud.Game.ClientState.Objects.Types;
|
||||||
public interface ICharacter : IGameObject
|
public interface ICharacter : IGameObject
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current HP of this character.
|
/// Gets the current HP of this Chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint CurrentHp { get; }
|
public uint CurrentHp { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the maximum HP of this character.
|
/// Gets the maximum HP of this Chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint MaxHp { get; }
|
public uint MaxHp { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current MP of this character.
|
/// Gets the current MP of this Chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint CurrentMp { get; }
|
public uint CurrentMp { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the maximum MP of this character.
|
/// Gets the maximum MP of this Chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint MaxMp { get; }
|
public uint MaxMp { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current GP of this character.
|
/// Gets the current GP of this Chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint CurrentGp { get; }
|
public uint CurrentGp { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the maximum GP of this character.
|
/// Gets the maximum GP of this Chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint MaxGp { get; }
|
public uint MaxGp { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current CP of this character.
|
/// Gets the current CP of this Chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint CurrentCp { get; }
|
public uint CurrentCp { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the maximum CP of this character.
|
/// Gets the maximum CP of this Chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint MaxCp { get; }
|
public uint MaxCp { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the shield percentage of this character.
|
/// Gets the shield percentage of this Chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte ShieldPercentage { get; }
|
public byte ShieldPercentage { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the ClassJob of this character.
|
/// Gets the ClassJob of this Chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RowRef<ClassJob> ClassJob { get; }
|
public RowRef<ClassJob> ClassJob { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the level of this character.
|
/// Gets the level of this Chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte Level { get; }
|
public byte Level { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a byte array describing the visual appearance of this character.
|
/// Gets a byte array describing the visual appearance of this Chara.
|
||||||
/// Indexed by <see cref="CustomizeIndex"/>.
|
/// Indexed by <see cref="CustomizeIndex"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte[] Customize { get; }
|
public byte[] Customize { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the underlying CustomizeData struct for this character.
|
/// Gets the Free Company tag of this chara.
|
||||||
/// </summary>
|
|
||||||
public ICustomizeData CustomizeData { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the Free Company tag of this character.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SeString CompanyTag { get; }
|
public SeString CompanyTag { get; }
|
||||||
|
|
||||||
|
|
@ -123,7 +116,7 @@ internal unsafe class Character : GameObject, ICharacter
|
||||||
/// This represents a non-static entity.
|
/// This represents a non-static entity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">The address of this character in memory.</param>
|
/// <param name="address">The address of this character in memory.</param>
|
||||||
internal Character(nint address)
|
internal Character(IntPtr address)
|
||||||
: base(address)
|
: base(address)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -162,12 +155,8 @@ internal unsafe class Character : GameObject, ICharacter
|
||||||
public byte Level => this.Struct->CharacterData.Level;
|
public byte Level => this.Struct->CharacterData.Level;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[Api15ToDo("Do not allocate on each call, use the CS Span and let consumers do allocation if necessary")]
|
|
||||||
public byte[] Customize => this.Struct->DrawData.CustomizeData.Data.ToArray();
|
public byte[] Customize => this.Struct->DrawData.CustomizeData.Data.ToArray();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ICustomizeData CustomizeData => new CustomizeData((nint)(&this.Struct->DrawData.CustomizeData));
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public SeString CompanyTag => SeString.Parse(this.Struct->FreeCompanyTag);
|
public SeString CompanyTag => SeString.Parse(this.Struct->FreeCompanyTag);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
using Dalamud.Data;
|
using Dalamud.Data;
|
||||||
using Dalamud.Game.Gui;
|
using Dalamud.Game.Gui;
|
||||||
using Dalamud.Hooking;
|
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Logging.Internal;
|
||||||
|
|
@ -17,9 +16,7 @@ using FFXIVClientStructs.FFXIV.Component.Exd;
|
||||||
using Lumina.Excel;
|
using Lumina.Excel;
|
||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.Sheets;
|
||||||
|
|
||||||
using AchievementSheet = Lumina.Excel.Sheets.Achievement;
|
|
||||||
using ActionSheet = Lumina.Excel.Sheets.Action;
|
using ActionSheet = Lumina.Excel.Sheets.Action;
|
||||||
using CSAchievement = FFXIVClientStructs.FFXIV.Client.Game.UI.Achievement;
|
|
||||||
using InstanceContentSheet = Lumina.Excel.Sheets.InstanceContent;
|
using InstanceContentSheet = Lumina.Excel.Sheets.InstanceContent;
|
||||||
using PublicContentSheet = Lumina.Excel.Sheets.PublicContent;
|
using PublicContentSheet = Lumina.Excel.Sheets.PublicContent;
|
||||||
|
|
||||||
|
|
@ -33,6 +30,8 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
|
||||||
{
|
{
|
||||||
private static readonly ModuleLog Log = new(nameof(UnlockState));
|
private static readonly ModuleLog Log = new(nameof(UnlockState));
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<Type, HashSet<uint>> cachedUnlockedRowIds = [];
|
||||||
|
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly DataManager dataManager = Service<DataManager>.Get();
|
private readonly DataManager dataManager = Service<DataManager>.Get();
|
||||||
|
|
||||||
|
|
@ -45,38 +44,17 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly RecipeData recipeData = Service<RecipeData>.Get();
|
private readonly RecipeData recipeData = Service<RecipeData>.Get();
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<Type, HashSet<uint>> cachedUnlockedRowIds = [];
|
|
||||||
private readonly Hook<CSAchievement.Delegates.SetAchievementCompleted> setAchievementCompletedHook;
|
|
||||||
private readonly Hook<TitleList.Delegates.SetTitleUnlocked> setTitleUnlockedHook;
|
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private UnlockState()
|
private UnlockState()
|
||||||
{
|
{
|
||||||
this.clientState.Login += this.OnLogin;
|
this.clientState.Login += this.OnLogin;
|
||||||
this.clientState.Logout += this.OnLogout;
|
this.clientState.Logout += this.OnLogout;
|
||||||
this.gameGui.AgentUpdate += this.OnAgentUpdate;
|
this.gameGui.AgentUpdate += this.OnAgentUpdate;
|
||||||
|
|
||||||
this.setAchievementCompletedHook = Hook<CSAchievement.Delegates.SetAchievementCompleted>.FromAddress(
|
|
||||||
(nint)CSAchievement.MemberFunctionPointers.SetAchievementCompleted,
|
|
||||||
this.SetAchievementCompletedDetour);
|
|
||||||
|
|
||||||
this.setTitleUnlockedHook = Hook<TitleList.Delegates.SetTitleUnlocked>.FromAddress(
|
|
||||||
(nint)TitleList.MemberFunctionPointers.SetTitleUnlocked,
|
|
||||||
this.SetTitleUnlockedDetour);
|
|
||||||
|
|
||||||
this.setAchievementCompletedHook.Enable();
|
|
||||||
this.setTitleUnlockedHook.Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public event IUnlockState.UnlockDelegate Unlock;
|
public event IUnlockState.UnlockDelegate Unlock;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool IsAchievementListLoaded => CSAchievement.Instance()->IsLoaded();
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool IsTitleListLoaded => UIState.Instance()->TitleList.DataReceived;
|
|
||||||
|
|
||||||
private bool IsLoaded => PlayerState.Instance()->IsLoaded;
|
private bool IsLoaded => PlayerState.Instance()->IsLoaded;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
@ -85,21 +63,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
|
||||||
this.clientState.Login -= this.OnLogin;
|
this.clientState.Login -= this.OnLogin;
|
||||||
this.clientState.Logout -= this.OnLogout;
|
this.clientState.Logout -= this.OnLogout;
|
||||||
this.gameGui.AgentUpdate -= this.OnAgentUpdate;
|
this.gameGui.AgentUpdate -= this.OnAgentUpdate;
|
||||||
|
|
||||||
this.setAchievementCompletedHook.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool IsAchievementComplete(AchievementSheet row)
|
|
||||||
{
|
|
||||||
// Only check for login state here as individual Achievements
|
|
||||||
// may be flagged as complete when you unlock them, regardless
|
|
||||||
// of whether the full Achievements list was loaded or not.
|
|
||||||
|
|
||||||
if (!this.IsLoaded)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return CSAchievement.Instance()->IsComplete((int)row.RowId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
@ -108,15 +71,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
|
||||||
return this.IsUnlockLinkUnlocked(row.UnlockLink.RowId);
|
return this.IsUnlockLinkUnlocked(row.UnlockLink.RowId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool IsAdventureComplete(Adventure row)
|
|
||||||
{
|
|
||||||
if (!this.IsLoaded)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return PlayerState.Instance()->IsAdventureComplete(row.RowId - 0x210000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool IsAetherCurrentUnlocked(AetherCurrent row)
|
public bool IsAetherCurrentUnlocked(AetherCurrent row)
|
||||||
{
|
{
|
||||||
|
|
@ -461,19 +415,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
|
||||||
return PlayerState.Instance()->IsSecretRecipeBookUnlocked(row.RowId);
|
return PlayerState.Instance()->IsSecretRecipeBookUnlocked(row.RowId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool IsTitleUnlocked(Title row)
|
|
||||||
{
|
|
||||||
// Only check for login state here as individual Titles
|
|
||||||
// may be flagged as complete when you unlock them, regardless
|
|
||||||
// of whether the full Titles list was loaded or not.
|
|
||||||
|
|
||||||
if (!this.IsLoaded)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return UIState.Instance()->TitleList.IsTitleUnlocked((ushort)row.RowId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool IsTraitUnlocked(Trait row)
|
public bool IsTraitUnlocked(Trait row)
|
||||||
{
|
{
|
||||||
|
|
@ -523,15 +464,9 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
|
||||||
if (!this.IsLoaded || rowRef.IsUntyped)
|
if (!this.IsLoaded || rowRef.IsUntyped)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (rowRef.TryGetValue<AchievementSheet>(out var achievementRow))
|
|
||||||
return this.IsAchievementComplete(achievementRow);
|
|
||||||
|
|
||||||
if (rowRef.TryGetValue<ActionSheet>(out var actionRow))
|
if (rowRef.TryGetValue<ActionSheet>(out var actionRow))
|
||||||
return this.IsActionUnlocked(actionRow);
|
return this.IsActionUnlocked(actionRow);
|
||||||
|
|
||||||
if (rowRef.TryGetValue<Adventure>(out var adventureRow))
|
|
||||||
return this.IsAdventureComplete(adventureRow);
|
|
||||||
|
|
||||||
if (rowRef.TryGetValue<AetherCurrent>(out var aetherCurrentRow))
|
if (rowRef.TryGetValue<AetherCurrent>(out var aetherCurrentRow))
|
||||||
return this.IsAetherCurrentUnlocked(aetherCurrentRow);
|
return this.IsAetherCurrentUnlocked(aetherCurrentRow);
|
||||||
|
|
||||||
|
|
@ -637,9 +572,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
|
||||||
if (rowRef.TryGetValue<SecretRecipeBook>(out var secretRecipeBookRow))
|
if (rowRef.TryGetValue<SecretRecipeBook>(out var secretRecipeBookRow))
|
||||||
return this.IsSecretRecipeBookUnlocked(secretRecipeBookRow);
|
return this.IsSecretRecipeBookUnlocked(secretRecipeBookRow);
|
||||||
|
|
||||||
if (rowRef.TryGetValue<Title>(out var titleRow))
|
|
||||||
return this.IsTitleUnlocked(titleRow);
|
|
||||||
|
|
||||||
if (rowRef.TryGetValue<Trait>(out var traitRow))
|
if (rowRef.TryGetValue<Trait>(out var traitRow))
|
||||||
return this.IsTraitUnlocked(traitRow);
|
return this.IsTraitUnlocked(traitRow);
|
||||||
|
|
||||||
|
|
@ -689,26 +621,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
|
||||||
this.Update();
|
this.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetAchievementCompletedDetour(CSAchievement* thisPtr, uint id)
|
|
||||||
{
|
|
||||||
this.setAchievementCompletedHook.Original(thisPtr, id);
|
|
||||||
|
|
||||||
if (!this.IsLoaded)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.RaiseUnlockSafely((RowRef)LuminaUtils.CreateRef<AchievementSheet>(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetTitleUnlockedDetour(TitleList* thisPtr, ushort id)
|
|
||||||
{
|
|
||||||
this.setTitleUnlockedHook.Original(thisPtr, id);
|
|
||||||
|
|
||||||
if (!this.IsLoaded)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.RaiseUnlockSafely((RowRef)LuminaUtils.CreateRef<Title>(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
if (!this.IsLoaded)
|
if (!this.IsLoaded)
|
||||||
|
|
@ -716,10 +628,7 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
|
||||||
|
|
||||||
Log.Verbose("Checking for new unlocks...");
|
Log.Verbose("Checking for new unlocks...");
|
||||||
|
|
||||||
// Do not check for Achievements or Titles here!
|
|
||||||
|
|
||||||
this.UpdateUnlocksForSheet<ActionSheet>();
|
this.UpdateUnlocksForSheet<ActionSheet>();
|
||||||
this.UpdateUnlocksForSheet<Adventure>();
|
|
||||||
this.UpdateUnlocksForSheet<AetherCurrent>();
|
this.UpdateUnlocksForSheet<AetherCurrent>();
|
||||||
this.UpdateUnlocksForSheet<AetherCurrentCompFlgSet>();
|
this.UpdateUnlocksForSheet<AetherCurrentCompFlgSet>();
|
||||||
this.UpdateUnlocksForSheet<AozAction>();
|
this.UpdateUnlocksForSheet<AozAction>();
|
||||||
|
|
@ -766,6 +675,7 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
|
||||||
// For some other day:
|
// For some other day:
|
||||||
// - FishingSpot
|
// - FishingSpot
|
||||||
// - Spearfishing
|
// - Spearfishing
|
||||||
|
// - Adventure (Sightseeing)
|
||||||
// - MinerFolkloreTome
|
// - MinerFolkloreTome
|
||||||
// - BotanistFolkloreTome
|
// - BotanistFolkloreTome
|
||||||
// - FishingFolkloreTome
|
// - FishingFolkloreTome
|
||||||
|
|
@ -778,6 +688,8 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
|
||||||
// - EmjCostume
|
// - EmjCostume
|
||||||
|
|
||||||
// Probably not happening, because it requires fetching data from server:
|
// Probably not happening, because it requires fetching data from server:
|
||||||
|
// - Achievements
|
||||||
|
// - Titles
|
||||||
// - Bozjan Field Notes
|
// - Bozjan Field Notes
|
||||||
// - Support/Phantom Jobs, which require to be in Occult Crescent, because it checks the jobs level for != 0
|
// - Support/Phantom Jobs, which require to be in Occult Crescent, because it checks the jobs level for != 0
|
||||||
}
|
}
|
||||||
|
|
@ -800,21 +712,16 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
|
||||||
|
|
||||||
// Log.Verbose($"Unlock detected: {typeof(T).Name}#{row.RowId}");
|
// Log.Verbose($"Unlock detected: {typeof(T).Name}#{row.RowId}");
|
||||||
|
|
||||||
this.RaiseUnlockSafely((RowRef)rowRef);
|
foreach (var action in Delegate.EnumerateInvocationList(this.Unlock))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RaiseUnlockSafely(RowRef rowRef)
|
|
||||||
{
|
|
||||||
foreach (var action in Delegate.EnumerateInvocationList(this.Unlock))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
action(rowRef);
|
try
|
||||||
}
|
{
|
||||||
catch (Exception ex)
|
action((RowRef)rowRef);
|
||||||
{
|
}
|
||||||
Log.Error(ex, "Exception during raise of {handler}", action.Method);
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Exception during raise of {handler}", action.Method);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -844,21 +751,9 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public event IUnlockState.UnlockDelegate? Unlock;
|
public event IUnlockState.UnlockDelegate? Unlock;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool IsAchievementListLoaded => this.unlockStateService.IsAchievementListLoaded;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool IsTitleListLoaded => this.unlockStateService.IsTitleListLoaded;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool IsAchievementComplete(AchievementSheet row) => this.unlockStateService.IsAchievementComplete(row);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool IsActionUnlocked(ActionSheet row) => this.unlockStateService.IsActionUnlocked(row);
|
public bool IsActionUnlocked(ActionSheet row) => this.unlockStateService.IsActionUnlocked(row);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool IsAdventureComplete(Adventure row) => this.unlockStateService.IsAdventureComplete(row);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool IsAetherCurrentCompFlgSetUnlocked(AetherCurrentCompFlgSet row) => this.unlockStateService.IsAetherCurrentCompFlgSetUnlocked(row);
|
public bool IsAetherCurrentCompFlgSetUnlocked(AetherCurrentCompFlgSet row) => this.unlockStateService.IsAetherCurrentCompFlgSetUnlocked(row);
|
||||||
|
|
||||||
|
|
@ -979,9 +874,6 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool IsSecretRecipeBookUnlocked(SecretRecipeBook row) => this.unlockStateService.IsSecretRecipeBookUnlocked(row);
|
public bool IsSecretRecipeBookUnlocked(SecretRecipeBook row) => this.unlockStateService.IsSecretRecipeBookUnlocked(row);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool IsTitleUnlocked(Title row) => this.unlockStateService.IsTitleUnlocked(row);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool IsTraitUnlocked(Trait row) => this.unlockStateService.IsTraitUnlocked(row);
|
public bool IsTraitUnlocked(Trait row) => this.unlockStateService.IsTraitUnlocked(row);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ using Dalamud.Configuration.Internal;
|
||||||
using Dalamud.Hooking.Internal;
|
using Dalamud.Hooking.Internal;
|
||||||
using Dalamud.Hooking.Internal.Verification;
|
using Dalamud.Hooking.Internal.Verification;
|
||||||
|
|
||||||
using TerraFX.Interop.Windows;
|
|
||||||
|
|
||||||
namespace Dalamud.Hooking;
|
namespace Dalamud.Hooking;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -22,8 +20,6 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate
|
||||||
private const ulong IMAGE_ORDINAL_FLAG64 = 0x8000000000000000;
|
private const ulong IMAGE_ORDINAL_FLAG64 = 0x8000000000000000;
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
private const uint IMAGE_ORDINAL_FLAG32 = 0x80000000;
|
private const uint IMAGE_ORDINAL_FLAG32 = 0x80000000;
|
||||||
// ReSharper disable once InconsistentNaming
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_IMPORT = 1;
|
|
||||||
#pragma warning restore SA1310
|
#pragma warning restore SA1310
|
||||||
|
|
||||||
private readonly IntPtr address;
|
private readonly IntPtr address;
|
||||||
|
|
@ -128,25 +124,25 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate
|
||||||
module ??= Process.GetCurrentProcess().MainModule;
|
module ??= Process.GetCurrentProcess().MainModule;
|
||||||
if (module == null)
|
if (module == null)
|
||||||
throw new InvalidOperationException("Current module is null?");
|
throw new InvalidOperationException("Current module is null?");
|
||||||
var pDos = (IMAGE_DOS_HEADER*)module.BaseAddress;
|
var pDos = (PeHeader.IMAGE_DOS_HEADER*)module.BaseAddress;
|
||||||
var pNt = (IMAGE_FILE_HEADER*)(module.BaseAddress + pDos->e_lfanew + 4);
|
var pNt = (PeHeader.IMAGE_FILE_HEADER*)(module.BaseAddress + (int)pDos->e_lfanew + 4);
|
||||||
var isPe64 = pNt->SizeOfOptionalHeader == Marshal.SizeOf<IMAGE_OPTIONAL_HEADER64>();
|
var isPe64 = pNt->SizeOfOptionalHeader == Marshal.SizeOf<PeHeader.IMAGE_OPTIONAL_HEADER64>();
|
||||||
IMAGE_DATA_DIRECTORY* pDataDirectory;
|
PeHeader.IMAGE_DATA_DIRECTORY* pDataDirectory;
|
||||||
if (isPe64)
|
if (isPe64)
|
||||||
{
|
{
|
||||||
var pOpt = (IMAGE_OPTIONAL_HEADER64*)(module.BaseAddress + pDos->e_lfanew + 4 + Marshal.SizeOf<IMAGE_FILE_HEADER>());
|
var pOpt = (PeHeader.IMAGE_OPTIONAL_HEADER64*)(module.BaseAddress + (int)pDos->e_lfanew + 4 + Marshal.SizeOf<PeHeader.IMAGE_FILE_HEADER>());
|
||||||
pDataDirectory = &pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
pDataDirectory = &pOpt->ImportTable;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var pOpt = (IMAGE_OPTIONAL_HEADER32*)(module.BaseAddress + pDos->e_lfanew + 4 + Marshal.SizeOf<IMAGE_FILE_HEADER>());
|
var pOpt = (PeHeader.IMAGE_OPTIONAL_HEADER32*)(module.BaseAddress + (int)pDos->e_lfanew + 4 + Marshal.SizeOf<PeHeader.IMAGE_FILE_HEADER>());
|
||||||
pDataDirectory = &pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
pDataDirectory = &pOpt->ImportTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
var moduleNameLowerWithNullTerminator = (moduleName + "\0").ToLowerInvariant();
|
var moduleNameLowerWithNullTerminator = (moduleName + "\0").ToLowerInvariant();
|
||||||
foreach (ref var importDescriptor in new Span<IMAGE_IMPORT_DESCRIPTOR>(
|
foreach (ref var importDescriptor in new Span<PeHeader.IMAGE_IMPORT_DESCRIPTOR>(
|
||||||
(IMAGE_IMPORT_DESCRIPTOR*)(module.BaseAddress + (int)pDataDirectory->VirtualAddress),
|
(PeHeader.IMAGE_IMPORT_DESCRIPTOR*)(module.BaseAddress + (int)pDataDirectory->VirtualAddress),
|
||||||
(int)(pDataDirectory->Size / Marshal.SizeOf<IMAGE_IMPORT_DESCRIPTOR>())))
|
(int)(pDataDirectory->Size / Marshal.SizeOf<PeHeader.IMAGE_IMPORT_DESCRIPTOR>())))
|
||||||
{
|
{
|
||||||
// Having all zero values signals the end of the table. We didn't find anything.
|
// Having all zero values signals the end of the table. We didn't find anything.
|
||||||
if (importDescriptor.Characteristics == 0)
|
if (importDescriptor.Characteristics == 0)
|
||||||
|
|
@ -252,7 +248,7 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate
|
||||||
ObjectDisposedException.ThrowIf(this.IsDisposed, this);
|
ObjectDisposedException.ThrowIf(this.IsDisposed, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe IntPtr FromImportHelper32(IntPtr baseAddress, ref IMAGE_IMPORT_DESCRIPTOR desc, ref IMAGE_DATA_DIRECTORY dir, string functionName, uint hintOrOrdinal)
|
private static unsafe IntPtr FromImportHelper32(IntPtr baseAddress, ref PeHeader.IMAGE_IMPORT_DESCRIPTOR desc, ref PeHeader.IMAGE_DATA_DIRECTORY dir, string functionName, uint hintOrOrdinal)
|
||||||
{
|
{
|
||||||
var importLookupsOversizedSpan = new Span<uint>((uint*)(baseAddress + (int)desc.OriginalFirstThunk), (int)((dir.Size - desc.OriginalFirstThunk) / Marshal.SizeOf<int>()));
|
var importLookupsOversizedSpan = new Span<uint>((uint*)(baseAddress + (int)desc.OriginalFirstThunk), (int)((dir.Size - desc.OriginalFirstThunk) / Marshal.SizeOf<int>()));
|
||||||
var importAddressesOversizedSpan = new Span<uint>((uint*)(baseAddress + (int)desc.FirstThunk), (int)((dir.Size - desc.FirstThunk) / Marshal.SizeOf<int>()));
|
var importAddressesOversizedSpan = new Span<uint>((uint*)(baseAddress + (int)desc.FirstThunk), (int)((dir.Size - desc.FirstThunk) / Marshal.SizeOf<int>()));
|
||||||
|
|
@ -302,7 +298,7 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate
|
||||||
throw new MissingMethodException("Specified method not found");
|
throw new MissingMethodException("Specified method not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe IntPtr FromImportHelper64(IntPtr baseAddress, ref IMAGE_IMPORT_DESCRIPTOR desc, ref IMAGE_DATA_DIRECTORY dir, string functionName, uint hintOrOrdinal)
|
private static unsafe IntPtr FromImportHelper64(IntPtr baseAddress, ref PeHeader.IMAGE_IMPORT_DESCRIPTOR desc, ref PeHeader.IMAGE_DATA_DIRECTORY dir, string functionName, uint hintOrOrdinal)
|
||||||
{
|
{
|
||||||
var importLookupsOversizedSpan = new Span<ulong>((ulong*)(baseAddress + (int)desc.OriginalFirstThunk), (int)((dir.Size - desc.OriginalFirstThunk) / Marshal.SizeOf<ulong>()));
|
var importLookupsOversizedSpan = new Span<ulong>((ulong*)(baseAddress + (int)desc.OriginalFirstThunk), (int)((dir.Size - desc.OriginalFirstThunk) / Marshal.SizeOf<ulong>()));
|
||||||
var importAddressesOversizedSpan = new Span<ulong>((ulong*)(baseAddress + (int)desc.FirstThunk), (int)((dir.Size - desc.FirstThunk) / Marshal.SizeOf<ulong>()));
|
var importAddressesOversizedSpan = new Span<ulong>((ulong*)(baseAddress + (int)desc.FirstThunk), (int)((dir.Size - desc.FirstThunk) / Marshal.SizeOf<ulong>()));
|
||||||
|
|
|
||||||
390
Dalamud/Hooking/Internal/PeHeader.cs
Normal file
390
Dalamud/Hooking/Internal/PeHeader.cs
Normal file
|
|
@ -0,0 +1,390 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
#pragma warning disable
|
||||||
|
namespace Dalamud.Hooking.Internal;
|
||||||
|
|
||||||
|
internal class PeHeader
|
||||||
|
{
|
||||||
|
public struct IMAGE_DOS_HEADER
|
||||||
|
{
|
||||||
|
public UInt16 e_magic;
|
||||||
|
public UInt16 e_cblp;
|
||||||
|
public UInt16 e_cp;
|
||||||
|
public UInt16 e_crlc;
|
||||||
|
public UInt16 e_cparhdr;
|
||||||
|
public UInt16 e_minalloc;
|
||||||
|
public UInt16 e_maxalloc;
|
||||||
|
public UInt16 e_ss;
|
||||||
|
public UInt16 e_sp;
|
||||||
|
public UInt16 e_csum;
|
||||||
|
public UInt16 e_ip;
|
||||||
|
public UInt16 e_cs;
|
||||||
|
public UInt16 e_lfarlc;
|
||||||
|
public UInt16 e_ovno;
|
||||||
|
public UInt16 e_res_0;
|
||||||
|
public UInt16 e_res_1;
|
||||||
|
public UInt16 e_res_2;
|
||||||
|
public UInt16 e_res_3;
|
||||||
|
public UInt16 e_oemid;
|
||||||
|
public UInt16 e_oeminfo;
|
||||||
|
public UInt16 e_res2_0;
|
||||||
|
public UInt16 e_res2_1;
|
||||||
|
public UInt16 e_res2_2;
|
||||||
|
public UInt16 e_res2_3;
|
||||||
|
public UInt16 e_res2_4;
|
||||||
|
public UInt16 e_res2_5;
|
||||||
|
public UInt16 e_res2_6;
|
||||||
|
public UInt16 e_res2_7;
|
||||||
|
public UInt16 e_res2_8;
|
||||||
|
public UInt16 e_res2_9;
|
||||||
|
public UInt32 e_lfanew;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct IMAGE_DATA_DIRECTORY
|
||||||
|
{
|
||||||
|
public UInt32 VirtualAddress;
|
||||||
|
public UInt32 Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct IMAGE_OPTIONAL_HEADER32
|
||||||
|
{
|
||||||
|
public UInt16 Magic;
|
||||||
|
public Byte MajorLinkerVersion;
|
||||||
|
public Byte MinorLinkerVersion;
|
||||||
|
public UInt32 SizeOfCode;
|
||||||
|
public UInt32 SizeOfInitializedData;
|
||||||
|
public UInt32 SizeOfUninitializedData;
|
||||||
|
public UInt32 AddressOfEntryPoint;
|
||||||
|
public UInt32 BaseOfCode;
|
||||||
|
public UInt32 BaseOfData;
|
||||||
|
public UInt32 ImageBase;
|
||||||
|
public UInt32 SectionAlignment;
|
||||||
|
public UInt32 FileAlignment;
|
||||||
|
public UInt16 MajorOperatingSystemVersion;
|
||||||
|
public UInt16 MinorOperatingSystemVersion;
|
||||||
|
public UInt16 MajorImageVersion;
|
||||||
|
public UInt16 MinorImageVersion;
|
||||||
|
public UInt16 MajorSubsystemVersion;
|
||||||
|
public UInt16 MinorSubsystemVersion;
|
||||||
|
public UInt32 Win32VersionValue;
|
||||||
|
public UInt32 SizeOfImage;
|
||||||
|
public UInt32 SizeOfHeaders;
|
||||||
|
public UInt32 CheckSum;
|
||||||
|
public UInt16 Subsystem;
|
||||||
|
public UInt16 DllCharacteristics;
|
||||||
|
public UInt32 SizeOfStackReserve;
|
||||||
|
public UInt32 SizeOfStackCommit;
|
||||||
|
public UInt32 SizeOfHeapReserve;
|
||||||
|
public UInt32 SizeOfHeapCommit;
|
||||||
|
public UInt32 LoaderFlags;
|
||||||
|
public UInt32 NumberOfRvaAndSizes;
|
||||||
|
|
||||||
|
public IMAGE_DATA_DIRECTORY ExportTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY ImportTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY ResourceTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY ExceptionTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY CertificateTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY BaseRelocationTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY Debug;
|
||||||
|
public IMAGE_DATA_DIRECTORY Architecture;
|
||||||
|
public IMAGE_DATA_DIRECTORY GlobalPtr;
|
||||||
|
public IMAGE_DATA_DIRECTORY TLSTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY LoadConfigTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY BoundImport;
|
||||||
|
public IMAGE_DATA_DIRECTORY IAT;
|
||||||
|
public IMAGE_DATA_DIRECTORY DelayImportDescriptor;
|
||||||
|
public IMAGE_DATA_DIRECTORY CLRRuntimeHeader;
|
||||||
|
public IMAGE_DATA_DIRECTORY Reserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct IMAGE_OPTIONAL_HEADER64
|
||||||
|
{
|
||||||
|
public UInt16 Magic;
|
||||||
|
public Byte MajorLinkerVersion;
|
||||||
|
public Byte MinorLinkerVersion;
|
||||||
|
public UInt32 SizeOfCode;
|
||||||
|
public UInt32 SizeOfInitializedData;
|
||||||
|
public UInt32 SizeOfUninitializedData;
|
||||||
|
public UInt32 AddressOfEntryPoint;
|
||||||
|
public UInt32 BaseOfCode;
|
||||||
|
public UInt64 ImageBase;
|
||||||
|
public UInt32 SectionAlignment;
|
||||||
|
public UInt32 FileAlignment;
|
||||||
|
public UInt16 MajorOperatingSystemVersion;
|
||||||
|
public UInt16 MinorOperatingSystemVersion;
|
||||||
|
public UInt16 MajorImageVersion;
|
||||||
|
public UInt16 MinorImageVersion;
|
||||||
|
public UInt16 MajorSubsystemVersion;
|
||||||
|
public UInt16 MinorSubsystemVersion;
|
||||||
|
public UInt32 Win32VersionValue;
|
||||||
|
public UInt32 SizeOfImage;
|
||||||
|
public UInt32 SizeOfHeaders;
|
||||||
|
public UInt32 CheckSum;
|
||||||
|
public UInt16 Subsystem;
|
||||||
|
public UInt16 DllCharacteristics;
|
||||||
|
public UInt64 SizeOfStackReserve;
|
||||||
|
public UInt64 SizeOfStackCommit;
|
||||||
|
public UInt64 SizeOfHeapReserve;
|
||||||
|
public UInt64 SizeOfHeapCommit;
|
||||||
|
public UInt32 LoaderFlags;
|
||||||
|
public UInt32 NumberOfRvaAndSizes;
|
||||||
|
|
||||||
|
public IMAGE_DATA_DIRECTORY ExportTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY ImportTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY ResourceTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY ExceptionTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY CertificateTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY BaseRelocationTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY Debug;
|
||||||
|
public IMAGE_DATA_DIRECTORY Architecture;
|
||||||
|
public IMAGE_DATA_DIRECTORY GlobalPtr;
|
||||||
|
public IMAGE_DATA_DIRECTORY TLSTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY LoadConfigTable;
|
||||||
|
public IMAGE_DATA_DIRECTORY BoundImport;
|
||||||
|
public IMAGE_DATA_DIRECTORY IAT;
|
||||||
|
public IMAGE_DATA_DIRECTORY DelayImportDescriptor;
|
||||||
|
public IMAGE_DATA_DIRECTORY CLRRuntimeHeader;
|
||||||
|
public IMAGE_DATA_DIRECTORY Reserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct IMAGE_FILE_HEADER
|
||||||
|
{
|
||||||
|
public UInt16 Machine;
|
||||||
|
public UInt16 NumberOfSections;
|
||||||
|
public UInt32 TimeDateStamp;
|
||||||
|
public UInt32 PointerToSymbolTable;
|
||||||
|
public UInt32 NumberOfSymbols;
|
||||||
|
public UInt16 SizeOfOptionalHeader;
|
||||||
|
public UInt16 Characteristics;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
public struct IMAGE_SECTION_HEADER
|
||||||
|
{
|
||||||
|
[FieldOffset(0)]
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||||
|
public char[] Name;
|
||||||
|
[FieldOffset(8)]
|
||||||
|
public UInt32 VirtualSize;
|
||||||
|
[FieldOffset(12)]
|
||||||
|
public UInt32 VirtualAddress;
|
||||||
|
[FieldOffset(16)]
|
||||||
|
public UInt32 SizeOfRawData;
|
||||||
|
[FieldOffset(20)]
|
||||||
|
public UInt32 PointerToRawData;
|
||||||
|
[FieldOffset(24)]
|
||||||
|
public UInt32 PointerToRelocations;
|
||||||
|
[FieldOffset(28)]
|
||||||
|
public UInt32 PointerToLinenumbers;
|
||||||
|
[FieldOffset(32)]
|
||||||
|
public UInt16 NumberOfRelocations;
|
||||||
|
[FieldOffset(34)]
|
||||||
|
public UInt16 NumberOfLinenumbers;
|
||||||
|
[FieldOffset(36)]
|
||||||
|
public DataSectionFlags Characteristics;
|
||||||
|
|
||||||
|
public string Section
|
||||||
|
{
|
||||||
|
get { return new string(Name); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum DataSectionFlags : uint
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved for future use.
|
||||||
|
/// </summary>
|
||||||
|
TypeReg = 0x00000000,
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved for future use.
|
||||||
|
/// </summary>
|
||||||
|
TypeDsect = 0x00000001,
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved for future use.
|
||||||
|
/// </summary>
|
||||||
|
TypeNoLoad = 0x00000002,
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved for future use.
|
||||||
|
/// </summary>
|
||||||
|
TypeGroup = 0x00000004,
|
||||||
|
/// <summary>
|
||||||
|
/// The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
TypeNoPadded = 0x00000008,
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved for future use.
|
||||||
|
/// </summary>
|
||||||
|
TypeCopy = 0x00000010,
|
||||||
|
/// <summary>
|
||||||
|
/// The section contains executable code.
|
||||||
|
/// </summary>
|
||||||
|
ContentCode = 0x00000020,
|
||||||
|
/// <summary>
|
||||||
|
/// The section contains initialized data.
|
||||||
|
/// </summary>
|
||||||
|
ContentInitializedData = 0x00000040,
|
||||||
|
/// <summary>
|
||||||
|
/// The section contains uninitialized data.
|
||||||
|
/// </summary>
|
||||||
|
ContentUninitializedData = 0x00000080,
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved for future use.
|
||||||
|
/// </summary>
|
||||||
|
LinkOther = 0x00000100,
|
||||||
|
/// <summary>
|
||||||
|
/// The section contains comments or other information. The .drectve section has this type. This is valid for object files only.
|
||||||
|
/// </summary>
|
||||||
|
LinkInfo = 0x00000200,
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved for future use.
|
||||||
|
/// </summary>
|
||||||
|
TypeOver = 0x00000400,
|
||||||
|
/// <summary>
|
||||||
|
/// The section will not become part of the image. This is valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
LinkRemove = 0x00000800,
|
||||||
|
/// <summary>
|
||||||
|
/// The section contains COMDAT data. For more information, see section 5.5.6, COMDAT Sections (Object Only). This is valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
LinkComDat = 0x00001000,
|
||||||
|
/// <summary>
|
||||||
|
/// Reset speculative exceptions handling bits in the TLB entries for this section.
|
||||||
|
/// </summary>
|
||||||
|
NoDeferSpecExceptions = 0x00004000,
|
||||||
|
/// <summary>
|
||||||
|
/// The section contains data referenced through the global pointer (GP).
|
||||||
|
/// </summary>
|
||||||
|
RelativeGP = 0x00008000,
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved for future use.
|
||||||
|
/// </summary>
|
||||||
|
MemPurgeable = 0x00020000,
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved for future use.
|
||||||
|
/// </summary>
|
||||||
|
Memory16Bit = 0x00020000,
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved for future use.
|
||||||
|
/// </summary>
|
||||||
|
MemoryLocked = 0x00040000,
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved for future use.
|
||||||
|
/// </summary>
|
||||||
|
MemoryPreload = 0x00080000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on a 1-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align1Bytes = 0x00100000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on a 2-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align2Bytes = 0x00200000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on a 4-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align4Bytes = 0x00300000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on an 8-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align8Bytes = 0x00400000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on a 16-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align16Bytes = 0x00500000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on a 32-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align32Bytes = 0x00600000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on a 64-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align64Bytes = 0x00700000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on a 128-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align128Bytes = 0x00800000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on a 256-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align256Bytes = 0x00900000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on a 512-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align512Bytes = 0x00A00000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on a 1024-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align1024Bytes = 0x00B00000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on a 2048-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align2048Bytes = 0x00C00000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on a 4096-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align4096Bytes = 0x00D00000,
|
||||||
|
/// <summary>
|
||||||
|
/// Align data on an 8192-byte boundary. Valid only for object files.
|
||||||
|
/// </summary>
|
||||||
|
Align8192Bytes = 0x00E00000,
|
||||||
|
/// <summary>
|
||||||
|
/// The section contains extended relocations.
|
||||||
|
/// </summary>
|
||||||
|
LinkExtendedRelocationOverflow = 0x01000000,
|
||||||
|
/// <summary>
|
||||||
|
/// The section can be discarded as needed.
|
||||||
|
/// </summary>
|
||||||
|
MemoryDiscardable = 0x02000000,
|
||||||
|
/// <summary>
|
||||||
|
/// The section cannot be cached.
|
||||||
|
/// </summary>
|
||||||
|
MemoryNotCached = 0x04000000,
|
||||||
|
/// <summary>
|
||||||
|
/// The section is not pageable.
|
||||||
|
/// </summary>
|
||||||
|
MemoryNotPaged = 0x08000000,
|
||||||
|
/// <summary>
|
||||||
|
/// The section can be shared in memory.
|
||||||
|
/// </summary>
|
||||||
|
MemoryShared = 0x10000000,
|
||||||
|
/// <summary>
|
||||||
|
/// The section can be executed as code.
|
||||||
|
/// </summary>
|
||||||
|
MemoryExecute = 0x20000000,
|
||||||
|
/// <summary>
|
||||||
|
/// The section can be read.
|
||||||
|
/// </summary>
|
||||||
|
MemoryRead = 0x40000000,
|
||||||
|
/// <summary>
|
||||||
|
/// The section can be written to.
|
||||||
|
/// </summary>
|
||||||
|
MemoryWrite = 0x80000000
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
public struct IMAGE_IMPORT_DESCRIPTOR
|
||||||
|
{
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public uint Characteristics;
|
||||||
|
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public uint OriginalFirstThunk;
|
||||||
|
|
||||||
|
[FieldOffset(4)]
|
||||||
|
public uint TimeDateStamp;
|
||||||
|
|
||||||
|
[FieldOffset(8)]
|
||||||
|
public uint ForwarderChain;
|
||||||
|
|
||||||
|
[FieldOffset(12)]
|
||||||
|
public uint Name;
|
||||||
|
|
||||||
|
[FieldOffset(16)]
|
||||||
|
public uint FirstThunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,8 +6,6 @@ using System.Runtime.InteropServices;
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Logging.Internal;
|
||||||
|
|
||||||
using FFXIVClientStructs.FFXIV.Application.Network;
|
|
||||||
|
|
||||||
using InteropGenerator.Runtime;
|
using InteropGenerator.Runtime;
|
||||||
|
|
||||||
namespace Dalamud.Hooking.Internal.Verification;
|
namespace Dalamud.Hooking.Internal.Verification;
|
||||||
|
|
@ -27,12 +25,7 @@ internal static class HookVerifier
|
||||||
"ActorControlSelf",
|
"ActorControlSelf",
|
||||||
"E8 ?? ?? ?? ?? 0F B7 0B 83 E9 64",
|
"E8 ?? ?? ?? ?? 0F B7 0B 83 E9 64",
|
||||||
typeof(ActorControlSelfDelegate), // TODO: change this to CS delegate
|
typeof(ActorControlSelfDelegate), // TODO: change this to CS delegate
|
||||||
"Signature changed in Patch 7.4"), // 7.4 (new parameters)
|
"Signature changed in Patch 7.4") // 7.4 (new parameters)
|
||||||
new(
|
|
||||||
"SendPacket",
|
|
||||||
ZoneClient.Addresses.SendPacket.String,
|
|
||||||
typeof(ZoneClient.Delegates.SendPacket),
|
|
||||||
"Force marshaling context") // If people hook with 4 byte return this locks people out from logging in
|
|
||||||
];
|
];
|
||||||
|
|
||||||
private static readonly string ClientStructsInteropNamespacePrefix = string.Join(".", nameof(FFXIVClientStructs), nameof(FFXIVClientStructs.Interop));
|
private static readonly string ClientStructsInteropNamespacePrefix = string.Join(".", nameof(FFXIVClientStructs), nameof(FFXIVClientStructs.Interop));
|
||||||
|
|
@ -74,7 +67,6 @@ internal static class HookVerifier
|
||||||
}
|
}
|
||||||
|
|
||||||
var passedType = typeof(T);
|
var passedType = typeof(T);
|
||||||
var isAssemblyMarshaled = passedType.Assembly.GetCustomAttribute<DisableRuntimeMarshallingAttribute>() is null;
|
|
||||||
|
|
||||||
// Directly compare delegates
|
// Directly compare delegates
|
||||||
if (passedType == entry.TargetDelegateType)
|
if (passedType == entry.TargetDelegateType)
|
||||||
|
|
@ -86,7 +78,7 @@ internal static class HookVerifier
|
||||||
var enforcedInvoke = entry.TargetDelegateType.GetMethod("Invoke")!;
|
var enforcedInvoke = entry.TargetDelegateType.GetMethod("Invoke")!;
|
||||||
|
|
||||||
// Compare Return Type
|
// Compare Return Type
|
||||||
var mismatch = !CheckParam(passedInvoke.ReturnType, enforcedInvoke.ReturnType, isAssemblyMarshaled);
|
var mismatch = !CheckParam(passedInvoke.ReturnType, enforcedInvoke.ReturnType);
|
||||||
|
|
||||||
// Compare Parameter Count
|
// Compare Parameter Count
|
||||||
var passedParams = passedInvoke.GetParameters();
|
var passedParams = passedInvoke.GetParameters();
|
||||||
|
|
@ -101,7 +93,7 @@ internal static class HookVerifier
|
||||||
// Compare Parameter Types
|
// Compare Parameter Types
|
||||||
for (var i = 0; i < passedParams.Length; i++)
|
for (var i = 0; i < passedParams.Length; i++)
|
||||||
{
|
{
|
||||||
if (!CheckParam(passedParams[i].ParameterType, enforcedParams[i].ParameterType, isAssemblyMarshaled))
|
if (!CheckParam(passedParams[i].ParameterType, enforcedParams[i].ParameterType))
|
||||||
{
|
{
|
||||||
mismatch = true;
|
mismatch = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -115,23 +107,23 @@ internal static class HookVerifier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool CheckParam(Type paramLeft, Type paramRight, bool isMarshaled)
|
private static bool CheckParam(Type paramLeft, Type paramRight)
|
||||||
{
|
{
|
||||||
var sameType = paramLeft == paramRight;
|
var sameType = paramLeft == paramRight;
|
||||||
return sameType || SizeOf(paramLeft, isMarshaled) == SizeOf(paramRight, false);
|
return sameType || SizeOf(paramLeft) == SizeOf(paramRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int SizeOf(Type type, bool isMarshaled)
|
private static int SizeOf(Type type)
|
||||||
{
|
{
|
||||||
return type switch {
|
return type switch {
|
||||||
_ when type == typeof(sbyte) || type == typeof(byte) || (type == typeof(bool) && !isMarshaled) => 1,
|
_ when type == typeof(sbyte) || type == typeof(byte) || type == typeof(bool) => 1,
|
||||||
_ when type == typeof(char) || type == typeof(short) || type == typeof(ushort) || type == typeof(Half) => 2,
|
_ when type == typeof(char) || type == typeof(short) || type == typeof(ushort) || type == typeof(Half) => 2,
|
||||||
_ when type == typeof(int) || type == typeof(uint) || type == typeof(float) || (type == typeof(bool) && isMarshaled) => 4,
|
_ when type == typeof(int) || type == typeof(uint) || type == typeof(float) => 4,
|
||||||
_ when type == typeof(long) || type == typeof(ulong) || type == typeof(double) || type.IsPointer || type.IsFunctionPointer || type.IsUnmanagedFunctionPointer || (type.Name == "Pointer`1" && type.Namespace.AsSpan().SequenceEqual(ClientStructsInteropNamespacePrefix)) || type == typeof(CStringPointer) => 8,
|
_ when type == typeof(long) || type == typeof(ulong) || type == typeof(double) || type.IsPointer || type.IsFunctionPointer || type.IsUnmanagedFunctionPointer || (type.Name == "Pointer`1" && type.Namespace.AsSpan().SequenceEqual(ClientStructsInteropNamespacePrefix)) || type == typeof(CStringPointer) => 8,
|
||||||
_ when type.Name.StartsWith("FixedSizeArray") => SizeOf(type.GetGenericArguments()[0], isMarshaled) * int.Parse(type.Name[14..type.Name.IndexOf('`')]),
|
_ when type.Name.StartsWith("FixedSizeArray") => SizeOf(type.GetGenericArguments()[0]) * int.Parse(type.Name[14..type.Name.IndexOf('`')]),
|
||||||
_ when type.GetCustomAttribute<InlineArrayAttribute>() is { Length: var length } => SizeOf(type.GetGenericArguments()[0], isMarshaled) * length,
|
_ when type.GetCustomAttribute<InlineArrayAttribute>() is { Length: var length } => SizeOf(type.GetGenericArguments()[0]) * length,
|
||||||
_ when IsStruct(type) && !type.IsGenericType && (type.StructLayoutAttribute?.Value ?? LayoutKind.Sequential) != LayoutKind.Sequential => type.StructLayoutAttribute?.Size ?? (int?)typeof(Unsafe).GetMethod("SizeOf")?.MakeGenericMethod(type).Invoke(null, null) ?? 0,
|
_ when IsStruct(type) && !type.IsGenericType && (type.StructLayoutAttribute?.Value ?? LayoutKind.Sequential) != LayoutKind.Sequential => type.StructLayoutAttribute?.Size ?? (int?)typeof(Unsafe).GetMethod("SizeOf")?.MakeGenericMethod(type).Invoke(null, null) ?? 0,
|
||||||
_ when type.IsEnum => SizeOf(Enum.GetUnderlyingType(type), isMarshaled),
|
_ when type.IsEnum => SizeOf(Enum.GetUnderlyingType(type)),
|
||||||
_ when type.IsGenericType => Marshal.SizeOf(Activator.CreateInstance(type)!),
|
_ when type.IsGenericType => Marshal.SizeOf(Activator.CreateInstance(type)!),
|
||||||
_ => GetSizeOf(type),
|
_ => GetSizeOf(type),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -657,7 +657,6 @@ public partial class FileDialog
|
||||||
this.fileNameBuffer = $"{this.selectedFileNames.Count} files Selected";
|
this.fileNameBuffer = $"{this.selectedFileNames.Count} files Selected";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.SelectionChanged(this, this.GetFilePathName());
|
|
||||||
if (setLastSelection)
|
if (setLastSelection)
|
||||||
{
|
{
|
||||||
this.lastSelectedFileName = name;
|
this.lastSelectedFileName = name;
|
||||||
|
|
|
||||||
|
|
@ -97,8 +97,6 @@ public partial class FileDialog
|
||||||
this.SetupSideBar();
|
this.SetupSideBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler<string>? SelectionChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shows the dialog.
|
/// Shows the dialog.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,6 @@ public class FileDialogManager
|
||||||
private Action<bool, List<string>>? multiCallback;
|
private Action<bool, List<string>>? multiCallback;
|
||||||
private string savedPath = ".";
|
private string savedPath = ".";
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event fires when a new file is selected by the user
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Returns the path of the file as a string</returns>
|
|
||||||
public event EventHandler<string>? SelectionChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a dialog which selects an already existing folder.
|
/// Create a dialog which selects an already existing folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -181,8 +175,6 @@ public class FileDialogManager
|
||||||
this.multiCallback = null;
|
this.multiCallback = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSelectionChange(object sender, string path) => this.SelectionChanged?.Invoke(sender, path);
|
|
||||||
|
|
||||||
private void SetDialog(
|
private void SetDialog(
|
||||||
string id,
|
string id,
|
||||||
string title,
|
string title,
|
||||||
|
|
@ -208,7 +200,6 @@ public class FileDialogManager
|
||||||
if (this.dialog is not null)
|
if (this.dialog is not null)
|
||||||
{
|
{
|
||||||
this.dialog.SortOrderChanged -= this.OnSortOrderChange;
|
this.dialog.SortOrderChanged -= this.OnSortOrderChange;
|
||||||
this.dialog.SelectionChanged -= this.OnSelectionChange;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dialog = new FileDialog(id, title, filters, path, defaultFileName, defaultExtension, selectionCountMax, isModal, flags);
|
this.dialog = new FileDialog(id, title, filters, path, defaultFileName, defaultExtension, selectionCountMax, isModal, flags);
|
||||||
|
|
@ -226,7 +217,6 @@ public class FileDialogManager
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dialog.SortOrderChanged += this.OnSortOrderChange;
|
this.dialog.SortOrderChanged += this.OnSortOrderChange;
|
||||||
this.dialog.SelectionChanged += this.OnSelectionChange;
|
|
||||||
this.dialog.WindowFlags |= this.AddedWindowFlags;
|
this.dialog.WindowFlags |= this.AddedWindowFlags;
|
||||||
foreach (var (name, location, icon, position) in this.CustomSideBarItems)
|
foreach (var (name, location, icon, position) in this.CustomSideBarItems)
|
||||||
this.dialog.SetQuickAccess(name, location, icon, position);
|
this.dialog.SetQuickAccess(name, location, icon, position);
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ internal class DataShareWidget : IDataWindowWidget
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dataShare = Service<DataShare>.Get();
|
var dataShare = Service<DataShare>.Get();
|
||||||
var data2 = dataShare.GetData<object>(name, new DataCachePluginId("DataShareWidget", Guid.Empty));
|
var data2 = dataShare.GetData<object>(name);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
data = Encoding.UTF8.GetBytes(
|
data = Encoding.UTF8.GetBytes(
|
||||||
|
|
@ -98,7 +98,7 @@ internal class DataShareWidget : IDataWindowWidget
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
dataShare.RelinquishData(name, new DataCachePluginId("DataShareWidget", Guid.Empty));
|
dataShare.RelinquishData(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
@ -284,7 +284,7 @@ internal class DataShareWidget : IDataWindowWidget
|
||||||
|
|
||||||
ImGui.TableSetupColumn("Shared Tag"u8);
|
ImGui.TableSetupColumn("Shared Tag"u8);
|
||||||
ImGui.TableSetupColumn("Show"u8);
|
ImGui.TableSetupColumn("Show"u8);
|
||||||
ImGui.TableSetupColumn("Creator"u8);
|
ImGui.TableSetupColumn("Creator Assembly"u8);
|
||||||
ImGui.TableSetupColumn("#"u8, ImGuiTableColumnFlags.WidthFixed, 30 * ImGuiHelpers.GlobalScale);
|
ImGui.TableSetupColumn("#"u8, ImGuiTableColumnFlags.WidthFixed, 30 * ImGuiHelpers.GlobalScale);
|
||||||
ImGui.TableSetupColumn("Consumers"u8);
|
ImGui.TableSetupColumn("Consumers"u8);
|
||||||
ImGui.TableHeadersRow();
|
ImGui.TableHeadersRow();
|
||||||
|
|
@ -312,9 +312,9 @@ internal class DataShareWidget : IDataWindowWidget
|
||||||
this.nextTab = 2 + index;
|
this.nextTab = 2 + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.DrawTextCell(share.CreatorPluginId.InternalName, () => share.CreatorPluginId.EffectiveWorkingId.ToString(), true);
|
this.DrawTextCell(share.CreatorAssembly, null, true);
|
||||||
this.DrawTextCell(share.UserPluginIds.Length.ToString(), null, true);
|
this.DrawTextCell(share.Users.Length.ToString(), null, true);
|
||||||
this.DrawTextCell(string.Join(", ", share.UserPluginIds.Select(c => c.InternalName)), () => string.Join("\n", share.UserPluginIds.Select(c => $"{c.InternalName} ({c.EffectiveWorkingId.ToString()}")), true);
|
this.DrawTextCell(string.Join(", ", share.Users), null, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,10 @@ internal unsafe class NetworkMonitorWidget : IDataWindowWidget
|
||||||
{
|
{
|
||||||
private readonly ConcurrentQueue<NetworkPacketData> packets = new();
|
private readonly ConcurrentQueue<NetworkPacketData> packets = new();
|
||||||
|
|
||||||
private Hook<PacketDispatcher.Delegates.OnReceivePacket>? hookZoneDown;
|
private Hook<PacketDispatcher.Delegates.OnReceivePacket>? hookDown;
|
||||||
private Hook<ZoneClient.Delegates.SendPacket>? hookZoneUp;
|
private Hook<ZoneClient.Delegates.SendPacket>? hookUp;
|
||||||
|
|
||||||
private bool trackZoneUp;
|
private bool trackNetwork;
|
||||||
private bool trackZoneDown;
|
|
||||||
private int trackedPackets = 20;
|
private int trackedPackets = 20;
|
||||||
private ulong nextPacketIndex;
|
private ulong nextPacketIndex;
|
||||||
private string filterString = string.Empty;
|
private string filterString = string.Empty;
|
||||||
|
|
@ -36,8 +35,8 @@ internal unsafe class NetworkMonitorWidget : IDataWindowWidget
|
||||||
/// <summary> Finalizes an instance of the <see cref="NetworkMonitorWidget"/> class. </summary>
|
/// <summary> Finalizes an instance of the <see cref="NetworkMonitorWidget"/> class. </summary>
|
||||||
~NetworkMonitorWidget()
|
~NetworkMonitorWidget()
|
||||||
{
|
{
|
||||||
this.hookZoneDown?.Dispose();
|
this.hookDown?.Dispose();
|
||||||
this.hookZoneUp?.Dispose();
|
this.hookUp?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum NetworkMessageDirection
|
private enum NetworkMessageDirection
|
||||||
|
|
@ -61,41 +60,26 @@ internal unsafe class NetworkMonitorWidget : IDataWindowWidget
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Draw()
|
public void Draw()
|
||||||
{
|
{
|
||||||
this.hookZoneDown ??= Hook<PacketDispatcher.Delegates.OnReceivePacket>.FromAddress(
|
this.hookDown ??= Hook<PacketDispatcher.Delegates.OnReceivePacket>.FromAddress(
|
||||||
(nint)PacketDispatcher.StaticVirtualTablePointer->OnReceivePacket,
|
(nint)PacketDispatcher.StaticVirtualTablePointer->OnReceivePacket,
|
||||||
this.OnReceivePacketDetour);
|
this.OnReceivePacketDetour);
|
||||||
|
|
||||||
this.hookZoneUp ??= Hook<ZoneClient.Delegates.SendPacket>.FromAddress(
|
this.hookUp ??= Hook<ZoneClient.Delegates.SendPacket>.FromAddress(
|
||||||
(nint)ZoneClient.MemberFunctionPointers.SendPacket,
|
(nint)ZoneClient.MemberFunctionPointers.SendPacket,
|
||||||
this.SendPacketDetour);
|
this.SendPacketDetour);
|
||||||
|
|
||||||
if (ImGui.Checkbox("Track ZoneUp"u8, ref this.trackZoneUp))
|
if (ImGui.Checkbox("Track Network Packets"u8, ref this.trackNetwork))
|
||||||
{
|
{
|
||||||
if (this.trackZoneUp)
|
if (this.trackNetwork)
|
||||||
{
|
{
|
||||||
if (!this.trackZoneDown)
|
this.nextPacketIndex = 0;
|
||||||
this.nextPacketIndex = 0;
|
this.hookDown?.Enable();
|
||||||
|
this.hookUp?.Enable();
|
||||||
this.hookZoneUp?.Enable();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.hookZoneUp?.Disable();
|
this.hookDown?.Disable();
|
||||||
}
|
this.hookUp?.Disable();
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui.Checkbox("Track ZoneDown"u8, ref this.trackZoneDown))
|
|
||||||
{
|
|
||||||
if (this.trackZoneDown)
|
|
||||||
{
|
|
||||||
if (!this.trackZoneUp)
|
|
||||||
this.nextPacketIndex = 0;
|
|
||||||
|
|
||||||
this.hookZoneDown?.Enable();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.hookZoneDown?.Disable();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,7 +92,6 @@ internal unsafe class NetworkMonitorWidget : IDataWindowWidget
|
||||||
if (ImGui.Button("Clear Stored Packets"u8))
|
if (ImGui.Button("Clear Stored Packets"u8))
|
||||||
{
|
{
|
||||||
this.packets.Clear();
|
this.packets.Clear();
|
||||||
this.nextPacketIndex = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
@ -119,7 +102,7 @@ internal unsafe class NetworkMonitorWidget : IDataWindowWidget
|
||||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||||
ImGui.Checkbox("##FilterRecording"u8, ref this.filterRecording);
|
ImGui.Checkbox("##FilterRecording"u8, ref this.filterRecording);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip("When enabled, packets are filtered before being recorded.\nWhen disabled, all packets are recorded and filtering only affects packets displayed in the table."u8);
|
ImGui.SetTooltip("Apply filter to incoming packets.\nUncheck to record all packets and filter the table instead."u8);
|
||||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||||
ImGuiComponents.HelpMarker("Enter OpCodes in a comma-separated list.\nRanges are supported. Exclude OpCodes with exclamation mark.\nExample: -400,!50-100,650,700-980,!941");
|
ImGuiComponents.HelpMarker("Enter OpCodes in a comma-separated list.\nRanges are supported. Exclude OpCodes with exclamation mark.\nExample: -400,!50-100,650,700-980,!941");
|
||||||
|
|
||||||
|
|
@ -221,14 +204,14 @@ internal unsafe class NetworkMonitorWidget : IDataWindowWidget
|
||||||
var opCode = *(ushort*)(packet + 2);
|
var opCode = *(ushort*)(packet + 2);
|
||||||
var targetName = GetTargetName(targetId);
|
var targetName = GetTargetName(targetId);
|
||||||
this.RecordPacket(new NetworkPacketData(Interlocked.Increment(ref this.nextPacketIndex), DateTime.Now, opCode, NetworkMessageDirection.ZoneDown, targetId, targetName));
|
this.RecordPacket(new NetworkPacketData(Interlocked.Increment(ref this.nextPacketIndex), DateTime.Now, opCode, NetworkMessageDirection.ZoneDown, targetId, targetName));
|
||||||
this.hookZoneDown.OriginalDisposeSafe(thisPtr, targetId, packet);
|
this.hookDown.OriginalDisposeSafe(thisPtr, targetId, packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool SendPacketDetour(ZoneClient* thisPtr, nint packet, uint a3, uint a4, bool a5)
|
private bool SendPacketDetour(ZoneClient* thisPtr, nint packet, uint a3, uint a4, bool a5)
|
||||||
{
|
{
|
||||||
var opCode = *(ushort*)packet;
|
var opCode = *(ushort*)packet;
|
||||||
this.RecordPacket(new NetworkPacketData(Interlocked.Increment(ref this.nextPacketIndex), DateTime.Now, opCode, NetworkMessageDirection.ZoneUp, 0, string.Empty));
|
this.RecordPacket(new NetworkPacketData(Interlocked.Increment(ref this.nextPacketIndex), DateTime.Now, opCode, NetworkMessageDirection.ZoneUp, 0, string.Empty));
|
||||||
return this.hookZoneUp.OriginalDisposeSafe(thisPtr, packet, a3, a4, a5);
|
return this.hookUp.OriginalDisposeSafe(thisPtr, packet, a3, a4, a5);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RecordPacket(NetworkPacketData packet)
|
private void RecordPacket(NetworkPacketData packet)
|
||||||
|
|
|
||||||
|
|
@ -3804,7 +3804,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
if (!manifest.Punchline.IsNullOrEmpty())
|
if (!manifest.Punchline.IsNullOrEmpty())
|
||||||
scores.Add(matcher.Matches(manifest.Punchline.ToLowerInvariant()) * 100);
|
scores.Add(matcher.Matches(manifest.Punchline.ToLowerInvariant()) * 100);
|
||||||
if (manifest.Tags != null)
|
if (manifest.Tags != null)
|
||||||
scores.Add(matcher.MatchesAny(manifest.Tags.Select(tag => tag.ToLowerInvariant()).ToArray()) * 100);
|
scores.Add(matcher.MatchesAny(manifest.Tags.ToArray()) * 100);
|
||||||
|
|
||||||
return scores.Max();
|
return scores.Max();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,6 @@ internal sealed class BitmapCodecInfo : IBitmapCodecInfo
|
||||||
_ = readFuncPtr(codecInfo, 0, null, &cch);
|
_ = readFuncPtr(codecInfo, 0, null, &cch);
|
||||||
var buf = stackalloc char[(int)cch + 1];
|
var buf = stackalloc char[(int)cch + 1];
|
||||||
Marshal.ThrowExceptionForHR(readFuncPtr(codecInfo, cch + 1, buf, &cch));
|
Marshal.ThrowExceptionForHR(readFuncPtr(codecInfo, cch + 1, buf, &cch));
|
||||||
return new string(buf, 0, (int)cch).Trim('\0');
|
return new(buf, 0, (int)cch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ internal sealed class GamePathSharedImmediateTexture : SharedImmediateTexture
|
||||||
}
|
}
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var wrap = tm.NoThrottleCreateFromTexFile(file.Header, file.TextureBuffer);
|
var wrap = tm.NoThrottleCreateFromTexFile(file);
|
||||||
tm.BlameSetName(wrap, this.ToString());
|
tm.BlameSetName(wrap, this.ToString());
|
||||||
return wrap;
|
return wrap;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
using Dalamud.Data;
|
using Dalamud.Data;
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
|
using Dalamud.Interface.ImGuiSeStringRenderer.Internal;
|
||||||
using Dalamud.Interface.Internal;
|
using Dalamud.Interface.Internal;
|
||||||
using Dalamud.Interface.Textures.Internal.SharedImmediateTextures;
|
using Dalamud.Interface.Textures.Internal.SharedImmediateTextures;
|
||||||
using Dalamud.Interface.Textures.TextureWraps;
|
using Dalamud.Interface.Textures.TextureWraps;
|
||||||
|
|
@ -19,7 +20,6 @@ using Dalamud.Utility.TerraFxCom;
|
||||||
|
|
||||||
using Lumina.Data;
|
using Lumina.Data;
|
||||||
using Lumina.Data.Files;
|
using Lumina.Data.Files;
|
||||||
using Lumina.Data.Parsing.Tex.Buffers;
|
|
||||||
|
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
using TerraFX.Interop.Windows;
|
using TerraFX.Interop.Windows;
|
||||||
|
|
@ -219,7 +219,7 @@ internal sealed partial class TextureManager
|
||||||
null,
|
null,
|
||||||
_ => Task.FromResult(
|
_ => Task.FromResult(
|
||||||
this.BlameSetName(
|
this.BlameSetName(
|
||||||
this.NoThrottleCreateFromTexFile(file.Header, file.TextureBuffer),
|
this.NoThrottleCreateFromTexFile(file),
|
||||||
debugName ?? $"{nameof(this.CreateFromTexFile)}({ForceNullable(file.FilePath)?.Path})")),
|
debugName ?? $"{nameof(this.CreateFromTexFile)}({ForceNullable(file.FilePath)?.Path})")),
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|
||||||
|
|
@ -345,14 +345,14 @@ internal sealed partial class TextureManager
|
||||||
|
|
||||||
/// <summary>Creates a texture from the given <see cref="TexFile"/>. Skips the load throttler; intended to be used
|
/// <summary>Creates a texture from the given <see cref="TexFile"/>. Skips the load throttler; intended to be used
|
||||||
/// from implementation of <see cref="SharedImmediateTexture"/>s.</summary>
|
/// from implementation of <see cref="SharedImmediateTexture"/>s.</summary>
|
||||||
/// <param name="header">Header of a <c>.tex</c> file.</param>
|
/// <param name="file">The data.</param>
|
||||||
/// <param name="buffer">Texture buffer.</param>
|
|
||||||
/// <returns>The loaded texture.</returns>
|
/// <returns>The loaded texture.</returns>
|
||||||
internal IDalamudTextureWrap NoThrottleCreateFromTexFile(TexFile.TexHeader header, TextureBuffer buffer)
|
internal IDalamudTextureWrap NoThrottleCreateFromTexFile(TexFile file)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(this.disposeCts.IsCancellationRequested, this);
|
ObjectDisposedException.ThrowIf(this.disposeCts.IsCancellationRequested, this);
|
||||||
|
|
||||||
var (dxgiFormat, conversion) = TexFile.GetDxgiFormatFromTextureFormat(header.Format, false);
|
var buffer = file.TextureBuffer;
|
||||||
|
var (dxgiFormat, conversion) = TexFile.GetDxgiFormatFromTextureFormat(file.Header.Format, false);
|
||||||
if (conversion != TexFile.DxgiFormatConversion.NoConversion ||
|
if (conversion != TexFile.DxgiFormatConversion.NoConversion ||
|
||||||
!this.IsDxgiFormatSupported((DXGI_FORMAT)dxgiFormat))
|
!this.IsDxgiFormatSupported((DXGI_FORMAT)dxgiFormat))
|
||||||
{
|
{
|
||||||
|
|
@ -361,31 +361,34 @@ internal sealed partial class TextureManager
|
||||||
}
|
}
|
||||||
|
|
||||||
var wrap = this.NoThrottleCreateFromRaw(new(buffer.Width, buffer.Height, dxgiFormat), buffer.RawData);
|
var wrap = this.NoThrottleCreateFromRaw(new(buffer.Width, buffer.Height, dxgiFormat), buffer.RawData);
|
||||||
this.BlameSetName(wrap, $"{nameof(this.NoThrottleCreateFromTexFile)}({header.Width} x {header.Height})");
|
this.BlameSetName(wrap, $"{nameof(this.NoThrottleCreateFromTexFile)}({ForceNullable(file.FilePath).Path})");
|
||||||
return wrap;
|
return wrap;
|
||||||
|
|
||||||
|
static T? ForceNullable<T>(T s) => s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates a texture from the given <paramref name="fileBytes"/>, trying to interpret it as a
|
/// <summary>Creates a texture from the given <paramref name="fileBytes"/>, trying to interpret it as a
|
||||||
/// <see cref="TexFile"/>.</summary>
|
/// <see cref="TexFile"/>.</summary>
|
||||||
/// <param name="fileBytes">The file bytes.</param>
|
/// <param name="fileBytes">The file bytes.</param>
|
||||||
/// <returns>The loaded texture.</returns>
|
/// <returns>The loaded texture.</returns>
|
||||||
internal unsafe IDalamudTextureWrap NoThrottleCreateFromTexFile(ReadOnlySpan<byte> fileBytes)
|
internal IDalamudTextureWrap NoThrottleCreateFromTexFile(ReadOnlySpan<byte> fileBytes)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(this.disposeCts.IsCancellationRequested, this);
|
ObjectDisposedException.ThrowIf(this.disposeCts.IsCancellationRequested, this);
|
||||||
|
|
||||||
if (!TexFileExtensions.IsPossiblyTexFile2D(fileBytes))
|
if (!TexFileExtensions.IsPossiblyTexFile2D(fileBytes))
|
||||||
throw new InvalidDataException("The file is not a TexFile.");
|
throw new InvalidDataException("The file is not a TexFile.");
|
||||||
|
|
||||||
TexFile.TexHeader header;
|
var bytesArray = fileBytes.ToArray();
|
||||||
TextureBuffer buffer;
|
var tf = new TexFile();
|
||||||
fixed (byte* p = fileBytes)
|
typeof(TexFile).GetProperty(nameof(tf.Data))!.GetSetMethod(true)!.Invoke(
|
||||||
{
|
tf,
|
||||||
var lbr = new LuminaBinaryReader(new UnmanagedMemoryStream(p, fileBytes.Length));
|
[bytesArray]);
|
||||||
header = lbr.ReadStructure<TexFile.TexHeader>();
|
typeof(TexFile).GetProperty(nameof(tf.Reader))!.GetSetMethod(true)!.Invoke(
|
||||||
buffer = TextureBuffer.FromStream(header, lbr);
|
tf,
|
||||||
}
|
[new LuminaBinaryReader(bytesArray)]);
|
||||||
|
// Note: FileInfo and FilePath are not used from TexFile; skip it.
|
||||||
|
|
||||||
var wrap = this.NoThrottleCreateFromTexFile(header, buffer);
|
var wrap = this.NoThrottleCreateFromTexFile(tf);
|
||||||
this.BlameSetName(wrap, $"{nameof(this.NoThrottleCreateFromTexFile)}({fileBytes.Length:n0})");
|
this.BlameSetName(wrap, $"{nameof(this.NoThrottleCreateFromTexFile)}({fileBytes.Length:n0})");
|
||||||
return wrap;
|
return wrap;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -227,19 +227,19 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public T GetOrCreateData<T>(string tag, Func<T> dataGenerator) where T : class
|
public T GetOrCreateData<T>(string tag, Func<T> dataGenerator) where T : class
|
||||||
=> Service<DataShare>.Get().GetOrCreateData(tag, new DataCachePluginId(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId), dataGenerator);
|
=> Service<DataShare>.Get().GetOrCreateData(tag, dataGenerator);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void RelinquishData(string tag)
|
public void RelinquishData(string tag)
|
||||||
=> Service<DataShare>.Get().RelinquishData(tag, new DataCachePluginId(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId));
|
=> Service<DataShare>.Get().RelinquishData(tag);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool TryGetData<T>(string tag, [NotNullWhen(true)] out T? data) where T : class
|
public bool TryGetData<T>(string tag, [NotNullWhen(true)] out T? data) where T : class
|
||||||
=> Service<DataShare>.Get().TryGetData(tag, new DataCachePluginId(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId), out data);
|
=> Service<DataShare>.Get().TryGetData(tag, out data);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public T? GetData<T>(string tag) where T : class
|
public T? GetData<T>(string tag) where T : class
|
||||||
=> Service<DataShare>.Get().GetData<T>(tag, new DataCachePluginId(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId));
|
=> Service<DataShare>.Get().GetData<T>(tag);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ICallGateProvider<TRet> GetIpcProvider<TRet>(string name)
|
public ICallGateProvider<TRet> GetIpcProvider<TRet>(string name)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
using Dalamud.Plugin.Ipc.Internal;
|
|
||||||
|
|
||||||
namespace Dalamud.Plugin.Ipc.Exceptions;
|
namespace Dalamud.Plugin.Ipc.Exceptions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -11,11 +9,11 @@ public class DataCacheCreationError : IpcError
|
||||||
/// Initializes a new instance of the <see cref="DataCacheCreationError"/> class.
|
/// Initializes a new instance of the <see cref="DataCacheCreationError"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tag">Tag of the data cache.</param>
|
/// <param name="tag">Tag of the data cache.</param>
|
||||||
/// <param name="creatorPluginId">The plugin ID of the creating plugin.</param>
|
/// <param name="creator">The assembly name of the caller.</param>
|
||||||
/// <param name="expectedType">The type expected.</param>
|
/// <param name="expectedType">The type expected.</param>
|
||||||
/// <param name="ex">The thrown exception.</param>
|
/// <param name="ex">The thrown exception.</param>
|
||||||
public DataCacheCreationError(string tag, DataCachePluginId creatorPluginId, Type expectedType, Exception ex)
|
public DataCacheCreationError(string tag, string creator, Type expectedType, Exception ex)
|
||||||
: base($"The creation of the {expectedType} data cache {tag} initialized by {creatorPluginId.InternalName} ({creatorPluginId.EffectiveWorkingId}) was unsuccessful.", ex)
|
: base($"The creation of the {expectedType} data cache {tag} initialized by {creator} was unsuccessful.", ex)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
using Dalamud.Plugin.Ipc.Internal;
|
|
||||||
|
|
||||||
namespace Dalamud.Plugin.Ipc.Exceptions;
|
namespace Dalamud.Plugin.Ipc.Exceptions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -11,11 +9,11 @@ public class DataCacheTypeMismatchError : IpcError
|
||||||
/// Initializes a new instance of the <see cref="DataCacheTypeMismatchError"/> class.
|
/// Initializes a new instance of the <see cref="DataCacheTypeMismatchError"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tag">Tag of the data cache.</param>
|
/// <param name="tag">Tag of the data cache.</param>
|
||||||
/// <param name="creatorPluginId">The plugin ID of the creating plugin.</param>
|
/// <param name="creator">Assembly name of the plugin creating the cache.</param>
|
||||||
/// <param name="requestedType">The requested type.</param>
|
/// <param name="requestedType">The requested type.</param>
|
||||||
/// <param name="actualType">The stored type.</param>
|
/// <param name="actualType">The stored type.</param>
|
||||||
public DataCacheTypeMismatchError(string tag, DataCachePluginId creatorPluginId, Type requestedType, Type actualType)
|
public DataCacheTypeMismatchError(string tag, string creator, Type requestedType, Type actualType)
|
||||||
: base($"Data cache {tag} was requested with type {requestedType}, but {creatorPluginId.InternalName} ({creatorPluginId.EffectiveWorkingId}) created type {actualType}.")
|
: base($"Data cache {tag} was requested with type {requestedType}, but {creator} created type {actualType}.")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.ExceptionServices;
|
using System.Runtime.ExceptionServices;
|
||||||
|
|
||||||
using Dalamud.Plugin.Ipc.Exceptions;
|
using Dalamud.Plugin.Ipc.Exceptions;
|
||||||
|
|
@ -17,12 +16,12 @@ internal readonly struct DataCache
|
||||||
/// <summary> Name of the data. </summary>
|
/// <summary> Name of the data. </summary>
|
||||||
internal readonly string Tag;
|
internal readonly string Tag;
|
||||||
|
|
||||||
/// <summary> The creating plugin ID of this DataCache entry. </summary>
|
/// <summary> The assembly name of the initial creator. </summary>
|
||||||
internal readonly DataCachePluginId CreatorPluginId;
|
internal readonly string CreatorAssemblyName;
|
||||||
|
|
||||||
/// <summary> A distinct list of plugin IDs that are using this data. </summary>
|
/// <summary> A not-necessarily distinct list of current users. </summary>
|
||||||
/// <remarks> Also used as a reference count tracker. </remarks>
|
/// <remarks> Also used as a reference count tracker. </remarks>
|
||||||
internal readonly List<DataCachePluginId> UserPluginIds;
|
internal readonly List<string> UserAssemblyNames;
|
||||||
|
|
||||||
/// <summary> The type the data was registered as. </summary>
|
/// <summary> The type the data was registered as. </summary>
|
||||||
internal readonly Type Type;
|
internal readonly Type Type;
|
||||||
|
|
@ -34,14 +33,14 @@ internal readonly struct DataCache
|
||||||
/// Initializes a new instance of the <see cref="DataCache"/> struct.
|
/// Initializes a new instance of the <see cref="DataCache"/> struct.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tag">Name of the data.</param>
|
/// <param name="tag">Name of the data.</param>
|
||||||
/// <param name="creatorPluginId">The internal name and effective working ID of the creating plugin.</param>
|
/// <param name="creatorAssemblyName">The assembly name of the initial creator.</param>
|
||||||
/// <param name="data">A reference to data.</param>
|
/// <param name="data">A reference to data.</param>
|
||||||
/// <param name="type">The type of the data.</param>
|
/// <param name="type">The type of the data.</param>
|
||||||
public DataCache(string tag, DataCachePluginId creatorPluginId, object? data, Type type)
|
public DataCache(string tag, string creatorAssemblyName, object? data, Type type)
|
||||||
{
|
{
|
||||||
this.Tag = tag;
|
this.Tag = tag;
|
||||||
this.CreatorPluginId = creatorPluginId;
|
this.CreatorAssemblyName = creatorAssemblyName;
|
||||||
this.UserPluginIds = [];
|
this.UserAssemblyNames = [];
|
||||||
this.Data = data;
|
this.Data = data;
|
||||||
this.Type = type;
|
this.Type = type;
|
||||||
}
|
}
|
||||||
|
|
@ -50,40 +49,40 @@ internal readonly struct DataCache
|
||||||
/// Creates a new instance of the <see cref="DataCache"/> struct, using the given data generator function.
|
/// Creates a new instance of the <see cref="DataCache"/> struct, using the given data generator function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tag">The name for the data cache.</param>
|
/// <param name="tag">The name for the data cache.</param>
|
||||||
/// <param name="creatorPluginId">The internal name and effective working ID of the creating plugin.</param>
|
/// <param name="creatorAssemblyName">The assembly name of the initial creator.</param>
|
||||||
/// <param name="dataGenerator">The function that generates the data if it does not already exist.</param>
|
/// <param name="dataGenerator">The function that generates the data if it does not already exist.</param>
|
||||||
/// <typeparam name="T">The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.</typeparam>
|
/// <typeparam name="T">The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.</typeparam>
|
||||||
/// <returns>The new instance of <see cref="DataCache"/>.</returns>
|
/// <returns>The new instance of <see cref="DataCache"/>.</returns>
|
||||||
public static DataCache From<T>(string tag, DataCachePluginId creatorPluginId, Func<T> dataGenerator)
|
public static DataCache From<T>(string tag, string creatorAssemblyName, Func<T> dataGenerator)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = new DataCache(tag, creatorPluginId, dataGenerator.Invoke(), typeof(T));
|
var result = new DataCache(tag, creatorAssemblyName, dataGenerator.Invoke(), typeof(T));
|
||||||
Log.Verbose(
|
Log.Verbose(
|
||||||
"[{who}] Created new data for [{Tag:l}] for creator {Creator:l}.",
|
"[{who}] Created new data for [{Tag:l}] for creator {Creator:l}.",
|
||||||
nameof(DataShare),
|
nameof(DataShare),
|
||||||
tag,
|
tag,
|
||||||
creatorPluginId);
|
creatorAssemblyName);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
throw ExceptionDispatchInfo.SetCurrentStackTrace(
|
throw ExceptionDispatchInfo.SetCurrentStackTrace(
|
||||||
new DataCacheCreationError(tag, creatorPluginId, typeof(T), e));
|
new DataCacheCreationError(tag, creatorAssemblyName, typeof(T), e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to fetch the data.
|
/// Attempts to fetch the data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="callingPluginId">The calling plugin ID.</param>
|
/// <param name="callerName">The name of the caller assembly.</param>
|
||||||
/// <param name="value">The value, if succeeded.</param>
|
/// <param name="value">The value, if succeeded.</param>
|
||||||
/// <param name="ex">The exception, if failed.</param>
|
/// <param name="ex">The exception, if failed.</param>
|
||||||
/// <typeparam name="T">Desired type of the data.</typeparam>
|
/// <typeparam name="T">Desired type of the data.</typeparam>
|
||||||
/// <returns><c>true</c> on success.</returns>
|
/// <returns><c>true</c> on success.</returns>
|
||||||
public bool TryGetData<T>(
|
public bool TryGetData<T>(
|
||||||
DataCachePluginId callingPluginId,
|
string callerName,
|
||||||
[NotNullWhen(true)] out T? value,
|
[NotNullWhen(true)] out T? value,
|
||||||
[NotNullWhen(false)] out Exception? ex)
|
[NotNullWhen(false)] out Exception? ex)
|
||||||
where T : class
|
where T : class
|
||||||
|
|
@ -99,21 +98,16 @@ internal readonly struct DataCache
|
||||||
value = data;
|
value = data;
|
||||||
ex = null;
|
ex = null;
|
||||||
|
|
||||||
// Register the access history. The effective working ID is unique per plugin and persists between reloads, so only add it once.
|
// Register the access history
|
||||||
lock (this.UserPluginIds)
|
lock (this.UserAssemblyNames)
|
||||||
{
|
this.UserAssemblyNames.Add(callerName);
|
||||||
if (this.UserPluginIds.All(c => c.EffectiveWorkingId != callingPluginId.EffectiveWorkingId))
|
|
||||||
{
|
|
||||||
this.UserPluginIds.Add(callingPluginId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
value = null;
|
value = null;
|
||||||
ex = ExceptionDispatchInfo.SetCurrentStackTrace(
|
ex = ExceptionDispatchInfo.SetCurrentStackTrace(
|
||||||
new DataCacheTypeMismatchError(this.Tag, this.CreatorPluginId, typeof(T), this.Type));
|
new DataCacheTypeMismatchError(this.Tag, this.CreatorAssemblyName, typeof(T), this.Type));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Runtime.ExceptionServices;
|
|
||||||
|
|
||||||
using Dalamud.Plugin.Ipc.Exceptions;
|
|
||||||
|
|
||||||
using Serilog;
|
|
||||||
|
|
||||||
namespace Dalamud.Plugin.Ipc.Internal;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stores the internal name and effective working ID of a plugin accessing datashare.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="InternalName">The internal name of the plugin.</param>
|
|
||||||
/// <param name="EffectiveWorkingId">The effective working ID of the plugin.</param>
|
|
||||||
public record DataCachePluginId(string InternalName, Guid EffectiveWorkingId);
|
|
||||||
|
|
@ -33,23 +33,24 @@ internal class DataShare : IServiceType
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.</typeparam>
|
/// <typeparam name="T">The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.</typeparam>
|
||||||
/// <param name="tag">The name for the data cache.</param>
|
/// <param name="tag">The name for the data cache.</param>
|
||||||
/// <param name="callingPluginId">The ID of the calling plugin.</param>
|
|
||||||
/// <param name="dataGenerator">The function that generates the data if it does not already exist.</param>
|
/// <param name="dataGenerator">The function that generates the data if it does not already exist.</param>
|
||||||
/// <returns>Either the existing data for <paramref name="tag"/> or the data generated by <paramref name="dataGenerator"/>.</returns>
|
/// <returns>Either the existing data for <paramref name="tag"/> or the data generated by <paramref name="dataGenerator"/>.</returns>
|
||||||
/// <exception cref="DataCacheTypeMismatchError">Thrown if a cache for <paramref name="tag"/> exists, but contains data of a type not assignable to <typeparamref name="T>"/>.</exception>
|
/// <exception cref="DataCacheTypeMismatchError">Thrown if a cache for <paramref name="tag"/> exists, but contains data of a type not assignable to <typeparamref name="T>"/>.</exception>
|
||||||
/// <exception cref="DataCacheValueNullError">Thrown if the stored data for a cache is null.</exception>
|
/// <exception cref="DataCacheValueNullError">Thrown if the stored data for a cache is null.</exception>
|
||||||
/// <exception cref="DataCacheCreationError">Thrown if <paramref name="dataGenerator"/> throws an exception or returns null.</exception>
|
/// <exception cref="DataCacheCreationError">Thrown if <paramref name="dataGenerator"/> throws an exception or returns null.</exception>
|
||||||
public T GetOrCreateData<T>(string tag, DataCachePluginId callingPluginId, Func<T> dataGenerator)
|
public T GetOrCreateData<T>(string tag, Func<T> dataGenerator)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
|
var callerName = GetCallerName();
|
||||||
|
|
||||||
Lazy<DataCache> cacheLazy;
|
Lazy<DataCache> cacheLazy;
|
||||||
lock (this.caches)
|
lock (this.caches)
|
||||||
{
|
{
|
||||||
if (!this.caches.TryGetValue(tag, out cacheLazy))
|
if (!this.caches.TryGetValue(tag, out cacheLazy))
|
||||||
this.caches[tag] = cacheLazy = new(() => DataCache.From(tag, callingPluginId, dataGenerator));
|
this.caches[tag] = cacheLazy = new(() => DataCache.From(tag, callerName, dataGenerator));
|
||||||
}
|
}
|
||||||
|
|
||||||
return cacheLazy.Value.TryGetData<T>(callingPluginId, out var value, out var ex) ? value : throw ex;
|
return cacheLazy.Value.TryGetData<T>(callerName, out var value, out var ex) ? value : throw ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -57,8 +58,7 @@ internal class DataShare : IServiceType
|
||||||
/// If no assembly uses the data anymore, the cache will be removed from the data share and if it is an IDisposable, Dispose will be called on it.
|
/// If no assembly uses the data anymore, the cache will be removed from the data share and if it is an IDisposable, Dispose will be called on it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tag">The name for the data cache.</param>
|
/// <param name="tag">The name for the data cache.</param>
|
||||||
/// <param name="callingPluginId">The ID of the calling plugin.</param>
|
public void RelinquishData(string tag)
|
||||||
public void RelinquishData(string tag, DataCachePluginId callingPluginId)
|
|
||||||
{
|
{
|
||||||
DataCache cache;
|
DataCache cache;
|
||||||
lock (this.caches)
|
lock (this.caches)
|
||||||
|
|
@ -66,8 +66,10 @@ internal class DataShare : IServiceType
|
||||||
if (!this.caches.TryGetValue(tag, out var cacheLazy))
|
if (!this.caches.TryGetValue(tag, out var cacheLazy))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var callerName = GetCallerName();
|
||||||
|
|
||||||
cache = cacheLazy.Value;
|
cache = cacheLazy.Value;
|
||||||
if (!cache.UserPluginIds.Remove(callingPluginId) || cache.UserPluginIds.Count > 0)
|
if (!cache.UserAssemblyNames.Remove(callerName) || cache.UserAssemblyNames.Count > 0)
|
||||||
return;
|
return;
|
||||||
if (!this.caches.Remove(tag))
|
if (!this.caches.Remove(tag))
|
||||||
return;
|
return;
|
||||||
|
|
@ -97,10 +99,9 @@ internal class DataShare : IServiceType
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.</typeparam>
|
/// <typeparam name="T">The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.</typeparam>
|
||||||
/// <param name="tag">The name for the data cache.</param>
|
/// <param name="tag">The name for the data cache.</param>
|
||||||
/// <param name="callingPluginId">The ID of the calling plugin.</param>
|
|
||||||
/// <param name="data">The requested data on success, null otherwise.</param>
|
/// <param name="data">The requested data on success, null otherwise.</param>
|
||||||
/// <returns>True if the requested data exists and is assignable to the requested type.</returns>
|
/// <returns>True if the requested data exists and is assignable to the requested type.</returns>
|
||||||
public bool TryGetData<T>(string tag, DataCachePluginId callingPluginId, [NotNullWhen(true)] out T? data)
|
public bool TryGetData<T>(string tag, [NotNullWhen(true)] out T? data)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
data = null;
|
data = null;
|
||||||
|
|
@ -111,7 +112,7 @@ internal class DataShare : IServiceType
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cacheLazy.Value.TryGetData(callingPluginId, out data, out _);
|
return cacheLazy.Value.TryGetData(GetCallerName(), out data, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -120,12 +121,11 @@ internal class DataShare : IServiceType
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.</typeparam>
|
/// <typeparam name="T">The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.</typeparam>
|
||||||
/// <param name="tag">The name for the data cache.</param>
|
/// <param name="tag">The name for the data cache.</param>
|
||||||
/// <param name="callingPluginId">The ID of the calling plugin.</param>
|
|
||||||
/// <returns>The requested data.</returns>
|
/// <returns>The requested data.</returns>
|
||||||
/// <exception cref="KeyNotFoundException">Thrown if <paramref name="tag"/> is not registered.</exception>
|
/// <exception cref="KeyNotFoundException">Thrown if <paramref name="tag"/> is not registered.</exception>
|
||||||
/// <exception cref="DataCacheTypeMismatchError">Thrown if a cache for <paramref name="tag"/> exists, but contains data of a type not assignable to <typeparamref name="T>"/>.</exception>
|
/// <exception cref="DataCacheTypeMismatchError">Thrown if a cache for <paramref name="tag"/> exists, but contains data of a type not assignable to <typeparamref name="T>"/>.</exception>
|
||||||
/// <exception cref="DataCacheValueNullError">Thrown if the stored data for a cache is null.</exception>
|
/// <exception cref="DataCacheValueNullError">Thrown if the stored data for a cache is null.</exception>
|
||||||
public T GetData<T>(string tag, DataCachePluginId callingPluginId)
|
public T GetData<T>(string tag)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
Lazy<DataCache> cacheLazy;
|
Lazy<DataCache> cacheLazy;
|
||||||
|
|
@ -135,19 +135,35 @@ internal class DataShare : IServiceType
|
||||||
throw new KeyNotFoundException($"The data cache [{tag}] is not registered.");
|
throw new KeyNotFoundException($"The data cache [{tag}] is not registered.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return cacheLazy.Value.TryGetData<T>(callingPluginId, out var value, out var ex) ? value : throw ex;
|
return cacheLazy.Value.TryGetData<T>(GetCallerName(), out var value, out var ex) ? value : throw ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Obtain a read-only list of data shares.
|
/// Obtain a read-only list of data shares.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>All currently subscribed tags, their creator names and all their users.</returns>
|
/// <returns>All currently subscribed tags, their creator names and all their users.</returns>
|
||||||
internal IEnumerable<(string Tag, DataCachePluginId CreatorPluginId, DataCachePluginId[] UserPluginIds)> GetAllShares()
|
internal IEnumerable<(string Tag, string CreatorAssembly, string[] Users)> GetAllShares()
|
||||||
{
|
{
|
||||||
lock (this.caches)
|
lock (this.caches)
|
||||||
{
|
{
|
||||||
return this.caches.Select(
|
return this.caches.Select(
|
||||||
kvp => (kvp.Key, kvp.Value.Value.CreatorPluginId, kvp.Value.Value.UserPluginIds.ToArray()));
|
kvp => (kvp.Key, kvp.Value.Value.CreatorAssemblyName, kvp.Value.Value.UserAssemblyNames.ToArray()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Obtain the last assembly name in the stack trace that is not a system or dalamud assembly. </summary>
|
||||||
|
private static string GetCallerName()
|
||||||
|
{
|
||||||
|
var frames = new StackTrace().GetFrames();
|
||||||
|
foreach (var frame in frames.Reverse())
|
||||||
|
{
|
||||||
|
var name = frame.GetMethod()?.DeclaringType?.Assembly.GetName().Name ?? "Unknown";
|
||||||
|
if (!name.StartsWith("System") && !name.StartsWith("Dalamud"))
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ public interface IPlayerState : IDalamudService
|
||||||
RowRef<Aetheryte> FreeAetheryte { get; }
|
RowRef<Aetheryte> FreeAetheryte { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the amount of rested experience available to the local player.
|
/// Gets the amount of received player commendations of the local player.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
uint BaseRestedExperience { get; }
|
uint BaseRestedExperience { get; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
using Lumina.Excel;
|
using Lumina.Excel;
|
||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.Sheets;
|
||||||
|
|
||||||
|
|
@ -21,24 +23,6 @@ public interface IUnlockState : IDalamudService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event UnlockDelegate? Unlock;
|
event UnlockDelegate? Unlock;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the full Achievements list was received.
|
|
||||||
/// </summary>
|
|
||||||
bool IsAchievementListLoaded { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the full Titles list was received.
|
|
||||||
/// </summary>
|
|
||||||
bool IsTitleListLoaded { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified Achievement is completed.<br/>
|
|
||||||
/// Requires that the player requested the Achievements list (can be chcked with <see cref="IsAchievementListLoaded"/>).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="row">The Achievement row to check.</param>
|
|
||||||
/// <returns><see langword="true"/> if completed; otherwise, <see langword="false"/>.</returns>
|
|
||||||
bool IsAchievementComplete(Achievement row);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified Action is unlocked.
|
/// Determines whether the specified Action is unlocked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -46,13 +30,6 @@ public interface IUnlockState : IDalamudService
|
||||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||||
bool IsActionUnlocked(Lumina.Excel.Sheets.Action row);
|
bool IsActionUnlocked(Lumina.Excel.Sheets.Action row);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified Adventure is completed.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="row">The Adventure row to check.</param>
|
|
||||||
/// <returns><see langword="true"/> if completed; otherwise, <see langword="false"/>.</returns>
|
|
||||||
public bool IsAdventureComplete(Adventure row);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified AetherCurrentCompFlgSet is unlocked.
|
/// Determines whether the specified AetherCurrentCompFlgSet is unlocked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -334,14 +311,6 @@ public interface IUnlockState : IDalamudService
|
||||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
||||||
bool IsSecretRecipeBookUnlocked(SecretRecipeBook row);
|
bool IsSecretRecipeBookUnlocked(SecretRecipeBook row);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified Title is unlocked.<br/>
|
|
||||||
/// Requires that the player requested the Titles list (can be chcked with <see cref="IsTitleListLoaded"/>).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="row">The Title row to check.</param>
|
|
||||||
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
|
|
||||||
bool IsTitleUnlocked(Title row);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified Trait is unlocked.
|
/// Determines whether the specified Trait is unlocked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
|
@ -69,7 +68,6 @@ public static class Troubleshooting
|
||||||
{
|
{
|
||||||
var payload = new TroubleshootingPayload
|
var payload = new TroubleshootingPayload
|
||||||
{
|
{
|
||||||
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
|
||||||
LoadedPlugins = pluginManager?.InstalledPlugins?.Select(x => x.Manifest as LocalPluginManifest)?.OrderByDescending(x => x.InternalName).ToArray(),
|
LoadedPlugins = pluginManager?.InstalledPlugins?.Select(x => x.Manifest as LocalPluginManifest)?.OrderByDescending(x => x.InternalName).ToArray(),
|
||||||
PluginStates = pluginManager?.InstalledPlugins?.Where(x => !x.IsDev).ToDictionary(x => x.Manifest.InternalName, x => x.IsBanned ? "Banned" : x.State.ToString()),
|
PluginStates = pluginManager?.InstalledPlugins?.Where(x => !x.IsDev).ToDictionary(x => x.Manifest.InternalName, x => x.IsBanned ? "Banned" : x.State.ToString()),
|
||||||
EverStartedLoadingPlugins = pluginManager?.InstalledPlugins.Where(x => x.HasEverStartedLoad).Select(x => x.InternalName).ToList(),
|
EverStartedLoadingPlugins = pluginManager?.InstalledPlugins.Where(x => x.HasEverStartedLoad).Select(x => x.InternalName).ToList(),
|
||||||
|
|
@ -87,12 +85,6 @@ public static class Troubleshooting
|
||||||
|
|
||||||
var encodedPayload = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload)));
|
var encodedPayload = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload)));
|
||||||
Log.Information($"TROUBLESHOOTING:{encodedPayload}");
|
Log.Information($"TROUBLESHOOTING:{encodedPayload}");
|
||||||
|
|
||||||
File.WriteAllText(
|
|
||||||
Path.Join(
|
|
||||||
startInfo.LogPath,
|
|
||||||
"dalamud.troubleshooting.json"),
|
|
||||||
JsonConvert.SerializeObject(payload, Formatting.Indented));
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -111,8 +103,6 @@ public static class Troubleshooting
|
||||||
|
|
||||||
private class TroubleshootingPayload
|
private class TroubleshootingPayload
|
||||||
{
|
{
|
||||||
public long Timestamp { get; set; }
|
|
||||||
|
|
||||||
public LocalPluginManifest[]? LoadedPlugins { get; set; }
|
public LocalPluginManifest[]? LoadedPlugins { get; set; }
|
||||||
|
|
||||||
public Dictionary<string, string>? PluginStates { get; set; }
|
public Dictionary<string, string>? PluginStates { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,6 @@
|
||||||
#include <ShObjIdl.h>
|
#include <ShObjIdl.h>
|
||||||
#include <shlobj_core.h>
|
#include <shlobj_core.h>
|
||||||
|
|
||||||
#include <dxgi.h>
|
|
||||||
#pragma comment(lib, "dxgi.lib")
|
|
||||||
|
|
||||||
#pragma comment(lib, "comctl32.lib")
|
#pragma comment(lib, "comctl32.lib")
|
||||||
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
|
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
|
||||||
|
|
||||||
|
|
@ -473,39 +470,12 @@ void open_folder_and_select_items(HWND hwndOpener, const std::wstring& path) {
|
||||||
ILFree(piid);
|
ILFree(piid);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<IDXGIAdapter1*> enum_dxgi_adapters()
|
|
||||||
{
|
|
||||||
std::vector<IDXGIAdapter1*> vAdapters;
|
|
||||||
|
|
||||||
IDXGIFactory1* pFactory = NULL;
|
|
||||||
if (FAILED(CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&pFactory)))
|
|
||||||
{
|
|
||||||
return vAdapters;
|
|
||||||
}
|
|
||||||
|
|
||||||
IDXGIAdapter1* pAdapter;
|
|
||||||
for (UINT i = 0;
|
|
||||||
pFactory->EnumAdapters1(i, &pAdapter) != DXGI_ERROR_NOT_FOUND;
|
|
||||||
++i)
|
|
||||||
{
|
|
||||||
vAdapters.push_back(pAdapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pFactory)
|
|
||||||
{
|
|
||||||
pFactory->Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
return vAdapters;
|
|
||||||
}
|
|
||||||
|
|
||||||
void export_tspack(HWND hWndParent, const std::filesystem::path& logDir, const std::string& crashLog, const std::string& troubleshootingPackData) {
|
void export_tspack(HWND hWndParent, const std::filesystem::path& logDir, const std::string& crashLog, const std::string& troubleshootingPackData) {
|
||||||
static const char* SourceLogFiles[] = {
|
static const char* SourceLogFiles[] = {
|
||||||
"output.log", // XIVLauncher for Windows
|
"output.log", // XIVLauncher for Windows
|
||||||
"launcher.log", // XIVLauncher.Core for [mostly] Linux
|
"launcher.log", // XIVLauncher.Core for [mostly] Linux
|
||||||
"patcher.log",
|
"patcher.log",
|
||||||
"dalamud.log",
|
"dalamud.log",
|
||||||
"dalamud.troubleshooting.json",
|
|
||||||
"dalamud.injector.log",
|
"dalamud.injector.log",
|
||||||
"dalamud.boot.log",
|
"dalamud.boot.log",
|
||||||
"aria.log",
|
"aria.log",
|
||||||
|
|
@ -1052,13 +1022,6 @@ int main() {
|
||||||
log << std::format(L"System Time: {0:%F} {0:%T} {0:%Ez}", std::chrono::system_clock::now()) << std::endl;
|
log << std::format(L"System Time: {0:%F} {0:%T} {0:%Ez}", std::chrono::system_clock::now()) << std::endl;
|
||||||
log << std::format(L"CPU Vendor: {}", vendor) << std::endl;
|
log << std::format(L"CPU Vendor: {}", vendor) << std::endl;
|
||||||
log << std::format(L"CPU Brand: {}", brand) << std::endl;
|
log << std::format(L"CPU Brand: {}", brand) << std::endl;
|
||||||
|
|
||||||
for (IDXGIAdapter1* adapter : enum_dxgi_adapters()) {
|
|
||||||
DXGI_ADAPTER_DESC1 adapterDescription{};
|
|
||||||
adapter->GetDesc1(&adapterDescription);
|
|
||||||
log << std::format(L"GPU Desc: {}", adapterDescription.Description) << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
log << L"\n" << stackTrace << std::endl;
|
log << L"\n" << stackTrace << std::endl;
|
||||||
|
|
||||||
if (pProgressDialog)
|
if (pProgressDialog)
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit a97e9f89d72d40eabd0f3b52266862dca3eba872
|
Subproject commit 9ba281cab958049b47bbf7199ab14742c609cd4b
|
||||||
Loading…
Add table
Add a link
Reference in a new issue