diff --git a/Dalamud/Game/ClientState/Actors/Types/PartyMember.cs b/Dalamud/Game/ClientState/Actors/Types/PartyMember.cs
new file mode 100644
index 000000000..07fef0e75
--- /dev/null
+++ b/Dalamud/Game/ClientState/Actors/Types/PartyMember.cs
@@ -0,0 +1,34 @@
+using Dalamud.Game.ClientState.Structs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Dalamud.Game.ClientState.Actors.Types
+{
+ public class PartyMember
+ {
+ public string CharacterName;
+ public long Unknown;
+ public Actor Actor;
+ public ObjectKind ObjectKind;
+
+ public PartyMember(ActorTable table, Structs.PartyMember rawData)
+ {
+ CharacterName = Marshal.PtrToStringAnsi(rawData.namePtr);
+ Unknown = rawData.unknown;
+ Actor = null;
+ for (var i = 0; i < table.Length; i++)
+ {
+ if (table[i].ActorId == rawData.actorId)
+ {
+ Actor = table[i];
+ break;
+ }
+ }
+ ObjectKind = rawData.objectKind;
+ }
+ }
+}
diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs
index 6215be83b..aff9b2ec8 100644
--- a/Dalamud/Game/ClientState/ClientState.cs
+++ b/Dalamud/Game/ClientState/ClientState.cs
@@ -80,6 +80,11 @@ namespace Dalamud.Game.ClientState
///
public JobGauges JobGauges;
+ ///
+ /// The class facilitating party list data access
+ ///
+ public PartyList PartyList;
+
///
/// Provides access to the keypress state of keyboard keys in game.
///
@@ -101,6 +106,8 @@ namespace Dalamud.Game.ClientState
this.Actors = new ActorTable(dalamud, Address);
+ this.PartyList = new PartyList(dalamud, Address);
+
this.JobGauges = new JobGauges(Address);
this.KeyState = new KeyState(Address, scanner.Module.BaseAddress);
@@ -116,11 +123,13 @@ namespace Dalamud.Game.ClientState
public void Enable() {
this.Actors.Enable();
+ this.PartyList.Enable();
this.setupTerritoryTypeHook.Enable();
}
public void Dispose() {
this.Actors.Dispose();
+ this.PartyList.Dispose();
this.setupTerritoryTypeHook.Dispose();
}
diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs
index 27717981a..45ed5577a 100644
--- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs
+++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs
@@ -13,7 +13,8 @@ namespace Dalamud.Game.ClientState
// Functions
public IntPtr SetupTerritoryType { get; private set; }
public IntPtr SomeActorTableAccess { get; private set; }
-
+ public IntPtr PartyListUpdate { get; private set; }
+
protected override void Setup64Bit(SigScanner sig) {
ViewportActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? 85 ED", 0) + 0x148;
SomeActorTableAccess = sig.ScanText("E8 ?? ?? ?? ?? 48 8D 55 A0 48 8D 8E ?? ?? ?? ??");
@@ -25,6 +26,8 @@ namespace Dalamud.Game.ClientState
// This resolves to a fixed offset only, without the base address added in, so GetStaticAddressFromSig() can't be used
KeyboardState = sig.ScanText("48 8D 0C 85 ?? ?? ?? ?? 8B 04 31 85 C2 0F 85") + 0x4;
+
+ PartyListUpdate = sig.ScanText("E8 ?? ?? ?? ?? 49 8B D4 4C 8D 87 ?? ?? ?? ??");
}
}
}
diff --git a/Dalamud/Game/ClientState/PartyList.cs b/Dalamud/Game/ClientState/PartyList.cs
new file mode 100644
index 000000000..edb6206e7
--- /dev/null
+++ b/Dalamud/Game/ClientState/PartyList.cs
@@ -0,0 +1,113 @@
+using Dalamud.Game.ClientState.Actors.Types;
+using Dalamud.Hooking;
+using Dalamud.Plugin;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Dalamud.Game.ClientState
+
+{
+ public class PartyList : ICollection, IDisposable
+ {
+ private ClientStateAddressResolver Address { get; }
+ private Dalamud dalamud;
+
+ private delegate long PartyListUpdateDelegate(IntPtr structBegin, long param2, char param3);
+
+ private Hook partyListUpdateHook;
+ private IntPtr partyListBegin;
+ private bool isReady = false;
+
+ public PartyList(Dalamud dalamud, ClientStateAddressResolver addressResolver)
+ {
+ Address = addressResolver;
+ this.dalamud = dalamud;
+ partyListUpdateHook = new Hook(Address.PartyListUpdate, new PartyListUpdateDelegate(PartyListUpdateDetour), this);
+ }
+
+ public void Enable()
+ {
+ partyListUpdateHook.Enable();
+ }
+
+ public void Dispose()
+ {
+ if (!this.isReady)
+ partyListUpdateHook.Dispose();
+ isReady = false;
+ }
+
+ private long PartyListUpdateDetour(IntPtr structBegin, long param2, char param3)
+ {
+ var result = partyListUpdateHook.Original(structBegin, param2, param3);
+ partyListBegin = structBegin + 0xB48;
+ partyListUpdateHook.Dispose();
+ isReady = true;
+ return result;
+ }
+ public PartyMember this[int index]
+ {
+ get
+ {
+ if (!this.isReady)
+ return null;
+ if (index >= Length)
+ return null;
+ var tblIndex = partyListBegin + index * 24;
+ var memberStruct = Marshal.PtrToStructure(tblIndex);
+ return new PartyMember(dalamud.ClientState.Actors, memberStruct);
+ }
+ }
+
+ public void CopyTo(Array array, int index)
+ {
+ for (var i = 0; i < Length; i++)
+ {
+ array.SetValue(this[i], index);
+ index++;
+ }
+ }
+
+ private class PartyListEnumerator : IEnumerator
+ {
+ private readonly PartyList party;
+ private int currentIndex;
+
+ public PartyListEnumerator(PartyList list)
+ {
+ party = list;
+ }
+
+ public object Current => party[currentIndex];
+
+ public bool MoveNext()
+ {
+ currentIndex++;
+ return currentIndex != party.Length;
+ }
+
+ public void Reset()
+ {
+ currentIndex = 0;
+ }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return new PartyListEnumerator(this);
+ }
+
+ public int Length => !this.isReady ? 0 : Marshal.ReadByte(partyListBegin + 0xF0);
+
+ public int Count => Length;
+
+ public object SyncRoot => this;
+
+ public bool IsSynchronized => false;
+ }
+}
diff --git a/Dalamud/Game/ClientState/Structs/PartyMember.cs b/Dalamud/Game/ClientState/Structs/PartyMember.cs
new file mode 100644
index 000000000..71dbf0d91
--- /dev/null
+++ b/Dalamud/Game/ClientState/Structs/PartyMember.cs
@@ -0,0 +1,19 @@
+using Dalamud.Game.ClientState.Actors;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Dalamud.Game.ClientState.Structs
+{
+ [StructLayout(LayoutKind.Explicit)]
+ public struct PartyMember
+ {
+ [FieldOffset(0x0)] public IntPtr namePtr;
+ [FieldOffset(0x8)] public long unknown;
+ [FieldOffset(0x10)] public int actorId;
+ [FieldOffset(0x14)] public ObjectKind objectKind;
+ }
+}