Decompiled source of Hugins Desk Configuration Manager v0.6.3

plugins/RuneConfig/RuneConfig.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ConditionalConfigSync;
using ConfigurationManager.Utilities;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RuneConfig.Presentation;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Hugin's Desk")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Hugin's Desk")]
[assembly: AssemblyCopyright("Copyright ©  2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("0bac4d10-1d45-4b13-861c-48bae48536e9")]
[assembly: AssemblyFileVersion("0.6.3")]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.6.3.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
public struct HSLColor
{
	public float h;

	public float s;

	public float l;

	public float a;

	public HSLColor(float h, float s, float l, float a)
	{
		this.h = h;
		this.s = s;
		this.l = l;
		this.a = a;
	}

	public HSLColor(float h, float s, float l)
	{
		this.h = h;
		this.s = s;
		this.l = l;
		a = 1f;
	}

	public HSLColor(Color c)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		HSLColor hSLColor = FromRGBA(c);
		h = hSLColor.h;
		s = hSLColor.s;
		l = hSLColor.l;
		a = hSLColor.a;
	}

	public static HSLColor FromRGBA(Color c)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		float num = c.a;
		float num2 = Mathf.Min(Mathf.Min(c.r, c.g), c.b);
		float num3 = Mathf.Max(Mathf.Max(c.r, c.g), c.b);
		float num4 = (num2 + num3) / 2f;
		float num5;
		float num6;
		if (num2 == num3)
		{
			num5 = 0f;
			num6 = 0f;
		}
		else
		{
			float num7 = num3 - num2;
			num5 = ((num4 <= 0.5f) ? (num7 / (num3 + num2)) : (num7 / (2f - (num3 + num2))));
			num6 = 0f;
			if (c.r == num3)
			{
				num6 = (c.g - c.b) / num7;
			}
			else if (c.g == num3)
			{
				num6 = 2f + (c.b - c.r) / num7;
			}
			else if (c.b == num3)
			{
				num6 = 4f + (c.r - c.g) / num7;
			}
			num6 = Mathf.Repeat(num6 * 60f, 360f);
		}
		return new HSLColor(num6, num5, num4, num);
	}

	public Color ToRGBA()
	{
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		float num = a;
		float num2 = ((l <= 0.5f) ? (l * (1f + s)) : (l + s - l * s));
		float n = 2f * l - num2;
		float num3;
		float num4;
		float num5;
		if (s == 0f)
		{
			num3 = (num4 = (num5 = l));
		}
		else
		{
			num3 = Value(n, num2, h + 120f);
			num4 = Value(n, num2, h);
			num5 = Value(n, num2, h - 120f);
		}
		return new Color(num3, num4, num5, num);
	}

	private static float Value(float n1, float n2, float hue)
	{
		hue = Mathf.Repeat(hue, 360f);
		if (hue < 60f)
		{
			return n1 + (n2 - n1) * hue / 60f;
		}
		if (hue < 180f)
		{
			return n2;
		}
		if (hue < 240f)
		{
			return n1 + (n2 - n1) * (240f - hue) / 60f;
		}
		return n1;
	}

	public static implicit operator HSLColor(Color src)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		return FromRGBA(src);
	}

	public static implicit operator Color(HSLColor src)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		return src.ToRGBA();
	}
}
namespace RuneConfig.Presentation
{
	public class RuneTabPage
	{
		public string PluginGuid { get; set; } = "";

		public string Title { get; set; } = "";

		public string Category { get; set; }

		public string Search { get; set; } = "";

		public int Filter { get; set; }

		public float ScrollY { get; set; }

		public float SectionScrollX { get; set; }

		public float SectionScrollY { get; set; }

		public float SidebarScrollY { get; set; }

		public Dictionary<string, bool> Collapsed { get; set; } = new Dictionary<string, bool>();
	}
	public sealed class RuneModTab : RuneTabPage
	{
		public string Id { get; set; } = Guid.NewGuid().ToString("N");

		public List<RuneTabPage> History { get; set; } = new List<RuneTabPage>();

		public int HistoryIndex { get; set; } = -1;

		public RuneTabPage Snapshot()
		{
			return new RuneTabPage
			{
				PluginGuid = base.PluginGuid,
				Title = base.Title,
				Category = base.Category,
				Search = base.Search,
				Filter = base.Filter,
				ScrollY = base.ScrollY,
				SectionScrollX = base.SectionScrollX,
				SectionScrollY = base.SectionScrollY,
				SidebarScrollY = base.SidebarScrollY,
				Collapsed = new Dictionary<string, bool>(base.Collapsed)
			};
		}

		public void CopyFrom(RuneTabPage p)
		{
			base.PluginGuid = p.PluginGuid;
			base.Title = p.Title;
			base.Category = p.Category;
			base.Search = p.Search;
			base.Filter = p.Filter;
			base.ScrollY = p.ScrollY;
			base.SectionScrollX = p.SectionScrollX;
			base.SectionScrollY = p.SectionScrollY;
			base.SidebarScrollY = p.SidebarScrollY;
			base.Collapsed = new Dictionary<string, bool>(p.Collapsed);
		}

		public void Remember()
		{
			if (HistoryIndex < 0)
			{
				History.Add(Snapshot());
				HistoryIndex = 0;
			}
			else
			{
				History[HistoryIndex] = Snapshot();
			}
		}

		public void Navigate(string guid, string title, string category = null, int filter = 0, string search = "")
		{
			if (!(base.PluginGuid == guid) || !(base.Category == category) || base.Filter != filter || !(base.Search == search))
			{
				Remember();
				History.RemoveRange(HistoryIndex + 1, History.Count - HistoryIndex - 1);
				bool flag = base.PluginGuid == guid;
				CopyFrom(new RuneTabPage
				{
					PluginGuid = guid,
					Title = title,
					Category = category,
					Filter = filter,
					Search = search,
					Collapsed = (flag ? base.Collapsed : new Dictionary<string, bool>()),
					SidebarScrollY = base.SidebarScrollY,
					SectionScrollY = (flag ? base.SectionScrollY : 0f)
				});
				History.Add(Snapshot());
				if (History.Count > 40)
				{
					History.RemoveAt(0);
				}
				HistoryIndex = History.Count - 1;
			}
		}

		public bool CanGo(int delta)
		{
			if (HistoryIndex + delta >= 0)
			{
				return HistoryIndex + delta < History.Count;
			}
			return false;
		}

		public bool Go(int delta)
		{
			if (!CanGo(delta))
			{
				return false;
			}
			Remember();
			HistoryIndex += delta;
			CopyFrom(History[HistoryIndex]);
			return true;
		}
	}
	public sealed class RuneBrowserState
	{
		public int Schema { get; set; } = 1;

		public bool Initialized { get; set; }

		public List<RuneModTab> Tabs { get; set; } = new List<RuneModTab>();

		public List<RuneModTab> Closed { get; set; } = new List<RuneModTab>();

		public string ActiveId { get; set; }

		public float StripScroll { get; set; }

		public bool ConsoleOpen { get; set; }

		public bool SidebarCollapsed { get; set; }

		public string ConsoleDraft { get; set; } = "";

		public List<string> ConsoleHistory { get; set; } = new List<string>();

		[IgnoreDataMember]
		public RuneModTab Active => Tabs.FirstOrDefault((RuneModTab x) => x.Id == ActiveId);

		public RuneModTab Open(string guid, string title, bool duplicate = false)
		{
			if (string.IsNullOrWhiteSpace(guid))
			{
				return null;
			}
			RuneModTab runeModTab = (duplicate ? null : Tabs.FirstOrDefault((RuneModTab x) => x.PluginGuid == guid));
			if (runeModTab == null)
			{
				if (Tabs.Count >= 128)
				{
					return null;
				}
				runeModTab = new RuneModTab
				{
					PluginGuid = guid,
					Title = title
				};
				Tabs.Add(runeModTab);
			}
			ActiveId = runeModTab.Id;
			Initialized = true;
			return runeModTab;
		}

		public bool Activate(string id)
		{
			if (!Tabs.Any((RuneModTab t) => t.Id == id))
			{
				return false;
			}
			ActiveId = id;
			return true;
		}

		public bool Close(string id)
		{
			int num = Tabs.FindIndex((RuneModTab t) => t.Id == id);
			if (num < 0)
			{
				return false;
			}
			RuneModTab item = Tabs[num];
			Tabs.RemoveAt(num);
			Closed.RemoveAll((RuneModTab t) => t.Id == id);
			Closed.Add(item);
			if (Closed.Count > 12)
			{
				Closed.RemoveAt(0);
			}
			if (ActiveId == id)
			{
				ActiveId = ((Tabs.Count == 0) ? null : Tabs[Math.Min(num, Tabs.Count - 1)].Id);
			}
			return true;
		}

		public RuneModTab Reopen()
		{
			if (Closed.Count == 0 || Tabs.Count >= 128)
			{
				return null;
			}
			RuneModTab tab = Closed[Closed.Count - 1];
			Closed.RemoveAt(Closed.Count - 1);
			if (Tabs.Any((RuneModTab t) => t.Id == tab.Id))
			{
				tab.Id = Guid.NewGuid().ToString("N");
			}
			Tabs.Add(tab);
			ActiveId = tab.Id;
			return tab;
		}

		public bool Move(string id, int target)
		{
			int num = Tabs.FindIndex((RuneModTab t) => t.Id == id);
			if (num < 0)
			{
				return false;
			}
			target = Math.Max(0, Math.Min(Tabs.Count - 1, target));
			if (num == target)
			{
				return false;
			}
			RuneModTab item = Tabs[num];
			Tabs.RemoveAt(num);
			Tabs.Insert(target, item);
			return true;
		}

		public void Cycle(int delta)
		{
			if (Tabs.Count != 0)
			{
				int val = Tabs.FindIndex((RuneModTab t) => t.Id == ActiveId);
				ActiveId = Tabs[(Math.Max(0, val) + delta % Tabs.Count + Tabs.Count) % Tabs.Count].Id;
			}
		}

		public static float Scroll(float value)
		{
			if (!float.IsNaN(value) && !float.IsInfinity(value))
			{
				return Math.Max(0f, Math.Min(10000000f, value));
			}
			return 0f;
		}

		public void Normalize()
		{
			if (Schema != 1)
			{
				throw new InvalidOperationException("Unsupported workspace schema: " + Schema);
			}
			Tabs = (Tabs ?? new List<RuneModTab>()).Where((RuneModTab t) => t != null && !string.IsNullOrWhiteSpace(t.PluginGuid)).Take(128).ToList();
			Closed = (Closed ?? new List<RuneModTab>()).Where((RuneModTab t) => t != null && !string.IsNullOrWhiteSpace(t.PluginGuid)).Take(12).ToList();
			HashSet<string> hashSet = new HashSet<string>();
			foreach (RuneModTab item in Tabs.Concat(Closed))
			{
				if (string.IsNullOrEmpty(item.Id) || !hashSet.Add(item.Id))
				{
					item.Id = Guid.NewGuid().ToString("N");
					hashSet.Add(item.Id);
				}
				NormalizePage(item);
				item.History = (item.History ?? new List<RuneTabPage>()).Where((RuneTabPage h) => h != null && !string.IsNullOrWhiteSpace(h.PluginGuid)).Take(40).ToList();
				foreach (RuneTabPage item2 in item.History)
				{
					NormalizePage(item2);
				}
				item.HistoryIndex = ((item.History.Count == 0) ? (-1) : Math.Max(0, Math.Min(item.History.Count - 1, item.HistoryIndex)));
				item.Remember();
			}
			if (Active == null)
			{
				ActiveId = Tabs.FirstOrDefault()?.Id;
			}
			StripScroll = Scroll(StripScroll);
			ConsoleDraft = Limit(ConsoleDraft, 2048);
			ConsoleHistory = (from x in ConsoleHistory ?? new List<string>()
				where !string.IsNullOrWhiteSpace(x)
				select Limit(x, 2048)).Reverse().Take(100).Reverse()
				.ToList();
		}

		private static void NormalizePage(RuneTabPage t)
		{
			t.Title = Limit(t.Title, 256);
			t.PluginGuid = Limit(t.PluginGuid, 512);
			t.Search = Limit(t.Search, 2048);
			t.Category = ((t.Category == null) ? null : Limit(t.Category, 1024));
			t.Filter = Math.Max(0, Math.Min(3, t.Filter));
			t.ScrollY = Scroll(t.ScrollY);
			t.SectionScrollX = Scroll(t.SectionScrollX);
			t.SectionScrollY = Scroll(t.SectionScrollY);
			t.SidebarScrollY = Scroll(t.SidebarScrollY);
			t.Collapsed = (t.Collapsed ?? new Dictionary<string, bool>()).Where((KeyValuePair<string, bool> x) => x.Key != null && x.Key.Length <= 1024).Take(2048).ToDictionary((KeyValuePair<string, bool> x) => x.Key, (KeyValuePair<string, bool> x) => x.Value);
		}

		private static string Limit(string value, int max)
		{
			if (!string.IsNullOrEmpty(value))
			{
				if (value.Length <= max)
				{
					return value;
				}
				return value.Substring(0, max);
			}
			return "";
		}
	}
	public struct Box
	{
		public float X;

		public float Y;

		public float W;

		public float H;

		public float Right => X + W;

		public float Bottom => Y + H;

		public Box(float x, float y, float w, float h)
		{
			X = x;
			Y = y;
			W = w;
			H = h;
		}

		public bool Contains(float x, float y)
		{
			if (x >= X && y >= Y && x < Right)
			{
				return y < Bottom;
			}
			return false;
		}

		public Box Move(float x, float y)
		{
			return new Box(X + x, Y + y, W, H);
		}

		public Box Inset(float n)
		{
			return new Box(X + n, Y + n, Math.Max(0f, W - 2f * n), Math.Max(0f, H - 2f * n));
		}

		public bool Intersects(Box b)
		{
			if (X < b.Right && Right > b.X && Y < b.Bottom)
			{
				return Bottom > b.Y;
			}
			return false;
		}
	}
	public interface IRuneCanvas
	{
		void Image(string asset, Box destination, Box? source = null, float opacity = 1f, float radius = 0f);

		void Fill(Box rect, string color, float opacity = 1f);

		void Round(Box rect, string color, float radius = 6f, float opacity = 1f);

		void Stroke(Box rect, string color, float radius = 7f, float opacity = 1f);

		void Line(float x1, float y1, float x2, float y2, string color, float width = 1.5f);

		void Text(string text, Box rect, int size, string color, string font = "body", string align = "left");

		void PushClip(Box rect);

		void PopClip();
	}
	public static class RuneGeometry
	{
		public const float Width = 1474f;

		public const float Height = 930f;

		public static readonly Box Window = new Box(0f, 0f, 1474f, 930f);

		public static readonly Box Body = new RuneLayout(collapsed: false).Body;

		public static readonly Box Sidebar = new Box(16f, 264f, 236f, 564f);

		public static readonly Box Search = new Box(302f, 22f, 456f, 42f);

		public static readonly Box SearchText = new Box(342f, 32f, 353f, 27f);

		public static readonly Box Profiles = new Box(782f, 22f, 220f, 42f);

		public static readonly Box Tabs = new RuneLayout(collapsed: false).Sections;

		public static readonly Box ModTabs = new Box(390f, 100f, 996f, 42f);

		public static readonly Box NewTab = new Box(1398f, 102f, 40f, 38f);

		public static readonly Box Back = new Box(298f, 103f, 36f, 36f);

		public static readonly Box Forward = new Box(340f, 103f, 36f, 36f);

		public static readonly Box ConsoleButton = new Box(28f, 854f, 215f, 35f);

		public static readonly Box SourceButton = new Box(1260f, 169f, 178f, 36f);

		public static readonly Box ConsoleDrawer = new Box(302f, 575f, 1136f, 267f);

		public static readonly Box ConsoleInput = new Box(320f, 788f, 790f, 36f);

		public static readonly Box Close = new Box(1400f, 22f, 42f, 42f);

		public static readonly Box Help = new Box(1212f, 22f, 150f, 42f);

		public static readonly Box Tools = new Box(1040f, 22f, 148f, 42f);

		public static readonly Box Transparency = new Box(957f, 874f, 164f, 28f);

		public static readonly Box TransparencyReset = new Box(1179f, 874f, 28f, 28f);

		public static readonly Box Footer = new Box(302f, 863f, 1136f, 45f);

		public const float RowHeight = 64f;

		public const float CompactRowHeight = 48f;

		public const float SectionHeight = 44f;

		public const float SectionGap = 16f;

		public static float Fit(float w, float h, float requested = 1f)
		{
			return Math.Max(0.1f, Math.Min(requested, Math.Min(w / 1536f, h / 1024f)));
		}

		public static float Clamp(float v, float min, float max)
		{
			if (!float.IsNaN(v))
			{
				return Math.Max(min, Math.Min(max, v));
			}
			return min;
		}

		public static float SliderValue(float pointer, float left, float width)
		{
			return Clamp((pointer - left - 10f) / (width - 20f), 0f, 1f);
		}

		public static Box Control(float y)
		{
			return new Box(1090f, y + 15f, 210f, 38f);
		}

		public static Box Range(float y)
		{
			return new Box(990f, y + 22f, 210f, 24f);
		}

		public static Box Star(float y)
		{
			return new Box(1340f, y + 15f, 36f, 38f);
		}

		public static Box Reset(float y)
		{
			return new Box(1392f, y + 15f, 36f, 38f);
		}
	}
	public static class RunePaint
	{
		private struct IconPen
		{
			private readonly IRuneCanvas canvas;

			private readonly float x;

			private readonly float y;

			private readonly float k;

			private readonly string color;

			internal IconPen(IRuneCanvas c, float x, float y, float k, string color)
			{
				canvas = c;
				this.x = x;
				this.y = y;
				this.k = k;
				this.color = color;
			}

			internal void Line(float a, float b, float d, float e)
			{
				canvas.Line(x + a * k, y + b * k, x + d * k, y + e * k, color, 1.6f);
			}

			internal void Box(float a, float b, float w, float h)
			{
				Line(a, b, a + w, b);
				Line(a + w, b, a + w, b + h);
				Line(a + w, b + h, a, b + h);
				Line(a, b + h, a, b);
			}
		}

		public const string Accent = "#CACACA";

		public const string Gold = "#CACACA";

		public const string TextColor = "#E6E6E6";

		public const string Description = "#AAAAAA";

		public const string Muted = "#AAAAAA";

		public const string Background = "#1D1D1D";

		public const string Sidebar = "#191919";

		public const string Surface = "#262626";

		public const string Border = "#414141";

		public const string Selected = "#333333";

		public static void Outline(IRuneCanvas c, Box r, string fill = "#262626", float radius = 7f, string border = "#414141")
		{
			c.Round(r, fill, radius);
			c.Stroke(r, border, radius);
		}

		public static void Icon(IRuneCanvas c, string name, Box r, string color = null)
		{
			color = color ?? "#AAAAAA";
			float x = r.X;
			float y = r.Y;
			float num = Math.Min(r.W, r.H) / 24f;
			IconPen iconPen = new IconPen(c, x, y, num, color);
			switch (name)
			{
			case "profiles":
				iconPen.Box(7f, 7f, 14f, 14f);
				iconPen.Line(3f, 16f, 3f, 3f);
				iconPen.Line(3f, 3f, 16f, 3f);
				break;
			case "discord":
				c.Image("discord-mark", r);
				break;
			case "info":
				c.Stroke(new Box(x + 2f * num, y + 2f * num, 20f * num, 20f * num), color, 10f * num);
				iconPen.Line(12f, 10f, 12f, 17f);
				c.Round(new Box(x + 11f * num, y + 6f * num, 2f * num, 2f * num), color, num);
				break;
			case "sidebar":
				iconPen.Box(3f, 4f, 18f, 16f);
				iconPen.Line(9f, 4f, 9f, 20f);
				break;
			case "plus":
				iconPen.Line(5f, 12f, 19f, 12f);
				iconPen.Line(12f, 5f, 12f, 19f);
				break;
			case "back":
				iconPen.Line(15f, 5f, 8f, 12f);
				iconPen.Line(8f, 12f, 15f, 19f);
				break;
			case "forward":
				iconPen.Line(9f, 5f, 16f, 12f);
				iconPen.Line(16f, 12f, 9f, 19f);
				break;
			case "external":
				iconPen.Line(13f, 4f, 21f, 4f);
				iconPen.Line(21f, 4f, 21f, 12f);
				iconPen.Line(21f, 4f, 10f, 15f);
				iconPen.Line(8f, 6f, 4f, 6f);
				iconPen.Line(4f, 6f, 4f, 21f);
				iconPen.Line(4f, 21f, 19f, 21f);
				iconPen.Line(19f, 21f, 19f, 17f);
				break;
			case "console":
				iconPen.Line(4f, 6f, 10f, 12f);
				iconPen.Line(10f, 12f, 4f, 18f);
				iconPen.Line(13f, 18f, 21f, 18f);
				break;
			case "close":
				iconPen.Line(6f, 6f, 18f, 18f);
				iconPen.Line(18f, 6f, 6f, 18f);
				break;
			case "chevron-down":
				iconPen.Line(7f, 10f, 12f, 15f);
				iconPen.Line(12f, 15f, 17f, 10f);
				break;
			case "chevron-right":
				iconPen.Line(10f, 7f, 15f, 12f);
				iconPen.Line(15f, 12f, 10f, 17f);
				break;
			case "check":
				iconPen.Line(5f, 12f, 10f, 17f);
				iconPen.Line(10f, 17f, 19f, 7f);
				break;
			case "lock":
			case "shield":
				iconPen.Box(5f, 10f, 14f, 11f);
				iconPen.Line(8f, 10f, 8f, 6f);
				iconPen.Line(8f, 6f, 11f, 3f);
				iconPen.Line(11f, 3f, 13f, 3f);
				iconPen.Line(13f, 3f, 16f, 6f);
				iconPen.Line(16f, 6f, 16f, 10f);
				iconPen.Line(12f, 14f, 12f, 17f);
				break;
			case "magnifier":
				c.Round(new Box(x + 3f * num, y + 3f * num, 13f * num, 13f * num), color, 7f * num);
				c.Round(new Box(x + 5f * num, y + 5f * num, 9f * num, 9f * num), "#262626", 5f * num);
				iconPen.Line(15f, 15f, 21f, 21f);
				break;
			case "settings":
				iconPen.Line(4f, 6f, 20f, 6f);
				iconPen.Line(4f, 12f, 20f, 12f);
				iconPen.Line(4f, 18f, 20f, 18f);
				c.Round(new Box(x + 7f * num, y + 3f * num, 3f * num, 6f * num), color, 1f);
				c.Round(new Box(x + 15f * num, y + 9f * num, 3f * num, 6f * num), color, 1f);
				c.Round(new Box(x + 6f * num, y + 15f * num, 3f * num, 6f * num), color, 1f);
				break;
			case "star":
			case "star-small":
			{
				double num2 = 0.0;
				double num3 = 0.0;
				double num4 = 0.0;
				double num5 = 0.0;
				for (int i = 0; i < 10; i++)
				{
					double num6 = (double)(i * 36 - 90) * Math.PI / 180.0;
					double num7 = ((i % 2 == 0) ? 10.0 : 4.7);
					double num8 = 12.0 + Math.Cos(num6) * num7;
					double num9 = 12.0 + Math.Sin(num6) * num7;
					if (i > 0)
					{
						iconPen.Line((float)num2, (float)num3, (float)num8, (float)num9);
					}
					else
					{
						num4 = num8;
						num5 = num9;
					}
					num2 = num8;
					num3 = num9;
				}
				iconPen.Line((float)num2, (float)num3, (float)num4, (float)num5);
				break;
			}
			case "reset":
				iconPen.Line(5f, 9f, 8f, 5f);
				iconPen.Line(8f, 5f, 15f, 5f);
				iconPen.Line(15f, 5f, 20f, 10f);
				iconPen.Line(20f, 10f, 20f, 15f);
				iconPen.Line(20f, 15f, 15f, 20f);
				iconPen.Line(15f, 20f, 8f, 20f);
				iconPen.Line(5f, 4f, 5f, 10f);
				iconPen.Line(5f, 10f, 11f, 10f);
				break;
			case "checkbox":
				iconPen.Box(3f, 3f, 18f, 18f);
				break;
			case "hammer":
				iconPen.Line(5f, 19f, 15f, 9f);
				iconPen.Line(13f, 4f, 20f, 11f);
				iconPen.Line(10f, 7f, 17f, 14f);
				iconPen.Line(10f, 7f, 13f, 4f);
				iconPen.Line(17f, 14f, 20f, 11f);
				break;
			case "tree":
				iconPen.Line(12f, 3f, 4f, 15f);
				iconPen.Line(4f, 15f, 20f, 15f);
				iconPen.Line(20f, 15f, 12f, 3f);
				iconPen.Line(12f, 15f, 12f, 21f);
				break;
			case "helmet":
				iconPen.Line(5f, 19f, 5f, 9f);
				iconPen.Line(5f, 9f, 9f, 4f);
				iconPen.Line(9f, 4f, 15f, 4f);
				iconPen.Line(15f, 4f, 19f, 9f);
				iconPen.Line(19f, 9f, 19f, 19f);
				iconPen.Line(5f, 19f, 9f, 16f);
				iconPen.Line(19f, 19f, 15f, 16f);
				iconPen.Line(12f, 4f, 12f, 21f);
				break;
			default:
				iconPen.Box(3f, 3f, 7f, 7f);
				iconPen.Box(14f, 3f, 7f, 7f);
				iconPen.Box(3f, 14f, 7f, 7f);
				iconPen.Box(14f, 14f, 7f, 7f);
				break;
			}
		}

		public static void Shell(IRuneCanvas c, RuneLayout layout = default(RuneLayout))
		{
			c.Round(new Box(0f, 5f, 1474f, 925f), "#000000", 18f, 0.22f);
			Outline(c, new Box(1f, 1f, 1472f, 926f), "#1D1D1D", 16f, "#484848");
			c.Fill(new Box(layout.ModWidth, 96f, 1f, 805f), "#363636");
			c.Fill(new Box(layout.ContentX - 16f, 164f, 1f, 678f), "#303030");
			c.Fill(new Box(20f, 83f, 1434f, 1f), "#383838");
			c.Fill(new Box(302f, 857f, 1136f, 1f), "#363636");
			Outline(c, RuneGeometry.Search);
		}

		public static void Chrome(IRuneCanvas c, string language, bool locked, string status = "", float transparency = 12f, RuneLayout layout = default(RuneLayout))
		{
			bool flag = language == "ru";
			c.Image("trxtr-raven", TrxtrBrand.Logo);
			c.Text("Hugin's Desk", new Box(65f, 33f, 187f, 25f), 16, "#AAAAAA");
			Icon(c, "magnifier", new Box(313f, 31f, 23f, 23f));
			c.Text("Ctrl F", new Box(RuneGeometry.Search.Right - 57f, 34f, 45f, 22f), 12, "#AAAAAA", "body", "right");
			c.Text(flag ? "Настройки" : "Settings", new Box(1075f, 33f, 113f, 24f), 16, "#AAAAAA");
			c.Text(flag ? "Справка" : "Help", new Box(1224f, 33f, 130f, 24f), 16, "#AAAAAA");
			Icon(c, "close", new Box(1409f, 31f, 24f, 24f));
			if (!layout.Collapsed)
			{
				c.Text(flag ? "Моды" : "Mods", new Box(29f, 109f, 180f, 26f), 17, "#E6E6E6", "heading");
			}
			Icon(c, "sidebar", layout.Collapse.Inset(6f));
			c.Text(flag ? "РАЗДЕЛЫ" : "SECTIONS", new Box(layout.Sections.X + 12f, 176f, 180f, 22f), 12, "#AAAAAA");
			for (int i = 0; i < 6; i++)
			{
				c.Round(new Box(276 + i % 2 * 5, 35 + i / 2 * 5, 2f, 2f), "#AAAAAA", 1f, 0.65f);
			}
			c.Text(layout.Collapsed ? "F1" : "F1 / Esc", new Box(layout.Collapsed ? 28 : 32, 900f, layout.ModWidth - 36f, 18f), 12, "#AAAAAA");
			Icon(c, locked ? "lock" : "all-mods", new Box(302f, 879f, 17f, 17f));
			c.Text((!locked) ? (flag ? "Управление доступно" : "Character controls enabled") : (flag ? "Управление заблокировано" : "Character controls locked"), new Box(329f, 878f, 420f, 25f), 14, "#AAAAAA");
			if (!string.IsNullOrEmpty(status))
			{
				c.Text(status, new Box(329f, 900f, 520f, 22f), 12, "#AAAAAA");
			}
			c.Text(flag ? "Прозрачность" : "Transparency", new Box(828f, 880f, 128f, 23f), 14, "#AAAAAA");
			Slider(c, RuneGeometry.Transparency, RuneOpacity.ToSlider(transparency));
			c.Text((int)RuneOpacity.Clamp(transparency) + "%", new Box(1131f, 880f, 43f, 24f), 14, "#E6E6E6", "body", "right");
			Icon(c, "reset", new Box(1184f, 880f, 17f, 17f), "#AAAAAA");
			c.Text(flag ? "Автосохранение" : "Auto-save on", new Box(1225f, 878f, 155f, 25f), 14, "#AAAAAA", "body", "right");
			Icon(c, "discord", TrxtrBrand.Discord.Inset(8f), "#AAAAAA");
		}

		public static void Navigation(IRuneCanvas c, Box r, string label, string icon, bool selected)
		{
			if (selected)
			{
				c.Round(r.Inset(2f), "#333333", 7f);
			}
			if (!string.IsNullOrEmpty(icon))
			{
				Icon(c, icon, new Box(r.X + 14f, r.Y + 9f, 24f, 24f));
			}
			if (!string.IsNullOrEmpty(label))
			{
				c.Text(label, new Box(r.X + 48f, r.Y + (r.H - 24f) / 2f, Math.Max(0f, r.W - 58f), 26f), 17, selected ? "#E6E6E6" : "#AAAAAA", selected ? "heading" : "body");
			}
		}

		public static void Heading(IRuneCanvas c, string title, string hint = "", RuneLayout layout = default(RuneLayout))
		{
			c.Text(title, new Box(layout.ContentX, 166f, 1248f - layout.ContentX, 38f), 26, "#E6E6E6", "heading");
			if (!string.IsNullOrEmpty(hint))
			{
				c.Text(hint, new Box(layout.ContentX, 204f, 1438f - layout.ContentX, 21f), 13, "#AAAAAA");
			}
		}

		public static void DocumentTab(IRuneCanvas c, Box r, string title, bool active, bool missing = false)
		{
			if (active)
			{
				Outline(c, r, "#333333", 8f, "#505050");
			}
			else
			{
				c.Round(r, "#262626", 8f);
			}
			c.Text(title, new Box(r.X + 38f, r.Y + 12f, r.W - 76f, 24f), 15, missing ? "#AAAAAA" : "#E6E6E6", active ? "heading" : "body");
			Icon(c, "close", new Box(r.Right - 27f, r.Y + 13f, 16f, 16f));
			if (missing)
			{
				c.Text("?", new Box(r.X + 12f, r.Y + 12f, 18f, 22f), 14, "#AAAAAA");
			}
		}

		public static void ConsoleShell(IRuneCanvas c, string language, RuneLayout layout = default(RuneLayout))
		{
			bool flag = language == "ru";
			Outline(c, layout.ConsoleDrawer, "#1D1D1D", 10f);
			Icon(c, "console", new Box(layout.ContentX + 18f, 590f, 20f, 20f));
			c.Text(flag ? "Консоль" : "Console", new Box(layout.ContentX + 48f, 591f, 145f, 26f), 16, "#E6E6E6", "heading");
			c.Text(flag ? "Enter — выполнить · Tab — дополнить · ↑↓ — история" : "Enter to run · Tab to complete · ↑↓ history", new Box(layout.ContentX + 205f, 591f, 1240f - layout.ContentX - 205f, 26f), 13, "#AAAAAA");
			Field(c, layout.ConsoleInput, "");
		}

		public static void Tab(IRuneCanvas c, Box r, string label, bool selected)
		{
			if (selected)
			{
				c.Round(new Box(r.X + 3f, r.Y + 4f, r.W - 6f, r.H - 8f), "#333333", 7f);
				c.Stroke(new Box(r.X + 3f, r.Y + 4f, r.W - 6f, r.H - 8f), "#474747");
			}
			c.Text(label, new Box(r.X + 16f, r.Y + 11f, r.W - 32f, 26f), 17, selected ? "#E6E6E6" : "#AAAAAA");
		}

		public static void Section(IRuneCanvas c, Box r, string label, bool collapsed)
		{
			Icon(c, collapsed ? "chevron-right" : "chevron-down", new Box(r.X + 2f, r.Y + 10f, 20f, 20f));
			c.Text(label, new Box(r.X + 32f, r.Y + 10f, r.W - 64f, 27f), 16, "#AAAAAA", "heading");
		}

		public static void Row(IRuneCanvas c, Box r, string title, string description, bool compact = false, bool hovered = false)
		{
			if (hovered)
			{
				c.Round(r, "#242424", 8f);
			}
			c.Fill(new Box(r.X + 32f, r.Bottom, r.W - 32f, 1f), "#313131");
			c.Text(title, new Box(r.X + 32f, r.Y + (float)(compact ? 13 : 9), Math.Max(0f, 958f - r.X - 32f), 27f), 18, "#E6E6E6");
			if (!compact)
			{
				c.Text(description, new Box(r.X + 32f, r.Y + 36f, Math.Max(0f, 958f - r.X - 32f), 25f), 15, "#AAAAAA");
			}
		}

		public static void Toggle(IRuneCanvas c, Box r, bool on, bool disabled = false, string language = "en")
		{
			Box r2 = new Box(r.X + 3f, r.Y + 9f, 20f, 20f);
			Outline(c, r2, disabled ? "#1D1D1D" : (on ? "#CACACA" : "#262626"), 4f, disabled ? "#686868" : "#CACACA");
			if (on)
			{
				Icon(c, "check", new Box(r2.X + 1f, r2.Y + 1f, 18f, 18f), "#1D1D1D");
			}
			if (disabled)
			{
				c.Text((language == "ru") ? "Задано сервером" : "Server controlled", new Box(r.X + 34f, r.Y + 10f, 185f, 25f), 14, "#AAAAAA");
			}
		}

		public static void Slider(IRuneCanvas c, Box r, float value)
		{
			value = RuneGeometry.Clamp(value, 0f, 1f);
			float num = 10f + (r.W - 20f) * value;
			c.Round(new Box(r.X + 10f, r.Y + 10f, r.W - 20f, 4f), "#4B4B4B", 2f);
			c.Round(new Box(r.X + 10f, r.Y + 10f, Math.Max(0f, num - 10f), 4f), "#CACACA", 2f);
			c.Round(new Box(r.X + num - 7f, r.Y + 5f, 14f, 14f), "#E2E2E2", 7f);
		}

		public static void Field(IRuneCanvas c, Box r, string value, bool dropdown = false, bool centered = false)
		{
			Outline(c, r);
			c.Text(value, new Box(r.X + 12f, r.Y + (r.H - 23f) / 2f, r.W - (float)(dropdown ? 42 : 24), 26f), 17, "#E6E6E6", "body", centered ? "center" : "left");
			if (dropdown)
			{
				Icon(c, "chevron-down", new Box(r.Right - 29f, r.Y + (r.H - 20f) / 2f, 20f, 20f));
			}
		}

		public static void Actions(IRuneCanvas c, float y, bool favorite, bool locked, bool canReset = true, string language = "en", bool hovered = false)
		{
			Icon(c, "info", new Box(1312f, y + 24f, 18f, 18f), "#AAAAAA");
			if (locked)
			{
				Icon(c, "lock", new Box(1348f, y + 24f, 18f, 18f), "#AAAAAA");
			}
			else if (favorite || hovered)
			{
				Icon(c, "star", new Box(1348f, y + 24f, 18f, 18f), "#CACACA");
			}
			if (canReset && !locked && hovered)
			{
				Icon(c, "reset", new Box(1400f, y + 24f, 18f, 18f), "#AAAAAA");
			}
		}

		public static void FramedWindow(IRuneCanvas c, Box r, string title)
		{
			Outline(c, r, "#1D1D1D", 14f);
			c.Fill(new Box(r.X + 24f, r.Y + 75f, r.W - 48f, 1f), "#414141");
			if (!string.IsNullOrEmpty(title))
			{
				c.Text(title, new Box(r.X + 30f, r.Y + 25f, r.W - 110f, 39f), 23, "#E6E6E6", "heading");
			}
		}

		public static void Popup(IRuneCanvas c, Box r, string title)
		{
			c.Round(r, "#1D1D1D", 10f);
			c.Round(r.Move(0f, 3f), "#000000", 11f, 0.16f);
			Outline(c, r, "#262626", 10f, "#565656");
			if (!string.IsNullOrEmpty(title))
			{
				c.Text(title, new Box(r.X + 24f, r.Y + 20f, r.W - 70f, 36f), 23, "#E6E6E6", "heading");
			}
		}
	}
	public sealed class RuneCommandHint
	{
		public string Command;

		public string Description;

		public string Completion;
	}
	public static class RuneConsoleLogic
	{
		public static List<RuneCommandHint> Match(IEnumerable<RuneCommandHint> commands, string query, int limit = 6)
		{
			query = (query ?? "").Trim();
			return (from c in commands
				where c.Command.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0 || (c.Description ?? "").IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0
				orderby (!c.Command.StartsWith(query, StringComparison.OrdinalIgnoreCase)) ? 1 : 0
				select c).ThenBy<RuneCommandHint, string>((RuneCommandHint c) => c.Command, StringComparer.OrdinalIgnoreCase).Take(limit).ToList();
		}

		public static void AddHistory(List<string> history, string command)
		{
			command = (command ?? "").Trim();
			if (command.Length != 0)
			{
				history.RemoveAll((string x) => x == command);
				history.Add((command.Length > 2048) ? command.Substring(0, 2048) : command);
				if (history.Count > 100)
				{
					history.RemoveRange(0, history.Count - 100);
				}
			}
		}

		public static string Plain(string text)
		{
			return Regex.Replace(text ?? "", "</?(?:color|size|b|i|u|sprite|font|link|style|alpha|voffset|cspace|mspace|mark)(?:\\s+[^>]*|=[^>]*)?>", "", RegexOptions.IgnoreCase);
		}
	}
	public enum RuneEditAction
	{
		None,
		Undo,
		Redo
	}
	public static class RuneEditShortcuts
	{
		public static RuneEditAction Resolve(char key, bool control, bool command, bool shift, bool alt, bool blocked, bool editingText, bool pointerCaptured)
		{
			if (blocked || editingText || pointerCaptured || alt || (!control && !command))
			{
				return RuneEditAction.None;
			}
			switch (key)
			{
			case 'Z':
			case 'z':
				if (!shift)
				{
					return RuneEditAction.Undo;
				}
				return RuneEditAction.Redo;
			case 'Y':
			case 'y':
				if (!shift)
				{
					return RuneEditAction.Redo;
				}
				break;
			}
			return RuneEditAction.None;
		}

		public static bool IsTextControl(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			switch (name)
			{
			default:
				if (!name.StartsWith("RuneDraft:", StringComparison.Ordinal) && !name.StartsWith("RuneValue:", StringComparison.Ordinal) && !name.StartsWith("RuneVector:", StringComparison.Ordinal))
				{
					return name.StartsWith("RuneHex:", StringComparison.Ordinal);
				}
				break;
			case "profile-name":
			case "searchBox":
			case "rune-console-input":
			case "tab-mod-search":
				break;
			}
			return true;
		}
	}
	public static class RuneFontPolicy
	{
		public static string[] Candidates(IEnumerable<string> installed, bool mac)
		{
			HashSet<string> hashSet = new HashSet<string>(installed ?? Enumerable.Empty<string>(), StringComparer.OrdinalIgnoreCase);
			return ((!mac) ? new string[4] { "Segoe UI", "Arial", "Liberation Sans", "DejaVu Sans" } : new string[4] { "Helvetica Neue", "Helvetica", "Arial", "Verdana" }).Where(hashSet.Contains).ToArray();
		}
	}
	public sealed class RuneHelpRow
	{
		public readonly string Title;

		public readonly string Detail;

		public readonly string[][] Keys;

		public float Height => Math.Max(74 + (Detail.Split(new char[1] { '\n' }).Length - 1) * 20, Keys.Length * 32 + 20);

		public RuneHelpRow(string title, string detail, string keys)
		{
			Title = title;
			Detail = detail;
			Keys = Array.ConvertAll(keys.Split(new char[1] { '\n' }), (string line) => line.Split(new char[1] { '|' }));
		}
	}
	public sealed class RuneHelpTopic
	{
		public readonly string Title;

		public readonly string Subtitle;

		public readonly string Note;

		public readonly string Icon;

		public readonly RuneHelpRow[] Rows;

		public float ContentHeight
		{
			get
			{
				float num = Note.Split(new char[1] { '\n' }).Length * 22 + 34;
				RuneHelpRow[] rows = Rows;
				foreach (RuneHelpRow runeHelpRow in rows)
				{
					num += runeHelpRow.Height + 8f;
				}
				return num;
			}
		}

		public RuneHelpTopic(string title, string subtitle, string note, string icon, params RuneHelpRow[] rows)
		{
			Title = title;
			Subtitle = subtitle;
			Note = note;
			Icon = icon;
			Rows = rows;
		}
	}
	public static class RuneHelpGuide
	{
		public static readonly Box Window = new Box(177f, 65f, 1120f, 800f);

		public static readonly Box Viewport = new Box(459f, 279f, 797f, 506f);

		public static readonly Box Close = new Box(1239f, 85f, 36f, 36f);

		public static readonly Box Done = new Box(1115f, 812f, 154f, 35f);

		public static readonly RuneHelpTopic[] Russian = new RuneHelpTopic[8]
		{
			new RuneHelpTopic("Основное", "Самые нужные действия — под рукой.", "В справке: колесо или ↑ ↓ — прокрутка. PgUp / PgDn — на страницу.\nHome / End — в начало или конец раздела.", "all-mods", R("Открыть или закрыть окно", "Горячая клавиша по умолчанию.", "F1"), R("Вернуться в игру", "Кликните за пределами окна и редакторов.\nУправление вернётся после отпускания кнопки.", "Клик снаружи"), R("Закрыть верхнюю панель", "Затем редактор, консоль или всё окно.", "Esc"), R("Найти настройку", "По имени мода, разделу, описанию и значению.", "Ctrl / ⌘|F"), R("Отменить изменение", "Последний шаг редактирования настройки.", "Ctrl / ⌘|Z"), R("Повторить изменение", "Вернуть отменённый шаг.", "Ctrl / ⌘|Y\nCtrl / ⌘|Shift|Z"), R("Узнать больше о поле", "Описание, диапазон, значение по умолчанию.", "ⓘ")),
			new RuneHelpTopic("Отмена и правки", "История изменений настроек, общая для всех вкладок.", "До 100 шагов за текущий запуск игры. Ввод текста не перехватывается.\nЗаблокированное сервером или изменённое извне поле не перезаписывается.", "reset", R("Отменить шаг", "Один проход ползунка — один шаг.", "Ctrl / ⌘|Z"), R("Повторить шаг", "Новое изменение очищает историю повтора.", "Ctrl / ⌘|Y\nCtrl / ⌘|Shift|Z"), R("Завершить ввод текста", "Выйдите из поля, чтобы отменять его значение.\nЩёлкните по свободному месту окна.", "Клик вне поля"), R("Открыть подробный редактор", "Нажмите правой кнопкой на название поля.", "ПКМ"), R("Показать описание", "Панель с прокруткой открывается по клику.", "ⓘ"), R("Добавить в избранное", "Звезда справа от значения настройки.", "Звезда"), R("Сбросить значение", "Кнопка справа от поля; сброс всех — в настройках.", "Сброс"), R("Сохранить изменение", "Значения применяются сразу, сохраняет мод.\nКнопка «Применить» не требуется.", "Автоматически")),
			new RuneHelpTopic("Вкладки", "Несколько модов или разные разделы одного мода.", "Открытые вкладки, их порядок, поиск, разделы и прокрутка\nсохраняются при закрытии окна и после перезапуска игры.", "sidebar", R("Создать вкладку", "Откроется список модов для выбора.", "Ctrl / ⌘|T"), R("Закрыть текущую", "Также: × или средняя кнопка на вкладке.", "Ctrl / ⌘|W"), R("Вернуть закрытую", "Восстановить последнюю закрытую вкладку.", "Ctrl / ⌘|Shift|T"), R("Следующая / предыдущая", "Переключайтесь между открытыми вкладками.", "Ctrl|Tab\nCtrl|Shift|Tab"), R("Перейти к номеру", "1–8 — по порядку, 9 — последняя вкладка.", "Ctrl / ⌘|1–9"), R("Открыть мод в фоне", "Средняя кнопка или Ctrl / ⌘ + клик по моду.", "Средняя кнопка\nCtrl / ⌘|Клик"), R("Открыть и сразу перейти", "Добавьте Shift к открытию мода в фоне.", "Shift|Средняя кнопка\nCtrl / ⌘|Shift|Клик"), R("Изменить порядок", "Тяните вкладку за её название.", "Перетаскивание")),
			new RuneHelpTopic("Назад и вперёд", "Переходы внутри текущей вкладки, как в браузере.", "Это история навигации. Для отмены значений настроек\nиспользуйте Ctrl / ⌘ + Z и Ctrl / ⌘ + Y.", "back", R("Назад / вперёд", "Вернуться к предыдущему моду или разделу.", "Alt|← / →\n⌘|[ / ]"), R("Боковые кнопки мыши", "Работают при поддержке устройством и игрой.", "Mouse 4 / 5"), R("Жест трекпадом", "Горизонтальный свайп над полями настроек.", "Свайп ← / →"), R("Прокрутить вкладки", "Над полосой вкладок жест прокручивает её.\nВертикальный скролл всегда прокручивает список.", "Колесо / трекпад"), R("Выбрать раздел", "Вертикальный список сразу справа от модов.", "Клик по разделу")),
			new RuneHelpTopic("Консоль", "Команды игры с подсказками и историей.", "Нативная консоль Valheim должна быть разрешена. Команды\nисполняются с обычными правами игры и сервера; читы не включаются.", "console", R("Открыть консоль", "Или нажмите «Консоль» внизу списка модов.", "Ctrl / ⌘|`"), R("Выполнить команду", "Результат появится в области вывода.", "Enter"), R("Дополнить ввод", "Название команды и поддерживаемые аргументы.", "Tab"), R("Посмотреть историю", "Выбрать ранее введённую команду.", "↑ / ↓"), R("Закрыть консоль", "Черновик и история команд сохраняются.", "Esc")),
			new RuneHelpTopic("Вид и удобства", "Настройте рабочее пространство под себя.", "Положение окна, прозрачность и свёрнутый список модов сохраняются.\nПока окно открыто, управление персонажем блокируется по настройке.", "settings", R("Переместить окно", "Тяните за свободный фон в любой части окна.\nПоложение сохранится после отпускания мыши.", "Перетаскивание"), R("Оставить только иконки", "Кнопка над списком модов разворачивает его.", "Панель модов"), R("Настроить прозрачность", "Ползунок внизу: 0–80%. Сброс — до 12%.", "Ползунок"), R("Точно менять прозрачность", "Стрелки: шаг 1%. Home / End: границы.", "← / →\nHome / End"), R("Вернуть окно на экран", "Или дважды щёлкните по области логотипа.", "Ctrl|F1"), R("Восстановить масштаб", "Или трижды щёлкните по области логотипа.", "Shift|F1"), R("Масштаб, язык и плотность", "Откройте «Настройки» в верхней панели.", "Настройки"), R("Быстро отфильтровать", "Избранное — слева. Изменённые, клавиши\nи расширенные параметры — в настройках.", "Фильтры")),
			new RuneHelpTopic("Моды и файлы", "Ссылки, локальные файлы и сообщество.", "Иконки берутся из установленных пакетов. Если страницы мода\nне найдены в метаданных, доступны ссылки поиска по площадкам.", "external", R("Открыть страницу мода", "Thunderstore, Nexus или Hexium.", "↗"), R("Перейти в Discord TRXTR", "Иконка в правом нижнем углу окна.", "Discord"), R("Редактировать файлы", "Настройки → Редактор файлов.\nЭто отдельный редактор, вне истории Ctrl+Z.", "Настройки"), R("Обновить список модов", "Повторно собрать доступные настройки.", "Настройки")),
			new RuneHelpTopic("Профили", "Разные наборы настроек без выхода из игры.", "Переключение очищает историю Ctrl+Z; прежние значения сохраняются\nчерез «До переключения». Некоторые моды требуют перезапуск.", "profiles", R("Создать профиль", "Меню в шапке → Создать. Сохранит текущие\nнастройки доступных модов, кроме Hugin's Desk.", "Профили"), R("Быстро переключиться", "Выбери название в меню профилей. При наличии\nправок появится выбор: сохранить или отбросить.", "Клик по имени"), R("Посмотреть отличия", "Управление → выбери профиль. Видны будущие\nизменения и недоступные поля.", "Управление"), R("Изменить настройки профиля", "Нажми «Редактировать», меняй обычные поля.\nЗатем сохрани изменения через меню профиля.", "Редактировать"), R("Переименовать или скопировать", "Действия в нижней части окна управления.", "Управление"), R("Удалить профиль", "Удаляется только снимок. Значения в игре\nостанутся; удаление нужно подтвердить.", "Удалить"), R("Вернуться к прежним значениям", "Восстановить снимок перед переключением.\nСерверные блокировки продолжают действовать.", "До переключения"))
		};

		public static readonly RuneHelpTopic[] English = new RuneHelpTopic[8]
		{
			new RuneHelpTopic("Essentials", "Everyday controls, a keystroke away.", "In this guide: wheel or ↑ ↓ to scroll. PgUp / PgDn to turn a page.\nHome / End jump to the beginning or end of a topic.", "all-mods", R("Open or close the window", "Default shortcut.", "F1"), R("Return to the game", "Click outside the window and its editors.\nControls resume after releasing the button.", "Click outside"), R("Close the top panel", "Then the editor, console or entire window.", "Esc"), R("Find a setting", "Search mod, section, description or value.", "Ctrl / ⌘|F"), R("Undo a change", "Revert the last setting edit.", "Ctrl / ⌘|Z"), R("Redo a change", "Restore the step you just undid.", "Ctrl / ⌘|Y\nCtrl / ⌘|Shift|Z"), R("Learn about a field", "Description, range and default value.", "ⓘ")),
			new RuneHelpTopic("Undo & editing", "Setting history is shared across all tabs.", "Up to 100 steps for this game session. Text input keeps its own shortcuts.\nServer-locked or externally changed settings are never overwritten.", "reset", R("Undo a step", "One slider drag counts as one step.", "Ctrl / ⌘|Z"), R("Redo a step", "A new edit clears the redo history.", "Ctrl / ⌘|Y\nCtrl / ⌘|Shift|Z"), R("Finish typing", "Leave the field before undoing its value.\nClick an empty area of the window.", "Click outside"), R("Open the detailed editor", "Right-click the setting name.", "Right-click"), R("Read field information", "Click to open a scrollable information panel.", "ⓘ"), R("Add a favorite", "Use the star beside the value.", "Star"), R("Restore the default", "Reset beside the field; reset visible in Settings.", "Reset"), R("Save a change", "Values apply immediately and are saved by the mod.\nNo Apply button is needed.", "Automatic")),
			new RuneHelpTopic("Tabs", "Keep several mods or sections of one mod open.", "Open tabs, their order, search, sections and scroll positions survive\nclosing the window and restarting the game.", "sidebar", R("Create a tab", "Choose a mod from the picker.", "Ctrl / ⌘|T"), R("Close the current tab", "Also: × or middle-click on the tab.", "Ctrl / ⌘|W"), R("Reopen a closed tab", "Restore the last closed tab.", "Ctrl / ⌘|Shift|T"), R("Next / previous tab", "Cycle through open tabs.", "Ctrl|Tab\nCtrl|Shift|Tab"), R("Select a tab by number", "1–8 select by position; 9 selects the last tab.", "Ctrl / ⌘|1–9"), R("Open a mod in the background", "Middle-click or Ctrl / ⌘ + click a mod.", "Middle-click\nCtrl / ⌘|Click"), R("Open and activate a mod", "Hold Shift while opening in the background.", "Shift|Middle-click\nCtrl / ⌘|Shift|Click"), R("Reorder tabs", "Drag a tab by its title.", "Drag")),
			new RuneHelpTopic("Back & forward", "Navigation within the current tab, like a browser.", "This is navigation history. To undo or redo setting values instead,\nuse Ctrl / ⌘ + Z and Ctrl / ⌘ + Y.", "back", R("Go back / forward", "Return to the previous mod or section.", "Alt|← / →\n⌘|[ / ]"), R("Mouse thumb buttons", "When supported by your device and the game.", "Mouse 4 / 5"), R("Trackpad gesture", "Swipe horizontally over the setting fields.", "Swipe ← / →"), R("Scroll the tab strip", "Over tabs, the gesture scrolls the strip.\nVertical scrolling always scrolls the list.", "Wheel / trackpad"), R("Select a section", "Use the vertical list beside the mods.", "Click section")),
			new RuneHelpTopic("Console", "Game commands with suggestions and history.", "The native Valheim console must be enabled. Commands use normal game\nand server permissions; this panel does not enable cheats.", "console", R("Open the console", "Or use Console below the mod list.", "Ctrl / ⌘|`"), R("Run a command", "See the result in the output area.", "Enter"), R("Complete input", "Command names and supported arguments.", "Tab"), R("Browse command history", "Recall a previously entered command.", "↑ / ↓"), R("Close the console", "Your draft and command history are saved.", "Esc")),
			new RuneHelpTopic("Layout & comfort", "Make the workspace your own.", "Position, transparency and the collapsed mod list are saved.\nCharacter input is blocked while open, according to your settings.", "settings", R("Move the window", "Drag empty space anywhere in the window.\nIts position is saved when you release.", "Drag"), R("Show only mod icons", "The button above mods expands the list again.", "Mod panel"), R("Set transparency", "Footer slider: 0–80%. Reset restores 12%.", "Slider"), R("Adjust transparency precisely", "Arrow keys: 1%. Home / End: minimum / maximum.", "← / →\nHome / End"), R("Restore window position", "Or double-click the logo area.", "Ctrl|F1"), R("Restore interface scale", "Or triple-click the logo area.", "Shift|F1"), R("Scale, language and density", "Open Settings in the top header.", "Settings"), R("Filter settings", "Favorites on the left; changed, keybinds\nand advanced settings in the Settings menu.", "Filters")),
			new RuneHelpTopic("Mods & files", "Package pages, local files and the community.", "Icons come from installed packages. When a mod's metadata has no\nverified package page, labelled marketplace search links are provided.", "external", R("Open a mod page", "Thunderstore, Nexus or Hexium.", "↗"), R("Visit TRXTR Discord", "The icon in the bottom right of the window.", "Discord"), R("Edit config files", "Settings → File editor.\nThis editor is outside the setting undo history.", "Settings"), R("Refresh the mod list", "Collect the available settings again.", "Settings")),
			new RuneHelpTopic("Profiles", "Different setting presets without leaving the game.", "Switching clears Ctrl+Z history; use Before last switch to recover values.\nSome mods require a restart to use changed settings.", "profiles", R("Create a profile", "Header menu → Create. Captures available mod\nsettings, excluding Hugin's Desk preferences.", "Profiles"), R("Switch quickly", "Choose a name in the profile menu. Unsaved edits\ncan be saved or discarded before switching.", "Click name"), R("Review differences", "Manage → choose a profile. See the changes\nand any unavailable fields before applying.", "Manage"), R("Edit profile values", "Use Edit settings, then edit ordinary fields.\nSave changes from the profile menu when done.", "Edit settings"), R("Rename or duplicate", "Actions are at the bottom of the manager.", "Manage"), R("Delete a profile", "Only the snapshot is removed. Current game\nvalues stay unchanged; confirmation is required.", "Delete"), R("Recover previous values", "Restore the snapshot from before switching.\nServer locks still apply.", "Before last switch"))
		};

		public static Box TopicButton(int index)
		{
			return new Box(197f, 198 + index * 59, 222f, 50f);
		}

		private static RuneHelpRow R(string title, string detail, string keys)
		{
			return new RuneHelpRow(title, detail, keys);
		}

		public static RuneHelpTopic[] Topics(bool russian)
		{
			if (!russian)
			{
				return English;
			}
			return Russian;
		}

		public static void Frame(IRuneCanvas c, bool russian, int selected)
		{
			RunePaint.Outline(c, Window, "#1D1D1D", 16f);
			c.Text(russian ? "Управление и возможности" : "Controls & features", new Box(209f, 92f, 960f, 40f), 28, "#E6E6E6", "heading");
			c.Text(russian ? "Всё, что нужно для удобной работы с конфигами." : "A quick reference for your configuration workspace.", new Box(210f, 138f, 960f, 26f), 16, "#AAAAAA");
			RunePaint.Icon(c, "close", Close.Inset(9f));
			c.Fill(new Box(197f, 177f, 1080f, 1f), "#414141");
			c.Fill(new Box(437f, 198f, 1f, 586f), "#414141");
			RuneHelpTopic[] array = Topics(russian);
			for (int i = 0; i < array.Length; i++)
			{
				Box rect = TopicButton(i);
				bool flag = selected == i;
				if (flag)
				{
					c.Round(rect, "#333333", 9f);
				}
				RunePaint.Icon(c, array[i].Icon, new Box(rect.X + 14f, rect.Y + 14f, 21f, 21f), flag ? "#E6E6E6" : "#AAAAAA");
				c.Text(array[i].Title, new Box(rect.X + 47f, rect.Y + 14f, rect.W - 55f, 27f), 16, flag ? "#E6E6E6" : "#AAAAAA");
			}
			c.Text(russian ? "⌘ — Command на Mac" : "⌘ means Command on Mac", new Box(209f, 680f, 216f, 25f), 13, "#AAAAAA");
			c.Text(russian ? "Ctrl работает и на Mac." : "Ctrl also works on Mac.", new Box(209f, 706f, 216f, 25f), 13, "#AAAAAA");
			RuneHelpTopic runeHelpTopic = array[selected];
			c.Text(runeHelpTopic.Title, new Box(467f, 202f, 781f, 35f), 24, "#E6E6E6", "heading");
			c.Text(runeHelpTopic.Subtitle, new Box(467f, 245f, 781f, 27f), 16, "#AAAAAA");
			c.Fill(new Box(197f, 799f, 1080f, 1f), "#414141");
			c.Text(russian ? "Esc — закрыть справку" : "Esc closes this guide", new Box(209f, 819f, 600f, 26f), 14, "#AAAAAA");
			RunePaint.Outline(c, Done, "#333333");
			c.Text(russian ? "Понятно" : "Got it", new Box(Done.X, Done.Y + 7f, Done.W, 26f), 16, "#E6E6E6", "body", "center");
		}

		public static void Content(IRuneCanvas c, bool russian, int selected, float scroll)
		{
			RuneHelpTopic runeHelpTopic = Topics(russian)[selected];
			float num = Viewport.Y - scroll;
			c.PushClip(Viewport);
			RuneHelpRow[] rows = runeHelpTopic.Rows;
			foreach (RuneHelpRow runeHelpRow in rows)
			{
				Box rect = new Box(Viewport.X + 8f, num, Viewport.W - 16f, runeHelpRow.Height);
				if (rect.Intersects(Viewport))
				{
					c.Round(rect, "#262626", 9f);
					c.Text(runeHelpRow.Title, new Box(rect.X + 18f, num + 13f, 450f, 29f), 18, "#E6E6E6", "heading");
					Lines(c, runeHelpRow.Detail, new Box(rect.X + 18f, num + 41f, 450f, runeHelpRow.Height - 41f), 15, "#AAAAAA", 20f);
					float num2 = num + (runeHelpRow.Height - (float)(runeHelpRow.Keys.Length * 32) + 4f) / 2f;
					string[][] keys = runeHelpRow.Keys;
					foreach (string[] array in keys)
					{
						float num3 = 0f;
						string[] array2 = array;
						foreach (string key in array2)
						{
							num3 += KeyWidth(key) + 6f;
						}
						float num4 = rect.Right - 18f - num3 + 6f;
						array2 = array;
						foreach (string text in array2)
						{
							float num5 = KeyWidth(text);
							Box rect2 = new Box(num4, num2, num5, 27f);
							c.Round(rect2, "#323232", 5f);
							c.Stroke(rect2, "#4A4A4A", 5f);
							c.Text(text, new Box(num4, num2 + 4f, num5, 23f), 14, "#E6E6E6", "body", "center");
							num4 += num5 + 6f;
						}
						num2 += 32f;
					}
				}
				num += runeHelpRow.Height + 8f;
			}
			Lines(c, runeHelpTopic.Note, new Box(Viewport.X + 15f, num + 12f, Viewport.W - 30f, 100f), 14, "#AAAAAA", 22f);
			c.PopClip();
		}

		private static float KeyWidth(string key)
		{
			return Math.Max(28, key.Length * 8 + 18);
		}

		private static void Lines(IRuneCanvas c, string text, Box box, int size, string color, float lineHeight)
		{
			string[] array = text.Split(new char[1] { '\n' });
			foreach (string text2 in array)
			{
				c.Text(text2, new Box(box.X, box.Y, box.W, lineHeight + 2f), size, color);
				box.Y += lineHeight;
			}
		}
	}
	public sealed class RuneHistoryGesture
	{
		private float last = -100f;

		private float total;

		private bool consumed;

		public int Feed(float x, float y, float time)
		{
			if (time - last > 0.25f)
			{
				total = 0f;
				consumed = false;
			}
			last = time;
			if (Math.Abs(x) <= Math.Abs(y) * 2f || Math.Abs(x) <= 0.1f)
			{
				return 0;
			}
			if (consumed)
			{
				return 0;
			}
			total += x;
			if (Math.Abs(total) <= 9f)
			{
				return 0;
			}
			consumed = true;
			if (!(total > 0f))
			{
				return -1;
			}
			return 1;
		}
	}
	public readonly struct RuneLayout
	{
		public readonly bool Collapsed;

		public static readonly Box Header = new Box(12f, 8f, 1450f, 76f);

		public float ModWidth => Collapsed ? 76 : 268;

		public float ContentX => ModWidth + 244f;

		public Box Mods => new Box(16f, 264f, ModWidth - 32f, 564f);

		public Box Collapse => new Box(ModWidth - 48f, 100f, 32f, 36f);

		public Box Sections => new Box(ModWidth + 16f, 204f, 204f, 638f);

		public Box Body => new Box(ContentX, 246f, 1438f - ContentX, 596f);

		public Box ConsoleBody => new Box(ContentX, 246f, 1438f - ContentX, 313f);

		public Box ConsoleDrawer => new Box(ContentX, 575f, 1438f - ContentX, 267f);

		public Box ConsoleInput => new Box(ContentX + 18f, 788f, 1110f - ContentX - 18f, 36f);

		public Box ConsoleButton => new Box(16f, 854f, ModWidth - 32f, 35f);

		public Box ModTabs => new Box(ModWidth + 122f, 100f, 1386f - ModWidth - 122f, 42f);

		public Box Back => new Box(ModWidth + 30f, 103f, 36f, 36f);

		public Box Forward => new Box(ModWidth + 72f, 103f, 36f, 36f);

		public RuneLayout(bool collapsed)
		{
			Collapsed = collapsed;
		}

		public static bool HeaderCanDrag(float x, float y)
		{
			if (Header.Contains(x, y) && !RuneGeometry.Search.Contains(x, y) && !RuneGeometry.Profiles.Contains(x, y) && !RuneGeometry.Tools.Contains(x, y) && !RuneGeometry.Help.Contains(x, y))
			{
				return !RuneGeometry.Close.Contains(x, y);
			}
			return false;
		}

		public static Box Info(float y, bool compact = false)
		{
			return new Box(1306f, y + (float)(compact ? 5 : 15), 30f, 38f);
		}

		public static Box InfoPopup(Box anchor)
		{
			return new Box(RuneGeometry.Clamp(anchor.Right - 520f, 16f, 938f), RuneGeometry.Clamp(anchor.Bottom + 8f, 98f, 514f), 520f, 328f);
		}

		public static Box ClampWindow(Box window, float screenWidth, float screenHeight)
		{
			return new Box(RuneGeometry.Clamp(window.X, 0f, Math.Max(0f, screenWidth - window.W)), RuneGeometry.Clamp(window.Y, 0f, Math.Max(0f, screenHeight - window.H)), window.W, window.H);
		}
	}
	public sealed class RuneLineMask
	{
		public int Width;

		public int Height;

		public float LogicalWidth;

		public float LogicalHeight;

		public float OffsetX;

		public float OffsetY;

		public byte[] Alpha;

		public static RuneLineMask Create(float dx, float dy, float thickness, float density = 2f)
		{
			float num = thickness / 2f + 1f;
			RuneLineMask runeLineMask = new RuneLineMask
			{
				OffsetX = Math.Min(0f, dx) - num,
				OffsetY = Math.Min(0f, dy) - num,
				LogicalWidth = Math.Abs(dx) + num * 2f,
				LogicalHeight = Math.Abs(dy) + num * 2f
			};
			runeLineMask.Width = Math.Max(1, (int)Math.Ceiling(runeLineMask.LogicalWidth * density));
			runeLineMask.Height = Math.Max(1, (int)Math.Ceiling(runeLineMask.LogicalHeight * density));
			runeLineMask.Alpha = new byte[runeLineMask.Width * runeLineMask.Height];
			float num2 = dx * dx + dy * dy;
			for (int i = 0; i < runeLineMask.Height; i++)
			{
				for (int j = 0; j < runeLineMask.Width; j++)
				{
					float num3 = ((float)j + 0.5f) / (float)runeLineMask.Width * runeLineMask.LogicalWidth + runeLineMask.OffsetX;
					float num4 = ((float)i + 0.5f) / (float)runeLineMask.Height * runeLineMask.LogicalHeight + runeLineMask.OffsetY;
					float num5 = ((num2 < 1E-05f) ? 0f : Math.Max(0f, Math.Min(1f, (num3 * dx + num4 * dy) / num2)));
					float num6 = num3 - num5 * dx;
					float num7 = num4 - num5 * dy;
					float num8 = (float)Math.Sqrt(num6 * num6 + num7 * num7);
					float num9 = Math.Max(0f, Math.Min(1f, (thickness / 2f - num8) * density + 0.5f));
					runeLineMask.Alpha[(runeLineMask.Height - 1 - i) * runeLineMask.Width + j] = (byte)Math.Round(num9 * 255f);
				}
			}
			return runeLineMask;
		}
	}
	public static class RuneOpacity
	{
		public const float DefaultTransparency = 12f;

		public const float MaxTransparency = 80f;

		public static float Clamp(float percent)
		{
			if (!float.IsNaN(percent) && !float.IsInfinity(percent))
			{
				return (float)Math.Round(Math.Max(0f, Math.Min(80f, percent)));
			}
			return 12f;
		}

		public static float Alpha(float percent)
		{
			return 1f - Clamp(percent) / 100f;
		}

		public static float ToSlider(float percent)
		{
			return Clamp(percent) / 80f;
		}

		public static float FromSlider(float value)
		{
			return Clamp(RuneGeometry.Clamp(value, 0f, 1f) * 80f);
		}
	}
	public sealed class RuneOpacityState
	{
		public float Value { get; private set; }

		public bool Dirty { get; private set; }

		public float Alpha => RuneOpacity.Alpha(Value);

		public RuneOpacityState(float value)
		{
			Reset(value);
		}

		public void Reset(float value)
		{
			Value = RuneOpacity.Clamp(value);
			Dirty = false;
		}

		public void Set(float value)
		{
			value = RuneOpacity.Clamp(value);
			if (value != Value)
			{
				Value = value;
				Dirty = true;
			}
		}

		public bool Commit(Action<float> save)
		{
			if (!Dirty)
			{
				return false;
			}
			try
			{
				save(Value);
				Dirty = false;
				return true;
			}
			catch
			{
				Dirty = true;
				throw;
			}
		}
	}
	public static class RuneOutsideClick
	{
		public static bool ShouldClose(float x, float y, int button, bool captured, Box main, Box? fileEditor, Box? settingEditor)
		{
			if (button >= 0 && button <= 2 && !captured && !main.Contains(x, y) && (!fileEditor.HasValue || !fileEditor.GetValueOrDefault().Contains(x, y)))
			{
				return !(settingEditor?.Contains(x, y) ?? false);
			}
			return false;
		}
	}
	public sealed class RuneCloseClickGuard
	{
		private int _releaseThrough = -1;

		public int Button { get; private set; } = -1;

		public bool Blocks(int frame)
		{
			if (Button < 0)
			{
				return frame <= _releaseThrough;
			}
			return true;
		}

		public void Begin(int button, int frame)
		{
			Button = button;
			_releaseThrough = frame + 1;
		}

		public void Poll(bool held, int frame)
		{
			if (!(Button < 0 || held))
			{
				Button = -1;
				_releaseThrough = frame + 1;
			}
		}

		public void Clear()
		{
			Button = -1;
			_releaseThrough = -1;
		}
	}
	public readonly struct RuneProfileMenuLayout
	{
		private readonly float height;

		public Box Menu => new Box(766f, 73f, 346f, height + 162f);

		public Box List => new Box(782f, 127f, 308f, height);

		public Box Create => new Box(782f, 145f + height, 148f, 38f);

		public Box Manage => new Box(942f, 145f + height, 148f, 38f);

		public Box Note => new Box(782f, 195f + height, 308f, 24f);

		public RuneProfileMenuLayout(int count)
		{
			height = Math.Min(306, Math.Max(76, count * 44));
		}
	}
	public static class RuneProfilePaint
	{
		public static readonly Box Window = new Box(197f, 100f, 1080f, 740f);

		public static readonly Box Close = new Box(1225f, 118f, 34f, 34f);

		public static readonly Box List = new Box(215f, 193f, 239f, 535f);

		public static readonly Box Create = new Box(215f, 754f, 239f, 39f);

		public static readonly Box Changes = new Box(483f, 432f, 750f, 286f);

		public static readonly Box Apply = new Box(483f, 266f, 178f, 40f);

		public static readonly Box Edit = new Box(675f, 266f, 178f, 40f);

		public static readonly Box Save = new Box(867f, 266f, 366f, 40f);

		public static readonly Box Rename = new Box(483f, 754f, 155f, 39f);

		public static readonly Box Duplicate = new Box(650f, 754f, 155f, 39f);

		public static readonly Box Delete = new Box(817f, 754f, 135f, 39f);

		public static readonly Box Restore = new Box(964f, 754f, 269f, 39f);

		public static readonly Box NameWindow = new Box(462f, 280f, 550f, 310f);

		public static readonly Box NameField = new Box(488f, 367f, 498f, 40f);

		public static readonly Box NameCancel = new Box(670f, 526f, 148f, 40f);

		public static readonly Box NameSave = new Box(834f, 526f, 152f, 40f);

		public static void Header(IRuneCanvas c, string name, bool dirty, bool ru)
		{
			Box profiles = RuneGeometry.Profiles;
			RunePaint.Outline(c, profiles);
			RunePaint.Icon(c, "profiles", new Box(profiles.X + 12f, profiles.Y + 12f, 19f, 19f), "#AAAAAA");
			c.Text(name ?? (ru ? "Профили" : "Profiles"), new Box(profiles.X + 41f, profiles.Y + 11f, profiles.W - 80f, 27f), 16, "#E6E6E6");
			if (dirty)
			{
				c.Round(new Box(profiles.Right - 38f, profiles.Y + 18f, 6f, 6f), "#E6E6E6", 3f);
			}
			RunePaint.Icon(c, "chevron-down", new Box(profiles.Right - 27f, profiles.Y + 12f, 18f, 18f));
		}

		public static void MenuFrame(IRuneCanvas c, bool ru, int count)
		{
			RuneProfileMenuLayout runeProfileMenuLayout = new RuneProfileMenuLayout(count);
			RunePaint.Popup(c, runeProfileMenuLayout.Menu, ru ? "Профили конфигов" : "Config profiles");
			Button(c, runeProfileMenuLayout.Create, ru ? "+ Создать" : "+ Create");
			Button(c, runeProfileMenuLayout.Manage, ru ? "Управление" : "Manage");
			c.Text(ru ? "Выбор профиля применит его настройки." : "Selecting a profile applies its settings.", runeProfileMenuLayout.Note, 12, "#AAAAAA");
		}

		public static void Item(IRuneCanvas c, Box r, string name, string detail, bool active, bool selected, bool dirty)
		{
			if (selected)
			{
				c.Round(r, "#333333", 7f);
			}
			if (active)
			{
				RunePaint.Icon(c, "check", new Box(r.X + 10f, r.Y + 12f, 18f, 18f));
			}
			c.Text(name + (dirty ? " •" : ""), new Box(r.X + 36f, r.Y + 9f, r.W - 48f, 26f), 16, active ? "#E6E6E6" : "#AAAAAA");
			if (!string.IsNullOrEmpty(detail))
			{
				c.Text(detail, new Box(r.X + 36f, r.Y + 35f, r.W - 48f, 21f), 12, "#AAAAAA");
			}
		}

		public static void Frame(IRuneCanvas c, bool ru)
		{
			RunePaint.Outline(c, Window, "#1D1D1D", 16f);
			c.Text(ru ? "Профили конфигов" : "Config profiles", new Box(223f, 125f, 960f, 40f), 27, "#E6E6E6", "heading");
			RunePaint.Icon(c, "close", Close.Inset(8f));
			c.Fill(new Box(215f, 177f, 1044f, 1f), "#414141");
			c.Fill(new Box(467f, 193f, 1f, 535f), "#414141");
			c.Fill(new Box(215f, 737f, 1044f, 1f), "#414141");
			Button(c, Create, ru ? "+ Новый профиль" : "+ New profile");
			c.Text(ru ? "Профили меняют настройки модов. Интерфейс и вкладки сохраняются отдельно." : "Profiles change mod settings. Your interface and tabs are stored separately.", new Box(223f, 809f, 1030f, 24f), 13, "#AAAAAA");
		}

		public static void Heading(IRuneCanvas c, string name, string subtitle)
		{
			c.Text(name, new Box(483f, 195f, 750f, 36f), 24, "#E6E6E6", "heading");
			c.Text(subtitle, new Box(483f, 234f, 750f, 24f), 14, "#AAAAAA");
		}

		public static void Stats(IRuneCanvas c, bool ru, int ready, int unchanged, int unavailable)
		{
			string[] array = ((!ru) ? new string[3] { "Will change", "Unchanged", "Unavailable" } : new string[3] { "Изменится", "Без изменений", "Недоступно" });
			int[] array2 = new int[3] { ready, unchanged, unavailable };
			for (int i = 0; i < 3; i++)
			{
				Box rect = new Box(483 + i * 254, 326f, 242f, 70f);
				c.Round(rect, "#262626", 9f);
				c.Text(array2[i].ToString(), new Box(rect.X + 16f, rect.Y + 10f, 64f, 32f), 23, "#E6E6E6", "heading");
				c.Text(array[i], new Box(rect.X + 16f, rect.Y + 43f, 210f, 22f), 13, "#AAAAAA");
			}
			c.Text(ru ? "ИЗМЕНЕНИЯ ПРИ ПЕРЕКЛЮЧЕНИИ" : "CHANGES ON SWITCH", new Box(483f, 409f, 750f, 20f), 12, "#AAAAAA");
		}

		public static void Change(IRuneCanvas c, Box r, string title, string mod, string before, string after, string status, bool unavailable)
		{
			c.Round(r, "#262626", 7f);
			c.Text(title, new Box(r.X + 14f, r.Y + 9f, r.W - 205f, 25f), 16, "#E6E6E6");
			c.Text(status, new Box(r.Right - 190f, r.Y + 11f, 175f, 23f), 12, "#AAAAAA", "body", "right");
			c.Text(mod, new Box(r.X + 14f, r.Y + 36f, r.W - 28f, 20f), 12, "#AAAAAA");
			c.Text(before ?? "—", new Box(r.X + 14f, r.Y + 60f, 280f, 24f), 14, "#AAAAAA");
			c.Text("→", new Box(r.X + 308f, r.Y + 60f, 24f, 24f), 14, "#AAAAAA");
			c.Text(after ?? "—", new Box(r.X + 347f, r.Y + 60f, r.W - 362f, 24f), 14, unavailable ? "#AAAAAA" : "#E6E6E6");
		}

		public static void Button(IRuneCanvas c, Box r, string title, bool enabled = true)
		{
			RunePaint.Outline(c, r, enabled ? "#262626" : "#1D1D1D");
			c.Text(title, new Box(r.X + 8f, r.Y + 10f, r.W - 16f, 27f), 15, enabled ? "#E6E6E6" : "#777777", "body", "center");
		}

		public static void NameForm(IRuneCanvas c, bool ru, string title, string description, string error)
		{
			RunePaint.Popup(c, NameWindow, title);
			c.Text(description, new Box(488f, 336f, 498f, 24f), 14, "#AAAAAA");
			RunePaint.Outline(c, NameField);
			c.Text(error ?? (ru ? "Название: до 48 символов." : "Name: up to 48 characters."), new Box(488f, 424f, 498f, 72f), 14, "#AAAAAA");
			Button(c, NameCancel, ru ? "Отмена" : "Cancel");
			Button(c, NameSave, ru ? "Сохранить" : "Save");
		}
	}
	public sealed class RuneProfileValue
	{
		public string Guid;

		public string Mod;

		public string Section;

		public string Key;

		public string Type;

		public string Value;

		public bool Restart;

		public string Identity()
		{
			return Part(Guid) + Part(Section) + Part(Key);
		}

		private static string Part(string s)
		{
			return (s ?? "").Length + ":" + s;
		}

		public RuneProfileValue Copy()
		{
			return (RuneProfileValue)MemberwiseClone();
		}
	}
	public sealed class RuneProfile
	{
		public string Id;

		public string Name;

		public string UpdatedUtc;

		public List<RuneProfileValue> Values = new List<RuneProfileValue>();
	}
	public sealed class RuneProfileRecovery
	{
		public string PreviousActiveId;

		public string CreatedUtc;

		public List<RuneProfileValue> Values = new List<RuneProfileValue>();
	}
	public sealed class RuneProfileLibrary
	{
		public int Schema = 1;

		public string ActiveId;

		public List<RuneProfile> Profiles = new List<RuneProfile>();

		public RuneProfileRecovery Recovery;

		public RuneProfile Find(string id)
		{
			return Profiles.FirstOrDefault((RuneProfile p) => p.Id == id);
		}

		public string CheckedName(string name, string exceptId = null)
		{
			name = (name ?? "").Trim();
			if (name.Length == 0 || name.Length > 48 || name.Any(char.IsControl))
			{
				throw new ArgumentException("Use 1–48 characters without line breaks.");
			}
			if (Profiles.Any((RuneProfile p) => p.Id != exceptId && string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase)))
			{
				throw new ArgumentException("A profile with this name already exists.");
			}
			return name;
		}

		public RuneProfile Create(string name, IEnumerable<RuneProfileValue> values, bool activate = true)
		{
			if (Profiles.Count >= 64)
			{
				throw new InvalidOperationException("The limit is 64 profiles.");
			}
			RuneProfile runeProfile = new RuneProfile
			{
				Id = Guid.NewGuid().ToString("N"),
				Name = CheckedName(name),
				UpdatedUtc = DateTime.UtcNow.ToString("o"),
				Values = values.Select((RuneProfileValue v) => v.Copy()).ToList()
			};
			if (runeProfile.Values.Count == 0)
			{
				throw new InvalidOperationException("There are no supported settings to save.");
			}
			Profiles.Add(runeProfile);
			if (activate)
			{
				ActiveId = runeProfile.Id;
			}
			return runeProfile;
		}

		public void Rename(string id, string name)
		{
			RuneProfile obj = Find(id) ?? throw new InvalidOperationException("Profile not found.");
			obj.Name = CheckedName(name, id);
			obj.UpdatedUtc = DateTime.UtcNow.ToString("o");
		}

		public void Update(string id, IEnumerable<RuneProfileValue> values)
		{
			RuneProfile runeProfile = Find(id) ?? throw new InvalidOperationException("Profile not found.");
			Dictionary<string, RuneProfileValue> dictionary = runeProfile.Values.ToDictionary<RuneProfileValue, string, RuneProfileValue>((RuneProfileValue v) => v.Identity(), (RuneProfileValue v) => v.Copy(), StringComparer.Ordinal);
			foreach (RuneProfileValue value in values)
			{
				dictionary[value.Identity()] = value.Copy();
			}
			runeProfile.Values = (from v in dictionary.Values
				orderby v.Mod, v.Section, v.Key
				select v).ToList();
			runeProfile.UpdatedUtc = DateTime.UtcNow.ToString("o");
		}

		public void Remove(string id)
		{
			Profiles.RemoveAll((RuneProfile p) => p.Id == id);
			if (ActiveId == id)
			{
				ActiveId = null;
			}
			if (Recovery?.PreviousActiveId == id)
			{
				Recovery.PreviousActiveId = null;
			}
		}
	}
	public sealed class RuneWindowDrag
	{
		private Box _start;

		private float _pointerX;

		private float _pointerY;

		public bool Active { get; private set; }

		public bool Moved { get; private set; }

		public static bool CanStart(float localX, float localY, int button, bool available, bool used, bool captured, bool overControl)
		{
			if (button == 0 && available && !used && !captured && !overControl)
			{
				return RuneGeometry.Window.Contains(localX, localY);
			}
			return false;
		}

		public void Begin(Box window, float desktopX, float desktopY)
		{
			_start = window;
			_pointerX = desktopX;
			_pointerY = desktopY;
			Active = true;
			Moved = false;
		}

		public bool Move(float desktopX, float desktopY, float screenWidth, float screenHeight, out Box window)
		{
			window = _start;
			if (!Active)
			{
				return false;
			}
			float num = desktopX - _pointerX;
			float num2 = desktopY - _pointerY;
			if (!Moved && num * num + num2 * num2 < 9f)
			{
				return false;
			}
			Moved = true;
			window = RuneLayout.ClampWindow(_start.Move(num, num2), screenWidth, screenHeight);
			return true;
		}

		public void End()
		{
			Active = false;
			Moved = false;
		}
	}
	public static class TrxtrBrand
	{
		public const string Name = "Hugin's Desk";

		public const string FullName = "Hugin's Desk — Configuration Manager";

		public const string LogoAsset = "trxtr-raven";

		public const string DiscordAsset = "discord-mark";

		public const string DiscordUrl = "https://discord.gg/y4wDkScu4";

		public static readonly Box Logo = new Box(30f, 31f, 24f, 24f);

		public static readonly Box Discord = new Box(1400f, 870f, 40f, 38f);

		public static string Caption(string existing)
		{
			if (!(existing == "RuneConfig") && !(existing == "TRXTR Config"))
			{
				return existing;
			}
			return "Hugin's Desk";
		}

		public static string DiscordLink(string value)
		{
			if (!Uri.TryCreate(value?.Trim(), UriKind.Absolute, out Uri result) || result.Scheme != "https" || !string.IsNullOrEmpty(result.UserInfo) || !result.IsDefaultPort)
			{
				return null;
			}
			string text = result.Host.ToLowerInvariant();
			string absolutePath = result.AbsolutePath;
			bool num = text == "discord.gg" && absolutePath.Length > 1;
			bool flag = (text == "discord.com" || text == "www.discord.com") && ((absolutePath.StartsWith("/invite/", StringComparison.Ordinal) && absolutePath.Length > 8) || (absolutePath.StartsWith("/channels/", StringComparison.Ordinal) && absolutePath.Length > 10));
			if (!(num || flag))
			{
				return null;
			}
			return result.AbsoluteUri;
		}
	}
}
namespace ConfigurationManager
{
	public class ConfigFilesEditor : IDisposable
	{
		private enum FileEditState
		{
			None,
			CreatingFolder,
			CreatingFile,
			RenamingFile
		}

		private static readonly string _trashBinDirectory = Path.Combine(Paths.CachePath, "ConfigurationManagerTrashBin");

		private static readonly string[] _directories = new string[3]
		{
			Paths.ConfigPath,
			Paths.PluginPath,
			_trashBinDirectory
		};

		private readonly Dictionary<string, bool> _folderStates = new Dictionary<string, bool>();

		private Vector2 _scrollPosition;

		private string _fileContent;

		private Vector2 _textScrollPosition;

		private Rect _windowRect = new Rect(ConfigurationManager._windowPositionTextEditor.Value, ConfigurationManager._windowSizeTextEditor.Value);

		private const int WindowId = -680;

		private const string SearchBoxName = "searchBoxEditor";

		private const int DirectoryOffset = 20;

		private const string TextEditorControlName = "textEditorTextField";

		private bool _focusSearchBox;

		private bool _focusTextArea;

		private string _searchString;

		private string _errorText;

		private string _activeFile;

		private string _activeDirectory;

		private string _newItemName;

		private string _newItemErrorText;

		private FileEditState _fileNameState;

		private bool _isOpen;

		private bool _clearCache;

		private bool _windowGeometryDirty;

		private Vector2? _pendingWindowSize;

		private int _directoryDepth;

		private readonly Dictionary<string, string[]> _cachedFileTree = new Dictionary<string, string[]>();

		private readonly Dictionary<string, string[]> _cachedDirectories = new Dictionary<string, string[]>();

		private FileSystemWatcher[] _watchers;

		private string SearchString
		{
			get
			{
				return _searchString;
			}
			set
			{
				if (value == null)
				{
					value = string.Empty;
				}
				_searchString = value;
			}
		}

		internal Rect WindowBounds
		{
			get
			{
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				if (!_windowGeometryDirty)
				{
					return new Rect(ConfigurationManager._windowPositionTextEditor.Value, ConfigurationManager._windowSizeTextEditor.Value);
				}
				return _windowRect;
			}
		}

		public bool IsOpen
		{
			get
			{
				return _isOpen;
			}
			set
			{
				//IL_0038: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: Unknown result type (might be due to invalid IL or missing references)
				//IL_004c: Unknown result type (might be due to invalid IL or missing references)
				if (_isOpen != value)
				{
					if (_isOpen && !value && _windowGeometryDirty)
					{
						SaveCurrentSizeAndPosition();
					}
					_isOpen = value;
					if (_isOpen)
					{
						_windowRect = new Rect(ConfigurationManager._windowPositionTextEditor.Value, ConfigurationManager._windowSizeTextEditor.Value);
						SaveCurrentSizeAndPosition();
						_windowGeometryDirty = false;
						_pendingWindowSize = null;
					}
					ClearCache();
				}
			}
		}

		private void SetFileEditState(FileEditState newState)
		{
			_fileNameState = newState;
			_newItemErrorText = string.Empty;
			_newItemName = string.Empty;
		}

		public void OnGUI()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if (IsOpen)
			{
				if (!_windowGeometryDirty)
				{
					((Rect)(ref _windowRect)).size = ConfigurationManager._windowSizeTextEditor.Value;
					((Rect)(ref _windowRect)).position = ConfigurationManager._windowPositionTextEditor.Value;
				}
				Color backgroundColor = GUI.backgroundColor;
				GUI.backgroundColor = Color.white;
				Rect windowRect = GUI.Window(-680, _windowRect, new WindowFunction(DrawWindow), "", ConfigurationManagerStyles.GetWindowStyle());
				if (_pendingWindowSize.HasValue)
				{
					((Rect)(ref windowRect)).size = _pendingWindowSize.Value;
					_pendingWindowSize = null;
				}
				_windowRect = windowRect;
				if (((Rect)(ref _windowRect)).position != ConfigurationManager._windowPositionTextEditor.Value)
				{
					_windowGeometryDirty = true;
				}
				if (_windowGeometryDirty && !UnityInput.Current.GetMouseButton(0))
				{
					SaveCurrentSizeAndPosition();
				}
				GUI.backgroundColor = backgroundColor;
			}
		}

		internal void SaveCurrentSizeAndPosition()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			Vector2 windowSize = new Vector2(Mathf.Clamp(((Rect)(ref _windowRect)).size.x, Mathf.Min(1000f, ConfigurationManager.instance.ScreenWidth), ConfigurationManager.instance.ScreenWidth), Mathf.Clamp(((Rect)(ref _windowRect)).size.y, Mathf.Min(600f, ConfigurationManager.instance.ScreenHeight), ConfigurationManager.instance.ScreenHeight));
			Vector2 windowPosition = new Vector2(Mathf.Clamp(((Rect)(ref _windowRect)).position.x, 0f, ConfigurationManager.instance.ScreenWidth - windowSize.x), Mathf.Clamp(((Rect)(ref _windowRect)).position.y, 0f, ConfigurationManager.instance.ScreenHeight - windowSize.y));
			ConfigurationManager.instance.SaveOwnConfigChanges(delegate
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				ConfigurationManager._windowSizeTextEditor.Value = windowSize;
				ConfigurationManager._windowPositionTextEditor.Value = windowPosition;
			});
			_windowRect = new Rect(windowPosition, windowSize);
			_windowGeometryDirty = false;
			_pendingWindowSize = null;
		}

		private void DrawFilters()
		{
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(ConfigurationManager.RuneText("Типы", "Types"), ConfigurationManagerStyles.GetLabelStyle(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(52f) });
			ConfigurationManager._editableExtensions.Value = GUILayout.TextField(ConfigurationManager._editableExtensions.Value, ConfigurationManagerStyles.GetTextStyle(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(GetFileListWidth() - 82f) });
			GUILayout.EndHorizontal();
			ConfigurationManager._hideModConfigs.Value = RuneTheme.Check(ConfigurationManager._hideModConfigs.Value, ConfigurationManager.RuneText("Скрыть конфиги модов", "Hide active mod configs"));
		}

		private void DrawSearchBox()
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUI.SetNextControlName("searchBoxEditor");
			SearchString = GUILayout.TextField(SearchString, ConfigurationManagerStyles.GetTextStyle(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(GetFileListWidth() - 72f) });
			if (_focusSearchBox)
			{
				GUI.FocusWindow(-680);
				GUI.FocusControl("searchBoxEditor");
				_focusSearchBox = false;
			}
			if (RuneTheme.IconButton(GUILayoutUtility.GetRect(32f, 36f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(32f) }), "reset", ConfigurationManager.RuneText("Очистить поиск", "Clear search")))
			{
				SearchString = string.Empty;
			}
			GUILayout.EndHorizontal();
		}

		private void DrawContentButtons()
		{
			float num = ((Rect)(ref _windowRect)).width - GetFileListWidth() - 84f;
			bool flag = File.Exists(_activeFile);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			try
			{
				GUI.enabled = flag && _fileContent != File.ReadAllText(_activeFile);
				if (GUILayout.Button(ConfigurationManager.RuneText("Сохранить", "Save"), ConfigurationManagerStyles.GetButtonStyle(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num * 0.28f) }))
				{
					File.WriteAllText(_activeFile, _fileContent);
				}
			}
			catch (Exception ex)
			{
				_errorText = ex.Message;
			}
			GUI.enabled = flag;
			if (GUILayout.Button("JSON ✓", ConfigurationManagerStyles.GetButtonStyle(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num * 0.23f) }))
			{
				_errorText = IsValidJSON(_fileContent);
			}
			if (GUILayout.Button("YAML ✓", ConfigurationManagerStyles.GetButtonStyle(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num * 0.23f) }))
			{
				_errorText = IsValidYAML(_fileContent);
			}
			GUI.enabled = true;
			if (GUILayout.Button(ConfigurationManager.RuneText("Закрыть", "Close"), ConfigurationManagerStyles.GetButtonStyle(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num * 0.23f) }))
			{
				IsOpen = false;
			}
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(ConfigurationManager.RuneText("Шрифт", "Font"), ConfigurationManagerStyles.GetLabelStyle(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) });
			if (int.TryParse(GUILayout.TextField(StringExtensionMethods.ToFastString(ConfigurationManager._textEditorFontSize.Value), ConfigurationManagerStyles.GetTextStyle(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(58f) }), out var result))
			{
				ConfigurationManager._textEditorFontSize.Value = Mathf.Clamp(result, 12, 36);
			}
			ConfigurationManager._textEditorWordWrap.Value = RuneTheme.Check(ConfigurationManager._textEditorWordWrap.Value, ConfigurationManager.RuneText("Перенос", "Wrap"), GUILayout.Width(125f));
			ConfigurationManager._textEditorRichText.Value = RuneTheme.Check(ConfigurationManager._textEditorRichText.Value, "Rich text", GUILayout.Width(135f));
			GUILayout.EndHorizontal();
			GUILayout.Label(_errorText ?? "", RuneTheme.Small, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(num),
				GUILayout.Height(22f)
			});
		}

		private void DrawWindow(int windowID)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			if (RuneTheme.ChildFrame(new Rect(Vector2.zero, ((Rect)(ref _windowRect)).size), ConfigurationManager._windowTitleTextEditor.Value))
			{
				IsOpen = false;
			}
			GUITooltips.BeginWindow(_windowRect);
			ComboBox.BeginWindow(windowID);
			try
			{
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				Color backgroundColor = GUI.backgroundColor;
				GUI.backgroundColor = Color.white;
				GUILayout.BeginVertical(ConfigurationManagerStyles.GetBackgroundStyle(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(GetFileListWidth()) });
				DrawFilters();
				DrawSearchBox();
				_scrollPosition = GUITooltips.BeginScrollView(_scrollPosition, GUILayout.Width(GetFileListWidth() - 16f));
				_directoryDepth = 0;
				DrawDirectories(_directories);
				GUITooltips.EndScrollView();
				DrawDirectoriesMenu();
				GUILayout.EndVertical();
				GUILayout.BeginVertical(ConfigurationManagerStyles.GetBackgroundStyle(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(((Rect)(ref _windowRect)).width - GetFileListWidth() - 64f) });
				DrawContentButtons();
				_textScrollPosition = GUITooltips.BeginScrollView(_textScrollPosition);
				GUI.enabled = File.Exists(_activeFile);
				GUI.SetNextControlName("textEditorTextField");
				_fileContent = GUILayout.TextArea(_fileContent, ConfigurationManagerStyles.GetFileEditorTextArea(), (GUILayoutOption[])(object)new GUILayoutOption[2]
				{
					GUILayout.Width(((Rect)(ref _windowRect)).width - GetFileListWidth() - 86f),
					GUILayout.ExpandHeight(true)
				});
				if (_focusTextArea || GUI.GetNameOfFocusedControl() == "textEditorTextField")
				{
					GUI.FocusWindow(-680);
					GUI.FocusControl("textEditorTextField");
					_focusTextArea = false;
				}
				GUI.enabled = true;
				GUITooltips.EndScrollView();
				if (ConfigurationManager._showFullName.Value)
				{
					GUILayout.TextField(_activeFile, ConfigurationManagerStyles.GetTextStyle(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(((Rect)(ref _windowRect)).width - GetFileListWidth() - 86f) });
				}
				GUILayout.EndVertical();
				GUI.backgroundColor = backgroundColor;
				GUILayout.EndHorizontal();
				GUI.DragWindow(new Rect(70f, 10f, ((Rect)(ref _windowRect)).width - 160f, 46f));
				if (!SettingFieldDrawer.DrawCurrentDropdown())
				{
					ConfigurationManager.DrawTooltip(_windowRect);
				}
				bool sizeChanged;
				Rect windowRect = Utils.ResizeWindow(windowID, _windowRect, out sizeChanged);
				if (sizeChanged)
				{
					_windowRect = windowRect;
					_pendingWindowSize = ((Rect)(ref windowRect)).size;
					_windowGeometryDirty = true;
				}
			}
			finally
			{
				ComboBox.EndWindow();
				GUITooltips.EndWindow();
			}
		}

		private float GetFileListWidth()
		{
			return Mathf.Clamp(((Rect)(ref _windowRect)).width * 0.3f, 290f, 430f);
		}

		private void DrawDirectory(string path)
		{
			_directoryDepth++;
			DrawDirectories(GetDirectories(path));
			if (path == _activeDirectory && _fileNameState == FileEditState.CreatingFolder)
			{
				DrawFileNameField();
			}
			DrawFiles(path);
			_directoryDepth--;
		}

		private void DrawDirectories(IEnumerable<string> directories)
		{
			foreach (string directory in directories)
			{
				if ((ConfigurationManager._showTrashBin.Value || !(directory == _trashBinDirectory)) && (ConfigurationManager._showEmptyFolders.Value || DirectoryContainsValidFiles(directory)))
				{
					if (!_folderStates.ContainsKey(directory))
					{
						_folderStates[directory] = false;
					}
					bool num = _folderStates[directory];
					bool flag = (_folderStates[directory] = GUILayout.Toggle(_folderStates[directory], Path.GetFileName(directory), ConfigurationManagerStyles.GetDirectoryStyle(directory == _activeDirectory), Array.Empty<GUILayoutOption>()));
					if (num != flag && _folderStates[directory])
					{
						_activeDirectory = directory;
						SetFileEditState(FileEditState.None);
					}
					if (_folderStates[directory])
					{
						GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
						GUILayout.Space(20f);
						GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
						DrawDirectory(directory);
						GUILayout.EndVertical();
						GUILayout.EndHorizontal();
					}
				}
			}
		}

		private void DrawFileNameField()
		{
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(GetFileListWidth() - (float)(20 * (_directoryDepth + 1)) - 5f) });
			if (_fileNameState == FileEditState.CreatingFolder || _fileNameState == FileEditState.CreatingFile)
			{
				GUILayout.Label((_fileNameState == FileEditState.CreatingFolder) ? ConfigurationManager._newFolderLabelTextEditor.Value : ConfigurationManager._newFileLabelTextEditor.Value, ConfigurationManagerStyles.GetLabelStyle(), GUIHelper.FixedWidth);
			}
			_newItemName = GUILayout.TextField(_newItemName, ConfigurationManagerStyles.GetFileNameFieldStyle(), GUIHelper.ExpandWidth);
			if (GUILayout.Button(ConfigurationManager._newEntryOKButtonTextEditor.Value, ConfigurationManagerStyles.GetButtonStyle(), GUIHelper.FixedWidth))
			{
				if (_fileNameState == FileEditState.CreatingFolder || _fileNameState == FileEditState.CreatingFile)
				{
					CreateNewItem(_newItemName, _fileNameState == FileEditState.CreatingFolder);
				}
				else if (_fileNameState == FileEditState.RenamingFile)
				{
					RenameActiveFile();
				}
			}
			GUILayout.EndHorizontal();
			if (!Utility.IsNullOrWhiteSpace(_newItemErrorText))
			{
				GUILayout.Label(_newItemErrorText, ConfigurationManagerStyles.GetFileNameErrorStyle(), GUIHelper.ExpandWidth);
			}
			GUILayout.EndVertical();
		}

		private void RenameActiveFile()
		{
			if (_newItemName == Path.GetFileName(_activeFile))
			{
				SetFileEditState(FileEditState.None);
				return;
			}
			string directoryName = Path.GetDirectoryName(_activeFile);
			if (directoryName == null)
			{
				return;
			}
			string text = Path.Combine(directoryName, _newItemName);
			if (File.Exists(text))
			{
				_newItemErrorText = ConfigurationManager._fileExistsTextEditor.Value;
				return;
			}
			try
			{
				File.Move(_activeFile, text);
				_activeFile = text;
				SetFileEditState(FileEditState.None);
			}
			catch (Exception ex)
			{
				_newItemErrorText = ex.Message;
			}
		}

		private void MoveActiveFileToTrash()
		{
			if (Utility.IsNullOrWhiteSpace(_activeFile))
			{
				return;
			}
			Directory.CreateDirectory(_trashBinDirectory);
			string text = Path.Combine(_trashBinDirectory, Path.GetFileName(_activeFile));
			if (File.Exists(text))
			{
				text = Path.Combine(_trashBinDirectory, $"{Path.GetFileNameWithoutExtension(text)}_{DateTime.Now:yyyyMMdd_HHmmss}{Path.GetExtension(text)}");
			}
			try
			{
				if (_activeFile != null)
				{
					File.Move(_activeFile, text);
				}
				_activeFile = string.Empty;
				_fileContent = string.Empty;
				SetFileEditState(FileEditState.None);
			}
			catch (Exception ex)
			{
				_newItemErrorText = ex.Message;
			}
		}

		public ConfigFilesEditor()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			InitializeFileWatcher();
		}

		public void Dispose()
		{
			if (_watchers != null)
			{
				FileSystemWatcher[] watchers = _watchers;
				for (int i = 0; i < watchers.Length; i++)
				{
					watchers[i].Dispose();
				}
				_watchers = Array.Empty<FileSystemWatcher>();
			}
		}

		private void InitializeFileWatcher()
		{
			_watchers = new FileSystemWatcher[2]
			{
				new FileSystemWatcher(Paths.ConfigPath),
				new FileSystemWatcher(Paths.PluginPath)
			};
			FileSystemWatcher[] watchers = _watchers;
			foreach (FileSystemWatcher obj in watchers)
			{
				obj.IncludeSubdirectories = true;
				obj.Changed += delegate
				{
					ClearCache();
				};
				obj.Created += delegate
				{
					ClearCache();
				};
				obj.Deleted += delegate
				{
					ClearCache();
				};
				obj.Renamed += delegate
				{
					ClearCache();
				};
			}
		}

		private void ClearCache()
		{
			_clearCache = true;
			FileSystemWatcher[] watchers = _watchers;
			for (int i = 0; i < watchers.Length; i++)
			{
				watchers[i].EnableRaisingEvents = IsOpen;
			}
		}

		private string[] GetFiles(string path)
		{
			if (_clearCache)
			{
				_cachedFileTree.Clear();
				_cachedDirectories.Clear();
			}
			_clearCache = false;
			if (_cachedFileTree.TryGetValue(path, out var value))
			{
				return value;
			}
			string[] array = (Directory.Exists(path) ? Directory.GetFiles(path) : Array.Empty<string>());
			_cachedFileTree[path] = array;