refactor: obsolete SeStringManager, move into SeString

This commit is contained in:
goat 2021-08-28 19:02:18 +02:00
parent e5cad2edde
commit ca201e1a14
No known key found for this signature in database
GPG key ID: F18F057873895461
8 changed files with 223 additions and 139 deletions

View file

@ -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<Payload>(payloads);
}
/// <summary>
/// Gets a list of Payloads necessary to display the arrow link marker icon in chat
/// with the appropriate glow and coloring.
/// </summary>
/// <returns>A list of all the payloads required to insert the link marker.</returns>
public static IEnumerable<Payload> TextArrowPayloads => new List<Payload>(new Payload[]
{
new UIForegroundPayload(0x01F4),
new UIGlowPayload(0x01F5),
new TextPayload($"{(char)SeIconChar.LinkMarker}"),
UIGlowPayload.UIGlowOff,
UIForegroundPayload.UIForegroundOff,
});
/// <summary>
/// Gets the ordered list of payloads included in this SeString.
/// </summary>
@ -76,7 +94,179 @@ namespace Dalamud.Game.Text.SeStringHandling
/// </summary>
/// <param name="luminaString">The Lumina SeString.</param>
/// <returns>The re-parsed Dalamud SeString.</returns>
public static implicit operator SeString(Lumina.Text.SeString luminaString) => Service<SeStringManager>.Get().Parse(luminaString.RawData);
public static implicit operator SeString(Lumina.Text.SeString luminaString) => Parse(luminaString.RawData);
/// <summary>
/// Parse a binary game message into an SeString.
/// </summary>
/// <param name="ptr">Pointer to the string's data in memory.</param>
/// <param name="len">Length of the string's data in memory.</param>
/// <returns>An SeString containing parsed Payload objects for each payload in the data.</returns>
public static unsafe SeString Parse(byte* ptr, int len)
{
var payloads = new List<Payload>();
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);
}
/// <summary>
/// Parse a binary game message into an SeString.
/// </summary>
/// <param name="data">Binary message payload data in SE's internal format.</param>
/// <returns>An SeString containing parsed Payload objects for each payload in the data.</returns>
public static unsafe SeString Parse(ReadOnlySpan<byte> data)
{
fixed (byte* ptr = data)
{
return Parse(ptr, data.Length);
}
}
/// <summary>
/// Parse a binary game message into an SeString.
/// </summary>
/// <param name="bytes">Binary message payload data in SE's internal format.</param>
/// <returns>An SeString containing parsed Payload objects for each payload in the data.</returns>
public static SeString Parse(byte[] bytes) => Parse(new ReadOnlySpan<byte>(bytes));
/// <summary>
/// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log.
/// </summary>
/// <param name="itemId">The id of the item to link.</param>
/// <param name="isHq">Whether to link the high-quality variant of the item.</param>
/// <param name="displayNameOverride">An optional name override to display, instead of the actual item name.</param>
/// <returns>An SeString containing all the payloads necessary to display an item link in the chat log.</returns>
public static SeString CreateItemLink(uint itemId, bool isHq, string? displayNameOverride = null)
{
var data = Service<DataManager>.Get();
var displayName = displayNameOverride ?? data.GetExcelSheet<Item>()?.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<Payload>(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);
}
/// <summary>
/// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log.
/// </summary>
/// <param name="item">The Lumina Item to link.</param>
/// <param name="isHq">Whether to link the high-quality variant of the item.</param>
/// <param name="displayNameOverride">An optional name override to display, instead of the actual item name.</param>
/// <returns>An SeString containing all the payloads necessary to display an item link in the chat log.</returns>
public static SeString CreateItemLink(Item item, bool isHq, string? displayNameOverride = null)
{
return CreateItemLink(item.RowId, isHq, displayNameOverride ?? item.Name);
}
/// <summary>
/// Creates an SeString representing an entire Payload chain that can be used to link a map position in the chat log.
/// </summary>
/// <param name="territoryId">The id of the TerritoryType for this map link.</param>
/// <param name="mapId">The id of the Map for this map link.</param>
/// <param name="rawX">The raw x-coordinate for this link.</param>
/// <param name="rawY">The raw y-coordinate for this link..</param>
/// <returns>An SeString containing all of the payloads necessary to display a map link in the chat log.</returns>
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<Payload>(new Payload[]
{
mapPayload,
// arrow goes here
new TextPayload(nameString),
RawPayload.LinkTerminator,
});
payloads.InsertRange(1, TextArrowPayloads);
return new SeString(payloads);
}
/// <summary>
/// Creates an SeString representing an entire Payload chain that can be used to link a map position in the chat log.
/// </summary>
/// <param name="territoryId">The id of the TerritoryType for this map link.</param>
/// <param name="mapId">The id of the Map for this map link.</param>
/// <param name="xCoord">The human-readable x-coordinate for this link.</param>
/// <param name="yCoord">The human-readable y-coordinate for this link.</param>
/// <param name="fudgeFactor">An optional offset to account for rounding and truncation errors; it is best to leave this untouched in most cases.</param>
/// <returns>An SeString containing all of the payloads necessary to display a map link in the chat log.</returns>
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<Payload>(new Payload[]
{
mapPayload,
// arrow goes here
new TextPayload(nameString),
RawPayload.LinkTerminator,
});
payloads.InsertRange(1, TextArrowPayloads);
return new SeString(payloads);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="placeName">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.</param>
/// <param name="xCoord">The human-readable x-coordinate for this link.</param>
/// <param name="yCoord">The human-readable y-coordinate for this link.</param>
/// <param name="fudgeFactor">An optional offset to account for rounding and truncation errors; it is best to leave this untouched in most cases.</param>
/// <returns>An SeString containing all of the payloads necessary to display a map link in the chat log.</returns>
public static SeString? CreateMapLink(string placeName, float xCoord, float yCoord, float fudgeFactor = 0.05f)
{
var data = Service<DataManager>.Get();
var mapSheet = data.GetExcelSheet<Map>();
var matches = data.GetExcelSheet<PlaceName>()
.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;
}
/// <summary>
/// Creates a SeString from a json. (For testing - not recommended for production use.)