diff --git a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs
index 2e5439ed0..6520ee4cf 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs
@@ -94,8 +94,6 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
/// The listener to unregister.
internal void UnregisterListener(AddonLifecycleEventListener listener)
{
- listener.IsRequestedToClear = true;
-
if (this.isInvokingListeners)
{
this.framework.RunOnTick(() => this.UnregisterListenerMethod(listener));
@@ -124,8 +122,6 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
{
foreach (var listener in globalListeners)
{
- if (listener.IsRequestedToClear) continue;
-
try
{
listener.FunctionDelegate.Invoke(eventType, args);
@@ -142,8 +138,6 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
{
foreach (var listener in addonListener)
{
- if (listener.IsRequestedToClear) continue;
-
try
{
listener.FunctionDelegate.Invoke(eventType, args);
diff --git a/Dalamud/Game/Addon/Lifecycle/AddonLifecycleEventListener.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleEventListener.cs
index 38c081e65..fc82e0582 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonLifecycleEventListener.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleEventListener.cs
@@ -35,9 +35,4 @@ internal class AddonLifecycleEventListener
/// Gets the delegate this listener invokes.
///
public IAddonLifecycle.AddonEventDelegate FunctionDelegate { get; init; }
-
- ///
- /// Gets or sets if the listener is requested to be cleared.
- ///
- internal bool IsRequestedToClear { get; set; }
}
diff --git a/Dalamud/Game/Agent/AgentLifecycle.cs b/Dalamud/Game/Agent/AgentLifecycle.cs
index 1c895f9da..45f0dec5c 100644
--- a/Dalamud/Game/Agent/AgentLifecycle.cs
+++ b/Dalamud/Game/Agent/AgentLifecycle.cs
@@ -107,8 +107,6 @@ internal unsafe class AgentLifecycle : IInternalDisposableService
/// The listener to unregister.
internal void UnregisterListener(AgentLifecycleEventListener listener)
{
- listener.IsRequestedToClear = true;
-
if (this.isInvokingListeners)
{
this.framework.RunOnTick(() => this.UnregisterListenerMethod(listener));
@@ -137,8 +135,6 @@ internal unsafe class AgentLifecycle : IInternalDisposableService
{
foreach (var listener in globalListeners)
{
- if (listener.IsRequestedToClear) continue;
-
try
{
listener.FunctionDelegate.Invoke(eventType, args);
@@ -155,8 +151,6 @@ internal unsafe class AgentLifecycle : IInternalDisposableService
{
foreach (var listener in agentListener)
{
- if (listener.IsRequestedToClear) continue;
-
try
{
listener.FunctionDelegate.Invoke(eventType, args);
diff --git a/Dalamud/Game/Agent/AgentLifecycleEventListener.cs b/Dalamud/Game/Agent/AgentLifecycleEventListener.cs
index 592c126ba..91f8aa3d3 100644
--- a/Dalamud/Game/Agent/AgentLifecycleEventListener.cs
+++ b/Dalamud/Game/Agent/AgentLifecycleEventListener.cs
@@ -35,9 +35,4 @@ public class AgentLifecycleEventListener
/// Gets the delegate this listener invokes.
///
public IAgentLifecycle.AgentEventDelegate FunctionDelegate { get; init; }
-
- ///
- /// Gets or sets if the listener is requested to be cleared.
- ///
- internal bool IsRequestedToClear { get; set; }
}
diff --git a/Dalamud/Game/ClientState/Customize/CustomizeData.cs b/Dalamud/Game/ClientState/Customize/CustomizeData.cs
deleted file mode 100644
index baf8d3a0a..000000000
--- a/Dalamud/Game/ClientState/Customize/CustomizeData.cs
+++ /dev/null
@@ -1,311 +0,0 @@
-using Dalamud.Game.ClientState.Objects.Types;
-
-namespace Dalamud.Game.ClientState.Customize;
-
-///
-/// This collection represents customization data a has.
-///
-public interface ICustomizeData
-{
- ///
- /// Gets the current race.
- /// E.g., Miqo'te, Aura.
- ///
- public byte Race { get; }
-
- ///
- /// Gets the current sex.
- ///
- public byte Sex { get; }
-
- ///
- /// Gets the current body type.
- ///
- public byte BodyType { get; }
-
- ///
- /// Gets the current height (0 to 100).
- ///
- public byte Height { get; }
-
- ///
- /// Gets the current tribe.
- /// E.g., Seeker of the Sun, Keeper of the Moon.
- ///
- public byte Tribe { get; }
-
- ///
- /// Gets the current face (1 to 4).
- ///
- public byte Face { get; }
-
- ///
- /// Gets the current hairstyle.
- ///
- public byte Hairstyle { get; }
-
- ///
- /// Gets the current skin color.
- ///
- public byte SkinColor { get; }
-
- ///
- /// Gets the current color of the left eye.
- ///
- public byte EyeColorLeft { get; }
-
- ///
- /// Gets the current color of the right eye.
- ///
- public byte EyeColorRight { get; }
-
- ///
- /// Gets the current main hair color.
- ///
- public byte HairColor { get; }
-
- ///
- /// Gets the current highlight hair color.
- ///
- public byte HighlightsColor { get; }
-
- ///
- /// Gets the current tattoo color.
- ///
- public byte TattooColor { get; }
-
- ///
- /// Gets the current eyebrow type.
- ///
- public byte Eyebrows { get; }
-
- ///
- /// Gets the current nose type.
- ///
- public byte Nose { get; }
-
- ///
- /// Gets the current jaw type.
- ///
- public byte Jaw { get; }
-
- ///
- /// Gets the current lip color fur pattern.
- ///
- public byte LipColorFurPattern { get; }
-
- ///
- /// Gets the current muscle mass value.
- ///
- public byte MuscleMass { get; }
-
- ///
- /// Gets the current tail type (1 to 4).
- ///
- public byte TailShape { get; }
-
- ///
- /// Gets the current bust size (0 to 100).
- ///
- public byte BustSize { get; }
-
- ///
- /// Gets the current color of the face paint.
- ///
- public byte FacePaintColor { get; }
-
- ///
- /// Gets a value indicating whether highlight color is used.
- ///
- public bool Highlights { get; }
-
- ///
- /// Gets a value indicating whether this facial feature is used.
- ///
- public bool FacialFeature1 { get; }
-
- ///
- public bool FacialFeature2 { get; }
-
- ///
- public bool FacialFeature3 { get; }
-
- ///
- public bool FacialFeature4 { get; }
-
- ///
- public bool FacialFeature5 { get; }
-
- ///
- public bool FacialFeature6 { get; }
-
- ///
- public bool FacialFeature7 { get; }
-
- ///
- /// Gets a value indicating whether the legacy tattoo is used.
- ///
- public bool LegacyTattoo { get; }
-
- ///
- /// Gets the current eye shape type.
- ///
- public byte EyeShape { get; }
-
- ///
- /// Gets a value indicating whether small iris is used.
- ///
- public bool SmallIris { get; }
-
- ///
- /// Gets the current mouth type.
- ///
- public byte Mouth { get; }
-
- ///
- /// Gets a value indicating whether lipstick is used.
- ///
- public bool Lipstick { get; }
-
- ///
- /// Gets the current face paint type.
- ///
- public byte FacePaint { get; }
-
- ///
- /// Gets a value indicating whether face paint reversed is used.
- ///
- public bool FacePaintReversed { get; }
-}
-
-///
-internal readonly unsafe struct CustomizeData : ICustomizeData
-{
- ///
- /// Gets or sets the address of the customize data struct in memory.
- ///
- public readonly nint Address;
-
- ///
- /// Initializes a new instance of the struct.
- ///
- /// Address of the status list.
- internal CustomizeData(nint address)
- {
- this.Address = address;
- }
-
- ///
- public byte Race => this.Struct->Race;
-
- ///
- public byte Sex => this.Struct->Sex;
-
- ///
- public byte BodyType => this.Struct->BodyType;
-
- ///
- public byte Height => this.Struct->Height;
-
- ///
- public byte Tribe => this.Struct->Tribe;
-
- ///
- public byte Face => this.Struct->Face;
-
- ///
- public byte Hairstyle => this.Struct->Hairstyle;
-
- ///
- public byte SkinColor => this.Struct->SkinColor;
-
- ///
- public byte EyeColorLeft => this.Struct->EyeColorLeft;
-
- ///
- public byte EyeColorRight => this.Struct->EyeColorRight;
-
- ///
- public byte HairColor => this.Struct->HairColor;
-
- ///
- public byte HighlightsColor => this.Struct->HighlightsColor;
-
- ///
- public byte TattooColor => this.Struct->TattooColor;
-
- ///
- public byte Eyebrows => this.Struct->Eyebrows;
-
- ///
- public byte Nose => this.Struct->Nose;
-
- ///
- public byte Jaw => this.Struct->Jaw;
-
- ///
- public byte LipColorFurPattern => this.Struct->LipColorFurPattern;
-
- ///
- public byte MuscleMass => this.Struct->MuscleMass;
-
- ///
- public byte TailShape => this.Struct->TailShape;
-
- ///
- public byte BustSize => this.Struct->BustSize;
-
- ///
- public byte FacePaintColor => this.Struct->FacePaintColor;
-
- ///
- public bool Highlights => this.Struct->Highlights;
-
- ///
- public bool FacialFeature1 => this.Struct->FacialFeature1;
-
- ///
- public bool FacialFeature2 => this.Struct->FacialFeature2;
-
- ///
- public bool FacialFeature3 => this.Struct->FacialFeature3;
-
- ///
- public bool FacialFeature4 => this.Struct->FacialFeature4;
-
- ///
- public bool FacialFeature5 => this.Struct->FacialFeature5;
-
- ///
- public bool FacialFeature6 => this.Struct->FacialFeature6;
-
- ///
- public bool FacialFeature7 => this.Struct->FacialFeature7;
-
- ///
- public bool LegacyTattoo => this.Struct->LegacyTattoo;
-
- ///
- public byte EyeShape => this.Struct->EyeShape;
-
- ///
- public bool SmallIris => this.Struct->SmallIris;
-
- ///
- public byte Mouth => this.Struct->Mouth;
-
- ///
- public bool Lipstick => this.Struct->Lipstick;
-
- ///
- public byte FacePaint => this.Struct->FacePaint;
-
- ///
- public bool FacePaintReversed => this.Struct->FacePaintReversed;
-
- ///
- /// Gets the underlying structure.
- ///
- internal FFXIVClientStructs.FFXIV.Client.Game.Character.CustomizeData* Struct =>
- (FFXIVClientStructs.FFXIV.Client.Game.Character.CustomizeData*)this.Address;
-}
diff --git a/Dalamud/Game/ClientState/Objects/Types/Character.cs b/Dalamud/Game/ClientState/Objects/Types/Character.cs
index f122f1f27..2002a16b8 100644
--- a/Dalamud/Game/ClientState/Objects/Types/Character.cs
+++ b/Dalamud/Game/ClientState/Objects/Types/Character.cs
@@ -1,8 +1,6 @@
using Dalamud.Data;
-using Dalamud.Game.ClientState.Customize;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.Text.SeStringHandling;
-using Dalamud.Utility;
using Lumina.Excel;
using Lumina.Excel.Sheets;
@@ -15,73 +13,68 @@ namespace Dalamud.Game.ClientState.Objects.Types;
public interface ICharacter : IGameObject
{
///
- /// Gets the current HP of this character.
+ /// Gets the current HP of this Chara.
///
public uint CurrentHp { get; }
///
- /// Gets the maximum HP of this character.
+ /// Gets the maximum HP of this Chara.
///
public uint MaxHp { get; }
///
- /// Gets the current MP of this character.
+ /// Gets the current MP of this Chara.
///
public uint CurrentMp { get; }
///
- /// Gets the maximum MP of this character.
+ /// Gets the maximum MP of this Chara.
///
public uint MaxMp { get; }
///
- /// Gets the current GP of this character.
+ /// Gets the current GP of this Chara.
///
public uint CurrentGp { get; }
///
- /// Gets the maximum GP of this character.
+ /// Gets the maximum GP of this Chara.
///
public uint MaxGp { get; }
///
- /// Gets the current CP of this character.
+ /// Gets the current CP of this Chara.
///
public uint CurrentCp { get; }
///
- /// Gets the maximum CP of this character.
+ /// Gets the maximum CP of this Chara.
///
public uint MaxCp { get; }
///
- /// Gets the shield percentage of this character.
+ /// Gets the shield percentage of this Chara.
///
public byte ShieldPercentage { get; }
///
- /// Gets the ClassJob of this character.
+ /// Gets the ClassJob of this Chara.
///
public RowRef ClassJob { get; }
///
- /// Gets the level of this character.
+ /// Gets the level of this Chara.
///
public byte Level { get; }
///
- /// Gets a byte array describing the visual appearance of this character.
+ /// Gets a byte array describing the visual appearance of this Chara.
/// Indexed by .
///
public byte[] Customize { get; }
///
- /// Gets the underlying CustomizeData struct for this character.
- ///
- public ICustomizeData CustomizeData { get; }
-
- ///
- /// Gets the Free Company tag of this character.
+ /// Gets the Free Company tag of this chara.
///
public SeString CompanyTag { get; }
@@ -99,12 +92,12 @@ public interface ICharacter : IGameObject
/// Gets the status flags.
///
public StatusFlags StatusFlags { get; }
-
+
///
/// Gets the current mount for this character. Will be null if the character doesn't have a mount.
///
public RowRef? CurrentMount { get; }
-
+
///
/// Gets the current minion summoned for this character. Will be null if the character doesn't have a minion.
/// This method *will* return information about a spawned (but invisible) minion, e.g. if the character is riding a
@@ -123,7 +116,7 @@ internal unsafe class Character : GameObject, ICharacter
/// This represents a non-static entity.
///
/// The address of this character in memory.
- internal Character(nint address)
+ internal Character(IntPtr address)
: base(address)
{
}
@@ -162,12 +155,8 @@ internal unsafe class Character : GameObject, ICharacter
public byte Level => this.Struct->CharacterData.Level;
///
- [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 ICustomizeData CustomizeData => new CustomizeData((nint)(&this.Struct->DrawData.CustomizeData));
-
///
public SeString CompanyTag => SeString.Parse(this.Struct->FreeCompanyTag);
@@ -194,14 +183,14 @@ internal unsafe class Character : GameObject, ICharacter
(this.Struct->IsAllianceMember ? StatusFlags.AllianceMember : StatusFlags.None) |
(this.Struct->IsFriend ? StatusFlags.Friend : StatusFlags.None) |
(this.Struct->IsCasting ? StatusFlags.IsCasting : StatusFlags.None);
-
+
///
public RowRef? CurrentMount
{
get
{
if (this.Struct->IsNotMounted()) return null; // just for safety.
-
+
var mountId = this.Struct->Mount.MountId;
return mountId == 0 ? null : LuminaUtils.CreateRef(mountId);
}
@@ -212,7 +201,7 @@ internal unsafe class Character : GameObject, ICharacter
{
get
{
- if (this.Struct->CompanionObject != null)
+ if (this.Struct->CompanionObject != null)
return LuminaUtils.CreateRef(this.Struct->CompanionObject->BaseId);
// this is only present if a minion is summoned but hidden (e.g. the player's on a mount).
diff --git a/Dalamud/Game/UnlockState/UnlockState.cs b/Dalamud/Game/UnlockState/UnlockState.cs
index 4b83e114a..5ccd7fadb 100644
--- a/Dalamud/Game/UnlockState/UnlockState.cs
+++ b/Dalamud/Game/UnlockState/UnlockState.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using Dalamud.Data;
using Dalamud.Game.Gui;
-using Dalamud.Hooking;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Logging.Internal;
@@ -17,9 +16,7 @@ using FFXIVClientStructs.FFXIV.Component.Exd;
using Lumina.Excel;
using Lumina.Excel.Sheets;
-using AchievementSheet = Lumina.Excel.Sheets.Achievement;
using ActionSheet = Lumina.Excel.Sheets.Action;
-using CSAchievement = FFXIVClientStructs.FFXIV.Client.Game.UI.Achievement;
using InstanceContentSheet = Lumina.Excel.Sheets.InstanceContent;
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 readonly ConcurrentDictionary> cachedUnlockedRowIds = [];
+
[ServiceManager.ServiceDependency]
private readonly DataManager dataManager = Service.Get();
@@ -45,38 +44,17 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
[ServiceManager.ServiceDependency]
private readonly RecipeData recipeData = Service.Get();
- private readonly ConcurrentDictionary> cachedUnlockedRowIds = [];
- private readonly Hook setAchievementCompletedHook;
- private readonly Hook setTitleUnlockedHook;
-
[ServiceManager.ServiceConstructor]
private UnlockState()
{
this.clientState.Login += this.OnLogin;
this.clientState.Logout += this.OnLogout;
this.gameGui.AgentUpdate += this.OnAgentUpdate;
-
- this.setAchievementCompletedHook = Hook.FromAddress(
- (nint)CSAchievement.MemberFunctionPointers.SetAchievementCompleted,
- this.SetAchievementCompletedDetour);
-
- this.setTitleUnlockedHook = Hook.FromAddress(
- (nint)TitleList.MemberFunctionPointers.SetTitleUnlocked,
- this.SetTitleUnlockedDetour);
-
- this.setAchievementCompletedHook.Enable();
- this.setTitleUnlockedHook.Enable();
}
///
public event IUnlockState.UnlockDelegate Unlock;
- ///
- public bool IsAchievementListLoaded => CSAchievement.Instance()->IsLoaded();
-
- ///
- public bool IsTitleListLoaded => UIState.Instance()->TitleList.DataReceived;
-
private bool IsLoaded => PlayerState.Instance()->IsLoaded;
///
@@ -85,21 +63,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
this.clientState.Login -= this.OnLogin;
this.clientState.Logout -= this.OnLogout;
this.gameGui.AgentUpdate -= this.OnAgentUpdate;
-
- this.setAchievementCompletedHook.Dispose();
- }
-
- ///
- 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);
}
///
@@ -108,15 +71,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
return this.IsUnlockLinkUnlocked(row.UnlockLink.RowId);
}
- ///
- public bool IsAdventureComplete(Adventure row)
- {
- if (!this.IsLoaded)
- return false;
-
- return PlayerState.Instance()->IsAdventureComplete(row.RowId - 0x210000);
- }
-
///
public bool IsAetherCurrentUnlocked(AetherCurrent row)
{
@@ -461,19 +415,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
return PlayerState.Instance()->IsSecretRecipeBookUnlocked(row.RowId);
}
- ///
- 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);
- }
-
///
public bool IsTraitUnlocked(Trait row)
{
@@ -523,15 +464,9 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
if (!this.IsLoaded || rowRef.IsUntyped)
return false;
- if (rowRef.TryGetValue(out var achievementRow))
- return this.IsAchievementComplete(achievementRow);
-
if (rowRef.TryGetValue(out var actionRow))
return this.IsActionUnlocked(actionRow);
- if (rowRef.TryGetValue(out var adventureRow))
- return this.IsAdventureComplete(adventureRow);
-
if (rowRef.TryGetValue(out var aetherCurrentRow))
return this.IsAetherCurrentUnlocked(aetherCurrentRow);
@@ -637,9 +572,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
if (rowRef.TryGetValue(out var secretRecipeBookRow))
return this.IsSecretRecipeBookUnlocked(secretRecipeBookRow);
- if (rowRef.TryGetValue(out var titleRow))
- return this.IsTitleUnlocked(titleRow);
-
if (rowRef.TryGetValue(out var traitRow))
return this.IsTraitUnlocked(traitRow);
@@ -689,26 +621,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
this.Update();
}
- private void SetAchievementCompletedDetour(CSAchievement* thisPtr, uint id)
- {
- this.setAchievementCompletedHook.Original(thisPtr, id);
-
- if (!this.IsLoaded)
- return;
-
- this.RaiseUnlockSafely((RowRef)LuminaUtils.CreateRef(id));
- }
-
- private void SetTitleUnlockedDetour(TitleList* thisPtr, ushort id)
- {
- this.setTitleUnlockedHook.Original(thisPtr, id);
-
- if (!this.IsLoaded)
- return;
-
- this.RaiseUnlockSafely((RowRef)LuminaUtils.CreateRef(id));
- }
-
private void Update()
{
if (!this.IsLoaded)
@@ -716,10 +628,7 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
Log.Verbose("Checking for new unlocks...");
- // Do not check for Achievements or Titles here!
-
this.UpdateUnlocksForSheet();
- this.UpdateUnlocksForSheet();
this.UpdateUnlocksForSheet();
this.UpdateUnlocksForSheet();
this.UpdateUnlocksForSheet();
@@ -766,6 +675,7 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
// For some other day:
// - FishingSpot
// - Spearfishing
+ // - Adventure (Sightseeing)
// - MinerFolkloreTome
// - BotanistFolkloreTome
// - FishingFolkloreTome
@@ -778,6 +688,8 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
// - EmjCostume
// Probably not happening, because it requires fetching data from server:
+ // - Achievements
+ // - Titles
// - Bozjan Field Notes
// - 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}");
- this.RaiseUnlockSafely((RowRef)rowRef);
- }
- }
-
- private void RaiseUnlockSafely(RowRef rowRef)
- {
- foreach (var action in Delegate.EnumerateInvocationList(this.Unlock))
- {
- try
+ foreach (var action in Delegate.EnumerateInvocationList(this.Unlock))
{
- action(rowRef);
- }
- catch (Exception ex)
- {
- Log.Error(ex, "Exception during raise of {handler}", action.Method);
+ try
+ {
+ action((RowRef)rowRef);
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "Exception during raise of {handler}", action.Method);
+ }
}
}
}
@@ -844,21 +751,9 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat
///
public event IUnlockState.UnlockDelegate? Unlock;
- ///
- public bool IsAchievementListLoaded => this.unlockStateService.IsAchievementListLoaded;
-
- ///
- public bool IsTitleListLoaded => this.unlockStateService.IsTitleListLoaded;
-
- ///
- public bool IsAchievementComplete(AchievementSheet row) => this.unlockStateService.IsAchievementComplete(row);
-
///
public bool IsActionUnlocked(ActionSheet row) => this.unlockStateService.IsActionUnlocked(row);
- ///
- public bool IsAdventureComplete(Adventure row) => this.unlockStateService.IsAdventureComplete(row);
-
///
public bool IsAetherCurrentCompFlgSetUnlocked(AetherCurrentCompFlgSet row) => this.unlockStateService.IsAetherCurrentCompFlgSetUnlocked(row);
@@ -979,9 +874,6 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat
///
public bool IsSecretRecipeBookUnlocked(SecretRecipeBook row) => this.unlockStateService.IsSecretRecipeBookUnlocked(row);
- ///
- public bool IsTitleUnlocked(Title row) => this.unlockStateService.IsTitleUnlocked(row);
-
///
public bool IsTraitUnlocked(Trait row) => this.unlockStateService.IsTraitUnlocked(row);
diff --git a/Dalamud/Hooking/Hook.cs b/Dalamud/Hooking/Hook.cs
index 265b118bc..c54a8f399 100644
--- a/Dalamud/Hooking/Hook.cs
+++ b/Dalamud/Hooking/Hook.cs
@@ -6,8 +6,6 @@ using Dalamud.Configuration.Internal;
using Dalamud.Hooking.Internal;
using Dalamud.Hooking.Internal.Verification;
-using TerraFX.Interop.Windows;
-
namespace Dalamud.Hooking;
///
@@ -22,8 +20,6 @@ public abstract class Hook : IDalamudHook where T : Delegate
private const ulong IMAGE_ORDINAL_FLAG64 = 0x8000000000000000;
// ReSharper disable once InconsistentNaming
private const uint IMAGE_ORDINAL_FLAG32 = 0x80000000;
- // ReSharper disable once InconsistentNaming
- private const int IMAGE_DIRECTORY_ENTRY_IMPORT = 1;
#pragma warning restore SA1310
private readonly IntPtr address;
@@ -128,25 +124,25 @@ public abstract class Hook : IDalamudHook where T : Delegate
module ??= Process.GetCurrentProcess().MainModule;
if (module == null)
throw new InvalidOperationException("Current module is null?");
- var pDos = (IMAGE_DOS_HEADER*)module.BaseAddress;
- var pNt = (IMAGE_FILE_HEADER*)(module.BaseAddress + pDos->e_lfanew + 4);
- var isPe64 = pNt->SizeOfOptionalHeader == Marshal.SizeOf();
- IMAGE_DATA_DIRECTORY* pDataDirectory;
+ var pDos = (PeHeader.IMAGE_DOS_HEADER*)module.BaseAddress;
+ var pNt = (PeHeader.IMAGE_FILE_HEADER*)(module.BaseAddress + (int)pDos->e_lfanew + 4);
+ var isPe64 = pNt->SizeOfOptionalHeader == Marshal.SizeOf();
+ PeHeader.IMAGE_DATA_DIRECTORY* pDataDirectory;
if (isPe64)
{
- var pOpt = (IMAGE_OPTIONAL_HEADER64*)(module.BaseAddress + pDos->e_lfanew + 4 + Marshal.SizeOf());
- pDataDirectory = &pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
+ var pOpt = (PeHeader.IMAGE_OPTIONAL_HEADER64*)(module.BaseAddress + (int)pDos->e_lfanew + 4 + Marshal.SizeOf());
+ pDataDirectory = &pOpt->ImportTable;
}
else
{
- var pOpt = (IMAGE_OPTIONAL_HEADER32*)(module.BaseAddress + pDos->e_lfanew + 4 + Marshal.SizeOf());
- pDataDirectory = &pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
+ var pOpt = (PeHeader.IMAGE_OPTIONAL_HEADER32*)(module.BaseAddress + (int)pDos->e_lfanew + 4 + Marshal.SizeOf());
+ pDataDirectory = &pOpt->ImportTable;
}
var moduleNameLowerWithNullTerminator = (moduleName + "\0").ToLowerInvariant();
- foreach (ref var importDescriptor in new Span(
- (IMAGE_IMPORT_DESCRIPTOR*)(module.BaseAddress + (int)pDataDirectory->VirtualAddress),
- (int)(pDataDirectory->Size / Marshal.SizeOf())))
+ foreach (ref var importDescriptor in new Span(
+ (PeHeader.IMAGE_IMPORT_DESCRIPTOR*)(module.BaseAddress + (int)pDataDirectory->VirtualAddress),
+ (int)(pDataDirectory->Size / Marshal.SizeOf())))
{
// Having all zero values signals the end of the table. We didn't find anything.
if (importDescriptor.Characteristics == 0)
@@ -252,7 +248,7 @@ public abstract class Hook : IDalamudHook where T : Delegate
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*)(baseAddress + (int)desc.OriginalFirstThunk), (int)((dir.Size - desc.OriginalFirstThunk) / Marshal.SizeOf()));
var importAddressesOversizedSpan = new Span((uint*)(baseAddress + (int)desc.FirstThunk), (int)((dir.Size - desc.FirstThunk) / Marshal.SizeOf()));
@@ -302,7 +298,7 @@ public abstract class Hook : IDalamudHook where T : Delegate
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*)(baseAddress + (int)desc.OriginalFirstThunk), (int)((dir.Size - desc.OriginalFirstThunk) / Marshal.SizeOf()));
var importAddressesOversizedSpan = new Span((ulong*)(baseAddress + (int)desc.FirstThunk), (int)((dir.Size - desc.FirstThunk) / Marshal.SizeOf()));
diff --git a/Dalamud/Hooking/Internal/PeHeader.cs b/Dalamud/Hooking/Internal/PeHeader.cs
new file mode 100644
index 000000000..51df4a174
--- /dev/null
+++ b/Dalamud/Hooking/Internal/PeHeader.cs
@@ -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
+ {
+ ///
+ /// Reserved for future use.
+ ///
+ TypeReg = 0x00000000,
+ ///
+ /// Reserved for future use.
+ ///
+ TypeDsect = 0x00000001,
+ ///
+ /// Reserved for future use.
+ ///
+ TypeNoLoad = 0x00000002,
+ ///
+ /// Reserved for future use.
+ ///
+ TypeGroup = 0x00000004,
+ ///
+ /// 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.
+ ///
+ TypeNoPadded = 0x00000008,
+ ///
+ /// Reserved for future use.
+ ///
+ TypeCopy = 0x00000010,
+ ///
+ /// The section contains executable code.
+ ///
+ ContentCode = 0x00000020,
+ ///
+ /// The section contains initialized data.
+ ///
+ ContentInitializedData = 0x00000040,
+ ///
+ /// The section contains uninitialized data.
+ ///
+ ContentUninitializedData = 0x00000080,
+ ///
+ /// Reserved for future use.
+ ///
+ LinkOther = 0x00000100,
+ ///
+ /// The section contains comments or other information. The .drectve section has this type. This is valid for object files only.
+ ///
+ LinkInfo = 0x00000200,
+ ///
+ /// Reserved for future use.
+ ///
+ TypeOver = 0x00000400,
+ ///
+ /// The section will not become part of the image. This is valid only for object files.
+ ///
+ LinkRemove = 0x00000800,
+ ///
+ /// The section contains COMDAT data. For more information, see section 5.5.6, COMDAT Sections (Object Only). This is valid only for object files.
+ ///
+ LinkComDat = 0x00001000,
+ ///
+ /// Reset speculative exceptions handling bits in the TLB entries for this section.
+ ///
+ NoDeferSpecExceptions = 0x00004000,
+ ///
+ /// The section contains data referenced through the global pointer (GP).
+ ///
+ RelativeGP = 0x00008000,
+ ///
+ /// Reserved for future use.
+ ///
+ MemPurgeable = 0x00020000,
+ ///
+ /// Reserved for future use.
+ ///
+ Memory16Bit = 0x00020000,
+ ///
+ /// Reserved for future use.
+ ///
+ MemoryLocked = 0x00040000,
+ ///
+ /// Reserved for future use.
+ ///
+ MemoryPreload = 0x00080000,
+ ///
+ /// Align data on a 1-byte boundary. Valid only for object files.
+ ///
+ Align1Bytes = 0x00100000,
+ ///
+ /// Align data on a 2-byte boundary. Valid only for object files.
+ ///
+ Align2Bytes = 0x00200000,
+ ///
+ /// Align data on a 4-byte boundary. Valid only for object files.
+ ///
+ Align4Bytes = 0x00300000,
+ ///
+ /// Align data on an 8-byte boundary. Valid only for object files.
+ ///
+ Align8Bytes = 0x00400000,
+ ///
+ /// Align data on a 16-byte boundary. Valid only for object files.
+ ///
+ Align16Bytes = 0x00500000,
+ ///
+ /// Align data on a 32-byte boundary. Valid only for object files.
+ ///
+ Align32Bytes = 0x00600000,
+ ///
+ /// Align data on a 64-byte boundary. Valid only for object files.
+ ///
+ Align64Bytes = 0x00700000,
+ ///
+ /// Align data on a 128-byte boundary. Valid only for object files.
+ ///
+ Align128Bytes = 0x00800000,
+ ///
+ /// Align data on a 256-byte boundary. Valid only for object files.
+ ///
+ Align256Bytes = 0x00900000,
+ ///
+ /// Align data on a 512-byte boundary. Valid only for object files.
+ ///
+ Align512Bytes = 0x00A00000,
+ ///
+ /// Align data on a 1024-byte boundary. Valid only for object files.
+ ///
+ Align1024Bytes = 0x00B00000,
+ ///
+ /// Align data on a 2048-byte boundary. Valid only for object files.
+ ///
+ Align2048Bytes = 0x00C00000,
+ ///
+ /// Align data on a 4096-byte boundary. Valid only for object files.
+ ///
+ Align4096Bytes = 0x00D00000,
+ ///
+ /// Align data on an 8192-byte boundary. Valid only for object files.
+ ///
+ Align8192Bytes = 0x00E00000,
+ ///
+ /// The section contains extended relocations.
+ ///
+ LinkExtendedRelocationOverflow = 0x01000000,
+ ///
+ /// The section can be discarded as needed.
+ ///
+ MemoryDiscardable = 0x02000000,
+ ///
+ /// The section cannot be cached.
+ ///
+ MemoryNotCached = 0x04000000,
+ ///
+ /// The section is not pageable.
+ ///
+ MemoryNotPaged = 0x08000000,
+ ///
+ /// The section can be shared in memory.
+ ///
+ MemoryShared = 0x10000000,
+ ///
+ /// The section can be executed as code.
+ ///
+ MemoryExecute = 0x20000000,
+ ///
+ /// The section can be read.
+ ///
+ MemoryRead = 0x40000000,
+ ///
+ /// The section can be written to.
+ ///
+ 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;
+ }
+}
diff --git a/Dalamud/Hooking/Internal/Verification/HookVerifier.cs b/Dalamud/Hooking/Internal/Verification/HookVerifier.cs
index 98568a567..ebe6851ce 100644
--- a/Dalamud/Hooking/Internal/Verification/HookVerifier.cs
+++ b/Dalamud/Hooking/Internal/Verification/HookVerifier.cs
@@ -6,8 +6,6 @@ using System.Runtime.InteropServices;
using Dalamud.Game;
using Dalamud.Logging.Internal;
-using FFXIVClientStructs.FFXIV.Application.Network;
-
using InteropGenerator.Runtime;
namespace Dalamud.Hooking.Internal.Verification;
@@ -27,12 +25,7 @@ internal static class HookVerifier
"ActorControlSelf",
"E8 ?? ?? ?? ?? 0F B7 0B 83 E9 64",
typeof(ActorControlSelfDelegate), // TODO: change this to CS delegate
- "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
+ "Signature changed in Patch 7.4") // 7.4 (new parameters)
];
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 isAssemblyMarshaled = passedType.Assembly.GetCustomAttribute() is null;
// Directly compare delegates
if (passedType == entry.TargetDelegateType)
@@ -86,7 +78,7 @@ internal static class HookVerifier
var enforcedInvoke = entry.TargetDelegateType.GetMethod("Invoke")!;
// Compare Return Type
- var mismatch = !CheckParam(passedInvoke.ReturnType, enforcedInvoke.ReturnType, isAssemblyMarshaled);
+ var mismatch = !CheckParam(passedInvoke.ReturnType, enforcedInvoke.ReturnType);
// Compare Parameter Count
var passedParams = passedInvoke.GetParameters();
@@ -101,7 +93,7 @@ internal static class HookVerifier
// Compare Parameter Types
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;
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;
- 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 {
- _ 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(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.Name.StartsWith("FixedSizeArray") => SizeOf(type.GetGenericArguments()[0], isMarshaled) * int.Parse(type.Name[14..type.Name.IndexOf('`')]),
- _ when type.GetCustomAttribute() is { Length: var length } => SizeOf(type.GetGenericArguments()[0], isMarshaled) * length,
+ _ when type.Name.StartsWith("FixedSizeArray") => SizeOf(type.GetGenericArguments()[0]) * int.Parse(type.Name[14..type.Name.IndexOf('`')]),
+ _ when type.GetCustomAttribute() 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 type.IsEnum => SizeOf(Enum.GetUnderlyingType(type), isMarshaled),
+ _ when type.IsEnum => SizeOf(Enum.GetUnderlyingType(type)),
_ when type.IsGenericType => Marshal.SizeOf(Activator.CreateInstance(type)!),
_ => GetSizeOf(type),
};
diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs
index b1fc6e049..ef886e957 100644
--- a/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs
+++ b/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs
@@ -657,7 +657,6 @@ public partial class FileDialog
this.fileNameBuffer = $"{this.selectedFileNames.Count} files Selected";
}
- this.SelectionChanged(this, this.GetFilePathName());
if (setLastSelection)
{
this.lastSelectedFileName = name;
diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs
index b9ac634ab..e33fc2fc4 100644
--- a/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs
+++ b/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs
@@ -97,8 +97,6 @@ public partial class FileDialog
this.SetupSideBar();
}
- public event EventHandler? SelectionChanged;
-
///
/// Shows the dialog.
///
diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialogManager.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialogManager.cs
index 7332cd735..ee12e7424 100644
--- a/Dalamud/Interface/ImGuiFileDialog/FileDialogManager.cs
+++ b/Dalamud/Interface/ImGuiFileDialog/FileDialogManager.cs
@@ -30,12 +30,6 @@ public class FileDialogManager
private Action>? multiCallback;
private string savedPath = ".";
- ///
- /// Event fires when a new file is selected by the user
- ///
- /// Returns the path of the file as a string
- public event EventHandler? SelectionChanged;
-
///
/// Create a dialog which selects an already existing folder.
///
@@ -181,8 +175,6 @@ public class FileDialogManager
this.multiCallback = null;
}
- private void OnSelectionChange(object sender, string path) => this.SelectionChanged?.Invoke(sender, path);
-
private void SetDialog(
string id,
string title,
@@ -208,7 +200,6 @@ public class FileDialogManager
if (this.dialog is not null)
{
this.dialog.SortOrderChanged -= this.OnSortOrderChange;
- this.dialog.SelectionChanged -= this.OnSelectionChange;
}
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.SelectionChanged += this.OnSelectionChange;
this.dialog.WindowFlags |= this.AddedWindowFlags;
foreach (var (name, location, icon, position) in this.CustomSideBarItems)
this.dialog.SetQuickAccess(name, location, icon, position);
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs
index ebb5b6581..73e9d18f8 100644
--- a/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs
@@ -87,7 +87,7 @@ internal class DataShareWidget : IDataWindowWidget
try
{
var dataShare = Service.Get();
- var data2 = dataShare.GetData
/// Name of the data.
- /// The internal name and effective working ID of the creating plugin.
+ /// The assembly name of the initial creator.
/// A reference to data.
/// The type of the data.
- public DataCache(string tag, DataCachePluginId creatorPluginId, object? data, Type type)
+ public DataCache(string tag, string creatorAssemblyName, object? data, Type type)
{
this.Tag = tag;
- this.CreatorPluginId = creatorPluginId;
- this.UserPluginIds = [];
+ this.CreatorAssemblyName = creatorAssemblyName;
+ this.UserAssemblyNames = [];
this.Data = data;
this.Type = type;
}
@@ -50,40 +49,40 @@ internal readonly struct DataCache
/// Creates a new instance of the struct, using the given data generator function.
///
/// The name for the data cache.
- /// The internal name and effective working ID of the creating plugin.
+ /// The assembly name of the initial creator.
/// The function that generates the data if it does not already exist.
/// The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.
/// The new instance of .
- public static DataCache From(string tag, DataCachePluginId creatorPluginId, Func dataGenerator)
+ public static DataCache From(string tag, string creatorAssemblyName, Func dataGenerator)
where T : class
{
try
{
- var result = new DataCache(tag, creatorPluginId, dataGenerator.Invoke(), typeof(T));
+ var result = new DataCache(tag, creatorAssemblyName, dataGenerator.Invoke(), typeof(T));
Log.Verbose(
"[{who}] Created new data for [{Tag:l}] for creator {Creator:l}.",
nameof(DataShare),
tag,
- creatorPluginId);
+ creatorAssemblyName);
return result;
}
catch (Exception e)
{
throw ExceptionDispatchInfo.SetCurrentStackTrace(
- new DataCacheCreationError(tag, creatorPluginId, typeof(T), e));
+ new DataCacheCreationError(tag, creatorAssemblyName, typeof(T), e));
}
}
///
/// Attempts to fetch the data.
///
- /// The calling plugin ID.
+ /// The name of the caller assembly.
/// The value, if succeeded.
/// The exception, if failed.
/// Desired type of the data.
/// true on success.
public bool TryGetData(
- DataCachePluginId callingPluginId,
+ string callerName,
[NotNullWhen(true)] out T? value,
[NotNullWhen(false)] out Exception? ex)
where T : class
@@ -99,21 +98,16 @@ internal readonly struct DataCache
value = data;
ex = null;
- // Register the access history. The effective working ID is unique per plugin and persists between reloads, so only add it once.
- lock (this.UserPluginIds)
- {
- if (this.UserPluginIds.All(c => c.EffectiveWorkingId != callingPluginId.EffectiveWorkingId))
- {
- this.UserPluginIds.Add(callingPluginId);
- }
- }
+ // Register the access history
+ lock (this.UserAssemblyNames)
+ this.UserAssemblyNames.Add(callerName);
return true;
default:
value = null;
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;
}
}
diff --git a/Dalamud/Plugin/Ipc/Internal/DataCachePluginId.cs b/Dalamud/Plugin/Ipc/Internal/DataCachePluginId.cs
deleted file mode 100644
index c68dc7c06..000000000
--- a/Dalamud/Plugin/Ipc/Internal/DataCachePluginId.cs
+++ /dev/null
@@ -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;
-
-///
-/// Stores the internal name and effective working ID of a plugin accessing datashare.
-///
-/// The internal name of the plugin.
-/// The effective working ID of the plugin.
-public record DataCachePluginId(string InternalName, Guid EffectiveWorkingId);
diff --git a/Dalamud/Plugin/Ipc/Internal/DataShare.cs b/Dalamud/Plugin/Ipc/Internal/DataShare.cs
index ffad4876e..becbe1211 100644
--- a/Dalamud/Plugin/Ipc/Internal/DataShare.cs
+++ b/Dalamud/Plugin/Ipc/Internal/DataShare.cs
@@ -33,23 +33,24 @@ internal class DataShare : IServiceType
///
/// The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.
/// The name for the data cache.
- /// The ID of the calling plugin.
/// The function that generates the data if it does not already exist.
/// Either the existing data for or the data generated by .
/// Thrown if a cache for exists, but contains data of a type not assignable to .
/// Thrown if the stored data for a cache is null.
/// Thrown if throws an exception or returns null.
- public T GetOrCreateData(string tag, DataCachePluginId callingPluginId, Func dataGenerator)
+ public T GetOrCreateData(string tag, Func dataGenerator)
where T : class
{
+ var callerName = GetCallerName();
+
Lazy cacheLazy;
lock (this.caches)
{
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(callingPluginId, out var value, out var ex) ? value : throw ex;
+ return cacheLazy.Value.TryGetData(callerName, out var value, out var ex) ? value : throw ex;
}
///
@@ -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.
///
/// The name for the data cache.
- /// The ID of the calling plugin.
- public void RelinquishData(string tag, DataCachePluginId callingPluginId)
+ public void RelinquishData(string tag)
{
DataCache cache;
lock (this.caches)
@@ -66,8 +66,10 @@ internal class DataShare : IServiceType
if (!this.caches.TryGetValue(tag, out var cacheLazy))
return;
+ var callerName = GetCallerName();
+
cache = cacheLazy.Value;
- if (!cache.UserPluginIds.Remove(callingPluginId) || cache.UserPluginIds.Count > 0)
+ if (!cache.UserAssemblyNames.Remove(callerName) || cache.UserAssemblyNames.Count > 0)
return;
if (!this.caches.Remove(tag))
return;
@@ -82,7 +84,7 @@ internal class DataShare : IServiceType
}
catch (Exception e)
{
- Log.Error(e, "[DataShare] Failed to dispose [{Tag:l}] after it was removed from all shares.", tag);
+ Log.Error(e, "[DataShare] Failed to dispose [{Tag:l}] after it was removed from all shares.", tag);
}
}
else
@@ -97,10 +99,9 @@ internal class DataShare : IServiceType
///
/// The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.
/// The name for the data cache.
- /// The ID of the calling plugin.
/// The requested data on success, null otherwise.
/// True if the requested data exists and is assignable to the requested type.
- public bool TryGetData(string tag, DataCachePluginId callingPluginId, [NotNullWhen(true)] out T? data)
+ public bool TryGetData(string tag, [NotNullWhen(true)] out T? data)
where T : class
{
data = null;
@@ -111,7 +112,7 @@ internal class DataShare : IServiceType
return false;
}
- return cacheLazy.Value.TryGetData(callingPluginId, out data, out _);
+ return cacheLazy.Value.TryGetData(GetCallerName(), out data, out _);
}
///
@@ -120,12 +121,11 @@ internal class DataShare : IServiceType
///
/// The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.
/// The name for the data cache.
- /// The ID of the calling plugin.
/// The requested data.
/// Thrown if is not registered.
/// Thrown if a cache for exists, but contains data of a type not assignable to .
/// Thrown if the stored data for a cache is null.
- public T GetData(string tag, DataCachePluginId callingPluginId)
+ public T GetData(string tag)
where T : class
{
Lazy cacheLazy;
@@ -135,19 +135,35 @@ internal class DataShare : IServiceType
throw new KeyNotFoundException($"The data cache [{tag}] is not registered.");
}
- return cacheLazy.Value.TryGetData(callingPluginId, out var value, out var ex) ? value : throw ex;
+ return cacheLazy.Value.TryGetData(GetCallerName(), out var value, out var ex) ? value : throw ex;
}
///
/// Obtain a read-only list of data shares.
///
/// All currently subscribed tags, their creator names and all their users.
- internal IEnumerable<(string Tag, DataCachePluginId CreatorPluginId, DataCachePluginId[] UserPluginIds)> GetAllShares()
+ internal IEnumerable<(string Tag, string CreatorAssembly, string[] Users)> GetAllShares()
{
lock (this.caches)
{
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()));
}
}
+
+ /// Obtain the last assembly name in the stack trace that is not a system or dalamud assembly.
+ 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";
+ }
}
diff --git a/Dalamud/Plugin/Services/IPlayerState.cs b/Dalamud/Plugin/Services/IPlayerState.cs
index 838d5a346..21d88010b 100644
--- a/Dalamud/Plugin/Services/IPlayerState.cs
+++ b/Dalamud/Plugin/Services/IPlayerState.cs
@@ -159,7 +159,7 @@ public interface IPlayerState : IDalamudService
RowRef FreeAetheryte { get; }
///
- /// Gets the amount of rested experience available to the local player.
+ /// Gets the amount of received player commendations of the local player.
///
uint BaseRestedExperience { get; }
diff --git a/Dalamud/Plugin/Services/IUnlockState.cs b/Dalamud/Plugin/Services/IUnlockState.cs
index 4f92b2b3d..f51222ba1 100644
--- a/Dalamud/Plugin/Services/IUnlockState.cs
+++ b/Dalamud/Plugin/Services/IUnlockState.cs
@@ -1,3 +1,5 @@
+using System.Diagnostics.CodeAnalysis;
+
using Lumina.Excel;
using Lumina.Excel.Sheets;
@@ -21,24 +23,6 @@ public interface IUnlockState : IDalamudService
///
event UnlockDelegate? Unlock;
- ///
- /// Gets a value indicating whether the full Achievements list was received.
- ///
- bool IsAchievementListLoaded { get; }
-
- ///
- /// Gets a value indicating whether the full Titles list was received.
- ///
- bool IsTitleListLoaded { get; }
-
- ///
- /// Determines whether the specified Achievement is completed.
- /// Requires that the player requested the Achievements list (can be chcked with ).
- ///
- /// The Achievement row to check.
- /// if completed; otherwise, .
- bool IsAchievementComplete(Achievement row);
-
///
/// Determines whether the specified Action is unlocked.
///
@@ -46,13 +30,6 @@ public interface IUnlockState : IDalamudService
/// if unlocked; otherwise, .
bool IsActionUnlocked(Lumina.Excel.Sheets.Action row);
- ///
- /// Determines whether the specified Adventure is completed.
- ///
- /// The Adventure row to check.
- /// if completed; otherwise, .
- public bool IsAdventureComplete(Adventure row);
-
///
/// Determines whether the specified AetherCurrentCompFlgSet is unlocked.
///
@@ -334,14 +311,6 @@ public interface IUnlockState : IDalamudService
/// if unlocked; otherwise, .
bool IsSecretRecipeBookUnlocked(SecretRecipeBook row);
- ///
- /// Determines whether the specified Title is unlocked.
- /// Requires that the player requested the Titles list (can be chcked with ).
- ///
- /// The Title row to check.
- /// if unlocked; otherwise, .
- bool IsTitleUnlocked(Title row);
-
///
/// Determines whether the specified Trait is unlocked.
///
diff --git a/Dalamud/Support/Troubleshooting.cs b/Dalamud/Support/Troubleshooting.cs
index 779754ee8..2dd0fb623 100644
--- a/Dalamud/Support/Troubleshooting.cs
+++ b/Dalamud/Support/Troubleshooting.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Text;
@@ -69,7 +68,6 @@ public static class Troubleshooting
{
var payload = new TroubleshootingPayload
{
- Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
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()),
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)));
Log.Information($"TROUBLESHOOTING:{encodedPayload}");
-
- File.WriteAllText(
- Path.Join(
- startInfo.LogPath,
- "dalamud.troubleshooting.json"),
- JsonConvert.SerializeObject(payload, Formatting.Indented));
}
catch (Exception ex)
{
@@ -111,8 +103,6 @@ public static class Troubleshooting
private class TroubleshootingPayload
{
- public long Timestamp { get; set; }
-
public LocalPluginManifest[]? LoadedPlugins { get; set; }
public Dictionary? PluginStates { get; set; }
diff --git a/DalamudCrashHandler/DalamudCrashHandler.cpp b/DalamudCrashHandler/DalamudCrashHandler.cpp
index f883ba55b..f28715dc1 100644
--- a/DalamudCrashHandler/DalamudCrashHandler.cpp
+++ b/DalamudCrashHandler/DalamudCrashHandler.cpp
@@ -28,9 +28,6 @@
#include
#include
-#include
-#pragma comment(lib, "dxgi.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='*'\"")
@@ -473,39 +470,12 @@ void open_folder_and_select_items(HWND hwndOpener, const std::wstring& path) {
ILFree(piid);
}
-std::vector enum_dxgi_adapters()
-{
- std::vector 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) {
static const char* SourceLogFiles[] = {
"output.log", // XIVLauncher for Windows
"launcher.log", // XIVLauncher.Core for [mostly] Linux
"patcher.log",
"dalamud.log",
- "dalamud.troubleshooting.json",
"dalamud.injector.log",
"dalamud.boot.log",
"aria.log",
@@ -723,7 +693,7 @@ void restart_game_using_injector(int nRadioButton, const std::vector cpui;
int nIds_;
@@ -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"CPU Vendor: {}", vendor) << 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;
if (pProgressDialog)
diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs
index a97e9f89d..9ba281cab 160000
--- a/lib/FFXIVClientStructs
+++ b/lib/FFXIVClientStructs
@@ -1 +1 @@
-Subproject commit a97e9f89d72d40eabd0f3b52266862dca3eba872
+Subproject commit 9ba281cab958049b47bbf7199ab14742c609cd4b