using System; using System.Runtime.InteropServices; using CSNetwork.GPDataType; namespace BrewMonster.UI { // Define and Macro from EC_GameUIMan.h // 定义与宏 public static class EC_GameUIMan_Constants { // public const int LAYOUTDATA_VERSION = 15; // #define CECGAMEUIMAN_MAX_MSGS 200 // #define CECGAMEUIMAN_MAX_BROKEN 8 // #define CECGAMEUIMAN_MAX_TASKHINT 6 public const int CECGAMEUIMAN_MAX_MARKS = 5; public const int CECGAMEUIMAN_MARK_NAME_LEN = 8; public const int CECGAMEUIMAN_MAX_GROUPS = 10; public const int CECGAMEUIMAN_MAX_CHATS = 10; public const int CECGAMEUIMAN_MAX_QSHOPITEMS = 8; public const int CECGAMEUIMAN_MAX_FASHIONSHOPITEMS = 6; /// /// C++ sizeof(USER_LAYOUT) with default packing (MSVC pack 8). /// 与 C++ sizeof(USER_LAYOUT) 一致(MSVC 默认 pack 8)。 /// public const int USER_LAYOUT_SIZE = 344; } // SAVE_MARK: saved mark (NPC, position, name) // 保存的标记(NPC、位置、名称) [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct SAVE_MARK { public int nNPC; public A3DVECTOR3 vecPos; [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_GameUIMan_Constants.CECGAMEUIMAN_MARK_NAME_LEN + 1)] public ushort[] szName; // ACHAR in C++ (wchar_t) → ushort[] } // USER_LAYOUT_Flat: flat struct for binary serialization (total size 344 to match C++ sizeof). // 扁平结构体,用于二进制序列化(总大小 344,与 C++ sizeof 一致)。 // Field order matches C++ EC_GameUIMan.h. Pack=1 + explicit padding for exact 344-byte output. [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct USER_LAYOUT_Flat { public sbyte nVersion; public sbyte nMapMode; [MarshalAs(UnmanagedType.U1)] public bool bQuickbar1Mode; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] public byte[] bChecked1; [MarshalAs(UnmanagedType.U1)] public bool bQuickbar2Mode; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] bChecked2; [MarshalAs(UnmanagedType.U1)] public bool bQuickbarPetMode; public int nChatWinSize; public int nCurChatColor; [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_GameUIMan_Constants.CECGAMEUIMAN_MAX_MARKS)] public SAVE_MARK[] a_Mark; [MarshalAs(UnmanagedType.U1)] public bool bOnlineNotify; [MarshalAs(UnmanagedType.U1)] public bool bAutoReply; [MarshalAs(UnmanagedType.U1)] public bool bSaveHistory; [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_GameUIMan_Constants.CECGAMEUIMAN_MAX_GROUPS)] public int[] idGroup; [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_GameUIMan_Constants.CECGAMEUIMAN_MAX_GROUPS)] public uint[] clrGroup; [MarshalAs(UnmanagedType.U1)] public bool bTraceAll; [MarshalAs(UnmanagedType.U1)] public bool bQuickbarShowAll1; [MarshalAs(UnmanagedType.U1)] public bool bQuickbarShowAll2; public int nQuickbarCurPanel1, nQuickbarCurPanel2, nQuickbarDisplayPanels1, nQuickbarDisplayPanels2; [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_GameUIMan_Constants.CECGAMEUIMAN_MAX_MARKS)] public short[] a_MarkMapID; public uint dwTraceMask; public byte ucCurSystemModuleSC; [MarshalAs(UnmanagedType.U1)] public bool bSystemModuleQuickBarMini; [MarshalAs(UnmanagedType.U1)] public bool bMenuMode; [MarshalAs(UnmanagedType.U1)] public bool bShowCompareDesc; [MarshalAs(UnmanagedType.U1)] public bool bShowLowHP; [MarshalAs(UnmanagedType.U1)] public bool bShowTargetOfTarget; // Padding to reach C++ sizeof(USER_LAYOUT)=344 (C# Pack=1 yields 313, +31 padding) [MarshalAs(UnmanagedType.ByValArray, SizeConst = 31)] public byte[] _padding; } // TASK_HINT: task hint data // 任务提示数据 // TODO: ACString strHint is not inline in C++ (pointer); when reading from C++ binary skip ptr after dwTime and fill strHint separately. [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct TASK_HINT { public uint dwTime; // DWORD → uint public string strHint; } // USER_LAYOUT_6: user layout version 6 // 用户布局版本 6 // Base for layout versions: common fields (v6 and v7 differ only by bChecked1 size). // 布局版本基类:公共字段(v6 与 v7 仅 bChecked1 长度不同) [StructLayout(LayoutKind.Sequential, Pack = 1)] public class USER_LAYOUT_Base { public sbyte nVersion; // __int8 → sbyte public sbyte nMapMode; [MarshalAs(UnmanagedType.U1)] public bool bQuickbar1Mode; [MarshalAs(UnmanagedType.U1)] public bool bQuickbar2Mode; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] bChecked2; [MarshalAs(UnmanagedType.U1)] public bool bQuickbarPetMode; public int nChatWinSize; public int nCurChatColor; [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_GameUIMan_Constants.CECGAMEUIMAN_MAX_MARKS)] public SAVE_MARK[] a_Mark; [MarshalAs(UnmanagedType.U1)] public bool bOnlineNotify; [MarshalAs(UnmanagedType.U1)] public bool bAutoReply; [MarshalAs(UnmanagedType.U1)] public bool bSaveHistory; [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_GameUIMan_Constants.CECGAMEUIMAN_MAX_GROUPS)] public int[] idGroup; [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_GameUIMan_Constants.CECGAMEUIMAN_MAX_GROUPS)] public uint[] clrGroup; // A3DCOLOR (DWORD) → uint } [StructLayout(LayoutKind.Sequential, Pack = 1)] public class USER_LAYOUT_6 : USER_LAYOUT_Base { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] bChecked1; } // USER_LAYOUT_7: user layout version 7 (bChecked1[5]); base for v8–current // 用户布局版本 7(bChecked1[5]);v8 至当前版本的基类 [StructLayout(LayoutKind.Sequential, Pack = 1)] public class USER_LAYOUT_7 : USER_LAYOUT_Base { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] public byte[] bChecked1; } // USER_LAYOUT_8: extends USER_LAYOUT_7 // 用户布局版本 8 [StructLayout(LayoutKind.Sequential, Pack = 1)] public class USER_LAYOUT_8 : USER_LAYOUT_7 { [MarshalAs(UnmanagedType.U1)] public bool bTraceAll; [MarshalAs(UnmanagedType.U1)] public bool bQuickbarShowAll1; [MarshalAs(UnmanagedType.U1)] public bool bQuickbarShowAll2; public int nQuickbarCurPanel1; public int nQuickbarCurPanel2; public int nQuickbarDisplayPanels1; public int nQuickbarDisplayPanels2; } // USER_LAYOUT_9: extends USER_LAYOUT_8 // 用户布局版本 9 [StructLayout(LayoutKind.Sequential, Pack = 1)] public class USER_LAYOUT_9 : USER_LAYOUT_8 { [MarshalAs(UnmanagedType.ByValArray, SizeConst = EC_GameUIMan_Constants.CECGAMEUIMAN_MAX_MARKS)] public short[] a_MarkMapID; // init to 1 per element in C++; caller must init in C# } // USER_LAYOUT_10: extends USER_LAYOUT_9 // 用户布局版本 10 [StructLayout(LayoutKind.Sequential, Pack = 1)] public class USER_LAYOUT_10 : USER_LAYOUT_9 { public uint dwTraceMask; // DWORD → uint; init 0 } // USER_LAYOUT_11: extends USER_LAYOUT_10 // 用户布局版本 11 [StructLayout(LayoutKind.Sequential, Pack = 1)] public class USER_LAYOUT_11 : USER_LAYOUT_10 { public byte ucCurSystemModuleSC; // unsigned char → byte; init 0 } // USER_LAYOUT_12: extends USER_LAYOUT_11 // 用户布局版本 12 [StructLayout(LayoutKind.Sequential, Pack = 1)] public class USER_LAYOUT_12 : USER_LAYOUT_11 { [MarshalAs(UnmanagedType.U1)] public bool bSystemModuleQuickBarMini; // init false } // USER_LAYOUT_13: extends USER_LAYOUT_12 // 用户布局版本 13 [StructLayout(LayoutKind.Sequential, Pack = 1)] public class USER_LAYOUT_13 : USER_LAYOUT_12 { [MarshalAs(UnmanagedType.U1)] public bool bMenuMode; // init false } // USER_LAYOUT_14: extends USER_LAYOUT_13 // 用户布局版本 14 [StructLayout(LayoutKind.Sequential, Pack = 1)] public class USER_LAYOUT_14 : USER_LAYOUT_13 { [MarshalAs(UnmanagedType.U1)] public bool bShowCompareDesc; // init true [MarshalAs(UnmanagedType.U1)] public bool bShowLowHP; // init true } // USER_LAYOUT: current user layout (extends USER_LAYOUT_14) // 当前用户布局 [StructLayout(LayoutKind.Sequential, Pack = 1)] public class USER_LAYOUT : USER_LAYOUT_14 { [MarshalAs(UnmanagedType.U1)] public bool bShowTargetOfTarget; // init true /// /// Serializes this USER_LAYOUT to a byte array (344 bytes to match C++ sizeof(USER_LAYOUT)). /// 序列化为字节数组(344 字节,与 C++ sizeof(USER_LAYOUT) 一致)。 /// public byte[] ToBytes() { USER_LAYOUT_Flat flat = ToFlat(); byte[] bytes = new byte[EC_GameUIMan_Constants.USER_LAYOUT_SIZE]; IntPtr ptr = Marshal.AllocHGlobal(EC_GameUIMan_Constants.USER_LAYOUT_SIZE); try { Marshal.StructureToPtr(flat, ptr, false); Marshal.Copy(ptr, bytes, 0, EC_GameUIMan_Constants.USER_LAYOUT_SIZE); return bytes; } finally { Marshal.FreeHGlobal(ptr); } } /// Converts this USER_LAYOUT to USER_LAYOUT_Flat for binary serialization. public USER_LAYOUT_Flat ToFlat() { var flat = new USER_LAYOUT_Flat { nVersion = nVersion, nMapMode = nMapMode, bQuickbar1Mode = bQuickbar1Mode, bChecked1 = bChecked1 ?? new byte[5], bQuickbar2Mode = bQuickbar2Mode, bChecked2 = bChecked2 ?? new byte[3], bQuickbarPetMode = bQuickbarPetMode, nChatWinSize = nChatWinSize, nCurChatColor = nCurChatColor, a_Mark = a_Mark ?? new SAVE_MARK[EC_GameUIMan_Constants.CECGAMEUIMAN_MAX_MARKS], bOnlineNotify = bOnlineNotify, bAutoReply = bAutoReply, bSaveHistory = bSaveHistory, idGroup = idGroup ?? new int[EC_GameUIMan_Constants.CECGAMEUIMAN_MAX_GROUPS], clrGroup = clrGroup ?? new uint[EC_GameUIMan_Constants.CECGAMEUIMAN_MAX_GROUPS], bTraceAll = bTraceAll, bQuickbarShowAll1 = bQuickbarShowAll1, bQuickbarShowAll2 = bQuickbarShowAll2, nQuickbarCurPanel1 = nQuickbarCurPanel1, nQuickbarCurPanel2 = nQuickbarCurPanel2, nQuickbarDisplayPanels1 = nQuickbarDisplayPanels1, nQuickbarDisplayPanels2 = nQuickbarDisplayPanels2, a_MarkMapID = a_MarkMapID ?? new short[EC_GameUIMan_Constants.CECGAMEUIMAN_MAX_MARKS], dwTraceMask = dwTraceMask, ucCurSystemModuleSC = ucCurSystemModuleSC, bSystemModuleQuickBarMini = bSystemModuleQuickBarMini, bMenuMode = bMenuMode, bShowCompareDesc = bShowCompareDesc, bShowLowHP = bShowLowHP, bShowTargetOfTarget = bShowTargetOfTarget, _padding = new byte[31] }; // Ensure a_Mark elements have szName initialized for (int i = 0; i < flat.a_Mark.Length; i++) { if (flat.a_Mark[i].szName == null) flat.a_Mark[i].szName = new ushort[EC_GameUIMan_Constants.CECGAMEUIMAN_MARK_NAME_LEN + 1]; } return flat; } } }