using PSO2SERVER.Protocol; using System; using System.IO; using System.Runtime.InteropServices; using System.Text; using static PSO2SERVER.Models.CharacterStruct; namespace PSO2SERVER.Models { [StructLayout(LayoutKind.Sequential)] public unsafe struct ShortItemId { public byte ItemType; public byte Id; public ushort Subid; } [StructLayout(LayoutKind.Sequential)] public unsafe struct ItemId { public ushort ItemType; public ushort Id; public ushort Unk3; public ushort Subid; } [StructLayout(LayoutKind.Explicit)] public struct Items { [FieldOffset(0)] public PSO2ItemWeapon Weapon; [FieldOffset(0)] public PSO2ItemClothing Clothing; [FieldOffset(0)] public PSO2ItemConsumable Consumable; [FieldOffset(0)] public PSO2ItemCamo Camo; [FieldOffset(0)] public PSO2ItemUnit Unit; //[FieldOffset(0)] //public byte[] Unknown; //[FieldOffset(0)] //public PSO2ItemNone None; } [StructLayout(LayoutKind.Sequential)] public struct PSO2Items { public ulong uuid; public ItemId id; public Items data; public byte[] ToByteArray() { using (var pkt = new PacketWriter()) { pkt.Write(uuid); pkt.WriteStruct(id); pkt.WriteStruct(data); return pkt.ToArray(); } } // 从字节流转换为结构体 public static PSO2Items FromByteArray(byte[] byteArray) { using (var reader = new PacketReader(byteArray)) { PSO2Items items = new PSO2Items { uuid = reader.ReadUInt64(), id = reader.ReadStruct(), data = reader.ReadStruct() }; return items; } } public ulong GetGUID() { return uuid; } public void SetGUID(ulong guid) { uuid = guid; } } [StructLayout(LayoutKind.Sequential)] public unsafe struct PSO2ItemNone { } [StructLayout(LayoutKind.Sequential)] public unsafe struct PSO2ItemWeapon { public byte flags; public byte element; public byte force; public byte grind; public byte grindPercent; public byte unknown1; public short unknown2; public fixed short affixes[8]; public uint potential; public byte extend; public byte unknown3; public ushort unknown4; public uint unknown5; public uint unknown6; } [StructLayout(LayoutKind.Sequential)] public unsafe struct PSO2ItemClothing { public ushort flags; public fixed byte unk1[0x14]; public HSVColor Color; public fixed byte unk2[0xA]; public ushort Unk3; } [StructLayout(LayoutKind.Sequential)] public unsafe struct PSO2ItemConsumable { public ushort flags; public fixed byte unk1[0x24]; public ushort amount; } [StructLayout(LayoutKind.Sequential)] public unsafe struct PSO2ItemCamo { public byte unk1; public byte unk2; public byte unk3; public fixed byte unk4[0x24]; public byte unk5; } [StructLayout(LayoutKind.Sequential)] public unsafe struct PSO2ItemUnit { public byte flags; public byte EnhLevel; public byte EnhPercent; public byte Unk1; // 使用 fixed 数组来存储附加信息 public fixed ushort Affixes[8]; // Item affix IDs (0 to 4095) public fixed byte unk4[0x7]; public uint Potential; // 使用 fixed 数组来存储未知字段 public fixed byte Unk2[4]; public uint Unk3; public ushort Unk4; public ushort Unk5; // 提供访问固定数组的属性 public Span AffixSpan { get { fixed (ushort* p = Affixes) { return new Span(p, 8); } } } } public struct Campaign { /// Campaign ID. public uint Id; // 对应 Rust 的 u32 /// Start timestamp. public TimeSpan StartDate; // 对应 Rust 的 Duration /// End timestamp. public TimeSpan EndDate; // 对应 Rust 的 Duration /// Campaign title (固定长度 0x3E). public const int TitleLength = 0x3E; public byte[] titleBytes; public string Title { get => Encoding.ASCII.GetString(titleBytes).TrimEnd('\0'); set { titleBytes = new byte[TitleLength]; byte[] valueBytes = Encoding.ASCII.GetBytes(value); Array.Copy(valueBytes, titleBytes, Math.Min(valueBytes.Length, TitleLength)); } } /// Campaign conditions (固定长度 0x102). public const int ConditionsLength = 0x102; public byte[] conditionsBytes; public string Conditions { get => Encoding.ASCII.GetString(conditionsBytes).TrimEnd('\0'); set { conditionsBytes = new byte[ConditionsLength]; byte[] valueBytes = Encoding.ASCII.GetBytes(value); Array.Copy(valueBytes, conditionsBytes, Math.Min(valueBytes.Length, ConditionsLength)); } } // 从数据流中读取 Campaign public static Campaign FromStream(Stream stream) { using (BinaryReader reader = new BinaryReader(stream, Encoding.ASCII, true)) { Campaign campaign = new Campaign { Id = reader.ReadUInt32(), StartDate = TimeSpan.FromTicks(reader.ReadInt64()), EndDate = TimeSpan.FromTicks(reader.ReadInt64()), }; campaign.titleBytes = reader.ReadBytes(TitleLength); campaign.conditionsBytes = reader.ReadBytes(ConditionsLength); return campaign; } } // 将 Campaign 写入数据流 public void WriteToStream(Stream stream) { using (BinaryWriter writer = new BinaryWriter(stream, Encoding.ASCII, true)) { writer.Write(Id); writer.Write(StartDate.Ticks); writer.Write(EndDate.Ticks); writer.Write(titleBytes); writer.Write(conditionsBytes); } } } public enum ItemTypes { NoItem, Weapon, Clothing, Consumable, Camo, Unit, Unknown } [Flags] public enum ItemFlags { Locked = 0x01, BoundToOwner = 0x02 } public enum ItemElement { None, Fire, Ice, Lightning, Wind, Light, Dark } } public static class AffixUtils { public static ushort[] ReadPackedAffixes(Stream reader) { byte[] packed = new byte[12]; reader.Read(packed, 0, packed.Length); ushort[] affixes = new ushort[8]; for (int i = 0; i < 4; i++) { affixes[i * 2] = BitConverter.ToUInt16(new byte[] { packed[i * 3], (byte)((packed[i * 3 + 2] & 0xF0) >> 4) }, 0); affixes[i * 2 + 1] = BitConverter.ToUInt16(new byte[] { packed[i * 3 + 1], (byte)(packed[i * 3 + 2] & 0xF) }, 0); } return affixes; } public static void WritePackedAffixes(ushort[] affixes, Stream writer) { byte[] packed = new byte[12]; for (int i = 0; i < 4; i++) { byte[] affix1 = BitConverter.GetBytes(affixes[i * 2]); byte[] affix2 = BitConverter.GetBytes(affixes[i * 2 + 1]); packed[i * 3] = affix1[0]; packed[i * 3 + 1] = affix2[0]; packed[i * 3 + 2] = (byte)((affix1[1] << 4) | (affix2[1] & 0xF)); } writer.Write(packed, 0, packed.Length); } } public class PacketError : Exception { public string PacketName { get; } public string FieldName { get; } public PacketError(string packetName, string fieldName, Exception innerException) : base($"Error in packet '{packetName}', field '{fieldName}'", innerException) { PacketName = packetName; FieldName = fieldName; } }