diff --git a/Dalamud/Game/Gui/PartyFinder/Internal/PartyFinderPacket.cs b/Dalamud/Game/Gui/PartyFinder/Internal/PartyFinderPacket.cs
index a7ab12e4b..324ae6b28 100644
--- a/Dalamud/Game/Gui/PartyFinder/Internal/PartyFinderPacket.cs
+++ b/Dalamud/Game/Gui/PartyFinder/Internal/PartyFinderPacket.cs
@@ -9,8 +9,8 @@ namespace Dalamud.Game.Gui.PartyFinder.Internal;
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "Sequential struct marshaling.")]
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Sequential struct marshaling.")]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the field usage.")]
-[StructLayout(LayoutKind.Sequential)]
-internal readonly struct PartyFinderPacket
+[StructLayout(LayoutKind.Sequential, Pack = 1)]
+internal unsafe struct PartyFinderPacket
{
///
/// Gets the size of this packet.
@@ -19,8 +19,7 @@ internal readonly struct PartyFinderPacket
internal readonly int BatchNumber;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
- private readonly byte[] padding1;
+ private fixed byte paddingOuter[0x8];
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
internal readonly PartyFinderPacketListing[] Listings;
diff --git a/Dalamud/Game/Gui/PartyFinder/Internal/PartyFinderPacketListing.cs b/Dalamud/Game/Gui/PartyFinder/Internal/PartyFinderPacketListing.cs
index 8cf9acf08..107d28cfb 100644
--- a/Dalamud/Game/Gui/PartyFinder/Internal/PartyFinderPacketListing.cs
+++ b/Dalamud/Game/Gui/PartyFinder/Internal/PartyFinderPacketListing.cs
@@ -9,95 +9,113 @@ namespace Dalamud.Game.Gui.PartyFinder.Internal;
///
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Sequential struct marshaling.")]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the field usage.")]
-[StructLayout(LayoutKind.Sequential)]
-internal readonly struct PartyFinderPacketListing
+[StructLayout(LayoutKind.Sequential, Pack = 1)]
+internal unsafe struct PartyFinderPacketListing
{
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
- private readonly byte[] padding1;
- internal readonly uint Id;
+ private fixed byte padding1[4];
+ internal uint Id;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
- private readonly byte[] padding2;
+ private fixed byte padding2[4];
+ internal uint PaddingId;
- internal readonly uint PaddingId;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
- private readonly byte[] padding3;
+ private fixed byte padding3[4];
+ internal ulong ContentId;
- internal readonly ulong ContentId;
+ private fixed byte padding4[4];
+ internal byte Category;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
- private readonly byte[] padding4;
+ private fixed byte padding5[3];
+ internal ushort Duty;
+ internal byte DutyType;
- internal readonly byte Category;
+ private fixed byte padding6[11];
+ internal ushort World;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
- private readonly byte[] padding5;
+ private fixed byte padding7[8];
+ internal byte Objective;
+ internal byte BeginnersWelcome;
+ internal byte Conditions;
+ internal byte DutyFinderSettings;
+ internal byte LootRules;
- internal readonly ushort Duty;
- internal readonly byte DutyType;
+ private fixed byte padding8[3];
+ internal uint LastPatchHotfixTimestamp; // last time the servers were restarted?
+ internal ushort SecondsRemaining;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
- private readonly byte[] padding6;
+ private fixed byte padding9[6];
+ internal ushort MinimumItemLevel;
+ internal ushort HomeWorld;
+ internal ushort CurrentWorld;
- internal readonly ushort World;
+ private byte padding10;
+ internal byte NumSlots;
+ internal byte NumSlotsFilled;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
- private readonly byte[] padding7;
+ private byte padding11;
+ internal byte SearchArea;
- internal readonly byte Objective;
- internal readonly byte BeginnersWelcome;
- internal readonly byte Conditions;
- internal readonly byte DutyFinderSettings;
- internal readonly byte LootRules;
+ private byte padding12;
+ internal byte NumParties;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
- private readonly byte[] padding8; // all zero in every pf I've examined
+ private fixed byte padding13[7];
+ private fixed ulong slots[8];
+ private fixed byte jobsPresent[8];
+ private fixed byte name[32];
+ private fixed byte description[192];
- internal readonly uint LastPatchHotfixTimestamp; // last time the servers were restarted?
- internal readonly ushort SecondsRemaining;
-
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
- private readonly byte[] padding9; // 00 00 01 00 00 00 in every pf I've examined
-
- internal readonly ushort MinimumItemLevel;
- internal readonly ushort HomeWorld;
- internal readonly ushort CurrentWorld;
-
- private readonly byte padding10;
-
- internal readonly byte NumSlots;
- internal readonly byte NumSlotsFilled;
-
- private readonly byte padding11;
-
- internal readonly byte SearchArea;
-
- private readonly byte padding12;
-
- internal readonly byte NumParties;
-
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
- private readonly byte[] padding13; // 00 00 00 always. maybe numParties is a u32?
-
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
- internal readonly ulong[] Slots;
-
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
- internal readonly byte[] JobsPresent;
-
- // Note that ByValTStr will not work here because the strings are UTF-8 and there's only a CharSet for UTF-16 in C#.
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
- internal readonly byte[] Name;
-
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 192)]
- internal readonly byte[] Description;
-
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
- private readonly byte[] padding14;
+ private fixed byte padding14[4];
internal bool IsNull()
{
// a valid party finder must have at least one slot set
return this.Slots.All(slot => slot == 0);
}
+
+ #region Helper
+
+ internal ulong[] Slots
+ {
+ get
+ {
+ fixed (ulong* ptr = this.slots)
+ {
+ return new ReadOnlySpan(ptr, 8).ToArray();
+ }
+ }
+ }
+
+ internal byte[] JobsPresent
+ {
+ get
+ {
+ fixed (byte* ptr = this.jobsPresent)
+ {
+ return new ReadOnlySpan(ptr, 8).ToArray();
+ }
+ }
+ }
+
+ internal byte[] Name
+ {
+ get
+ {
+ fixed (byte* ptr = this.name)
+ {
+ return new ReadOnlySpan(ptr, 32).ToArray();
+ }
+ }
+ }
+
+ internal byte[] Description
+ {
+ get
+ {
+ fixed (byte* ptr = this.description)
+ {
+ return new ReadOnlySpan(ptr, 192).ToArray();
+ }
+ }
+ }
+
+ #endregion
}