From f2cee5f5bc5eb23ed2ae492173dbbed6bea0d332 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Tue, 21 Apr 2020 16:41:03 -0700 Subject: [PATCH 01/17] Initial pieces of payload rework; everything is read-only at the moment, pending next stages. Lumina is now used for on-demand property resolution, properties are only exposed for user-facing values, and mostly expose the lumina object(s) where applicable --- Dalamud/Dalamud.cs | 4 + Dalamud/Data/TransientSheet/Completion.cs | 53 ++++++ Dalamud/DiscordBot/DiscordBotManager.cs | 4 +- Dalamud/Game/Chat/SeStringHandling/Payload.cs | 28 +++- .../Payloads/AutoTranslatePayload.cs | 151 ++++++++++-------- .../SeStringHandling/Payloads/ItemPayload.cs | 55 +++---- .../Payloads/MapLinkPayload.cs | 102 ++++++++---- .../Payloads/PlayerPayload.cs | 35 ++-- .../SeStringHandling/Payloads/RawPayload.cs | 14 +- .../Payloads/StatusPayload.cs | 33 ++-- .../SeStringHandling/Payloads/TextPayload.cs | 33 +--- .../Payloads/UIForegroundPayload.cs | 34 ++-- .../Payloads/UIGlowPayload.cs | 34 ++-- .../Game/Chat/SeStringHandling/SeString.cs | 17 +- Dalamud/Game/ChatHandlers.cs | 4 +- Dalamud/Game/Internal/Gui/ChatGui.cs | 4 +- Dalamud/Game/Internal/Gui/GameGui.cs | 3 +- 17 files changed, 355 insertions(+), 253 deletions(-) create mode 100644 Dalamud/Data/TransientSheet/Completion.cs diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index 649d61653..6165dd89a 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -12,6 +12,7 @@ using Dalamud.Data; using Dalamud.DiscordBot; using Dalamud.Game; using Dalamud.Game.Chat; +using Dalamud.Game.Chat.SeStringHandling; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Actors.Types; using Dalamud.Game.ClientState.Actors.Types.NonPlayer; @@ -103,6 +104,9 @@ namespace Dalamud { this.Data = new DataManager(this.StartInfo.Language); this.Data.Initialize(); + // TODO: better way to do this? basically for lumina injection + SeString.Dalamud = this; + this.ClientState = new ClientState(this, info, this.SigScanner); this.BotManager = new DiscordBotManager(this, this.Configuration.DiscordFeatureConfig); diff --git a/Dalamud/Data/TransientSheet/Completion.cs b/Dalamud/Data/TransientSheet/Completion.cs new file mode 100644 index 000000000..e437971c3 --- /dev/null +++ b/Dalamud/Data/TransientSheet/Completion.cs @@ -0,0 +1,53 @@ +using Lumina.Excel; + +namespace Dalamud.Data.TransientSheet +{ + [Sheet( "Completion", columnHash: 0x2e6c55a3 )] + public class Completion : IExcelRow + { + // column defs from Mon, 02 Mar 2020 11:00:20 GMT + + + // col: 03 offset: 0000 + public string Text; + + // col: 04 offset: 0004 + public string GroupTitle; + + // col: 02 offset: 0008 + public string LookupTable; + + // col: 00 offset: 000c + public ushort Group; + + // col: 01 offset: 000e + public ushort Key; + + + public int RowId { get; set; } + public int SubRowId { get; set; } + + public void PopulateData( RowParser parser, Lumina.Lumina lumina ) + { + RowId = parser.Row; + SubRowId = parser.SubRow; + + // col: 3 offset: 0000 + Text = parser.ReadOffset< string >( 0x0 ); + + // col: 4 offset: 0004 + GroupTitle = parser.ReadOffset< string >( 0x4 ); + + // col: 2 offset: 0008 + LookupTable = parser.ReadOffset< string >( 0x8 ); + + // col: 0 offset: 000c + Group = parser.ReadOffset< ushort >( 0xc ); + + // col: 1 offset: 000e + Key = parser.ReadOffset< ushort >( 0xe ); + + + } + } +} diff --git a/Dalamud/DiscordBot/DiscordBotManager.cs b/Dalamud/DiscordBot/DiscordBotManager.cs index 4dae26982..1189489e9 100644 --- a/Dalamud/DiscordBot/DiscordBotManager.cs +++ b/Dalamud/DiscordBot/DiscordBotManager.cs @@ -205,10 +205,8 @@ namespace Dalamud.DiscordBot { senderWorld = this.dalamud.ClientState.LocalPlayer.HomeWorld.GameData.Name; } else { - playerLink.Resolve(); - senderName = wasOutgoingTell ? this.dalamud.ClientState.LocalPlayer.Name : playerLink.PlayerName; - senderWorld = playerLink.ServerName; + senderWorld = playerLink.World.Name; } var rawMessage = SeString.Parse(message.RawData).TextValue; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payload.cs b/Dalamud/Game/Chat/SeStringHandling/Payload.cs index 716ad0eed..a69e68b3e 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payload.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using Dalamud.Data; using Dalamud.Game.Chat.SeStringHandling.Payloads; using Serilog; @@ -11,6 +12,7 @@ using Serilog; // - lumina DI // - design for handling raw values vs resolved values, both for input and output // - wrapper class(es) for handling of composite links in chat (item, map etc) and formatting operations +// - add italics payload namespace Dalamud.Game.Chat.SeStringHandling { @@ -21,12 +23,23 @@ namespace Dalamud.Game.Chat.SeStringHandling { public abstract PayloadType Type { get; } - public abstract void Resolve(); - public abstract byte[] Encode(); protected abstract void ProcessChunkImpl(BinaryReader reader, long endOfStream); + // :( + protected DataManager dataResolver; + + public Payload() + { + // this is not a good way to do this, but I don't want to have to include a dalamud + // reference on multiple methods in every payload class + // We could also just directly reference this static where we use it, but this at least + // allows for more easily changing how this is injected later, without affecting code + // that makes use of it + this.dataResolver = SeString.Dalamud.Data; + } + public static Payload Process(BinaryReader reader) { Payload payload = null; @@ -78,10 +91,13 @@ namespace Dalamud.Game.Chat.SeStringHandling case EmbeddedInfoType.LinkTerminator: // this has no custom handling and so needs to fallthrough to ensure it is captured default: - Log.Verbose("Unhandled EmbeddedInfoType: {0}", subType); + // but I'm also tired of this log + if (subType != EmbeddedInfoType.LinkTerminator) + { + 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; } } @@ -101,11 +117,11 @@ namespace Dalamud.Game.Chat.SeStringHandling default: Log.Verbose("Unhandled SeStringChunkType: {0}", chunkType); - payload = new RawPayload((byte)chunkType); break; } - payload?.ProcessChunkImpl(reader, reader.BaseStream.Position + chunkLen - 1); + payload ??= new RawPayload((byte)chunkType); + payload.ProcessChunkImpl(reader, reader.BaseStream.Position + chunkLen - 1); // read through the rest of the packet var readBytes = (uint)(reader.BaseStream.Position - packetStart); diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs index 87b25aa45..0a6cb8104 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs @@ -1,7 +1,10 @@ +using Dalamud.Data.TransientSheet; +using Lumina.Excel.GeneratedSheets; +using Serilog; using System; using System.Collections.Generic; using System.IO; - +using System.Linq; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { @@ -9,78 +12,29 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public override PayloadType Type => PayloadType.AutoTranslateText; - public uint Group { get; set; } - - public uint Key { get; set; } - - public string Text { get; set; } - - public override void Resolve() + private string text; + public string Text { - // TODO: fixup once lumina DI is in - - //if (string.IsNullOrEmpty(Text)) - //{ - // var sheet = dalamud.Data.GetExcelSheet(); - - // Completion row = null; - // try - // { - // // try to get the row in the Completion table itself, because this is 'easiest' - // // The row may not exist at all (if the Key is for another table), or it could be the wrong row - // // (again, if it's meant for another table) - // row = sheet.GetRow(Key); - // } - // catch {} // don't care, row will be null - - // if (row?.Group == Group) - // { - // // if the row exists in this table and the group matches, this is actually the correct data - // Text = $"{{ {row.Text} }} "; - // } - // else - // { - // Log.Verbose("row mismatch"); - // try - // { - // // we need to get the linked table and do the lookup there instead - // // in this case, there will only be one entry for this group id - // row = sheet.GetRows().First(r => r.Group == Group); - // // many of the names contain valid id ranges after the table name, but we don't need those - // var actualTableName = row.LookupTable.Split('[')[0]; - - // var name = actualTableName switch - // { - // // TODO: rest of xref'd tables - // "Action" => dalamud.Data.GetExcelSheet().GetRow(Key).Name, - // "ClassJob" => dalamud.Data.GetExcelSheet().GetRow(Key).Name, - // "CraftAction" => dalamud.Data.GetExcelSheet().GetRow(Key).Name, - // "Mount" => dalamud.Data.GetExcelSheet().GetRow(Key).Singular, - // "PlaceName" => dalamud.Data.GetExcelSheet().GetRow(Key).Name, - // "Race" => dalamud.Data.GetExcelSheet().GetRow(Key).Masculine, - // _ => throw new Exception(actualTableName) - // }; - - // Text = $"{{ {name} }} "; - // } - // catch (Exception e) - // { - // Log.Error(e, $"AutoTranslatePayload - failed to resolve: {this}"); - // } - // } - //} + get + { + this.text ??= Resolve(); + return this.text; + } } + private uint group; + private uint key; + public override byte[] Encode() { - var keyBytes = MakeInteger(Key); + var keyBytes = MakeInteger(this.key); var chunkLen = keyBytes.Length + 2; var bytes = new List() { START_BYTE, (byte)SeStringChunkType.AutoTranslateKey, (byte)chunkLen, - (byte)Group + (byte)this.group }; bytes.AddRange(keyBytes); bytes.Add(END_BYTE); @@ -90,16 +44,83 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - Group: {Group}, Key: {Key}, Text: {Text}"; + return $"{Type} - Group: {group}, Key: {key}"; } protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) { // this seems to always be a bare byte, and not following normal integer encoding // the values in the table are all <70 so this is presumably ok - Group = reader.ReadByte(); + this.group = reader.ReadByte(); - Key = GetInteger(reader); + this.key = GetInteger(reader); + } + + private string Resolve() + { + string value = null; + + var sheet = this.dataResolver.GetExcelSheet(); + + Completion row = null; + try + { + // try to get the row in the Completion table itself, because this is 'easiest' + // The row may not exist at all (if the Key is for another table), or it could be the wrong row + // (again, if it's meant for another table) + row = sheet.GetRow((int)this.key); + } + catch { } // don't care, row will be null + + if (row?.Group == this.group) + { + // if the row exists in this table and the group matches, this is actually the correct data + value = row.Text; + } + else + { + try + { + // we need to get the linked table and do the lookup there instead + // in this case, there will only be one entry for this group id + row = sheet.GetRows().First(r => r.Group == this.group); + // many of the names contain valid id ranges after the table name, but we don't need those + var actualTableName = row.LookupTable.Split('[')[0]; + + var ikey = (int)this.key; + + var name = actualTableName switch + { + "Action" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, + "ActionComboRoute" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, + "BuddyAction" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, + "ClassJob" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, + "Companion" => this.dataResolver.GetExcelSheet().GetRow(ikey).Singular, + "CraftAction" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, + "GeneralAction" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, + "GuardianDeity" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, + "MainCommand" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, + "Mount" => this.dataResolver.GetExcelSheet().GetRow(ikey).Singular, + "Pet" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, + "PetAction" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, + // TODO: lumina doesn't have PetMirage + "PlaceName" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, + "Race" => this.dataResolver.GetExcelSheet().GetRow(ikey).Masculine, + "TextCommand" => this.dataResolver.GetExcelSheet().GetRow(ikey).Command, + "Tribe" => this.dataResolver.GetExcelSheet().GetRow(ikey).Masculine, + "Weather" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, + _ => throw new Exception(actualTableName) + }; + + value = name; + } + catch (Exception e) + { + Log.Error(e, $"AutoTranslatePayload - failed to resolve: {this}"); + } + } + + return value; } } } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs index bc58d488e..5f8133953 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs @@ -1,9 +1,9 @@ +using Dalamud.Data.TransientSheet; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -using System.Threading.Tasks; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { @@ -11,38 +11,34 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public override PayloadType Type => PayloadType.Item; - public uint ItemId { get; private set; } - public string ItemName { get; private set; } = string.Empty; - public bool IsHQ { get; private set; } = false; - - public ItemPayload() { } - - public ItemPayload(uint itemId, bool isHQ) + private Item item; + public Item Item { - ItemId = itemId; - IsHQ = isHQ; - } - - public override void Resolve() - { - if (string.IsNullOrEmpty(ItemName)) + get { - dynamic item = XivApi.GetItem((int)ItemId).GetAwaiter().GetResult(); - ItemName = item.Name; + this.item ??= this.dataResolver.GetExcelSheet().GetRow((int)this.itemId); + return this.item; } } + public bool IsHQ { get; private set; } = false; + + private uint itemId; + // mainly to allow overriding the name (for things like owo) + private string displayName; + + public override byte[] Encode() { - var actualItemId = IsHQ ? ItemId + 1000000 : ItemId; + var actualItemId = IsHQ ? this.itemId + 1000000 : this.itemId; var idBytes = MakeInteger(actualItemId); - bool hasName = !string.IsNullOrEmpty(ItemName); + bool hasName = !string.IsNullOrEmpty(this.displayName); 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 - chunkLen += (1 + 1 + ItemName.Length); + chunkLen += (1 + 1 + this.displayName.Length); if (IsHQ) { chunkLen += 4; // unicode representation of the HQ symbol is 3 bytes, preceded by a space @@ -61,7 +57,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads // Links don't have to include the name, but if they do, it requires additional work if (hasName) { - var nameLen = ItemName.Length + 1; + var nameLen = this.displayName.Length + 1; if (IsHQ) { nameLen += 4; // space plus 3 bytes for HQ symbol @@ -72,7 +68,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads 0xFF, // unk (byte)nameLen }); - bytes.AddRange(Encoding.UTF8.GetBytes(ItemName)); + bytes.AddRange(Encoding.UTF8.GetBytes(this.displayName)); if (IsHQ) { @@ -88,16 +84,16 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - ItemId: {ItemId}, ItemName: {ItemName}, IsHQ: {IsHQ}"; + return $"{Type} - ItemId: {itemId}, IsHQ: {IsHQ}"; } protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) { - ItemId = GetInteger(reader); + this.itemId = GetInteger(reader); - if (ItemId > 1000000) + if (this.itemId > 1000000) { - ItemId -= 1000000; + this.itemId -= 1000000; IsHQ = true; } @@ -109,6 +105,11 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads var itemNameLen = (int)GetInteger(reader); var itemNameBytes = reader.ReadBytes(itemNameLen); + // it probably isn't necessary to store this, as we now get the lumina Item + // on demand from the id, which will have the name + // For incoming links, the name "should?" always match + // but we'll store it for use in encode just in case it doesn't + // 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) @@ -116,7 +117,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads itemNameBytes = itemNameBytes.Take(itemNameLen - 4).ToArray(); } - ItemName = Encoding.UTF8.GetString(itemNameBytes); + this.displayName = Encoding.UTF8.GetString(itemNameBytes); } } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs index f422133d8..377dc9eef 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs @@ -9,18 +9,69 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public override PayloadType Type => PayloadType.MapLink; - // pre-Resolve() values - public uint TerritoryTypeId { get; set; } - public uint MapId { get; set; } - public uint RawX { get; set; } - public uint RawY { get; set; } + private Map map; + public Map Map + { + get + { + this.map ??= this.dataResolver.GetExcelSheet().GetRow((int)this.mapId); + return this.map; + } + } - // Resolved values - // It might make sense to have Territory be an external type, that has assorted relevant info - public string Territory { get; private set; } - public string Zone { get; private set; } - public float XCoord { get; private set; } - public float YCoord { get; private set; } + private TerritoryType territoryType; + public TerritoryType TerritoryType + { + get + { + this.territoryType ??= this.dataResolver.GetExcelSheet().GetRow((int)this.territoryTypeId); + return this.territoryType; + } + } + + // these could be cached, but this isn't really too egregious + public float XCoord + { + get + { + return ConvertRawPositionToMapCoordinate(this.rawX, Map.SizeFactor); + } + } + + public float YCoord + { + get + { + return ConvertRawPositionToMapCoordinate(this.rawY, Map.SizeFactor); + } + } + + private string placeNameRegion; + public string PlaceNameRegion + { + get + { + this.placeNameRegion ??= this.dataResolver.GetExcelSheet().GetRow(TerritoryType.PlaceNameRegion).Name; + return this.placeNameRegion; + } + } + + private string placeName; + public string PlaceName + { + get + { + this.placeName ??= this.dataResolver.GetExcelSheet().GetRow(TerritoryType.PlaceName).Name; + return this.placeName; + } + } + + public string DataString => $"m:{TerritoryType.RowId},{Map.RowId},{unchecked((int)rawX)},{unchecked((int)rawY)}"; + + private uint territoryTypeId; + private uint mapId; + private uint rawX; + private uint rawY; // there is no Z; it's purely in the text payload where applicable public override byte[] Encode() @@ -28,9 +79,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads // TODO: for now we just encode the raw/internal values // eventually we should allow creation using 'nice' values that then encode properly - var packedTerritoryAndMapBytes = MakePackedInteger(TerritoryTypeId, MapId); - var xBytes = MakeInteger(RawX); - var yBytes = MakeInteger(RawY); + var packedTerritoryAndMapBytes = MakePackedInteger(this.territoryTypeId, this.mapId); + var xBytes = MakeInteger(this.rawX); + var yBytes = MakeInteger(this.rawY); var chunkLen = 4 + packedTerritoryAndMapBytes.Length + xBytes.Length + yBytes.Length; @@ -50,24 +101,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads return bytes.ToArray(); } - public override void Resolve() - { - // TODO: add once lumina DI is figured out - //if (string.IsNullOrEmpty(Territory)) - //{ - // var terrRow = dataResolver.GetExcelSheet().GetRow((int)TerritoryTypeId); - // Territory = dataResolver.GetExcelSheet().GetRow(terrRow.PlaceName).Name; - // Zone = dataResolver.GetExcelSheet().GetRow(terrRow.PlaceNameZone).Name; - - // var mapSizeFactor = dataResolver.GetExcelSheet().GetRow((int)MapId).SizeFactor; - // XCoord = ConvertRawPositionToMapCoordinate(RawX, mapSizeFactor); - // YCoord = ConvertRawPositionToMapCoordinate(RawY, mapSizeFactor); - //} - } - public override string ToString() { - return $"{Type} - TerritoryTypeId: {TerritoryTypeId}, MapId: {MapId}, RawX: {RawX}, RawY: {RawY}"; + return $"{Type} - TerritoryTypeId: {territoryTypeId}, MapId: {mapId}, RawX: {rawX}, RawY: {rawY}"; } protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) @@ -79,9 +115,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads try { - (TerritoryTypeId, MapId) = GetPackedIntegers(reader); - RawX = (uint)GetInteger(reader); - RawY = (uint)GetInteger(reader); + (this.territoryTypeId, this.mapId) = GetPackedIntegers(reader); + this.rawX = (uint)GetInteger(reader); + this.rawY = (uint)GetInteger(reader); // the Z coordinate is never in this chunk, just the text (if applicable) // seems to always be FF 01 diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs index 16dd6ab45..747ee8c75 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs @@ -1,9 +1,8 @@ +using Lumina.Excel.GeneratedSheets; using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; -using System.Threading.Tasks; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { @@ -12,26 +11,19 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override PayloadType Type => PayloadType.Player; public string PlayerName { get; private set; } - public uint ServerId { get; private set; } - public string ServerName { get; private set; } = String.Empty; - public PlayerPayload() { } - - public PlayerPayload(string playerName, uint serverId) + private World world; + public World World { - PlayerName = playerName; - ServerId = serverId; - } - - public override void Resolve() - { - if (string.IsNullOrEmpty(ServerName)) + get { - dynamic server = XivApi.Get($"World/{ServerId}").GetAwaiter().GetResult(); - ServerName = server.Name; + this.world ??= this.dataResolver.GetExcelSheet().GetRow((int)this.serverId); + return this.world; } } + private uint serverId; + public override byte[] Encode() { var chunkLen = PlayerName.Length + 7; @@ -40,7 +32,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads START_BYTE, (byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.PlayerName, /* unk */ 0x01, - (byte)(ServerId+1), // I didn't want to deal with single-byte values in MakeInteger, so we have to do the +1 manually + (byte)(this.serverId+1), // I didn't want to deal with single-byte values in MakeInteger, so we have to do the +1 manually /* unk */0x01, /* unk */0xFF, // these sometimes vary but are frequently this (byte)(PlayerName.Length+1) }; @@ -50,7 +42,10 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads // encoded names are followed by the name in plain text again // use the payload parsing for consistency, as this is technically a new chunk - bytes.AddRange(new TextPayload(PlayerName).Encode()); + // bytes.AddRange(new TextPayload(PlayerName).Encode()); + + // FIXME + bytes.AddRange(Encoding.UTF8.GetBytes(PlayerName)); // unsure about this entire packet, but it seems to always follow a name bytes.AddRange(new byte[] @@ -65,7 +60,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - PlayerName: {PlayerName}, ServerId: {ServerId}, ServerName: {ServerName}"; + return $"{Type} - PlayerName: {PlayerName}, ServerId: {serverId}"; } protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) @@ -73,7 +68,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads // unk reader.ReadByte(); - ServerId = GetInteger(reader); + this.serverId = GetInteger(reader); // unk reader.ReadBytes(2); diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs index 77480291b..ac4707af4 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs @@ -8,17 +8,13 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public override PayloadType Type => PayloadType.Unknown; - public byte ChunkType { get; private set; } public byte[] Data { get; private set; } + private byte chunkType; + public RawPayload(byte chunkType) { - ChunkType = chunkType; - } - - public override void Resolve() - { - // nothing to do + this.chunkType = chunkType; } public override byte[] Encode() @@ -28,7 +24,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads var bytes = new List() { START_BYTE, - ChunkType, + this.chunkType, (byte)chunkLen }; bytes.AddRange(Data); @@ -40,7 +36,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - Chunk type: {ChunkType:X}, Data: {BitConverter.ToString(Data).Replace("-", " ")}"; + return $"{Type} - Chunk type: {chunkType:X}, Data: {BitConverter.ToString(Data).Replace("-", " ")}"; } protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs index e76d1590f..5dd3de457 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs @@ -1,9 +1,7 @@ +using Lumina.Excel.GeneratedSheets; using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { @@ -11,30 +9,21 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public override PayloadType Type => PayloadType.Status; - public uint StatusId { get; private set; } - - public string StatusName { get; private set; } = string.Empty; - - public StatusPayload() { } - - public StatusPayload(uint statusId) + private Status status; + public Status Status { - StatusId = statusId; - } - - public override void Resolve() - { - if (string.IsNullOrEmpty(StatusName)) + get { - dynamic status = XivApi.Get($"Status/{StatusId}").GetAwaiter().GetResult(); - //Console.WriteLine($"Resolved status {StatusId} to {status.Name}"); - StatusName = status.Name; + status ??= this.dataResolver.GetExcelSheet().GetRow((int)this.statusId); + return status; } } + private uint statusId; + public override byte[] Encode() { - var idBytes = MakeInteger(StatusId); + var idBytes = MakeInteger(this.statusId); var chunkLen = idBytes.Length + 7; var bytes = new List() @@ -51,12 +40,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - StatusId: {StatusId}, StatusName: {StatusName}"; + return $"{Type} - StatusId: {statusId}"; } protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) { - StatusId = GetInteger(reader); + this.statusId = GetInteger(reader); } } } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs index 161cd82db..bb189a6f8 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; -using System.Threading.Tasks; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { @@ -11,36 +9,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public override PayloadType Type => PayloadType.RawText; - private string textConverted = null; - - /// - /// The Text of this text payload as an UTF-8 converted string. - /// Don't rely on this for accurate representation of SE payload data, please check RawData instead. - /// - public string Text { - get { return this.textConverted ??= Encoding.UTF8.GetString(RawData); } - set { - this.textConverted = value; - RawData = Encoding.UTF8.GetBytes(value); - } - } - - /// - /// The raw unconverted data of this text payload. - /// - public byte[] RawData { get; set; } - - public TextPayload() { } - - public TextPayload(string text) - { - Text = text; - } - - public override void Resolve() - { - // nothing to do - } + public string Text { get; private set; } public override byte[] Encode() { diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs index a9afebfa8..359c0694d 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs @@ -1,3 +1,4 @@ +using Lumina.Excel.GeneratedSheets; using System; using System.Collections.Generic; using System.IO; @@ -8,15 +9,29 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public override PayloadType Type => PayloadType.UIForeground; - public ushort RawColor { get; private set; } + private UIColor color; + public UIColor UIColor + { + get + { + this.color ??= this.dataResolver.GetExcelSheet().GetRow(this.colorKey); + return this.color; + } + } - //public int Red { get; private set; } - //public int Green { get; private set; } - //public int Blue { get; private set; } + public uint RGB + { + get + { + return UIColor.UIForeground; + } + } + + private ushort colorKey; public override byte[] Encode() { - var colorBytes = MakeInteger(RawColor); + var colorBytes = MakeInteger(this.colorKey); var chunkLen = colorBytes.Length + 1; var bytes = new List(new byte[] @@ -30,19 +45,14 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads 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}"; + return $"{Type} - UIColor: {colorKey}"; } protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) { - RawColor = (ushort)GetInteger(reader); + this.colorKey = (ushort)GetInteger(reader); } protected override byte GetMarkerForIntegerBytes(byte[] bytes) diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs index 3bc98fc6e..297bce2a4 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs @@ -1,3 +1,4 @@ +using Lumina.Excel.GeneratedSheets; using System; using System.Collections.Generic; using System.IO; @@ -8,15 +9,29 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public override PayloadType Type => PayloadType.UIGlow; - public ushort RawColor { get; private set; } + private UIColor color; + public UIColor UIColor + { + get + { + this.color ??= this.dataResolver.GetExcelSheet().GetRow(this.colorKey); + return this.color; + } + } - //public int Red { get; private set; } - //public int Green { get; private set; } - //public int Blue { get; private set; } + public uint RGB + { + get + { + return UIColor.UIGlow; + } + } + + private ushort colorKey; public override byte[] Encode() { - var colorBytes = MakeInteger(RawColor); + var colorBytes = MakeInteger(this.colorKey); var chunkLen = colorBytes.Length + 1; var bytes = new List(new byte[] @@ -30,19 +45,14 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads 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}"; + return $"{Type} - UIColor: {colorKey}"; } protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) { - RawColor = (ushort)GetInteger(reader); + this.colorKey = (ushort)GetInteger(reader); } protected override byte GetMarkerForIntegerBytes(byte[] bytes) diff --git a/Dalamud/Game/Chat/SeStringHandling/SeString.cs b/Dalamud/Game/Chat/SeStringHandling/SeString.cs index 87e7dee62..d98c5f0c0 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeString.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeString.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; -using System.Threading.Tasks; -using Dalamud.Data; using Dalamud.Game.Chat.SeStringHandling.Payloads; namespace Dalamud.Game.Chat.SeStringHandling @@ -14,6 +11,9 @@ namespace Dalamud.Game.Chat.SeStringHandling /// public class SeString { + // TODO: probably change how this is done/where it comes from + public static Dalamud Dalamud { get; internal set; } + public List Payloads { get; } public SeString(List payloads) @@ -37,6 +37,11 @@ namespace Dalamud.Game.Chat.SeStringHandling { sb.Append(((TextPayload)p).Text); } + // TODO: temporary (probably) + else if (p.Type == PayloadType.AutoTranslateText) + { + sb.Append($"{{ {((AutoTranslatePayload)p).Text} }}"); + } } return sb.ToString(); @@ -52,9 +57,9 @@ namespace Dalamud.Game.Chat.SeStringHandling { var payloads = new List(); - using (var stream = new MemoryStream(bytes)) { - using var reader = new BinaryReader(stream); - + using (var stream = new MemoryStream(bytes)) + using (var reader = new BinaryReader(stream)) + { while (stream.Position < bytes.Length) { var payload = Payload.Process(reader); diff --git a/Dalamud/Game/ChatHandlers.cs b/Dalamud/Game/ChatHandlers.cs index 3c2d59474..e10047ff5 100644 --- a/Dalamud/Game/ChatHandlers.cs +++ b/Dalamud/Game/ChatHandlers.cs @@ -181,14 +181,14 @@ namespace Dalamud.Game { break; } - Log.Debug($"Probable retainer sale: {message}, decoded item {itemLink.ItemId}, HQ {itemLink.IsHQ}"); + Log.Debug($"Probable retainer sale: {message}, decoded item {itemLink.Item.RowId}, HQ {itemLink.IsHQ}"); var valueInfo = matchInfo.Groups["value"]; // not sure if using a culture here would work correctly, so just strip symbols instead if (!valueInfo.Success || !int.TryParse(valueInfo.Value.Replace(",", "").Replace(".", ""), out var itemValue)) continue; - Task.Run(() => this.dalamud.BotManager.ProcessRetainerSale((int)itemLink.ItemId, itemValue, itemLink.IsHQ)); + Task.Run(() => this.dalamud.BotManager.ProcessRetainerSale((int)itemLink.Item.RowId, itemValue, itemLink.IsHQ)); break; } } diff --git a/Dalamud/Game/Internal/Gui/ChatGui.cs b/Dalamud/Game/Internal/Gui/ChatGui.cs index c9eb128c9..a52c3b345 100644 --- a/Dalamud/Game/Internal/Gui/ChatGui.cs +++ b/Dalamud/Game/Internal/Gui/ChatGui.cs @@ -112,8 +112,8 @@ namespace Dalamud.Game.Internal.Gui { if (!FastByteArrayCompare(oldEdited, newEdited)) { Log.Verbose("SeString was edited, taking precedence over StdString edit."); message.RawData = newEdited; - } - Log.Debug($"\nOLD: {BitConverter.ToString(originalMessageData)}\nNEW: {BitConverter.ToString(newEdited)}"); + Log.Debug($"\nOLD: {BitConverter.ToString(originalMessageData)}\nNEW: {BitConverter.ToString(newEdited)}"); + } var messagePtr = pMessage; OwnedStdString allocatedString = null; diff --git a/Dalamud/Game/Internal/Gui/GameGui.cs b/Dalamud/Game/Internal/Gui/GameGui.cs index 89f64eb2e..76a96007e 100644 --- a/Dalamud/Game/Internal/Gui/GameGui.cs +++ b/Dalamud/Game/Internal/Gui/GameGui.cs @@ -151,8 +151,7 @@ namespace Dalamud.Game.Internal.Gui { openMapWithFlag = Address.GetVirtualFunction(uiMapObjectPtr, 0, 63); - var mapLinkString = - $"m:{mapLink.TerritoryTypeId},{mapLink.MapId},{unchecked((int)mapLink.RawX)},{unchecked((int)mapLink.RawY)}"; + var mapLinkString = mapLink.DataString; Log.Debug($"OpenMapWithMapLink: Opening Map Link: {mapLinkString}"); From 7bab3a45f550a192e35fa8b36bb70440519247b1 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Tue, 21 Apr 2020 17:22:42 -0700 Subject: [PATCH 02/17] more uniform names for encode/decode methods, beginnings of cached/dirty data concept for payloads, to prevent regeneration when they are unmodified. This may change or go away entirely as more work is done --- Dalamud/Game/Chat/SeStringHandling/Payload.cs | 49 +++++++++++++++---- .../Payloads/AutoTranslatePayload.cs | 14 +++--- .../SeStringHandling/Payloads/ItemPayload.cs | 13 +++-- .../Payloads/MapLinkPayload.cs | 14 +++--- .../Payloads/PlayerPayload.cs | 14 +++--- .../SeStringHandling/Payloads/RawPayload.cs | 14 +++--- .../Payloads/StatusPayload.cs | 14 +++--- .../SeStringHandling/Payloads/TextPayload.cs | 12 ++--- .../Payloads/UIForegroundPayload.cs | 14 +++--- .../Payloads/UIGlowPayload.cs | 14 +++--- .../Game/Chat/SeStringHandling/SeString.cs | 2 +- 11 files changed, 101 insertions(+), 73 deletions(-) diff --git a/Dalamud/Game/Chat/SeStringHandling/Payload.cs b/Dalamud/Game/Chat/SeStringHandling/Payload.cs index a69e68b3e..7b001b920 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payload.cs @@ -9,7 +9,6 @@ using Serilog; // TODOs: // - refactor integer handling now that we have multiple packed types // - common construction/property design for subclasses -// - lumina DI // - design for handling raw values vs resolved values, both for input and output // - wrapper class(es) for handling of composite links in chat (item, map etc) and formatting operations // - add italics payload @@ -23,13 +22,17 @@ namespace Dalamud.Game.Chat.SeStringHandling { public abstract PayloadType Type { get; } - public abstract byte[] Encode(); + public bool Dirty { get; protected set; } = true; - protected abstract void ProcessChunkImpl(BinaryReader reader, long endOfStream); + protected abstract byte[] EncodeImpl(); + + protected abstract void DecodeImpl(BinaryReader reader, long endOfStream); // :( protected DataManager dataResolver; + protected byte[] encodedData; + public Payload() { // this is not a good way to do this, but I don't want to have to include a dalamud @@ -40,22 +43,48 @@ namespace Dalamud.Game.Chat.SeStringHandling this.dataResolver = SeString.Dalamud.Data; } - public static Payload Process(BinaryReader reader) + public byte[] Encode(bool force = false) { + if (Dirty || force) + { + this.encodedData = EncodeImpl(); + Dirty = false; + } + + return this.encodedData; + } + + public static Payload Decode(BinaryReader reader) + { + var payloadStartPos = reader.BaseStream.Position; + Payload payload = null; if ((byte)reader.PeekChar() != START_BYTE) { - payload = ProcessText(reader); + payload = DecodeText(reader); } else { - payload = ProcessChunk(reader); + payload = DecodeChunk(reader); } + // for now, cache off the actual binary data for this payload, so we don't have to + // regenerate it if the payload isn't modified + // TODO: probably better ways to handle this + var payloadEndPos = reader.BaseStream.Position; + + reader.BaseStream.Position = payloadStartPos; + payload.encodedData = reader.ReadBytes((int)(payloadEndPos - payloadStartPos)); + payload.Dirty = false; + + // Log.Verbose($"got payload bytes {BitConverter.ToString(payload.encodedData).Replace("-", " ")}"); + + reader.BaseStream.Position = payloadEndPos; + return payload; } - private static Payload ProcessChunk(BinaryReader reader) + private static Payload DecodeChunk(BinaryReader reader) { Payload payload = null; @@ -121,7 +150,7 @@ namespace Dalamud.Game.Chat.SeStringHandling } payload ??= new RawPayload((byte)chunkType); - payload.ProcessChunkImpl(reader, reader.BaseStream.Position + chunkLen - 1); + payload.DecodeImpl(reader, reader.BaseStream.Position + chunkLen - 1); // read through the rest of the packet var readBytes = (uint)(reader.BaseStream.Position - packetStart); @@ -130,10 +159,10 @@ namespace Dalamud.Game.Chat.SeStringHandling return payload; } - private static Payload ProcessText(BinaryReader reader) + private static Payload DecodeText(BinaryReader reader) { var payload = new TextPayload(); - payload.ProcessChunkImpl(reader, reader.BaseStream.Length); + payload.DecodeImpl(reader, reader.BaseStream.Length); return payload; } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs index 0a6cb8104..4e6fbfb3a 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs @@ -25,7 +25,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads private uint group; private uint key; - public override byte[] Encode() + public override string ToString() + { + return $"{Type} - Group: {group}, Key: {key}"; + } + + protected override byte[] EncodeImpl() { var keyBytes = MakeInteger(this.key); @@ -42,12 +47,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads return bytes.ToArray(); } - public override string ToString() - { - return $"{Type} - Group: {group}, Key: {key}"; - } - - protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) + protected override void DecodeImpl(BinaryReader reader, long endOfStream) { // this seems to always be a bare byte, and not following normal integer encoding // the values in the table are all <70 so this is presumably ok diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs index 5f8133953..c1f95aae1 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs @@ -27,8 +27,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads // mainly to allow overriding the name (for things like owo) private string displayName; + public override string ToString() + { + return $"{Type} - ItemId: {itemId}, IsHQ: {IsHQ}"; + } - public override byte[] Encode() + protected override byte[] EncodeImpl() { var actualItemId = IsHQ ? this.itemId + 1000000 : this.itemId; var idBytes = MakeInteger(actualItemId); @@ -82,12 +86,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads return bytes.ToArray(); } - public override string ToString() - { - return $"{Type} - ItemId: {itemId}, IsHQ: {IsHQ}"; - } - - protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) + protected override void DecodeImpl(BinaryReader reader, long endOfStream) { this.itemId = GetInteger(reader); diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs index 377dc9eef..beaa80e3a 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs @@ -74,7 +74,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads private uint rawY; // there is no Z; it's purely in the text payload where applicable - public override byte[] Encode() + public override string ToString() + { + return $"{Type} - TerritoryTypeId: {territoryTypeId}, MapId: {mapId}, RawX: {rawX}, RawY: {rawY}"; + } + + protected override byte[] EncodeImpl() { // TODO: for now we just encode the raw/internal values // eventually we should allow creation using 'nice' values that then encode properly @@ -101,12 +106,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads return bytes.ToArray(); } - public override string ToString() - { - return $"{Type} - TerritoryTypeId: {territoryTypeId}, MapId: {mapId}, RawX: {rawX}, RawY: {rawY}"; - } - - protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) + protected override void DecodeImpl(BinaryReader reader, long endOfStream) { // for debugging for now var oldPos = reader.BaseStream.Position; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs index 747ee8c75..a04d75d1c 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs @@ -24,7 +24,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads private uint serverId; - public override byte[] Encode() + public override string ToString() + { + return $"{Type} - PlayerName: {PlayerName}, ServerId: {serverId}"; + } + + protected override byte[] EncodeImpl() { var chunkLen = PlayerName.Length + 7; var bytes = new List() @@ -58,12 +63,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads return bytes.ToArray(); } - public override string ToString() - { - return $"{Type} - PlayerName: {PlayerName}, ServerId: {serverId}"; - } - - protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) + protected override void DecodeImpl(BinaryReader reader, long endOfStream) { // unk reader.ReadByte(); diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs index ac4707af4..4d4788623 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs @@ -17,7 +17,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads this.chunkType = chunkType; } - public override byte[] Encode() + public override string ToString() + { + return $"{Type} - Chunk type: {chunkType:X}, Data: {BitConverter.ToString(Data).Replace("-", " ")}"; + } + + protected override byte[] EncodeImpl() { var chunkLen = Data.Length + 1; @@ -34,12 +39,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads 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) + protected override void DecodeImpl(BinaryReader reader, long endOfStream) { Data = reader.ReadBytes((int)(endOfStream - reader.BaseStream.Position + 1)); } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs index 5dd3de457..0a08c5e5b 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs @@ -21,7 +21,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads private uint statusId; - public override byte[] Encode() + public override string ToString() + { + return $"{Type} - StatusId: {statusId}"; + } + + protected override byte[] EncodeImpl() { var idBytes = MakeInteger(this.statusId); @@ -38,12 +43,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads return bytes.ToArray(); } - public override string ToString() - { - return $"{Type} - StatusId: {statusId}"; - } - - protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) + protected override void DecodeImpl(BinaryReader reader, long endOfStream) { this.statusId = GetInteger(reader); } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs index bb189a6f8..8d81a2b5e 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs @@ -11,17 +11,17 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public string Text { get; private set; } - public override byte[] Encode() - { - return Encoding.UTF8.GetBytes(Text); - } - public override string ToString() { return $"{Type} - Text: {Text}"; } - protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) + protected override byte[] EncodeImpl() + { + return Encoding.UTF8.GetBytes(Text); + } + + protected override void DecodeImpl(BinaryReader reader, long endOfStream) { var text = new List(); diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs index 359c0694d..443c57507 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs @@ -29,7 +29,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads private ushort colorKey; - public override byte[] Encode() + public override string ToString() + { + return $"{Type} - UIColor: {colorKey}"; + } + + protected override byte[] EncodeImpl() { var colorBytes = MakeInteger(this.colorKey); var chunkLen = colorBytes.Length + 1; @@ -45,12 +50,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads return bytes.ToArray(); } - public override string ToString() - { - return $"{Type} - UIColor: {colorKey}"; - } - - protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) + protected override void DecodeImpl(BinaryReader reader, long endOfStream) { this.colorKey = (ushort)GetInteger(reader); } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs index 297bce2a4..b6332bc5a 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs @@ -29,7 +29,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads private ushort colorKey; - public override byte[] Encode() + public override string ToString() + { + return $"{Type} - UIColor: {colorKey}"; + } + + protected override byte[] EncodeImpl() { var colorBytes = MakeInteger(this.colorKey); var chunkLen = colorBytes.Length + 1; @@ -45,12 +50,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads return bytes.ToArray(); } - public override string ToString() - { - return $"{Type} - UIColor: {colorKey}"; - } - - protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream) + protected override void DecodeImpl(BinaryReader reader, long endOfStream) { this.colorKey = (ushort)GetInteger(reader); } diff --git a/Dalamud/Game/Chat/SeStringHandling/SeString.cs b/Dalamud/Game/Chat/SeStringHandling/SeString.cs index d98c5f0c0..248a0692f 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeString.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeString.cs @@ -62,7 +62,7 @@ namespace Dalamud.Game.Chat.SeStringHandling { while (stream.Position < bytes.Length) { - var payload = Payload.Process(reader); + var payload = Payload.Decode(reader); if (payload != null) payloads.Add(payload); } From 442fc9d137c411abd5ad229d38aacadce5b22a8f Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Tue, 21 Apr 2020 17:59:43 -0700 Subject: [PATCH 03/17] Expose a few mutable properties that make sense; most are still read-only for existing payloads. The obvious major point missing in this is the ability to create new payloads from fields.. coming soon --- .../SeStringHandling/Payloads/ItemPayload.cs | 18 ++++++++++-- .../Payloads/PlayerPayload.cs | 29 +++++++++++++------ .../SeStringHandling/Payloads/TextPayload.cs | 20 +++++++++---- .../Payloads/UIForegroundPayload.cs | 11 +++++++ .../Payloads/UIGlowPayload.cs | 11 +++++++ 5 files changed, 73 insertions(+), 16 deletions(-) diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs index c1f95aae1..ffc27af3e 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs @@ -21,11 +21,25 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + // mainly to allow overriding the name (for things like owo) + private string displayName; + public string DisplayName + { + get + { + return this.displayName; + } + + set + { + this.displayName = value; + Dirty = true; + } + } + public bool IsHQ { get; private set; } = false; private uint itemId; - // mainly to allow overriding the name (for things like owo) - private string displayName; public override string ToString() { diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs index a04d75d1c..7561713e6 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs @@ -10,7 +10,16 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public override PayloadType Type => PayloadType.Player; - public string PlayerName { get; private set; } + private string playerName; + public string PlayerName + { + get { return this.playerName; } + set + { + this.playerName = value; + Dirty = true; + } + } private World world; public World World @@ -31,7 +40,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads protected override byte[] EncodeImpl() { - var chunkLen = PlayerName.Length + 7; + var chunkLen = this.playerName.Length + 7; var bytes = new List() { START_BYTE, @@ -39,18 +48,20 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads /* unk */ 0x01, (byte)(this.serverId+1), // I didn't want to deal with single-byte values in MakeInteger, so we have to do the +1 manually /* unk */0x01, /* unk */0xFF, // these sometimes vary but are frequently this - (byte)(PlayerName.Length+1) + (byte)(this.playerName.Length+1) }; - bytes.AddRange(Encoding.UTF8.GetBytes(PlayerName)); + bytes.AddRange(Encoding.UTF8.GetBytes(this.playerName)); bytes.Add(END_BYTE); // encoded names are followed by the name in plain text again // use the payload parsing for consistency, as this is technically a new chunk - // bytes.AddRange(new TextPayload(PlayerName).Encode()); - - // FIXME - bytes.AddRange(Encoding.UTF8.GetBytes(PlayerName)); + bytes.AddRange( + new TextPayload() + { + Text = playerName + }.Encode() + ); // unsure about this entire packet, but it seems to always follow a name bytes.AddRange(new byte[] @@ -74,7 +85,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads reader.ReadBytes(2); var nameLen = (int)GetInteger(reader); - PlayerName = Encoding.UTF8.GetString(reader.ReadBytes(nameLen)); + this.playerName = Encoding.UTF8.GetString(reader.ReadBytes(nameLen)); } } } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs index 8d81a2b5e..e9a171b38 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs @@ -9,7 +9,17 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public override PayloadType Type => PayloadType.RawText; - public string Text { get; private set; } + // allow modifying the text of existing payloads on the fly + private string text; + public string Text + { + get { return this.text; } + set + { + this.text = value; + Dirty = true; + } + } public override string ToString() { @@ -23,7 +33,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads protected override void DecodeImpl(BinaryReader reader, long endOfStream) { - var text = new List(); + var textBytes = new List(); while (reader.BaseStream.Position < endOfStream) { @@ -31,13 +41,13 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads break; // not the most efficient, but the easiest - text.Add(reader.ReadByte()); + textBytes.Add(reader.ReadByte()); } - if (text.Count > 0) + if (textBytes.Count > 0) { // TODO: handling of the game's assorted special unicode characters - Text = Encoding.UTF8.GetString(text.ToArray()); + this.text = Encoding.UTF8.GetString(textBytes.ToArray()); } } } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs index 443c57507..014b94354 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs @@ -19,6 +19,17 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + public ushort ColorKey + { + get { return this.colorKey; } + set + { + this.colorKey = value; + this.color = null; + Dirty = true; + } + } + public uint RGB { get diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs index b6332bc5a..0b594c416 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs @@ -19,6 +19,17 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + public ushort ColorKey + { + get { return this.colorKey; } + set + { + this.colorKey = value; + this.color = null; + Dirty = true; + } + } + public uint RGB { get From 1832702a0a547641543667563b0d509f79c1045f Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Wed, 22 Apr 2020 06:11:20 -0700 Subject: [PATCH 04/17] add codepoints for colored brackets around auto-translate text; minor fixes --- Dalamud/Game/Chat/SeStringHandling/Payload.cs | 6 ++++-- .../Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs | 4 +++- .../Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs | 2 ++ .../Chat/SeStringHandling/Payloads/UIForegroundPayload.cs | 2 +- .../Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs | 2 +- Dalamud/Game/Chat/SeStringHandling/SeString.cs | 2 +- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Dalamud/Game/Chat/SeStringHandling/Payload.cs b/Dalamud/Game/Chat/SeStringHandling/Payload.cs index 7b001b920..92dc7be4f 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payload.cs @@ -9,9 +9,11 @@ using Serilog; // TODOs: // - refactor integer handling now that we have multiple packed types // - common construction/property design for subclasses -// - design for handling raw values vs resolved values, both for input and output // - wrapper class(es) for handling of composite links in chat (item, map etc) and formatting operations // - add italics payload +// Maybes: +// - convert parsing to custom structs for each payload? would make some code prettier and easier to work with +// but also wouldn't work out as well for things that are dynamically-sized namespace Dalamud.Game.Chat.SeStringHandling { @@ -200,8 +202,8 @@ namespace Dalamud.Game.Chat.SeStringHandling Int16 = 0xF2, Int16Packed = 0xF4, // seen in map links, seemingly 2 8-bit values packed into 2 bytes with only one marker Int24Special = 0xF6, // unsure how different form Int24 - used for hq items that add 1 million, also used for normal 24-bit values in map links - Int24Packed = 0xFC, // used in map links- sometimes short+byte, sometimes... not?? Int24 = 0xFA, + Int24Packed = 0xFC, // used in map links- sometimes short+byte, sometimes... not?? Int32 = 0xFE } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs index 4e6fbfb3a..88c42de1d 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs @@ -17,7 +17,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { get { - this.text ??= Resolve(); + // wrap the text in the colored brackets that is uses in-game, since those + // are not actually part of any of the payloads + this.text ??= $"\uE040 {Resolve()} \uE041"; return this.text; } } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs index 7561713e6..7c1be4876 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs @@ -54,6 +54,8 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads bytes.AddRange(Encoding.UTF8.GetBytes(this.playerName)); bytes.Add(END_BYTE); + // TODO: should these really be here? additional payloads should come in separately already... + // encoded names are followed by the name in plain text again // use the payload parsing for consistency, as this is technically a new chunk bytes.AddRange( diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs index 014b94354..d88d0e1c7 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs @@ -34,7 +34,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { get { - return UIColor.UIForeground; + return (UIColor.UIForeground & 0xFFFFFF); } } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs index 0b594c416..33f5dde12 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs @@ -34,7 +34,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { get { - return UIColor.UIGlow; + return (UIColor.UIGlow & 0xFFFFFF); } } diff --git a/Dalamud/Game/Chat/SeStringHandling/SeString.cs b/Dalamud/Game/Chat/SeStringHandling/SeString.cs index 248a0692f..210b4fb92 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeString.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeString.cs @@ -40,7 +40,7 @@ namespace Dalamud.Game.Chat.SeStringHandling // TODO: temporary (probably) else if (p.Type == PayloadType.AutoTranslateText) { - sb.Append($"{{ {((AutoTranslatePayload)p).Text} }}"); + sb.Append(((AutoTranslatePayload)p).Text); } } From 5416c4bc7ee97d721ce120ced31131f285c9c3a1 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Wed, 22 Apr 2020 07:22:54 -0700 Subject: [PATCH 05/17] explicit interface for when payloads include user-displayed text - this may go away later --- .../Chat/SeStringHandling/ITextProvider.cs | 9 +++++++ Dalamud/Game/Chat/SeStringHandling/Payload.cs | 1 + .../Payloads/AutoTranslatePayload.cs | 2 +- .../SeStringHandling/Payloads/TextPayload.cs | 2 +- .../Game/Chat/SeStringHandling/SeString.cs | 24 ++++++------------- 5 files changed, 19 insertions(+), 19 deletions(-) create mode 100644 Dalamud/Game/Chat/SeStringHandling/ITextProvider.cs diff --git a/Dalamud/Game/Chat/SeStringHandling/ITextProvider.cs b/Dalamud/Game/Chat/SeStringHandling/ITextProvider.cs new file mode 100644 index 000000000..9604dbc04 --- /dev/null +++ b/Dalamud/Game/Chat/SeStringHandling/ITextProvider.cs @@ -0,0 +1,9 @@ +using System; + +namespace Dalamud.Game.Chat.SeStringHandling +{ + interface ITextProvider + { + string Text { get; } + } +} diff --git a/Dalamud/Game/Chat/SeStringHandling/Payload.cs b/Dalamud/Game/Chat/SeStringHandling/Payload.cs index 92dc7be4f..0eaf4ee9e 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payload.cs @@ -200,6 +200,7 @@ namespace Dalamud.Game.Chat.SeStringHandling Byte = 0xF0, ByteTimes256 = 0xF1, Int16 = 0xF2, + // ByteTimes65536 = 0xF3, // from RE but never seen Int16Packed = 0xF4, // seen in map links, seemingly 2 8-bit values packed into 2 bytes with only one marker Int24Special = 0xF6, // unsure how different form Int24 - used for hq items that add 1 million, also used for normal 24-bit values in map links Int24 = 0xFA, diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs index 88c42de1d..7d81dc624 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs @@ -8,7 +8,7 @@ using System.Linq; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { - public class AutoTranslatePayload : Payload + public class AutoTranslatePayload : Payload, ITextProvider { public override PayloadType Type => PayloadType.AutoTranslateText; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs index e9a171b38..1f8e3e584 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs @@ -5,7 +5,7 @@ using System.Text; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { - public class TextPayload : Payload + public class TextPayload : Payload, ITextProvider { public override PayloadType Type => PayloadType.RawText; diff --git a/Dalamud/Game/Chat/SeStringHandling/SeString.cs b/Dalamud/Game/Chat/SeStringHandling/SeString.cs index 210b4fb92..222d00ec1 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeString.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeString.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; -using Dalamud.Game.Chat.SeStringHandling.Payloads; namespace Dalamud.Game.Chat.SeStringHandling { @@ -29,22 +29,12 @@ namespace Dalamud.Game.Chat.SeStringHandling /// public string TextValue { - get { - var sb = new StringBuilder(); - foreach (var p in Payloads) - { - if (p.Type == PayloadType.RawText) - { - sb.Append(((TextPayload)p).Text); - } - // TODO: temporary (probably) - else if (p.Type == PayloadType.AutoTranslateText) - { - sb.Append(((AutoTranslatePayload)p).Text); - } - } - - return sb.ToString(); + get + { + return Payloads + .Where(p => p is ITextProvider) + .Cast() + .Aggregate(new StringBuilder(), (sb, tp) => sb.Append(tp.Text), sb => sb.ToString()); } } From 2d5fdd3c4c471570c9dfb93f6997cf496c05b4c7 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Wed, 22 Apr 2020 08:51:24 -0700 Subject: [PATCH 06/17] payload constructors for user-created payloads --- Dalamud/Game/Chat/SeStringHandling/Payload.cs | 2 +- .../Payloads/AutoTranslatePayload.cs | 10 ++++ .../SeStringHandling/Payloads/ItemPayload.cs | 9 ++++ .../Payloads/MapLinkPayload.cs | 53 ++++++++++++++----- .../Payloads/PlayerPayload.cs | 15 +++--- .../SeStringHandling/Payloads/RawPayload.cs | 28 +++++++--- .../Payloads/StatusPayload.cs | 7 +++ .../SeStringHandling/Payloads/TextPayload.cs | 7 +++ .../Payloads/UIForegroundPayload.cs | 7 +++ .../Payloads/UIGlowPayload.cs | 7 +++ 10 files changed, 119 insertions(+), 26 deletions(-) diff --git a/Dalamud/Game/Chat/SeStringHandling/Payload.cs b/Dalamud/Game/Chat/SeStringHandling/Payload.cs index 0eaf4ee9e..519cd1e5c 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payload.cs @@ -35,7 +35,7 @@ namespace Dalamud.Game.Chat.SeStringHandling protected byte[] encodedData; - public Payload() + protected Payload() { // this is not a good way to do this, but I don't want to have to include a dalamud // reference on multiple methods in every payload class diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs index 7d81dc624..eadf0074f 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs @@ -27,6 +27,16 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads private uint group; private uint key; + internal AutoTranslatePayload() { } + + public AutoTranslatePayload(uint group, uint key) + { + this.group = group; + this.key = key; + } + + // TODO: friendlier ctor? not sure how to handle that given how weird the tables are + public override string ToString() { return $"{Type} - Group: {group}, Key: {key}"; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs index ffc27af3e..cda7e33a8 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs @@ -41,6 +41,15 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads private uint itemId; + internal ItemPayload() { } + + public ItemPayload(uint itemId, bool isHQ, string displayNameOverride = null) + { + this.itemId = itemId; + this.IsHQ = isHQ; + this.displayName = displayNameOverride; + } + public override string ToString() { return $"{Type} - ItemId: {itemId}, IsHQ: {IsHQ}"; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs index beaa80e3a..7b78b0423 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs @@ -46,6 +46,18 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + public string CoordinateString + { + get + { + // this truncates the values to one decimal without rounding, which is what the game does + var x = Math.Truncate(XCoord * 10.0f) / 10.0f; + var y = Math.Truncate(YCoord * 10.0f) / 10.0f; + + return $"( {x.ToString("0.0")}, {y.ToString("0.0")} )"; + } + } + private string placeNameRegion; public string PlaceNameRegion { @@ -66,14 +78,32 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } - public string DataString => $"m:{TerritoryType.RowId},{Map.RowId},{unchecked((int)rawX)},{unchecked((int)rawY)}"; + public string DataString => $"m:{TerritoryType.RowId},{Map.RowId},{rawX},{rawY}"; private uint territoryTypeId; private uint mapId; - private uint rawX; - private uint rawY; + private int rawX; + private int rawY; // there is no Z; it's purely in the text payload where applicable + internal MapLinkPayload() { } + + public MapLinkPayload(uint territoryTypeId, uint mapId, float niceXCoord, float niceYCoord) + { + this.territoryTypeId = territoryTypeId; + this.mapId = mapId; + this.rawX = this.ConvertMapCoordinateToRawPosition(niceXCoord, Map.SizeFactor); + this.rawY = this.ConvertMapCoordinateToRawPosition(niceYCoord, Map.SizeFactor); + } + + public MapLinkPayload(uint territoryTypeId, uint mapId, int rawX, int rawY) + { + this.territoryTypeId = territoryTypeId; + this.mapId = mapId; + this.rawX = rawX; + this.rawY = rawY; + } + public override string ToString() { return $"{Type} - TerritoryTypeId: {territoryTypeId}, MapId: {mapId}, RawX: {rawX}, RawY: {rawY}"; @@ -81,12 +111,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads protected override byte[] EncodeImpl() { - // TODO: for now we just encode the raw/internal values - // eventually we should allow creation using 'nice' values that then encode properly - var packedTerritoryAndMapBytes = MakePackedInteger(this.territoryTypeId, this.mapId); - var xBytes = MakeInteger(this.rawX); - var yBytes = MakeInteger(this.rawY); + var xBytes = MakeInteger(unchecked((uint)this.rawX)); + var yBytes = MakeInteger(unchecked((uint)this.rawY)); var chunkLen = 4 + packedTerritoryAndMapBytes.Length + xBytes.Length + yBytes.Length; @@ -116,8 +143,8 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads try { (this.territoryTypeId, this.mapId) = GetPackedIntegers(reader); - this.rawX = (uint)GetInteger(reader); - this.rawY = (uint)GetInteger(reader); + this.rawX = unchecked((int)GetInteger(reader)); + this.rawY = unchecked((int)GetInteger(reader)); // the Z coordinate is never in this chunk, just the text (if applicable) // seems to always be FF 01 @@ -134,16 +161,16 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads #region ugliness // from https://github.com/xivapi/ffxiv-datamining/blob/master/docs/MapCoordinates.md // extra 1/1000 because that is how the network ints are done - private float ConvertRawPositionToMapCoordinate(uint pos, float scale) + private float ConvertRawPositionToMapCoordinate(int pos, float scale) { var c = scale / 100.0f; - var scaledPos = (int)pos * c / 1000.0f; + var scaledPos = pos * c / 1000.0f; return ((41.0f / c) * ((scaledPos + 1024.0f) / 2048.0f)) + 1.0f; } // Created as the inverse of ConvertRawPositionToMapCoordinate(), since no one seemed to have a version of that - private float ConvertMapCoordinateToRawPosition(float pos, float scale) + private int ConvertMapCoordinateToRawPosition(float pos, float scale) { var c = scale / 100.0f; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs index 7c1be4876..6a05f1a74 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs @@ -33,6 +33,14 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads private uint serverId; + internal PlayerPayload() { } + + public PlayerPayload(string playerName, uint serverId) + { + this.playerName = playerName; + this.serverId = serverId; + } + public override string ToString() { return $"{Type} - PlayerName: {PlayerName}, ServerId: {serverId}"; @@ -58,12 +66,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads // encoded names are followed by the name in plain text again // use the payload parsing for consistency, as this is technically a new chunk - bytes.AddRange( - new TextPayload() - { - Text = playerName - }.Encode() - ); + bytes.AddRange(new TextPayload(playerName).Encode()); // unsure about this entire packet, but it seems to always follow a name bytes.AddRange(new byte[] diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs index 4d4788623..a71060cfb 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { @@ -8,23 +9,38 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public override PayloadType Type => PayloadType.Unknown; - public byte[] Data { get; private set; } + private byte[] data; + public byte[] Data + { + get + { + // for now don't allow modifying the contents + // because we don't really have a way to track Dirty + return (byte[])data.Clone(); + } + } private byte chunkType; - public RawPayload(byte chunkType) + internal RawPayload(byte chunkType) { this.chunkType = chunkType; } + public RawPayload(byte[] data) + { + this.chunkType = data[0]; + this.data = data.Skip(1).ToArray(); + } + public override string ToString() { - return $"{Type} - Chunk type: {chunkType:X}, Data: {BitConverter.ToString(Data).Replace("-", " ")}"; + return $"{Type} - Chunk type: {chunkType:X}, Data: {BitConverter.ToString(data).Replace("-", " ")}"; } protected override byte[] EncodeImpl() { - var chunkLen = Data.Length + 1; + var chunkLen = this.data.Length + 1; var bytes = new List() { @@ -32,7 +48,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads this.chunkType, (byte)chunkLen }; - bytes.AddRange(Data); + bytes.AddRange(this.data); bytes.Add(END_BYTE); @@ -41,7 +57,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads protected override void DecodeImpl(BinaryReader reader, long endOfStream) { - Data = reader.ReadBytes((int)(endOfStream - reader.BaseStream.Position + 1)); + this.data = reader.ReadBytes((int)(endOfStream - reader.BaseStream.Position + 1)); } } } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs index 0a08c5e5b..3b822c239 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs @@ -21,6 +21,13 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads private uint statusId; + internal StatusPayload() { } + + public StatusPayload(uint statusId) + { + this.statusId = statusId; + } + public override string ToString() { return $"{Type} - StatusId: {statusId}"; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs index 1f8e3e584..8bb5eff1e 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs @@ -26,6 +26,13 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads return $"{Type} - Text: {Text}"; } + internal TextPayload() { } + + public TextPayload(string text) + { + this.text = text; + } + protected override byte[] EncodeImpl() { return Encoding.UTF8.GetBytes(Text); diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs index d88d0e1c7..78150f08d 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs @@ -40,6 +40,13 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads private ushort colorKey; + internal UIForegroundPayload() { } + + public UIForegroundPayload(ushort colorKey) + { + this.colorKey = colorKey; + } + public override string ToString() { return $"{Type} - UIColor: {colorKey}"; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs index 33f5dde12..19fd8365a 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs @@ -40,6 +40,13 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads private ushort colorKey; + internal UIGlowPayload() { } + + public UIGlowPayload(ushort colorKey) + { + this.colorKey = colorKey; + } + public override string ToString() { return $"{Type} - UIColor: {colorKey}"; From 9f884f63f809a8f477ab01cf157a200dbbb84ddf Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Wed, 22 Apr 2020 09:33:04 -0700 Subject: [PATCH 07/17] add basic italics payload --- Dalamud/Game/Chat/SeStringHandling/Payload.cs | 9 +++- .../Game/Chat/SeStringHandling/PayloadType.cs | 4 ++ .../Payloads/EmphasisItalicPayload.cs | 47 +++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs diff --git a/Dalamud/Game/Chat/SeStringHandling/Payload.cs b/Dalamud/Game/Chat/SeStringHandling/Payload.cs index 519cd1e5c..2320a8dfb 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payload.cs @@ -8,12 +8,12 @@ using Serilog; // TODOs: // - refactor integer handling now that we have multiple packed types -// - common construction/property design for subclasses // - wrapper class(es) for handling of composite links in chat (item, map etc) and formatting operations -// - add italics payload // Maybes: // - convert parsing to custom structs for each payload? would make some code prettier and easier to work with // but also wouldn't work out as well for things that are dynamically-sized +// - [SeString] some way to add surrounding formatting information as flags/data to text (or other?) payloads? +// eg, if a text payload is surrounded by italics payloads, strip them out and mark the text payload as italicized namespace Dalamud.Game.Chat.SeStringHandling { @@ -98,6 +98,10 @@ namespace Dalamud.Game.Chat.SeStringHandling switch (chunkType) { + case SeStringChunkType.EmphasisItalic: + payload = new EmphasisItalicPayload(); + break; + case SeStringChunkType.Interactable: { var subType = (EmbeddedInfoType)reader.ReadByte(); @@ -176,6 +180,7 @@ namespace Dalamud.Game.Chat.SeStringHandling protected enum SeStringChunkType { + EmphasisItalic = 0x1A, Interactable = 0x27, AutoTranslateKey = 0x2E, UIForeground = 0x48, diff --git a/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs b/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs index 92e8afa44..fe379ffd2 100644 --- a/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs +++ b/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs @@ -39,6 +39,10 @@ namespace Dalamud.Game.Chat.SeStringHandling /// AutoTranslateText, /// + /// An SeString payload representing italic emphasis formatting on text. + /// + EmphasisItalic, + /// /// An SeString payload representing any data we don't handle. /// Unknown diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs new file mode 100644 index 000000000..359866247 --- /dev/null +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Dalamud.Game.Chat.SeStringHandling.Payloads +{ + class EmphasisItalicPayload : Payload + { + public override PayloadType Type => PayloadType.EmphasisItalic; + + public bool IsEnabled { get; private set; } + + internal EmphasisItalicPayload() { } + + public EmphasisItalicPayload(bool enabled) + { + IsEnabled = enabled; + } + + public override string ToString() + { + return $"{Type} - Enabled: {IsEnabled}"; + } + + protected override byte[] EncodeImpl() + { + // realistically this will always be a single byte of value 1 or 2 + // but we'll treat it normally anyway + var enabledBytes = MakeInteger(IsEnabled ? (uint)1 : 0); + + var chunkLen = enabledBytes.Length + 1; + var bytes = new List() + { + START_BYTE, (byte)SeStringChunkType.EmphasisItalic, (byte)chunkLen + }; + bytes.AddRange(enabledBytes); + bytes.Add(END_BYTE); + + return bytes.ToArray(); + } + + protected override void DecodeImpl(BinaryReader reader, long endOfStream) + { + IsEnabled = (GetInteger(reader) == 1); + } + } +} From fb40a697856b22a6fb1ed282016f9d862109d95d Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Wed, 22 Apr 2020 11:17:11 -0700 Subject: [PATCH 08/17] fix user-created RawPayload to take entire chunk, testing helper for full item link string --- Dalamud/Dalamud.cs | 22 +++------- .../SeStringHandling/Payloads/RawPayload.cs | 6 ++- .../SeStringHandling/Payloads/TextPayload.cs | 9 +++- .../Payloads/UIForegroundPayload.cs | 2 + .../Payloads/UIGlowPayload.cs | 2 + .../Game/Chat/SeStringHandling/SeString.cs | 43 ++++++++++++------- .../Chat/SeStringHandling/SeStringUtils.cs | 37 ++++++++++++++++ 7 files changed, 86 insertions(+), 35 deletions(-) create mode 100644 Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index 6165dd89a..43591ea4f 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -558,23 +558,11 @@ namespace Dalamud { private void OnItemLinkCommand(string command, string arguments) { this.itemSearchCommandWindow = new ItemSearchWindow(this.Data, new UiBuilder(this.InterfaceManager, "ItemSearcher"), false); - this.itemSearchCommandWindow.OnItemChosen += (sender, item) => { - var hexData = new byte[] { - 0x02, 0x13, 0x06, 0xFE, 0xFF, 0xF3, 0xF3, 0xF3, 0x03, 0x02, 0x27, 0x07, 0x03, 0xF2, 0x3A, 0x2F, - 0x02, 0x01, 0x03, 0x02, 0x13, 0x06, 0xFE, 0xFF, 0xFF, 0x7B, 0x1A, 0x03, 0xEE, 0x82, 0xBB, 0x02, - 0x13, 0x02, 0xEC, 0x03 - }; - - var endTag = new byte[] { - 0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03, 0x02, 0x13, 0x02, 0xEC, 0x03 - }; - - BitConverter.GetBytes((short) item.RowId).Reverse().ToArray().CopyTo(hexData, 14); - - hexData = hexData.Concat(Encoding.UTF8.GetBytes(item.Name)).Concat(endTag).ToArray(); - - this.Framework.Gui.Chat.PrintChat(new XivChatEntry { - MessageBytes = hexData + this.itemSearchCommandWindow.OnItemChosen += (sender, item) => + { + this.Framework.Gui.Chat.PrintChat(new XivChatEntry + { + MessageBytes = SeStringUtils.CreateItemLink((uint)item.RowId, false).Encode() }); }; this.isImguiDrawItemSearchWindow = true; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs index a71060cfb..4bde12652 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs @@ -29,8 +29,10 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public RawPayload(byte[] data) { - this.chunkType = data[0]; - this.data = data.Skip(1).ToArray(); + // this payload is 'special' in that we require the entire chunk to be passed in + // and not just the data after the header + this.chunkType = data[1]; + this.data = data.Skip(3).Take(data.Length-4).ToArray(); } public override string ToString() diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs index 8bb5eff1e..5b3fc9882 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs @@ -35,7 +35,14 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads protected override byte[] EncodeImpl() { - return Encoding.UTF8.GetBytes(Text); + // special case to allow for empty text payloads, so users don't have to check + // this may change or go away + if (string.IsNullOrEmpty(this.text)) + { + return new byte[] { }; + } + + return Encoding.UTF8.GetBytes(this.text); } protected override void DecodeImpl(BinaryReader reader, long endOfStream) diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs index 78150f08d..1f06d7dce 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs @@ -9,6 +9,8 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public override PayloadType Type => PayloadType.UIForeground; + public bool IsEnabled => ColorKey != 0; + private UIColor color; public UIColor UIColor { diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs index 19fd8365a..3c9664073 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs @@ -9,6 +9,8 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public override PayloadType Type => PayloadType.UIGlow; + public bool IsEnabled => ColorKey != 0; + private UIColor color; public UIColor UIColor { diff --git a/Dalamud/Game/Chat/SeStringHandling/SeString.cs b/Dalamud/Game/Chat/SeStringHandling/SeString.cs index 222d00ec1..01f01d593 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeString.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeString.cs @@ -12,15 +12,10 @@ namespace Dalamud.Game.Chat.SeStringHandling public class SeString { // TODO: probably change how this is done/where it comes from - public static Dalamud Dalamud { get; internal set; } + internal static Dalamud Dalamud { get; set; } public List Payloads { get; } - public SeString(List payloads) - { - Payloads = payloads; - } - /// /// Helper function to get all raw text from a message as a single joined string /// @@ -39,10 +34,10 @@ namespace Dalamud.Game.Chat.SeStringHandling } /// - /// Parse an array of bytes to a SeString. + /// Parse a binary game message into an SeString. /// - /// - /// + /// Binary message payload data in SE's internal format. + /// An SeString containing parsed Payload objects for each payload in the data. public static SeString Parse(byte[] bytes) { var payloads = new List(); @@ -61,12 +56,30 @@ namespace Dalamud.Game.Chat.SeStringHandling return new SeString(payloads); } - /// - /// Encode a parsed/created SeString to an array of bytes, to be used for injection. - /// - /// - /// The bytes of the message. - public byte[] Encode() + public SeString(List payloads) + { + Payloads = payloads; + } + + public SeString Append(SeString other) + { + Payloads.AddRange(other.Payloads); + return this; + } + + public SeString Append(List payloads) + { + Payloads.AddRange(payloads); + return this; + } + + public SeString Append(Payload payload) + { + Payloads.Add(payload); + return this; + } + + internal byte[] Encode() { var messageBytes = new List(); foreach (var p in Payloads) diff --git a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs new file mode 100644 index 000000000..cf30a296b --- /dev/null +++ b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs @@ -0,0 +1,37 @@ +using Dalamud.Data.TransientSheet; +using Dalamud.Game.Chat.SeStringHandling.Payloads; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Dalamud.Game.Chat.SeStringHandling +{ + public class SeStringUtils + { + public static SeString CreateItemLink(uint itemId, bool isHQ, string displayNameOverride = null) + { + string displayName = displayNameOverride ?? SeString.Dalamud.Data.GetExcelSheet().GetRow((int)itemId).Name; + if (isHQ) + { + displayName += " \uE03C"; + } + + var payloads = new List(new Payload[] + { + new UIForegroundPayload(0x0225), + new UIGlowPayload(0x0226), + new ItemPayload(itemId, isHQ), + new UIForegroundPayload(0x01F4), + new UIGlowPayload(0x01F5), + new TextPayload("\uE0BB"), + new UIGlowPayload(0), + new UIForegroundPayload(0), + new TextPayload(displayName), + new RawPayload(new byte[] { 0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03 }) + }); + + return new SeString(payloads); + } + } +} From ea6c130aa7e7b8ab8b7e0a132787aaf17a81465b Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Wed, 22 Apr 2020 14:22:39 -0700 Subject: [PATCH 09/17] basic map link helpers, fix some rounding/truncation issues with created map links and displayed values --- .../Payloads/MapLinkPayload.cs | 20 +++++--- .../Chat/SeStringHandling/SeStringUtils.cs | 47 ++++++++++++++++++- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs index 7b78b0423..30d11e5fe 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs @@ -51,10 +51,13 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads get { // this truncates the values to one decimal without rounding, which is what the game does - var x = Math.Truncate(XCoord * 10.0f) / 10.0f; - var y = Math.Truncate(YCoord * 10.0f) / 10.0f; + // the fudge also just attempts to correct the truncated/displayed value for rounding/fp issues + // TODO: should this fudge factor be the same as in the ctor? currently not since that is customizable + const float fudge = 0.02f; + var x = Math.Truncate((XCoord+fudge) * 10.0f) / 10.0f; + var y = Math.Truncate((YCoord+fudge) * 10.0f) / 10.0f; - return $"( {x.ToString("0.0")}, {y.ToString("0.0")} )"; + return $"( {x.ToString("0.0")} , {y.ToString("0.0")} )"; } } @@ -88,12 +91,15 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads internal MapLinkPayload() { } - public MapLinkPayload(uint territoryTypeId, uint mapId, float niceXCoord, float niceYCoord) + public MapLinkPayload(uint territoryTypeId, uint mapId, float niceXCoord, float niceYCoord, float fudgeFactor = 0.05f) { this.territoryTypeId = territoryTypeId; this.mapId = mapId; - this.rawX = this.ConvertMapCoordinateToRawPosition(niceXCoord, Map.SizeFactor); - this.rawY = this.ConvertMapCoordinateToRawPosition(niceYCoord, Map.SizeFactor); + // this fudge is necessary basically to ensure we don't shift down a full tenth + // because essentially values are truncated instead of rounded, so 3.09999f will become + // 3.0f and not 3.1f + this.rawX = this.ConvertMapCoordinateToRawPosition(niceXCoord + fudgeFactor, Map.SizeFactor); + this.rawY = this.ConvertMapCoordinateToRawPosition(niceYCoord + fudgeFactor, Map.SizeFactor); } public MapLinkPayload(uint territoryTypeId, uint mapId, int rawX, int rawY) @@ -177,7 +183,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads var scaledPos = ((((pos - 1.0f) * c / 41.0f) * 2048.0f) - 1024.0f) / c; scaledPos *= 1000.0f; - return (int)Math.Round(scaledPos); + return (int)scaledPos; } #endregion diff --git a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs index cf30a296b..90b0e93b4 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs @@ -1,17 +1,19 @@ -using Dalamud.Data.TransientSheet; using Dalamud.Game.Chat.SeStringHandling.Payloads; +using Lumina.Excel.GeneratedSheets; using System; using System.Collections.Generic; using System.Linq; using System.Text; +using DalamudItem = Dalamud.Data.TransientSheet.Item; + namespace Dalamud.Game.Chat.SeStringHandling { public class SeStringUtils { public static SeString CreateItemLink(uint itemId, bool isHQ, string displayNameOverride = null) { - string displayName = displayNameOverride ?? SeString.Dalamud.Data.GetExcelSheet().GetRow((int)itemId).Name; + string displayName = displayNameOverride ?? SeString.Dalamud.Data.GetExcelSheet().GetRow((int)itemId).Name; if (isHQ) { displayName += " \uE03C"; @@ -33,5 +35,46 @@ namespace Dalamud.Game.Chat.SeStringHandling return new SeString(payloads); } + + public static SeString CreateMapLink(uint territoryId, uint mapId, float xCoord, float yCoord, float fudgeFactor = 0.05f) + { + var mapPayload = new MapLinkPayload(territoryId, mapId, xCoord, yCoord, fudgeFactor); + var nameString = $"{mapPayload.PlaceName} {mapPayload.CoordinateString}"; + + var payloads = new List(new Payload[] + { + mapPayload, + new UIForegroundPayload(0x01F4), + new UIGlowPayload(0x01F5), + new TextPayload("\uE0BB"), + new UIGlowPayload(0), + new UIForegroundPayload(0), + new TextPayload(nameString), + new RawPayload(new byte[] { 0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03 }) + }); + + return new SeString(payloads); + } + + public static SeString CreateMapLink(string placeName, float xCoord, float yCoord, float fudgeFactor = 0.05f) + { + var mapSheet = SeString.Dalamud.Data.GetExcelSheet(); + + var matches = SeString.Dalamud.Data.GetExcelSheet().GetRows() + .Where(row => row.Name.ToLowerInvariant() == placeName.ToLowerInvariant()) + .ToArray(); + + foreach (var place in matches) + { + var map = mapSheet.GetRows().FirstOrDefault(row => row.PlaceName == place.RowId); + if (map != null) + { + return CreateMapLink(map.TerritoryType, (uint)map.RowId, xCoord, yCoord); + } + } + + // TODO: empty? throw? + return null; + } } } From 430d834074b7dcbcd2b1aa653d4a948d96ac0fe8 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Wed, 22 Apr 2020 14:51:55 -0700 Subject: [PATCH 10/17] Some static helpers for frequent payloads and groupings --- .../Payloads/EmphasisItalicPayload.cs | 3 ++ .../SeStringHandling/Payloads/RawPayload.cs | 3 ++ .../Payloads/UIForegroundPayload.cs | 2 ++ .../Payloads/UIGlowPayload.cs | 2 ++ .../Chat/SeStringHandling/SeStringUtils.cs | 31 ++++++++++++------- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs index 359866247..19909f516 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs @@ -6,6 +6,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { class EmphasisItalicPayload : Payload { + public static EmphasisItalicPayload ItalicsOn => new EmphasisItalicPayload(true); + public static EmphasisItalicPayload ItalicsOff => new EmphasisItalicPayload(false); + public override PayloadType Type => PayloadType.EmphasisItalic; public bool IsEnabled { get; private set; } diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs index 4bde12652..2a1d04b55 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs @@ -7,6 +7,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public class RawPayload : Payload { + // this and others could be an actual static member somewhere and avoid construction costs, but that probably isn't a real concern + public static RawPayload LinkTerminator => new RawPayload(new byte[] { 0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03 }); + public override PayloadType Type => PayloadType.Unknown; private byte[] data; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs index 1f06d7dce..8d7290058 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs @@ -7,6 +7,8 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public class UIForegroundPayload : Payload { + public static UIForegroundPayload UIForegroundOff => new UIForegroundPayload(0); + public override PayloadType Type => PayloadType.UIForeground; public bool IsEnabled => ColorKey != 0; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs index 3c9664073..fcabfe742 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs @@ -7,6 +7,8 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { public class UIGlowPayload : Payload { + public static UIGlowPayload UIGlowOff => new UIGlowPayload(0); + public override PayloadType Type => PayloadType.UIGlow; public bool IsEnabled => ColorKey != 0; diff --git a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs index 90b0e93b4..44999af20 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs @@ -19,19 +19,17 @@ namespace Dalamud.Game.Chat.SeStringHandling displayName += " \uE03C"; } + // TODO: probably a cleaner way to build these than doing the bulk+insert var payloads = new List(new Payload[] { new UIForegroundPayload(0x0225), new UIGlowPayload(0x0226), new ItemPayload(itemId, isHQ), - new UIForegroundPayload(0x01F4), - new UIGlowPayload(0x01F5), - new TextPayload("\uE0BB"), - new UIGlowPayload(0), - new UIForegroundPayload(0), + // arrow goes here new TextPayload(displayName), - new RawPayload(new byte[] { 0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03 }) + RawPayload.LinkTerminator }); + payloads.InsertRange(3, TextArrowPayloads()); return new SeString(payloads); } @@ -44,14 +42,11 @@ namespace Dalamud.Game.Chat.SeStringHandling var payloads = new List(new Payload[] { mapPayload, - new UIForegroundPayload(0x01F4), - new UIGlowPayload(0x01F5), - new TextPayload("\uE0BB"), - new UIGlowPayload(0), - new UIForegroundPayload(0), + // arrow goes here new TextPayload(nameString), - new RawPayload(new byte[] { 0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03 }) + RawPayload.LinkTerminator }); + payloads.InsertRange(1, TextArrowPayloads()); return new SeString(payloads); } @@ -76,5 +71,17 @@ namespace Dalamud.Game.Chat.SeStringHandling // TODO: empty? throw? return null; } + + public static List TextArrowPayloads() + { + return new List(new Payload[] + { + new UIForegroundPayload(0x01F4), + new UIGlowPayload(0x01F5), + new TextPayload("\uE0BB"), + UIGlowPayload.UIGlowOff, + UIForegroundPayload.UIForegroundOff + }); + } } } From 75aaebccc2aedc97767b9d208142c586eaf33544 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Wed, 22 Apr 2020 15:01:57 -0700 Subject: [PATCH 11/17] switch italics to payloads instead of raw bytes - this is actually messier until the rest of chat there is moved to SeString, but this at least removes some random raw hex. Once more string support is in, the rest can probably be ported as well --- Dalamud/Game/ChatHandlers.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Dalamud/Game/ChatHandlers.cs b/Dalamud/Game/ChatHandlers.cs index e10047ff5..aa4811b50 100644 --- a/Dalamud/Game/ChatHandlers.cs +++ b/Dalamud/Game/ChatHandlers.cs @@ -224,8 +224,16 @@ namespace Dalamud.Game { } private static string MakeItalics(string text) { - return Encoding.UTF8.GetString(new byte[] {0x02, 0x1A, 0x02, 0x02, 0x03}) + text + - Encoding.UTF8.GetString(new byte[] {0x02, 0x1A, 0x02, 0x01, 0x03}); + // TODO: when the above code is switched to SeString, this can be a straight insertion of the + // italics payloads only, and be a lot cleaner + var italicString = new SeString(new List(new Payload[] + { + EmphasisItalicPayload.ItalicsOn, + new TextPayload(text), + EmphasisItalicPayload.ItalicsOff + })); + + return Encoding.UTF8.GetString(italicString.Encode()); } } } From 535312dd220c3cd2f59abfce441e2c5af41b6ae8 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Sat, 25 Apr 2020 19:20:56 -0700 Subject: [PATCH 12/17] fix for linking items with ids that are exact multiples of 256 - they seem to require a specific encoding. This may apply to other things as well. Also just adds to the necessity for a refactor of the integer handling in payloads --- .../SeStringHandling/Payloads/ItemPayload.cs | 24 +++++++++++++++++++ .../Chat/SeStringHandling/SeStringUtils.cs | 7 ++++++ 2 files changed, 31 insertions(+) diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs index cda7e33a8..da09fc5d0 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs @@ -143,6 +143,24 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + protected override byte[] MakeInteger(uint value, bool withMarker = true, bool incrementSmallInts = true) + { + // TODO: as part of refactor + + // linking an item id that is a multiple of 256 seemingly *requires* using byte*256 marker encoding + // or the link will not display correctly + // I am unsure if this applies to other data types as well, so keeping localized here, pending the + // refactor of all this integer handling mess + if (value % 256 == 0) + { + value /= 256; + // this is no longer a small int, but it was likely converted to that range + incrementSmallInts = false; + } + + return base.MakeInteger(value, withMarker, incrementSmallInts); + } + protected override byte GetMarkerForIntegerBytes(byte[] bytes) { // custom marker just for hq items? @@ -151,6 +169,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads return (byte)IntegerType.Int24Special; } + // TODO: as in the above function + if (bytes.Length == 1 && (this.itemId % 256 == 0)) + { + return (byte)IntegerType.ByteTimes256; + } + return base.GetMarkerForIntegerBytes(bytes); } } diff --git a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs index 44999af20..6e8d18053 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs @@ -28,12 +28,19 @@ namespace Dalamud.Game.Chat.SeStringHandling // arrow goes here new TextPayload(displayName), RawPayload.LinkTerminator + // sometimes there is another set of uiglow/foreground off payloads here + // might be necessary when including additional text after the item name }); payloads.InsertRange(3, TextArrowPayloads()); return new SeString(payloads); } + public static SeString CreateItemLink(Item item, bool isHQ, string displayNameOverride = null) + { + return CreateItemLink((uint)item.RowId, isHQ, displayNameOverride ?? item.Name); + } + public static SeString CreateMapLink(uint territoryId, uint mapId, float xCoord, float yCoord, float fudgeFactor = 0.05f) { var mapPayload = new MapLinkPayload(territoryId, mapId, xCoord, yCoord, fudgeFactor); From 94bba30c2172a9da69e7795d1d4f8b83a29741cb Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Sat, 25 Apr 2020 19:31:48 -0700 Subject: [PATCH 13/17] add version of CreateItemLink that takes a lumina Item to avoid a duplicate lookup --- Dalamud/Dalamud.cs | 2 +- Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index 43591ea4f..56cce8f4d 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -562,7 +562,7 @@ namespace Dalamud { { this.Framework.Gui.Chat.PrintChat(new XivChatEntry { - MessageBytes = SeStringUtils.CreateItemLink((uint)item.RowId, false).Encode() + MessageBytes = SeStringUtils.CreateItemLink(item, false).Encode() }); }; this.isImguiDrawItemSearchWindow = true; diff --git a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs index 6e8d18053..489c6590c 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs @@ -3,7 +3,6 @@ using Lumina.Excel.GeneratedSheets; using System; using System.Collections.Generic; using System.Linq; -using System.Text; using DalamudItem = Dalamud.Data.TransientSheet.Item; @@ -36,7 +35,7 @@ namespace Dalamud.Game.Chat.SeStringHandling return new SeString(payloads); } - public static SeString CreateItemLink(Item item, bool isHQ, string displayNameOverride = null) + public static SeString CreateItemLink(DalamudItem item, bool isHQ, string displayNameOverride = null) { return CreateItemLink((uint)item.RowId, isHQ, displayNameOverride ?? item.Name); } From 474efadc5762c395736f6d9cff6b531240af7b87 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Sat, 25 Apr 2020 20:05:34 -0700 Subject: [PATCH 14/17] switch to SeIConChar values for custom unicode characters; it would be nice to be able to use these without casting, but I'll wait on that change in case there are other reasons to stay as it is --- .../Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs | 2 +- Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs index eadf0074f..31f2a9243 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs @@ -19,7 +19,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { // wrap the text in the colored brackets that is uses in-game, since those // are not actually part of any of the payloads - this.text ??= $"\uE040 {Resolve()} \uE041"; + this.text ??= $"{(char)SeIconChar.AutoTranslateOpen} {Resolve()} {(char)SeIconChar.AutoTranslateClose}"; return this.text; } } diff --git a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs index 489c6590c..4a5dbfc9f 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs @@ -15,7 +15,7 @@ namespace Dalamud.Game.Chat.SeStringHandling string displayName = displayNameOverride ?? SeString.Dalamud.Data.GetExcelSheet().GetRow((int)itemId).Name; if (isHQ) { - displayName += " \uE03C"; + displayName += $" {(char)SeIconChar.HighQuality}"; } // TODO: probably a cleaner way to build these than doing the bulk+insert @@ -84,7 +84,7 @@ namespace Dalamud.Game.Chat.SeStringHandling { new UIForegroundPayload(0x01F4), new UIGlowPayload(0x01F5), - new TextPayload("\uE0BB"), + new TextPayload($"{(char)SeIconChar.LinkMarker}"), UIGlowPayload.UIGlowOff, UIForegroundPayload.UIForegroundOff }); From 66e7fda058448cd47f4fc2da5f60e6e8d51d4313 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Sat, 25 Apr 2020 22:04:31 -0700 Subject: [PATCH 15/17] comment a bunch of things, fix some accessibility issues, update ToString display, assorted minor cleanup --- Dalamud/Game/Chat/SeStringHandling/Payload.cs | 44 +++++++++- .../Payloads/AutoTranslatePayload.cs | 20 ++++- .../Payloads/EmphasisItalicPayload.cs | 22 ++++- .../SeStringHandling/Payloads/ItemPayload.cs | 31 ++++++- .../Payloads/MapLinkPayload.cs | 86 ++++++++++++++++--- .../Payloads/PlayerPayload.cs | 25 +++++- .../SeStringHandling/Payloads/RawPayload.cs | 20 ++++- .../Payloads/StatusPayload.cs | 15 +++- .../SeStringHandling/Payloads/TextPayload.cs | 11 +++ .../Payloads/UIForegroundPayload.cs | 27 +++++- .../Payloads/UIGlowPayload.cs | 27 +++++- .../Game/Chat/SeStringHandling/SeString.cs | 29 ++++++- .../Chat/SeStringHandling/SeStringUtils.cs | 58 ++++++++++++- Dalamud/Game/Chat/XivChatType.cs | 2 +- 14 files changed, 386 insertions(+), 31 deletions(-) diff --git a/Dalamud/Game/Chat/SeStringHandling/Payload.cs b/Dalamud/Game/Chat/SeStringHandling/Payload.cs index 2320a8dfb..07baef08e 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payload.cs @@ -8,7 +8,6 @@ using Serilog; // TODOs: // - refactor integer handling now that we have multiple packed types -// - wrapper class(es) for handling of composite links in chat (item, map etc) and formatting operations // Maybes: // - convert parsing to custom structs for each payload? would make some code prettier and easier to work with // but also wouldn't work out as well for things that are dynamically-sized @@ -22,18 +21,41 @@ namespace Dalamud.Game.Chat.SeStringHandling /// public abstract class Payload { + /// + /// The type of this payload. + /// public abstract PayloadType Type { get; } + /// + /// Whether this payload has been modified since the last Encode(). + /// public bool Dirty { get; protected set; } = true; + /// + /// Encodes the internal state of this payload into a byte[] suitable for sending to in-game + /// handlers such as the chat log. + /// + /// Encoded binary payload data suitable for use with in-game handlers. protected abstract byte[] EncodeImpl(); + // TODO: endOfStream is somewhat legacy now that payload length is always handled correctly. + // This could be changed to just take a straight byte[], but that would complicate reading + // but we could probably at least remove the end param + /// + /// Decodes a byte stream from the game into a payload object. + /// + /// A BinaryReader containing at least all the data for this payload. + /// The location holding the end of the data for this payload. protected abstract void DecodeImpl(BinaryReader reader, long endOfStream); - // :( + /// + /// The Lumina instance to use for any necessary data lookups. + /// protected DataManager dataResolver; - protected byte[] encodedData; + // private for now, since subclasses shouldn't interact with this + // To force-invalidate it, Dirty can be set to true + private byte[] encodedData; protected Payload() { @@ -45,6 +67,11 @@ namespace Dalamud.Game.Chat.SeStringHandling this.dataResolver = SeString.Dalamud.Data; } + /// + /// Encode this payload object into a byte[] useable in-game for things like the chat log. + /// + /// If true, ignores any cached value and forcibly reencodes the payload from its internal representation. + /// A byte[] suitable for use with in-game handlers such as the chat log. public byte[] Encode(bool force = false) { if (Dirty || force) @@ -56,6 +83,11 @@ namespace Dalamud.Game.Chat.SeStringHandling return this.encodedData; } + /// + /// Decodes a binary representation of a payload into its corresponding nice object payload. + /// + /// A reader positioned at the start of the payload, and containing at least one entire payload. + /// The constructed Payload-derived object that was decoded from the binary data. public static Payload Decode(BinaryReader reader) { var payloadStartPos = reader.BaseStream.Position; @@ -96,6 +128,7 @@ namespace Dalamud.Game.Chat.SeStringHandling var packetStart = reader.BaseStream.Position; + // any unhandled payload types will be turned into a RawPayload with the exact same binary data switch (chunkType) { case SeStringChunkType.EmphasisItalic: @@ -197,6 +230,10 @@ namespace Dalamud.Game.Chat.SeStringHandling LinkTerminator = 0xCF // not clear but seems to always follow a link } + + // TODO - everything below needs to be completely refactored, now that we have run into + // a lot more cases than were originally handled. + protected enum IntegerType { // used as an internal marker; sometimes single bytes are bare with no marker at all @@ -205,7 +242,6 @@ namespace Dalamud.Game.Chat.SeStringHandling Byte = 0xF0, ByteTimes256 = 0xF1, Int16 = 0xF2, - // ByteTimes65536 = 0xF3, // from RE but never seen Int16Packed = 0xF4, // seen in map links, seemingly 2 8-bit values packed into 2 bytes with only one marker Int24Special = 0xF6, // unsure how different form Int24 - used for hq items that add 1 million, also used for normal 24-bit values in map links Int24 = 0xFA, diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs index 31f2a9243..3c022d8ec 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs @@ -8,11 +8,20 @@ using System.Linq; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload containing an auto-translation/completion chat message. + /// public class AutoTranslatePayload : Payload, ITextProvider { public override PayloadType Type => PayloadType.AutoTranslateText; private string text; + /// + /// The actual text displayed in-game for this payload. + /// + /// + /// Value is evaluated lazily and cached. + /// public string Text { get @@ -29,6 +38,15 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads internal AutoTranslatePayload() { } + /// + /// Creates a new auto-translate payload. + /// + /// The group id for this message. + /// The key/row id for this message. Which table this is in depends on the group id and details the Completion table. + /// + /// This table is somewhat complicated in structure, and so using this constructor may not be very nice. + /// There is probably little use to create one of these, however. + /// public AutoTranslatePayload(uint group, uint key) { this.group = group; @@ -39,7 +57,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - Group: {group}, Key: {key}"; + return $"{Type} - Group: {group}, Key: {key}, Text: {Text}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs index 19909f516..341439edb 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/EmphasisItalicPayload.cs @@ -4,17 +4,37 @@ using System.IO; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { - class EmphasisItalicPayload : Payload + /// + /// An SeString Payload containing information about enabling or disabling italics formatting on following text. + /// + /// + /// As with other formatting payloads, this is only useful in a payload block, where it affects any subsequent + /// text payloads. + /// + public class EmphasisItalicPayload : Payload { + /// + /// Payload representing enabling italics on following text. + /// public static EmphasisItalicPayload ItalicsOn => new EmphasisItalicPayload(true); + /// + /// Payload representing disabling italics on following text. + /// public static EmphasisItalicPayload ItalicsOff => new EmphasisItalicPayload(false); public override PayloadType Type => PayloadType.EmphasisItalic; + /// + /// Whether this payload enables italics formatting for following text. + /// public bool IsEnabled { get; private set; } internal EmphasisItalicPayload() { } + /// + /// Creates an EmphasisItalicPayload. + /// + /// Whether italics formatting should be enabled or disabled for following text. public EmphasisItalicPayload(bool enabled) { IsEnabled = enabled; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs index da09fc5d0..87b3ab87d 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs @@ -7,11 +7,20 @@ using System.Text; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing an interactable item link. + /// public class ItemPayload : Payload { public override PayloadType Type => PayloadType.Item; private Item item; + /// + /// The underlying Lumina Item represented by this payload. + /// + /// + /// Value is evaluated lazily and cached. + /// public Item Item { get @@ -22,7 +31,14 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } // mainly to allow overriding the name (for things like owo) - private string displayName; + // TODO: even though this is present in some item links, it may not really have a use at all + // For things like owo, changing the text payload is probably correct, whereas changing the + // actual embedded name might not work properly. + private string displayName = null; + /// + /// The displayed name for this item link. Note that incoming links only sometimes have names embedded, + /// often the name is only present in a following text payload. + /// public string DisplayName { get @@ -37,12 +53,23 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// Whether or not this item link is for a high-quality version of the item. + /// public bool IsHQ { get; private set; } = false; private uint itemId; internal ItemPayload() { } + /// + /// Creates a payload representing an interactable item link for the specified item. + /// + /// The id of the item. + /// Whether or not the link should be for the high-quality variant of the item. + /// An optional name to include in the item link. Typically this should + /// be left as null, or set to the normal item name. Actual overrides are better done with the subsequent + /// TextPayload that is a part of a full item link in chat. public ItemPayload(uint itemId, bool isHQ, string displayNameOverride = null) { this.itemId = itemId; @@ -52,7 +79,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - ItemId: {itemId}, IsHQ: {IsHQ}"; + return $"{Type} - ItemId: {itemId}, IsHQ: {IsHQ}, Name: {this.displayName ?? Item.Name}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs index 30d11e5fe..e73eb8393 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs @@ -5,11 +5,20 @@ using System.IO; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing an interactable map position link. + /// public class MapLinkPayload : Payload { public override PayloadType Type => PayloadType.MapLink; private Map map; + /// + /// The Map specified for this map link. + /// + /// + /// Value is evaluated lazily and cached. + /// public Map Map { get @@ -20,6 +29,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } private TerritoryType territoryType; + /// + /// The TerritoryType specified for this map link. + /// + /// + /// Value is evaluated lazily and cached. + /// public TerritoryType TerritoryType { get @@ -29,23 +44,43 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// The internal x-coordinate for this map position. + /// + public int RawX { get; private set; } + + /// + /// The internal y-coordinate for this map position. + /// + public int RawY { get; private set; } + // these could be cached, but this isn't really too egregious + /// + /// The readable x-coordinate position for this map link. This value is approximate and unrounded. + /// public float XCoord { get { - return ConvertRawPositionToMapCoordinate(this.rawX, Map.SizeFactor); + return ConvertRawPositionToMapCoordinate(RawX, Map.SizeFactor); } } + /// + /// The readable y-coordinate position for this map link. This value is approximate and unrounded. + /// public float YCoord { get { - return ConvertRawPositionToMapCoordinate(this.rawY, Map.SizeFactor); + return ConvertRawPositionToMapCoordinate(RawY, Map.SizeFactor); } } + /// + /// The printable map coordinates for this link. This value tries to match the in-game printable text as closely as possible + /// but is an approximation and may be slightly off for some positions. + /// public string CoordinateString { get @@ -57,11 +92,15 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads var x = Math.Truncate((XCoord+fudge) * 10.0f) / 10.0f; var y = Math.Truncate((YCoord+fudge) * 10.0f) / 10.0f; + // the formatting and spacing the game uses return $"( {x.ToString("0.0")} , {y.ToString("0.0")} )"; } } private string placeNameRegion; + /// + /// The region name for this map link. This corresponds to the upper zone name found in the actual in-game map UI. eg, "La Noscea" + /// public string PlaceNameRegion { get @@ -72,6 +111,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } private string placeName; + /// + /// The place name for this map link. This corresponds to the lower zone name found in the actual in-game map UI. eg, "Limsa Lominsa Upper Decks" + /// public string PlaceName { get @@ -81,16 +123,25 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } - public string DataString => $"m:{TerritoryType.RowId},{Map.RowId},{rawX},{rawY}"; + /// + /// The data string for this map link, for use by internal game functions that take a string variant and not a binary payload. + /// + public string DataString => $"m:{TerritoryType.RowId},{Map.RowId},{RawX},{RawY}"; private uint territoryTypeId; private uint mapId; - private int rawX; - private int rawY; // there is no Z; it's purely in the text payload where applicable internal MapLinkPayload() { } + /// + /// Creates an interactable MapLinkPayload from a human-readable position. + /// + /// The id of the TerritoryType entry for this link. + /// The id of the Map entry for this link. + /// The human-readable x-coordinate for this link. + /// The human-readable y-coordinate for this link. + /// An optional offset to account for rounding and truncation errors; it is best to leave this untouched in most cases. public MapLinkPayload(uint territoryTypeId, uint mapId, float niceXCoord, float niceYCoord, float fudgeFactor = 0.05f) { this.territoryTypeId = territoryTypeId; @@ -98,28 +149,35 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads // this fudge is necessary basically to ensure we don't shift down a full tenth // because essentially values are truncated instead of rounded, so 3.09999f will become // 3.0f and not 3.1f - this.rawX = this.ConvertMapCoordinateToRawPosition(niceXCoord + fudgeFactor, Map.SizeFactor); - this.rawY = this.ConvertMapCoordinateToRawPosition(niceYCoord + fudgeFactor, Map.SizeFactor); + RawX = this.ConvertMapCoordinateToRawPosition(niceXCoord + fudgeFactor, Map.SizeFactor); + RawY = this.ConvertMapCoordinateToRawPosition(niceYCoord + fudgeFactor, Map.SizeFactor); } + /// + /// Creates an interactable MapLinkPayload from a raw position. + /// + /// The id of the TerritoryType entry for this link. + /// The id of the Map entry for this link. + /// The internal raw x-coordinate for this link. + /// The internal raw y-coordinate for this link. public MapLinkPayload(uint territoryTypeId, uint mapId, int rawX, int rawY) { this.territoryTypeId = territoryTypeId; this.mapId = mapId; - this.rawX = rawX; - this.rawY = rawY; + RawX = rawX; + RawY = rawY; } public override string ToString() { - return $"{Type} - TerritoryTypeId: {territoryTypeId}, MapId: {mapId}, RawX: {rawX}, RawY: {rawY}"; + return $"{Type} - TerritoryTypeId: {territoryTypeId}, MapId: {mapId}, RawX: {RawX}, RawY: {RawY}, display: {PlaceName} {CoordinateString}"; } protected override byte[] EncodeImpl() { var packedTerritoryAndMapBytes = MakePackedInteger(this.territoryTypeId, this.mapId); - var xBytes = MakeInteger(unchecked((uint)this.rawX)); - var yBytes = MakeInteger(unchecked((uint)this.rawY)); + var xBytes = MakeInteger(unchecked((uint)RawX)); + var yBytes = MakeInteger(unchecked((uint)RawY)); var chunkLen = 4 + packedTerritoryAndMapBytes.Length + xBytes.Length + yBytes.Length; @@ -149,8 +207,8 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads try { (this.territoryTypeId, this.mapId) = GetPackedIntegers(reader); - this.rawX = unchecked((int)GetInteger(reader)); - this.rawY = unchecked((int)GetInteger(reader)); + RawX = unchecked((int)GetInteger(reader)); + RawY = unchecked((int)GetInteger(reader)); // the Z coordinate is never in this chunk, just the text (if applicable) // seems to always be FF 01 diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs index 6a05f1a74..d2c0cbfb8 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/PlayerPayload.cs @@ -6,11 +6,17 @@ using System.Text; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing a player link. + /// public class PlayerPayload : Payload { public override PayloadType Type => PayloadType.Player; private string playerName; + /// + /// The player's displayed name. This does not contain the server name. + /// public string PlayerName { get { return this.playerName; } @@ -22,6 +28,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } private World world; + /// + /// The Lumina object representing the player's home server. + /// + /// + /// Value is evaluated lazily and cached. + /// public World World { get @@ -31,10 +43,21 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// A text representation of this player link matching how it might appear in-game. + /// The world name will always be present. + /// + public string DisplayedName => $"{PlayerName}{(char)SeIconChar.CrossWorld}{World.Name}"; + private uint serverId; internal PlayerPayload() { } + /// + /// Create a PlayerPayload link for the specified player. + /// + /// The player's displayed name. + /// The player's home server id. public PlayerPayload(string playerName, uint serverId) { this.playerName = playerName; @@ -43,7 +66,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - PlayerName: {PlayerName}, ServerId: {serverId}"; + return $"{Type} - PlayerName: {PlayerName}, ServerId: {serverId}, ServerName: {World.Name}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs index 2a1d04b55..301bde07e 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/RawPayload.cs @@ -5,21 +5,36 @@ using System.Linq; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing unhandled raw payload data. + /// Mainly useful for constructing unhandled hardcoded payloads, or forwarding any unknown + /// payloads without modification. + /// public class RawPayload : Payload { // this and others could be an actual static member somewhere and avoid construction costs, but that probably isn't a real concern + /// + /// A fixed Payload representing a common link-termination sequence, found in many payload chains. + /// public static RawPayload LinkTerminator => new RawPayload(new byte[] { 0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03 }); public override PayloadType Type => PayloadType.Unknown; private byte[] data; + // this is a bit different from the underlying data + // We need to store just the chunk data for decode to behave nicely, but when reading data out + // it makes more sense to get the entire payload + /// + /// The entire payload byte sequence for this payload. + /// The returned data is a clone and modifications will not be persisted. + /// public byte[] Data { get { // for now don't allow modifying the contents // because we don't really have a way to track Dirty - return (byte[])data.Clone(); + return (byte[])Encode().Clone(); } } @@ -34,13 +49,14 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads { // this payload is 'special' in that we require the entire chunk to be passed in // and not just the data after the header + // This sets data to hold the chunk data fter the header, excluding the END_BYTE this.chunkType = data[1]; this.data = data.Skip(3).Take(data.Length-4).ToArray(); } public override string ToString() { - return $"{Type} - Chunk type: {chunkType:X}, Data: {BitConverter.ToString(data).Replace("-", " ")}"; + return $"{Type} - Data: {BitConverter.ToString(Data).Replace("-", " ")}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs index 3b822c239..d5753e40c 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/StatusPayload.cs @@ -5,11 +5,20 @@ using System.IO; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing an interactable status link. + /// public class StatusPayload : Payload { public override PayloadType Type => PayloadType.Status; private Status status; + /// + /// The Lumina Status object represented by this payload. + /// + /// + /// Value is evaluated lazily and cached. + /// public Status Status { get @@ -23,6 +32,10 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads internal StatusPayload() { } + /// + /// Creates a new StatusPayload for the given status id. + /// + /// The id of the Status for this link. public StatusPayload(uint statusId) { this.statusId = statusId; @@ -30,7 +43,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - StatusId: {statusId}"; + return $"{Type} - StatusId: {statusId}, Name: {Status.Name}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs index 5b3fc9882..ae7bcb23b 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/TextPayload.cs @@ -5,12 +5,19 @@ using System.Text; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing a plain text string. + /// public class TextPayload : Payload, ITextProvider { public override PayloadType Type => PayloadType.RawText; // allow modifying the text of existing payloads on the fly private string text; + /// + /// The text contained in this payload. + /// This may contain SE's special unicode characters. + /// public string Text { get { return this.text; } @@ -28,6 +35,10 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads internal TextPayload() { } + /// + /// Creates a new TextPayload for the given text. + /// + /// The text to include for this payload. public TextPayload(string text) { this.text = text; diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs index 8d7290058..9a8e5780c 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIForegroundPayload.cs @@ -5,15 +5,30 @@ using System.IO; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing a UI foreground color applied to following text payloads. + /// public class UIForegroundPayload : Payload { + /// + /// Payload representing disabling foreground color on following text. + /// public static UIForegroundPayload UIForegroundOff => new UIForegroundPayload(0); public override PayloadType Type => PayloadType.UIForeground; + /// + /// Whether or not this payload represents applying a foreground color, or disabling one. + /// public bool IsEnabled => ColorKey != 0; private UIColor color; + /// + /// A Lumina UIColor object representing this payload. The actual color data is at UIColor.UIForeground + /// + /// + /// Value is evaluated lazily and cached. + /// public UIColor UIColor { get @@ -23,6 +38,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// The color key used as a lookup in the UIColor table for this foreground color. + /// public ushort ColorKey { get { return this.colorKey; } @@ -34,6 +52,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// The Red/Green/Blue values for this foreground color, encoded as a typical hex color. + /// public uint RGB { get @@ -46,6 +67,10 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads internal UIForegroundPayload() { } + /// + /// Creates a new UIForegroundPayload for the given UIColor key. + /// + /// public UIForegroundPayload(ushort colorKey) { this.colorKey = colorKey; @@ -53,7 +78,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - UIColor: {colorKey}"; + return $"{Type} - UIColor: {colorKey} color: {(IsEnabled ? RGB : 0)}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs index fcabfe742..7731b431a 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/UIGlowPayload.cs @@ -5,15 +5,30 @@ using System.IO; namespace Dalamud.Game.Chat.SeStringHandling.Payloads { + /// + /// An SeString Payload representing a UI glow color applied to following text payloads. + /// public class UIGlowPayload : Payload { + /// + /// Payload representing disabling glow color on following text. + /// public static UIGlowPayload UIGlowOff => new UIGlowPayload(0); public override PayloadType Type => PayloadType.UIGlow; + /// + /// Whether or not this payload represents applying a glow color, or disabling one. + /// public bool IsEnabled => ColorKey != 0; private UIColor color; + /// + /// A Lumina UIColor object representing this payload. The actual color data is at UIColor.UIGlow + /// + /// + /// Value is evaluated lazily and cached. + /// public UIColor UIColor { get @@ -23,6 +38,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// The color key used as a lookup in the UIColor table for this glow color. + /// public ushort ColorKey { get { return this.colorKey; } @@ -34,6 +52,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads } } + /// + /// The Red/Green/Blue values for this glow color, encoded as a typical hex color. + /// public uint RGB { get @@ -46,6 +67,10 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads internal UIGlowPayload() { } + /// + /// Creates a new UIForegroundPayload for the given UIColor key. + /// + /// public UIGlowPayload(ushort colorKey) { this.colorKey = colorKey; @@ -53,7 +78,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads public override string ToString() { - return $"{Type} - UIColor: {colorKey}"; + return $"{Type} - UIColor: {colorKey} color: {(IsEnabled ? RGB : 0)}"; } protected override byte[] EncodeImpl() diff --git a/Dalamud/Game/Chat/SeStringHandling/SeString.cs b/Dalamud/Game/Chat/SeStringHandling/SeString.cs index 01f01d593..26e7e7342 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeString.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeString.cs @@ -14,6 +14,9 @@ namespace Dalamud.Game.Chat.SeStringHandling // TODO: probably change how this is done/where it comes from internal static Dalamud Dalamud { get; set; } + /// + /// The ordered list of payloads included in this SeString. + /// public List Payloads { get; } /// @@ -56,30 +59,54 @@ namespace Dalamud.Game.Chat.SeStringHandling return new SeString(payloads); } + /// + /// Creates a new SeString from an ordered list of payloads. + /// + /// The Payload objects to make up this string. public SeString(List payloads) { Payloads = payloads; } + /// + /// Appends the contents of one SeString to this one. + /// + /// The SeString to append to this one. + /// This object. public SeString Append(SeString other) { Payloads.AddRange(other.Payloads); return this; } + /// + /// Appends a list of payloads to this SeString. + /// + /// The Payloads to append. + /// This object. public SeString Append(List payloads) { Payloads.AddRange(payloads); return this; } + /// + /// Appends a single payload to this SeString. + /// + /// The payload to append. + /// This object. public SeString Append(Payload payload) { Payloads.Add(payload); return this; } - internal byte[] Encode() + /// + /// Encodes the Payloads in this SeString into a binary representation + /// suitable for use by in-game handlers, such as the chat log. + /// + /// The binary encoded payload data. + public byte[] Encode() { var messageBytes = new List(); foreach (var p in Payloads) diff --git a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs index 4a5dbfc9f..e7b4cc43f 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeStringUtils.cs @@ -8,8 +8,18 @@ using DalamudItem = Dalamud.Data.TransientSheet.Item; namespace Dalamud.Game.Chat.SeStringHandling { - public class SeStringUtils + /// + /// A utility class for working with common SeString variants. + /// + public static class SeStringUtils { + /// + /// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log. + /// + /// The id of the item to link. + /// Whether to link the high-quality variant of the item. + /// An optional name override to display, instead of the actual item name. + /// An SeString containing all the payloads necessary to display an item link in the chat log. public static SeString CreateItemLink(uint itemId, bool isHQ, string displayNameOverride = null) { string displayName = displayNameOverride ?? SeString.Dalamud.Data.GetExcelSheet().GetRow((int)itemId).Name; @@ -35,11 +45,44 @@ namespace Dalamud.Game.Chat.SeStringHandling return new SeString(payloads); } + /// + /// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log. + /// + /// The Lumina Item to link. + /// Whether to link the high-quality variant of the item. + /// An optional name override to display, instead of the actual item name. + /// An SeString containing all the payloads necessary to display an item link in the chat log. public static SeString CreateItemLink(DalamudItem item, bool isHQ, string displayNameOverride = null) { return CreateItemLink((uint)item.RowId, isHQ, displayNameOverride ?? item.Name); } + public static SeString CreateMapLink(uint territoryId, uint mapId, int rawX, int rawY) + { + var mapPayload = new MapLinkPayload(territoryId, mapId, rawX, rawY); + var nameString = $"{mapPayload.PlaceName} {mapPayload.CoordinateString}"; + + var payloads = new List(new Payload[] + { + mapPayload, + // arrow goes here + new TextPayload(nameString), + RawPayload.LinkTerminator + }); + payloads.InsertRange(1, TextArrowPayloads()); + + return new SeString(payloads); + } + + /// + /// Creates an SeString representing an entire Payload chain that can be used to link a map position in the chat log. + /// + /// The id of the TerritoryType for this map link. + /// The id of the Map for this map link. + /// The human-readable x-coordinate for this link. + /// The human-readable y-coordinate for this link. + /// An optional offset to account for rounding and truncation errors; it is best to leave this untouched in most cases. + /// An SeString containing all of the payloads necessary to display a map link in the chat log. public static SeString CreateMapLink(uint territoryId, uint mapId, float xCoord, float yCoord, float fudgeFactor = 0.05f) { var mapPayload = new MapLinkPayload(territoryId, mapId, xCoord, yCoord, fudgeFactor); @@ -57,6 +100,14 @@ namespace Dalamud.Game.Chat.SeStringHandling return new SeString(payloads); } + /// + /// Creates an SeString representing an entire Payload chain that can be used to link a map position in the chat log, matching a specified zone name. + /// + /// The name of the location for this link. This should be exactly the name as seen in a displayed map link in-game for the same zone. + /// The human-readable x-coordinate for this link. + /// The human-readable y-coordinate for this link. + /// An optional offset to account for rounding and truncation errors; it is best to leave this untouched in most cases. + /// An SeString containing all of the payloads necessary to display a map link in the chat log. public static SeString CreateMapLink(string placeName, float xCoord, float yCoord, float fudgeFactor = 0.05f) { var mapSheet = SeString.Dalamud.Data.GetExcelSheet(); @@ -78,6 +129,11 @@ namespace Dalamud.Game.Chat.SeStringHandling return null; } + /// + /// Creates a list of Payloads necessary to display the arrow link marker icon in chat + /// with the appropriate glow and coloring. + /// + /// A list of all the payloads required to insert the link marker. public static List TextArrowPayloads() { return new List(new Payload[] diff --git a/Dalamud/Game/Chat/XivChatType.cs b/Dalamud/Game/Chat/XivChatType.cs index 7e8eb5326..b0f04a93b 100644 --- a/Dalamud/Game/Chat/XivChatType.cs +++ b/Dalamud/Game/Chat/XivChatType.cs @@ -6,7 +6,7 @@ namespace Dalamud.Game.Chat /// /// The FFXIV chat types as seen in the LogKind ex table. /// - public enum XivChatType : ushort + public enum XivChatType : ushort // FIXME: this is a single byte { None = 0, Debug = 1, From 2e8295247eec5e1d5e2971611f98477b8b327848 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Sun, 26 Apr 2020 15:33:56 -0700 Subject: [PATCH 16/17] add very basic PetMirage table for auto translate --- Dalamud/Data/TransientSheet/PetMirage.cs | 401 ++++++++++++++++++ .../Payloads/AutoTranslatePayload.cs | 2 +- 2 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 Dalamud/Data/TransientSheet/PetMirage.cs diff --git a/Dalamud/Data/TransientSheet/PetMirage.cs b/Dalamud/Data/TransientSheet/PetMirage.cs new file mode 100644 index 000000000..42e39337d --- /dev/null +++ b/Dalamud/Data/TransientSheet/PetMirage.cs @@ -0,0 +1,401 @@ +using Lumina.Excel; + +namespace Dalamud.Data.TransientSheet +{ + [Sheet( "PetMirage", columnHash: 0x720608f1 )] + public class PetMirage : IExcelRow + { + // column defs from Sun, 26 Apr 2020 15:17:06 GMT + + + // col: 02 offset: 0000 + public string Name; + + // col: 03 offset: 0004 + public ushort unknown4; + + // col: 33 offset: 0006 + public ushort unknown6; + + // col: 48 offset: 0008 + public ushort unknown8; + + // col: 18 offset: 000a + public byte unknowna; + + // col: 04 offset: 000c + public ushort unknownc; + + // col: 34 offset: 000e + public ushort unknowne; + + // col: 49 offset: 0010 + public ushort unknown10; + + // col: 19 offset: 0012 + public byte unknown12; + + // col: 05 offset: 0014 + public ushort unknown14; + + // col: 35 offset: 0016 + public ushort unknown16; + + // col: 50 offset: 0018 + public ushort unknown18; + + // col: 20 offset: 001a + public byte unknown1a; + + // col: 06 offset: 001c + public ushort unknown1c; + + // col: 36 offset: 001e + public ushort unknown1e; + + // col: 51 offset: 0020 + public ushort unknown20; + + // col: 21 offset: 0022 + public byte unknown22; + + // col: 07 offset: 0024 + public ushort unknown24; + + // col: 37 offset: 0026 + public ushort unknown26; + + // col: 52 offset: 0028 + public ushort unknown28; + + // col: 22 offset: 002a + public byte unknown2a; + + // col: 08 offset: 002c + public ushort unknown2c; + + // col: 38 offset: 002e + public ushort unknown2e; + + // col: 53 offset: 0030 + public ushort unknown30; + + // col: 23 offset: 0032 + public byte unknown32; + + // col: 09 offset: 0034 + public ushort unknown34; + + // col: 39 offset: 0036 + public ushort unknown36; + + // col: 54 offset: 0038 + public ushort unknown38; + + // col: 24 offset: 003a + public byte unknown3a; + + // col: 10 offset: 003c + public ushort unknown3c; + + // col: 40 offset: 003e + public ushort unknown3e; + + // col: 55 offset: 0040 + public ushort unknown40; + + // col: 25 offset: 0042 + public byte unknown42; + + // col: 11 offset: 0044 + public ushort unknown44; + + // col: 41 offset: 0046 + public ushort unknown46; + + // col: 56 offset: 0048 + public ushort unknown48; + + // col: 26 offset: 004a + public byte unknown4a; + + // col: 12 offset: 004c + public ushort unknown4c; + + // col: 42 offset: 004e + public ushort unknown4e; + + // col: 57 offset: 0050 + public ushort unknown50; + + // col: 27 offset: 0052 + public byte unknown52; + + // col: 13 offset: 0054 + public ushort unknown54; + + // col: 43 offset: 0056 + public ushort unknown56; + + // col: 58 offset: 0058 + public ushort unknown58; + + // col: 28 offset: 005a + public byte unknown5a; + + // col: 14 offset: 005c + public ushort unknown5c; + + // col: 44 offset: 005e + public ushort unknown5e; + + // col: 59 offset: 0060 + public ushort unknown60; + + // col: 29 offset: 0062 + public byte unknown62; + + // col: 15 offset: 0064 + public ushort unknown64; + + // col: 45 offset: 0066 + public ushort unknown66; + + // col: 60 offset: 0068 + public ushort unknown68; + + // col: 30 offset: 006a + public byte unknown6a; + + // col: 16 offset: 006c + public ushort unknown6c; + + // col: 46 offset: 006e + public ushort unknown6e; + + // col: 61 offset: 0070 + public ushort unknown70; + + // col: 31 offset: 0072 + public byte unknown72; + + // col: 17 offset: 0074 + public ushort unknown74; + + // col: 47 offset: 0076 + public ushort unknown76; + + // col: 62 offset: 0078 + public ushort unknown78; + + // col: 32 offset: 007a + public byte unknown7a; + + // col: 00 offset: 007c + public float unknown7c; + + // col: 01 offset: 0080 + public ushort unknown80; + + + public int RowId { get; set; } + public int SubRowId { get; set; } + + public void PopulateData( RowParser parser, Lumina.Lumina lumina ) + { + RowId = parser.Row; + SubRowId = parser.SubRow; + + // col: 2 offset: 0000 + Name = parser.ReadOffset< string >( 0x0 ); + + // col: 3 offset: 0004 + unknown4 = parser.ReadOffset< ushort >( 0x4 ); + + // col: 33 offset: 0006 + unknown6 = parser.ReadOffset< ushort >( 0x6 ); + + // col: 48 offset: 0008 + unknown8 = parser.ReadOffset< ushort >( 0x8 ); + + // col: 18 offset: 000a + unknowna = parser.ReadOffset< byte >( 0xa ); + + // col: 4 offset: 000c + unknownc = parser.ReadOffset< ushort >( 0xc ); + + // col: 34 offset: 000e + unknowne = parser.ReadOffset< ushort >( 0xe ); + + // col: 49 offset: 0010 + unknown10 = parser.ReadOffset< ushort >( 0x10 ); + + // col: 19 offset: 0012 + unknown12 = parser.ReadOffset< byte >( 0x12 ); + + // col: 5 offset: 0014 + unknown14 = parser.ReadOffset< ushort >( 0x14 ); + + // col: 35 offset: 0016 + unknown16 = parser.ReadOffset< ushort >( 0x16 ); + + // col: 50 offset: 0018 + unknown18 = parser.ReadOffset< ushort >( 0x18 ); + + // col: 20 offset: 001a + unknown1a = parser.ReadOffset< byte >( 0x1a ); + + // col: 6 offset: 001c + unknown1c = parser.ReadOffset< ushort >( 0x1c ); + + // col: 36 offset: 001e + unknown1e = parser.ReadOffset< ushort >( 0x1e ); + + // col: 51 offset: 0020 + unknown20 = parser.ReadOffset< ushort >( 0x20 ); + + // col: 21 offset: 0022 + unknown22 = parser.ReadOffset< byte >( 0x22 ); + + // col: 7 offset: 0024 + unknown24 = parser.ReadOffset< ushort >( 0x24 ); + + // col: 37 offset: 0026 + unknown26 = parser.ReadOffset< ushort >( 0x26 ); + + // col: 52 offset: 0028 + unknown28 = parser.ReadOffset< ushort >( 0x28 ); + + // col: 22 offset: 002a + unknown2a = parser.ReadOffset< byte >( 0x2a ); + + // col: 8 offset: 002c + unknown2c = parser.ReadOffset< ushort >( 0x2c ); + + // col: 38 offset: 002e + unknown2e = parser.ReadOffset< ushort >( 0x2e ); + + // col: 53 offset: 0030 + unknown30 = parser.ReadOffset< ushort >( 0x30 ); + + // col: 23 offset: 0032 + unknown32 = parser.ReadOffset< byte >( 0x32 ); + + // col: 9 offset: 0034 + unknown34 = parser.ReadOffset< ushort >( 0x34 ); + + // col: 39 offset: 0036 + unknown36 = parser.ReadOffset< ushort >( 0x36 ); + + // col: 54 offset: 0038 + unknown38 = parser.ReadOffset< ushort >( 0x38 ); + + // col: 24 offset: 003a + unknown3a = parser.ReadOffset< byte >( 0x3a ); + + // col: 10 offset: 003c + unknown3c = parser.ReadOffset< ushort >( 0x3c ); + + // col: 40 offset: 003e + unknown3e = parser.ReadOffset< ushort >( 0x3e ); + + // col: 55 offset: 0040 + unknown40 = parser.ReadOffset< ushort >( 0x40 ); + + // col: 25 offset: 0042 + unknown42 = parser.ReadOffset< byte >( 0x42 ); + + // col: 11 offset: 0044 + unknown44 = parser.ReadOffset< ushort >( 0x44 ); + + // col: 41 offset: 0046 + unknown46 = parser.ReadOffset< ushort >( 0x46 ); + + // col: 56 offset: 0048 + unknown48 = parser.ReadOffset< ushort >( 0x48 ); + + // col: 26 offset: 004a + unknown4a = parser.ReadOffset< byte >( 0x4a ); + + // col: 12 offset: 004c + unknown4c = parser.ReadOffset< ushort >( 0x4c ); + + // col: 42 offset: 004e + unknown4e = parser.ReadOffset< ushort >( 0x4e ); + + // col: 57 offset: 0050 + unknown50 = parser.ReadOffset< ushort >( 0x50 ); + + // col: 27 offset: 0052 + unknown52 = parser.ReadOffset< byte >( 0x52 ); + + // col: 13 offset: 0054 + unknown54 = parser.ReadOffset< ushort >( 0x54 ); + + // col: 43 offset: 0056 + unknown56 = parser.ReadOffset< ushort >( 0x56 ); + + // col: 58 offset: 0058 + unknown58 = parser.ReadOffset< ushort >( 0x58 ); + + // col: 28 offset: 005a + unknown5a = parser.ReadOffset< byte >( 0x5a ); + + // col: 14 offset: 005c + unknown5c = parser.ReadOffset< ushort >( 0x5c ); + + // col: 44 offset: 005e + unknown5e = parser.ReadOffset< ushort >( 0x5e ); + + // col: 59 offset: 0060 + unknown60 = parser.ReadOffset< ushort >( 0x60 ); + + // col: 29 offset: 0062 + unknown62 = parser.ReadOffset< byte >( 0x62 ); + + // col: 15 offset: 0064 + unknown64 = parser.ReadOffset< ushort >( 0x64 ); + + // col: 45 offset: 0066 + unknown66 = parser.ReadOffset< ushort >( 0x66 ); + + // col: 60 offset: 0068 + unknown68 = parser.ReadOffset< ushort >( 0x68 ); + + // col: 30 offset: 006a + unknown6a = parser.ReadOffset< byte >( 0x6a ); + + // col: 16 offset: 006c + unknown6c = parser.ReadOffset< ushort >( 0x6c ); + + // col: 46 offset: 006e + unknown6e = parser.ReadOffset< ushort >( 0x6e ); + + // col: 61 offset: 0070 + unknown70 = parser.ReadOffset< ushort >( 0x70 ); + + // col: 31 offset: 0072 + unknown72 = parser.ReadOffset< byte >( 0x72 ); + + // col: 17 offset: 0074 + unknown74 = parser.ReadOffset< ushort >( 0x74 ); + + // col: 47 offset: 0076 + unknown76 = parser.ReadOffset< ushort >( 0x76 ); + + // col: 62 offset: 0078 + unknown78 = parser.ReadOffset< ushort >( 0x78 ); + + // col: 32 offset: 007a + unknown7a = parser.ReadOffset< byte >( 0x7a ); + + // col: 0 offset: 007c + unknown7c = parser.ReadOffset< float >( 0x7c ); + + // col: 1 offset: 0080 + unknown80 = parser.ReadOffset< ushort >( 0x80 ); + + + } + } +} diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs index 3c022d8ec..97e925b11 100644 --- a/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs +++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/AutoTranslatePayload.cs @@ -133,7 +133,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads "Mount" => this.dataResolver.GetExcelSheet().GetRow(ikey).Singular, "Pet" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, "PetAction" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, - // TODO: lumina doesn't have PetMirage + "PetMirage" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, "PlaceName" => this.dataResolver.GetExcelSheet().GetRow(ikey).Name, "Race" => this.dataResolver.GetExcelSheet().GetRow(ikey).Masculine, "TextCommand" => this.dataResolver.GetExcelSheet().GetRow(ikey).Command, From 2c73e07e367ae665fced9ce7b50af788c3726be6 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Sun, 26 Apr 2020 18:35:28 -0700 Subject: [PATCH 17/17] Add helper SeString ctor that just takes an array of payloads instead of requiring a list wrapper - simplifies some creation --- Dalamud/Game/Chat/SeStringHandling/SeString.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Dalamud/Game/Chat/SeStringHandling/SeString.cs b/Dalamud/Game/Chat/SeStringHandling/SeString.cs index 26e7e7342..0ca95b3e5 100644 --- a/Dalamud/Game/Chat/SeStringHandling/SeString.cs +++ b/Dalamud/Game/Chat/SeStringHandling/SeString.cs @@ -68,6 +68,15 @@ namespace Dalamud.Game.Chat.SeStringHandling Payloads = payloads; } + /// + /// Creates a new SeString from an ordered list of payloads. + /// + /// The Payload objects to make up this string. + public SeString(Payload[] payloads) + { + Payloads = new List(payloads); + } + /// /// Appends the contents of one SeString to this one. ///