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.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("SULFUR Config")]
[assembly: AssemblyDescription("In-game configuration editor for SULFUR BepInEx mods.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Ryuka")]
[assembly: AssemblyProduct("SULFUR Config")]
[assembly: AssemblyCopyright("Copyright © Ryuka 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("5a9d2c4e-e1a5-45a4-a09b-2e2945f22014")]
[assembly: AssemblyFileVersion("0.3.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.3.0.0")]
namespace Ryuka.SulfurConfig;
internal struct ApplyResult
{
public int Applied;
public int Invalid;
public int Failed;
}
internal sealed class ConfigApplier
{
private readonly ManualLogSource logger;
private readonly HashSet<ConfigFile> unsavedFiles = new HashSet<ConfigFile>();
public ConfigApplier(ManualLogSource logger)
{
this.logger = logger;
}
public ApplyResult Apply(IEnumerable<ConfigEntryModel> entries, DraftStore drafts)
{
ApplyResult result = default(ApplyResult);
Dictionary<ConfigFile, List<ConfigEntryModel>> dictionary = new Dictionary<ConfigFile, List<ConfigEntryModel>>();
List<ConfigFile> list = new List<ConfigFile>();
foreach (ConfigEntryModel entry in entries)
{
if (drafts.IsPending(entry))
{
ConfigFile configFile = entry.Entry.ConfigFile;
if (!dictionary.TryGetValue(configFile, out var value))
{
value = new List<ConfigEntryModel>();
dictionary.Add(configFile, value);
list.Add(configFile);
}
value.Add(entry);
}
}
foreach (ConfigFile item in list)
{
try
{
ApplyFile(item, dictionary[item], drafts, ref result);
}
catch (Exception ex)
{
result.Failed += dictionary[item].Count;
logger.LogWarning((object)("Failed to apply settings of " + item.ConfigFilePath + ": " + ex));
}
}
foreach (ConfigFile item2 in new List<ConfigFile>(unsavedFiles))
{
if (!dictionary.ContainsKey(item2) && !TrySave(item2))
{
result.Failed++;
}
}
return result;
}
private void ApplyFile(ConfigFile file, List<ConfigEntryModel> entries, DraftStore drafts, ref ApplyResult result)
{
int num = 0;
bool saveOnConfigSet = file.SaveOnConfigSet;
try
{
file.SaveOnConfigSet = false;
foreach (ConfigEntryModel entry in entries)
{
entry.RefreshApplied();
if (!entry.TryParse(drafts.GetValue(entry), out var value))
{
result.Invalid++;
continue;
}
string text = entry.SerializeValue(value);
if (text == entry.AppliedValue)
{
drafts.Discard(entry);
continue;
}
try
{
entry.Entry.BoxedValue = value;
}
catch (Exception ex)
{
logger.LogWarning((object)("Failed to apply " + Describe(entry) + ": " + ex.GetType().Name + ": " + ex.Message));
}
entry.RefreshApplied();
if (entry.AppliedValue != text)
{
result.Failed++;
continue;
}
drafts.Discard(entry);
num++;
}
}
finally
{
file.SaveOnConfigSet = saveOnConfigSet;
}
if (num != 0 || unsavedFiles.Contains(file))
{
if (TrySave(file))
{
result.Applied += num;
}
else
{
result.Failed += num;
}
}
}
private bool TrySave(ConfigFile file)
{
try
{
file.Save();
unsavedFiles.Remove(file);
return true;
}
catch (Exception ex)
{
unsavedFiles.Add(file);
logger.LogWarning((object)("Failed to save " + file.ConfigFilePath + ": " + ex.GetType().Name + ": " + ex.Message));
return false;
}
}
private static string Describe(ConfigEntryModel entry)
{
return entry.PluginGuid + " / " + entry.Section + " / " + entry.Key;
}
}
internal enum EntryEditorKind
{
Toggle,
Cycle,
Number,
Text
}
internal sealed class ConfigEntryModel
{
private const int FloatWidgetSignificantDigits = 7;
private const double FloatWidgetLimit = 9999999.0;
private const int DoubleWidgetSignificantDigits = 15;
private const double DoubleWidgetLimit = 999999999999999.0;
private const int MinFloatDecimals = 3;
private const int MaxFloatDecimals = 6;
private static readonly string[] RestartTags = new string[5] { "SULFURConfigRestartRequired", "SulfurConfigRestartRequired", "RyukaConfigRestartRequired", "RequiresRestart", "RestartRequired" };
private static readonly string[] LiveApplyTags = new string[5] { "SULFURConfigLiveApply", "SulfurConfigLiveApply", "RyukaConfigLiveApply", "LiveApply", "RuntimeEditable" };
private static readonly string[] AdvancedTags = new string[4] { "SULFURConfigAdvanced", "SulfurConfigAdvanced", "RyukaConfigAdvanced", "Advanced" };
private static readonly string[] HiddenTags = new string[10] { "SULFURConfigHidden", "SulfurConfigHidden", "RyukaConfigHidden", "Hidden", "HideFromSULFURConfig", "HideFromSulfurConfig", "HideFromRyukaConfig", "HideFromConfigPanel", "HideFromREPOConfig", "HideREPOConfig" };
private static readonly string[] DangerousTags = new string[5] { "SULFURConfigDangerous", "SulfurConfigDangerous", "RyukaConfigDangerous", "Dangerous", "Unsafe" };
public string Id { get; private set; }
public string PluginGuid { get; private set; }
public string PluginName { get; private set; }
public string Section { get; private set; }
public string Key { get; private set; }
public string DisplayName { get; private set; }
public string Description { get; private set; }
public ConfigEntryBase Entry { get; private set; }
public Type SettingType { get; private set; }
public string DefaultValue { get; private set; }
public string AppliedValue { get; private set; }
public bool RequiresRestart { get; private set; }
public bool LiveApply { get; private set; }
public bool Advanced { get; private set; }
public bool Hidden { get; private set; }
public bool Dangerous { get; private set; }
public EntryEditorKind Kind { get; private set; }
public bool IsFlagsEnum { get; private set; }
public bool IsIntegral { get; private set; }
public IReadOnlyList<string> Options { get; private set; }
public double? RangeMin { get; private set; }
public double? RangeMax { get; private set; }
public double NumberMin { get; private set; }
public double NumberMax { get; private set; }
public bool UsesDoubleWidget { get; private set; }
private ConfigEntryModel()
{
}
public static string MakeId(string pluginGuid, string section, string key)
{
return pluginGuid + "\u001f" + section + "\u001f" + key;
}
public static ConfigEntryModel Create(string pluginGuid, string pluginName, ConfigEntryBase entry, Func<string, string, string> localize)
{
string section = entry.Definition.Section;
string key = entry.Definition.Key;
ConfigDescription description = entry.Description;
ConfigEntryModel configEntryModel = new ConfigEntryModel();
configEntryModel.Id = MakeId(pluginGuid, section, key);
configEntryModel.PluginGuid = pluginGuid;
configEntryModel.PluginName = pluginName;
configEntryModel.Section = section;
configEntryModel.Key = key;
configEntryModel.Entry = entry;
configEntryModel.SettingType = entry.SettingType;
configEntryModel.DisplayName = localize("entry." + section + "." + key + ".name", key);
configEntryModel.Description = localize("entry." + section + "." + key + ".description", (description != null) ? (description.Description ?? "") : "");
configEntryModel.DefaultValue = Serialize(entry.DefaultValue, entry.SettingType);
configEntryModel.RequiresRestart = HasAnyTag(description, RestartTags);
configEntryModel.LiveApply = HasAnyTag(description, LiveApplyTags);
configEntryModel.Advanced = HasAnyTag(description, AdvancedTags);
configEntryModel.Hidden = HasAnyTag(description, HiddenTags);
configEntryModel.Dangerous = HasAnyTag(description, DangerousTags);
configEntryModel.Options = new string[0];
configEntryModel.AppliedValue = entry.GetSerializedValue();
configEntryModel.ChooseEditor((description != null) ? description.AcceptableValues : null);
return configEntryModel;
}
public bool RefreshApplied()
{
string serializedValue;
try
{
serializedValue = Entry.GetSerializedValue();
}
catch (Exception)
{
return false;
}
if (serializedValue == AppliedValue)
{
return false;
}
AppliedValue = serializedValue;
return true;
}
public bool TryParse(string serialized, out object value)
{
value = null;
if (SettingType == typeof(KeyboardShortcut) && !IsReadableShortcut(serialized))
{
return false;
}
try
{
value = TomlTypeConverter.ConvertToValue(serialized ?? "", SettingType);
}
catch (Exception)
{
return false;
}
if (value == null && SettingType.IsValueType)
{
return false;
}
AcceptableValueBase val = ((Entry.Description != null) ? Entry.Description.AcceptableValues : null);
try
{
if (val != null && !val.IsValid(value))
{
return false;
}
}
catch (Exception)
{
return false;
}
return true;
}
public bool IsValid(string serialized)
{
object value;
return TryParse(serialized, out value);
}
public string SerializeValue(object value)
{
return Serialize(value, SettingType);
}
public bool TryGetNumber(string serialized, out double number)
{
number = 0.0;
if (!TryParse(serialized, out var value) && !TryParseLoose(serialized, out value))
{
return false;
}
try
{
number = ToDecimalDouble(value);
return true;
}
catch (Exception)
{
return false;
}
}
public int GetDecimals(string currentSerialized)
{
if (IsIntegral)
{
return 0;
}
int val = 3;
if (TryGetNumber(currentSerialized, out var number))
{
val = Math.Max(val, CountDecimals(number));
}
if (TryGetNumber(DefaultValue, out number))
{
val = Math.Max(val, CountDecimals(number));
}
if (RangeMin.HasValue)
{
val = Math.Max(val, CountDecimals(RangeMin.Value));
}
if (RangeMax.HasValue)
{
val = Math.Max(val, CountDecimals(RangeMax.Value));
}
return Math.Min(val, 6);
}
public bool TrySerializeNumber(double number, int decimals, out string serialized)
{
serialized = null;
if (double.IsNaN(number) || double.IsInfinity(number))
{
return false;
}
double num = (IsIntegral ? Math.Round(number) : Math.Round(number, decimals));
if (RangeMin.HasValue && num < RangeMin.Value)
{
num = RangeMin.Value;
}
if (RangeMax.HasValue && num > RangeMax.Value)
{
num = RangeMax.Value;
}
string serialized2 = (IsIntegral ? num.ToString("0", CultureInfo.InvariantCulture) : num.ToString("0.#########", CultureInfo.InvariantCulture));
if (!TryParse(serialized2, out var value))
{
return false;
}
serialized = SerializeValue(value);
return true;
}
public string CanonicalizeIfApplied(string value)
{
if (value != AppliedValue && TryParse(value, out var value2) && SerializeValue(value2) == AppliedValue)
{
return AppliedValue;
}
return value;
}
public string GetValueLabel(string serialized)
{
return SulfurLanguage.FixRtl(SulfurLocalization.Get(PluginGuid, "value." + Section + "." + Key + "." + serialized, serialized));
}
private void ChooseEditor(AcceptableValueBase acceptable)
{
Type settingType = SettingType;
if (settingType == typeof(bool))
{
Kind = EntryEditorKind.Toggle;
return;
}
List<string> list = ReadAcceptableList(acceptable, settingType);
if (list.Count > 0)
{
Kind = EntryEditorKind.Cycle;
Options = list;
return;
}
if (settingType.IsEnum)
{
IsFlagsEnum = settingType.IsDefined(typeof(FlagsAttribute), inherit: false);
if (IsFlagsEnum)
{
Kind = EntryEditorKind.Text;
Options = Enum.GetNames(settingType);
}
else
{
Kind = EntryEditorKind.Cycle;
Options = Enum.GetNames(settingType);
}
return;
}
ReadAcceptableRange(acceptable);
if (TryGetNumericSpan(settingType, out var min, out var max, out var integral))
{
IsIntegral = integral;
bool flag = settingType == typeof(double) || settingType == typeof(decimal);
int num = (flag ? 15 : 7);
double num2 = (RangeMin.HasValue ? Math.Max(RangeMin.Value, min) : min);
double num3 = (RangeMax.HasValue ? Math.Min(RangeMax.Value, max) : max);
if (num2 <= num3 && TryGetNumber(AppliedValue, out var number) && FitsWidget(number, num2, num3, num) && (!TryGetNumber(DefaultValue, out var number2) || FitsWidget(number2, num2, num3, num)) && (!RangeMin.HasValue || SignificantDigits(RangeMin.Value) <= num) && (!RangeMax.HasValue || SignificantDigits(RangeMax.Value) <= num))
{
Kind = EntryEditorKind.Number;
UsesDoubleWidget = flag;
NumberMin = num2;
NumberMax = num3;
return;
}
}
Kind = EntryEditorKind.Text;
}
private static bool TryGetNumericSpan(Type type, out double min, out double max, out bool integral)
{
integral = true;
if (type == typeof(int) || type == typeof(long) || type == typeof(uint) || type == typeof(ulong))
{
min = ((type == typeof(uint) || type == typeof(ulong)) ? 0.0 : (-9999999.0));
max = 9999999.0;
return true;
}
if (type == typeof(short))
{
min = -32768.0;
max = 32767.0;
return true;
}
if (type == typeof(ushort))
{
min = 0.0;
max = 65535.0;
return true;
}
if (type == typeof(byte))
{
min = 0.0;
max = 255.0;
return true;
}
if (type == typeof(sbyte))
{
min = -128.0;
max = 127.0;
return true;
}
integral = false;
if (type == typeof(float))
{
min = -9999999.0;
max = 9999999.0;
return true;
}
if (type == typeof(double) || type == typeof(decimal))
{
min = -999999999999999.0;
max = 999999999999999.0;
return true;
}
min = 0.0;
max = 0.0;
return false;
}
private List<string> ReadAcceptableList(AcceptableValueBase acceptable, Type type)
{
List<string> list = new List<string>();
if (acceptable == null)
{
return list;
}
PropertyInfo property = ((object)acceptable).GetType().GetProperty("AcceptableValues");
if (property == null)
{
return list;
}
Array array;
try
{
array = property.GetValue(acceptable, null) as Array;
}
catch (Exception)
{
return list;
}
if (array == null)
{
return list;
}
foreach (object item2 in array)
{
if (item2 != null)
{
string item = Serialize(item2, type);
if (!list.Contains(item))
{
list.Add(item);
}
}
}
return list;
}
private void ReadAcceptableRange(AcceptableValueBase acceptable)
{
if (acceptable == null)
{
return;
}
Type type = ((object)acceptable).GetType();
PropertyInfo property = type.GetProperty("MinValue");
PropertyInfo property2 = type.GetProperty("MaxValue");
if (property == null || property2 == null)
{
return;
}
try
{
RangeMin = ToDecimalDouble(property.GetValue(acceptable, null));
RangeMax = ToDecimalDouble(property2.GetValue(acceptable, null));
}
catch (Exception)
{
RangeMin = null;
RangeMax = null;
}
}
private bool TryParseLoose(string serialized, out object value)
{
value = null;
try
{
value = TomlTypeConverter.ConvertToValue(serialized ?? "", SettingType);
return value != null;
}
catch (Exception)
{
return false;
}
}
private static string Serialize(object value, Type type)
{
if (value == null)
{
return "";
}
try
{
return TomlTypeConverter.ConvertToString(value, type);
}
catch (Exception)
{
return Convert.ToString(value, CultureInfo.InvariantCulture) ?? "";
}
}
private static bool FitsWidget(double value, double min, double max, int digits)
{
if (value >= min && value <= max && SignificantDigits(value) <= digits)
{
return CountDecimals(value) <= 6;
}
return false;
}
private static int SignificantDigits(double value)
{
return Math.Abs(value).ToString("0.#########", CultureInfo.InvariantCulture).Replace(".", "")
.Trim(new char[1] { '0' })
.Length;
}
private static bool IsReadableShortcut(string serialized)
{
if (string.IsNullOrWhiteSpace(serialized))
{
return true;
}
string[] array = serialized.Split(new char[5] { ' ', '+', ',', ';', '|' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string value in array)
{
try
{
Enum.Parse(typeof(KeyCode), value);
}
catch (Exception)
{
return false;
}
}
return true;
}
private static double ToDecimalDouble(object value)
{
if (value is float num)
{
return double.Parse(num.ToString("R", CultureInfo.InvariantCulture), NumberStyles.Float, CultureInfo.InvariantCulture);
}
return Convert.ToDouble(value, CultureInfo.InvariantCulture);
}
private static int CountDecimals(double value)
{
string text = value.ToString("0.#########", CultureInfo.InvariantCulture);
int num = text.IndexOf('.');
if (num >= 0)
{
return text.Length - num - 1;
}
return 0;
}
private static bool HasAnyTag(ConfigDescription description, string[] tagNames)
{
if (description == null || description.Tags == null)
{
return false;
}
object[] tags = 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 SectionView
{
public ConfigSectionGroup Section;
public List<ConfigEntryModel> Entries = new List<ConfigEntryModel>();
public int PendingCount;
}
internal sealed class PluginView
{
public ConfigPluginGroup Plugin;
public List<SectionView> Sections = new List<SectionView>();
public int EntryCount;
public int PendingCount;
}
internal sealed class ConfigFilter
{
private static readonly string[] AuthorGuidPrefixes = new string[3] { "ryuka.", "kumo.sulfur.", "lingyun67." };
public string Text = "";
public bool OnlyPending;
public bool OnlyAuthorMods;
public bool IncludeAdvanced;
public bool IncludeHidden;
public bool OnlyRestartRequired;
public bool OnlyLiveApply;
public bool OnlyDangerous;
public bool HasText => !string.IsNullOrWhiteSpace(Text);
public bool NarrowsResults
{
get
{
if (!HasText && !OnlyPending && !OnlyRestartRequired && !OnlyLiveApply)
{
return OnlyDangerous;
}
return true;
}
}
public List<PluginView> BuildView(List<ConfigPluginGroup> plugins, DraftStore drafts)
{
List<PluginView> list = new List<PluginView>();
string text = (HasText ? Text.Trim() : null);
foreach (ConfigPluginGroup plugin in plugins)
{
if (OnlyAuthorMods && !IsAuthorPlugin(plugin))
{
continue;
}
bool flag = text == null || Contains(plugin.DisplayName, text) || Contains(plugin.Guid, text) || Contains(plugin.Name, text) || Contains(plugin.Description, text);
PluginView pluginView = null;
foreach (ConfigSectionGroup section in plugin.Sections)
{
bool flag2 = flag || Contains(section.DisplayName, text) || Contains(section.Name, text);
SectionView sectionView = null;
foreach (ConfigEntryModel entry in section.Entries)
{
if (PassesSwitches(entry, drafts) && (flag2 || EntryTextMatches(entry, text)))
{
if (sectionView == null)
{
sectionView = new SectionView
{
Section = section
};
}
sectionView.Entries.Add(entry);
if (drafts.IsPending(entry))
{
sectionView.PendingCount++;
}
}
}
if (sectionView != null)
{
if (pluginView == null)
{
pluginView = new PluginView
{
Plugin = plugin
};
}
pluginView.Sections.Add(sectionView);
pluginView.EntryCount += sectionView.Entries.Count;
pluginView.PendingCount += sectionView.PendingCount;
}
}
if (pluginView != null)
{
list.Add(pluginView);
}
}
return list;
}
private bool PassesSwitches(ConfigEntryModel entry, DraftStore drafts)
{
if (entry.Advanced && !IncludeAdvanced)
{
return false;
}
if (entry.Hidden && !IncludeHidden)
{
return false;
}
if (OnlyPending && !drafts.IsPending(entry))
{
return false;
}
if (OnlyRestartRequired && !entry.RequiresRestart)
{
return false;
}
if (OnlyLiveApply && !entry.LiveApply)
{
return false;
}
if (OnlyDangerous && !entry.Dangerous)
{
return false;
}
return true;
}
private static bool EntryTextMatches(ConfigEntryModel entry, string text)
{
if (!Contains(entry.Key, text) && !Contains(entry.DisplayName, text))
{
return Contains(entry.Description, text);
}
return true;
}
private static bool IsAuthorPlugin(ConfigPluginGroup plugin)
{
string[] authorGuidPrefixes = AuthorGuidPrefixes;
foreach (string value in authorGuidPrefixes)
{
if (plugin.Guid != null && plugin.Guid.StartsWith(value, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return Contains(plugin.Name, "ryuka");
}
private static bool Contains(string value, string text)
{
if (!string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(text))
{
return value.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0;
}
return false;
}
}
internal sealed class ConfigNativePage
{
private sealed class PendingBadgeRow
{
public SulfurBadgeRowHandle Handle;
public int PendingIndex;
public List<ConfigEntryModel> Entries;
}
private readonly ConfigScanner scanner;
private readonly DraftStore drafts = new DraftStore();
private readonly ConfigFilter filter = new ConfigFilter();
private readonly ConfigApplier applier;
private readonly EntryRowBuilder rows;
private List<ConfigPluginGroup> plugins = new List<ConfigPluginGroup>();
private List<ConfigEntryModel> allEntries = new List<ConfigEntryModel>();
private bool needsScan = true;
private int scannedLanguageVersion = -1;
private string filterDraft = "";
private string status = "";
private bool openMatchesByDefault = true;
private int filterGeneration;
private readonly Dictionary<string, PendingBadgeRow> pluginBadges = new Dictionary<string, PendingBadgeRow>(StringComparer.Ordinal);
private readonly Dictionary<string, PendingBadgeRow> sectionBadges = new Dictionary<string, PendingBadgeRow>(StringComparer.Ordinal);
public ConfigNativePage(ManualLogSource logger)
{
scanner = new ConfigScanner(logger);
applier = new ConfigApplier(logger);
rows = new EntryRowBuilder(drafts, OnDraftChanged, SetStatusAndRebuild);
}
public void Build(SulfurOptionsContext ctx)
{
pluginBadges.Clear();
sectionBadges.Clear();
PrepareData();
List<PluginView> view = filter.BuildView(plugins, drafts);
SetFooter(ctx);
BuildTools(ctx);
BuildPlugins(ctx, view);
}
private void PrepareData()
{
SulfurLocalization.RefreshCurrentLanguage(false);
if (scannedLanguageVersion != SulfurLocalization.LanguageVersion)
{
needsScan = true;
}
if (needsScan)
{
plugins = scanner.Scan();
allEntries = ConfigScanner.AllEntries(plugins).ToList();
scannedLanguageVersion = SulfurLocalization.LanguageVersion;
needsScan = false;
}
else
{
foreach (ConfigEntryModel allEntry in allEntries)
{
allEntry.RefreshApplied();
}
}
drafts.Reconcile(allEntries);
}
private void BuildTools(SulfurOptionsContext ctx)
{
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: 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_0097: Expected O, but got Unknown
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00bc: 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_00d9: Unknown result type (might be due to invalid IL or missing references)
//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
//IL_010e: 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_014f: Unknown result type (might be due to invalid IL or missing references)
//IL_0154: Unknown result type (might be due to invalid IL or missing references)
//IL_017e: Unknown result type (might be due to invalid IL or missing references)
//IL_0183: Unknown result type (might be due to invalid IL or missing references)
//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
//IL_021d: Unknown result type (might be due to invalid IL or missing references)
//IL_0222: Unknown result type (might be due to invalid IL or missing references)
//IL_024c: Unknown result type (might be due to invalid IL or missing references)
//IL_0251: Unknown result type (might be due to invalid IL or missing references)
//IL_027b: Unknown result type (might be due to invalid IL or missing references)
//IL_0280: Unknown result type (might be due to invalid IL or missing references)
ctx.AddSection(ConfigText.ToolsSection);
SulfurSettingHandle val = default(SulfurSettingHandle);
SulfurOptionsSettingRows.AddSettingTextEx(ctx, new SulfurSettingRow
{
Label = ConfigText.FilterLabel,
Description = ConfigText.FilterDescription,
IsDirty = (filterDraft != filter.Text),
ShowDefaultButton = false,
DirtyText = ConfigText.PendingBadge,
CleanText = ConfigText.UnchangedBadge
}, filterDraft, (Action<string, SulfurSettingHandle>)delegate(string value, SulfurSettingHandle handle)
{
filterDraft = value ?? "";
if (handle != null)
{
handle.SetDirty(filterDraft != filter.Text, ConfigText.PendingBadge, ConfigText.UnchangedBadge);
}
status = ConfigText.StatusFilterDraft;
ctx.SetFooterStatus(status);
}, ref val);
ctx.AddButtonRow((SulfurButton[])(object)new SulfurButton[4]
{
new SulfurButton(ConfigText.ApplyFilterButton, (Action)delegate
{
filter.Text = filterDraft;
OnFilterChanged();
SetStatusAndRebuild(ctx, ConfigText.StatusFilterApplied);
}),
new SulfurButton(ConfigText.ClearFilterButton, (Action)delegate
{
filterDraft = "";
filter.Text = "";
OnFilterChanged();
SetStatusAndRebuild(ctx, ConfigText.StatusFilterCleared);
}),
new SulfurButton(ConfigText.RefreshButton, (Action)delegate
{
needsScan = true;
SetStatusAndRebuild(ctx, ConfigText.StatusRefreshing);
}),
new SulfurButton(ConfigText.CollapseAllButton, (Action)delegate
{
SulfurOptionsStateExtensions.ClearPageState(ctx);
openMatchesByDefault = false;
SetStatusAndRebuild(ctx, ConfigText.StatusCollapsed);
})
});
ctx.AddButtonRow((SulfurButton[])(object)new SulfurButton[4]
{
Switch(ctx, ConfigText.PendingFilter(filter.OnlyPending), delegate
{
filter.OnlyPending = !filter.OnlyPending;
}),
Switch(ctx, ConfigText.AdvancedFilter(filter.IncludeAdvanced), delegate
{
filter.IncludeAdvanced = !filter.IncludeAdvanced;
}),
Switch(ctx, ConfigText.HiddenFilter(filter.IncludeHidden), delegate
{
filter.IncludeHidden = !filter.IncludeHidden;
}),
Switch(ctx, ConfigText.RyukaFilter(filter.OnlyAuthorMods), delegate
{
filter.OnlyAuthorMods = !filter.OnlyAuthorMods;
})
});
ctx.AddButtonRow((SulfurButton[])(object)new SulfurButton[3]
{
Switch(ctx, ConfigText.RestartFilter(filter.OnlyRestartRequired), delegate
{
filter.OnlyRestartRequired = !filter.OnlyRestartRequired;
}),
Switch(ctx, ConfigText.LiveFilter(filter.OnlyLiveApply), delegate
{
filter.OnlyLiveApply = !filter.OnlyLiveApply;
}),
Switch(ctx, ConfigText.DangerousFilter(filter.OnlyDangerous), delegate
{
filter.OnlyDangerous = !filter.OnlyDangerous;
})
});
ctx.AddSpacer(12f);
}
private void BuildPlugins(SulfurOptionsContext ctx, List<PluginView> view)
{
if (view.Count == 0)
{
ctx.AddWarning(ConfigText.NoResults);
return;
}
bool narrowsResults = filter.NarrowsResults;
bool flag = narrowsResults && openMatchesByDefault;
string text = (narrowsResults ? ("filtered." + filterGeneration + ".") : "");
SulfurBadgeRowHandle handle = default(SulfurBadgeRowHandle);
foreach (PluginView item in view)
{
ConfigPluginGroup plugin = item.Plugin;
bool num = SulfurOptionsFoldoutExtensions.AddPluginFoldoutWithBadges(ctx, text + "plugin." + plugin.Guid, SulfurLanguage.FixRtl(plugin.DisplayName), flag, false, (IReadOnlyList<string>)new string[2]
{
ConfigText.EntriesBadge + ": " + item.EntryCount,
PendingBadge(item.PendingCount)
}, ref handle);
pluginBadges[plugin.Guid] = new PendingBadgeRow
{
Handle = handle,
PendingIndex = 1,
Entries = item.Sections.SelectMany((SectionView s) => s.Entries).ToList()
};
if (!num)
{
continue;
}
if (!string.IsNullOrWhiteSpace(plugin.Description))
{
ctx.AddDescription(SulfurLanguage.FixRtl(plugin.Description));
}
if (item.Sections.Count == 1)
{
DrawSection(ctx, plugin, item.Sections[0], showName: true);
continue;
}
foreach (SectionView section in item.Sections)
{
if (SulfurOptionsFoldoutExtensions.AddSectionFoldout(ctx, text + "plugin." + plugin.Guid + ".section." + section.Section.Name, SulfurLanguage.FixRtl(section.Section.DisplayName), flag, false))
{
DrawSection(ctx, plugin, section, showName: false);
}
}
}
}
private void DrawSection(SulfurOptionsContext ctx, ConfigPluginGroup plugin, SectionView sectionView, bool showName)
{
using (ctx.BeginThemedGroup("section.content." + plugin.Guid + "." + sectionView.Section.Name))
{
string text = ConfigText.EntriesBadge + ": " + sectionView.Entries.Count;
string text2 = PendingBadge(sectionView.PendingCount);
if (showName)
{
ctx.AddTextRow(SulfurLanguage.FixRtl(sectionView.Section.DisplayName));
}
sectionBadges[SectionKey(plugin.Guid, sectionView.Section.Name)] = new PendingBadgeRow
{
Handle = ctx.AddBadgeRowEx(new string[2] { text, text2 }),
PendingIndex = 1,
Entries = sectionView.Entries
};
foreach (ConfigEntryModel entry in sectionView.Entries)
{
rows.Draw(ctx, entry);
}
}
}
private SulfurButton Switch(SulfurOptionsContext ctx, string label, Action toggle)
{
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
return new SulfurButton(label, (Action)delegate
{
toggle();
OnFilterChanged();
ctx.Rebuild();
});
}
private void ApplyAll(SulfurOptionsContext ctx)
{
foreach (ConfigEntryModel allEntry in allEntries)
{
allEntry.RefreshApplied();
}
drafts.Reconcile(allEntries);
ApplyResult applyResult = applier.Apply(allEntries, drafts);
string text = ConfigText.StatusApplyResult + applyResult.Applied + ", " + ConfigText.StatusFailed + applyResult.Failed;
if (applyResult.Invalid > 0)
{
text = text + ", " + ConfigText.StatusInvalid + applyResult.Invalid;
}
SetStatusAndRebuild(ctx, text + ".");
}
private void OnFilterChanged()
{
filterGeneration++;
openMatchesByDefault = true;
}
private void OnDraftChanged(SulfurOptionsContext ctx, ConfigEntryModel entry, string newStatus)
{
status = newStatus;
SetFooter(ctx);
UpdatePendingBadge(pluginBadges, entry.PluginGuid);
UpdatePendingBadge(sectionBadges, SectionKey(entry.PluginGuid, entry.Section));
}
private void UpdatePendingBadge(Dictionary<string, PendingBadgeRow> rows, string key)
{
if (!rows.TryGetValue(key, out var value) || value.Handle == null || !value.Handle.IsAlive)
{
return;
}
int num = 0;
foreach (ConfigEntryModel entry in value.Entries)
{
if (drafts.IsPending(entry))
{
num++;
}
}
value.Handle.SetBadge(value.PendingIndex, PendingBadge(num));
}
private static string SectionKey(string pluginGuid, string section)
{
return pluginGuid + "\u001f" + section;
}
private void SetStatusAndRebuild(SulfurOptionsContext ctx, string newStatus)
{
status = newStatus;
ctx.Rebuild();
}
private void SetFooter(SulfurOptionsContext ctx)
{
ctx.SetFooter(ConfigText.EntriesBadge + ": " + allEntries.Count + " | " + ConfigText.PendingBadge + ": " + drafts.Count, string.IsNullOrWhiteSpace(status) ? ConfigText.FooterReady : status, ConfigText.ApplyButton, (Action)delegate
{
ApplyAll(ctx);
});
}
private static string PendingBadge(int pendingCount)
{
if (pendingCount <= 0)
{
return ConfigText.UnchangedBadge;
}
return ConfigText.PendingBadge + ": " + pendingCount;
}
}
internal sealed class ConfigPluginGroup
{
public string Guid;
public string Name;
public string DisplayName;
public string Description;
public List<ConfigSectionGroup> Sections = new List<ConfigSectionGroup>();
}
internal sealed class ConfigScanner
{
private readonly ManualLogSource logger;
private readonly HashSet<string> localizationLoaded = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<string> failureLogged = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
public ConfigScanner(ManualLogSource logger)
{
this.logger = logger;
}
public List<ConfigPluginGroup> Scan()
{
List<ConfigPluginGroup> list = new List<ConfigPluginGroup>();
foreach (PluginInfo value in Chainloader.PluginInfos.Values)
{
if (value == null || (Object)(object)value.Instance == (Object)null || value.Metadata == null)
{
continue;
}
string text = value.Metadata.GUID ?? "unknown.guid";
try
{
ConfigPluginGroup configPluginGroup = ScanPlugin(value, text);
if (configPluginGroup != null)
{
list.Add(configPluginGroup);
}
}
catch (Exception ex)
{
if (failureLogged.Add(text))
{
logger.LogWarning((object)("Skipped the config of " + text + ": " + ex.GetType().Name + ": " + ex.Message));
}
}
}
list.Sort((ConfigPluginGroup a, ConfigPluginGroup b) => string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase));
return list;
}
private ConfigPluginGroup ScanPlugin(PluginInfo pluginInfo, string guid)
{
ConfigFile config = pluginInfo.Instance.Config;
if (config == null)
{
return null;
}
ConfigEntryBase[] configEntries = config.GetConfigEntries();
if (configEntries == null || configEntries.Length == 0)
{
return null;
}
if (localizationLoaded.Add(guid))
{
SulfurLocalization.LoadPluginLocalization(guid, pluginInfo.Location ?? "");
}
string text = pluginInfo.Metadata.Name ?? guid;
Func<string, string, string> func = (string key, string fallback) => SulfurLocalization.Get(guid, key, fallback);
ConfigPluginGroup configPluginGroup = new ConfigPluginGroup
{
Guid = guid,
Name = text,
DisplayName = func("plugin.name", text) + " (" + guid + ")",
Description = func("plugin.description", "")
};
Dictionary<string, ConfigSectionGroup> dictionary = new Dictionary<string, ConfigSectionGroup>(StringComparer.Ordinal);
ConfigEntryBase[] array = configEntries;
foreach (ConfigEntryBase val in array)
{
if (val == null || val.Definition == (ConfigDefinition)null)
{
continue;
}
ConfigEntryModel configEntryModel;
try
{
configEntryModel = ConfigEntryModel.Create(guid, text, val, func);
}
catch (Exception ex)
{
if (failureLogged.Add(guid + "\u001f" + val.Definition.Section + "\u001f" + val.Definition.Key))
{
logger.LogWarning((object)("Skipped " + guid + " / " + val.Definition.Section + " / " + val.Definition.Key + ": " + ex.GetType().Name + ": " + ex.Message));
}
continue;
}
if (!dictionary.TryGetValue(configEntryModel.Section, out var value))
{
value = new ConfigSectionGroup
{
Name = configEntryModel.Section,
DisplayName = func("section." + configEntryModel.Section, configEntryModel.Section)
};
dictionary.Add(configEntryModel.Section, value);
configPluginGroup.Sections.Add(value);
}
value.Entries.Add(configEntryModel);
}
configPluginGroup.Sections.Sort((ConfigSectionGroup a, ConfigSectionGroup b) => string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase));
foreach (ConfigSectionGroup section in configPluginGroup.Sections)
{
section.Entries.Sort((ConfigEntryModel a, ConfigEntryModel b) => string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase));
}
if (configPluginGroup.Sections.Count <= 0)
{
return null;
}
return configPluginGroup;
}
public static IEnumerable<ConfigEntryModel> AllEntries(IEnumerable<ConfigPluginGroup> plugins)
{
return plugins.SelectMany((ConfigPluginGroup p) => p.Sections).SelectMany((ConfigSectionGroup s) => s.Entries);
}
}
internal sealed class ConfigSectionGroup
{
public string Name;
public string DisplayName;
public List<ConfigEntryModel> Entries = new List<ConfigEntryModel>();
}
internal static class ConfigText
{
public static string PageName => L("page.name", "SULFUR Config");
public static string ToolsSection => L("section.tools", "Tools");
public static string FilterLabel => L("filter.label", "Filter");
public static string FilterDescription => L("filter.description", "Search by mod name, GUID, section, key, or description.");
public static string ApplyFilterButton => L("button.apply_filter", "Apply Filter");
public static string ClearFilterButton => L("button.clear_filter", "Clear Filter");
public static string RefreshButton => L("button.refresh", "Refresh");
public static string CollapseAllButton => L("button.collapse_all", "Collapse All");
public static string ApplyButton => L("button.apply_dirty", "Apply");
public static string EntriesBadge => L("badge.entries", "Entries");
public static string PendingBadge => L("badge.dirty", "Pending");
public static string UnchangedBadge => L("badge.clean", "Unchanged");
public static string FooterReady => L("footer.ready", "Ready. Changes are not saved until you click Apply.");
public static string StatusChanged => L("status.changed", "Pending change: ");
public static string StatusDefaultDraft => L("status.default_draft", "Default draft set: ");
public static string StatusApplyResult => L("status.apply_result", "Applied and saved: ");
public static string StatusFailed => L("status.failed", "Failed: ");
public static string StatusInvalid => L("status.invalid", "Invalid: ");
public static string StatusFilterDraft => L("status.filter_draft", "Filter draft changed.");
public static string StatusFilterApplied => L("status.filter_applied", "Filter applied.");
public static string StatusFilterCleared => L("status.filter_cleared", "Filter cleared.");
public static string StatusRefreshing => L("status.refreshing", "Refreshing config list.");
public static string StatusCollapsed => L("status.collapsed", "All mods collapsed.");
public static string NoResults => L("message.no_results", "No config entries match the current filters.");
public static string PendingWarning => L("message.pending_warning", "This setting has unsaved pending changes. Click Apply to write it to cfg.");
public static string InvalidValue => L("message.invalid_value", "This value is not valid for this setting. It will not be applied.");
public static string AllowedRangeHint => L("message.allowed_range", "Allowed range: ");
public static string AllowedValuesHint => L("message.allowed_values", "Allowed values: ");
public static string PendingFilter(bool on)
{
if (!on)
{
return L("filter.dirty_off", "Pending: OFF");
}
return L("filter.dirty_on", "Pending: ON");
}
public static string RyukaFilter(bool on)
{
if (!on)
{
return L("filter.ryuka_off", "Ryuka: OFF");
}
return L("filter.ryuka_on", "Ryuka: ON");
}
public static string AdvancedFilter(bool on)
{
if (!on)
{
return L("filter.advanced_off", "Advanced: OFF");
}
return L("filter.advanced_on", "Advanced: ON");
}
public static string HiddenFilter(bool on)
{
if (!on)
{
return L("filter.hidden_off", "Hidden: OFF");
}
return L("filter.hidden_on", "Hidden: ON");
}
public static string RestartFilter(bool on)
{
if (!on)
{
return L("filter.restart_off", "Restart: OFF");
}
return L("filter.restart_on", "Restart: ON");
}
public static string LiveFilter(bool on)
{
if (!on)
{
return L("filter.live_off", "Live: OFF");
}
return L("filter.live_on", "Live: ON");
}
public static string DangerousFilter(bool on)
{
if (!on)
{
return L("filter.dangerous_off", "Dangerous: OFF");
}
return L("filter.dangerous_on", "Dangerous: ON");
}
public static string TypeName(Type type, bool isFlagsEnum)
{
if (type == null)
{
return "";
}
if (type.IsEnum)
{
if (!isFlagsEnum)
{
return L("type.Enum", "Option");
}
return L("type.Flags", "Flags");
}
if (type == typeof(bool))
{
return L("type.Boolean", "Boolean");
}
if (type == typeof(int))
{
return L("type.Int32", "Integer");
}
if (type == typeof(float))
{
return L("type.Single", "Decimal");
}
if (type == typeof(double))
{
return L("type.Double", "Decimal");
}
if (type == typeof(string))
{
return L("type.String", "Text");
}
if (type == typeof(KeyboardShortcut))
{
return L("type.KeyboardShortcut", "Hotkey");
}
return type.Name;
}
private static string L(string key, string fallback)
{
return SulfurLanguage.FixRtl(SulfurLocalization.Get("ryuka.sulfur.config", key, fallback));
}
}
internal sealed class DraftStore
{
private readonly Dictionary<string, string> drafts = new Dictionary<string, string>(StringComparer.Ordinal);
public int Count => drafts.Count;
public string GetValue(ConfigEntryModel entry)
{
if (!drafts.TryGetValue(entry.Id, out var value))
{
return entry.AppliedValue;
}
return value;
}
public bool IsPending(ConfigEntryModel entry)
{
return drafts.ContainsKey(entry.Id);
}
public void Set(ConfigEntryModel entry, string value)
{
value = value ?? "";
if (value == entry.AppliedValue)
{
drafts.Remove(entry.Id);
}
else
{
drafts[entry.Id] = value;
}
}
public void Discard(ConfigEntryModel entry)
{
drafts.Remove(entry.Id);
}
public void Reconcile(IEnumerable<ConfigEntryModel> entries)
{
if (drafts.Count == 0)
{
return;
}
HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
foreach (ConfigEntryModel entry in entries)
{
hashSet.Add(entry.Id);
if (drafts.TryGetValue(entry.Id, out var value) && value == entry.AppliedValue)
{
drafts.Remove(entry.Id);
}
}
List<string> list = null;
foreach (string key in drafts.Keys)
{
if (!hashSet.Contains(key))
{
if (list == null)
{
list = new List<string>();
}
list.Add(key);
}
}
if (list == null)
{
return;
}
foreach (string item in list)
{
drafts.Remove(item);
}
}
}
internal sealed class EntryRowBuilder
{
private const string HintSeparator = " | ";
private readonly DraftStore drafts;
private readonly Action<SulfurOptionsContext, ConfigEntryModel, string> draftChanged;
private readonly Action<SulfurOptionsContext, string> rebuildWithStatus;
public EntryRowBuilder(DraftStore drafts, Action<SulfurOptionsContext, ConfigEntryModel, string> draftChanged, Action<SulfurOptionsContext, string> rebuildWithStatus)
{
this.drafts = drafts;
this.draftChanged = draftChanged;
this.rebuildWithStatus = rebuildWithStatus;
}
public void Draw(SulfurOptionsContext ctx, ConfigEntryModel entry)
{
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Expected O, but got Unknown
//IL_0173: Unknown result type (might be due to invalid IL or missing references)
//IL_0175: Unknown result type (might be due to invalid IL or missing references)
string value = drafts.GetValue(entry);
bool flag = drafts.IsPending(entry);
SulfurSettingHandle handle = null;
Func<string, bool> showValue = null;
SulfurSettingRow val = new SulfurSettingRow();
val.Label = SulfurLanguage.FixRtl(entry.DisplayName);
val.Description = SulfurLanguage.FixRtl(entry.Description);
val.IndentLevel = 1;
val.IsDirty = flag;
val.RequiresRestart = entry.RequiresRestart;
val.LiveApply = entry.LiveApply;
val.Advanced = entry.Advanced;
val.Hidden = entry.Hidden;
val.Dangerous = entry.Dangerous;
val.DirtyText = ConfigText.PendingBadge;
val.CleanText = ConfigText.UnchangedBadge;
val.ExtraBadges = new string[1] { ConfigText.TypeName(entry.SettingType, entry.IsFlagsEnum) };
val.ShowDefaultButton = value != entry.DefaultValue;
val.OnDefault = delegate
{
ResetToDefault(ctx, entry, handle, showValue);
};
SulfurSettingRow val2 = val;
val2.Message = GetMessage(entry, value, flag, out var kind);
val2.MessageKind = kind;
switch (entry.Kind)
{
case EntryEditorKind.Toggle:
showValue = DrawToggle(ctx, entry, val2, value, out handle);
break;
case EntryEditorKind.Cycle:
showValue = DrawCycle(ctx, entry, val2, value, out handle);
break;
case EntryEditorKind.Number:
showValue = DrawNumber(ctx, entry, val2, value, out handle);
break;
default:
showValue = DrawText(ctx, entry, val2, value, out handle);
break;
}
}
private Func<string, bool> DrawToggle(SulfurOptionsContext ctx, ConfigEntryModel entry, SulfurSettingRow row, string current, out SulfurSettingHandle handle)
{
bool.TryParse(current, out var result);
SulfurOptionsSettingRows.AddSettingToggleEx(ctx, row, result, (Action<bool, SulfurSettingHandle>)delegate(bool newValue, SulfurSettingHandle h)
{
Commit(ctx, entry, entry.SerializeValue(newValue), h);
}, ref handle);
SulfurSettingHandle rowHandle = handle;
bool result2;
return (string serialized) => bool.TryParse(serialized, out result2) && rowHandle != null && rowHandle.SetValueWithoutNotify(result2);
}
private Func<string, bool> DrawCycle(SulfurOptionsContext ctx, ConfigEntryModel entry, SulfurSettingRow row, string current, out SulfurSettingHandle handle)
{
List<string> options = new List<string>(entry.Options);
if (!options.Contains(current))
{
options.Insert(0, current);
}
List<string> list = new List<string>(options.Count);
foreach (string item in options)
{
list.Add(entry.GetValueLabel(item));
}
SulfurOptionsSettingRows.AddSettingCycleEx(ctx, row, (IReadOnlyList<string>)list, options.IndexOf(current), (Action<int, string, SulfurSettingHandle>)delegate(int index, string label, SulfurSettingHandle h)
{
if (index >= 0 && index < options.Count)
{
Commit(ctx, entry, options[index], h);
}
}, ref handle);
SulfurSettingHandle rowHandle = handle;
return delegate(string serialized)
{
int num = options.IndexOf(serialized);
return num >= 0 && rowHandle != null && rowHandle.SetIndexWithoutNotify(num);
};
}
private Func<string, bool> DrawNumber(SulfurOptionsContext ctx, ConfigEntryModel entry, SulfurSettingRow row, string current, out SulfurSettingHandle handle)
{
entry.TryGetNumber(current, out var number);
int decimals = entry.GetDecimals(current);
double halfStep = 0.5 * Math.Pow(10.0, -decimals);
double number2;
Action<double, SulfurSettingHandle> onChanged = delegate(double value, SulfurSettingHandle h)
{
if (!entry.TryGetNumber(drafts.GetValue(entry), out number2) || !(Math.Abs(value - number2) < halfStep))
{
if (!entry.TrySerializeNumber(value, decimals, out var serialized))
{
serialized = value.ToString("R", CultureInfo.InvariantCulture);
}
Commit(ctx, entry, serialized, h);
}
};
if (entry.UsesDoubleWidget)
{
SulfurOptionsSettingRows.AddSettingNumberEx(ctx, row, number, entry.NumberMin, entry.NumberMax, decimals, (Action<double, SulfurSettingHandle>)delegate(double value, SulfurSettingHandle h)
{
onChanged(value, h);
}, ref handle);
}
else
{
SulfurOptionsSettingRows.AddSettingNumberEx(ctx, row, (float)number, (float)entry.NumberMin, (float)entry.NumberMax, decimals, (Action<float, SulfurSettingHandle>)delegate(float value, SulfurSettingHandle h)
{
onChanged(value, h);
}, ref handle);
}
SulfurSettingHandle rowHandle = handle;
return (string serialized) => entry.TryGetNumber(serialized, out number2) && rowHandle != null && rowHandle.SetValueWithoutNotify(number2);
}
private Func<string, bool> DrawText(SulfurOptionsContext ctx, ConfigEntryModel entry, SulfurSettingRow row, string current, out SulfurSettingHandle handle)
{
SulfurOptionsSettingRows.AddSettingTextEx(ctx, row, current, (Action<string, SulfurSettingHandle>)delegate(string value, SulfurSettingHandle h)
{
Commit(ctx, entry, value ?? "", h);
}, ref handle);
SulfurSettingHandle rowHandle = handle;
return (string serialized) => rowHandle != null && rowHandle.SetValueWithoutNotify(serialized ?? "");
}
private void ResetToDefault(SulfurOptionsContext ctx, ConfigEntryModel entry, SulfurSettingHandle handle, Func<string, bool> showValue)
{
entry.RefreshApplied();
drafts.Set(entry, entry.DefaultValue);
string text = ConfigText.StatusDefaultDraft + SulfurLanguage.FixRtl(entry.DisplayName);
if (handle == null || showValue == null || !showValue(entry.DefaultValue))
{
rebuildWithStatus(ctx, text);
return;
}
RefreshRow(entry, handle);
draftChanged(ctx, entry, text);
}
private void Commit(SulfurOptionsContext ctx, ConfigEntryModel entry, string value, SulfurSettingHandle handle)
{
entry.RefreshApplied();
value = entry.CanonicalizeIfApplied(value);
if (!(value == drafts.GetValue(entry)))
{
drafts.Set(entry, value);
if (handle != null)
{
RefreshRow(entry, handle);
}
draftChanged(ctx, entry, ConfigText.StatusChanged + SulfurLanguage.FixRtl(entry.DisplayName));
}
}
private void RefreshRow(ConfigEntryModel entry, SulfurSettingHandle handle)
{
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
string value = drafts.GetValue(entry);
bool flag = drafts.IsPending(entry);
handle.SetDirty(flag, ConfigText.PendingBadge, ConfigText.UnchangedBadge);
handle.SetMessage(GetMessage(entry, value, flag, out var kind), kind);
handle.SetDefaultButtonVisible(value != entry.DefaultValue);
}
private static string GetMessage(ConfigEntryModel entry, string current, bool pending, out SulfurMessageKind kind)
{
string hint = GetHint(entry);
if (pending && !entry.IsValid(current))
{
kind = (SulfurMessageKind)2;
if (hint.Length <= 0)
{
return ConfigText.InvalidValue;
}
return ConfigText.InvalidValue + " | " + hint;
}
if (pending)
{
kind = (SulfurMessageKind)1;
return ConfigText.PendingWarning;
}
kind = (SulfurMessageKind)0;
return hint;
}
private static string GetHint(ConfigEntryModel entry)
{
if (entry.RangeMin.HasValue && entry.RangeMax.HasValue)
{
return ConfigText.AllowedRangeHint + FormatNumber(entry.RangeMin.Value) + " – " + FormatNumber(entry.RangeMax.Value);
}
if (entry.IsFlagsEnum && entry.Options.Count > 0)
{
return ConfigText.AllowedValuesHint + string.Join(", ", entry.Options);
}
return "";
}
private static string FormatNumber(double value)
{
return value.ToString("0.######", CultureInfo.InvariantCulture);
}
}
[BepInDependency("ryuka.sulfur.nativeui", "0.11.0")]
[BepInPlugin("ryuka.sulfur.config", "SULFUR Config", "0.3.0")]
public sealed class Plugin : BaseUnityPlugin
{
public const string PluginGuid = "ryuka.sulfur.config";
public const string PluginName = "SULFUR Config";
public const string PluginVersion = "0.3.0";
private const string NativeUiGuid = "ryuka.sulfur.nativeui";
private const string NativeUiMinimumVersion = "0.11.0";
private ConfigNativePage nativePage;
private void Awake()
{
//IL_0026: 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_0041: 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)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_008d: Expected O, but got Unknown
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 = () => ConfigText.PageName,
BuildPage = nativePage.Build
});
((BaseUnityPlugin)this).Logger.LogInfo((object)"SULFUR Config 0.3.0 registered its options page.");
}
private void OnDestroy()
{
SulfurOptionsApi.UnregisterPage("ryuka.sulfur.config");
}
}