Add UIGlow and UIForeground SeString payloads; refactor some handling of integer markers in payloads, to make encoding a bit simpler

This commit is contained in:
meli 2020-04-01 20:06:19 -07:00
parent 1e00a33577
commit 4c12b1dfb0
6 changed files with 186 additions and 32 deletions

View file

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

View file

@ -28,6 +28,14 @@ namespace Dalamud.Game.Chat.SeStringHandling
/// </summary>
RawText,
/// <summary>
/// An SeString payload representing a text foreground color.
/// </summary>
UIForeground,
/// <summary>
/// An SeString payload representing a text glow color.
/// </summary>
UIGlow,
/// <summary>
/// An SeString payload representing any data we don't handle.
/// </summary>
Unknown

View file

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

View file

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

View file

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

View file

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