Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LethalEditor v1.2.2
LethalEditor.dll
Decompiled 2 years agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using LethalEditor.Configuration; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Unity.Netcode; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("LethalEditor")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+a50a4b9309fff851727ceb7264988c601eba2a71")] [assembly: AssemblyProduct("LethalEditor")] [assembly: AssemblyTitle("LethalEditor")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/Kyxino/LethalEditor")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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; } } } namespace LethalEditor { internal class Utils { public static string MakeFileNameSafe(string FileName) { return Regex.Replace(FileName, "[\\/:*?\"<>|]", ""); } public static void AddAllTypes() { TomlTypeConverter.AddConverter(typeof(AnimationCurve), (TypeConverter)(object)new AnimationCurve_TypeConverter()); TomlTypeConverter.AddConverter(typeof(string[]), (TypeConverter)(object)new StringArray_TypeConverter()); TomlTypeConverter.AddConverter(typeof(Battery), (TypeConverter)(object)new Battery_TypeConverter()); TomlTypeConverter.AddConverter(typeof(IntWithRarity[]), (TypeConverter)(object)new IntWithRarityArray_TypeConverter()); TomlTypeConverter.AddConverter(typeof(CompatibleNoun[]), (TypeConverter)(object)new CompatibleNounArray_TypeConverter()); TomlTypeConverter.AddConverter(typeof(TerminalKeyword), (TypeConverter)(object)new TerminalKeyword_TypeConverter()); TomlTypeConverter.AddConverter(typeof(TerminalNode), (TypeConverter)(object)new TerminalNode_TypeConverter()); TomlTypeConverter.AddConverter(typeof(SpawnableEnemyWithRarity[]), (TypeConverter)(object)new SpawnableEnemyWithRarityArray_TypeConverter()); TomlTypeConverter.AddConverter(typeof(SpawnableItemWithRarity[]), (TypeConverter)(object)new SpawnableItemWithRarityArray_TypeConverter()); TomlTypeConverter.AddConverter(typeof(List<SpawnableEnemyWithRarity>), (TypeConverter)(object)new SpawnableEnemyWithRarityList_TypeConverter()); TomlTypeConverter.AddConverter(typeof(List<SpawnableItemWithRarity>), (TypeConverter)(object)new SpawnableItemWithRarityList_TypeConverter()); TomlTypeConverter.AddConverter(typeof(EnemyType), (TypeConverter)(object)new EnemyType_TypeConverter()); TomlTypeConverter.AddConverter(typeof(List<ItemGroup>), (TypeConverter)(object)new ItemGroupList_TypeConverter()); TomlTypeConverter.AddConverter(typeof(RandomWeatherWithVariables[]), (TypeConverter)(object)new RandomWeatherWithVariablesArray_TypeConverter()); } } internal class AnimationCurve_TypeConverter : TypeConverter { public AnimationCurve_TypeConverter() { ((TypeConverter)this).ConvertToObject = delegate(string input, Type type) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_0328: Expected O, but got Unknown //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_0302: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) if (type != typeof(AnimationCurve)) { return (object)new AnimationCurve(); } string text = Regex.Replace(input, "[\\s%()%[\\]]+", ""); string[] array2 = text.Split(','); if (string.IsNullOrEmpty(array2[0])) { return (object)new AnimationCurve(); } Keyframe[] array3 = (Keyframe[])(object)new Keyframe[array2.Length]; Keyframe val2 = default(Keyframe); for (int j = 0; j < array3.Length; j++) { string[] array4 = array2[j].Split(';'); string[] array5 = ((array4 != null && array4.Length >= 1) ? ((array4 != null) ? array4[0]?.Split(':') : null) : null); string[] array6 = ((array4 != null && array4.Length >= 2) ? ((array4 != null) ? array4[1]?.Split('|') : null) : null); string[] array7 = ((array4 != null && array4.Length >= 3) ? ((array4 != null) ? array4[2]?.Split(':') : null) : null); string[] array8 = ((array7 != null && array7.Length >= 2) ? ((array7 != null) ? array7[1] : null) : ((array7 != null) ? array7[0] : null))?.Split('|'); float result = (float.TryParse(((array5 != null) ? array5[0] : null) ?? "0", NumberStyles.Float, new CultureInfo("en-US"), out result) ? result : 0f); float result2 = (float.TryParse((array5 != null && array5.Length >= 2) ? ((array5 != null) ? array5[1] : null) : "0", NumberStyles.Float, new CultureInfo("en-US"), out result2) ? result2 : 0f); float result3 = (float.TryParse(((array6 != null) ? array6[0] : null) ?? "0", NumberStyles.Float, new CultureInfo("en-US"), out result3) ? result3 : 0f); float result4 = (float.TryParse((array6 != null && array6.Length >= 2) ? ((array6 != null) ? array6[1] : null) : (((array6 != null) ? array6[0] : null) ?? "0"), NumberStyles.Float, new CultureInfo("en-US"), out result4) ? result4 : 0f); float result5 = (float.TryParse(((array8 != null) ? array8[0] : null) ?? "0", NumberStyles.Float, new CultureInfo("en-US"), out result5) ? result5 : 0f); float result6 = (float.TryParse((array8 != null && array8.Length >= 2) ? ((array8 != null) ? array8[1] : null) : (((array8 != null) ? array8[0] : null) ?? "0"), NumberStyles.Float, new CultureInfo("en-US"), out result6) ? result6 : 0f); int result7 = (int.TryParse(((array7 != null) ? array7[0] : null) ?? "0", NumberStyles.Float, new CultureInfo("en-US"), out result7) ? result7 : 0); ((Keyframe)(ref val2))..ctor(result, result2, result3, result4, result5, result6); ((Keyframe)(ref val2)).weightedMode = (WeightedMode)result7; Keyframe val3 = val2; array3[j] = val3; } return (object)new AnimationCurve(array3); }; ((TypeConverter)this).ConvertToString = delegate(object input, Type type) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Expected I4, but got Unknown if (type != typeof(AnimationCurve)) { return ""; } Keyframe[] keys = ((AnimationCurve)input).keys; if (keys.Length == 0) { return ""; } if (keys.Length == 1) { return ((Keyframe)(ref keys[0])).value.ToString(new CultureInfo("en-US")); } StringBuilder stringBuilder = new StringBuilder(); Keyframe[] array = keys; for (int i = 0; i < array.Length; i++) { Keyframe val = array[i]; float time = ((Keyframe)(ref val)).time; float value = ((Keyframe)(ref val)).value; float inTangent = ((Keyframe)(ref val)).inTangent; float outTangent = ((Keyframe)(ref val)).outTangent; float inWeight = ((Keyframe)(ref val)).inWeight; float outWeight = ((Keyframe)(ref val)).outWeight; int num = (int)((Keyframe)(ref val)).weightedMode; stringBuilder.Append(time.ToString(new CultureInfo("en-US")) + ":" + value.ToString(new CultureInfo("en-US"))); if (inTangent != 0f || outTangent != 0f) { stringBuilder.Append((inTangent == outTangent) ? $";({inTangent})" : $";({inTangent}|{outTangent})"); if (inWeight != 0f || outWeight != 0f) { stringBuilder.Append((num != 0) ? (";" + num.ToString(new CultureInfo("en-US")) + ":") : ";"); stringBuilder.Append((inWeight == outWeight) ? ("[" + inWeight.ToString(new CultureInfo("en-US")) + "]") : ("[" + inWeight.ToString(new CultureInfo("en-US")) + "|" + outWeight.ToString(new CultureInfo("en-US")) + "]")); } else if (num != 0) { stringBuilder.Append(";" + num.ToString(new CultureInfo("en-US"))); } } stringBuilder.Append(", "); } return stringBuilder.ToString(0, Math.Max(0, stringBuilder.Length - 2)); }; } } internal class StringArray_TypeConverter : TypeConverter { public StringArray_TypeConverter() { ((TypeConverter)this).ConvertToObject = (string input, Type type) => (type != typeof(string[])) ? new string[0] : JsonConvert.DeserializeObject<string[]>(input); ((TypeConverter)this).ConvertToString = (object input, Type type) => (type != typeof(string[])) ? "[]" : JsonConvert.SerializeObject(input); } } internal class Battery_TypeConverter : TypeConverter { public Battery_TypeConverter() { ((TypeConverter)this).ConvertToObject = (string input, Type type) => (type != typeof(Battery)) ? ((object)new Battery(false, 1f)) : ((object)JsonConvert.DeserializeObject<Battery>(input)); ((TypeConverter)this).ConvertToString = (object input, Type type) => (type != typeof(Battery)) ? JsonConvert.SerializeObject((object)new Battery(false, 1f)) : JsonConvert.SerializeObject(input); } } internal class IntWithRarityArray_TypeConverter : TypeConverter { public IntWithRarityArray_TypeConverter() { ((TypeConverter)this).ConvertToObject = delegate(string input, Type type) { //IL_0040: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown if (type != typeof(IntWithRarity[])) { return new IntWithRarity[0]; } Dictionary<int, int> dictionary2 = JsonConvert.DeserializeObject<Dictionary<int, int>>(input); List<IntWithRarity> list = new List<IntWithRarity>(); foreach (KeyValuePair<int, int> item in dictionary2) { list.Add(new IntWithRarity { id = item.Key, rarity = item.Value }); } return list.ToArray(); }; ((TypeConverter)this).ConvertToString = delegate(object input, Type type) { if (type != typeof(IntWithRarity[])) { return "{}"; } Dictionary<int, int> dictionary = new Dictionary<int, int>(); IntWithRarity[] array = (IntWithRarity[])input; foreach (IntWithRarity val in array) { dictionary.Add(val.id, val.rarity); } return JsonConvert.SerializeObject((object)dictionary); }; } } internal class CompatibleNounArray_TypeConverter : TypeConverter { public CompatibleNounArray_TypeConverter() { ((TypeConverter)this).ConvertToObject = delegate(string input, Type type) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown if (type != typeof(CompatibleNoun[])) { return new CompatibleNoun[0]; } Dictionary<string, string> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, string>>(input); List<CompatibleNoun> list = new List<CompatibleNoun>(); foreach (KeyValuePair<string, string> PAIR in dictionary2) { list.Add(new CompatibleNoun { noun = (from X in Resources.FindObjectsOfTypeAll<TerminalKeyword>() where Utils.MakeFileNameSafe(((Object)X).name) == PAIR.Key select X).FirstOrDefault(), result = (from X in Resources.FindObjectsOfTypeAll<TerminalNode>() where Utils.MakeFileNameSafe(((Object)X).name) == PAIR.Value select X).FirstOrDefault() }); } return list.ToArray(); }; ((TypeConverter)this).ConvertToString = delegate(object input, Type type) { if (type != typeof(CompatibleNoun[])) { return "{}"; } Dictionary<string, string> NewOptions = new Dictionary<string, string>(); foreach (CompatibleNoun item in ((CompatibleNoun[])input).Where((CompatibleNoun x) => (Object)(object)x.noun != (Object)null && !NewOptions.ContainsKey(((Object)x.noun).name))) { Dictionary<string, string> dictionary = NewOptions; string name = ((Object)item.noun).name; TerminalNode result = item.result; dictionary.Add(name, ((result != null) ? ((Object)result).name : null) ?? ""); } return JsonConvert.SerializeObject((object)NewOptions); }; } } internal class TerminalKeyword_TypeConverter : TypeConverter { public TerminalKeyword_TypeConverter() { ((TypeConverter)this).ConvertToObject = (string input, Type type) => (type != typeof(TerminalKeyword)) ? ((object)ScriptableObject.CreateInstance<TerminalKeyword>()) : ((object)(from X in Resources.FindObjectsOfTypeAll<TerminalKeyword>() where Utils.MakeFileNameSafe(((Object)X).name) == input select X).FirstOrDefault()); ((TypeConverter)this).ConvertToString = (object input, Type type) => (type != typeof(TerminalKeyword)) ? "" : ((Object)(TerminalKeyword)input).name; } } internal class TerminalNode_TypeConverter : TypeConverter { public TerminalNode_TypeConverter() { ((TypeConverter)this).ConvertToObject = (string input, Type type) => (type != typeof(TerminalNode)) ? ((object)ScriptableObject.CreateInstance<TerminalNode>()) : ((object)(from X in Resources.FindObjectsOfTypeAll<TerminalNode>() where Utils.MakeFileNameSafe(((Object)X).name) == input select X).FirstOrDefault()); ((TypeConverter)this).ConvertToString = (object input, Type type) => (type != typeof(TerminalNode)) ? "" : ((Object)(TerminalNode)input).name; } } internal class SpawnableEnemyWithRarityArray_TypeConverter : TypeConverter { public SpawnableEnemyWithRarityArray_TypeConverter() { ((TypeConverter)this).ConvertToObject = delegate(string input, Type type) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown if (type != typeof(SpawnableEnemyWithRarity[])) { return new SpawnableEnemyWithRarity[0]; } Dictionary<string, int> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, int>>(input); List<SpawnableEnemyWithRarity> list = new List<SpawnableEnemyWithRarity>(); foreach (KeyValuePair<string, int> PAIR in dictionary2) { EnemyType val2 = (from X in Resources.FindObjectsOfTypeAll<EnemyType>() where Utils.MakeFileNameSafe(X.enemyName) == PAIR.Key select X).FirstOrDefault(); if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)null)) { list.Add(new SpawnableEnemyWithRarity { enemyType = val2, rarity = PAIR.Value }); } } return list.ToArray(); }; ((TypeConverter)this).ConvertToString = delegate(object input, Type type) { if (type != typeof(SpawnableEnemyWithRarity[])) { return "{}"; } Dictionary<string, int> dictionary = new Dictionary<string, int>(); SpawnableEnemyWithRarity[] array = (SpawnableEnemyWithRarity[])input; foreach (SpawnableEnemyWithRarity val in array) { string text = val.enemyType?.enemyName; if (text != null && text != null) { if (dictionary.ContainsKey(text)) { dictionary[text] += val.rarity; } else { dictionary.Add(text, val.rarity); } } } return JsonConvert.SerializeObject((object)dictionary); }; } } internal class SpawnableItemWithRarityArray_TypeConverter : TypeConverter { public SpawnableItemWithRarityArray_TypeConverter() { ((TypeConverter)this).ConvertToObject = delegate(string input, Type type) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown if (type != typeof(SpawnableItemWithRarity[])) { return new SpawnableItemWithRarity[0]; } Dictionary<string, int> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, int>>(input); List<SpawnableItemWithRarity> list = new List<SpawnableItemWithRarity>(); foreach (KeyValuePair<string, int> PAIR in dictionary2) { Item val2 = (from X in Resources.FindObjectsOfTypeAll<Item>() where Utils.MakeFileNameSafe(X.itemName) == PAIR.Key select X).FirstOrDefault(); if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)null)) { list.Add(new SpawnableItemWithRarity { spawnableItem = val2, rarity = PAIR.Value }); } } return list.ToArray(); }; ((TypeConverter)this).ConvertToString = delegate(object input, Type type) { if (type != typeof(SpawnableItemWithRarity[])) { return "{}"; } Dictionary<string, int> dictionary = new Dictionary<string, int>(); SpawnableItemWithRarity[] array = (SpawnableItemWithRarity[])input; foreach (SpawnableItemWithRarity val in array) { string text = val.spawnableItem?.itemName; if (text != null && text != null) { if (dictionary.ContainsKey(text)) { dictionary[text] += val.rarity; } else { dictionary.Add(text, val.rarity); } } } return JsonConvert.SerializeObject((object)dictionary); }; } } internal class SpawnableEnemyWithRarityList_TypeConverter : TypeConverter { public SpawnableEnemyWithRarityList_TypeConverter() { ((TypeConverter)this).ConvertToObject = delegate(string input, Type type) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown if (type != typeof(List<SpawnableEnemyWithRarity>)) { return new List<SpawnableEnemyWithRarity>(); } Dictionary<string, int> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, int>>(input); List<SpawnableEnemyWithRarity> list = new List<SpawnableEnemyWithRarity>(); foreach (KeyValuePair<string, int> PAIR in dictionary2) { EnemyType val = (from X in Resources.FindObjectsOfTypeAll<EnemyType>() where Utils.MakeFileNameSafe(X.enemyName) == PAIR.Key select X).FirstOrDefault(); if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)null)) { list.Add(new SpawnableEnemyWithRarity { enemyType = val, rarity = PAIR.Value }); } } return list; }; ((TypeConverter)this).ConvertToString = delegate(object input, Type type) { if (type != typeof(List<SpawnableEnemyWithRarity>)) { return "{}"; } Dictionary<string, int> dictionary = new Dictionary<string, int>(); foreach (SpawnableEnemyWithRarity item in (List<SpawnableEnemyWithRarity>)input) { string text = item.enemyType?.enemyName; if (text != null && text != null) { if (dictionary.ContainsKey(text)) { dictionary[text] += item.rarity; } else { dictionary.Add(text, item.rarity); } } } return JsonConvert.SerializeObject((object)dictionary); }; } } internal class SpawnableItemWithRarityList_TypeConverter : TypeConverter { public SpawnableItemWithRarityList_TypeConverter() { ((TypeConverter)this).ConvertToObject = delegate(string input, Type type) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown if (type != typeof(List<SpawnableItemWithRarity>)) { return new List<SpawnableItemWithRarity>(); } Dictionary<string, int> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, int>>(input); List<SpawnableItemWithRarity> list = new List<SpawnableItemWithRarity>(); foreach (KeyValuePair<string, int> PAIR in dictionary2) { Item val = (from X in Resources.FindObjectsOfTypeAll<Item>() where Utils.MakeFileNameSafe(X.itemName) == PAIR.Key select X).FirstOrDefault(); if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)null)) { list.Add(new SpawnableItemWithRarity { spawnableItem = val, rarity = PAIR.Value }); } } return list; }; ((TypeConverter)this).ConvertToString = delegate(object input, Type type) { if (type != typeof(List<SpawnableItemWithRarity>)) { return "{}"; } Dictionary<string, int> dictionary = new Dictionary<string, int>(); foreach (SpawnableItemWithRarity item in (List<SpawnableItemWithRarity>)input) { string text = item.spawnableItem?.itemName; if (text != null && text != null) { if (dictionary.ContainsKey(text)) { dictionary[text] += item.rarity; } else { dictionary.Add(text, item.rarity); } } } return JsonConvert.SerializeObject((object)dictionary); }; } } internal class EnemyType_TypeConverter : TypeConverter { public EnemyType_TypeConverter() { ((TypeConverter)this).ConvertToObject = (string input, Type type) => (type != typeof(EnemyType)) ? ((object)new EnemyType()) : ((object)(from X in Resources.FindObjectsOfTypeAll<EnemyType>() where Utils.MakeFileNameSafe(X.enemyName) == input select X).FirstOrDefault()); ((TypeConverter)this).ConvertToString = (object input, Type type) => (type != typeof(EnemyType)) ? "" : ((EnemyType)input).enemyName; } } internal class ItemGroupList_TypeConverter : TypeConverter { public ItemGroupList_TypeConverter() { ((TypeConverter)this).ConvertToObject = delegate(string input, Type type) { if (type != typeof(List<ItemGroup>)) { return new List<ItemGroup>(); } List<ItemGroup> list2 = new List<ItemGroup>(); foreach (string item in JsonConvert.DeserializeObject<List<string>>(input)) { ItemGroup val = ScriptableObject.CreateInstance<ItemGroup>(); ((Object)val).name = item; list2.Add(val); } return list2; }; ((TypeConverter)this).ConvertToString = delegate(object input, Type type) { if (type != typeof(List<ItemGroup>)) { return "{}"; } List<string> list = new List<string>(); foreach (ItemGroup item2 in (List<ItemGroup>)input) { list.Add(((Object)item2).name); } return JsonConvert.SerializeObject((object)list); }; } } internal class RandomWeatherWithVariablesArray_TypeConverter : TypeConverter { public RandomWeatherWithVariablesArray_TypeConverter() { ((TypeConverter)this).ConvertToObject = delegate(string input, Type type) { //IL_0043: 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_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Expected O, but got Unknown //IL_00b2: Expected O, but got I4 //IL_005e->IL005e: Incompatible stack types: O vs I4 //IL_005c->IL005e: Incompatible stack types: I4 vs O //IL_005c->IL005e: Incompatible stack types: O vs I4 if (type != typeof(RandomWeatherWithVariables[])) { return new RandomWeatherWithVariables[0]; } Dictionary<string, List<int>> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, List<int>>>(input); List<RandomWeatherWithVariables> list = new List<RandomWeatherWithVariables>(); foreach (KeyValuePair<string, List<int>> item in dictionary2) { object obj = list; ? val2 = new RandomWeatherWithVariables(); val2 = val2; int num; if (!Enum.TryParse<LevelWeatherType>(item.Key, out LevelWeatherType result)) { num = -1; obj = num; num = (int)obj; } else { obj = result; num = (int)obj; } ((RandomWeatherWithVariables)val2).weatherType = (LevelWeatherType)obj; ((RandomWeatherWithVariables)val2).weatherVariable = ((item.Value.Count > 0) ? item.Value[0] : 0); ((RandomWeatherWithVariables)val2).weatherVariable2 = ((item.Value.Count > 1) ? item.Value[1] : 0); ((List<RandomWeatherWithVariables>)num).Add((RandomWeatherWithVariables)val2); } return list.ToArray(); }; ((TypeConverter)this).ConvertToString = delegate(object input, Type type) { if (type != typeof(RandomWeatherWithVariables[])) { return "{}"; } Dictionary<string, List<int>> dictionary = new Dictionary<string, List<int>>(); RandomWeatherWithVariables[] array = (RandomWeatherWithVariables[])input; foreach (RandomWeatherWithVariables val in array) { dictionary.Add(((object)(LevelWeatherType)(ref val.weatherType)).ToString(), new List<int>(2) { val.weatherVariable, val.weatherVariable2 }); } return JsonConvert.SerializeObject((object)dictionary); }; } } [Flags] public enum ConfigBehavior { DoNotRun = 0, ApplyExisting = 1, CreateNewFiles = 2 } [BepInPlugin("Kyxino.LethalEditor", "LethalEditor", "1.2.2")] public class EditorMod : BaseUnityPlugin { public static ManualLogSource MLS = Logger.CreateLogSource("Kyxino.LethalEditor"); public static DirectoryInfo MainDir = Directory.CreateDirectory(Paths.BepInExRootPath + "\\LethalEditor"); public static ConfigFile LoadConfig = new ConfigFile(MainDir.FullName + "\\EditorSettings.cfg", saveOnInit: true, MetadataHelper.GetMetadata(typeof(EditorMod))); public static ConfigEntry<bool> IgnoreOverlap = LoadConfig.Bind("Settings", "IgnoreOverlap", defaultValue: false, "When set to true, this WILL cause DefaultValues to be overwritten by non-Vanilla Values, but make Value more certain to apply.", 0); public static ConfigBehavior BehaviorGrabbables = LoadConfig.Bind("Item Config Creation", "Grabbables", ConfigBehavior.ApplyExisting, "CreateNewFiles will allow files to be generated.\nApplyExisting allows existing files to run and potentially apply their settings.\nCreateNewFiles, ApplyExisting does both.\nBehavior for all GrabbableObjects configs.", 1).Value; public static ConfigBehavior BehaviorItems = LoadConfig.Bind("Item Config Creation", "Items", ConfigBehavior.ApplyExisting, "Behavior for all Item configs.", 1).Value; public static ConfigBehavior BehaviorSpecialItems = LoadConfig.Bind("Item Config Creation", "SpecialItems", ConfigBehavior.ApplyExisting, "Behavior for all special item classes configs.", 1).Value; public static ConfigBehavior BehaviorEnemyAIs = LoadConfig.Bind("Enemy Config Creation", "EnemyAIs", ConfigBehavior.ApplyExisting, "Behavior for all EnemyAI configs.", 2).Value; public static ConfigBehavior BehaviorEnemyTypes = LoadConfig.Bind("Enemy Config Creation", "EnemyTypes", ConfigBehavior.ApplyExisting, "Behavior for all EnemyType configs.", 2).Value; public static ConfigBehavior BehaviorSpecialEnemies = LoadConfig.Bind("Enemy Config Creation", "SpecialEnemies", ConfigBehavior.ApplyExisting, "Behavior for all special enemy classes configs.", 2).Value; public static ConfigBehavior BehaviorLevels = LoadConfig.Bind("Misc Config Creation", "Levels", ConfigBehavior.ApplyExisting, "Behavior for all SelectableLevel configs.").Value; public static ConfigBehavior BehaviorUnlockableItems = LoadConfig.Bind("Misc Config Creation", "UnlockableItems", ConfigBehavior.ApplyExisting, "Behavior for all UnlockableItem configs.").Value; public static ConfigBehavior BehaviorTerminalNodes = LoadConfig.Bind("Misc Config Creation", "TerminalNodes", ConfigBehavior.ApplyExisting, "Behavior for all TerminalNode configs.").Value; public static ConfigBehavior BehaviorTerminalKeywords = LoadConfig.Bind("Misc Config Creation", "TerminalKeywords", ConfigBehavior.ApplyExisting, "Behavior for all TerminalKeyword configs.").Value; public static Dictionary<Type, List<string>> FilterSettings = new Dictionary<Type, List<string>> { [typeof(GrabbableObject)] = new List<string>(17) { "isHeld", "isHeldByEnemy", "hasHitGround", "itemUsedUp", "isPocketed", "isBeingUsed", "isInElevator", "isInShipRoom", "isInFactory", "wasOwnerLastFrame", "heldByPlayerOnServer", "reachedFloorTarget", "targetFloorPosition", "startFallingPosition", "currentUseCooldown", "fallTime", "scrapPersistedThroughRounds" }, [typeof(SelectableLevel)] = new List<string>(3) { "currentWeather", "minTotalScrapValue", "maxTotalScrapValue" }, [typeof(EnemyType)] = new List<string>(1) { "numberSpawned" }, [typeof(EnemyAI)] = new List<string>(18) { "currentBehaviourStateIndex", "previousBehaviourStateIndex", "isInsidePlayerShip", "inSpecialAnimation", "thisEnemyIndex", "isClientCalculatingAI", "movingTowardsTargetPlayer", "moveTowardsDestination", "destination", "addPlayerVelocityToDestination", "timeSinceSpawn", "ventAnimationFinished", "isEnemyDead", "daytimeEnemyLeaving", "stunnedIndefinitely", "stunNormalizedTimer", "postStunInvincibilityTimer", "isOutside" }, [typeof(BlobAI)] = new List<string>(1) { "currentSlimeRange" }, [typeof(UnlockableItem)] = new List<string>(5) { "hasBeenMoved", "placedPosition", "placedRotation", "inStorage", "hasBeenUnlockedByPlayer" } }; public static bool AllowFileCreation = false; private static List<object> ObjectApplied = new List<object>(); public static IEnumerable<Type> LethalCompanyClasses = from CheckType in Assembly.GetAssembly(typeof(GrabbableObject)).GetTypes() where CheckType.IsClass && !CheckType.IsAbstract select CheckType; private static readonly object LOCKERBOI = new object(); private void Awake() { Utils.AddAllTypes(); SceneManager.sceneLoaded += delegate(Scene scene, LoadSceneMode mode) { AllowFileCreation = (Object.op_Implicit((Object)(object)GameObject.Find("NetworkManager")) && NetworkManager.Singleton.IsHost) || ((Scene)(ref scene)).name == "InitSceneLaunchOptions" || ((Scene)(ref scene)).name == "InitScene" || ((Scene)(ref scene)).name == "MainMenu"; MLS.LogWarning((object)$"Scene changed: {((Scene)(ref scene)).name} ({((Scene)(ref scene)).path}) - Allow file creation? {AllowFileCreation}"); MLS.LogInfo((object)"Attempting to apply settings."); try { Stopwatch stopwatch = Stopwatch.StartNew(); foreach (Type item in LethalCompanyClasses.Where((Type CheckType) => CheckType.IsSubclassOf(typeof(GrabbableObject)))) { SaveCustom(item, "Items\\{GrabbableObject.itemName}\\{TypeName}Settings", BehaviorSpecialItems); } SaveCustom(typeof(GrabbableObject), "Items\\{GrabbableObject.itemName}\\GrabbableSettings", BehaviorGrabbables); SaveCustom(typeof(Item), "Items\\{itemName}\\ItemSettings", BehaviorItems); foreach (Type item2 in LethalCompanyClasses.Where((Type CheckType) => CheckType.IsSubclassOf(typeof(EnemyAI)))) { SaveCustom(item2, "Enemies\\{EnemyAI.enemyName}\\{TypeName}Settings", BehaviorSpecialEnemies); } SaveCustom(typeof(EnemyAI), "Enemies\\{EnemyAI.enemyName}\\EnemyAISettings", BehaviorEnemyAIs); SaveCustom(typeof(EnemyType), "Enemies\\{enemyName}\\EnemyTypeSettings", BehaviorEnemyTypes); SaveCustom(typeof(SelectableLevel), "Maps\\{PlanetName}\\MapSettings", BehaviorLevels); SaveCustom(typeof(UnlockableItem), "UnlockableItems\\{unlockableName}\\UnlockableItemSettings", BehaviorUnlockableItems, Resources.FindObjectsOfTypeAll<UnlockablesList>().SelectMany((UnlockablesList x) => x.unlockables)); SaveCustom(typeof(TerminalNode), "TerminalNode\\{name}", BehaviorTerminalNodes); SaveCustom(typeof(TerminalKeyword), "TerminalKeywords\\{name}", BehaviorTerminalKeywords); MLS.LogInfo((object)$"Done applying settings. ({(float)stopwatch.ElapsedTicks / 10000f:000.0000}ms)"); } catch (Exception ex) { MLS.LogWarning((object)"Failed to apply settings."); MLS.LogWarning((object)ex); } }; } public static string ReplaceIfPresent(string ModifyingPath, string StringToReplace, Func<object, string> GetReplacement, object CopyObject = null) { if (!ModifyingPath.Contains(StringToReplace)) { return ModifyingPath; } string text = Utils.MakeFileNameSafe(GetReplacement(CopyObject)); if (Utility.IsNullOrWhiteSpace(text)) { return ModifyingPath; } return ModifyingPath.Replace(StringToReplace, text); } private static void SaveCustom(Type SaveType, string Path, ConfigBehavior Behavior, IEnumerable<object> CustomLookup = null) { //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) if (Behavior == ConfigBehavior.DoNotRun) { return; } lock (LOCKERBOI) { FieldInfo[] fields = SaveType.GetFields(BindingFlags.Instance | BindingFlags.Public); Path = ReplaceIfPresent(Path, "{TypeName}", (object CopyObject) => SaveType.Name); foreach (object item in (CustomLookup ?? Resources.FindObjectsOfTypeAll(SaveType)).Where((object x) => !ObjectApplied.Contains(x))) { if (item == null) { MLS.LogWarning((object)$"Missing object class: {item} {SaveType}"); continue; } if (!IgnoreOverlap.Value) { ObjectApplied.Add(item); } string text = Path; try { object obj; if (!SaveType.IsAssignableFrom(typeof(Object))) { obj = ""; } else { Object val = (Object)item; obj = (((int)val != 0) ? val.name : null); } string Name = (string)obj; text = ReplaceIfPresent(text, "{name}", (object CopyObject) => Name, item); text = ReplaceIfPresent(text, "{PlanetName}", delegate(object CopyObject) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) SelectableLevel val6 = (SelectableLevel)CopyObject; return (((int)val6 != 0) ? val6.PlanetName : null) ?? Name; }, item); text = ReplaceIfPresent(text, "{GrabbableObject.itemName}", delegate(object CopyObject) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) GrabbableObject val5 = (GrabbableObject)CopyObject; return (((int)val5 == 0) ? null : val5.itemProperties?.itemName) ?? Name; }, item); text = ReplaceIfPresent(text, "{itemName}", delegate(object CopyObject) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) Item val4 = (Item)CopyObject; return (((int)val4 != 0) ? val4.itemName : null) ?? Name; }, item); text = ReplaceIfPresent(text, "{EnemyAI.enemyName}", delegate(object CopyObject) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) EnemyAI val3 = (EnemyAI)CopyObject; return (((int)val3 == 0) ? null : val3.enemyType?.enemyName) ?? Name; }, item); text = ReplaceIfPresent(text, "{enemyName}", delegate(object CopyObject) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) EnemyType val2 = (EnemyType)CopyObject; return (((int)val2 != 0) ? val2.enemyName : null) ?? Name; }, item); if ((!Behavior.HasFlag(ConfigBehavior.CreateNewFiles) || !AllowFileCreation) && !File.Exists(MainDir.FullName + "\\" + text + ".cfg")) { continue; } ConfigFile configFile = new ConfigFile(MainDir.FullName + "\\" + text + ".cfg", saveOnInit: false, MetadataHelper.GetMetadata(typeof(EditorMod))); ConfigEntry<bool> configEntry = configFile.Bind("ACTIVE SETTINGS", "ConfigEnabled", defaultValue: false, "Whether or not this config file will apply.", 0); configFile.Bind("ACTIVE SETTINGS", "SaveType", SaveType.Name, "The type of this config.", 0); ConfigEntry<string[]> configEntry2 = configFile.Bind("ACTIVE SETTINGS", "LoadSettings", new string[0], "Settings that will load and apply to the game.\nEx: [\"SettingName\",\"OtherSettingName\"]", 0); int num = 1; string text2 = "SETTINGS"; FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { foreach (CustomAttributeTypedArgument item2 in fieldInfo.CustomAttributes.Where((CustomAttributeData x) => x.AttributeType == typeof(HeaderAttribute)).SelectMany((CustomAttributeData Data) => Data.ConstructorArguments.ToList())) { num++; text2 = item2.Value.ToString().ToUpper() ?? ""; } if (fieldInfo.CustomAttributes.FirstOrDefault()?.AttributeType == typeof(HideInInspector) || fieldInfo.DeclaringType != SaveType || (FilterSettings.ContainsKey(SaveType) && FilterSettings[SaveType].Contains(fieldInfo.Name)) || !TomlTypeConverter.CanConvert(fieldInfo.FieldType)) { continue; } object value = configFile.Bind(text2, fieldInfo.Name, fieldInfo.FieldType, fieldInfo.GetValue(item), fieldInfo.CustomAttributes.Where((CustomAttributeData x) => x.AttributeType == typeof(TooltipAttribute)).FirstOrDefault()?.ConstructorArguments.FirstOrDefault().Value?.ToString() ?? "", num).Value; if (Behavior.HasFlag(ConfigBehavior.ApplyExisting) && configEntry.Value && configEntry2.Value.Contains(fieldInfo.Name)) { if (value.GetType() != fieldInfo.FieldType) { MLS.LogWarning((object)(text + ".cfg > " + text2 + " > " + fieldInfo.Name + "'s input type (" + value.GetType().Name + ") is not the same as the required type: " + fieldInfo.FieldType.Name + ".")); return; } fieldInfo.SetValue(item, value); } } configFile.Save(); } catch (Exception arg) { MLS.LogWarning((object)$"Failed to apply settings to a specific object. Path: {MainDir.FullName}\\{text}\n{arg}"); } } } } } } namespace LethalEditor.Configuration { public sealed class SettingChangedEventArgs : EventArgs { public ConfigEntryBase ChangedSetting { get; } public SettingChangedEventArgs(ConfigEntryBase changedSetting) { ChangedSetting = changedSetting; base..ctor(); } } public class ConfigDescription { public string Description { get; } = description ?? throw new ArgumentNullException("description"); public AcceptableValueBase AcceptableValues { get; } public object[] Tags { get; } public static ConfigDescription Empty { get; } = new ConfigDescription("", null); public ConfigDescription(string description, AcceptableValueBase acceptableValues = null, params object[] tags) { AcceptableValues = acceptableValues; Tags = tags; base..ctor(); } } public class ConfigDefinition : IEquatable<ConfigDefinition> { private static readonly char[] _invalidConfigChars = new char[8] { '=', '\n', '\t', '\\', '"', '\'', '[', ']' }; public int Order { get; } public string Section { get; } public string Key { get; } public ConfigDefinition(string section, string key, int order = -1) { CheckInvalidConfigChars(section, "section"); CheckInvalidConfigChars(key, "key"); Order = order; Key = key; Section = section; } private static void CheckInvalidConfigChars(string val, string name) { if (val == null) { throw new ArgumentNullException(name); } if (val != val.Trim()) { throw new ArgumentException("Cannot use whitespace characters at start or end of section and key names", name); } if (val.Any((char c) => _invalidConfigChars.Contains(c))) { throw new ArgumentException("Cannot use any of the following characters in section and key names: = \\n \\t \\ \" ' [ ]", name); } } public bool Equals(ConfigDefinition other) { return other != null && string.Equals(Key, other.Key) && string.Equals(Section, other.Section); } public override bool Equals(object obj) { if (obj == null) { return false; } if (this == (ConfigDefinition)obj) { return true; } return Equals(obj as ConfigDefinition); } public override int GetHashCode() { return (((Key != null) ? Key.GetHashCode() : 0) * 397) ^ ((Section != null) ? Section.GetHashCode() : 0); } public static bool operator ==(ConfigDefinition left, ConfigDefinition right) { return object.Equals(left, right); } public static bool operator !=(ConfigDefinition left, ConfigDefinition right) { return !object.Equals(left, right); } public override string ToString() { return Section + "." + Key; } } public abstract class ConfigEntryBase { public ConfigFile ConfigFile { get; } public ConfigDefinition Definition { get; } public ConfigDescription Description { get; } public Type SettingType { get; } public object DefaultValue { get; } public abstract object BoxedValue { get; set; } internal ConfigEntryBase(ConfigFile configFile, ConfigDefinition definition, Type settingType, object defaultValue, ConfigDescription configDescription) { ConfigFile = configFile ?? throw new ArgumentNullException("configFile"); Definition = definition ?? throw new ArgumentNullException("definition"); SettingType = settingType ?? throw new ArgumentNullException("settingType"); Description = configDescription ?? ConfigDescription.Empty; if (Description.AcceptableValues != null && !SettingType.IsAssignableFrom(Description.AcceptableValues.ValueType)) { throw new ArgumentException("configDescription.AcceptableValues is for a different type than the type of this setting"); } DefaultValue = defaultValue; BoxedValue = defaultValue; } public string GetSerializedValue() { return TomlTypeConverter.ConvertToString(BoxedValue, SettingType); } public void SetSerializedValue(string value) { try { object boxedValue = TomlTypeConverter.ConvertToValue(value, SettingType); BoxedValue = boxedValue; } catch (Exception ex) { EditorMod.MLS.Log((LogLevel)4, (object)$"Config value of setting \"{Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}; Value: {value}"); } } protected T ClampValue<T>(T value) { if (Description.AcceptableValues != null) { return (T)Description.AcceptableValues.Clamp((object)value); } return value; } protected void OnSettingChanged(object sender) { ConfigFile.OnSettingChanged(sender, this); } public void WriteDescription(StreamWriter writer) { if (!string.IsNullOrEmpty(Description.Description)) { writer.WriteLine("## " + Description.Description.Replace("\n", "\n## ")); } string text = ((typeof(List<>).Name == SettingType.Name && SettingType.GenericTypeArguments.Length != 0) ? ("List<" + SettingType.GenericTypeArguments[0].Name + ">") : SettingType.Name); writer.WriteLine("# Setting type: " + text); writer.WriteLine("# Default value: " + TomlTypeConverter.ConvertToString(DefaultValue, SettingType)); if (Description.AcceptableValues != null) { writer.WriteLine(Description.AcceptableValues.ToDescriptionString()); } else if (SettingType.IsEnum) { writer.WriteLine("# Acceptable values: " + string.Join(", ", Enum.GetNames(SettingType))); if (SettingType.GetCustomAttributes(typeof(FlagsAttribute), inherit: true).Any()) { writer.WriteLine("# Multiple values can be set at the same time by separating them with , (e.g. Debug, Warning)"); } } } } public sealed class ConfigEntry<T> : ConfigEntryBase { private T _typedValue; public T Value { get { return _typedValue; } set { value = ClampValue(value); if (!object.Equals(_typedValue, value)) { _typedValue = value; OnSettingChanged(this); } } } public override object BoxedValue { get { return Value; } set { Value = (T)value; } } public event EventHandler SettingChanged; internal ConfigEntry(ConfigFile configFile, ConfigDefinition definition, T defaultValue, ConfigDescription configDescription) : base(configFile, definition, typeof(T), defaultValue, configDescription) { configFile.SettingChanged += delegate(object sender, SettingChangedEventArgs args) { if (args.ChangedSetting == this) { this.SettingChanged?.Invoke(sender, args); } }; } internal ConfigEntry(ConfigFile configFile, ConfigDefinition definition, Type settingType, object defaultValue, ConfigDescription configDescription) : base(configFile, definition, settingType, defaultValue, configDescription) { configFile.SettingChanged += delegate(object sender, SettingChangedEventArgs args) { if (args.ChangedSetting == this) { this.SettingChanged?.Invoke(sender, args); } }; } } public class ConfigFile : IDictionary<ConfigDefinition, ConfigEntryBase>, ICollection<KeyValuePair<ConfigDefinition, ConfigEntryBase>>, IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>, IEnumerable { private readonly BepInPlugin _ownerMetadata; private readonly object _ioLock = new object(); internal static ConfigFile CoreConfig { get; } = new ConfigFile(Paths.BepInExConfigPath, saveOnInit: true); protected Dictionary<ConfigDefinition, ConfigEntryBase> Entries { get; } = new Dictionary<ConfigDefinition, ConfigEntryBase>(); private Dictionary<ConfigDefinition, string> OrphanedEntries { get; } = new Dictionary<ConfigDefinition, string>(); public string ConfigFilePath { get; } public bool SaveOnConfigSet { get; set; } = true; public int Count { get { lock (_ioLock) { return Entries.Count; } } } public bool IsReadOnly => false; ConfigEntryBase IDictionary<ConfigDefinition, ConfigEntryBase>.this[ConfigDefinition key] { get { lock (_ioLock) { return Entries[key]; } } set { throw new InvalidOperationException("Directly setting a config entry is not supported"); } } public ConfigEntryBase this[ConfigDefinition key] { get { lock (_ioLock) { return Entries[key]; } } } public ConfigEntryBase this[string section, string key] => this[new ConfigDefinition(section, key)]; public ICollection<ConfigDefinition> Keys { get { lock (_ioLock) { return Entries.Keys.ToArray(); } } } ICollection<ConfigEntryBase> IDictionary<ConfigDefinition, ConfigEntryBase>.Values { get { lock (_ioLock) { return Entries.Values.ToArray(); } } } public event EventHandler ConfigReloaded; public event EventHandler<SettingChangedEventArgs> SettingChanged; public ConfigFile(string configPath, bool saveOnInit) : this(configPath, saveOnInit, null) { } public ConfigFile(string configPath, bool saveOnInit, BepInPlugin ownerMetadata) { _ownerMetadata = ownerMetadata; if (configPath == null) { throw new ArgumentNullException("configPath"); } configPath = Path.GetFullPath(configPath); ConfigFilePath = configPath; if (File.Exists(ConfigFilePath)) { Reload(); } else if (saveOnInit) { Save(); } } public void Reload() { lock (_ioLock) { OrphanedEntries.Clear(); string section = string.Empty; string[] array = File.ReadAllLines(ConfigFilePath); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.StartsWith("#")) { continue; } if (text.StartsWith("[") && text.EndsWith("]")) { string text2 = text; section = text2.Substring(1, text2.Length - 1 - 1); continue; } string[] array2 = text.Split(new char[1] { '=' }, 2); if (array2.Length == 2) { string key = array2[0].Trim(); string text3 = array2[1].Trim(); ConfigDefinition key2 = new ConfigDefinition(section, key); Entries.TryGetValue(key2, out var value); if (value != null) { value.SetSerializedValue(text3); } else { OrphanedEntries[key2] = text3; } } } } OnConfigReloaded(); } public void Save() { lock (_ioLock) { string directoryName = Path.GetDirectoryName(ConfigFilePath); if (directoryName != null) { Directory.CreateDirectory(directoryName); } using StreamWriter streamWriter = new StreamWriter(ConfigFilePath, append: false, Utility.UTF8NoBom); if (_ownerMetadata != null) { streamWriter.WriteLine($"## Settings file was created by plugin {_ownerMetadata.Name} v{_ownerMetadata.Version}"); streamWriter.WriteLine("## Plugin GUID: " + _ownerMetadata.GUID); streamWriter.WriteLine(); } foreach (var item in from x in Entries.Select((KeyValuePair<ConfigDefinition, ConfigEntryBase> x) => new { Key = x.Key, entry = x.Value, value = x.Value.GetSerializedValue() }).Concat(OrphanedEntries.Select((KeyValuePair<ConfigDefinition, string> x) => new { Key = x.Key, entry = (ConfigEntryBase)null, value = x.Value })) group x by (x.Key.Order, x.Key.Section) into x orderby x.Key.Order == -1, x.Key.Order, x.Key.Section select x) { streamWriter.WriteLine("[" + item.Key.Item2 + "]"); foreach (var item2 in item) { streamWriter.WriteLine(); item2.entry?.WriteDescription(streamWriter); streamWriter.WriteLine(item2.Key.Key + " = " + item2.value); } streamWriter.WriteLine(); } } } public bool TryGetEntry<T>(ConfigDefinition configDefinition, out ConfigEntry<T> entry) { lock (_ioLock) { if (Entries.TryGetValue(configDefinition, out var value)) { entry = (ConfigEntry<T>)value; return true; } entry = null; return false; } } public bool TryGetEntry<T>(string section, string key, out ConfigEntry<T> entry) { return TryGetEntry(new ConfigDefinition(section, key), out entry); } public ConfigEntry<T> Bind<T>(ConfigDefinition configDefinition, Type settingType, T defaultValue, ConfigDescription configDescription = null) { if (!TomlTypeConverter.CanConvert(settingType)) { throw new ArgumentException(string.Format("Type {0} is not supported by the config system. Supported types: {1}", settingType, string.Join(", ", (from x in TomlTypeConverter.GetSupportedTypes() select x.Name).ToArray()))); } lock (_ioLock) { if (Entries.TryGetValue(configDefinition, out var value)) { return (ConfigEntry<T>)value; } ConfigEntry<T> configEntry = new ConfigEntry<T>(this, configDefinition, settingType, defaultValue, configDescription); Entries[configDefinition] = configEntry; if (OrphanedEntries.TryGetValue(configDefinition, out var value2)) { configEntry.SetSerializedValue(value2); OrphanedEntries.Remove(configDefinition); } if (SaveOnConfigSet) { Save(); } return configEntry; } } public ConfigEntry<T> Bind<T>(ConfigDefinition configDefinition, T defaultValue, ConfigDescription configDescription = null) { return Bind(configDefinition, typeof(T), defaultValue, configDescription); } public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, ConfigDescription configDescription = null, int order = -1) { return Bind(new ConfigDefinition(section, key, order), defaultValue, configDescription); } public ConfigEntry<T> Bind<T>(string section, string key, Type settingType, T defaultValue, ConfigDescription configDescription = null, int order = -1) { return Bind(new ConfigDefinition(section, key, order), settingType, defaultValue, configDescription); } public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, string description, int order = -1) { return Bind(new ConfigDefinition(section, key, order), defaultValue, new ConfigDescription(description, null)); } public ConfigEntry<T> Bind<T>(string section, string key, Type settingType, T defaultValue, string description, int order = -1) { return Bind(new ConfigDefinition(section, key, order), settingType, defaultValue, new ConfigDescription(description, null)); } internal void OnSettingChanged(object sender, ConfigEntryBase changedEntryBase) { if (changedEntryBase == null) { throw new ArgumentNullException("changedEntryBase"); } if (SaveOnConfigSet) { Save(); } EventHandler<SettingChangedEventArgs> settingChanged = this.SettingChanged; if (settingChanged == null) { return; } SettingChangedEventArgs e = new SettingChangedEventArgs(changedEntryBase); foreach (EventHandler<SettingChangedEventArgs> item in settingChanged.GetInvocationList().Cast<EventHandler<SettingChangedEventArgs>>()) { try { item(sender, e); } catch (Exception ex) { EditorMod.MLS.Log((LogLevel)2, (object)ex); } } } private void OnConfigReloaded() { EventHandler configReloaded = this.ConfigReloaded; if (configReloaded == null) { return; } foreach (EventHandler item in configReloaded.GetInvocationList().Cast<EventHandler>()) { try { item(this, EventArgs.Empty); } catch (Exception ex) { EditorMod.MLS.Log((LogLevel)2, (object)ex); } } } public IEnumerator<KeyValuePair<ConfigDefinition, ConfigEntryBase>> GetEnumerator() { return Entries.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } void ICollection<KeyValuePair<ConfigDefinition, ConfigEntryBase>>.Add(KeyValuePair<ConfigDefinition, ConfigEntryBase> item) { lock (_ioLock) { Entries.Add(item.Key, item.Value); } } public bool Contains(KeyValuePair<ConfigDefinition, ConfigEntryBase> item) { lock (_ioLock) { return ((ICollection<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)Entries).Contains(item); } } void ICollection<KeyValuePair<ConfigDefinition, ConfigEntryBase>>.CopyTo(KeyValuePair<ConfigDefinition, ConfigEntryBase>[] array, int arrayIndex) { lock (_ioLock) { ((ICollection<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)Entries).CopyTo(array, arrayIndex); } } bool ICollection<KeyValuePair<ConfigDefinition, ConfigEntryBase>>.Remove(KeyValuePair<ConfigDefinition, ConfigEntryBase> item) { lock (_ioLock) { return Entries.Remove(item.Key); } } public bool ContainsKey(ConfigDefinition key) { lock (_ioLock) { return Entries.ContainsKey(key); } } public void Add(ConfigDefinition key, ConfigEntryBase value) { throw new InvalidOperationException("Directly adding a config entry is not supported"); } public bool Remove(ConfigDefinition key) { lock (_ioLock) { return Entries.Remove(key); } } public void Clear() { lock (_ioLock) { Entries.Clear(); } } bool IDictionary<ConfigDefinition, ConfigEntryBase>.TryGetValue(ConfigDefinition key, out ConfigEntryBase value) { lock (_ioLock) { return Entries.TryGetValue(key, out value); } } } }