Decompiled source of SULFUR Config v0.2.3

SULFUR Config.dll

Decompiled 5 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Ryuka.Sulfur.NativeUI;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("SULFUR Config")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SULFUR Config")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("5a9d2c4e-e1a5-45a4-a09b-2e2945f22014")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Ryuka.SulfurConfig;

internal sealed class ConfigEntryModel
{
	public string PluginGuid;

	public string PluginName;

	public string Section;

	public string Key;

	public string Description;

	public string DisplayName;

	public Type SettingType;

	public ConfigEntryBase Entry;

	public string AppliedValue;

	public string DraftValue;

	public string DefaultValue;

	public bool RequiresRestart;

	public bool LiveApply;

	public bool Advanced;

	public bool Hidden;

	public bool Dangerous;

	public List<string> Badges = new List<string>();

	public List<string> ValueList = new List<string>();

	public float? MinValue;

	public float? MaxValue;

	public string RangeText;

	public bool IsDirty => AppliedValue != DraftValue;

	public static ConfigEntryModel Create(string pluginGuid, string pluginName, string section, string key, ConfigEntryBase entry)
	{
		ConfigEntryModel configEntryModel = new ConfigEntryModel();
		string text = ((entry.Description != null) ? entry.Description.Description : "");
		configEntryModel.PluginGuid = pluginGuid;
		configEntryModel.PluginName = pluginName;
		configEntryModel.Section = section;
		configEntryModel.Key = key;
		configEntryModel.DisplayName = SulfurLocalization.Get(pluginGuid, "entry." + section + "." + key + ".name", key);
		configEntryModel.Description = SulfurLocalization.Get(pluginGuid, "entry." + section + "." + key + ".description", text);
		configEntryModel.SettingType = entry.SettingType;
		configEntryModel.Entry = entry;
		configEntryModel.AppliedValue = entry.GetSerializedValue();
		configEntryModel.DraftValue = configEntryModel.AppliedValue;
		configEntryModel.DefaultValue = SerializeDefaultValue(entry.DefaultValue, entry.SettingType);
		configEntryModel.RequiresRestart = HasAnyTag(entry, "SULFURConfigRestartRequired", "SulfurConfigRestartRequired", "RyukaConfigRestartRequired", "RequiresRestart", "RestartRequired");
		configEntryModel.LiveApply = HasAnyTag(entry, "SULFURConfigLiveApply", "SulfurConfigLiveApply", "RyukaConfigLiveApply", "LiveApply", "RuntimeEditable");
		configEntryModel.Advanced = HasAnyTag(entry, "SULFURConfigAdvanced", "SulfurConfigAdvanced", "RyukaConfigAdvanced", "Advanced");
		configEntryModel.Hidden = HasAnyTag(entry, "SULFURConfigHidden", "SulfurConfigHidden", "RyukaConfigHidden", "Hidden", "HideFromSULFURConfig", "HideFromSulfurConfig", "HideFromRyukaConfig", "HideFromConfigPanel", "HideFromREPOConfig", "HideREPOConfig");
		configEntryModel.Dangerous = HasAnyTag(entry, "SULFURConfigDangerous", "SulfurConfigDangerous", "RyukaConfigDangerous", "Dangerous", "Unsafe");
		configEntryModel.ReadAcceptableValues();
		configEntryModel.BuildBadges();
		return configEntryModel;
	}

	public bool GetBoolDraft()
	{
		bool result;
		return bool.TryParse(DraftValue, out result) && result;
	}

	public float GetFloatDraft()
	{
		if (float.TryParse(DraftValue, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
		{
			return result;
		}
		return 0f;
	}

	public float GetDefaultNumericMin()
	{
		return -999999f;
	}

	public float GetDefaultNumericMax()
	{
		return 999999f;
	}

	public void SetDraft(string value)
	{
		DraftValue = value ?? "";
	}

	public void SetDefaultDraft()
	{
		DraftValue = DefaultValue ?? "";
	}

	public void Apply()
	{
		Entry.SetSerializedValue(DraftValue);
		Entry.ConfigFile.Save();
		AppliedValue = Entry.GetSerializedValue();
		DraftValue = AppliedValue;
	}

	public string GetLocalizedValueLabel(string rawValue)
	{
		return SulfurLocalization.Get(PluginGuid, "value." + Section + "." + Key + "." + rawValue, rawValue);
	}

	private void ReadAcceptableValues()
	{
		if (Entry.Description == null || Entry.Description.AcceptableValues == null)
		{
			return;
		}
		object acceptableValues = Entry.Description.AcceptableValues;
		Type type = acceptableValues.GetType();
		PropertyInfo property = type.GetProperty("AcceptableValues");
		if (property != null && property.GetValue(acceptableValues, null) is Array array)
		{
			foreach (object item in array)
			{
				if (item != null)
				{
					ValueList.Add(item.ToString());
				}
			}
		}
		PropertyInfo property2 = type.GetProperty("MinValue");
		PropertyInfo property3 = type.GetProperty("MaxValue");
		if (property2 != null && property3 != null)
		{
			object value = property2.GetValue(acceptableValues, null);
			object value2 = property3.GetValue(acceptableValues, null);
			if (TryToFloat(value, out var result) && TryToFloat(value2, out var result2))
			{
				MinValue = result;
				MaxValue = result2;
			}
		}
	}

	private void BuildBadges()
	{
		if (SettingType != null)
		{
			Badges.Add(SettingType.Name);
		}
		if (MinValue.HasValue && MaxValue.HasValue)
		{
			RangeText = "Allowed range: " + MinValue.Value.ToString("0.###", CultureInfo.InvariantCulture) + " - " + MaxValue.Value.ToString("0.###", CultureInfo.InvariantCulture);
			Badges.Add("Range");
		}
		if (ValueList.Count > 0)
		{
			Badges.Add("Values: " + ValueList.Count);
		}
	}

	private static string SerializeDefaultValue(object value, Type type)
	{
		if (value == null)
		{
			return "";
		}
		if (type == typeof(float))
		{
			return ((float)value).ToString(CultureInfo.InvariantCulture);
		}
		if (type == typeof(double))
		{
			return ((double)value).ToString(CultureInfo.InvariantCulture);
		}
		if (type == typeof(int))
		{
			return ((int)value).ToString(CultureInfo.InvariantCulture);
		}
		if (type == typeof(bool))
		{
			return ((bool)value) ? "true" : "false";
		}
		return value.ToString();
	}

	private static bool TryToFloat(object value, out float result)
	{
		result = 0f;
		if (value == null)
		{
			return false;
		}
		try
		{
			result = Convert.ToSingle(value, CultureInfo.InvariantCulture);
			return true;
		}
		catch
		{
			return false;
		}
	}

	private static bool HasAnyTag(ConfigEntryBase entry, params string[] tagNames)
	{
		if (entry == null || entry.Description == null || entry.Description.Tags == null)
		{
			return false;
		}
		object[] tags = entry.Description.Tags;
		foreach (object obj in tags)
		{
			if (obj == null)
			{
				continue;
			}
			string a = obj.ToString();
			foreach (string b in tagNames)
			{
				if (string.Equals(a, b, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
		}
		return false;
	}
}
internal sealed class ConfigPluginGroup
{
	public string Guid;

	public string Name;

	public string DisplayName;

	public string Description;

	public string Location;

	public List<ConfigSectionGroup> Sections = new List<ConfigSectionGroup>();
}
internal static class ConfigScanner
{
	public static List<ConfigPluginGroup> Scan(ManualLogSource logger)
	{
		List<ConfigPluginGroup> list = new List<ConfigPluginGroup>();
		foreach (PluginInfo value2 in Chainloader.PluginInfos.Values)
		{
			if (value2 == null || (Object)(object)value2.Instance == (Object)null || value2.Metadata == null)
			{
				continue;
			}
			ConfigFile config = value2.Instance.Config;
			if (config == null)
			{
				continue;
			}
			string text = value2.Metadata.GUID ?? "unknown.guid";
			string text2 = value2.Metadata.Name ?? text;
			string text3 = value2.Location ?? "";
			SulfurLocalization.LoadPluginLocalization(text, text3);
			string text4 = SulfurLocalization.Get(text, "plugin.name", text2);
			string description = SulfurLocalization.Get(text, "plugin.description", "");
			ConfigPluginGroup configPluginGroup = new ConfigPluginGroup
			{
				Guid = text,
				Name = text2,
				DisplayName = text4 + " (" + text + ")",
				Description = description,
				Location = text3
			};
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
			{
				ConfigEntryBase value = item.Value;
				if (value != null)
				{
					ConfigEntryModel model = ConfigEntryModel.Create(text, text2, item.Key.Section, item.Key.Key, value);
					ConfigSectionGroup configSectionGroup = configPluginGroup.Sections.FirstOrDefault((ConfigSectionGroup x) => x.Name == model.Section);
					if (configSectionGroup == null)
					{
						configSectionGroup = new ConfigSectionGroup
						{
							Name = model.Section,
							DisplayName = SulfurLocalization.Get(text, "section." + model.Section, model.Section)
						};
						configPluginGroup.Sections.Add(configSectionGroup);
					}
					configSectionGroup.Entries.Add(model);
				}
			}
			if (configPluginGroup.Sections.Count > 0)
			{
				list.Add(configPluginGroup);
			}
		}
		foreach (ConfigPluginGroup item2 in list)
		{
			item2.Sections = item2.Sections.OrderBy((ConfigSectionGroup x) => x.DisplayName).ToList();
			foreach (ConfigSectionGroup section in item2.Sections)
			{
				section.Entries = section.Entries.OrderBy((ConfigEntryModel x) => x.DisplayName).ToList();
			}
		}
		return list.OrderBy((ConfigPluginGroup x) => x.DisplayName).ToList();
	}
}
internal sealed class ConfigSectionGroup
{
	public string Name;

	public string DisplayName;

	public List<ConfigEntryModel> Entries = new List<ConfigEntryModel>();
}
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("ryuka.sulfur.config", "SULFUR Config", "0.2.1")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "ryuka.sulfur.config";

	public const string PluginName = "SULFUR Config";

	public const string PluginVersion = "0.2.1";

	private ConfigNativePage nativePage;

	internal static ManualLogSource Log { get; private set; }

	private void Awake()
	{
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		SulfurLocalization.LoadPluginLocalization("ryuka.sulfur.config", ((BaseUnityPlugin)this).Info.Location);
		nativePage = new ConfigNativePage(((BaseUnityPlugin)this).Logger);
		SulfurOptionsApi.RegisterPage(new SulfurOptionsPage
		{
			PageId = "ryuka.sulfur.config",
			DisplayName = "SULFUR Config",
			SortOrder = 9999,
			GetDisplayName = () => L("page.name", "SULFUR Config"),
			BuildPage = nativePage.Build
		});
		((BaseUnityPlugin)this).Logger.LogInfo((object)"SULFUR Config registered native options page.");
	}

	private void OnDestroy()
	{
		SulfurOptionsApi.UnregisterPage("ryuka.sulfur.config");
		if (Log == ((BaseUnityPlugin)this).Logger)
		{
			Log = null;
		}
	}

	internal static string L(string key, string fallback)
	{
		return SulfurLocalization.Get("ryuka.sulfur.config", key, fallback);
	}
}
internal sealed class ConfigNativePage
{
	private readonly ManualLogSource logger;

	private List<ConfigPluginGroup> groups = new List<ConfigPluginGroup>();

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

	private bool needsScan = true;

	private int lastLocalizationVersion = -1;

	private string filterDraft = "";

	private string activeFilter = "";

	private string status = "";

	private bool showOnlyDirty;

	private bool showOnlyRyuka;

	private bool showAdvanced;

	private bool showHidden;

	private bool showRestartRequired;

	private bool showLiveApply;

	private bool showDangerous;

	public ConfigNativePage(ManualLogSource logger)
	{
		this.logger = logger;
	}

	public void Build(SulfurOptionsContext ctx)
	{
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_015b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		//IL_018e: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ba: Expected O, but got Unknown
		SulfurLocalization.RefreshCurrentLanguage(true);
		if (lastLocalizationVersion != SulfurLocalization.LanguageVersion)
		{
			lastLocalizationVersion = SulfurLocalization.LanguageVersion;
			needsScan = true;
		}
		EnsureScanned();
		int num = CountEntries(groups);
		int num2 = CountDirty(groups);
		ctx.SetFooter(Plugin.L("badge.entries", "Entries") + ": " + num + " | " + Plugin.L("badge.dirty", "Pending") + ": " + num2, string.IsNullOrWhiteSpace(status) ? Plugin.L("footer.ready", "Ready. Changes are not saved until you click Apply.") : status, Plugin.L("button.apply_dirty", "Apply"), (Action)delegate
		{
			ApplyDirty(ctx);
		});
		ctx.AddSection(Plugin.L("section.tools", "Tools"));
		SulfurOptionsSettingRows.AddSettingText(ctx, new SulfurSettingRow
		{
			Label = Plugin.L("filter.label", "Filter"),
			Description = Plugin.L("filter.description", "Search by mod name, GUID, section, key, or description."),
			IsDirty = (filterDraft != activeFilter),
			ShowDefaultButton = false,
			DirtyText = Plugin.L("badge.dirty", "Pending"),
			CleanText = Plugin.L("badge.clean", "Unchanged")
		}, filterDraft, (Action<string>)delegate(string value)
		{
			filterDraft = value ?? "";
			ctx.SetFooterStatus(Plugin.L("status.filter_draft", "Filter draft changed."));
		});
		ctx.AddSmallButton(Plugin.L("button.apply_filter", "Apply Filter"), (Action)delegate
		{
			activeFilter = filterDraft ?? "";
			status = Plugin.L("status.filter_applied", "Filter applied.");
			ctx.Rebuild();
		});
		ctx.AddSmallButton(Plugin.L("button.clear_filter", "Clear Filter"), (Action)delegate
		{
			filterDraft = "";
			activeFilter = "";
			status = Plugin.L("status.filter_cleared", "Filter cleared.");
			ctx.Rebuild();
		});
		ctx.AddSmallButton(Plugin.L("button.refresh", "Refresh"), (Action)delegate
		{
			needsScan = true;
			status = Plugin.L("status.refreshing", "Refreshing config list.");
			ctx.Rebuild();
		});
		ctx.AddSmallButton(showOnlyDirty ? Plugin.L("filter.dirty_on", "Pending: ON") : Plugin.L("filter.dirty_off", "Pending: OFF"), (Action)delegate
		{
			showOnlyDirty = !showOnlyDirty;
			ctx.Rebuild();
		});
		ctx.AddSmallButton(showOnlyRyuka ? Plugin.L("filter.ryuka_on", "Ryuka: ON") : Plugin.L("filter.ryuka_off", "Ryuka: OFF"), (Action)delegate
		{
			showOnlyRyuka = !showOnlyRyuka;
			ctx.Rebuild();
		});
		ctx.AddSmallButton(showAdvanced ? Plugin.L("filter.advanced_on", "Advanced: ON") : Plugin.L("filter.advanced_off", "Advanced: OFF"), (Action)delegate
		{
			showAdvanced = !showAdvanced;
			ctx.Rebuild();
		});
		ctx.AddSmallButton(showHidden ? Plugin.L("filter.hidden_on", "Hidden: ON") : Plugin.L("filter.hidden_off", "Hidden: OFF"), (Action)delegate
		{
			showHidden = !showHidden;
			ctx.Rebuild();
		});
		ctx.AddSmallButton(showRestartRequired ? Plugin.L("filter.restart_on", "Restart: ON") : Plugin.L("filter.restart_off", "Restart: OFF"), (Action)delegate
		{
			showRestartRequired = !showRestartRequired;
			ctx.Rebuild();
		});
		ctx.AddSmallButton(showLiveApply ? Plugin.L("filter.live_on", "Live: ON") : Plugin.L("filter.live_off", "Live: OFF"), (Action)delegate
		{
			showLiveApply = !showLiveApply;
			ctx.Rebuild();
		});
		ctx.AddSmallButton(showDangerous ? Plugin.L("filter.dangerous_on", "Dangerous: ON") : Plugin.L("filter.dangerous_off", "Dangerous: OFF"), (Action)delegate
		{
			showDangerous = !showDangerous;
			ctx.Rebuild();
		});
		ctx.AddSpacer(12f);
		int num3 = 0;
		int num4 = 0;
		foreach (ConfigPluginGroup group in groups)
		{
			List<ConfigSectionGroup> visibleSections = GetVisibleSections(group);
			if (visibleSections.Count == 0)
			{
				continue;
			}
			int num5 = visibleSections.Sum((ConfigSectionGroup s) => s.Entries.Count(EntryMatches));
			int num6 = visibleSections.Sum((ConfigSectionGroup s) => s.Entries.Count((ConfigEntryModel e) => EntryMatches(e) && e.IsDirty));
			bool flag = HasFilterOrSpecialFilter();
			bool flag2 = SulfurOptionsFoldoutExtensions.AddPluginFoldoutWithBadges(ctx, "plugin." + group.Guid, group.DisplayName, false, flag, new string[2]
			{
				Plugin.L("badge.entries", "Entries") + ": " + num5,
				(num6 > 0) ? (Plugin.L("badge.dirty", "Pending") + ": " + num6) : Plugin.L("badge.clean", "Unchanged")
			});
			num3++;
			if (!flag2)
			{
				continue;
			}
			if (!string.IsNullOrWhiteSpace(group.Description))
			{
				ctx.AddDescription(group.Description);
			}
			foreach (ConfigSectionGroup item in visibleSections)
			{
				List<ConfigEntryModel> list = item.Entries.Where(EntryMatches).ToList();
				if (list.Count == 0)
				{
					continue;
				}
				int num7 = list.Count((ConfigEntryModel x) => x.IsDirty);
				if (!SulfurOptionsFoldoutExtensions.AddSectionFoldout(ctx, "plugin." + group.Guid + ".section." + item.Name, item.DisplayName, false, flag))
				{
					continue;
				}
				using (ctx.BeginThemedGroup("section.content." + group.Guid + "." + item.Name))
				{
					ctx.AddBadgeRow(new string[2]
					{
						Plugin.L("badge.entries", "Entries") + ": " + list.Count,
						(num7 > 0) ? (Plugin.L("badge.dirty", "Pending") + ": " + num7) : Plugin.L("badge.clean", "Unchanged")
					});
					foreach (ConfigEntryModel item2 in list)
					{
						DrawEntry(ctx, item2);
						num4++;
					}
				}
			}
		}
		if (num3 == 0)
		{
			ctx.AddWarning(Plugin.L("message.no_results", "No config entries match the current filters."));
		}
	}

	private void EnsureScanned()
	{
		if (needsScan || groups == null || groups.Count <= 0)
		{
			groups = ConfigScanner.Scan(logger);
			RestoreDrafts(groups);
			needsScan = false;
		}
	}

	private void DrawEntry(SulfurOptionsContext ctx, ConfigEntryModel entry)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0151: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ce: Expected O, but got Unknown
		SulfurSettingHandle handle = null;
		SulfurSettingRow val = new SulfurSettingRow
		{
			Label = entry.DisplayName,
			Description = entry.Description,
			IndentLevel = 1,
			IsDirty = entry.IsDirty,
			RequiresRestart = entry.RequiresRestart,
			LiveApply = entry.LiveApply,
			Advanced = entry.Advanced,
			Hidden = entry.Hidden,
			Dangerous = entry.Dangerous,
			Message = (entry.IsDirty ? Plugin.L("message.pending_warning", "This setting has unsaved pending changes. Click Apply to write it to cfg.") : entry.RangeText),
			MessageKind = (SulfurMessageKind)(entry.IsDirty ? 1 : 0),
			DirtyText = Plugin.L("badge.dirty", "Pending"),
			CleanText = Plugin.L("badge.clean", "Unchanged"),
			RestartRequiredText = Plugin.L("badge.restart_required", "Restart Required"),
			LiveApplyText = Plugin.L("badge.live_apply", "Live Apply"),
			AdvancedText = Plugin.L("badge.advanced", "Advanced"),
			HiddenText = Plugin.L("badge.hidden", "Hidden"),
			DangerousText = Plugin.L("badge.dangerous", "Dangerous"),
			DefaultButtonText = Plugin.L("button.default", "Default"),
			ExtraBadges = entry.Badges.ToArray(),
			OnDefault = delegate
			{
				entry.SetDefaultDraft();
				SaveDraft(entry);
				status = Plugin.L("status.default_draft", "Default draft set: ") + entry.Key;
				UpdatePendingUi(ctx, entry, handle);
			}
		};
		Type type = entry.SettingType;
		if (type == typeof(bool))
		{
			SulfurOptionsSettingRows.AddSettingToggleEx(ctx, val, entry.GetBoolDraft(), (Action<bool, SulfurSettingHandle>)delegate(bool value, SulfurSettingHandle h)
			{
				entry.SetDraft(value ? "true" : "false");
				SaveDraft(entry);
				status = Plugin.L("status.changed", "Pending change: ") + entry.Key;
				UpdatePendingUi(ctx, entry, h);
			}, ref handle);
		}
		else if (entry.ValueList.Count > 0)
		{
			List<string> list = entry.ValueList.Select(entry.GetLocalizedValueLabel).ToList();
			int num = entry.ValueList.IndexOf(entry.DraftValue);
			if (num < 0)
			{
				num = 0;
			}
			SulfurOptionsSettingRows.AddSettingCycleEx(ctx, val, (IReadOnlyList<string>)list, num, (Action<int, string, SulfurSettingHandle>)delegate(int newIndex, string selectedDisplayValue, SulfurSettingHandle h)
			{
				if (newIndex >= 0 && newIndex < entry.ValueList.Count)
				{
					entry.SetDraft(entry.ValueList[newIndex]);
				}
				SaveDraft(entry);
				status = Plugin.L("status.changed", "Pending change: ") + entry.Key;
				UpdatePendingUi(ctx, entry, h);
			}, ref handle);
		}
		else if (type.IsEnum)
		{
			List<string> rawValues = Enum.GetNames(type).ToList();
			List<string> list2 = rawValues.Select(entry.GetLocalizedValueLabel).ToList();
			int num2 = rawValues.IndexOf(entry.DraftValue);
			if (num2 < 0)
			{
				num2 = 0;
			}
			SulfurOptionsSettingRows.AddSettingCycleEx(ctx, val, (IReadOnlyList<string>)list2, num2, (Action<int, string, SulfurSettingHandle>)delegate(int newIndex, string selectedDisplayValue, SulfurSettingHandle h)
			{
				if (newIndex >= 0 && newIndex < rawValues.Count)
				{
					entry.SetDraft(rawValues[newIndex]);
				}
				SaveDraft(entry);
				status = Plugin.L("status.changed", "Pending change: ") + entry.Key;
				UpdatePendingUi(ctx, entry, h);
			}, ref handle);
		}
		else if (type == typeof(int) || type == typeof(float) || type == typeof(double))
		{
			float floatDraft = entry.GetFloatDraft();
			float num3 = (entry.MinValue.HasValue ? entry.MinValue.Value : entry.GetDefaultNumericMin());
			float num4 = (entry.MaxValue.HasValue ? entry.MaxValue.Value : entry.GetDefaultNumericMax());
			int num5 = ((!(type == typeof(int))) ? 3 : 0);
			SulfurOptionsSettingRows.AddSettingNumberEx(ctx, val, floatDraft, num3, num4, num5, (Action<float, SulfurSettingHandle>)delegate(float value, SulfurSettingHandle h)
			{
				if (type == typeof(int))
				{
					entry.SetDraft(Mathf.RoundToInt(value).ToString(CultureInfo.InvariantCulture));
				}
				else
				{
					entry.SetDraft(value.ToString("0.###", CultureInfo.InvariantCulture));
				}
				SaveDraft(entry);
				status = Plugin.L("status.changed", "Pending change: ") + entry.Key;
				UpdatePendingUi(ctx, entry, h);
			}, ref handle);
		}
		else
		{
			SulfurOptionsSettingRows.AddSettingTextEx(ctx, val, entry.DraftValue, (Action<string, SulfurSettingHandle>)delegate(string value, SulfurSettingHandle h)
			{
				entry.SetDraft(value);
				SaveDraft(entry);
				status = Plugin.L("status.changed", "Pending change: ") + entry.Key;
				UpdatePendingUi(ctx, entry, h);
			}, ref handle);
		}
	}

	private void UpdatePendingUi(SulfurOptionsContext ctx, ConfigEntryModel entry, SulfurSettingHandle handle)
	{
		if (handle != null)
		{
			handle.SetDirty(entry.IsDirty, Plugin.L("badge.dirty", "Pending"), Plugin.L("badge.clean", "Unchanged"));
			handle.SetMessage(entry.IsDirty ? Plugin.L("message.pending_warning", "This setting has unsaved pending changes. Click Apply to write it to cfg.") : entry.RangeText, (SulfurMessageKind)(entry.IsDirty ? 1 : 0));
		}
		UpdateFooter(ctx);
	}

	private void UpdateFooter(SulfurOptionsContext ctx)
	{
		if (ctx != null)
		{
			ctx.SetFooter(Plugin.L("badge.entries", "Entries") + ": " + CountEntries(groups) + " | " + Plugin.L("badge.dirty", "Pending") + ": " + CountDirty(groups), string.IsNullOrWhiteSpace(status) ? Plugin.L("footer.ready", "Ready. Changes are not saved until you click Apply.") : status, Plugin.L("button.apply_dirty", "Apply"), (Action)delegate
			{
				ApplyDirty(ctx);
			});
		}
	}

	private void ApplyDirty(SulfurOptionsContext ctx)
	{
		int num = 0;
		int num2 = 0;
		foreach (ConfigPluginGroup group in groups)
		{
			foreach (ConfigSectionGroup section in group.Sections)
			{
				foreach (ConfigEntryModel entry in section.Entries)
				{
					if (entry.IsDirty)
					{
						try
						{
							entry.Apply();
							ClearDraft(entry);
							num++;
						}
						catch (Exception ex)
						{
							num2++;
							logger.LogWarning((object)("Failed to apply " + entry.PluginGuid + " / " + entry.Section + " / " + entry.Key + ": " + ex.Message));
						}
					}
				}
			}
		}
		status = Plugin.L("status.apply_result", "Applied and saved: ") + num + ", " + Plugin.L("status.failed", "Failed: ") + num2 + ".";
		ctx.Rebuild();
	}

	private List<ConfigSectionGroup> GetVisibleSections(ConfigPluginGroup plugin)
	{
		List<ConfigSectionGroup> list = new List<ConfigSectionGroup>();
		if (plugin == null)
		{
			return list;
		}
		if (showOnlyRyuka && !Contains(plugin.Guid, "ryuka") && !Contains(plugin.Name, "ryuka"))
		{
			return list;
		}
		bool pluginTextHit = MatchesFilter(plugin.DisplayName) || MatchesFilter(plugin.Guid) || MatchesFilter(plugin.Name) || MatchesFilter(plugin.Description);
		foreach (ConfigSectionGroup section in plugin.Sections)
		{
			bool sectionTextHit = MatchesFilter(section.DisplayName) || MatchesFilter(section.Name);
			if (section.Entries.Any((ConfigEntryModel entry) => EntryPassesSpecialFilters(entry) && (NoTextFilter() || pluginTextHit || sectionTextHit || EntryTextMatches(entry))))
			{
				list.Add(section);
			}
		}
		return list;
	}

	private bool SectionMatches(ConfigSectionGroup section)
	{
		return section?.Entries.Any(EntryMatches) ?? false;
	}

	private bool EntryMatches(ConfigEntryModel entry)
	{
		if (entry == null)
		{
			return false;
		}
		if (!EntryPassesSpecialFilters(entry))
		{
			return false;
		}
		if (NoTextFilter())
		{
			return true;
		}
		return EntryTextMatches(entry);
	}

	private bool EntryPassesSpecialFilters(ConfigEntryModel entry)
	{
		if (entry == null)
		{
			return false;
		}
		if (showOnlyDirty && !entry.IsDirty)
		{
			return false;
		}
		if (showAdvanced)
		{
			if (!entry.Advanced)
			{
				return false;
			}
		}
		else if (entry.Advanced)
		{
			return false;
		}
		if (showHidden)
		{
			if (!entry.Hidden)
			{
				return false;
			}
		}
		else if (entry.Hidden)
		{
			return false;
		}
		if (showRestartRequired && !entry.RequiresRestart)
		{
			return false;
		}
		if (showLiveApply && !entry.LiveApply)
		{
			return false;
		}
		if (showDangerous && !entry.Dangerous)
		{
			return false;
		}
		return true;
	}

	private bool EntryTextMatches(ConfigEntryModel entry)
	{
		return MatchesFilter(entry.PluginName) || MatchesFilter(entry.PluginGuid) || MatchesFilter(entry.Section) || MatchesFilter(entry.Key) || MatchesFilter(entry.DisplayName) || MatchesFilter(entry.Description);
	}

	private bool MatchesFilter(string value)
	{
		if (NoTextFilter())
		{
			return true;
		}
		return Contains(value, activeFilter);
	}

	private bool NoTextFilter()
	{
		return string.IsNullOrWhiteSpace(activeFilter);
	}

	private bool HasFilterOrSpecialFilter()
	{
		return !NoTextFilter() || showOnlyDirty || showOnlyRyuka || showAdvanced || showHidden || showRestartRequired || showLiveApply || showDangerous;
	}

	private void SaveDraft(ConfigEntryModel entry)
	{
		if (entry != null)
		{
			string draftKey = GetDraftKey(entry);
			if (entry.IsDirty)
			{
				draftCache[draftKey] = entry.DraftValue;
			}
			else
			{
				draftCache.Remove(draftKey);
			}
		}
	}

	private void ClearDraft(ConfigEntryModel entry)
	{
		if (entry != null)
		{
			draftCache.Remove(GetDraftKey(entry));
		}
	}

	private void RestoreDrafts(List<ConfigPluginGroup> source)
	{
		if (source == null || draftCache.Count == 0)
		{
			return;
		}
		foreach (ConfigPluginGroup item in source)
		{
			foreach (ConfigSectionGroup section in item.Sections)
			{
				foreach (ConfigEntryModel entry in section.Entries)
				{
					if (draftCache.TryGetValue(GetDraftKey(entry), out var value))
					{
						entry.SetDraft(value);
					}
				}
			}
		}
	}

	private static string GetDraftKey(ConfigEntryModel entry)
	{
		return entry.PluginGuid + "\u001f" + entry.Section + "\u001f" + entry.Key;
	}

	private static bool Contains(string text, string filter)
	{
		return !string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(filter) && text.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0;
	}

	private static int CountEntries(List<ConfigPluginGroup> source)
	{
		return source?.Sum((Func<ConfigPluginGroup, int>)CountEntries) ?? 0;
	}

	private static int CountDirty(List<ConfigPluginGroup> source)
	{
		return source?.Sum((Func<ConfigPluginGroup, int>)CountDirty) ?? 0;
	}

	private static int CountEntries(ConfigPluginGroup plugin)
	{
		if (plugin == null || plugin.Sections == null)
		{
			return 0;
		}
		return plugin.Sections.Sum((ConfigSectionGroup s) => s.Entries.Count);
	}

	private static int CountDirty(ConfigPluginGroup plugin)
	{
		if (plugin == null || plugin.Sections == null)
		{
			return 0;
		}
		return plugin.Sections.Sum((ConfigSectionGroup s) => s.Entries.Count((ConfigEntryModel e) => e.IsDirty));
	}
}