Please disclose if any significant portion of your mod was created 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 PickaxeSoundChanger v1.0.4
plugins/PickaxeSoundChanger.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("LuminairPrime")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Changes the high-pitched pickaxe sound to use the default axe sound.")] [assembly: AssemblyFileVersion("1.0.4.0")] [assembly: AssemblyInformationalVersion("1.0.4+d772a278c412db69091713670daa432af72fe4bf")] [assembly: AssemblyProduct("PickaxeSoundChanger")] [assembly: AssemblyTitle("PickaxeSoundChanger")] [assembly: AssemblyVersion("1.0.4.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PickaxeSoundChanger { internal static class DonorPool { internal enum Lookup { Found, Missing, WorldNotReady } private static Dictionary<string, AudioClip[]> Cache = new Dictionary<string, AudioClip[]>(StringComparer.Ordinal); private static HashSet<string> Swept = new HashSet<string>(StringComparer.Ordinal); internal static void Clear() { Cache = new Dictionary<string, AudioClip[]>(StringComparer.Ordinal); Swept = new HashSet<string>(StringComparer.Ordinal); } internal static void Learn(string name, AudioClip[]? clips) { if (clips != null && clips.Length != 0 && !Cache.ContainsKey(name)) { Cache[name] = clips; } } internal static Lookup Resolve(string donorName, out AudioClip[]? clips) { clips = null; if (Cache.TryGetValue(donorName, out AudioClip[] value) && value.Length != 0) { clips = value; return Lookup.Found; } ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { return Lookup.WorldNotReady; } GameObject prefab = instance.GetPrefab(donorName); AudioClip[] array = ((!((Object)(object)prefab != (Object)null)) ? null : prefab.GetComponent<ZSFX>()?.m_audioClips); if (array != null && array.Length != 0) { Cache[donorName] = array; clips = array; return Lookup.Found; } if (!Swept.Add(donorName)) { return Lookup.Missing; } ZSFX[] array2 = Resources.FindObjectsOfTypeAll<ZSFX>(); foreach (ZSFX val in array2) { if (!((Object)(object)val == (Object)null) && val.m_audioClips != null && val.m_audioClips.Length != 0 && Utils.GetPrefabName(((Component)val).gameObject) == donorName) { Cache[donorName] = val.m_audioClips; clips = val.m_audioClips; return Lookup.Found; } } return Lookup.Missing; } } [BepInPlugin("LuminairPrime.valheim.pickaxesoundchanger", "Pickaxe Sound Changer", "1.0.4")] public sealed class PickaxeSoundChangerPlugin : BaseUnityPlugin { public const string PluginGuid = "LuminairPrime.valheim.pickaxesoundchanger"; public const string PluginName = "Pickaxe Sound Changer"; public const string PluginVersion = "1.0.4"; private const string GeneralSection = "00 - General"; private static readonly string[] MappingSlots = new string[8] { "Mapping 1", "Mapping 2", "Mapping 3", "Mapping 4", "Mapping 5", "Mapping 6", "Mapping 7", "Mapping 8" }; private static readonly (string Sound, string Pitch, string Volume)[] SlotDefaults = new(string, string, string)[3] { ("sfx_pickaxe_hit = sfx_axe_hit", "0.95-1.0", "0.4-0.45"), ("sfx_rock_hit = sfx_rock_destroyed", "1", "0.2"), ("sfx_gui_repairitem_forge = sfx_gui_repairitem_workbench", "0.9-1.0", "0.6-0.7") }; private const string MappingDescription = "Replace a sound using the format 'original = replacement'. Mute a sound with 'original = mute'. Enter one 'original' sound to tweak its volume/pitch."; private static readonly ConfigEntry<string>[] Mappings = new ConfigEntry<string>[MappingSlots.Length]; private static readonly ConfigEntry<string>[] Pitches = new ConfigEntry<string>[MappingSlots.Length]; private static readonly ConfigEntry<string>[] Volumes = new ConfigEntry<string>[MappingSlots.Length]; private const string PitchDescription = "A value between 0.0 and 1.0. A two-value range chooses a random intermediate value. Empty field acts as default."; private const string VolumeDescription = "A value between 0.0 and 1.0. A two-value range chooses a random intermediate value. Empty field acts as default."; internal static ConfigEntry<string> ToggleKey = null; internal static ConfigEntry<bool> LogSoundNames = null; private static KeyboardShortcut _toggleShortcut = KeyboardShortcut.Empty; private Harmony? _harmony; internal static ManualLogSource Log { get; private set; } = null; internal static bool SoundChangesEnabled { get; private set; } = true; private static string SectionFor(int index) { return $"{index + 1:D2} - {MappingSlots[index]}"; } private void Awake() { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; SoundChangesEnabled = true; ToggleKey = ((BaseUnityPlugin)this).Config.Bind<string>("00 - General", "Toggle Key", "k", "This key in Valheim switches the mod off and on so you can compare how things sound. Empty field disables hotkey."); ToggleKey.SettingChanged += OnToggleKeyChanged; ParseToggleKey(); LogSoundNames = ((BaseUnityPlugin)this).Config.Bind<bool>("00 - General", "Log Sound Names", false, "Enable this to monitor Valheim's sound effects in the BepInEx console. You can copy a sound name into the empty mappings below to change it!"); for (int i = 0; i < Mappings.Length; i++) { BindSlot(i); } ApplyMappings(); _harmony = new Harmony("LuminairPrime.valheim.pickaxesoundchanger"); _harmony.PatchAll(typeof(PickaxeSoundChangerPlugin).Assembly); Log.LogInfo((object)string.Format("{0} v{1} loaded ({2} mapping(s)). Sound changes start on for this game session.", "Pickaxe Sound Changer", "1.0.4", SoundMap.Count)); } private void BindSlot(int index) { (string, string, string) tuple = ((index < SlotDefaults.Length) ? SlotDefaults[index] : (string.Empty, string.Empty, string.Empty)); string text = SectionFor(index); Mappings[index] = ((BaseUnityPlugin)this).Config.Bind<string>(text, "Sound", tuple.Item1, "Replace a sound using the format 'original = replacement'. Mute a sound with 'original = mute'. Enter one 'original' sound to tweak its volume/pitch."); Volumes[index] = ((BaseUnityPlugin)this).Config.Bind<string>(text, "Volume", tuple.Item3, "A value between 0.0 and 1.0. A two-value range chooses a random intermediate value. Empty field acts as default."); Pitches[index] = ((BaseUnityPlugin)this).Config.Bind<string>(text, "Pitch", tuple.Item2, "A value between 0.0 and 1.0. A two-value range chooses a random intermediate value. Empty field acts as default."); Mappings[index].SettingChanged += OnMappingsChanged; Pitches[index].SettingChanged += OnMappingsChanged; Volumes[index].SettingChanged += OnMappingsChanged; } private void Update() { if (((KeyboardShortcut)(ref _toggleShortcut)).IsDown() && !IsTyping()) { SoundChangesEnabled = !SoundChangesEnabled; Log.LogInfo((object)("Sound changes are now " + (SoundChangesEnabled ? "on" : "off") + " for this game session.")); } } private static void ParseToggleKey() { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: 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) List<KeyCode> list = new List<KeyCode>(); foreach (string item in ToggleKeyParser.Normalize(ToggleKey.Value)) { if (!string.Equals(item, "None", StringComparison.OrdinalIgnoreCase)) { if (!Enum.TryParse<KeyCode>(item, ignoreCase: true, out KeyCode result) || !Enum.IsDefined(typeof(KeyCode), result)) { Log.LogWarning((object)("'Toggle Key' ignores '" + item + "': not the name of a Unity key. See the README for accepted spellings.")); } else { list.Add(result); } } } _toggleShortcut = (KeyboardShortcut)((list.Count == 0) ? KeyboardShortcut.Empty : new KeyboardShortcut(list[0], list.Skip(1).ToArray())); if ((int)((KeyboardShortcut)(ref _toggleShortcut)).MainKey != 0) { Log.LogInfo((object)("Toggle key bound to " + string.Join(" + ", list) + ".")); } else if (list.Count == 0 && ToggleKey.Value.Trim().Length > 0) { Log.LogWarning((object)("'Toggle Key' ('" + ToggleKey.Value.Trim() + "') holds no usable key, so nothing is bound.")); } } private static void OnToggleKeyChanged(object? sender, EventArgs e) { ParseToggleKey(); } private static bool IsTyping() { if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible()) { return TextInput.IsVisible(); } return true; } private void OnDestroy() { ToggleKey.SettingChanged -= OnToggleKeyChanged; for (int i = 0; i < Mappings.Length; i++) { Mappings[i].SettingChanged -= OnMappingsChanged; Pitches[i].SettingChanged -= OnMappingsChanged; Volumes[i].SettingChanged -= OnMappingsChanged; } Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; } private static void OnMappingsChanged(object? sender, EventArgs e) { ApplyMappings(); Log.LogInfo((object)$"Sound mappings reloaded ({SoundMap.Count} mapping(s))."); } private static void ApplyMappings() { List<(string, string, string, string)> list = new List<(string, string, string, string)>(Mappings.Length); for (int i = 0; i < Mappings.Length; i++) { list.Add((MappingSlots[i], Mappings[i].Value, Pitches[i].Value, Volumes[i].Value)); } IReadOnlyList<string> readOnlyList = SoundMap.Rebuild(list); DonorPool.Clear(); SfxAwakePatch.Reset(); foreach (string item in readOnlyList) { Log.LogWarning((object)item); } } } [HarmonyPatch(typeof(ZSFX), "Awake")] internal static class SfxAwakePatch { private const int MaxReportedFailures = 64; private static readonly AudioClip[] Silent = Array.Empty<AudioClip>(); private static HashSet<string> Warned = new HashSet<string>(StringComparer.Ordinal); private static HashSet<string> Swapped = new HashSet<string>(StringComparer.Ordinal); private static HashSet<string> Errored = new HashSet<string>(StringComparer.Ordinal); internal static void Reset() { Warned = new HashSet<string>(StringComparer.Ordinal); Swapped = new HashSet<string>(StringComparer.Ordinal); Errored = new HashSet<string>(StringComparer.Ordinal); } private static void Postfix(ZSFX __instance) { try { bool value = PickaxeSoundChangerPlugin.LogSoundNames.Value; if ((!value && (SoundMap.Count == 0 || !PickaxeSoundChangerPlugin.SoundChangesEnabled)) || (Object)(object)__instance == (Object)null) { return; } string prefabName = Utils.GetPrefabName(((Component)__instance).gameObject); if (string.IsNullOrEmpty(prefabName)) { return; } if (value) { LogSoundName(__instance, prefabName); } if (!PickaxeSoundChangerPlugin.SoundChangesEnabled) { return; } if (SoundMap.IsDonor(prefabName)) { DonorPool.Learn(prefabName, __instance.m_audioClips); } if (!SoundMap.TryGetDonor(prefabName, out string donor)) { return; } if (SoundMap.IsNoDonor(donor)) { ApplyTuningOnly(__instance, prefabName); return; } AudioClip[] array; if (SoundMap.IsMuteDonor(donor)) { array = Silent; } else { AudioClip[] clips; DonorPool.Lookup lookup = DonorPool.Resolve(donor, out clips); if (lookup == DonorPool.Lookup.WorldNotReady) { return; } if (lookup != DonorPool.Lookup.Found || clips == null) { if (Warned.Add(donor)) { PickaxeSoundChangerPlugin.Log.LogWarning((object)("Could not find the donor sound '" + donor + "' for '" + prefabName + "'; the original sound is unchanged.")); } return; } array = clips; } if (SoundMap.TryGetTuning(prefabName, out var tuning) && !ApplyTuning(__instance, in tuning)) { tuning = default(Tuning); } if (__instance.m_audioClips == array) { return; } __instance.m_audioClips = array; if (Swapped.Add(prefabName)) { if (array == Silent) { PickaxeSoundChangerPlugin.Log.LogInfo((object)("'" + prefabName + "' is now silent.")); return; } ReportIfMuted(__instance, prefabName); string text = Describe(in tuning); string text2 = ((text.Length == 0) ? string.Empty : ("; " + text)); PickaxeSoundChangerPlugin.Log.LogInfo((object)$"'{prefabName}' now plays the clips from '{donor}' ({array.Length} clip(s){text2})."); } } catch (Exception e) { ReportOnce(e, "Failed to process a sound effect; it was left unchanged."); } } private static bool ApplyTuning(ZSFX zsfx, in Tuning tuning) { try { if (tuning.Pitch.Has) { zsfx.m_minPitch = tuning.Pitch.Min; zsfx.m_maxPitch = tuning.Pitch.Max; } if (tuning.Volume.Has) { zsfx.m_minVol = tuning.Volume.Min; zsfx.m_maxVol = tuning.Volume.Max; } return true; } catch (Exception e) { ReportOnce(e, "Could not fully apply pitch/volume tuning; the clip swap will still be attempted."); return false; } } private static void ApplyTuningOnly(ZSFX zsfx, string name) { if (SoundMap.TryGetTuning(name, out var tuning) && tuning.HasAny && ApplyTuning(zsfx, in tuning) && Swapped.Add(name)) { ReportIfMuted(zsfx, name); PickaxeSoundChangerPlugin.Log.LogInfo((object)("'" + name + "' keeps its own clips and now plays at " + Describe(in tuning) + ".")); } } private static string Describe(in Tuning tuning) { List<string> list = new List<string>(); if (tuning.Pitch.Has) { list.Add("pitch " + tuning.Pitch.Text); } if (tuning.Volume.Has) { list.Add("volume " + tuning.Volume.Text); } return string.Join(", ", list); } private static void ReportIfMuted(ZSFX zsfx, string name) { try { AudioSource component = ((Component)zsfx).GetComponent<AudioSource>(); if ((Object)(object)component != (Object)null && component.mute) { PickaxeSoundChangerPlugin.Log.LogWarning((object)("'" + name + "' is muted, so editing its clips will not be audible. Another mod or the game has muted this sound effect; unmute it there for this mapping to be heard.")); } } catch (Exception e) { ReportOnce(e, "Could not check whether a sound effect is muted."); } } private static void LogSoundName(ZSFX zsfx, string name) { try { AudioSource component = ((Component)zsfx).GetComponent<AudioSource>(); AudioClip[] audioClips = zsfx.m_audioClips; int num = ((audioClips != null) ? audioClips.Length : 0); PickaxeSoundChangerPlugin.Log.LogInfo((object)($"[sfx] {name} ({num} clip(s), mute={(Object)(object)component != (Object)null && component.mute}, " + $"vibration={zsfx.m_useVibration})")); } catch (Exception e) { ReportOnce(e, "Could not read a sound effect's diagnostic information."); } } private static void ReportOnce(Exception e, string what) { if (Errored.Count < 64 && Errored.Add(e.GetType().Name + ": " + e.Message)) { PickaxeSoundChangerPlugin.Log.LogWarning((object)$"{what} {e}"); } } } internal readonly struct TuningRange { internal readonly bool Has; internal readonly float Min; internal readonly float Max; internal readonly string Text; internal TuningRange(float min, float max, string text) { Has = true; Min = min; Max = max; Text = text; } } internal readonly struct Tuning { internal readonly TuningRange Pitch; internal readonly TuningRange Volume; internal bool HasAny { get { if (!Pitch.Has) { return Volume.Has; } return true; } } internal Tuning(TuningRange pitch, TuningRange volume) { Pitch = pitch; Volume = volume; } } internal static class SoundMap { private static readonly char[] ExtraSeparators = new char[3] { ';', '\r', '\n' }; public const string PickaxeHitMapping = "sfx_pickaxe_hit = sfx_axe_hit"; public const string PickaxeHitPitch = "0.95-1.0"; public const string PickaxeHitVolume = "0.4-0.45"; public const string RockHitMapping = "sfx_rock_hit = sfx_rock_destroyed"; public const string RockHitPitch = "1"; public const string RockHitVolume = "0.2"; public const string ForgeRepairMapping = "sfx_gui_repairitem_forge = sfx_gui_repairitem_workbench"; public const string ForgeRepairPitch = "0.9-1.0"; public const string ForgeRepairVolume = "0.6-0.7"; internal const string MuteDonor = "mute"; internal const string NoDonor = ""; private static Dictionary<string, string> _targets = new Dictionary<string, string>(StringComparer.Ordinal); private static HashSet<string> _donors = new HashSet<string>(StringComparer.Ordinal); private static Dictionary<string, Tuning> _tunings = new Dictionary<string, Tuning>(StringComparer.Ordinal); internal static int Count => _targets.Count; internal static bool TryGetTuning(string name, out Tuning tuning) { return _tunings.TryGetValue(name, out tuning); } internal static bool IsMuteDonor(string value) { return string.Equals(value, "mute", StringComparison.OrdinalIgnoreCase); } internal static bool IsNoDonor(string value) { return value.Length == 0; } internal static bool IsDonor(string name) { return _donors.Contains(name); } internal static bool TryGetDonor(string name, out string donor) { if (_targets.TryGetValue(name, out string value)) { donor = value; return true; } donor = string.Empty; return false; } internal static IReadOnlyList<string> Rebuild(IReadOnlyList<(string Slot, string Value, string Pitch, string Volume)> slots) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal); Dictionary<string, (string, string)> dictionary2 = new Dictionary<string, (string, string)>(StringComparer.Ordinal); List<string> list = new List<string>(); Dictionary<string, Tuning> dictionary3 = new Dictionary<string, Tuning>(StringComparer.Ordinal); List<(string, string, string, string)> list2 = new List<(string, string, string, string)>(); foreach (var slot in slots) { string item = slot.Slot; string item2 = slot.Value; string item3 = slot.Pitch; string item4 = slot.Volume; string text = item2.Trim(); if (text.Length == 0) { continue; } if (text.IndexOfAny(ExtraSeparators) >= 0) { list.Add(item + " ('" + text + "') was dropped: each slot holds one 'target = donor' pair, so use another slot for a second mapping."); continue; } int num = text.IndexOf('='); string text2; string text3; if (num < 0) { if (!AcceptTuningOnly(item, text, item3, item4, list)) { continue; } text2 = text; text3 = ""; } else { if (text.IndexOf('=', num + 1) >= 0) { list.Add(item + " ('" + text + "') was dropped: only one '=' belongs in a slot."); continue; } text2 = text.Substring(0, num).Trim(); text3 = text.Substring(num + 1).Trim(); if (text2.Length == 0 || text3.Length == 0) { list.Add(item + " ('" + text + "') was dropped: both sides of '=' need a name."); continue; } } if (IsMuteDonor(text2)) { list.Add(item + " ('" + text + "') was dropped: 'mute' is a donor, so it belongs on the right of the '='."); continue; } if (string.Equals(text2, text3, StringComparison.Ordinal)) { list.Add(item + " ('" + text + "') was dropped: a sound cannot map to itself."); continue; } if (dictionary2.TryGetValue(text2, out var value)) { list.Add(item + " ('" + text + "') overrides '" + value.Item2 + "' from " + value.Item1 + "; both target '" + text2 + "'."); } dictionary[text2] = text3; dictionary2[text2] = (item, text); for (int num2 = list2.Count - 1; num2 >= 0; num2--) { if (string.Equals(list2[num2].Item1, text2, StringComparison.Ordinal)) { list2.RemoveAt(num2); } } if (!IsMuteDonor(text3)) { list2.Add((text2, item, item3, item4)); } } foreach (var item9 in list2) { string item5 = item9.Item1; string item6 = item9.Item2; string item7 = item9.Item3; string item8 = item9.Item4; Tuning value2 = new Tuning(ParseTuning(item6, "pitch", item7, list), ParseTuning(item6, "volume", item8, list)); if (value2.HasAny) { dictionary3[item5] = value2; } } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (string value3 in dictionary.Values) { if (!IsMuteDonor(value3) && !IsNoDonor(value3)) { hashSet.Add(value3); } } _targets = dictionary; _donors = hashSet; _tunings = dictionary3; return list; } private static bool AcceptTuningOnly(string slot, string text, string pitch, string volume, List<string> problems) { if (pitch.Trim().Length == 0 && volume.Trim().Length == 0) { problems.Add(slot + " ('" + text + "') was dropped: no '=' was found, and with no pitch or volume set there is nothing for it to change."); return false; } if (text.IndexOf(' ') >= 0 || text.IndexOf('\t') >= 0) { problems.Add(slot + " ('" + text + "') was dropped: no '=' was found."); return false; } return true; } private static TuningRange ParseTuning(string slot, string field, string value, List<string> problems) { string text = value.Trim(); if (text.Length == 0) { return default(TuningRange); } string text2 = null; float result; bool flag = float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out result); float result2 = result; if (!flag) { string[] array = text.Split('-'); flag = array.Length == 2 && float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out result) && float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out result2); } if (!flag) { text2 = "expected a number like '0.95' or a range like '0.95-1.0'. Use a dot as the decimal separator."; } else if (!float.IsFinite(result) || !float.IsFinite(result2)) { text2 = "expected a finite number like '0.95' or '0.95-1.0'."; } else if (field == "pitch" && (result <= 0f || result2 <= 0f)) { text2 = "pitch must be greater than 0. Use the 'mute' donor to silence a sound."; } else if (field == "volume" && (result < 0f || result2 < 0f)) { text2 = "volume cannot be negative."; } else if (result > result2) { text2 = "the first number must not be larger than the second."; } if (text2 != null) { problems.Add(slot + " " + field + " ('" + text + "') was ignored: " + text2); return default(TuningRange); } return new TuningRange(result, result2, text); } } internal static class ToggleKeyParser { private static readonly char[] Separators = new char[5] { ' ', '+', ',', ';', '|' }; private static readonly Dictionary<string, string> Aliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { ["0"] = "Alpha0", ["1"] = "Alpha1", ["2"] = "Alpha2", ["3"] = "Alpha3", ["4"] = "Alpha4", ["5"] = "Alpha5", ["6"] = "Alpha6", ["7"] = "Alpha7", ["8"] = "Alpha8", ["9"] = "Alpha9", ["enter"] = "Return", ["esc"] = "Escape", ["ctrl"] = "LeftControl", ["control"] = "LeftControl", ["shift"] = "LeftShift", ["alt"] = "LeftAlt", ["spacebar"] = "Space" }; internal static IReadOnlyList<string> Normalize(string? raw) { List<string> list = new List<string>(); if (raw == null) { return list; } string[] array = raw.Split(Separators, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { if (Aliases.TryGetValue(text, out string value)) { list.Add(value); continue; } char c = ((text.Length == 1) ? text[0] : '\0'); bool flag = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); list.Add(flag ? char.ToUpperInvariant(c).ToString() : text); } return list; } } }