338 lines
9 KiB
C#
338 lines
9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using System.Text.RegularExpressions;
|
|
using Dalamud.Bindings.ImGui;
|
|
using Dalamud.Interface;
|
|
using Dalamud.Interface.Colors;
|
|
using Dalamud.Interface.Utility.Raii;
|
|
using Dalamud.Plugin;
|
|
using Questionable.Data;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Questing;
|
|
using Questionable.Validation;
|
|
|
|
namespace Questionable.Windows.QuestComponents;
|
|
|
|
internal sealed class ValidationDetailsRenderer
|
|
{
|
|
private readonly QuestData _questData;
|
|
|
|
private readonly IDalamudPluginInterface _pluginInterface;
|
|
|
|
private readonly Dictionary<int, ValidationIssue> _storedIssues = new Dictionary<int, ValidationIssue>();
|
|
|
|
private readonly Dictionary<int, bool> _openDetailWindows = new Dictionary<int, bool>();
|
|
|
|
private static readonly Regex UnicodeEscapeRegex = new Regex("\\\\u([0-9A-Fa-f]{4})", RegexOptions.Compiled);
|
|
|
|
private static readonly Regex JsonEscapeRegex = new Regex("\\\\(.)", RegexOptions.Compiled);
|
|
|
|
private static readonly Regex ConsecutiveQuotesRegex = new Regex("\"{2,}", RegexOptions.Compiled);
|
|
|
|
public ValidationDetailsRenderer(QuestData questData, IDalamudPluginInterface pluginInterface)
|
|
{
|
|
_questData = questData;
|
|
_pluginInterface = pluginInterface;
|
|
}
|
|
|
|
public static string CleanJsonText(string text)
|
|
{
|
|
if (string.IsNullOrEmpty(text))
|
|
{
|
|
return text;
|
|
}
|
|
text = UnicodeEscapeRegex.Replace(text, (Match match) => int.TryParse(match.Groups[1].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result) ? ((char)result).ToString() : match.Value);
|
|
text = JsonEscapeRegex.Replace(text, (Match match) => match.Groups[1].Value switch
|
|
{
|
|
"\"" => "\"",
|
|
"\\" => "\\",
|
|
"/" => "/",
|
|
"b" => "\b",
|
|
"f" => "\f",
|
|
"n" => "\n",
|
|
"r" => "\r",
|
|
"t" => "\t",
|
|
_ => match.Value,
|
|
});
|
|
if (text.Contains("\"\"", StringComparison.Ordinal))
|
|
{
|
|
text = ConsecutiveQuotesRegex.Replace(text, "\"");
|
|
text = text.Replace("Expected \"\"", "Expected \"\"", StringComparison.Ordinal);
|
|
}
|
|
return text;
|
|
}
|
|
|
|
public void OpenDetails(ValidationIssue issue, int index)
|
|
{
|
|
_storedIssues[index] = issue;
|
|
_openDetailWindows[index] = true;
|
|
}
|
|
|
|
public void DrawDetailWindows()
|
|
{
|
|
List<int> list = new List<int>();
|
|
foreach (KeyValuePair<int, bool> item in _openDetailWindows.ToList())
|
|
{
|
|
if (!item.Value || !_storedIssues.TryGetValue(item.Key, out ValidationIssue value))
|
|
{
|
|
continue;
|
|
}
|
|
string text = $"Validation Details##{item.Key}";
|
|
bool open = true;
|
|
ImGui.SetNextWindowSize(new Vector2(800f, 600f), ImGuiCond.FirstUseEver);
|
|
ImGui.SetNextWindowSizeConstraints(new Vector2(500f, 300f), new Vector2(1200f, 800f));
|
|
using (ThemedWindow.PushTheme())
|
|
{
|
|
if (ImGui.Begin(text, ref open))
|
|
{
|
|
DrawIssueDetails(value);
|
|
}
|
|
ImGui.End();
|
|
}
|
|
if (!open)
|
|
{
|
|
list.Add(item.Key);
|
|
}
|
|
}
|
|
foreach (int item2 in list)
|
|
{
|
|
_openDetailWindows.Remove(item2);
|
|
_storedIssues.Remove(item2);
|
|
}
|
|
}
|
|
|
|
private void DrawIssueDetails(ValidationIssue issue)
|
|
{
|
|
Vector4 color = issue.Severity switch
|
|
{
|
|
EIssueSeverity.Error => ImGuiColors.DalamudRed,
|
|
EIssueSeverity.Warning => ImGuiColors.DalamudYellow,
|
|
_ => ImGuiColors.DalamudOrange,
|
|
};
|
|
FontAwesomeIcon icon = issue.Severity switch
|
|
{
|
|
EIssueSeverity.Error => FontAwesomeIcon.ExclamationTriangle,
|
|
EIssueSeverity.Warning => FontAwesomeIcon.ExclamationCircle,
|
|
_ => FontAwesomeIcon.InfoCircle,
|
|
};
|
|
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
|
|
{
|
|
using (ImRaii.PushColor(ImGuiCol.Text, color))
|
|
{
|
|
ImGui.TextUnformatted(icon.ToIconString());
|
|
}
|
|
}
|
|
ImGui.SameLine();
|
|
using (ImRaii.PushColor(ImGuiCol.Text, color))
|
|
{
|
|
ImU8String text = new ImU8String(2, 2);
|
|
text.AppendFormatted(issue.Severity);
|
|
text.AppendLiteral(": ");
|
|
text.AppendFormatted(issue.Type);
|
|
ImGui.Text(text);
|
|
}
|
|
ImGui.Separator();
|
|
ImU8String text2 = new ImU8String(6, 1);
|
|
text2.AppendLiteral("Type: ");
|
|
text2.AppendFormatted(issue.PathType);
|
|
ImGui.Text(text2);
|
|
if (issue.Id != null)
|
|
{
|
|
if (issue.PathType == EPathType.Quest && ElementId.TryFromString(issue.Id, out ElementId elementId) && elementId != null)
|
|
{
|
|
try
|
|
{
|
|
IQuestInfo questInfo = _questData.GetQuestInfo(elementId);
|
|
ImU8String text3 = new ImU8String(10, 2);
|
|
text3.AppendLiteral("Quest: ");
|
|
text3.AppendFormatted(issue.Id);
|
|
text3.AppendLiteral(" - ");
|
|
text3.AppendFormatted(questInfo.Name);
|
|
ImGui.Text(text3);
|
|
}
|
|
catch
|
|
{
|
|
ImU8String text4 = new ImU8String(4, 1);
|
|
text4.AppendLiteral("ID: ");
|
|
text4.AppendFormatted(issue.Id);
|
|
ImGui.Text(text4);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImU8String text5 = new ImU8String(4, 1);
|
|
text5.AppendLiteral("ID: ");
|
|
text5.AppendFormatted(issue.Id);
|
|
ImGui.Text(text5);
|
|
}
|
|
}
|
|
if (issue.Location != null)
|
|
{
|
|
ImU8String text6 = new ImU8String(10, 1);
|
|
text6.AppendLiteral("Location: ");
|
|
text6.AppendFormatted(issue.Location);
|
|
ImGui.Text(text6);
|
|
}
|
|
ImGui.Separator();
|
|
ImGui.Text("Description:");
|
|
string description = CleanJsonText(issue.Description ?? "(no description)");
|
|
if (issue.Type == EIssueType.QuestDisabled && issue.Id == null)
|
|
{
|
|
DrawDisabledTribesDetails(description);
|
|
}
|
|
else if (issue.Type == EIssueType.InvalidJsonSyntax)
|
|
{
|
|
DrawJsonSyntaxErrorDetails(description);
|
|
}
|
|
else
|
|
{
|
|
DrawGenericDetails(description);
|
|
}
|
|
}
|
|
|
|
private static void DrawJsonSyntaxErrorDetails(string description)
|
|
{
|
|
string[] array = description.Split('\n');
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
string text = array[i].Trim();
|
|
if (string.IsNullOrEmpty(text))
|
|
{
|
|
ImGui.Spacing();
|
|
}
|
|
else if (text.StartsWith("JSON parsing error", StringComparison.Ordinal))
|
|
{
|
|
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed))
|
|
{
|
|
ImGui.TextWrapped(text);
|
|
}
|
|
}
|
|
else if (text.StartsWith("This usually indicates", StringComparison.Ordinal) || text.StartsWith("Please check", StringComparison.Ordinal))
|
|
{
|
|
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen))
|
|
{
|
|
ImGui.TextWrapped(text);
|
|
}
|
|
}
|
|
else if (text.StartsWith("• ", StringComparison.Ordinal))
|
|
{
|
|
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow))
|
|
{
|
|
ImGui.TextWrapped(text);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImGui.TextWrapped(text);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawDisabledTribesDetails(string description)
|
|
{
|
|
string[] array = description.Split(':', 2);
|
|
if (array.Length < 2)
|
|
{
|
|
ImGui.TextWrapped(description);
|
|
return;
|
|
}
|
|
ImGui.TextWrapped(array[0]);
|
|
ImGui.Spacing();
|
|
List<string> list = (from x in array[1].Split(',', StringSplitOptions.RemoveEmptyEntries)
|
|
select x.Trim() into x
|
|
where !string.IsNullOrEmpty(x)
|
|
select x).Distinct().ToList();
|
|
if (list.Count == 0)
|
|
{
|
|
ImGui.TextWrapped("(no disabled quests listed)");
|
|
return;
|
|
}
|
|
ImGui.Text("Disabled Quests:");
|
|
ImGui.Indent();
|
|
Vector4[] array2 = new Vector4[6]
|
|
{
|
|
ImGuiColors.TankBlue,
|
|
ImGuiColors.HealerGreen,
|
|
ImGuiColors.DPSRed,
|
|
ImGuiColors.ParsedGreen,
|
|
ImGuiColors.ParsedBlue,
|
|
ImGuiColors.DalamudViolet
|
|
};
|
|
for (int num = 0; num < list.Count; num++)
|
|
{
|
|
string value = list[num];
|
|
Vector4 color = array2[num % array2.Length];
|
|
using (ImRaii.PushColor(ImGuiCol.Text, color))
|
|
{
|
|
if (ElementId.TryFromString(value, out ElementId elementId) && elementId != null)
|
|
{
|
|
try
|
|
{
|
|
IQuestInfo questInfo = _questData.GetQuestInfo(elementId);
|
|
ImU8String text = new ImU8String(5, 2);
|
|
text.AppendLiteral("• ");
|
|
text.AppendFormatted(value);
|
|
text.AppendLiteral(" - ");
|
|
text.AppendFormatted(questInfo.Name);
|
|
ImGui.TextWrapped(text);
|
|
}
|
|
catch
|
|
{
|
|
ImU8String text2 = new ImU8String(18, 1);
|
|
text2.AppendLiteral("• ");
|
|
text2.AppendFormatted(value);
|
|
text2.AppendLiteral(" (unknown quest)");
|
|
ImGui.TextWrapped(text2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImU8String text3 = new ImU8String(2, 1);
|
|
text3.AppendLiteral("• ");
|
|
text3.AppendFormatted(value);
|
|
ImGui.TextWrapped(text3);
|
|
}
|
|
}
|
|
}
|
|
ImGui.Unindent();
|
|
}
|
|
|
|
private static void DrawGenericDetails(string description)
|
|
{
|
|
string[] array = description.Split('\n');
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
string text = array[i].Trim();
|
|
if (string.IsNullOrEmpty(text))
|
|
{
|
|
ImGui.Spacing();
|
|
}
|
|
else if (text.StartsWith("Error:", StringComparison.Ordinal) || text.StartsWith("Invalid", StringComparison.Ordinal) || text.StartsWith("Missing", StringComparison.Ordinal))
|
|
{
|
|
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed))
|
|
{
|
|
ImGui.TextWrapped(text);
|
|
}
|
|
}
|
|
else if (text.Contains(':', StringComparison.Ordinal))
|
|
{
|
|
int num = text.IndexOf(':', StringComparison.Ordinal);
|
|
string text2 = text.Substring(0, num);
|
|
string text3 = text.Substring(num + 1).TrimStart();
|
|
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.ParsedBlue))
|
|
{
|
|
ImGui.Text(text2 + ":");
|
|
}
|
|
ImGui.SameLine();
|
|
ImGui.TextWrapped(text3);
|
|
}
|
|
else
|
|
{
|
|
ImGui.TextWrapped(text);
|
|
}
|
|
}
|
|
}
|
|
}
|