using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.RegularExpressions; using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Plugin.Services; using Dalamud.Utility; using Lumina.Excel; using Lumina.Excel.Sheets; using Lumina.Text; using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; namespace LLib; public static class DataManagerExtensions { public static ReadOnlySeString? GetSeString(this IDataManager dataManager, string key) where T : struct, IQuestDialogueText, IExcelRow { ArgumentNullException.ThrowIfNull(dataManager); return dataManager.GetExcelSheet() .Cast() .SingleOrDefault(x => x!.Value.Key == key) ?.Value; } public static string? GetString(this IDataManager dataManager, string key, IPluginLog? pluginLog) where T : struct, IQuestDialogueText, IExcelRow { string? text = GetSeString(dataManager, key)?.WithCertainMacroCodeReplacements(); pluginLog?.Verbose($"{typeof(T).Name}.{key} => {text}"); return text; } public static Regex? GetRegex(this IDataManager dataManager, string key, IPluginLog? pluginLog) where T : struct, IQuestDialogueText, IExcelRow { ReadOnlySeString? text = GetSeString(dataManager, key); if (text == null) return null; string regex = string.Join("", text.Select((ReadOnlySePayload payload) => { if (payload.Type == ReadOnlySePayloadType.Text) return Regex.Escape(payload.ToString()); else return "(.*)"; })); pluginLog?.Verbose($"{typeof(T).Name}.{key} => /{regex}/"); return new Regex(regex); } public static ReadOnlySeString? GetSeString(this IDataManager dataManager, uint rowId, Func mapper) where T : struct, IExcelRow { ArgumentNullException.ThrowIfNull(dataManager); ArgumentNullException.ThrowIfNull(mapper); var row = dataManager.GetExcelSheet().GetRowOrDefault(rowId); if (row == null) return null; return mapper(row.Value); } public static string? GetString(this IDataManager dataManager, uint rowId, Func mapper, IPluginLog? pluginLog = null) where T : struct, IExcelRow { string? text = GetSeString(dataManager, rowId, mapper)?.WithCertainMacroCodeReplacements(); pluginLog?.Verbose($"{typeof(T).Name}.{rowId} => {text}"); return text; } public static Regex? GetRegex(this IDataManager dataManager, uint rowId, Func mapper, IPluginLog? pluginLog = null) where T : struct, IExcelRow { ReadOnlySeString? text = GetSeString(dataManager, rowId, mapper); if (text == null) return null; Regex regex = text.ToRegex(); pluginLog?.Verbose($"{typeof(T).Name}.{rowId} => /{regex}/"); return regex; } public static Regex? GetRegex(this T excelRow, Func mapper, IPluginLog? pluginLog) where T : struct, IExcelRow { ArgumentNullException.ThrowIfNull(excelRow); ArgumentNullException.ThrowIfNull(mapper); ReadOnlySeString? text = mapper(excelRow); if (text == null) return null; Regex regex = text.ToRegex(); pluginLog?.Verbose($"{typeof(T).Name}.regex => /{regex}/"); return regex; } public static Regex ToRegex(this ReadOnlySeString? text) { ArgumentNullException.ThrowIfNull(text); return new Regex(string.Join("", text.Value.Select(payload => { if (payload.Type == ReadOnlySePayloadType.Text) return Regex.Escape(payload.ToString()); else return "(.*)"; }))); } public static string WithCertainMacroCodeReplacements(this ReadOnlySeString text) { return string.Join("", text.Select(payload => { if (payload.Type == ReadOnlySePayloadType.Text) return Regex.Escape(payload.ToString()); else if (payload is { Type: ReadOnlySePayloadType.Macro, MacroCode: MacroCode.NewLine }) return ""; else return payload.ToString(); })); } } public interface IQuestDialogueText { public ReadOnlySeString Key { get; } public ReadOnlySeString Value { get; } } [SuppressMessage("Performance", "CA1815")] [Sheet("PleaseSpecifyTheSheetExplicitly")] public readonly struct QuestDialogueText(ExcelPage page, uint offset, uint row) : IQuestDialogueText, IExcelRow { public uint RowId => row; public ReadOnlySeString Key => page.ReadString(offset, offset); public ReadOnlySeString Value => page.ReadString(offset + 4, offset); static QuestDialogueText IExcelRow.Create(ExcelPage page, uint offset, uint row) => new(page, offset, row); }