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}");