Decompiled source of BingBongVoiceLineAPI v1.2.3

BingBongVoiceLineAPI.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BingBongVoiceLineAPI.Config;
using BingBongVoiceLineAPI.Helpers;
using BingBongVoiceLineAPI.Patches;
using HarmonyLib;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("CustomBingBongResponseFramework")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CustomBingBongResponseFramework")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("54d0c49f-026d-49fa-bb01-9b30425dc9b9")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace BingBongVoiceLineAPI
{
	[BepInPlugin("MrBytesized.PEAK.BingBongVoiceLineAPI", "Bing Bong Voice Line API", "1.0.0")]
	public class BingBongVoiceLineAPI : BaseUnityPlugin
	{
		private const string mod_guid = "MrBytesized.PEAK.BingBongVoiceLineAPI";

		private const string mod_name = "Bing Bong Voice Line API";

		private const string mod_version = "1.0.0";

		private readonly Harmony harmony = new Harmony("MrBytesized.PEAK.BingBongVoiceLineAPI");

		internal static ManualLogSource Log;

		private (ConfigEntry<bool> configEntry, Action enablePatch, Action disablePatch, string description)[] _patchArray;

		public static BingBongVoiceLineAPI Instance { get; private set; }

		private void Awake()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			if ((Object)(object)Instance == (Object)null)
			{
				Instance = this;
			}
			Configuration.Init(((BaseUnityPlugin)this).Config);
			Log = Logger.CreateLogSource("MrBytesized.PEAK.BingBongVoiceLineAPI");
			Log.LogInfo((object)"Bing Bong Voice Line API has been activated");
			if ((Object)(object)Object.FindFirstObjectByType<CustomSoundManager>() == (Object)null)
			{
				GameObject val = new GameObject("BingBongVoiceLineAPI_AudioManager");
				val.AddComponent<CustomSoundManager>();
				Object.DontDestroyOnLoad((Object)val);
			}
			_patchArray = new(ConfigEntry<bool>, Action, Action, string)[1] { (Configuration.EnableMod, delegate
			{
				harmony.PatchAll(typeof(GameObjectPatch));
				harmony.PatchAll(typeof(UnityObjectPatch));
			}, delegate
			{
				harmony.UnpatchSelf();
			}, "Bing Bong Voice Line API") };
			(ConfigEntry<bool>, Action, Action, string)[] patchArray = _patchArray;
			for (int i = 0; i < patchArray.Length; i++)
			{
				var (configEntry, enablePatch, disablePatch, description) = patchArray[i];
				UpdatePatchFromConfig(configEntry, enablePatch, disablePatch, description);
				configEntry.SettingChanged += delegate
				{
					UpdatePatchFromConfig(configEntry, enablePatch, disablePatch, description);
				};
			}
		}

		private void UpdatePatchFromConfig(ConfigEntry<bool> configEntry, Action enablePatch, Action disablePatch, string description)
		{
			if (configEntry.Value)
			{
				enablePatch();
				Log.LogInfo((object)(description + " patch enabled."));
			}
			else
			{
				disablePatch();
				Log.LogInfo((object)(description + " patch disabled."));
			}
		}
	}
}
namespace BingBongVoiceLineAPI.Patches
{
	internal static class BingBongVoiceLineReplacer
	{
		public static void TryHandleCreatedObject(Object created)
		{
			try
			{
				if (created == (Object)null)
				{
					BingBongVoiceLineAPI.Log.LogWarning((object)"[BingBongMouthPatch] TryHandleCreatedObject called with null created object.");
					return;
				}
				GameObject val = null;
				GameObject val2 = (GameObject)(object)((created is GameObject) ? created : null);
				if (val2 != null)
				{
					val = val2;
				}
				else
				{
					Component val3 = (Component)(object)((created is Component) ? created : null);
					if (val3 != null)
					{
						val = val3.gameObject;
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					BingBongVoiceLineAPI.Log.LogWarning((object)("[BingBongMouthPatch] Created object is not a GameObject or Component. Type=" + ((object)created).GetType().FullName));
					return;
				}
				BingBong val4 = val.GetComponent<BingBong>() ?? val.GetComponentInChildren<BingBong>(true);
				Action_AskBingBong val5 = null;
				if ((Object)(object)val4 != (Object)null)
				{
					BingBongVoiceLineAPI.Log.LogInfo((object)("[BingBongMouthPatch] Found BingBong component on '" + ((Object)val).name + "' (type " + ((object)val4).GetType().FullName + ")."));
					val5 = ((Component)val4).GetComponent<Action_AskBingBong>() ?? ((Component)val4).GetComponentInChildren<Action_AskBingBong>(true);
				}
				else
				{
					val5 = val.GetComponent<Action_AskBingBong>() ?? val.GetComponentInChildren<Action_AskBingBong>(true);
				}
				if ((Object)(object)val5 != (Object)null)
				{
					BingBongVoiceLineAPI.Log.LogInfo((object)("[BingBongMouthPatch] Applying custom responses to Action_AskBingBong on '" + ((Object)val).name + "' (instance type " + ((object)val5).GetType().FullName + ")."));
					ApplyCustomResponses(val5);
				}
				else
				{
					BingBongVoiceLineAPI.Log.LogDebug((object)("[BingBongMouthPatch] No Action_AskBingBong found on '" + ((Object)val).name + "'."));
				}
			}
			catch (Exception arg)
			{
				BingBongVoiceLineAPI.Log.LogError((object)$"[BingBongMouthPatch] TryHandleCreatedObject exception: {arg}");
			}
		}

		public static void ApplyCustomResponses(Action_AskBingBong latest)
		{
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Expected O, but got Unknown
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Expected O, but got Unknown
			if ((Object)(object)latest == (Object)null)
			{
				return;
			}
			CustomSoundManager instance = CustomSoundManager.Instance;
			if ((Object)(object)instance == (Object)null || !instance.IsLoaded)
			{
				BingBongVoiceLineAPI.Log.LogWarning((object)"CustomSoundManager not ready, skipping custom responses.");
				return;
			}
			List<BingBongResponseConfigEntry> list = ConfigLoader.LoadConfig("response_sound_pack.json");
			if (list == null || list.Count == 0)
			{
				BingBongVoiceLineAPI.Log.LogWarning((object)"No custom Bing Bong responses found in config.");
				return;
			}
			List<BingBongResponse> list2 = new List<BingBongResponse>();
			foreach (BingBongResponseConfigEntry item2 in list)
			{
				AudioClip clip = instance.GetClip(item2.file);
				if ((Object)(object)clip == (Object)null)
				{
					BingBongVoiceLineAPI.Log.LogError((object)("AudioClip not found for " + item2.file));
					continue;
				}
				SFX_Instance val = ScriptableObject.CreateInstance<SFX_Instance>();
				val.clips = (AudioClip[])(object)new AudioClip[1] { clip };
				string file = item2.file;
				string text = (string.IsNullOrEmpty(item2.subtitle) ? "" : item2.subtitle);
				LocalizationInjector.AddCustomSubtitle(file, text);
				BingBongResponse item = new BingBongResponse
				{
					subtitleID = file,
					sfx = val,
					mouthCurve = new AnimationCurve(),
					mouthCurveTime = 1f
				};
				list2.Add(item);
			}
			if (list2.Count > 0)
			{
				if (!Configuration.ReplaceBingBongResponses.Value)
				{
					BingBongResponse[] responses = latest.responses.Concat(list2).ToArray();
					latest.responses = responses;
					BingBongVoiceLineAPI.Log.LogInfo((object)$"Combined Bing Bong responses. Total responses: {latest.responses.Length}");
				}
				else
				{
					latest.responses = list2.ToArray();
					BingBongVoiceLineAPI.Log.LogInfo((object)"Replaced Bing Bong responses with custom responses.");
				}
			}
		}
	}
	[HarmonyPatch(typeof(GameObject))]
	internal static class GameObjectPatch
	{
		[HarmonyPatch("AddComponent", new Type[] { typeof(Type) })]
		internal static void Postfix(GameObject __instance, ref Component __result)
		{
			try
			{
				BingBongVoiceLineReplacer.TryHandleCreatedObject((Object)(((object)__result) ?? ((object)__instance)));
			}
			catch (Exception arg)
			{
				BingBongVoiceLineAPI.Log.LogError((object)$"[GameObjectPatch] AddComponent_Postfix exception: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(Object))]
	internal static class UnityObjectPatch
	{
		[HarmonyPatch(typeof(Object), "Instantiate", new Type[] { typeof(Object) })]
		private static void Postfix(Object __result)
		{
			try
			{
				BingBongVoiceLineReplacer.TryHandleCreatedObject(__result);
			}
			catch (Exception arg)
			{
				BingBongVoiceLineAPI.Log.LogError((object)$"[UnityObjectPatch] Instantiate_Postfix exception: {arg}");
			}
		}
	}
}
namespace BingBongVoiceLineAPI.Helpers
{
	public static class AudioLoader
	{
		[CompilerGenerated]
		private sealed class <>c__DisplayClass6_0
		{
			public StringBuilder stdOutBuilder;

			public StringBuilder stdErrBuilder;

			internal void <ConvertAndLoadAudio>b__0(object sender, DataReceivedEventArgs args)
			{
				if (args.Data != null)
				{
					stdOutBuilder.AppendLine(args.Data);
				}
			}

			internal void <ConvertAndLoadAudio>b__1(object sender, DataReceivedEventArgs args)
			{
				if (args.Data != null)
				{
					stdErrBuilder.AppendLine(args.Data);
				}
			}
		}

		[CompilerGenerated]
		private sealed class <ConvertAndLoadAudio>d__6 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public string sourceFilePath;

			public string ffmpegExePath;

			public Action<AudioClip> onLoaded;

			private <>c__DisplayClass6_0 <>8__1;

			private string <outputWavPath>5__2;

			private Process <process>5__3;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ConvertAndLoadAudio>d__6(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<outputWavPath>5__2 = null;
				<process>5__3 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				string text;
				string text2;
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
				{
					<>1__state = -1;
					string fileHash = GetFileHash(sourceFilePath);
					<outputWavPath>5__2 = Path.Combine(audioCachePath, fileHash + ".wav");
					if (!File.Exists(<outputWavPath>5__2))
					{
						<>8__1 = new <>c__DisplayClass6_0();
						BingBongVoiceLineAPI.Log.LogInfo((object)("Converting '" + sourceFilePath + "' to WAV..."));
						string arguments = "-loglevel error -i \"" + sourceFilePath + "\" -vn -acodec pcm_s16le -ar 44100 -ac 2 \"" + <outputWavPath>5__2 + "\"";
						ProcessStartInfo startInfo = new ProcessStartInfo
						{
							FileName = ffmpegExePath,
							Arguments = arguments,
							UseShellExecute = false,
							RedirectStandardOutput = true,
							RedirectStandardError = true,
							CreateNoWindow = true
						};
						<>8__1.stdOutBuilder = new StringBuilder();
						<>8__1.stdErrBuilder = new StringBuilder();
						<process>5__3 = new Process
						{
							StartInfo = startInfo
						};
						<process>5__3.OutputDataReceived += delegate(object sender, DataReceivedEventArgs args)
						{
							if (args.Data != null)
							{
								<>8__1.stdOutBuilder.AppendLine(args.Data);
							}
						};
						<process>5__3.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs args)
						{
							if (args.Data != null)
							{
								<>8__1.stdErrBuilder.AppendLine(args.Data);
							}
						};
						bool flag = false;
						try
						{
							<process>5__3.Start();
							flag = true;
							<process>5__3.BeginOutputReadLine();
							<process>5__3.BeginErrorReadLine();
						}
						catch (Exception ex)
						{
							BingBongVoiceLineAPI.Log.LogError((object)("Exception starting FFmpeg process: " + ex.Message));
							onLoaded?.Invoke(null);
							return false;
						}
						if (flag)
						{
							goto IL_01da;
						}
						goto IL_02f8;
					}
					BingBongVoiceLineAPI.Log.LogInfo((object)("Found cached WAV file: " + <outputWavPath>5__2));
					goto IL_0322;
				}
				case 1:
					<>1__state = -1;
					goto IL_01da;
				case 2:
					{
						<>1__state = -1;
						return false;
					}
					IL_02f8:
					<>8__1 = null;
					<process>5__3 = null;
					goto IL_0322;
					IL_0322:
					<>2__current = LoadNativeAudio(<outputWavPath>5__2, (AudioType)20, onLoaded);
					<>1__state = 2;
					return true;
					IL_01da:
					if (!<process>5__3.HasExited)
					{
						<>2__current = null;
						<>1__state = 1;
						return true;
					}
					text = <>8__1.stdOutBuilder.ToString();
					text2 = <>8__1.stdErrBuilder.ToString();
					if (<process>5__3.ExitCode != 0)
					{
						BingBongVoiceLineAPI.Log.LogError((object)$"FFmpeg conversion failed with exit code {<process>5__3.ExitCode}.");
						BingBongVoiceLineAPI.Log.LogError((object)("FFmpeg Error: " + text2));
						if (!string.IsNullOrWhiteSpace(text))
						{
							BingBongVoiceLineAPI.Log.LogError((object)("FFmpeg Output: " + text));
						}
						if (File.Exists(<outputWavPath>5__2))
						{
							File.Delete(<outputWavPath>5__2);
						}
						onLoaded?.Invoke(null);
						return false;
					}
					BingBongVoiceLineAPI.Log.LogInfo((object)("Successfully converted file. WAV saved at: " + <outputWavPath>5__2));
					if (!string.IsNullOrWhiteSpace(text))
					{
						BingBongVoiceLineAPI.Log.LogDebug((object)("FFmpeg Output: " + text));
					}
					if (!string.IsNullOrWhiteSpace(text2))
					{
						BingBongVoiceLineAPI.Log.LogWarning((object)("FFmpeg Warnings: " + text2));
					}
					goto IL_02f8;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <LoadAudioClipFromPath>d__3 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public string filePath;

			public Action<AudioClip> onLoaded;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <LoadAudioClipFromPath>d__3(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_008b: Unknown result type (might be due to invalid IL or missing references)
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
				{
					<>1__state = -1;
					if (!File.Exists(filePath))
					{
						BingBongVoiceLineAPI.Log.LogError((object)("Audio file not found at path: " + filePath));
						onLoaded?.Invoke(null);
						return false;
					}
					string text = Path.GetExtension(filePath).ToLowerInvariant();
					AudioType? nativeAudioType = GetNativeAudioType(text);
					if (nativeAudioType.HasValue)
					{
						<>2__current = LoadNativeAudio(filePath, nativeAudioType.Value, onLoaded);
						<>1__state = 1;
						return true;
					}
					if (IsConversionSupported(text))
					{
						string text2 = FindFFmpeg();
						if (string.IsNullOrEmpty(text2))
						{
							BingBongVoiceLineAPI.Log.LogError((object)("Cannot load '" + text + "' file. ffmpeg.exe was not found."));
							onLoaded?.Invoke(null);
							return false;
						}
						<>2__current = ConvertAndLoadAudio(filePath, text2, onLoaded);
						<>1__state = 2;
						return true;
					}
					BingBongVoiceLineAPI.Log.LogWarning((object)("Unknown audio extension '" + text + "'. Defaulting to WAV load attempt."));
					<>2__current = LoadNativeAudio(filePath, (AudioType)20, onLoaded);
					<>1__state = 3;
					return true;
				}
				case 1:
					<>1__state = -1;
					break;
				case 2:
					<>1__state = -1;
					break;
				case 3:
					<>1__state = -1;
					break;
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <LoadNativeAudio>d__4 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public string filePath;

			public AudioType audioType;

			public Action<AudioClip> onLoaded;

			private UnityWebRequest <www>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <LoadNativeAudio>d__4(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<www>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_004a: Unknown result type (might be due to invalid IL or missing references)
				//IL_008b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0091: Invalid comparison between Unknown and I4
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
					{
						<>1__state = -1;
						BingBongVoiceLineAPI.Log.LogInfo((object)("Loading native audio from: " + filePath));
						string text = "file://" + filePath;
						<www>5__2 = UnityWebRequestMultimedia.GetAudioClip(text, audioType);
						<>1__state = -3;
						<>2__current = <www>5__2.SendWebRequest();
						<>1__state = 1;
						return true;
					}
					case 1:
						<>1__state = -3;
						if ((int)<www>5__2.result == 1)
						{
							AudioClip content = DownloadHandlerAudioClip.GetContent(<www>5__2);
							onLoaded?.Invoke(content);
						}
						else
						{
							BingBongVoiceLineAPI.Log.LogError((object)("Failed to load audio: " + <www>5__2.error));
							onLoaded?.Invoke(null);
						}
						<>m__Finally1();
						<www>5__2 = null;
						return false;
					}
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<www>5__2 != null)
				{
					((IDisposable)<www>5__2).Dispose();
				}
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static string ffmpegPath = null;

		private static bool ffmpegSearched = false;

		private static string audioCachePath = Path.Combine(Paths.CachePath, "BingBongAudioCache");

		[IteratorStateMachine(typeof(<LoadAudioClipFromPath>d__3))]
		public static IEnumerator LoadAudioClipFromPath(string filePath, Action<AudioClip> onLoaded)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <LoadAudioClipFromPath>d__3(0)
			{
				filePath = filePath,
				onLoaded = onLoaded
			};
		}

		[IteratorStateMachine(typeof(<LoadNativeAudio>d__4))]
		private static IEnumerator LoadNativeAudio(string filePath, AudioType audioType, Action<AudioClip> onLoaded)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <LoadNativeAudio>d__4(0)
			{
				filePath = filePath,
				audioType = audioType,
				onLoaded = onLoaded
			};
		}

		private static string FindFFmpeg()
		{
			if (ffmpegSearched)
			{
				return ffmpegPath;
			}
			ffmpegSearched = true;
			try
			{
				string[] files = Directory.GetFiles(Paths.PluginPath, "ffmpeg.exe", SearchOption.AllDirectories);
				if (files.Length == 0)
				{
					BingBongVoiceLineAPI.Log.LogWarning((object)"ffmpeg.exe not found anywhere in BepInEx plugin path. Non-native audio formats (e.g., .m4a) will not be loaded.");
					return null;
				}
				ffmpegPath = files[0];
				BingBongVoiceLineAPI.Log.LogInfo((object)("Found ffmpeg at: " + ffmpegPath));
				if (files.Length > 1)
				{
					BingBongVoiceLineAPI.Log.LogWarning((object)$"Found {files.Length} instances of ffmpeg.exe. Using the first one found.");
				}
				Directory.CreateDirectory(audioCachePath);
				return ffmpegPath;
			}
			catch (Exception ex)
			{
				BingBongVoiceLineAPI.Log.LogError((object)("Error while searching for ffmpeg.exe: " + ex.Message));
				return null;
			}
		}

		[IteratorStateMachine(typeof(<ConvertAndLoadAudio>d__6))]
		private static IEnumerator ConvertAndLoadAudio(string sourceFilePath, string ffmpegExePath, Action<AudioClip> onLoaded)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ConvertAndLoadAudio>d__6(0)
			{
				sourceFilePath = sourceFilePath,
				ffmpegExePath = ffmpegExePath,
				onLoaded = onLoaded
			};
		}

		private static AudioType? GetNativeAudioType(string extension)
		{
			switch (extension.ToLowerInvariant())
			{
			case ".wav":
				return (AudioType)20;
			case ".mp3":
				return (AudioType)13;
			case ".ogg":
				return (AudioType)14;
			case ".aif":
			case ".aiff":
				return (AudioType)2;
			case ".xm":
				return (AudioType)21;
			case ".mod":
				return (AudioType)12;
			case ".it":
				return (AudioType)10;
			case ".s3m":
				return (AudioType)17;
			default:
				return null;
			}
		}

		private static bool IsConversionSupported(string extension)
		{
			if (extension.ToLowerInvariant() == ".m4a")
			{
				return true;
			}
			return false;
		}

		private static string GetFileHash(string filePath)
		{
			using SHA256 sHA = SHA256.Create();
			byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(filePath.ToLowerInvariant()));
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < array.Length; i++)
			{
				stringBuilder.Append(array[i].ToString("x2"));
			}
			return stringBuilder.ToString();
		}
	}
	public static class ConfigLoader
	{
		public static List<BingBongResponseConfigEntry> LoadConfig(string configFileName)
		{
			List<BingBongResponseConfigEntry> list = new List<BingBongResponseConfigEntry>();
			string[] files = Directory.GetFiles(Paths.PluginPath, configFileName, SearchOption.AllDirectories);
			if (files.Length == 0)
			{
				BingBongVoiceLineAPI.Log.LogWarning((object)("No config files named '" + configFileName + "' found in '" + Paths.PluginPath + "'."));
				return list;
			}
			string[] array = files;
			foreach (string text in array)
			{
				try
				{
					BingBongResponseConfigFile bingBongResponseConfigFile = JsonConvert.DeserializeObject<BingBongResponseConfigFile>(File.ReadAllText(text));
					if (bingBongResponseConfigFile?.entries != null)
					{
						string directoryName = Path.GetDirectoryName(text);
						foreach (BingBongResponseConfigEntry entry in bingBongResponseConfigFile.entries)
						{
							entry.configDirectory = directoryName;
							entry.modName = bingBongResponseConfigFile.name;
							list.Add(entry);
						}
						BingBongVoiceLineAPI.Log.LogInfo((object)$"Loaded config for mod '{bingBongResponseConfigFile.name}' from with {bingBongResponseConfigFile.entries.Count} entries.");
					}
					else
					{
						BingBongVoiceLineAPI.Log.LogWarning((object)("Config file '" + text + "' is missing entries or mod name."));
					}
				}
				catch (Exception ex)
				{
					BingBongVoiceLineAPI.Log.LogError((object)("Error loading config file '" + text + "': " + ex.Message));
				}
			}
			list.Sort(delegate(BingBongResponseConfigEntry a, BingBongResponseConfigEntry b)
			{
				int num = string.Compare(a.modName, b.modName, StringComparison.Ordinal);
				if (num != 0)
				{
					return num;
				}
				int num2 = string.Compare(a.file, b.file, StringComparison.Ordinal);
				return (num2 != 0) ? num2 : string.Compare(a.subtitle, b.subtitle, StringComparison.Ordinal);
			});
			return list;
		}
	}
	[Serializable]
	public class BingBongResponseConfigFile
	{
		public string name;

		public List<BingBongResponseConfigEntry> entries;
	}
	[Serializable]
	public class BingBongResponseConfigEntry
	{
		public string file;

		public string subtitle;

		[NonSerialized]
		public string configDirectory;

		[NonSerialized]
		public string modName;
	}
	public class CustomSoundManager : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <>c__DisplayClass11_0
		{
			public CustomSoundManager <>4__this;

			public int filesProcessed;
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass11_1
		{
			public BingBongResponseConfigEntry currentEntry;

			public <>c__DisplayClass11_0 CS$<>8__locals1;

			internal void <LoadAllClips>b__0(AudioClip clip)
			{
				try
				{
					if ((Object)(object)clip != (Object)null)
					{
						CS$<>8__locals1.<>4__this._audioClips[currentEntry.file] = clip;
						BingBongVoiceLineAPI.Log.LogInfo((object)("CustomSoundManager: Loaded " + currentEntry.file));
					}
					else
					{
						BingBongVoiceLineAPI.Log.LogError((object)("CustomSoundManager: Failed to load " + currentEntry.file));
					}
				}
				catch (Exception ex)
				{
					BingBongVoiceLineAPI.Log.LogError((object)("CustomSoundManager: Error in callback for " + currentEntry.file + ": " + ex.Message));
				}
				finally
				{
					int filesProcessed = CS$<>8__locals1.filesProcessed;
					CS$<>8__locals1.filesProcessed = filesProcessed + 1;
				}
			}
		}

		[CompilerGenerated]
		private sealed class <LoadAllClips>d__11 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public CustomSoundManager <>4__this;

			private <>c__DisplayClass11_0 <>8__1;

			private int <totalFilesToLoad>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <LoadAllClips>d__11(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				CustomSoundManager customSoundManager = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
				{
					<>1__state = -1;
					<>8__1 = new <>c__DisplayClass11_0();
					<>8__1.<>4__this = <>4__this;
					List<BingBongResponseConfigEntry> list = ConfigLoader.LoadConfig("response_sound_pack.json");
					if (list == null || list.Count == 0)
					{
						BingBongVoiceLineAPI.Log.LogWarning((object)"CustomSoundManager: No config entries found.");
						customSoundManager._isLoaded = true;
						return false;
					}
					<totalFilesToLoad>5__2 = list.Count;
					<>8__1.filesProcessed = 0;
					BingBongVoiceLineAPI.Log.LogInfo((object)$"CustomSoundManager: Starting to load {<totalFilesToLoad>5__2} audio clips...");
					if (<totalFilesToLoad>5__2 == 0)
					{
						customSoundManager._isLoaded = true;
						BingBongVoiceLineAPI.Log.LogInfo((object)"CustomSoundManager: No clips to load.");
						return false;
					}
					foreach (BingBongResponseConfigEntry item in list)
					{
						<>c__DisplayClass11_1 CS$<>8__locals0 = new <>c__DisplayClass11_1
						{
							CS$<>8__locals1 = <>8__1,
							currentEntry = item
						};
						string filePath = Path.Combine(CS$<>8__locals0.currentEntry.configDirectory, CS$<>8__locals0.currentEntry.file);
						((MonoBehaviour)customSoundManager).StartCoroutine(AudioLoader.LoadAudioClipFromPath(filePath, delegate(AudioClip clip)
						{
							try
							{
								if ((Object)(object)clip != (Object)null)
								{
									CS$<>8__locals0.CS$<>8__locals1.<>4__this._audioClips[CS$<>8__locals0.currentEntry.file] = clip;
									BingBongVoiceLineAPI.Log.LogInfo((object)("CustomSoundManager: Loaded " + CS$<>8__locals0.currentEntry.file));
								}
								else
								{
									BingBongVoiceLineAPI.Log.LogError((object)("CustomSoundManager: Failed to load " + CS$<>8__locals0.currentEntry.file));
								}
							}
							catch (Exception ex)
							{
								BingBongVoiceLineAPI.Log.LogError((object)("CustomSoundManager: Error in callback for " + CS$<>8__locals0.currentEntry.file + ": " + ex.Message));
							}
							finally
							{
								int filesProcessed = CS$<>8__locals0.CS$<>8__locals1.filesProcessed;
								CS$<>8__locals0.CS$<>8__locals1.filesProcessed = filesProcessed + 1;
							}
						}));
					}
					break;
				}
				case 1:
					<>1__state = -1;
					break;
				}
				if (<>8__1.filesProcessed < <totalFilesToLoad>5__2)
				{
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				customSoundManager._isLoaded = true;
				BingBongVoiceLineAPI.Log.LogInfo((object)$"CustomSoundManager: Finished loading. {customSoundManager._audioClips.Count} / {<totalFilesToLoad>5__2} clips loaded.");
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private Dictionary<string, AudioClip> _audioClips = new Dictionary<string, AudioClip>();

		private bool _isLoaded;

		public static CustomSoundManager Instance { get; private set; }

		public IReadOnlyDictionary<string, AudioClip> AudioClips => _audioClips;

		public bool IsLoaded => _isLoaded;

		private void Awake()
		{
			if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			Instance = this;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			((MonoBehaviour)this).StartCoroutine(LoadAllClips());
		}

		[IteratorStateMachine(typeof(<LoadAllClips>d__11))]
		private IEnumerator LoadAllClips()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <LoadAllClips>d__11(0)
			{
				<>4__this = this
			};
		}

		public AudioClip GetClip(string fileName)
		{
			_audioClips.TryGetValue(fileName, out var value);
			return value;
		}
	}
	public static class LocalizationInjector
	{
		public static void AddCustomSubtitle(string id, string text)
		{
			LocalizedText.TryInitTables();
			List<string> list = new List<string>();
			for (int i = 0; i < 14; i++)
			{
				list.Add(text);
			}
			LocalizedText.mainTable[id.ToUpperInvariant()] = list;
			BingBongVoiceLineAPI.Log.LogInfo((object)("Added custom subtitle '" + id + "' to localization table."));
		}
	}
}
namespace BingBongVoiceLineAPI.Config
{
	internal class Configuration
	{
		public static ConfigEntry<bool> EnableMod;

		public static ConfigEntry<bool> ReplaceBingBongResponses;

		public static void Init(ConfigFile config)
		{
			EnableMod = config.Bind<bool>("General", "Bing Bong Voice Line API", true, "If enabled, the Bing Bong Voice Line API mod will be active.");
			ReplaceBingBongResponses = config.Bind<bool>("General", "Replace Bing Bong Responses", true, "If enabled, custom responses will replace the default Bing Bong responses instead of being added to them.");
		}
	}
}