diff --git a/Dalamud/Game/Chat/SeStringHandling/Payload.cs b/Dalamud/Game/Chat/SeStringHandling/Payload.cs index fbbaf2ab8..685b4c8a5 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payload.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Dalamud.Game.Chat.SeStringHandling.Payloads; using Serilog; @@ -74,6 +72,15 @@ namespace Dalamud.Game.Chat.SeStringHandling } } break; + + case SeStringChunkType.UIForeground: + payload = new UIForegroundPayload(); + break; + + case SeStringChunkType.UIGlow: + payload = new UIGlowPayload(); + break; + default: Log.Verbose("Unhandled SeStringChunkType: {0}", chunkType); payload = new RawPayload((byte)chunkType); @@ -104,7 +111,9 @@ namespace Dalamud.Game.Chat.SeStringHandling protected enum SeStringChunkType { - Interactable = 0x27 + Interactable = 0x27, + UIForeground = 0x48, + UIGlow = 0x49 } protected enum EmbeddedInfoType @@ -118,6 +127,9 @@ namespace Dalamud.Game.Chat.SeStringHandling protected enum IntegerType { + // Custom value indicating no marker at all + None = 0x0, + Byte = 0xF0, ByteTimes256 = 0xF1, Int16 = 0xF2, @@ -188,37 +200,49 @@ namespace Dalamud.Game.Chat.SeStringHandling } } - protected static byte[] MakeInteger(int value) + protected virtual byte[] MakeInteger(int value) { - // clearly the epitome of efficiency + // single-byte values below the marker values have no marker and have 1 added + if (value + 1 < (int)IntegerType.Byte) + { + value++; + return new byte[] { (byte)value }; + } var bytesPadded = BitConverter.GetBytes(value); Array.Reverse(bytesPadded); - return bytesPadded.SkipWhile(b => b == 0x00).ToArray(); + var shrunkValue = bytesPadded.SkipWhile(b => b == 0x00).ToArray(); + + var encodedNum = new List(); + + var marker = GetMarkerForIntegerBytes(shrunkValue); + if (marker != 0) + { + encodedNum.Add(marker); + } + + encodedNum.AddRange(shrunkValue); + + return encodedNum.ToArray(); } - protected static IntegerType GetTypeForIntegerBytes(byte[] bytes) + // This is only accurate in a very general sense + // Different payloads seem to use different default values for things + // So this should be overridden where necessary + protected virtual byte GetMarkerForIntegerBytes(byte[] bytes) { // not the most scientific, exists mainly for laziness - if (bytes.Length == 1) + var marker = bytes.Length switch { - return IntegerType.Byte; - } - else if (bytes.Length == 2) - { - return IntegerType.Int16; - } - else if (bytes.Length == 3) - { - return IntegerType.Int24; - } - else if (bytes.Length == 4) - { - return IntegerType.Int32; - } + 1 => IntegerType.Byte, + 2 => IntegerType.Int16, + 3 => IntegerType.Int24, + 4 => IntegerType.Int32, + _ => throw new NotSupportedException() + }; - throw new NotSupportedException(); + return (byte)marker; } #endregion } diff --git a/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs b/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs index 6576e0524..2a6107a76 100644 --- a/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs +++ b/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs @@ -28,6 +28,14 @@ namespace Dalamud.Game.Chat.SeStringHandling /// RawText, /// + /// An SeString payload representing a text foreground color. + /// + UIForeground, + /// + /// An SeString payload representing a text glow color. + /// + UIGlow, + /// /// An SeString payload representing any data we don't handle. /// Unknown diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs index 1a1dc0bb7..7ebf6ba23 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs @@ -38,9 +38,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads var idBytes = MakeInteger(actualItemId); bool hasName = !string.IsNullOrEmpty(ItemName); - var itemIdFlag = IsHQ ? IntegerType.Int16Plus1Million : IntegerType.Int16; - - var chunkLen = idBytes.Length + 5; + var chunkLen = idBytes.Length + 4; if (hasName) { // 1 additional unknown byte compared to the nameless version, 1 byte for the name length, and then the name itself @@ -54,8 +52,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads var bytes = new List() { START_BYTE, - (byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.ItemLink, - (byte)itemIdFlag + (byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.ItemLink }; bytes.AddRange(idBytes); // unk @@ -122,5 +119,16 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads ItemName = Encoding.UTF8.GetString(itemNameBytes); } } + + protected override byte GetMarkerForIntegerBytes(byte[] bytes) + { + // custom marker just for hq items? + if (bytes.Length == 3 && IsHQ) + { + return (byte)IntegerType.Int16Plus1Million; + } + + return base.GetMarkerForIntegerBytes(bytes); + } } } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs index 4169ac42a..410a044df 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs @@ -35,13 +35,11 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override byte[] Encode() { var idBytes = MakeInteger(StatusId); - var idPrefix = GetTypeForIntegerBytes(idBytes); - var chunkLen = idBytes.Length + 8; + var chunkLen = idBytes.Length + 7; var bytes = new List() { - START_BYTE, (byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.Status, - (byte)idPrefix + START_BYTE, (byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.Status }; bytes.AddRange(idBytes); diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs new file mode 100644 index 000000000..a9afebfa8 --- /dev/null +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Dalamud.Game.Chat.SeStringHandling.Payloads +{ + public class UIForegroundPayload : Payload + { + public override PayloadType Type => PayloadType.UIForeground; + + public ushort RawColor { get; private set; } + + //public int Red { get; private set; } + //public int Green { get; private set; } + //public int Blue { get; private set; } + + public override byte[] Encode() + { + var colorBytes = MakeInteger(RawColor); + var chunkLen = colorBytes.Length + 1; + + var bytes = new List(new byte[] + { + START_BYTE, (byte)SeStringChunkType.UIForeground, (byte)chunkLen + }); + + bytes.AddRange(colorBytes); + bytes.Add(END_BYTE); + + return bytes.ToArray(); + } + + public override void Resolve() + { + // TODO: resolve color keys to hex colors via UIColor table + } + + public override string ToString() + { + return $"{Type} - RawColor: {RawColor}"; + } + + protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) + { + RawColor = (ushort)GetInteger(reader); + } + + protected override byte GetMarkerForIntegerBytes(byte[] bytes) + { + return bytes.Length switch + { + // a single byte of 0x01 is used to 'disable' color, and has no marker + 1 => (byte)IntegerType.None, + _ => base.GetMarkerForIntegerBytes(bytes) + }; + } + } +} diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs new file mode 100644 index 000000000..3bc98fc6e --- /dev/null +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Dalamud.Game.Chat.SeStringHandling.Payloads +{ + public class UIGlowPayload : Payload + { + public override PayloadType Type => PayloadType.UIGlow; + + public ushort RawColor { get; private set; } + + //public int Red { get; private set; } + //public int Green { get; private set; } + //public int Blue { get; private set; } + + public override byte[] Encode() + { + var colorBytes = MakeInteger(RawColor); + var chunkLen = colorBytes.Length + 1; + + var bytes = new List(new byte[] + { + START_BYTE, (byte)SeStringChunkType.UIGlow, (byte)chunkLen + }); + + bytes.AddRange(colorBytes); + bytes.Add(END_BYTE); + + return bytes.ToArray(); + } + + public override void Resolve() + { + // TODO: resolve color keys to hex colors via UIColor table + } + + public override string ToString() + { + return $"{Type} - RawColor: {RawColor}"; + } + + protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) + { + RawColor = (ushort)GetInteger(reader); + } + + protected override byte GetMarkerForIntegerBytes(byte[] bytes) + { + return bytes.Length switch + { + // a single byte of 0x01 is used to 'disable' color, and has no marker + 1 => (byte)IntegerType.None, + _ => base.GetMarkerForIntegerBytes(bytes) + }; + } + } +}