From 4b3e56af534519ec055a23835db0ade9535f9507 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 27 Sep 2026 22:35:03 +0200 Subject: [PATCH 1/5] Add some Excel findings --- .../FFXIV/Client/UI/Agent/AgentFishGuide.cs | 4 +- .../FFXIV/Common/Component/Excel/ExcelPage.cs | 21 +++++++++ .../Common/Component/Excel/ExcelSheet.cs | 43 +++++++++++++++---- .../Component/Excel/ExcelSheetNameVar.cs | 11 +++++ .../Component/Excel/ExcelSheetWaiter.cs | 22 ++++++++++ .../Component/Excel/ExcelRowDescriptor.cs | 12 +++--- ida/data.yml | 19 +++++++- 7 files changed, 115 insertions(+), 17 deletions(-) create mode 100644 FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelPage.cs create mode 100644 FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheetNameVar.cs diff --git a/FFXIVClientStructs/FFXIV/Client/UI/Agent/AgentFishGuide.cs b/FFXIVClientStructs/FFXIV/Client/UI/Agent/AgentFishGuide.cs index 1e85affab1..038257bef7 100644 --- a/FFXIVClientStructs/FFXIV/Client/UI/Agent/AgentFishGuide.cs +++ b/FFXIVClientStructs/FFXIV/Client/UI/Agent/AgentFishGuide.cs @@ -123,7 +123,7 @@ public struct PlaceNameEntry { public partial struct FishGuideSheetWaiter { [FieldOffset(0x70)] public void* CallbackFn; // 5 args!? [FieldOffset(0x80)] public void* CallbackThisArg; - [FieldOffset(0x88)] public uint SheetIndex; + [FieldOffset(0x88)] public uint TargetSheetIndex; } // Client::UI::Agent::AgentFishGuide::FishGuideData @@ -146,7 +146,7 @@ public partial struct FishGuideData { public partial struct SpearfishGuideSheetWaiter { [FieldOffset(0x70)] public void* CallbackFn; // 5 args!? [FieldOffset(0x80)] public void* CallbackThisArg; - [FieldOffset(0x88)] public uint SheetIndex; + [FieldOffset(0x88)] public uint TargetSheetIndex; } // Client::UI::Agent::AgentFishGuide::SpearfishGuideData diff --git a/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelPage.cs b/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelPage.cs new file mode 100644 index 0000000000..40667af4a3 --- /dev/null +++ b/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelPage.cs @@ -0,0 +1,21 @@ +using FFXIVClientStructs.FFXIV.Component.Excel; + +namespace FFXIVClientStructs.FFXIV.Common.Component.Excel; + +// Common::Component::Excel::ExcelPage +[GenerateInterop] +[StructLayout(LayoutKind.Explicit, Size = 0x2C0)] +public unsafe partial struct ExcelPage { + [FieldOffset(0x00), CExporterBaseType] public LinkList Base; + [FieldOffset(0x20)] ExcelSheet* Sheet; + [FieldOffset(0x28)] ExcelSheetNameVar SheetNameVar; + + [FieldOffset(0x38)] public uint StartRowId; + [FieldOffset(0x3C)] public uint EndRowId; + + [MemberFunction("E8 ?? ?? ?? ?? 84 C0 75 ?? 48 85 DB 74 ?? 48 8B 5B ?? 48 3B DD")] + public partial bool ContainsRowId(ExcelRowDescriptor* descriptor); + + [Obsolete("Incorrect parameter type. Use ContainsRowId(ExcelRowDescriptor*)")] + public bool ContainsRowId(uint rowId) => false; +} diff --git a/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheet.cs b/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheet.cs index 8062938fad..23c4dee1ce 100644 --- a/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheet.cs +++ b/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheet.cs @@ -9,14 +9,14 @@ namespace FFXIVClientStructs.FFXIV.Common.Component.Excel; public unsafe partial struct ExcelSheet { [FieldOffset(0x00), CExporterBaseType] public LinkList Base; [FieldOffset(0x18)] public ExcelLanguage Language; - [FieldOffset(0x20)] public CStringPointer SheetName; // 32 Bytes + [FieldOffset(0x20)] public ExcelSheetNameVar SheetNameVar; + [FieldOffset(0x20), Obsolete("Use SheetNameVar.SheetName")] public CStringPointer SheetName; // 32 Bytes [FieldOffset(0x30)] public uint RowCount; [FieldOffset(0x38)] public ColumnInfo* ColumnDefinitions; [FieldOffset(0x40)] public uint DataOffset; [FieldOffset(0x44)] public uint RowLength; // in bytes - //[FieldOffset(0x70)] public LinkList ExcelPages; - // 0x88 LinkList for subrows? + [FieldOffset(0x70)] public LinkedListContainer ExcelPages; [FieldOffset(0xA8)] private uint CachedAllocatedRowCount; // not sure how it's cached yet. use the function which falls back to calling the function on RowResolver @@ -24,12 +24,13 @@ public unsafe partial struct ExcelSheet { [FieldOffset(0xD0)] public ExcelVariant Variant; [FieldOffset(0xD4)] public ushort ColumnCount; - [FieldOffset(0xD6)] public short StartColumn; // initialized with -1. then, if -1, set to the current index in a loop over ColumnDefinitions. so technically always 0 unless there are no columns + [FieldOffset(0xD6)] public short StartColumn; // initialized with -1. then, if -1, set to the current index in a loop over ColumnDefinitions. so technically always 0 unless there are no columns [FieldOffset(0xD8)] public ushort Version; [FieldOffset(0xDA)] public ushort SheetIndex; - - [FieldOffset(0xF8)] public uint WaiterCount; + [FieldOffset(0xDD)] public bool ExcelPagesLinked; + [FieldOffset(0xE0)] public WaiterContainer Waiters; + [FieldOffset(0xF8), Obsolete("Use WaiterContainer.Count")] public uint WaiterCount; [MemberFunction("E8 ?? ?? ?? ?? 8B D0 BD")] public partial uint GetAllocatedRowCount(); @@ -46,13 +47,39 @@ public unsafe partial struct ExcelSheet { [MemberFunction("E8 ?? ?? ?? ?? 8B D3 48 8B F8")] public partial IExcelRowWrapper* GetRowById(uint rowId, uint* outErrorCode = null); + [MemberFunction("E9 ?? ?? ?? ?? 13 6D")] + public partial bool AddWaiter(ExcelSheetWaiter* waiter); + + [MemberFunction("E8 ?? ?? ?? ?? 8B 53 ?? 44 8B C5")] + public partial void RemoveWaiter(ExcelSheetWaiter* waiter); + public Span ColumnDefinitionSpan => new(ColumnDefinitions, ColumnCount); [StructLayout(LayoutKind.Explicit, Size = 0x06)] public struct ColumnInfo { - [FieldOffset(0x00), CExporterIgnore] private ExcelColumnType ColumnType; - [FieldOffset(0x00)] public ushort Type; // TODO: use ExcelColumnType + [FieldOffset(0x00)] public ExcelColumnType ColumnType; + [FieldOffset(0x00), Obsolete("Use ColumnType")] public ushort Type; [FieldOffset(0x02)] public ushort Index; [FieldOffset(0x04)] public ushort Offset; } + + [StructLayout(LayoutKind.Explicit, Size = 0x38)] + public struct LinkedListContainer { + [FieldOffset(0x00)] public LinkList List1; + [FieldOffset(0x18)] public LinkList List2; + [FieldOffset(0x30)] public int NumCreatedExcelPages; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x20)] + public struct WaiterContainer { + [FieldOffset(0x00), CExporterBaseType] public LinkList Base; + [FieldOffset(0x18)] public int Count; + + [StructLayout(LayoutKind.Explicit, Size = 0x18)] + public struct WaiterEntry { + [FieldOffset(0x00)] public ExcelSheetWaiter* Waiter; + [FieldOffset(0x08)] public int Index; // ? + [FieldOffset(0x10)] public WaiterEntry* Next; + } + } } diff --git a/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheetNameVar.cs b/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheetNameVar.cs new file mode 100644 index 0000000000..ee434b3470 --- /dev/null +++ b/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheetNameVar.cs @@ -0,0 +1,11 @@ +namespace FFXIVClientStructs.FFXIV.Common.Component.Excel; + +// Common::Component::Excel::ExcelSheetNameVar +[GenerateInterop] +[StructLayout(LayoutKind.Explicit, Size = 0x10)] +public partial struct ExcelSheetNameVar { + [FieldOffset(0x00)] public CStringPointer SheetNamePtr; // 32 Bytes + [FieldOffset(0x00), CExporterIgnore, FixedSizeArray] internal FixedSizeArray8 _sheetName; + [FieldOffset(0x08)] public ushort SheetNameHash; + [FieldOffset(0x0A)] public byte Type; // 0 = Pointer, 1 = Inline? not entirely sure +} diff --git a/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheetWaiter.cs b/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheetWaiter.cs index f8462400b9..7c00fbb200 100644 --- a/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheetWaiter.cs +++ b/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheetWaiter.cs @@ -1,12 +1,34 @@ +using FFXIVClientStructs.FFXIV.Component.Excel; + namespace FFXIVClientStructs.FFXIV.Common.Component.Excel; // Common::Component::Excel::ExcelSheetWaiter [GenerateInterop(isInherited: true)] [StructLayout(LayoutKind.Explicit, Size = 0x30)] public unsafe partial struct ExcelSheetWaiter { + [FieldOffset(0x08)] public uint RowDescriptorCount; + [FieldOffset(0x0C)] private uint UnkC; // row counter? + + [FieldOffset(0x18)] public long SheetIndex; + [FieldOffset(0x20)] public ExcelRowDescriptor* RowDescriptors; + [FieldOffset(0x28)] public ExcelRow** Rows; + [VirtualFunction(0)] public partial ExcelSheetWaiter* Dtor(byte freeFlags); + [MemberFunction("E8 ?? ?? ?? ?? C0 E3")] + public partial void Reset(); + + [MemberFunction("E8 ?? ?? ?? ?? 48 39 6B")] + public partial void Prepare(uint a2, uint rowDescriptorCount); + + [MemberFunction("E8 ?? ?? ?? ?? 48 8D 74 24 ?? BF")] + public partial void SetSheetIndex(long sheetIndex); + + [MemberFunction("E8 ?? ?? ?? ?? FF C7 4D 8D B6")] + public partial void SetRowDescriptor(uint index, ExcelRowDescriptor* descriptor); + + /// Sets a with the given at given . [MemberFunction("E8 ?? ?? ?? ?? 49 8B 94 EE")] public partial void EnqueueRowLookup(uint index, uint rowId); } diff --git a/FFXIVClientStructs/FFXIV/Component/Excel/ExcelRowDescriptor.cs b/FFXIVClientStructs/FFXIV/Component/Excel/ExcelRowDescriptor.cs index 787fe59784..b19931c249 100644 --- a/FFXIVClientStructs/FFXIV/Component/Excel/ExcelRowDescriptor.cs +++ b/FFXIVClientStructs/FFXIV/Component/Excel/ExcelRowDescriptor.cs @@ -2,12 +2,14 @@ namespace FFXIVClientStructs.FFXIV.Component.Excel; // Component::Excel::ExcelRowDescriptor [GenerateInterop] -[StructLayout(LayoutKind.Explicit, Size = 0x10)] +[StructLayout(LayoutKind.Explicit, Size = 0x18)] public unsafe partial struct ExcelRowDescriptor { - [FieldOffset(0x0)] public uint RowId; - [FieldOffset(0x4)] public ushort LowerRowIdPart; // found to be assigned with = (ushort)RowId in a few calls - [FieldOffset(0x6)] public ushort SubRowCount; - [FieldOffset(0x8), FixedSizeArray] internal FixedSizeArray4 _subRowIds; + [FieldOffset(0x00)] public uint RowId; + [FieldOffset(0x04)] public ushort LowerRowIdPart; // found to be assigned with = (ushort)RowId in a few calls + [FieldOffset(0x06)] public ushort SubRowCount; // TODO: change to short (seen as -1 and -2) + [FieldOffset(0x08), FixedSizeArray] internal FixedSizeArray4 _subRowIds; // TODO: change ushort to short + [FieldOffset(0x10)] public IExcelRowWrapper* RowWrapper; + // [MemberFunction("E8 ?? ?? ?? ?? EB ?? 48 8B DF 48 8D 4E")] // public partial void Assign0xFFFF(); diff --git a/ida/data.yml b/ida/data.yml index 610016951f..55a37f898d 100644 --- a/ida/data.yml +++ b/ida/data.yml @@ -4424,10 +4424,14 @@ classes: vtbls: - ea: 0x14257F588 funcs: - 0x14208C870: ctor 0x14208C780: ctor_From + 0x14208C870: ctor 0x14208C8A0: Finalizer + 0x14208CC30: Reset + 0x14208CCF0: Prepare + 0x14208CDE0: SetRowDescriptor 0x14208CEE0: EnqueueRowLookup + 0x14208CF30: SetSheetIndex vfuncs: 0: dtor 1: Finish @@ -5017,11 +5021,19 @@ classes: base: Common::Component::Environment::ResourceInterface::ResourceHandleInterface - ea: 0x142178338 base: Common::Component::Excel::LinkList + Common::Component::Excel::LinkList: + vtbls: + - ea: 0x14257F3E8 + vfuncs: + 0: dtor Common::Component::Excel::LinkList: vtbls: - ea: 0x14257F3F0 vfuncs: 0: dtor + Common::Component::Excel::ExcelSheet::WaiterContainer: + vtbls: + - ea: 0x14257F450 Common::Component::Excel::ExcelSheet: vtbls: - ea: 0x14257F458 @@ -5039,13 +5051,16 @@ classes: 0x142088BB0: GetColumnStringByDescriptor 0x142088DC0: GetRowByIndex 0x142088E30: GetRowByDescriptor - 0x142088F60: GetSheetName + 0x142088F60: GetSheetInfo 0x142088F70: GetColumnOffset 0x142089020: GetColumnIndex2 0x142089030: GetColumnType 0x142089050: GetColumnTypeLength 0x142089060: GetWaiterCount 0x1420893D0: RemoveWaiter + Common::Component::Excel::ExcelSheetNameVar: + funcs: + 0x142084E90: SetSheetName Component::Excel::ExcelPage: funcs: 0x14208D4D0: ctor # (ExcelPage* this, ExcelDataPagination* exhPageInfo, ExcelSheetContainer* pOwner) From d865e42ea1d27aa840fd837cf300d0fd35d021cc Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 27 Sep 2026 22:48:43 +0200 Subject: [PATCH 2/5] Revert ExcelRowDescriptor size change --- .../FFXIV/Common/Component/Excel/ExcelSheetWaiter.cs | 8 +++++++- .../FFXIV/Component/Excel/ExcelRowDescriptor.cs | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheetWaiter.cs b/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheetWaiter.cs index 7c00fbb200..6ac73f763b 100644 --- a/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheetWaiter.cs +++ b/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelSheetWaiter.cs @@ -10,7 +10,7 @@ public unsafe partial struct ExcelSheetWaiter { [FieldOffset(0x0C)] private uint UnkC; // row counter? [FieldOffset(0x18)] public long SheetIndex; - [FieldOffset(0x20)] public ExcelRowDescriptor* RowDescriptors; + [FieldOffset(0x20)] public DescriptorRowWrapperEntry* RowDescriptors; [FieldOffset(0x28)] public ExcelRow** Rows; [VirtualFunction(0)] @@ -31,4 +31,10 @@ public unsafe partial struct ExcelSheetWaiter { /// Sets a with the given at given . [MemberFunction("E8 ?? ?? ?? ?? 49 8B 94 EE")] public partial void EnqueueRowLookup(uint index, uint rowId); + + [StructLayout(LayoutKind.Explicit, Size = 0x18)] + public struct DescriptorRowWrapperEntry { + [FieldOffset(0x00)] public ExcelRowDescriptor Descriptor; + [FieldOffset(0x10)] public IExcelRowWrapper* RowWrapper; + } } diff --git a/FFXIVClientStructs/FFXIV/Component/Excel/ExcelRowDescriptor.cs b/FFXIVClientStructs/FFXIV/Component/Excel/ExcelRowDescriptor.cs index b19931c249..3aeddadb2c 100644 --- a/FFXIVClientStructs/FFXIV/Component/Excel/ExcelRowDescriptor.cs +++ b/FFXIVClientStructs/FFXIV/Component/Excel/ExcelRowDescriptor.cs @@ -2,13 +2,12 @@ namespace FFXIVClientStructs.FFXIV.Component.Excel; // Component::Excel::ExcelRowDescriptor [GenerateInterop] -[StructLayout(LayoutKind.Explicit, Size = 0x18)] +[StructLayout(LayoutKind.Explicit, Size = 0x10)] public unsafe partial struct ExcelRowDescriptor { [FieldOffset(0x00)] public uint RowId; [FieldOffset(0x04)] public ushort LowerRowIdPart; // found to be assigned with = (ushort)RowId in a few calls [FieldOffset(0x06)] public ushort SubRowCount; // TODO: change to short (seen as -1 and -2) [FieldOffset(0x08), FixedSizeArray] internal FixedSizeArray4 _subRowIds; // TODO: change ushort to short - [FieldOffset(0x10)] public IExcelRowWrapper* RowWrapper; // [MemberFunction("E8 ?? ?? ?? ?? EB ?? 48 8B DF 48 8D 4E")] // public partial void Assign0xFFFF(); From abf3f87ef34d42785e4529df9bf209deb077fdcd Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 27 Sep 2026 22:54:15 +0200 Subject: [PATCH 3/5] Move ExcelPage --- .../FFXIV/{Common => }/Component/Excel/ExcelPage.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) rename FFXIVClientStructs/FFXIV/{Common => }/Component/Excel/ExcelPage.cs (65%) diff --git a/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelPage.cs b/FFXIVClientStructs/FFXIV/Component/Excel/ExcelPage.cs similarity index 65% rename from FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelPage.cs rename to FFXIVClientStructs/FFXIV/Component/Excel/ExcelPage.cs index 40667af4a3..63a548a878 100644 --- a/FFXIVClientStructs/FFXIV/Common/Component/Excel/ExcelPage.cs +++ b/FFXIVClientStructs/FFXIV/Component/Excel/ExcelPage.cs @@ -1,8 +1,8 @@ -using FFXIVClientStructs.FFXIV.Component.Excel; +using FFXIVClientStructs.FFXIV.Common.Component.Excel; -namespace FFXIVClientStructs.FFXIV.Common.Component.Excel; +namespace FFXIVClientStructs.FFXIV.Component.Excel; -// Common::Component::Excel::ExcelPage +// Component::Excel::ExcelPage [GenerateInterop] [StructLayout(LayoutKind.Explicit, Size = 0x2C0)] public unsafe partial struct ExcelPage { @@ -15,7 +15,4 @@ public unsafe partial struct ExcelPage { [MemberFunction("E8 ?? ?? ?? ?? 84 C0 75 ?? 48 85 DB 74 ?? 48 8B 5B ?? 48 3B DD")] public partial bool ContainsRowId(ExcelRowDescriptor* descriptor); - - [Obsolete("Incorrect parameter type. Use ContainsRowId(ExcelRowDescriptor*)")] - public bool ContainsRowId(uint rowId) => false; } From 8a9b7ad5cdb6e1083b7e574a26874d5e760cf167 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 27 Sep 2026 22:58:46 +0200 Subject: [PATCH 4/5] Update data.yml --- ida/data.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ida/data.yml b/ida/data.yml index 55a37f898d..1e568d98ff 100644 --- a/ida/data.yml +++ b/ida/data.yml @@ -5062,6 +5062,11 @@ classes: funcs: 0x142084E90: SetSheetName Component::Excel::ExcelPage: + vtbls: + - ea: 0x14257F5C8 + base: Common::Component::Excel::LinkList + - ea: 0x14257F5D0 + base: Common::Component::Excel::ExcelResourceListener funcs: 0x14208D4D0: ctor # (ExcelPage* this, ExcelDataPagination* exhPageInfo, ExcelSheetContainer* pOwner) 0x14208E350: ContainsRowId # (ExcelPage* this, int* pnRowId) -> bool From 688d3fd48d71777f3557dd69429cbfb73d2e9ba7 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 27 Sep 2026 23:02:54 +0200 Subject: [PATCH 5/5] Update ExcelPage --- FFXIVClientStructs/FFXIV/Component/Excel/ExcelPage.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/FFXIVClientStructs/FFXIV/Component/Excel/ExcelPage.cs b/FFXIVClientStructs/FFXIV/Component/Excel/ExcelPage.cs index 63a548a878..957f74ab58 100644 --- a/FFXIVClientStructs/FFXIV/Component/Excel/ExcelPage.cs +++ b/FFXIVClientStructs/FFXIV/Component/Excel/ExcelPage.cs @@ -12,6 +12,7 @@ public unsafe partial struct ExcelPage { [FieldOffset(0x38)] public uint StartRowId; [FieldOffset(0x3C)] public uint EndRowId; + [FieldOffset(0x40)] public uint RowCount; [MemberFunction("E8 ?? ?? ?? ?? 84 C0 75 ?? 48 85 DB 74 ?? 48 8B 5B ?? 48 3B DD")] public partial bool ContainsRowId(ExcelRowDescriptor* descriptor);