mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
232 lines
9 KiB
C#
232 lines
9 KiB
C#
using System.Linq;
|
|
|
|
using InteropGenerator.Runtime;
|
|
|
|
using Lumina.Text.Parse;
|
|
|
|
using Lumina.Text.ReadOnly;
|
|
|
|
using DSeString = Dalamud.Game.Text.SeStringHandling.SeString;
|
|
using DSeStringBuilder = Dalamud.Game.Text.SeStringHandling.SeStringBuilder;
|
|
using LSeString = Lumina.Text.SeString;
|
|
using LSeStringBuilder = Lumina.Text.SeStringBuilder;
|
|
|
|
namespace Dalamud.Utility;
|
|
|
|
/// <summary>
|
|
/// Extension methods for SeStrings.
|
|
/// </summary>
|
|
public static class SeStringExtensions
|
|
{
|
|
/// <summary>
|
|
/// Convert a Lumina SeString into a Dalamud SeString.
|
|
/// This conversion re-parses the string.
|
|
/// </summary>
|
|
/// <param name="originalString">The original Lumina SeString.</param>
|
|
/// <returns>The re-parsed Dalamud SeString.</returns>
|
|
[Obsolete("Switch to using ReadOnlySeString instead of Lumina's SeString.", true)]
|
|
public static DSeString ToDalamudString(this LSeString originalString) => DSeString.Parse(originalString.RawData);
|
|
|
|
/// <summary>
|
|
/// Convert a Lumina ReadOnlySeString into a Dalamud SeString.
|
|
/// This conversion re-parses the string.
|
|
/// </summary>
|
|
/// <param name="originalString">The original Lumina ReadOnlySeString.</param>
|
|
/// <returns>The re-parsed Dalamud SeString.</returns>
|
|
public static DSeString ToDalamudString(this ReadOnlySeString originalString) => DSeString.Parse(originalString.Data.Span);
|
|
|
|
/// <summary>
|
|
/// Convert a Lumina ReadOnlySeStringSpan into a Dalamud SeString.
|
|
/// This conversion re-parses the string.
|
|
/// </summary>
|
|
/// <param name="originalString">The original Lumina ReadOnlySeStringSpan.</param>
|
|
/// <returns>The re-parsed Dalamud SeString.</returns>
|
|
public static DSeString ToDalamudString(this ReadOnlySeStringSpan originalString) => DSeString.Parse(originalString.Data);
|
|
|
|
/// <summary>Compiles and appends a macro string.</summary>
|
|
/// <param name="ssb">Target SeString builder.</param>
|
|
/// <param name="macroString">Macro string in UTF-8 to compile and append to <paramref name="ssb"/>.</param>
|
|
/// <returns><c>this</c> for method chaining.</returns>
|
|
public static DSeStringBuilder AppendMacroString(this DSeStringBuilder ssb, ReadOnlySpan<byte> macroString)
|
|
{
|
|
var lssb = LSeStringBuilder.SharedPool.Get();
|
|
lssb.AppendMacroString(macroString, new() { ExceptionMode = MacroStringParseExceptionMode.EmbedError });
|
|
ssb.Append(DSeString.Parse(lssb.ToReadOnlySeString().Data.Span));
|
|
LSeStringBuilder.SharedPool.Return(lssb);
|
|
return ssb;
|
|
}
|
|
|
|
/// <summary>Compiles and appends a macro string.</summary>
|
|
/// <param name="ssb">Target SeString builder.</param>
|
|
/// <param name="macroString">Macro string in UTF-16 to compile and append to <paramref name="ssb"/>.</param>
|
|
/// <returns><c>this</c> for method chaining.</returns>
|
|
public static DSeStringBuilder AppendMacroString(this DSeStringBuilder ssb, ReadOnlySpan<char> macroString)
|
|
{
|
|
var lssb = LSeStringBuilder.SharedPool.Get();
|
|
lssb.AppendMacroString(macroString, new() { ExceptionMode = MacroStringParseExceptionMode.EmbedError });
|
|
ssb.Append(DSeString.Parse(lssb.ToReadOnlySeString().Data.Span));
|
|
LSeStringBuilder.SharedPool.Return(lssb);
|
|
return ssb;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate if character name is valid.
|
|
/// Both forename and surname must be between 2 and 15 characters and not total more than 20 characters combined.
|
|
/// Only letters, hyphens, and apostrophes can be used.
|
|
/// The first character of either name must be a letter.
|
|
/// Hyphens cannot be used in succession or placed immediately before or after apostrophes.
|
|
/// </summary>
|
|
/// <param name="value">character name to validate.</param>
|
|
/// <returns>indicator if character is name is valid.</returns>
|
|
public static bool IsValidCharacterName(this DSeString value) => value.ToString().IsValidCharacterName();
|
|
|
|
/// <summary>
|
|
/// Determines whether the <see cref="ReadOnlySeString"/> contains only text payloads.
|
|
/// </summary>
|
|
/// <param name="ross">The <see cref="ReadOnlySeString"/> to check.</param>
|
|
/// <returns><c>true</c> if the string contains only text payloads; otherwise, <c>false</c>.</returns>
|
|
public static bool IsTextOnly(this ReadOnlySeString ross)
|
|
{
|
|
return ross.AsSpan().IsTextOnly();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether the <see cref="ReadOnlySeStringSpan"/> contains only text payloads.
|
|
/// </summary>
|
|
/// <param name="rosss">The <see cref="ReadOnlySeStringSpan"/> to check.</param>
|
|
/// <returns><c>true</c> if the span contains only text payloads; otherwise, <c>false</c>.</returns>
|
|
public static bool IsTextOnly(this ReadOnlySeStringSpan rosss)
|
|
{
|
|
foreach (var payload in rosss)
|
|
{
|
|
if (payload.Type != ReadOnlySePayloadType.Text)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether the <see cref="ReadOnlySeString"/> contains the specified text.
|
|
/// </summary>
|
|
/// <param name="ross">The <see cref="ReadOnlySeString"/> to search.</param>
|
|
/// <param name="needle">The text to find.</param>
|
|
/// <returns><c>true</c> if the text is found; otherwise, <c>false</c>.</returns>
|
|
public static bool ContainsText(this ReadOnlySeString ross, ReadOnlySpan<byte> needle)
|
|
{
|
|
return ross.AsSpan().ContainsText(needle);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether the <see cref="ReadOnlySeStringSpan"/> contains the specified text.
|
|
/// </summary>
|
|
/// <param name="rosss">The <see cref="ReadOnlySeStringSpan"/> to search.</param>
|
|
/// <param name="needle">The text to find.</param>
|
|
/// <returns><c>true</c> if the text is found; otherwise, <c>false</c>.</returns>
|
|
public static bool ContainsText(this ReadOnlySeStringSpan rosss, ReadOnlySpan<byte> needle)
|
|
{
|
|
foreach (var payload in rosss)
|
|
{
|
|
if (payload.Type != ReadOnlySePayloadType.Text)
|
|
continue;
|
|
|
|
if (payload.Body.IndexOf(needle) != -1)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether the <see cref="LSeStringBuilder"/> contains the specified text.
|
|
/// </summary>
|
|
/// <param name="builder">The builder to search.</param>
|
|
/// <param name="needle">The text to find.</param>
|
|
/// <returns><c>true</c> if the text is found; otherwise, <c>false</c>.</returns>
|
|
public static bool ContainsText(this LSeStringBuilder builder, ReadOnlySpan<byte> needle)
|
|
{
|
|
return builder.ToReadOnlySeString().ContainsText(needle);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Replaces occurrences of a specified text in a <see cref="ReadOnlySeString"/> with another text.
|
|
/// </summary>
|
|
/// <param name="ross">The original string.</param>
|
|
/// <param name="toFind">The text to find.</param>
|
|
/// <param name="replacement">The replacement text.</param>
|
|
/// <returns>A new <see cref="ReadOnlySeString"/> with the replacements made.</returns>
|
|
public static ReadOnlySeString ReplaceText(
|
|
this ReadOnlySeString ross,
|
|
ReadOnlySpan<byte> toFind,
|
|
ReadOnlySpan<byte> replacement)
|
|
{
|
|
if (ross.IsEmpty)
|
|
return ross;
|
|
|
|
var sb = LSeStringBuilder.SharedPool.Get();
|
|
|
|
foreach (var payload in ross)
|
|
{
|
|
if (payload.Type == ReadOnlySePayloadType.Invalid)
|
|
continue;
|
|
|
|
if (payload.Type != ReadOnlySePayloadType.Text)
|
|
{
|
|
sb.Append(payload);
|
|
continue;
|
|
}
|
|
|
|
var index = payload.Body.Span.IndexOf(toFind);
|
|
if (index == -1)
|
|
{
|
|
sb.Append(payload);
|
|
continue;
|
|
}
|
|
|
|
var lastIndex = 0;
|
|
while (index != -1)
|
|
{
|
|
sb.Append(payload.Body.Span[lastIndex..index]);
|
|
|
|
if (!replacement.IsEmpty)
|
|
{
|
|
sb.Append(replacement);
|
|
}
|
|
|
|
lastIndex = index + toFind.Length;
|
|
index = payload.Body.Span[lastIndex..].IndexOf(toFind);
|
|
|
|
if (index != -1)
|
|
index += lastIndex;
|
|
}
|
|
|
|
sb.Append(payload.Body.Span[lastIndex..]);
|
|
}
|
|
|
|
var output = sb.ToReadOnlySeString();
|
|
LSeStringBuilder.SharedPool.Return(sb);
|
|
return output;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Replaces occurrences of a specified text in an <see cref="LSeStringBuilder"/> with another text.
|
|
/// </summary>
|
|
/// <param name="builder">The builder to modify.</param>
|
|
/// <param name="toFind">The text to find.</param>
|
|
/// <param name="replacement">The replacement text.</param>
|
|
public static void ReplaceText(
|
|
this LSeStringBuilder builder,
|
|
ReadOnlySpan<byte> toFind,
|
|
ReadOnlySpan<byte> replacement)
|
|
{
|
|
if (toFind.IsEmpty)
|
|
return;
|
|
|
|
var str = builder.ToReadOnlySeString();
|
|
if (str.IsEmpty)
|
|
return;
|
|
|
|
var replaced = ReplaceText(new ReadOnlySeString(builder.GetViewAsMemory()), toFind, replacement);
|
|
builder.Clear().Append(replaced);
|
|
}
|
|
}
|