Add SeStringEvaluator service (#2188)

* Add SeStringEvaluator service

* Move DrawCopyableText into WidgetUtil

* Use Icon2RemapTable in SeStringRenderer

* Beautify some code

* Make sure to use the correct language

* Add SeString Creator widget

* Fix getting local parameters

* Update expressionNames

* misc changes

* Use InvariantCulture in TryResolveSheet

* Add SeStringEvaluatorAgingStep

* Fix item id comparisons

* Add SheetRedirectResolverAgingStep

* Add NounProcessorAgingStep

* Update SeString.CreateItemLink

This also adds the internal ItemUtil class.

* Fix name of SeStringCreator widget

* Add Global Parameters tab to SeStringCreatorWidget

* Load widgets on demand

* Update SeStringCreatorWidget

* Resizable SeStringCreatorWidget panels

* Update GamepadStateAgingStep

* Experimental status was removed in #2144

* Update SheetRedirectResolver, rewrite Noun params

* Fixes for 4 am code

* Remove incorrect column offset

I have no idea how that happened.

* Draw names of linked things

---------

Co-authored-by: Soreepeong <3614868+Soreepeong@users.noreply.github.com>
Co-authored-by: KazWolfe <KazWolfe@users.noreply.github.com>
This commit is contained in:
Haselnussbomber 2025-03-24 17:00:27 +01:00 committed by GitHub
parent 7cac19ce81
commit fdbfdbb2cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 5831 additions and 196 deletions

View file

@ -47,11 +47,13 @@ internal class DataWindow : Window, IDisposable
new KeyStateWidget(),
new MarketBoardWidget(),
new NetworkMonitorWidget(),
new NounProcessorWidget(),
new ObjectTableWidget(),
new PartyListWidget(),
new PluginIpcWidget(),
new SeFontTestWidget(),
new ServicesWidget(),
new SeStringCreatorWidget(),
new SeStringRendererTestWidget(),
new StartInfoWidget(),
new TargetWidget(),
@ -68,6 +70,7 @@ internal class DataWindow : Window, IDisposable
private bool isExcept;
private bool selectionCollapsed;
private IDataWindowWidget currentWidget;
private bool isLoaded;
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
@ -81,8 +84,6 @@ internal class DataWindow : Window, IDisposable
this.RespectCloseHotkey = false;
this.orderedModules = this.modules.OrderBy(module => module.DisplayName);
this.currentWidget = this.orderedModules.First();
this.Load();
}
/// <inheritdoc/>
@ -91,6 +92,7 @@ internal class DataWindow : Window, IDisposable
/// <inheritdoc/>
public override void OnOpen()
{
this.Load();
}
/// <inheritdoc/>
@ -183,6 +185,7 @@ internal class DataWindow : Window, IDisposable
if (ImGuiComponents.IconButton("forceReload", FontAwesomeIcon.Sync))
{
this.isLoaded = false;
this.Load();
}
@ -236,6 +239,11 @@ internal class DataWindow : Window, IDisposable
private void Load()
{
if (this.isLoaded)
return;
this.isLoaded = true;
foreach (var widget in this.modules)
{
widget.Load();

View file

@ -0,0 +1,34 @@
using Dalamud.Interface.Utility;
using ImGuiNET;
namespace Dalamud.Interface.Internal.Windows.Data;
/// <summary>
/// Common utilities used in Widgets.
/// </summary>
internal class WidgetUtil
{
/// <summary>
/// Draws text that can be copied on click.
/// </summary>
/// <param name="text">The text shown and to be copied.</param>
/// <param name="tooltipText">The text in the tooltip.</param>
internal static void DrawCopyableText(string text, string tooltipText = "Copy")
{
ImGuiHelpers.SafeTextWrapped(text);
if (ImGui.IsItemHovered())
{
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
ImGui.BeginTooltip();
ImGui.TextUnformatted(tooltipText);
ImGui.EndTooltip();
}
if (ImGui.IsItemClicked())
{
ImGui.SetClipboardText(text);
}
}
}

View file

@ -57,24 +57,6 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget
this.DrawExtendArrayTab();
}
private static void DrawCopyableText(string text, string tooltipText)
{
ImGuiHelpers.SafeTextWrapped(text);
if (ImGui.IsItemHovered())
{
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
ImGui.BeginTooltip();
ImGui.TextUnformatted(tooltipText);
ImGui.EndTooltip();
}
if (ImGui.IsItemClicked())
{
ImGui.SetClipboardText(text);
}
}
private void DrawArrayList(Type? arrayType, int arrayCount, short* arrayKeys, AtkArrayData** arrays, ref int selectedIndex)
{
using var table = ImRaii.Table("ArkArrayTable", 3, ImGuiTableFlags.ScrollY | ImGuiTableFlags.Borders, new Vector2(300, -1));
@ -162,7 +144,7 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget
ImGui.SameLine();
ImGui.TextUnformatted("Address: ");
ImGui.SameLine(0, 0);
DrawCopyableText($"0x{(nint)array:X}", "Copy address");
WidgetUtil.DrawCopyableText($"0x{(nint)array:X}", "Copy address");
if (array->SubscribedAddonsCount > 0)
{
@ -238,22 +220,22 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget
var ptr = &array->IntArray[i];
ImGui.TableNextColumn(); // Address
DrawCopyableText($"0x{(nint)ptr:X}", "Copy entry address");
WidgetUtil.DrawCopyableText($"0x{(nint)ptr:X}", "Copy entry address");
ImGui.TableNextColumn(); // Integer
DrawCopyableText((*ptr).ToString(), "Copy value");
WidgetUtil.DrawCopyableText((*ptr).ToString(), "Copy value");
ImGui.TableNextColumn(); // Short
DrawCopyableText((*(short*)ptr).ToString(), "Copy as short");
WidgetUtil.DrawCopyableText((*(short*)ptr).ToString(), "Copy as short");
ImGui.TableNextColumn(); // Byte
DrawCopyableText((*(byte*)ptr).ToString(), "Copy as byte");
WidgetUtil.DrawCopyableText((*(byte*)ptr).ToString(), "Copy as byte");
ImGui.TableNextColumn(); // Float
DrawCopyableText((*(float*)ptr).ToString(), "Copy as float");
WidgetUtil.DrawCopyableText((*(float*)ptr).ToString(), "Copy as float");
ImGui.TableNextColumn(); // Hex
DrawCopyableText($"0x{array->IntArray[i]:X2}", "Copy Hex");
WidgetUtil.DrawCopyableText($"0x{array->IntArray[i]:X2}", "Copy Hex");
}
}
@ -333,11 +315,11 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget
if (this.showTextAddress)
{
if (!isNull)
DrawCopyableText($"0x{(nint)array->StringArray[i]:X}", "Copy text address");
WidgetUtil.DrawCopyableText($"0x{(nint)array->StringArray[i]:X}", "Copy text address");
}
else
{
DrawCopyableText($"0x{(nint)(&array->StringArray[i]):X}", "Copy entry address");
WidgetUtil.DrawCopyableText($"0x{(nint)(&array->StringArray[i]):X}", "Copy entry address");
}
ImGui.TableNextColumn(); // Managed
@ -351,7 +333,7 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget
{
if (this.showMacroString)
{
DrawCopyableText(new ReadOnlySeStringSpan(array->StringArray[i]).ToString(), "Copy text");
WidgetUtil.DrawCopyableText(new ReadOnlySeStringSpan(array->StringArray[i]).ToString(), "Copy text");
}
else
{
@ -408,11 +390,11 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget
ImGui.TextUnformatted($"#{i}");
ImGui.TableNextColumn(); // Address
DrawCopyableText($"0x{(nint)(&array->DataArray[i]):X}", "Copy entry address");
WidgetUtil.DrawCopyableText($"0x{(nint)(&array->DataArray[i]):X}", "Copy entry address");
ImGui.TableNextColumn(); // Pointer
if (!isNull)
DrawCopyableText($"0x{(nint)array->DataArray[i]:X}", "Copy address");
WidgetUtil.DrawCopyableText($"0x{(nint)array->DataArray[i]:X}", "Copy address");
}
}
}

View file

@ -69,10 +69,10 @@ internal class FateTableWidget : IDataWindowWidget
ImGui.TextUnformatted($"#{i}");
ImGui.TableNextColumn(); // Address
DrawCopyableText($"0x{fate.Address:X}", "Click to copy Address");
WidgetUtil.DrawCopyableText($"0x{fate.Address:X}", "Click to copy Address");
ImGui.TableNextColumn(); // FateId
DrawCopyableText(fate.FateId.ToString(), "Click to copy FateId (RowId of Fate sheet)");
WidgetUtil.DrawCopyableText(fate.FateId.ToString(), "Click to copy FateId (RowId of Fate sheet)");
ImGui.TableNextColumn(); // State
ImGui.TextUnformatted(fate.State.ToString());
@ -140,7 +140,7 @@ internal class FateTableWidget : IDataWindowWidget
ImGui.TableNextColumn(); // Name
DrawCopyableText(fate.Name.ToString(), "Click to copy Name");
WidgetUtil.DrawCopyableText(fate.Name.ToString(), "Click to copy Name");
ImGui.TableNextColumn(); // Progress
ImGui.TextUnformatted($"{fate.Progress}%");
@ -156,28 +156,10 @@ internal class FateTableWidget : IDataWindowWidget
ImGui.TextUnformatted(fate.HasBonus.ToString());
ImGui.TableNextColumn(); // Position
DrawCopyableText(fate.Position.ToString(), "Click to copy Position");
WidgetUtil.DrawCopyableText(fate.Position.ToString(), "Click to copy Position");
ImGui.TableNextColumn(); // Radius
DrawCopyableText(fate.Radius.ToString(), "Click to copy Radius");
}
}
private static void DrawCopyableText(string text, string tooltipText)
{
ImGuiHelpers.SafeTextWrapped(text);
if (ImGui.IsItemHovered())
{
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
ImGui.BeginTooltip();
ImGui.TextUnformatted(tooltipText);
ImGui.EndTooltip();
}
if (ImGui.IsItemClicked())
{
ImGui.SetClipboardText(text);
WidgetUtil.DrawCopyableText(fate.Radius.ToString(), "Click to copy Radius");
}
}
}

View file

@ -0,0 +1,207 @@
using System.Linq;
using System.Text;
using Dalamud.Data;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.Text.Noun;
using Dalamud.Game.Text.Noun.Enums;
using Dalamud.Interface.Utility.Raii;
using ImGuiNET;
using Lumina.Data;
using Lumina.Excel;
using Lumina.Excel.Sheets;
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// <summary>
/// Widget for the NounProcessor service.
/// </summary>
internal class NounProcessorWidget : IDataWindowWidget
{
/// <summary>A list of German grammatical cases.</summary>
internal static readonly string[] GermanCases = ["Nominative", "Genitive", "Dative", "Accusative"];
private static readonly Type[] NounSheets = [
typeof(Aetheryte),
typeof(BNpcName),
typeof(BeastTribe),
typeof(DeepDungeonEquipment),
typeof(DeepDungeonItem),
typeof(DeepDungeonMagicStone),
typeof(DeepDungeonDemiclone),
typeof(ENpcResident),
typeof(EObjName),
typeof(EurekaAetherItem),
typeof(EventItem),
typeof(GCRankGridaniaFemaleText),
typeof(GCRankGridaniaMaleText),
typeof(GCRankLimsaFemaleText),
typeof(GCRankLimsaMaleText),
typeof(GCRankUldahFemaleText),
typeof(GCRankUldahMaleText),
typeof(GatheringPointName),
typeof(Glasses),
typeof(GlassesStyle),
typeof(HousingPreset),
typeof(Item),
typeof(MJIName),
typeof(Mount),
typeof(Ornament),
typeof(TripleTriadCard),
];
private ClientLanguage[] languages = [];
private string[] languageNames = [];
private int selectedSheetNameIndex = 0;
private int selectedLanguageIndex = 0;
private int rowId = 1;
private int amount = 1;
/// <inheritdoc/>
public string[]? CommandShortcuts { get; init; } = { "noun" };
/// <inheritdoc/>
public string DisplayName { get; init; } = "Noun Processor";
/// <inheritdoc/>
public bool Ready { get; set; }
/// <inheritdoc/>
public void Load()
{
this.languages = Enum.GetValues<ClientLanguage>();
this.languageNames = Enum.GetNames<ClientLanguage>();
this.selectedLanguageIndex = (int)Service<ClientState>.Get().ClientLanguage;
this.Ready = true;
}
/// <inheritdoc/>
public void Draw()
{
var nounProcessor = Service<NounProcessor>.Get();
var dataManager = Service<DataManager>.Get();
var clientState = Service<ClientState>.Get();
var sheetType = NounSheets.ElementAt(this.selectedSheetNameIndex);
var language = this.languages[this.selectedLanguageIndex];
ImGui.SetNextItemWidth(300);
if (ImGui.Combo("###SelectedSheetName", ref this.selectedSheetNameIndex, NounSheets.Select(t => t.Name).ToArray(), NounSheets.Length))
{
this.rowId = 1;
}
ImGui.SameLine();
ImGui.SetNextItemWidth(120);
if (ImGui.Combo("###SelectedLanguage", ref this.selectedLanguageIndex, this.languageNames, this.languageNames.Length))
{
language = this.languages[this.selectedLanguageIndex];
this.rowId = 1;
}
ImGui.SetNextItemWidth(120);
var sheet = dataManager.Excel.GetSheet<RawRow>(Language.English, sheetType.Name);
var minRowId = (int)sheet.FirstOrDefault().RowId;
var maxRowId = (int)sheet.LastOrDefault().RowId;
if (ImGui.InputInt("RowId###RowId", ref this.rowId, 1, 10, ImGuiInputTextFlags.AutoSelectAll))
{
if (this.rowId < minRowId)
this.rowId = minRowId;
if (this.rowId >= maxRowId)
this.rowId = maxRowId;
}
ImGui.SameLine();
ImGui.TextUnformatted($"(Range: {minRowId} - {maxRowId})");
ImGui.SetNextItemWidth(120);
if (ImGui.InputInt("Amount###Amount", ref this.amount, 1, 10, ImGuiInputTextFlags.AutoSelectAll))
{
if (this.amount <= 0)
this.amount = 1;
}
var articleTypeEnumType = language switch
{
ClientLanguage.Japanese => typeof(JapaneseArticleType),
ClientLanguage.German => typeof(GermanArticleType),
ClientLanguage.French => typeof(FrenchArticleType),
_ => typeof(EnglishArticleType),
};
var numCases = language == ClientLanguage.German ? 4 : 1;
#if DEBUG
if (ImGui.Button("Copy as self-test entry"))
{
var sb = new StringBuilder();
foreach (var articleType in Enum.GetValues(articleTypeEnumType))
{
for (var grammaticalCase = 0; grammaticalCase < numCases; grammaticalCase++)
{
var nounParams = new NounParams()
{
SheetName = sheetType.Name,
RowId = (uint)this.rowId,
Language = language,
Quantity = this.amount,
ArticleType = (int)articleType,
GrammaticalCase = grammaticalCase,
};
var output = nounProcessor.ProcessNoun(nounParams).ExtractText().Replace("\"", "\\\"");
var caseParam = language == ClientLanguage.German ? $"(int)GermanCases.{GermanCases[grammaticalCase]}" : "1";
sb.AppendLine($"new(nameof(LSheets.{sheetType.Name}), {this.rowId}, ClientLanguage.{language}, {this.amount}, (int){articleTypeEnumType.Name}.{Enum.GetName(articleTypeEnumType, articleType)}, {caseParam}, \"{output}\"),");
}
}
ImGui.SetClipboardText(sb.ToString());
}
#endif
using var table = ImRaii.Table("TextDecoderTable", 1 + numCases, ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders | ImGuiTableFlags.NoSavedSettings);
if (!table) return;
ImGui.TableSetupColumn("ArticleType", ImGuiTableColumnFlags.WidthFixed, 150);
for (var i = 0; i < numCases; i++)
ImGui.TableSetupColumn(language == ClientLanguage.German ? GermanCases[i] : "Text");
ImGui.TableSetupScrollFreeze(6, 1);
ImGui.TableHeadersRow();
foreach (var articleType in Enum.GetValues(articleTypeEnumType))
{
ImGui.TableNextRow();
ImGui.TableNextColumn();
ImGui.TableHeader(articleType.ToString());
for (var currentCase = 0; currentCase < numCases; currentCase++)
{
ImGui.TableNextColumn();
try
{
var nounParams = new NounParams()
{
SheetName = sheetType.Name,
RowId = (uint)this.rowId,
Language = language,
Quantity = this.amount,
ArticleType = (int)articleType,
GrammaticalCase = currentCase,
};
ImGui.TextUnformatted(nounProcessor.ProcessNoun(nounParams).ExtractText());
}
catch (Exception ex)
{
ImGui.TextUnformatted(ex.ToString());
}
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,11 @@
using Dalamud.Game.ClientState.GamePad;
using System.Linq;
using ImGuiNET;
using Dalamud.Game.ClientState.GamePad;
using Dalamud.Interface.Utility;
using Lumina.Text.Payloads;
using LSeStringBuilder = Lumina.Text.SeStringBuilder;
namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps;
@ -17,11 +22,34 @@ internal class GamepadStateAgingStep : IAgingStep
{
var gamepadState = Service<GamepadState>.Get();
ImGui.Text("Hold down North, East, L1");
var buttons = new (GamepadButtons Button, uint IconId)[]
{
(GamepadButtons.North, 11),
(GamepadButtons.East, 8),
(GamepadButtons.L1, 12),
};
if (gamepadState.Raw(GamepadButtons.North) == 1
&& gamepadState.Raw(GamepadButtons.East) == 1
&& gamepadState.Raw(GamepadButtons.L1) == 1)
var builder = LSeStringBuilder.SharedPool.Get();
builder.Append("Hold down ");
for (var i = 0; i < buttons.Length; i++)
{
var (button, iconId) = buttons[i];
builder.BeginMacro(MacroCode.Icon).AppendUIntExpression(iconId).EndMacro();
builder.PushColorRgba(gamepadState.Raw(button) == 1 ? 0x0000FF00u : 0x000000FF);
builder.Append(button.ToString());
builder.PopColor();
builder.Append(i < buttons.Length - 1 ? ", " : ".");
}
ImGuiHelpers.SeStringWrapped(builder.ToReadOnlySeString());
LSeStringBuilder.SharedPool.Return(builder);
if (buttons.All(tuple => gamepadState.Raw(tuple.Button) == 1))
{
return SelfTestStepResult.Pass;
}

View file

@ -0,0 +1,259 @@
using Dalamud.Game;
using Dalamud.Game.Text.Noun;
using Dalamud.Game.Text.Noun.Enums;
using ImGuiNET;
using LSheets = Lumina.Excel.Sheets;
namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps;
/// <summary>
/// Test setup for NounProcessor.
/// </summary>
internal class NounProcessorAgingStep : IAgingStep
{
private NounTestEntry[] tests =
[
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.Japanese, 1, (int)JapaneseArticleType.NearListener, 1, "その蜂蜜酒の運び人"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.Japanese, 1, (int)JapaneseArticleType.Distant, 1, "蜂蜜酒の運び人"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.Japanese, 2, (int)JapaneseArticleType.NearListener, 1, "それらの蜂蜜酒の運び人"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.Japanese, 2, (int)JapaneseArticleType.Distant, 1, "あれらの蜂蜜酒の運び人"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.English, 1, (int)EnglishArticleType.Indefinite, 1, "a mead-porting Midlander"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.English, 1, (int)EnglishArticleType.Definite, 1, "the mead-porting Midlander"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.English, 2, (int)EnglishArticleType.Indefinite, 1, "mead-porting Midlanders"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.English, 2, (int)EnglishArticleType.Definite, 1, "mead-porting Midlanders"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Indefinite, (int)GermanCases.Nominative, "ein Met schleppender Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Indefinite, (int)GermanCases.Genitive, "eines Met schleppenden Wiesländers"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Indefinite, (int)GermanCases.Dative, "einem Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Indefinite, (int)GermanCases.Accusative, "einen Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Definite, (int)GermanCases.Nominative, "der Met schleppender Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Definite, (int)GermanCases.Genitive, "des Met schleppenden Wiesländers"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Definite, (int)GermanCases.Dative, "dem Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Definite, (int)GermanCases.Accusative, "den Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Possessive, (int)GermanCases.Nominative, "dein Met schleppende Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Possessive, (int)GermanCases.Genitive, "deines Met schleppenden Wiesländers"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Possessive, (int)GermanCases.Dative, "deinem Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Possessive, (int)GermanCases.Accusative, "deinen Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Negative, (int)GermanCases.Nominative, "kein Met schleppender Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Negative, (int)GermanCases.Genitive, "keines Met schleppenden Wiesländers"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Negative, (int)GermanCases.Dative, "keinem Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Negative, (int)GermanCases.Accusative, "keinen Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Nominative, "Met schleppender Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Genitive, "Met schleppenden Wiesländers"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Dative, "Met schleppendem Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Accusative, "Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Demonstrative, (int)GermanCases.Nominative, "dieser Met schleppende Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Demonstrative, (int)GermanCases.Genitive, "dieses Met schleppenden Wiesländers"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Demonstrative, (int)GermanCases.Dative, "diesem Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 1, (int)GermanArticleType.Demonstrative, (int)GermanCases.Accusative, "diesen Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Indefinite, (int)GermanCases.Nominative, "2 Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Indefinite, (int)GermanCases.Genitive, "2 Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Indefinite, (int)GermanCases.Dative, "2 Met schleppenden Wiesländern"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Indefinite, (int)GermanCases.Accusative, "2 Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Definite, (int)GermanCases.Nominative, "die Met schleppende Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Definite, (int)GermanCases.Genitive, "der Met schleppender Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Definite, (int)GermanCases.Dative, "den Met schleppenden Wiesländern"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Definite, (int)GermanCases.Accusative, "die Met schleppende Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Possessive, (int)GermanCases.Nominative, "deine Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Possessive, (int)GermanCases.Genitive, "deiner Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Possessive, (int)GermanCases.Dative, "deinen Met schleppenden Wiesländern"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Possessive, (int)GermanCases.Accusative, "deine Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Negative, (int)GermanCases.Nominative, "keine Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Negative, (int)GermanCases.Genitive, "keiner Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Negative, (int)GermanCases.Dative, "keinen Met schleppenden Wiesländern"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Negative, (int)GermanCases.Accusative, "keine Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Nominative, "Met schleppende Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Genitive, "Met schleppender Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Dative, "Met schleppenden Wiesländern"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Accusative, "Met schleppende Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Demonstrative, (int)GermanCases.Nominative, "diese Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Demonstrative, (int)GermanCases.Genitive, "dieser Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Demonstrative, (int)GermanCases.Dative, "diesen Met schleppenden Wiesländern"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.German, 2, (int)GermanArticleType.Demonstrative, (int)GermanCases.Accusative, "diese Met schleppenden Wiesländer"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.French, 1, (int)FrenchArticleType.Indefinite, 1, "un livreur d'hydromel"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.French, 1, (int)FrenchArticleType.Definite, 1, "le livreur d'hydromel"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.French, 1, (int)FrenchArticleType.PossessiveFirstPerson, 1, "mon livreur d'hydromel"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.French, 1, (int)FrenchArticleType.PossessiveSecondPerson, 1, "ton livreur d'hydromel"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.French, 1, (int)FrenchArticleType.PossessiveThirdPerson, 1, "son livreur d'hydromel"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.French, 2, (int)FrenchArticleType.Indefinite, 1, "des livreurs d'hydromel"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.French, 2, (int)FrenchArticleType.Definite, 1, "les livreurs d'hydromel"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.French, 2, (int)FrenchArticleType.PossessiveFirstPerson, 1, "mes livreurs d'hydromel"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.French, 2, (int)FrenchArticleType.PossessiveSecondPerson, 1, "tes livreurs d'hydromel"),
new(nameof(LSheets.BNpcName), 1330, ClientLanguage.French, 2, (int)FrenchArticleType.PossessiveThirdPerson, 1, "ses livreurs d'hydromel"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.Japanese, 1, (int)JapaneseArticleType.NearListener, 1, "その酔いどれのネル"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.Japanese, 1, (int)JapaneseArticleType.Distant, 1, "酔いどれのネル"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.English, 1, (int)EnglishArticleType.Indefinite, 1, "Nell Half-full"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.English, 1, (int)EnglishArticleType.Definite, 1, "Nell Half-full"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Indefinite, (int)GermanCases.Nominative, "Nell die Beschwipste"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Indefinite, (int)GermanCases.Genitive, "Nell der Beschwipsten"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Indefinite, (int)GermanCases.Dative, "Nell der Beschwipsten"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Indefinite, (int)GermanCases.Accusative, "Nell die Beschwipste"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Definite, (int)GermanCases.Nominative, "Nell die Beschwipste"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Definite, (int)GermanCases.Genitive, "Nell der Beschwipsten"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Definite, (int)GermanCases.Dative, "Nell der Beschwipsten"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Definite, (int)GermanCases.Accusative, "Nell die Beschwipste"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Possessive, (int)GermanCases.Nominative, "Nell die Beschwipste"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Possessive, (int)GermanCases.Genitive, "Nell der Beschwipsten"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Possessive, (int)GermanCases.Dative, "Nell der Beschwipsten"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Possessive, (int)GermanCases.Accusative, "Nell die Beschwipste"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Negative, (int)GermanCases.Nominative, "Nell die Beschwipste"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Negative, (int)GermanCases.Genitive, "Nell der Beschwipsten"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Negative, (int)GermanCases.Dative, "Nell der Beschwipsten"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Negative, (int)GermanCases.Accusative, "Nell die Beschwipste"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Nominative, "Nell die Beschwipste"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Genitive, "Nell der Beschwipsten"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Dative, "Nell der Beschwipsten"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Accusative, "Nell die Beschwipste"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Demonstrative, (int)GermanCases.Nominative, "Nell die Beschwipste"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Demonstrative, (int)GermanCases.Genitive, "Nell der Beschwipsten"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Demonstrative, (int)GermanCases.Dative, "Nell der Beschwipsten"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.German, 1, (int)GermanArticleType.Demonstrative, (int)GermanCases.Accusative, "Nell die Beschwipste"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.French, 1, (int)FrenchArticleType.Indefinite, 1, "Nell la Boit-sans-soif"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.French, 1, (int)FrenchArticleType.Definite, 1, "Nell la Boit-sans-soif"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.French, 1, (int)FrenchArticleType.PossessiveFirstPerson, 1, "ma Nell la Boit-sans-soif"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.French, 1, (int)FrenchArticleType.PossessiveSecondPerson, 1, "ta Nell la Boit-sans-soif"),
new(nameof(LSheets.ENpcResident), 1031947, ClientLanguage.French, 1, (int)FrenchArticleType.PossessiveThirdPerson, 1, "sa Nell la Boit-sans-soif"),
new(nameof(LSheets.Item), 44348, ClientLanguage.Japanese, 1, (int)JapaneseArticleType.NearListener, 1, "その希少トームストーン:幻想"),
new(nameof(LSheets.Item), 44348, ClientLanguage.Japanese, 1, (int)JapaneseArticleType.Distant, 1, "希少トームストーン:幻想"),
new(nameof(LSheets.Item), 44348, ClientLanguage.Japanese, 2, (int)JapaneseArticleType.NearListener, 1, "それらの希少トームストーン:幻想"),
new(nameof(LSheets.Item), 44348, ClientLanguage.Japanese, 2, (int)JapaneseArticleType.Distant, 1, "あれらの希少トームストーン:幻想"),
new(nameof(LSheets.Item), 44348, ClientLanguage.English, 1, (int)EnglishArticleType.Indefinite, 1, "an irregular tomestone of phantasmagoria"),
new(nameof(LSheets.Item), 44348, ClientLanguage.English, 1, (int)EnglishArticleType.Definite, 1, "the irregular tomestone of phantasmagoria"),
new(nameof(LSheets.Item), 44348, ClientLanguage.English, 2, (int)EnglishArticleType.Indefinite, 1, "irregular tomestones of phantasmagoria"),
new(nameof(LSheets.Item), 44348, ClientLanguage.English, 2, (int)EnglishArticleType.Definite, 1, "irregular tomestones of phantasmagoria"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Indefinite, (int)GermanCases.Nominative, "ein ungewöhnlicher Allagischer Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Indefinite, (int)GermanCases.Genitive, "eines ungewöhnlichen Allagischen Steins der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Indefinite, (int)GermanCases.Dative, "einem ungewöhnlichen Allagischen Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Indefinite, (int)GermanCases.Accusative, "einen ungewöhnlichen Allagischen Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Definite, (int)GermanCases.Nominative, "der ungewöhnlicher Allagischer Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Definite, (int)GermanCases.Genitive, "des ungewöhnlichen Allagischen Steins der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Definite, (int)GermanCases.Dative, "dem ungewöhnlichen Allagischen Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Definite, (int)GermanCases.Accusative, "den ungewöhnlichen Allagischen Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Possessive, (int)GermanCases.Nominative, "dein ungewöhnliche Allagische Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Possessive, (int)GermanCases.Genitive, "deines ungewöhnlichen Allagischen Steins der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Possessive, (int)GermanCases.Dative, "deinem ungewöhnlichen Allagischen Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Possessive, (int)GermanCases.Accusative, "deinen ungewöhnlichen Allagischen Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Negative, (int)GermanCases.Nominative, "kein ungewöhnlicher Allagischer Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Negative, (int)GermanCases.Genitive, "keines ungewöhnlichen Allagischen Steins der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Negative, (int)GermanCases.Dative, "keinem ungewöhnlichen Allagischen Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Negative, (int)GermanCases.Accusative, "keinen ungewöhnlichen Allagischen Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Nominative, "ungewöhnlicher Allagischer Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Genitive, "ungewöhnlichen Allagischen Steins der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Dative, "ungewöhnlichem Allagischem Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Accusative, "ungewöhnlichen Allagischen Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Demonstrative, (int)GermanCases.Nominative, "dieser ungewöhnliche Allagische Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Demonstrative, (int)GermanCases.Genitive, "dieses ungewöhnlichen Allagischen Steins der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Demonstrative, (int)GermanCases.Dative, "diesem ungewöhnlichen Allagischen Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 1, (int)GermanArticleType.Demonstrative, (int)GermanCases.Accusative, "diesen ungewöhnlichen Allagischen Stein der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Indefinite, (int)GermanCases.Nominative, "2 ungewöhnlichen Allagischen Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Indefinite, (int)GermanCases.Genitive, "2 ungewöhnlichen Allagischen Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Indefinite, (int)GermanCases.Dative, "2 ungewöhnlichen Allagischen Steinen der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Indefinite, (int)GermanCases.Accusative, "2 ungewöhnlichen Allagischen Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Definite, (int)GermanCases.Nominative, "die ungewöhnliche Allagische Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Definite, (int)GermanCases.Genitive, "der ungewöhnlicher Allagischer Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Definite, (int)GermanCases.Dative, "den ungewöhnlichen Allagischen Steinen der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Definite, (int)GermanCases.Accusative, "die ungewöhnliche Allagische Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Possessive, (int)GermanCases.Nominative, "deine ungewöhnlichen Allagischen Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Possessive, (int)GermanCases.Genitive, "deiner ungewöhnlichen Allagischen Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Possessive, (int)GermanCases.Dative, "deinen ungewöhnlichen Allagischen Steinen der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Possessive, (int)GermanCases.Accusative, "deine ungewöhnlichen Allagischen Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Negative, (int)GermanCases.Nominative, "keine ungewöhnlichen Allagischen Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Negative, (int)GermanCases.Genitive, "keiner ungewöhnlichen Allagischen Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Negative, (int)GermanCases.Dative, "keinen ungewöhnlichen Allagischen Steinen der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Negative, (int)GermanCases.Accusative, "keine ungewöhnlichen Allagischen Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Nominative, "ungewöhnliche Allagische Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Genitive, "ungewöhnlicher Allagischer Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Dative, "ungewöhnlichen Allagischen Steinen der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.ZeroArticle, (int)GermanCases.Accusative, "ungewöhnliche Allagische Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Demonstrative, (int)GermanCases.Nominative, "diese ungewöhnlichen Allagischen Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Demonstrative, (int)GermanCases.Genitive, "dieser ungewöhnlichen Allagischen Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Demonstrative, (int)GermanCases.Dative, "diesen ungewöhnlichen Allagischen Steinen der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.German, 2, (int)GermanArticleType.Demonstrative, (int)GermanCases.Accusative, "diese ungewöhnlichen Allagischen Steine der Phantasmagorie"),
new(nameof(LSheets.Item), 44348, ClientLanguage.French, 1, (int)FrenchArticleType.Indefinite, 1, "un mémoquartz inhabituel fantasmagorique"),
new(nameof(LSheets.Item), 44348, ClientLanguage.French, 1, (int)FrenchArticleType.Definite, 1, "le mémoquartz inhabituel fantasmagorique"),
new(nameof(LSheets.Item), 44348, ClientLanguage.French, 1, (int)FrenchArticleType.PossessiveFirstPerson, 1, "mon mémoquartz inhabituel fantasmagorique"),
new(nameof(LSheets.Item), 44348, ClientLanguage.French, 1, (int)FrenchArticleType.PossessiveSecondPerson, 1, "ton mémoquartz inhabituel fantasmagorique"),
new(nameof(LSheets.Item), 44348, ClientLanguage.French, 1, (int)FrenchArticleType.PossessiveThirdPerson, 1, "son mémoquartz inhabituel fantasmagorique"),
new(nameof(LSheets.Item), 44348, ClientLanguage.French, 2, (int)FrenchArticleType.Indefinite, 1, "des mémoquartz inhabituels fantasmagoriques"),
new(nameof(LSheets.Item), 44348, ClientLanguage.French, 2, (int)FrenchArticleType.Definite, 1, "les mémoquartz inhabituels fantasmagoriques"),
new(nameof(LSheets.Item), 44348, ClientLanguage.French, 2, (int)FrenchArticleType.PossessiveFirstPerson, 1, "mes mémoquartz inhabituels fantasmagoriques"),
new(nameof(LSheets.Item), 44348, ClientLanguage.French, 2, (int)FrenchArticleType.PossessiveSecondPerson, 1, "tes mémoquartz inhabituels fantasmagoriques"),
new(nameof(LSheets.Item), 44348, ClientLanguage.French, 2, (int)FrenchArticleType.PossessiveThirdPerson, 1, "ses mémoquartz inhabituels fantasmagoriques"),
new(nameof(LSheets.Action), 45, ClientLanguage.German, 1, (int)FrenchArticleType.Indefinite, 1, "Blumenflüsterer IV"),
];
private enum GermanCases
{
Nominative,
Genitive,
Dative,
Accusative,
}
/// <inheritdoc/>
public string Name => "Test NounProcessor";
/// <inheritdoc/>
public unsafe SelfTestStepResult RunStep()
{
var nounProcessor = Service<NounProcessor>.Get();
for (var i = 0; i < this.tests.Length; i++)
{
var e = this.tests[i];
var nounParams = new NounParams()
{
SheetName = e.SheetName,
RowId = e.RowId,
Language = e.Language,
Quantity = e.Quantity,
ArticleType = e.ArticleType,
GrammaticalCase = e.GrammaticalCase,
};
var output = nounProcessor.ProcessNoun(nounParams);
if (e.ExpectedResult != output)
{
ImGui.TextUnformatted($"Mismatch detected (Test #{i}):");
ImGui.TextUnformatted($"Got: {output}");
ImGui.TextUnformatted($"Expected: {e.ExpectedResult}");
if (ImGui.Button("Continue"))
return SelfTestStepResult.Fail;
return SelfTestStepResult.Waiting;
}
}
return SelfTestStepResult.Pass;
}
/// <inheritdoc/>
public void CleanUp()
{
// ignored
}
private record struct NounTestEntry(
string SheetName,
uint RowId,
ClientLanguage Language,
int Quantity,
int ArticleType,
int GrammaticalCase,
string ExpectedResult);
}

View file

@ -0,0 +1,92 @@
using Dalamud.Game.ClientState;
using Dalamud.Game.Text.Evaluator;
using ImGuiNET;
using Lumina.Text.ReadOnly;
namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps;
/// <summary>
/// Test setup for SeStringEvaluator.
/// </summary>
internal class SeStringEvaluatorAgingStep : IAgingStep
{
private int step = 0;
/// <inheritdoc/>
public string Name => "Test SeStringEvaluator";
/// <inheritdoc/>
public SelfTestStepResult RunStep()
{
var seStringEvaluator = Service<SeStringEvaluator>.Get();
switch (this.step)
{
case 0:
ImGui.TextUnformatted("Is this the current time, and is it ticking?");
// This checks that EvaluateFromAddon fetches the correct Addon row,
// that MacroDecoder.GetMacroTime()->SetTime() has been called
// and that local and global parameters have been read correctly.
ImGui.TextUnformatted(seStringEvaluator.EvaluateFromAddon(31, [(uint)DateTimeOffset.UtcNow.ToUnixTimeSeconds()]).ExtractText());
if (ImGui.Button("Yes"))
this.step++;
ImGui.SameLine();
if (ImGui.Button("No"))
return SelfTestStepResult.Fail;
break;
case 1:
ImGui.TextUnformatted("Checking pcname macro using the local player name...");
// This makes sure that NameCache.Instance()->TryGetCharacterInfoByEntityId() has been called,
// that it returned the local players name by using its EntityId,
// and that it didn't include the world name by checking the HomeWorldId against AgentLobby.Instance()->LobbyData.HomeWorldId.
var clientState = Service<ClientState>.Get();
var localPlayer = clientState.LocalPlayer;
if (localPlayer is null)
{
ImGui.TextUnformatted("You need to be logged in for this step.");
if (ImGui.Button("Skip"))
return SelfTestStepResult.NotRan;
return SelfTestStepResult.Waiting;
}
var evaluatedPlayerName = seStringEvaluator.Evaluate(ReadOnlySeString.FromMacroString("<pcname(lnum1)>"), [localPlayer.EntityId]).ExtractText();
var localPlayerName = localPlayer.Name.TextValue;
if (evaluatedPlayerName != localPlayerName)
{
ImGui.TextUnformatted("The player name doesn't match:");
ImGui.TextUnformatted($"Evaluated Player Name (got): {evaluatedPlayerName}");
ImGui.TextUnformatted($"Local Player Name (expected): {localPlayerName}");
if (ImGui.Button("Continue"))
return SelfTestStepResult.Fail;
return SelfTestStepResult.Waiting;
}
return SelfTestStepResult.Pass;
}
return SelfTestStepResult.Waiting;
}
/// <inheritdoc/>
public void CleanUp()
{
// ignored
this.step = 0;
}
}

View file

@ -0,0 +1,130 @@
using System.Runtime.InteropServices;
using Dalamud.Game;
using Dalamud.Game.Text.Evaluator.Internal;
using FFXIVClientStructs.FFXIV.Client.System.String;
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using ImGuiNET;
namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps;
/// <summary>
/// Test setup for SheetRedirectResolver.
/// </summary>
internal class SheetRedirectResolverAgingStep : IAgingStep
{
private RedirectEntry[] redirects =
[
new("Item", 10, SheetRedirectFlags.Item),
new("ItemHQ", 10, SheetRedirectFlags.Item | SheetRedirectFlags.HighQuality),
new("ItemMP", 10, SheetRedirectFlags.Item | SheetRedirectFlags.Collectible),
new("Item", 35588, SheetRedirectFlags.Item),
new("Item", 1035588, SheetRedirectFlags.Item | SheetRedirectFlags.HighQuality),
new("Item", 2000217, SheetRedirectFlags.Item | SheetRedirectFlags.EventItem),
new("ActStr", 10, SheetRedirectFlags.Action), // Trait
new("ActStr", 1000010, SheetRedirectFlags.Action | SheetRedirectFlags.ActionSheet), // Action
new("ActStr", 2000010, SheetRedirectFlags.Action), // Item
new("ActStr", 3000010, SheetRedirectFlags.Action), // EventItem
new("ActStr", 4000010, SheetRedirectFlags.Action), // EventAction
new("ActStr", 5000010, SheetRedirectFlags.Action), // GeneralAction
new("ActStr", 6000010, SheetRedirectFlags.Action), // BuddyAction
new("ActStr", 7000010, SheetRedirectFlags.Action), // MainCommand
new("ActStr", 8000010, SheetRedirectFlags.Action), // Companion
new("ActStr", 9000010, SheetRedirectFlags.Action), // CraftAction
new("ActStr", 10000010, SheetRedirectFlags.Action | SheetRedirectFlags.ActionSheet), // Action
new("ActStr", 11000010, SheetRedirectFlags.Action), // PetAction
new("ActStr", 12000010, SheetRedirectFlags.Action), // CompanyAction
new("ActStr", 13000010, SheetRedirectFlags.Action), // Mount
// new("ActStr", 14000010, RedirectFlags.Action),
// new("ActStr", 15000010, RedirectFlags.Action),
// new("ActStr", 16000010, RedirectFlags.Action),
// new("ActStr", 17000010, RedirectFlags.Action),
// new("ActStr", 18000010, RedirectFlags.Action),
new("ActStr", 19000010, SheetRedirectFlags.Action), // BgcArmyAction
new("ActStr", 20000010, SheetRedirectFlags.Action), // Ornament
new("ObjStr", 10), // BNpcName
new("ObjStr", 1000010), // ENpcResident
new("ObjStr", 2000010), // Treasure
new("ObjStr", 3000010), // Aetheryte
new("ObjStr", 4000010), // GatheringPointName
new("ObjStr", 5000010), // EObjName
new("ObjStr", 6000010), // Mount
new("ObjStr", 7000010), // Companion
// new("ObjStr", 8000010),
// new("ObjStr", 9000010),
new("ObjStr", 10000010), // Item
new("EObj", 2003479), // EObj => EObjName
new("Treasure", 1473), // Treasure (without name, falls back to rowId 0)
new("Treasure", 1474), // Treasure (with name)
new("WeatherPlaceName", 0),
new("WeatherPlaceName", 28),
new("WeatherPlaceName", 40),
new("WeatherPlaceName", 52),
new("WeatherPlaceName", 2300),
];
private unsafe delegate SheetRedirectFlags ResolveSheetRedirect(RaptureTextModule* thisPtr, Utf8String* sheetName, uint* rowId, uint* flags);
/// <inheritdoc/>
public string Name => "Test SheetRedirectResolver";
/// <inheritdoc/>
public unsafe SelfTestStepResult RunStep()
{
// Client::UI::Misc::RaptureTextModule_ResolveSheetRedirect
if (!Service<TargetSigScanner>.Get().TryScanText("E8 ?? ?? ?? ?? 44 8B E8 A8 10", out var addr))
return SelfTestStepResult.Fail;
var sheetRedirectResolver = Service<SheetRedirectResolver>.Get();
var resolveSheetRedirect = Marshal.GetDelegateForFunctionPointer<ResolveSheetRedirect>(addr);
var utf8SheetName = Utf8String.CreateEmpty();
try
{
for (var i = 0; i < this.redirects.Length; i++)
{
var redirect = this.redirects[i];
utf8SheetName->SetString(redirect.SheetName);
var rowId1 = redirect.RowId;
uint colIndex1 = ushort.MaxValue;
var flags1 = resolveSheetRedirect(RaptureTextModule.Instance(), utf8SheetName, &rowId1, &colIndex1);
var sheetName2 = redirect.SheetName;
var rowId2 = redirect.RowId;
uint colIndex2 = ushort.MaxValue;
var flags2 = sheetRedirectResolver.Resolve(ref sheetName2, ref rowId2, ref colIndex2);
if (utf8SheetName->ToString() != sheetName2 || rowId1 != rowId2 || colIndex1 != colIndex2 || flags1 != flags2)
{
ImGui.TextUnformatted($"Mismatch detected (Test #{i}):");
ImGui.TextUnformatted($"Input: {redirect.SheetName}#{redirect.RowId}");
ImGui.TextUnformatted($"Game: {utf8SheetName->ToString()}#{rowId1}-{colIndex1} ({flags1})");
ImGui.TextUnformatted($"Evaluated: {sheetName2}#{rowId2}-{colIndex2} ({flags2})");
if (ImGui.Button("Continue"))
return SelfTestStepResult.Fail;
return SelfTestStepResult.Waiting;
}
}
return SelfTestStepResult.Pass;
}
finally
{
utf8SheetName->Dtor(true);
}
}
/// <inheritdoc/>
public void CleanUp()
{
// ignored
}
private record struct RedirectEntry(string SheetName, uint RowId, SheetRedirectFlags Flags = SheetRedirectFlags.None);
}

View file

@ -50,6 +50,9 @@ internal class SelfTestWindow : Window
new DutyStateAgingStep(),
new GameConfigAgingStep(),
new MarketBoardAgingStep(),
new SheetRedirectResolverAgingStep(),
new NounProcessorAgingStep(),
new SeStringEvaluatorAgingStep(),
new LogoutEventAgingStep(),
};