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 ValheimSpeakerMode v1.0.1
ValheimSpeakerMode.dll
Decompiled 2 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; 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 TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; using Valheim.SettingsGui; [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("ValheimSpeakerMode")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+c2f09e5b130fe4c806a0e2f92c47ad58657b82ab")] [assembly: AssemblyProduct("ValheimSpeakerMode")] [assembly: AssemblyTitle("ValheimSpeakerMode")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ValheimSpeakerMode { [HarmonyPatch(typeof(AudioSettings), "Initialize")] internal static class AudioSettingsInitializePatch { private static void Postfix(AudioSettings __instance, Toggle ___m_continousMusic) { try { AudioModeDropdown.Attach(__instance, ___m_continousMusic); } catch (Exception ex) { Plugin.Log.LogError((object)("Could not inject speaker mode dropdown: " + ex)); } } } [HarmonyPatch(typeof(AudioSettings), "OnTabOpen")] internal static class AudioSettingsTabOpenPatch { private static void Postfix(AudioSettings __instance, Button backButton, Button okButton) { AudioModeDropdown component = ((Component)__instance).GetComponent<AudioModeDropdown>(); if ((Object)(object)component != (Object)null) { component.RefreshSelection(); component.UpdateNavigation(backButton, okButton); } } } [HarmonyPatch(typeof(AudioSettings), "OnOkAsync")] internal static class AudioSettingsOkPatch { private static void Prefix() { if ((Object)(object)Plugin.Instance != (Object)null) { Plugin.Instance.CommitSpeakerMode(); } } } [HarmonyPatch(typeof(AudioSettings), "OnBack")] internal static class AudioSettingsBackPatch { private static void Postfix() { if ((Object)(object)Plugin.Instance != (Object)null) { Plugin.Instance.RevertSpeakerMode(); } } } internal sealed class AudioModeDropdown : MonoBehaviour { private static readonly int[] ModeValues = new int[7] { 1, 2, 3, 4, 5, 6, 7 }; private TMP_Dropdown _dropdown; private Toggle _lastVanillaControl; internal static void Attach(AudioSettings audio, Toggle lastVanillaControl) { if (!((Object)(object)audio == (Object)null) && !((Object)(object)lastVanillaControl == (Object)null) && !((Object)(object)((Component)audio).GetComponent<AudioModeDropdown>() != (Object)null)) { Component val = FindNativeDropdown(); if ((Object)(object)val == (Object)null) { throw new InvalidOperationException("Could not find Valheim's native GuiDropdown template."); } Transform parent = ((Component)lastVanillaControl).transform.parent; if ((Object)(object)parent == (Object)null || (Object)(object)((Component)parent).GetComponent<VerticalLayoutGroup>() == (Object)null) { throw new InvalidOperationException("Could not find the Audio Settings VerticalLayoutGroup."); } Transform val2 = parent.Find("MasterVolume"); if ((Object)(object)val2 == (Object)null) { throw new InvalidOperationException("Could not find MasterVolume row."); } Transform obj = val2.Find("Label"); if ((Object)(object)obj == (Object)null) { throw new InvalidOperationException("Could not find MasterVolume Label."); } TMP_Text component = ((Component)obj).GetComponent<TMP_Text>(); if ((Object)(object)component == (Object)null) { throw new InvalidOperationException("MasterVolume Label has no TMP_Text component."); } AudioModeDropdown audioModeDropdown = ((Component)audio).gameObject.AddComponent<AudioModeDropdown>(); audioModeDropdown._lastVanillaControl = lastVanillaControl; audioModeDropdown.Build(parent, val2, component, val); } } private static Component FindNativeDropdown() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) Type type = AccessTools.TypeByName("Valheim.SettingsGui.GraphicsSettings"); if (type == null) { type = AccessTools.TypeByName("GraphicsSettings"); } if (type == null) { return null; } FieldInfo fieldInfo = AccessTools.Field(type, "m_resolutionDropdown"); if (fieldInfo == null) { return null; } Object[] array = Resources.FindObjectsOfTypeAll(type); Object[] array2 = array; foreach (Object obj in array2) { Component val = (Component)(object)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null) { continue; } Scene scene = val.gameObject.scene; if (((Scene)(ref scene)).IsValid()) { object? value = fieldInfo.GetValue(val); Component val2 = (Component)((value is Component) ? value : null); if ((Object)(object)val2 != (Object)null && (Object)(object)val2.GetComponent<TMP_Dropdown>() != (Object)null) { return val2; } } } array2 = array; foreach (Object obj2 in array2) { Component val3 = (Component)(object)((obj2 is Component) ? obj2 : null); if (!((Object)(object)val3 == (Object)null)) { object? value2 = fieldInfo.GetValue(val3); Component val4 = (Component)((value2 is Component) ? value2 : null); if ((Object)(object)val4 != (Object)null && (Object)(object)val4.GetComponent<TMP_Dropdown>() != (Object)null) { return val4; } } } return null; } private void Build(Transform layoutParent, Transform rowTemplate, TMP_Text labelSource, Component dropdownTemplate) { //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(((Component)rowTemplate).gameObject, layoutParent, false); ((Object)val).name = "SpeakerMode"; Transform val2 = val.transform.Find("Background"); Transform val3 = val.transform.Find("Fill Area"); Transform val4 = val.transform.Find("Handle Slide Area"); Transform val5 = val.transform.Find("Value"); if ((Object)(object)val2 != (Object)null) { Object.Destroy((Object)(object)((Component)val2).gameObject); } if ((Object)(object)val3 != (Object)null) { Object.Destroy((Object)(object)((Component)val3).gameObject); } if ((Object)(object)val4 != (Object)null) { Object.Destroy((Object)(object)((Component)val4).gameObject); } if ((Object)(object)val5 != (Object)null) { Object.Destroy((Object)(object)((Component)val5).gameObject); } Selectable component = val.GetComponent<Selectable>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } Slider component2 = val.GetComponent<Slider>(); if ((Object)(object)component2 != (Object)null) { Object.Destroy((Object)(object)component2); } Component[] components = val.GetComponents<Component>(); foreach (Component val6 in components) { if (!((Object)(object)val6 == (Object)null) && ((object)val6).GetType().Name == "GuiSlider") { Object.Destroy((Object)(object)val6); } } Transform obj = val.transform.Find("Label"); if ((Object)(object)obj == (Object)null) { throw new InvalidOperationException("Cloned slider row has no Label."); } TMP_Text component3 = ((Component)obj).GetComponent<TMP_Text>(); if ((Object)(object)component3 == (Object)null) { throw new InvalidOperationException("Cloned slider label has no TMP_Text."); } component3.text = "Speaker mode"; ((Graphic)component3).raycastTarget = false; LayoutElement val7 = val.GetComponent<LayoutElement>(); if ((Object)(object)val7 == (Object)null) { val7 = val.AddComponent<LayoutElement>(); } val7.minHeight = 46f; val7.preferredHeight = 46f; val7.flexibleHeight = 0f; ((Transform)val.GetComponent<RectTransform>()).localScale = Vector3.one; GameObject val8 = Object.Instantiate<GameObject>(dropdownTemplate.gameObject, val.transform, false); ((Object)val8).name = "SpeakerModeDropdown"; val8.SetActive(true); _dropdown = val8.GetComponent<TMP_Dropdown>(); if ((Object)(object)_dropdown == (Object)null) { throw new InvalidOperationException("Cloned native dropdown has no TMP_Dropdown."); } RectTransform component4 = val8.GetComponent<RectTransform>(); component4.anchorMin = new Vector2(0f, 0.5f); component4.anchorMax = new Vector2(0f, 0.5f); component4.pivot = new Vector2(0f, 0.5f); component4.SetSizeWithCurrentAnchors((Axis)0, 180f); component4.SetSizeWithCurrentAnchors((Axis)1, 46f); component4.anchoredPosition = Vector2.zero; ((Transform)component4).localScale = Vector3.one; LayoutElement val9 = val8.GetComponent<LayoutElement>(); if ((Object)(object)val9 == (Object)null) { val9 = val8.AddComponent<LayoutElement>(); } val9.ignoreLayout = true; _dropdown.ClearOptions(); _dropdown.AddOptions(new List<string> { "Mono", "Stereo", "Quad", "5.0 Surround", "5.1 Surround", "7.1 Surround", "Prologic" }); ((UnityEvent<int>)(object)_dropdown.onValueChanged).AddListener((UnityAction<int>)OnModeSelected); RefreshSelection(); val.transform.SetSiblingIndex(((Component)_lastVanillaControl).transform.GetSiblingIndex()); val.SetActive(true); LayoutRebuilder.ForceRebuildLayoutImmediate(((Component)layoutParent).GetComponent<RectTransform>()); } private void OnModeSelected(int index) { if (index >= 0 && index < ModeValues.Length && !((Object)(object)Plugin.Instance == (Object)null)) { Plugin.Instance.PreviewSpeakerMode(ModeValues[index]); } } internal void RefreshSelection() { if (!((Object)(object)_dropdown == (Object)null) && !((Object)(object)Plugin.Instance == (Object)null)) { _dropdown.SetValueWithoutNotify(IndexForMode(Plugin.Instance.PreviewMode)); _dropdown.RefreshShownValue(); } } internal void UpdateNavigation(Button backButton, Button okButton) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0092: 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: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_dropdown == (Object)null) && !((Object)(object)_lastVanillaControl == (Object)null)) { Navigation navigation = ((Selectable)_lastVanillaControl).navigation; ((Navigation)(ref navigation)).mode = (Mode)4; ((Navigation)(ref navigation)).selectOnDown = (Selectable)(object)_dropdown; ((Selectable)_lastVanillaControl).navigation = navigation; Navigation navigation2 = ((Selectable)_dropdown).navigation; ((Navigation)(ref navigation2)).mode = (Mode)4; ((Navigation)(ref navigation2)).selectOnUp = (Selectable)(object)_lastVanillaControl; if ((Object)(object)backButton != (Object)null) { ((Navigation)(ref navigation2)).selectOnDown = (Selectable)(object)backButton; } ((Selectable)_dropdown).navigation = navigation2; if ((Object)(object)backButton != (Object)null) { Navigation navigation3 = ((Selectable)backButton).navigation; ((Navigation)(ref navigation3)).mode = (Mode)4; ((Navigation)(ref navigation3)).selectOnUp = (Selectable)(object)_dropdown; ((Selectable)backButton).navigation = navigation3; } if ((Object)(object)okButton != (Object)null) { Navigation navigation4 = ((Selectable)okButton).navigation; ((Navigation)(ref navigation4)).mode = (Mode)4; ((Navigation)(ref navigation4)).selectOnUp = (Selectable)(object)_dropdown; ((Selectable)okButton).navigation = navigation4; } } } private static int IndexForMode(int mode) { for (int i = 0; i < ModeValues.Length; i++) { if (ModeValues[i] == mode) { return i; } } return 0; } } [BepInPlugin("com.patrickmollohan.valheimspeakermode", "Valheim Speaker Mode", "1.0.1")] public sealed class Plugin : BaseUnityPlugin { private sealed class MusicSnapshot { public object NamedMusic; public string MusicName; public int TimeSamples; public bool WasPlaying; } public const string PluginGuid = "com.patrickmollohan.valheimspeakermode"; public const string PluginName = "Valheim Speaker Mode"; public const string PluginVersion = "1.0.1"; private Harmony _harmony; private int _committedMode; private int _previewMode; private MusicSnapshot _musicSnapshot; private Coroutine _musicRestoreCoroutine; private static readonly BindingFlags PrivateInstance = BindingFlags.Instance | BindingFlags.NonPublic; private static readonly FieldInfo MusicManSourceField = typeof(MusicMan).GetField("m_musicSource", PrivateInstance); private static readonly FieldInfo MusicManCurrentField = typeof(MusicMan).GetField("m_currentMusic", PrivateInstance); private static readonly FieldInfo MusicManQueuedField = typeof(MusicMan).GetField("m_queuedMusic", PrivateInstance); private static readonly FieldInfo MusicManStopField = typeof(MusicMan).GetField("m_stopMusic", PrivateInstance); private static readonly FieldInfo NamedMusicNameField = typeof(NamedMusic).GetField("m_name"); private static readonly FieldInfo NamedMusicResumeField = typeof(NamedMusic).GetField("m_resume"); private static readonly FieldInfo NamedMusicLoopField = typeof(NamedMusic).GetField("m_loop"); private static readonly FieldInfo NamedMusicSavedPositionField = typeof(NamedMusic).GetField("m_savedPlaybackPos"); private static readonly FieldInfo NamedMusicLastPlayedField = typeof(NamedMusic).GetField("m_lastPlayedTime"); internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal ConfigEntry<int> SavedSpeakerMode { get; private set; } internal int PreviewMode => _previewMode; private void Awake() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; SavedSpeakerMode = ((BaseUnityPlugin)this).Config.Bind<int>("Audio", "SpeakerMode", 2, "Unity speaker mode."); _committedMode = SavedSpeakerMode.Value; _previewMode = _committedMode; _harmony = new Harmony("com.patrickmollohan.valheimspeakermode"); try { ApplySpeakerMode(_committedMode, "startup"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Could not apply the saved speaker mode: " + ex)); } _harmony.PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Valheim Speaker Mode 1.0.1 loaded."); } internal void PreviewSpeakerMode(int requestedMode) { _previewMode = requestedMode; ApplySpeakerMode(requestedMode, "preview"); } internal void CommitSpeakerMode() { _committedMode = _previewMode; SavedSpeakerMode.Value = _committedMode; ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Saved speaker mode: " + ModeName(_committedMode))); } internal void RevertSpeakerMode() { _previewMode = _committedMode; ApplySpeakerMode(_committedMode, "revert"); } private unsafe void ApplySpeakerMode(int requestedMode, string reason) { //IL_0035: 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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) if (!Enum.IsDefined(typeof(AudioSpeakerMode), requestedMode)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Ignoring invalid AudioSpeakerMode value: " + requestedMode)); return; } AudioSpeakerMode val = (AudioSpeakerMode)requestedMode; AudioConfiguration configuration = AudioSettings.GetConfiguration(); if (configuration.speakerMode == val) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Speaker mode already " + ((object)(*(AudioSpeakerMode*)(&val))/*cast due to .constrained prefix*/).ToString() + " (" + reason + ").")); return; } CaptureMusicState(); configuration.speakerMode = val; bool flag = AudioSettings.Reset(configuration); AudioSpeakerMode speakerMode = AudioSettings.GetConfiguration().speakerMode; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Speaker mode " + reason + ": requested " + ((object)(*(AudioSpeakerMode*)(&val))/*cast due to .constrained prefix*/).ToString() + ", actual " + ((object)(*(AudioSpeakerMode*)(&speakerMode))/*cast due to .constrained prefix*/).ToString() + ", Reset returned " + flag + ".")); if (!flag || speakerMode != val) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("The audio driver did not accept " + ((object)(*(AudioSpeakerMode*)(&val))/*cast due to .constrained prefix*/).ToString() + ". Check the operating system's speaker layout and audio device capabilities.")); } RestoreMusicStateAfterReset(); } private void CaptureMusicState() { _musicSnapshot = null; try { MusicMan instance = MusicMan.instance; if ((Object)(object)instance == (Object)null) { return; } object? obj = MusicManSourceField?.GetValue(instance); AudioSource val = (AudioSource)((obj is AudioSource) ? obj : null); object obj2 = MusicManCurrentField?.GetValue(instance); object obj3 = MusicManQueuedField?.GetValue(instance) ?? obj2; if (obj3 != null) { string text = NamedMusicNameField?.GetValue(obj3) as string; if (!string.IsNullOrEmpty(text)) { bool flag = (Object)(object)val != (Object)null && val.isPlaying; int timeSamples = (flag ? val.timeSamples : 0); _musicSnapshot = new MusicSnapshot { NamedMusic = obj3, MusicName = text, TimeSamples = timeSamples, WasPlaying = flag }; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Captured music before audio reset: " + text + ", playing=" + flag + ", samples=" + timeSamples)); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not capture music state before audio reset: " + ex)); } } private void RestoreMusicStateAfterReset() { if (_musicSnapshot != null && _musicSnapshot.WasPlaying) { if (_musicRestoreCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_musicRestoreCoroutine); } _musicRestoreCoroutine = ((MonoBehaviour)this).StartCoroutine(RestoreMusicAfterDelay()); } } private IEnumerator RestoreMusicAfterDelay() { yield return null; yield return null; try { MusicMan instance = MusicMan.instance; if ((Object)(object)instance == (Object)null || (Object)/*isinst with value type is only supported in some contexts*/ == (Object)null) { yield break; } object namedMusic = _musicSnapshot.NamedMusic; if (namedMusic != null) { MusicManQueuedField?.SetValue(instance, namedMusic); MusicManStopField?.SetValue(instance, false); bool flag = NamedMusicLoopField != null && (bool)NamedMusicLoopField.GetValue(namedMusic); bool flag2 = NamedMusicResumeField != null && (bool)NamedMusicResumeField.GetValue(namedMusic); if (flag && flag2) { NamedMusicSavedPositionField?.SetValue(namedMusic, Mathf.Max(0, _musicSnapshot.TimeSamples)); NamedMusicLastPlayedField?.SetValue(namedMusic, Time.time); } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Queued music restoration: " + _musicSnapshot.MusicName + ", resume=" + (flag && flag2) + ", samples=" + _musicSnapshot.TimeSamples)); _musicSnapshot = null; } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not restore music after audio reset: " + ex)); _musicSnapshot = null; } finally { _musicRestoreCoroutine = null; } } internal static string ModeName(int mode) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected I4, but got Unknown AudioSpeakerMode val = (AudioSpeakerMode)mode; return (val - 1) switch { 0 => "Mono", 1 => "Stereo", 2 => "Quad", 3 => "5.0 Surround", 4 => "5.1 Surround", 5 => "7.1 Surround", 6 => "Prologic", _ => "Unknown (" + mode + ")", }; } private void OnDestroy() { if (_musicRestoreCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_musicRestoreCoroutine); } if (_harmony != null) { _harmony.UnpatchSelf(); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } } }