Decompiled source of AudioOutput v1.0.1

BepInEx/plugins/AudioOutput.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("AudioOutput")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("\r\n      Change audio output devices live in-game.\r\n    ")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("AudioOutput")]
[assembly: AssemblyTitle("AudioOutput")]
[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.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]
	[Microsoft.CodeAnalysis.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]
	[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 MoreAudioSettings
{
	[BepInPlugin("cryingrgb.AudioOutput", "Audio Output", "1.0.1")]
	public class Plugin : BaseUnityPlugin
	{
		public static ConfigEntry<string> OutputDeviceConfig;

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

		private string currentDevice = string.Empty;

		private const string DefaultDevice = "Default System Device";

		private bool audioCmdletsInstalled;

		private void Awake()
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			audioCmdletsInstalled = IsAudioDeviceCmdletsInstalled();
			if (!audioCmdletsInstalled)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Audio switching will be disabled.");
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Scanning Windows playback devices...");
			List<string> windowsPlaybackDevices = GetWindowsPlaybackDevices();
			windowsPlaybackDevices.Insert(0, "Default System Device");
			DeviceMap["Default System Device"] = "__DEFAULT__";
			if (windowsPlaybackDevices.Count == 1)
			{
				windowsPlaybackDevices.Add("No Devices Found");
			}
			string text = windowsPlaybackDevices[0];
			OutputDeviceConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Audio", "Output Device", text, new ConfigDescription(audioCmdletsInstalled ? "Select the audio output device." : "AudioDeviceCmdlets missing. Run: Install-Module AudioDeviceCmdlets -Scope CurrentUser", (AcceptableValueBase)(object)new AcceptableValueList<string>(windowsPlaybackDevices.ToArray()), Array.Empty<object>()));
			currentDevice = OutputDeviceConfig.Value;
			OutputDeviceConfig.SettingChanged += delegate
			{
				if (!audioCmdletsInstalled)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Cannot switch audio device: AudioDeviceCmdlets is not installed.");
				}
				else
				{
					ApplyDeviceRouting(OutputDeviceConfig.Value);
				}
			};
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded with {windowsPlaybackDevices.Count - 1} playback devices.");
		}

		private void ApplyDeviceRouting(string friendlyName)
		{
			if (currentDevice == friendlyName)
			{
				return;
			}
			currentDevice = friendlyName;
			if (friendlyName == "Default System Device")
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Using Windows default audio device.");
				return;
			}
			if (!DeviceMap.TryGetValue(friendlyName, out string value))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Invalid device: " + friendlyName));
				return;
			}
			try
			{
				string text = "Import-Module AudioDeviceCmdlets; Set-AudioDevice -ID '" + value + "'";
				using Process process = new Process();
				process.StartInfo.FileName = "powershell.exe";
				process.StartInfo.Arguments = "-NoProfile -ExecutionPolicy Bypass -Command \"" + text + "\"";
				process.StartInfo.UseShellExecute = false;
				process.StartInfo.CreateNoWindow = true;
				process.StartInfo.RedirectStandardOutput = true;
				process.StartInfo.RedirectStandardError = true;
				process.Start();
				process.StandardOutput.ReadToEnd();
				string text2 = process.StandardError.ReadToEnd();
				process.WaitForExit();
				if (process.ExitCode == 0)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Switched audio device to: " + friendlyName));
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Failed switching to '" + friendlyName + "'\n" + text2));
				}
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Device switch failed:\n{arg}");
			}
		}

		private List<string> GetWindowsPlaybackDevices()
		{
			List<string> list = new List<string>();
			DeviceMap.Clear();
			try
			{
				using Process process = new Process();
				process.StartInfo.FileName = "powershell.exe";
				process.StartInfo.Arguments = "-NoProfile -ExecutionPolicy Bypass -Command \"Get-AudioDevice -List | Where-Object {$_.Type -eq 'Playback'} | Select-Object Name,ID\"";
				process.StartInfo.UseShellExecute = false;
				process.StartInfo.CreateNoWindow = true;
				process.StartInfo.RedirectStandardOutput = true;
				process.StartInfo.RedirectStandardError = true;
				process.Start();
				string text = process.StandardOutput.ReadToEnd();
				string text2 = process.StandardError.ReadToEnd();
				process.WaitForExit();
				if (!string.IsNullOrWhiteSpace(text2))
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("PowerShell Error:\n" + text2));
					return list;
				}
				string[] array = text.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
				for (int i = 0; i < array.Length; i++)
				{
					string text3 = array[i].Trim();
					if (text3.StartsWith("Name") || text3.StartsWith("---"))
					{
						continue;
					}
					int num = text3.IndexOf("{0.0.0.");
					if (num >= 0)
					{
						string text4 = text3.Substring(0, num).Trim();
						string value = text3.Substring(num).Trim();
						if (!string.IsNullOrWhiteSpace(text4) && !string.IsNullOrWhiteSpace(value) && !DeviceMap.ContainsKey(text4))
						{
							DeviceMap[text4] = value;
							list.Add(text4);
							((BaseUnityPlugin)this).Logger.LogInfo((object)("Detected device: " + text4));
						}
					}
				}
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to enumerate devices:\n{arg}");
			}
			return list;
		}

		private bool IsAudioDeviceCmdletsInstalled()
		{
			try
			{
				using Process process = Process.Start(new ProcessStartInfo
				{
					FileName = "powershell.exe",
					Arguments = "-NoProfile -ExecutionPolicy Bypass -Command \"Get-Module -ListAvailable AudioDeviceCmdlets\"",
					RedirectStandardOutput = true,
					RedirectStandardError = true,
					UseShellExecute = false,
					CreateNoWindow = true
				});
				string value = process.StandardOutput.ReadToEnd();
				process.StandardError.ReadToEnd();
				process.WaitForExit();
				if (string.IsNullOrWhiteSpace(value))
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"AudioDeviceCmdlets is NOT installed!");
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Audio Output requires AudioDeviceCmdlets to switch devices.");
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Run this command in PowerShell:");
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Install-Module AudioDeviceCmdlets -Scope CurrentUser");
					return false;
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)"AudioDeviceCmdlets detected.");
				return true;
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to check AudioDeviceCmdlets: {arg}");
				return false;
			}
		}
	}
}