Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions FFXIVClientStructs/FFXIV/Client/Game/Network/TofuPackets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using FFXIVClientStructs.FFXIV.Client.UI.Misc;

namespace FFXIVClientStructs.FFXIV.Client.Game.Network;

[StructLayout(LayoutKind.Explicit, Size = 0x49A)]
public partial struct TofuStartSharingPacket {
[FieldOffset(0x0)] public ulong SenderContentId;
[FieldOffset(0x8)] public uint Checksum; // uses BoardContent to create checksum
[FieldOffset(0x10)] public TofuPackedBoard BoardContent;
[FieldOffset(0x420), FixedSizeArray(isString: true)] internal FixedSizeArray60<byte> _folderName;
[FieldOffset(0x45C), FixedSizeArray(isString: true)] internal FixedSizeArray60<byte> _boardName;
[FieldOffset(0x498)] public byte BoardIndexInSharedFolder; // 1-based
[FieldOffset(0x499)] public byte TotalBoardsInSharedFolder;
}

[StructLayout(LayoutKind.Explicit, Size = 0x10)]
public partial struct TofuStopSharingPacket;

[StructLayout(LayoutKind.Explicit, Size = 0x40)]
public partial struct TofuRealTimeUpdatePacket {
[FieldOffset(0x0)] public uint Checksum; // based off: field = 0x8, size = 0x38
}

[StructLayout(LayoutKind.Explicit, Size = 0x09)]
public partial struct TofuConfirmationPacket {
[FieldOffset(0x0)] public ulong ReceiverContentId;
[FieldOffset(0x8)] public byte BoardIndexInFolder;
}
96 changes: 96 additions & 0 deletions FFXIVClientStructs/FFXIV/Client/UI/Agent/AgentTofuList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using FFXIVClientStructs.FFXIV.Component.GUI;

namespace FFXIVClientStructs.FFXIV.Client.UI.Agent;

// Client::UI::Agent::AgentTofuList
// Client::UI::Agent::AgentInterface
// Component::GUI::AtkModuleInterface::AtkEventInterface
[Agent(AgentId.TofuList)]
[GenerateInterop]
[Inherits<AgentInterface>]
[StructLayout(LayoutKind.Explicit, Size = 0x38)]
public unsafe partial struct AgentTofuList {
[FieldOffset(0x28)] public TofuListData* Data;

/// <remarks>
/// Ensure TofuListData pointer is non-null before calling otherwise this will crash the game. <br/>
/// The pointer is populated when the Agent is open. Use <see cref="TofuModule"/> to interact with Tofu <br/>
/// while Agent is closed.
/// </remarks>
[MemberFunction("E8 ?? ?? ?? ?? 88 46 ?? EB ?? C7 02")]
public partial void ContextMenuOptions(AtkValue* values, uint valueCount, uint code);
}

[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x3E98)]
public unsafe partial struct TofuListData {
[FieldOffset(0x0), FixedSizeArray] internal FixedSizeArray70<Board> _boards; // Starts with Saved then Shared
[FieldOffset(0x2530), FixedSizeArray] internal FixedSizeArray70<BoardShort> _boardShorts;
[FieldOffset(0x2BC0)] public uint TotalSavedList;
[FieldOffset(0x2BC4)] public uint TotalSavedBoards;
[FieldOffset(0x2BC8)] public uint TotalSaved;
[FieldOffset(0x2BCC)] public int SavedSelectedIndex;
[FieldOffset(0x2BD0)] public uint TotalSharedList;
[FieldOffset(0x2BD4)] public uint TotalSharedBoards;
[FieldOffset(0x2BD8)] public uint TotalShared;
[FieldOffset(0x2BDC)] public int SharedSelectedIndex;
[FieldOffset(0x2BE0)] public TofuMoveToFolder MoveToFolderDialog;
[FieldOffset(0x3D18)] public TofuSettings SettingsDialog;
[FieldOffset(0x3DF8)] public Utf8String SelectYesNoText;
[FieldOffset(0x3E60)] public AgentTofuList* Agent;
[FieldOffset(0x3E68)] public UIModule* UIModule;
[FieldOffset(0x3E70)] private nint Unk3E70; // struct of size 0x90

[FieldOffset(0x3E7C)] private byte Unk3E7C; // 3 when creating item: `40 53 48 83 EC ?? 48 8B 01 48 8B D9 FF 50 ?? 84 C0 74 ?? 48 8B 5B ?? 8B 83`, 1 on Agent.Show, 0 by default

[FieldOffset(0x3E84)] public uint IsSharedListOpen; // 4 byte bool, possible enum
[FieldOffset(0x3E88)] private int Unk3E88; // Defaults to -1
[FieldOffset(0x3E8C)] public uint ChildAddonId;

[MemberFunction("E8 ?? ?? ?? ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8B 47 ?? 89 A8")]
public partial void RenameItem(int index, Utf8String* newName);

[MemberFunction("E8 ?? ?? ?? ?? EB ?? 83 BB ?? ?? ?? ?? ?? 48 8D 8B")]
public partial void ReviewSelectedBoard();

[MemberFunction("E8 ?? ?? ?? ?? FF CB FF CF")]
public partial void SwapBoards(uint boardIndex, uint targetIndex);

[MemberFunction("E8 ?? ?? ?? ?? 41 8B 36")]
public partial void SwapFolders(uint folderIndex, uint targetIndex);
}

[StructLayout(LayoutKind.Explicit, Size = 0x88)]
public partial struct Board {
[FieldOffset(0x0)] public StdVector<TofuFullObject> Objects;
[FieldOffset(0x18)] public Utf8String Name;
[FieldOffset(0x84)] public byte Background;
}

[StructLayout(LayoutKind.Explicit, Size = 0x18)]
public unsafe partial struct BoardShort {
[FieldOffset(0x0)] public Board* Board;
[FieldOffset(0x8)] public CStringPointer Name;
}

[GenerateInterop]
// Includes 3 vfuncs, unk8, uimodule
[StructLayout(LayoutKind.Explicit, Size = 0x1138)]
public unsafe partial struct TofuMoveToFolder {
[FieldOffset(0x18)] private byte Unk18; // 4 when dialog is open
[FieldOffset(0x60), FixedSizeArray] internal FixedSizeArray75<TofuShortObject> _selectedBoardObjects; // Why is this 75 elements long, board can only have 50 objects
[FieldOffset(0x10CC), FixedSizeArray(isString: true)] internal FixedSizeArray64<byte> _boardName;
[FieldOffset(0x1118)] private nint Unk1118; // Pointer to array of available folders, includes the names
[FieldOffset(0x1120)] public byte NumberOfFolders;
[FieldOffset(0x1128)] public AgentTofuList* Agent;
}

//[GenerateInterop]
// Includes 3 vfuncs, unk8, uimodule, addonid
[StructLayout(LayoutKind.Explicit, Size = 0xE0)]
public unsafe partial struct TofuSettings {
[FieldOffset(0x48)] public bool IsSeparate; // either unified or separate
[FieldOffset(0xC4)] public byte IconHighlight;
[FieldOffset(0xD0)] public AgentTofuList* Agent;
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public unsafe partial struct InfoProxyCommonList {
public partial struct CharacterData {
[FieldOffset(0x00)] public ulong ContentId;
[FieldOffset(0x08)] public OnlineStatus State;
[FieldOffset(0x18)] public ulong AccountId;
//12 bytes
/// <summary>
/// Extra flags for status:
Expand Down
27 changes: 27 additions & 0 deletions FFXIVClientStructs/FFXIV/Client/UI/Misc/TofuHelper.Data.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace FFXIVClientStructs.FFXIV.Client.UI.Misc;

/// <summary>
/// The packed board is initially constructed in <see cref="TofuBoardOverview.ConstructPackedBoard"/> and then compressed via zlib. <br/>
/// Useful Information: https://github.com/xivdev/file-formats/blob/main/imhex/stgy.hexpat
/// </summary>
[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x410)]
public partial struct TofuPackedBoard {
[FieldOffset(0x0)] private ushort Unk0; // increments by 0x11 with number of objects starting at 0x03
[FieldOffset(0x2)] public SharingType SharingType;
[FieldOffset(0x6)] private byte Unk6; // lower nibble changes with background
}

[Flags]
public enum SharingType : byte {
Comment thread
bilk marked this conversation as resolved.
Normal = 0,
RealTime = 1,
Folder = 4,
}
Comment thread
bilk marked this conversation as resolved.

[Flags]
public enum SendState : uint {
None = 0,
Intermediate = 1,
Last = 2,
}
111 changes: 111 additions & 0 deletions FFXIVClientStructs/FFXIV/Client/UI/Misc/TofuHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
using FFXIVClientStructs.FFXIV.Client.Game.Network;
using static FFXIVClientStructs.FFXIV.Client.UI.Misc.TofuHelper;

namespace FFXIVClientStructs.FFXIV.Client.UI.Misc;

[GenerateInterop]
[Inherits<HelperInterface>]
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
public unsafe partial struct TofuHelper {
[FieldOffset(0x10)] public TofuHelperData* Data;

[MemberFunction("E9 ?? ?? ?? ?? 48 8B 01 FF 90 ?? ?? ?? ?? 48 8B D3 48 8B 48 ?? 48 83 C4 ?? 5B E9 ?? ?? ?? ?? 48 8B 01 FF 90 ?? ?? ?? ?? 48 8B D3 48 8B 48 ?? 48 83 C4 ?? 5B E9 ?? ?? ?? ?? 48 83 C4")]
public partial void HandleStartSharingPacket(ServerIpcSegment<TofuStartSharingPacket>* packet);

[MemberFunction("E8 ?? ?? ?? ?? 66 0F 6F 05 ?? ?? ?? ?? 48 8D 8B")]
public partial void HandleStopSharingPacket(ServerIpcSegment<TofuStopSharingPacket>* packet);

[MemberFunction("E8 ?? ?? ?? ?? EB ?? 48 8B 07 48 8B CF FF 90 ?? ?? ?? ?? 48 8B D6 48 8B 48 ?? E8 ?? ?? ?? ?? EB ?? 48 8B 07 48 8B CF FF 90 ?? ?? ?? ?? 48 8B D6 48 8B 48 ?? E8 ?? ?? ?? ?? EB")]
public partial void HandleRealTimeUpdatePacket(ServerIpcSegment<TofuRealTimeUpdatePacket>* packet);

[MemberFunction("48 83 EC ?? 4C 8B 51 ?? 4D 85 D2 0F 84")]
public partial void HandleTofuConfirmationPacket(ServerIpcSegment<TofuConfirmationPacket>* packet);

[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x3AA0)]
public unsafe partial struct TofuHelperData {
[FieldOffset(0x0)] public TofuBoardOverview ReceivedBoard;
[FieldOffset(0x88)] public TofuBoardOverview ReceivedRealTimeBoard;
[FieldOffset(0x868)] public TofuShareData TofuShareData;

[MemberFunction("48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 49 8D 78")]
public partial bool HandleSharePacket(Utf8String* a2, ServerIpcSegment<TofuStartSharingPacket>* packet);

/// <summary>
/// Function called to verify to the server the shared board was received. Prevents the server from resending the board.
/// </summary>
/// <param name="targetContentId">The contentId of the player who send the board.</param>
/// <param name="boardIndexInSharedFolder">1-based index of board in shared folder</param>
[MemberFunction("E8 ?? ?? ?? ?? 44 8B CE 44 89 6C 24")]
public partial void SendVerificationPacket(ulong targetContentId, uint boardIndexInSharedFolder);

/// <summary>
/// Saves the board to the shared list and plays a sound.
/// </summary>
/// <param name="packetData">Pointer to the data of the packet.</param>
/// <param name="boardInfo">Pointer to the packed board from the sender.</param>
/// <param name="boardIndexInSharedFolder">The index of the board within the folder thats shared. If no folder was shared, this is <c>0</c>.</param>
/// <param name="totalBoardsInSharedFolder">The total number of boards within the shared fodler. If no folder was shared, this is <c>1</c>.</param>
[MemberFunction("E8 ?? ?? ?? ?? 40 80 F5")]
public partial void SaveBoardAndPlaySound(TofuStartSharingPacket* packetData, TofuPackedBoard* boardInfo, uint boardIndexInSharedFolder, uint totalBoardsInSharedFolder);

/// <summary>
/// Shows a notification for the shared board.
/// </summary>
/// <param name="isNotRealTimeSharing"><see langword="false" /> if the board shared is Real-Time sharing </param>
/// <param name="openNotif"> <see langword="true"/> to open the notification, <see langword="false"/> to close the notification.</param>
[MemberFunction("48 89 6C 24 ?? 56 41 56 41 57 48 83 EC ?? 4C 8B F9 0F B6 EA")]
public partial void ShowSharedNotification(bool isNotRealTimeSharing, bool openNotif);
}
}

[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x88)]
public unsafe partial struct TofuBoardOverview {
/// <remarks> Objects only exist on received boards, not in boards to be sent </remarks>
[FieldOffset(0x0)] public StdVector<TofuFullObject> Objects;
[FieldOffset(0x18)] public Utf8String BoardName;
[FieldOffset(0x84)] public byte BoardBackground;

[MemberFunction("E8 ?? ?? ?? ?? 33 FF 85 C0 75 ?? 44 8B F7")]
public partial uint ConstructPackedBoard(nint buffer, uint size, RaptureAtkColorDataManager* colorDataManager);
}

[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x450)]
public partial struct TofuPackedBoardShare {
[FieldOffset(0x0)] public TofuPackedBoard PackedBoard;
[FieldOffset(0x410), FixedSizeArray(isString: true)] internal FixedSizeArray60<byte> _boardName;
[FieldOffset(0x44C)] private uint Unk44C; // Populated with data even if board is empty. Not timestamp and doesn't change with different boards
}

[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x3238)]
Comment thread
bilk marked this conversation as resolved.
public partial struct TofuShareData {
[FieldOffset(0x0), FixedSizeArray] internal FixedSizeArray10<uint> _boardIndex;
[FieldOffset(0x28)] public ulong SenderContentId;
[FieldOffset(0x30)] public uint FolderIndex; // this is the folder index for shared boards internally, not the ui display
[FieldOffset(0x34)] public byte TotalBoardsInSharedFolder;
[FieldOffset(0x38)] public bool IsLastBoardInSharedFolder;

[FieldOffset(0x40)] public TofuShareSession ShareSession;
}

[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x31F8)]
Comment thread
bilk marked this conversation as resolved.
public unsafe partial struct TofuShareSession {
[FieldOffset(0x0), FixedSizeArray] internal FixedSizeArray10<TofuBoardOverview> _boardOverviews;
[FieldOffset(0x550), FixedSizeArray] internal FixedSizeArray10<TofuPackedBoardShare> _boards;
[FieldOffset(0x3070), FixedSizeArray(isString: true)] internal FixedSizeArray60<byte> _folderName;
[FieldOffset(0x30AC)] public float SendCooldownSeconds; // client side rate limit for sending consecutive boards, set to 1 second
[FieldOffset(0x30B0)] public SendState SendState;
[FieldOffset(0x30B4)] public uint CurrentBoardIndex; // 1-based
[FieldOffset(0x30B8)] public uint TotalBoardsInSharedFolder;

[FieldOffset(0x30C8)] public ulong CurrentShareContentId;

[FieldOffset(0x31B8)] public bool IsNotSending;
[FieldOffset(0x31C0)] public TofuHelperData* Data;
[FieldOffset(0x31D0)] public TofuHelper* TofuHelper;
[FieldOffset(0x31D8)] public UIModule* UIModule;
}
Comment thread
bilk marked this conversation as resolved.
Loading
Loading