diff --git a/Dalamud/Game/Chat/SeStringHandling/Payload.cs b/Dalamud/Game/Chat/SeStringHandling/Payload.cs
index f5ff7aed0..fbbaf2ab8 100644
--- a/Dalamud/Game/Chat/SeStringHandling/Payload.cs
+++ b/Dalamud/Game/Chat/SeStringHandling/Payload.cs
@@ -62,17 +62,21 @@ namespace Dalamud.Game.Chat.SeStringHandling
case EmbeddedInfoType.Status:
payload = new StatusPayload();
break;
+
case EmbeddedInfoType.LinkTerminator:
- // Does not need to be handled
- break;
+ // this has no custom handling and so needs to fallthrough to ensure it is captured
default:
Log.Verbose("Unhandled EmbeddedInfoType: {0}", subType);
+ // rewind so we capture the Interactable byte in the raw data
+ reader.BaseStream.Seek(-1, SeekOrigin.Current);
+ payload = new RawPayload((byte)chunkType);
break;
}
}
break;
default:
Log.Verbose("Unhandled SeStringChunkType: {0}", chunkType);
+ payload = new RawPayload((byte)chunkType);
break;
}
diff --git a/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs b/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs
index 87a6a0461..6576e0524 100644
--- a/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs
+++ b/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs
@@ -26,6 +26,10 @@ namespace Dalamud.Game.Chat.SeStringHandling
///
/// An SeString payload representing raw, typed text.
///
- RawText
+ RawText,
+ ///
+ /// 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 d8bad2c0b..1a1dc0bb7 100644
--- a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs
+++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs
@@ -36,10 +36,21 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
{
var actualItemId = IsHQ ? ItemId + 1000000 : ItemId;
var idBytes = MakeInteger(actualItemId);
+ bool hasName = !string.IsNullOrEmpty(ItemName);
var itemIdFlag = IsHQ ? IntegerType.Int16Plus1Million : IntegerType.Int16;
var chunkLen = idBytes.Length + 5;
+ if (hasName)
+ {
+ // 1 additional unknown byte compared to the nameless version, 1 byte for the name length, and then the name itself
+ chunkLen += (1 + 1 + ItemName.Length);
+ if (IsHQ)
+ {
+ chunkLen += 4; // unicode representation of the HQ symbol is 3 bytes, preceded by a space
+ }
+ }
+
var bytes = new List()
{
START_BYTE,
@@ -48,7 +59,32 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
};
bytes.AddRange(idBytes);
// unk
- bytes.AddRange(new byte[] { 0x02, 0x01, END_BYTE });
+ bytes.AddRange(new byte[] { 0x02, 0x01 });
+
+ // Links don't have to include the name, but if they do, it requires additional work
+ if (hasName)
+ {
+ var nameLen = ItemName.Length + 1;
+ if (IsHQ)
+ {
+ nameLen += 4; // space plus 3 bytes for HQ symbol
+ }
+
+ bytes.AddRange(new byte[]
+ {
+ 0xFF, // unk
+ (byte)nameLen
+ });
+ bytes.AddRange(Encoding.UTF8.GetBytes(ItemName));
+
+ if (IsHQ)
+ {
+ // space and HQ symbol
+ bytes.AddRange(new byte[] { 0x20, 0xEE, 0x80, 0xBC });
+ }
+ }
+
+ bytes.Add(END_BYTE);
return bytes.ToArray();
}
@@ -74,7 +110,16 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
reader.ReadBytes(3);
var itemNameLen = GetInteger(reader);
- ItemName = Encoding.UTF8.GetString(reader.ReadBytes(itemNameLen));
+ var itemNameBytes = reader.ReadBytes(itemNameLen);
+
+ // HQ items have the HQ symbol as part of the name, but since we already recorded
+ // the HQ flag, we want just the bare name
+ if (IsHQ)
+ {
+ itemNameBytes = itemNameBytes.Take(itemNameLen - 4).ToArray();
+ }
+
+ ItemName = Encoding.UTF8.GetString(itemNameBytes);
}
}
}
diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs
new file mode 100644
index 000000000..77480291b
--- /dev/null
+++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Dalamud.Game.Chat.SeStringHandling.Payloads
+{
+ public class RawPayload : Payload
+ {
+ public override PayloadType Type => PayloadType.Unknown;
+
+ public byte ChunkType { get; private set; }
+ public byte[] Data { get; private set; }
+
+ public RawPayload(byte chunkType)
+ {
+ ChunkType = chunkType;
+ }
+
+ public override void Resolve()
+ {
+ // nothing to do
+ }
+
+ public override byte[] Encode()
+ {
+ var chunkLen = Data.Length + 1;
+
+ var bytes = new List()
+ {
+ START_BYTE,
+ ChunkType,
+ (byte)chunkLen
+ };
+ bytes.AddRange(Data);
+
+ bytes.Add(END_BYTE);
+
+ return bytes.ToArray();
+ }
+
+ public override string ToString()
+ {
+ return $"{Type} - Chunk type: {ChunkType:X}, Data: {BitConverter.ToString(Data).Replace("-", " ")}";
+ }
+
+ protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream)
+ {
+ Data = reader.ReadBytes((int)(endOfStream - reader.BaseStream.Position + 1));
+ }
+ }
+}