diff --git a/Dalamud.Test/Game/Text/SeStringHandling/SeStringManagerTests.cs b/Dalamud.Test/Game/Text/SeStringHandling/SeStringManagerTests.cs index f9f7b05b8..5b91bf961 100644 --- a/Dalamud.Test/Game/Text/SeStringHandling/SeStringManagerTests.cs +++ b/Dalamud.Test/Game/Text/SeStringHandling/SeStringManagerTests.cs @@ -9,11 +9,10 @@ namespace Dalamud.Test.Game.Text.SeStringHandling [Fact] public void TestNewLinePayload() { - var manager = new SeStringManager(); var newLinePayloadBytes = new byte[] {0x02, 0x10, 0x01, 0x03}; - - var seString = manager.Parse(newLinePayloadBytes); - + + var seString = SeString.Parse(newLinePayloadBytes); + Assert.True(newLinePayloadBytes.SequenceEqual(seString.Encode())); } } diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index a435e24d7..7a5c1e2fc 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -196,10 +196,6 @@ namespace Dalamud Log.Information("[T2] Data OK!"); - Service.Set(); - - Log.Information("[T2] SeString OK!"); - // Initialize managers. Basically handlers for the logic Service.Set(); diff --git a/Dalamud/Game/Gui/ChatGui.cs b/Dalamud/Game/Gui/ChatGui.cs index 0d0fc5ebc..8dd92e1dd 100644 --- a/Dalamud/Game/Gui/ChatGui.cs +++ b/Dalamud/Game/Gui/ChatGui.cs @@ -356,8 +356,8 @@ namespace Dalamud.Game.Gui var sender = StdString.ReadFromPointer(pSenderName); var message = StdString.ReadFromPointer(pMessage); - var parsedSender = Service.Get().Parse(sender.RawData); - var parsedMessage = Service.Get().Parse(message.RawData); + var parsedSender = SeString.Parse(sender.RawData); + var parsedMessage = SeString.Parse(message.RawData); Log.Verbose("[CHATGUI][{0}][{1}]", parsedSender.TextValue, parsedMessage.TextValue); @@ -439,7 +439,7 @@ namespace Dalamud.Game.Gui while (Marshal.ReadByte(payloadPtr, messageSize) != 0) messageSize++; var payloadBytes = new byte[messageSize]; Marshal.Copy(payloadPtr, payloadBytes, 0, messageSize); - var seStr = Service.Get().Parse(payloadBytes); + var seStr = SeString.Parse(payloadBytes); var terminatorIndex = seStr.Payloads.IndexOf(RawPayload.LinkTerminator); var payloads = terminatorIndex >= 0 ? seStr.Payloads.Take(terminatorIndex + 1).ToList() : seStr.Payloads; if (payloads.Count == 0) return; diff --git a/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListing.cs b/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListing.cs index c03a2dfcd..ae885168a 100644 --- a/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListing.cs +++ b/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListing.cs @@ -29,7 +29,6 @@ namespace Dalamud.Game.Gui.PartyFinder.Types internal PartyFinderListing(PartyFinderPacketListing listing) { var dataManager = Service.Get(); - var seStringManager = Service.Get(); this.objective = listing.Objective; this.conditions = listing.Conditions; @@ -41,8 +40,8 @@ namespace Dalamud.Game.Gui.PartyFinder.Types this.Id = listing.Id; this.ContentIdLower = listing.ContentIdLower; - this.Name = seStringManager.Parse(listing.Name.TakeWhile(b => b != 0).ToArray()); - this.Description = seStringManager.Parse(listing.Description.TakeWhile(b => b != 0).ToArray()); + this.Name = SeString.Parse(listing.Name.TakeWhile(b => b != 0).ToArray()); + this.Description = SeString.Parse(listing.Description.TakeWhile(b => b != 0).ToArray()); this.World = new Lazy(() => dataManager.GetExcelSheet().GetRow(listing.World)); this.HomeWorld = new Lazy(() => dataManager.GetExcelSheet().GetRow(listing.HomeWorld)); this.CurrentWorld = new Lazy(() => dataManager.GetExcelSheet().GetRow(listing.CurrentWorld)); diff --git a/Dalamud/Game/Gui/Toast/ToastGui.cs b/Dalamud/Game/Gui/Toast/ToastGui.cs index ce578ba0b..ca24df197 100644 --- a/Dalamud/Game/Gui/Toast/ToastGui.cs +++ b/Dalamud/Game/Gui/Toast/ToastGui.cs @@ -164,7 +164,7 @@ namespace Dalamud.Game.Gui.Toast } // call events - return Service.Get().Parse(bytes.ToArray()); + return SeString.Parse(bytes.ToArray()); } } diff --git a/Dalamud/Game/Text/SeStringHandling/SeString.cs b/Dalamud/Game/Text/SeStringHandling/SeString.cs index e0a3907e9..76efcf707 100644 --- a/Dalamud/Game/Text/SeStringHandling/SeString.cs +++ b/Dalamud/Game/Text/SeStringHandling/SeString.cs @@ -1,8 +1,12 @@ +using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; +using Dalamud.Data; using Dalamud.Game.Text.SeStringHandling.Payloads; +using Lumina.Excel.GeneratedSheets; using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling @@ -42,6 +46,20 @@ namespace Dalamud.Game.Text.SeStringHandling this.Payloads = new List(payloads); } + /// + /// Gets 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 IEnumerable TextArrowPayloads => new List(new Payload[] + { + new UIForegroundPayload(0x01F4), + new UIGlowPayload(0x01F5), + new TextPayload($"{(char)SeIconChar.LinkMarker}"), + UIGlowPayload.UIGlowOff, + UIForegroundPayload.UIForegroundOff, + }); + /// /// Gets the ordered list of payloads included in this SeString. /// @@ -76,7 +94,179 @@ namespace Dalamud.Game.Text.SeStringHandling /// /// The Lumina SeString. /// The re-parsed Dalamud SeString. - public static implicit operator SeString(Lumina.Text.SeString luminaString) => Service.Get().Parse(luminaString.RawData); + public static implicit operator SeString(Lumina.Text.SeString luminaString) => Parse(luminaString.RawData); + + /// + /// Parse a binary game message into an SeString. + /// + /// Pointer to the string's data in memory. + /// Length of the string's data in memory. + /// An SeString containing parsed Payload objects for each payload in the data. + public static unsafe SeString Parse(byte* ptr, int len) + { + var payloads = new List(); + + using (var stream = new UnmanagedMemoryStream(ptr, len)) + using (var reader = new BinaryReader(stream)) + { + while (stream.Position < len) + { + var payload = Payload.Decode(reader); + if (payload != null) + payloads.Add(payload); + } + } + + return new SeString(payloads); + } + + /// + /// 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 unsafe SeString Parse(ReadOnlySpan data) + { + fixed (byte* ptr = data) + { + return Parse(ptr, data.Length); + } + } + + /// + /// 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) => Parse(new ReadOnlySpan(bytes)); + + /// + /// 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) + { + var data = Service.Get(); + + var displayName = displayNameOverride ?? data.GetExcelSheet()?.GetRow(itemId)?.Name; + if (isHq) + { + displayName += $" {(char)SeIconChar.HighQuality}"; + } + + // 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), + // 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); + } + + /// + /// 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(Item item, bool isHq, string? displayNameOverride = null) + { + return CreateItemLink(item.RowId, isHq, displayNameOverride ?? item.Name); + } + + /// + /// 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 raw x-coordinate for this link. + /// The raw y-coordinate for this link.. + /// 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, 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); + 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, 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 data = Service.Get(); + + var mapSheet = data.GetExcelSheet(); + + var matches = data.GetExcelSheet() + .Where(row => row.Name.ToString().ToLowerInvariant() == placeName.ToLowerInvariant()) + .ToArray(); + + foreach (var place in matches) + { + var map = mapSheet.FirstOrDefault(row => row.PlaceName.Row == place.RowId); + if (map != null) + { + return CreateMapLink(map.TerritoryType.Row, map.RowId, xCoord, yCoord, fudgeFactor); + } + } + + // TODO: empty? throw? + return null; + } /// /// Creates a SeString from a json. (For testing - not recommended for production use.) diff --git a/Dalamud/Game/Text/SeStringHandling/SeStringManager.cs b/Dalamud/Game/Text/SeStringHandling/SeStringManager.cs index ee856f708..d46aa0c70 100644 --- a/Dalamud/Game/Text/SeStringHandling/SeStringManager.cs +++ b/Dalamud/Game/Text/SeStringHandling/SeStringManager.cs @@ -16,6 +16,7 @@ namespace Dalamud.Game.Text.SeStringHandling /// [PluginInterface] [InterfaceVersion("1.0")] + [Obsolete("This class is obsolete. Please use the static methods on SeString instead.", true)] public sealed class SeStringManager { /// @@ -31,43 +32,24 @@ namespace Dalamud.Game.Text.SeStringHandling /// Pointer to the string's data in memory. /// Length of the string's data in memory. /// An SeString containing parsed Payload objects for each payload in the data. - public unsafe SeString Parse(byte* ptr, int len) - { - var payloads = new List(); - - using (var stream = new UnmanagedMemoryStream(ptr, len)) - using (var reader = new BinaryReader(stream)) - { - while (stream.Position < len) - { - var payload = Payload.Decode(reader); - if (payload != null) - payloads.Add(payload); - } - } - - return new SeString(payloads); - } + [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] + public unsafe SeString Parse(byte* ptr, int len) => SeString.Parse(ptr, len); /// /// 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 unsafe SeString Parse(ReadOnlySpan data) - { - fixed (byte* ptr = data) - { - return this.Parse(ptr, data.Length); - } - } + [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] + public unsafe SeString Parse(ReadOnlySpan data) => SeString.Parse(data); /// /// 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 SeString Parse(byte[] bytes) => this.Parse(new ReadOnlySpan(bytes)); + [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] + public SeString Parse(byte[] bytes) => SeString.Parse(new ReadOnlySpan(bytes)); /// /// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log. @@ -76,32 +58,8 @@ namespace Dalamud.Game.Text.SeStringHandling /// 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 SeString CreateItemLink(uint itemId, bool isHQ, string displayNameOverride = null) - { - var data = Service.Get(); - - var displayName = displayNameOverride ?? data.GetExcelSheet().GetRow(itemId).Name; - if (isHQ) - { - displayName += $" {(char)SeIconChar.HighQuality}"; - } - - // 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), - // 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, this.TextArrowPayloads()); - - return new SeString(payloads); - } + [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] + public SeString CreateItemLink(uint itemId, bool isHQ, string displayNameOverride = null) => SeString.CreateItemLink(itemId, isHQ, displayNameOverride); /// /// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log. @@ -110,10 +68,8 @@ namespace Dalamud.Game.Text.SeStringHandling /// 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 SeString CreateItemLink(Item item, bool isHQ, string displayNameOverride = null) - { - return this.CreateItemLink(item.RowId, isHQ, displayNameOverride ?? item.Name); - } + [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] + public SeString CreateItemLink(Item item, bool isHQ, string displayNameOverride = null) => SeString.CreateItemLink(item, isHQ, displayNameOverride); /// /// Creates an SeString representing an entire Payload chain that can be used to link a map position in the chat log. @@ -123,22 +79,9 @@ namespace Dalamud.Game.Text.SeStringHandling /// The raw x-coordinate for this link. /// The raw y-coordinate for this link.. /// An SeString containing all of the payloads necessary to display a map link in the chat log. - public 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, this.TextArrowPayloads()); - - return new SeString(payloads); - } + [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] + public SeString CreateMapLink(uint territoryId, uint mapId, int rawX, int rawY) => + SeString.CreateMapLink(territoryId, mapId, rawX, rawY); /// /// Creates an SeString representing an entire Payload chain that can be used to link a map position in the chat log. @@ -149,22 +92,8 @@ namespace Dalamud.Game.Text.SeStringHandling /// 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 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, - // arrow goes here - new TextPayload(nameString), - RawPayload.LinkTerminator, - }); - payloads.InsertRange(1, this.TextArrowPayloads()); - - return new SeString(payloads); - } + [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] + public SeString CreateMapLink(uint territoryId, uint mapId, float xCoord, float yCoord, float fudgeFactor = 0.05f) => SeString.CreateMapLink(territoryId, mapId, xCoord, yCoord, fudgeFactor); /// /// 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. @@ -174,44 +103,15 @@ namespace Dalamud.Game.Text.SeStringHandling /// 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 SeString CreateMapLink(string placeName, float xCoord, float yCoord, float fudgeFactor = 0.05f) - { - var data = Service.Get(); - - var mapSheet = data.GetExcelSheet(); - - var matches = data.GetExcelSheet() - .Where(row => row.Name.ToString().ToLowerInvariant() == placeName.ToLowerInvariant()) - .ToArray(); - - foreach (var place in matches) - { - var map = mapSheet.FirstOrDefault(row => row.PlaceName.Row == place.RowId); - if (map != null) - { - return this.CreateMapLink(map.TerritoryType.Row, map.RowId, xCoord, yCoord, fudgeFactor); - } - } - - // TODO: empty? throw? - return null; - } + [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] + public SeString CreateMapLink(string placeName, float xCoord, float yCoord, float fudgeFactor = 0.05f) => SeString.CreateMapLink(placeName, xCoord, yCoord, fudgeFactor); /// /// 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 List TextArrowPayloads() - { - return new List(new Payload[] - { - new UIForegroundPayload(0x01F4), - new UIGlowPayload(0x01F5), - new TextPayload($"{(char)SeIconChar.LinkMarker}"), - UIGlowPayload.UIGlowOff, - UIForegroundPayload.UIForegroundOff, - }); - } + [Obsolete("This data is obsolete. Please use the static version on SeString instead.", true)] + public List TextArrowPayloads() => new(SeString.TextArrowPayloads); } } diff --git a/Dalamud/Memory/MemoryHelper.cs b/Dalamud/Memory/MemoryHelper.cs index e76bfae25..0a4b840ef 100644 --- a/Dalamud/Memory/MemoryHelper.cs +++ b/Dalamud/Memory/MemoryHelper.cs @@ -230,7 +230,7 @@ namespace Dalamud.Memory public static SeString ReadSeStringNullTerminated(IntPtr memoryAddress) { var buffer = ReadRawNullTerminated(memoryAddress); - return Service.Get().Parse(buffer); + return SeString.Parse(buffer); } /// @@ -246,13 +246,13 @@ namespace Dalamud.Memory var eos = Array.IndexOf(buffer, (byte)0); if (eos < 0) { - return Service.Get().Parse(buffer); + return SeString.Parse(buffer); } else { var newBuffer = new byte[eos]; Buffer.BlockCopy(buffer, 0, newBuffer, 0, eos); - return Service.Get().Parse(newBuffer); + return SeString.Parse(newBuffer); } }