Update SeString Payload handling to make explicit payloads for all chunk types, including unknown ones. Also update ItemPayload to properly handle encoding the item name if present. These changes allow an incoming string to be parsed into payloads, and then fully reconstructed into a valid string, with or without changes.

This commit is contained in:
meli 2020-02-13 14:52:41 -08:00
parent 3980929f71
commit 3d67aecc17
4 changed files with 109 additions and 5 deletions

View file

@ -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;
}

View file

@ -26,6 +26,10 @@ namespace Dalamud.Game.Chat.SeStringHandling
/// <summary>
/// An SeString payload representing raw, typed text.
/// </summary>
RawText
RawText,
/// <summary>
/// An SeString payload representing any data we don't handle.
/// </summary>
Unknown
}
}

View file

@ -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<byte>()
{
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);
}
}
}

View file

@ -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<byte>()
{
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));
}
}
}