Decompiled source of CaptainAudio v1.3.0

CaptainAudio.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.Security;
using System.Security.Permissions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.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 CaptainAudio
{
	public static class AudioLoader
	{
		private class LoadTask
		{
			public string FolderName;

			public string ClipName;

			public byte[] AudioData;

			public bool IsCompleted;

			public AudioClip LoadedClip;
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass5_0
		{
			public string resourcePrefix;

			internal bool <LoadEmbeddedResourcesParallel>b__0(string name)
			{
				return name.StartsWith(resourcePrefix);
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass6_0
		{
			public AudioClip clip;

			public bool success;

			internal void <LoadSingleClipWithRetry>b__0(AudioClip loadedClip)
			{
				clip = loadedClip;
				success = (Object)(object)loadedClip != (Object)null;
			}
		}

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

			private object <>2__current;

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

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

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

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

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					if (_isInitialized)
					{
						return false;
					}
					Plugin.CustomMusic.Clear();
					Plugin.CustomAmbient.Clear();
					Plugin.CustomSFX.Clear();
					Plugin.CustomMusicList.Clear();
					Plugin.CustomAmbientList.Clear();
					Plugin.CustomSFXList.Clear();
					<>2__current = LoadEmbeddedResourcesParallel("CaptainAudio.asset.Resources.Music", Plugin.CustomMusic, Plugin.CustomMusicList);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					<>2__current = LoadEmbeddedResourcesParallel("CaptainAudio.asset.Resources.Ambient", Plugin.CustomAmbient, Plugin.CustomAmbientList);
					<>1__state = 2;
					return true;
				case 2:
					<>1__state = -1;
					<>2__current = LoadEmbeddedResourcesParallel("CaptainAudio.asset.Resources.SFX", Plugin.CustomSFX, Plugin.CustomSFXList);
					<>1__state = 3;
					return true;
				case 3:
					<>1__state = -1;
					LoadExternalAudioFiles();
					StringMatchCache.Initialize();
					_isInitialized = true;
					Plugin.Log.LogInfo((object)$"Loaded: {Plugin.CustomMusicList.Count} folders, {Plugin.CustomMusic.Count} clips");
					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 <LoadAudioClipFromBytes>d__7 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public byte[] audioData;

			public string clipName;

			public Action<AudioClip> onComplete;

			private string <fileExtension>5__1;

			private string <tempPath>5__2;

			private AudioType <audioType>5__3;

			private string <uri>5__4;

			private UnityWebRequest <www>5__5;

			private DownloadHandlerAudioClip <handler>5__6;

			private AudioClip <clip>5__7;

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

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

			[DebuggerHidden]
			public <LoadAudioClipFromBytes>d__7(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();
					}
				}
				<fileExtension>5__1 = null;
				<tempPath>5__2 = null;
				<uri>5__4 = null;
				<www>5__5 = null;
				<handler>5__6 = null;
				<clip>5__7 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
				//IL_010e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0118: Expected O, but got Unknown
				//IL_016d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0173: Invalid comparison between Unknown and I4
				//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
				//IL_01c8: Invalid comparison between Unknown and I4
				//IL_0284: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						<fileExtension>5__1 = DetectAudioFormat(audioData);
						<tempPath>5__2 = Path.Combine(Application.temporaryCachePath, $"temp_audio_{clipName}_{Guid.NewGuid()}{<fileExtension>5__1}");
						try
						{
							Directory.CreateDirectory(Path.GetDirectoryName(<tempPath>5__2));
							File.WriteAllBytes(<tempPath>5__2, audioData);
						}
						catch
						{
							onComplete?.Invoke(null);
							return false;
						}
						<audioType>5__3 = GetAudioType(<fileExtension>5__1);
						<uri>5__4 = "file:///" + <tempPath>5__2.Replace("\\", "/");
						<www>5__5 = UnityWebRequestMultimedia.GetAudioClip(<uri>5__4, <audioType>5__3);
						<>1__state = -3;
						<handler>5__6 = (DownloadHandlerAudioClip)<www>5__5.downloadHandler;
						<handler>5__6.streamAudio = false;
						<handler>5__6.compressed = false;
						<www>5__5.timeout = 15;
						<>2__current = <www>5__5.SendWebRequest();
						<>1__state = 1;
						return true;
					case 1:
						<>1__state = -3;
						if ((int)<www>5__5.result == 1)
						{
							<clip>5__7 = DownloadHandlerAudioClip.GetContent(<www>5__5);
							if ((Object)(object)<clip>5__7 != (Object)null && <clip>5__7.length > 0f && <clip>5__7.samples > 0 && (int)<clip>5__7.loadState == 2)
							{
								((Object)<clip>5__7).name = clipName;
								onComplete?.Invoke(<clip>5__7);
							}
							else
							{
								ManualLogSource log = Plugin.Log;
								object[] obj = new object[4] { clipName, null, null, null };
								AudioClip obj2 = <clip>5__7;
								obj[1] = ((obj2 != null) ? new float?(obj2.length) : null);
								AudioClip obj3 = <clip>5__7;
								obj[2] = ((obj3 != null) ? new int?(obj3.samples) : null);
								AudioClip obj4 = <clip>5__7;
								obj[3] = ((obj4 != null) ? new AudioDataLoadState?(obj4.loadState) : null);
								log.LogWarning((object)string.Format("Invalid clip: {0} (length={1}, samples={2}, state={3})", obj));
								onComplete?.Invoke(null);
							}
							<clip>5__7 = null;
						}
						else
						{
							Plugin.Log.LogWarning((object)("Failed to load: " + clipName + " - " + <www>5__5.error));
							onComplete?.Invoke(null);
						}
						<handler>5__6 = null;
						<>m__Finally1();
						<www>5__5 = null;
						if (File.Exists(<tempPath>5__2))
						{
							try
							{
								File.Delete(<tempPath>5__2);
							}
							catch
							{
							}
						}
						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__5 != null)
				{
					((IDisposable)<www>5__5).Dispose();
				}
			}

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

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

			private object <>2__current;

			public string resourcePrefix;

			public Dictionary<string, AudioClip> singleDict;

			public Dictionary<string, Dictionary<string, AudioClip>> folderDict;

			private <>c__DisplayClass5_0 <>8__1;

			private Assembly <assembly>5__2;

			private string[] <resourceNames>5__3;

			private List<LoadTask> <tasks>5__4;

			private string[] <>s__5;

			private int <>s__6;

			private string <resourceName>5__7;

			private Stream <stream>5__8;

			private byte[] <audioData>5__9;

			private string <fileName>5__10;

			private string <folderName>5__11;

			private string <clipName>5__12;

			private LoadTask <task>5__13;

			private List<LoadTask>.Enumerator <>s__14;

			private LoadTask <task>5__15;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<assembly>5__2 = null;
				<resourceNames>5__3 = null;
				<tasks>5__4 = null;
				<>s__5 = null;
				<resourceName>5__7 = null;
				<stream>5__8 = null;
				<audioData>5__9 = null;
				<fileName>5__10 = null;
				<folderName>5__11 = null;
				<clipName>5__12 = null;
				<task>5__13 = null;
				<>s__14 = default(List<LoadTask>.Enumerator);
				<task>5__15 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>8__1 = new <>c__DisplayClass5_0();
					<>8__1.resourcePrefix = resourcePrefix;
					<assembly>5__2 = Assembly.GetExecutingAssembly();
					<resourceNames>5__3 = (from name in <assembly>5__2.GetManifestResourceNames()
						where name.StartsWith(<>8__1.resourcePrefix)
						select name).ToArray();
					if (<resourceNames>5__3.Length == 0)
					{
						return false;
					}
					<tasks>5__4 = new List<LoadTask>();
					<>s__5 = <resourceNames>5__3;
					for (<>s__6 = 0; <>s__6 < <>s__5.Length; <>s__6++)
					{
						<resourceName>5__7 = <>s__5[<>s__6];
						<stream>5__8 = <assembly>5__2.GetManifestResourceStream(<resourceName>5__7);
						if (<stream>5__8 != null)
						{
							<audioData>5__9 = new byte[<stream>5__8.Length];
							<stream>5__8.Read(<audioData>5__9, 0, <audioData>5__9.Length);
							<stream>5__8.Dispose();
							<fileName>5__10 = <resourceName>5__7.Substring(<>8__1.resourcePrefix.Length + 1);
							(<folderName>5__11, <clipName>5__12) = ParseResourcePath(<fileName>5__10);
							if (!string.IsNullOrEmpty(<clipName>5__12))
							{
								<task>5__13 = new LoadTask
								{
									FolderName = <folderName>5__11,
									ClipName = <clipName>5__12,
									AudioData = <audioData>5__9,
									IsCompleted = false,
									LoadedClip = null
								};
								<tasks>5__4.Add(<task>5__13);
								((MonoBehaviour)Plugin.instance).StartCoroutine(LoadSingleClipWithRetry(<task>5__13));
								<stream>5__8 = null;
								<audioData>5__9 = null;
								<fileName>5__10 = null;
								<folderName>5__11 = null;
								<clipName>5__12 = null;
								<task>5__13 = null;
								<resourceName>5__7 = null;
							}
						}
					}
					<>s__5 = null;
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				if (<tasks>5__4.Any((LoadTask t) => !t.IsCompleted))
				{
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				<>s__14 = <tasks>5__4.GetEnumerator();
				try
				{
					while (<>s__14.MoveNext())
					{
						<task>5__15 = <>s__14.Current;
						if ((Object)(object)<task>5__15.LoadedClip == (Object)null)
						{
							continue;
						}
						if (!string.IsNullOrEmpty(<task>5__15.FolderName))
						{
							if (!folderDict.ContainsKey(<task>5__15.FolderName))
							{
								folderDict[<task>5__15.FolderName] = new Dictionary<string, AudioClip>();
							}
							folderDict[<task>5__15.FolderName][<task>5__15.ClipName] = <task>5__15.LoadedClip;
						}
						else
						{
							singleDict[<task>5__15.ClipName] = <task>5__15.LoadedClip;
						}
						<task>5__15 = null;
					}
				}
				finally
				{
					((IDisposable)<>s__14).Dispose();
				}
				<>s__14 = default(List<LoadTask>.Enumerator);
				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 <LoadSingleClipWithRetry>d__6 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public LoadTask task;

			private int <retryCount>5__1;

			private int <attempt>5__2;

			private <>c__DisplayClass6_0 <>8__3;

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

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

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

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

			private bool MoveNext()
			{
				//IL_0102: Unknown result type (might be due to invalid IL or missing references)
				//IL_010c: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<retryCount>5__1 = Plugin.LoadRetryCount.Value;
					<attempt>5__2 = 0;
					break;
				case 1:
					<>1__state = -1;
					if (<>8__3.success)
					{
						task.LoadedClip = <>8__3.clip;
						task.IsCompleted = true;
						return false;
					}
					if (<attempt>5__2 < <retryCount>5__1 - 1)
					{
						<>2__current = (object)new WaitForSeconds(0.5f);
						<>1__state = 2;
						return true;
					}
					goto IL_011d;
				case 2:
					{
						<>1__state = -1;
						goto IL_011d;
					}
					IL_011d:
					<>8__3 = null;
					<attempt>5__2++;
					break;
				}
				if (<attempt>5__2 < <retryCount>5__1)
				{
					<>8__3 = new <>c__DisplayClass6_0();
					<>8__3.success = false;
					<>8__3.clip = null;
					<>2__current = LoadAudioClipFromBytes(task.AudioData, task.ClipName, delegate(AudioClip loadedClip)
					{
						<>8__3.clip = loadedClip;
						<>8__3.success = (Object)(object)loadedClip != (Object)null;
					});
					<>1__state = 1;
					return true;
				}
				task.IsCompleted = true;
				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 static bool _isInitialized = false;

		private const int MAX_RETRY_COUNT = 3;

		private const float RETRY_DELAY = 0.5f;

		private static HashSet<string> _externalOverrideFolders = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		[IteratorStateMachine(typeof(<InitializeAsync>d__4))]
		public static IEnumerator InitializeAsync()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <InitializeAsync>d__4(0);
		}

		[IteratorStateMachine(typeof(<LoadEmbeddedResourcesParallel>d__5))]
		private static IEnumerator LoadEmbeddedResourcesParallel(string resourcePrefix, Dictionary<string, AudioClip> singleDict, Dictionary<string, Dictionary<string, AudioClip>> folderDict)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <LoadEmbeddedResourcesParallel>d__5(0)
			{
				resourcePrefix = resourcePrefix,
				singleDict = singleDict,
				folderDict = folderDict
			};
		}

		[IteratorStateMachine(typeof(<LoadSingleClipWithRetry>d__6))]
		private static IEnumerator LoadSingleClipWithRetry(LoadTask task)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <LoadSingleClipWithRetry>d__6(0)
			{
				task = task
			};
		}

		[IteratorStateMachine(typeof(<LoadAudioClipFromBytes>d__7))]
		private static IEnumerator LoadAudioClipFromBytes(byte[] audioData, string clipName, Action<AudioClip> onComplete)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <LoadAudioClipFromBytes>d__7(0)
			{
				audioData = audioData,
				clipName = clipName,
				onComplete = onComplete
			};
		}

		private static void LoadExternalAudioFiles()
		{
			string path = Path.Combine(Paths.PluginPath, "korCaptain-CaptainAudio", "music");
			if (Directory.Exists(path))
			{
				_externalOverrideFolders.Clear();
				string[] directories = Directory.GetDirectories(path);
				foreach (string path2 in directories)
				{
					string fileName = Path.GetFileName(path2);
					if (Directory.GetFiles(path2).Any((string f) => IsValidAudioFile(f)))
					{
						_externalOverrideFolders.Add("Music:" + fileName);
						Plugin.Log.LogInfo((object)("[Override] Music/" + fileName + ": 외부 음악 감지됨 → 내장 음악 대체"));
					}
				}
				ApplyExternalOverrides();
				CollectAudioFiles(path, Plugin.CustomMusic, Plugin.CustomMusicList);
				return;
			}
			string text = Path.Combine(Paths.PluginPath, "CaptainAudio");
			if (Directory.Exists(text))
			{
				_externalOverrideFolders.Clear();
				ScanExternalFolders(text);
				ApplyExternalOverrides();
				if (Directory.Exists(Path.Combine(text, "Music")))
				{
					CollectAudioFiles(Path.Combine(text, "Music"), Plugin.CustomMusic, Plugin.CustomMusicList);
				}
				if (Directory.Exists(Path.Combine(text, "SFX")))
				{
					CollectAudioFiles(Path.Combine(text, "SFX"), Plugin.CustomSFX, Plugin.CustomSFXList);
				}
				if (Directory.Exists(Path.Combine(text, "Ambient")))
				{
					CollectAudioFiles(Path.Combine(text, "Ambient"), Plugin.CustomAmbient, Plugin.CustomAmbientList);
				}
			}
		}

		private static void ScanExternalFolders(string externalPath)
		{
			string[] array = new string[3] { "Music", "Ambient", "SFX" };
			string[] array2 = array;
			foreach (string text in array2)
			{
				string path = Path.Combine(externalPath, text);
				if (!Directory.Exists(path))
				{
					continue;
				}
				string[] directories = Directory.GetDirectories(path);
				foreach (string path2 in directories)
				{
					string fileName = Path.GetFileName(path2);
					if (Directory.GetFiles(path2).Any((string file) => IsValidAudioFile(file)))
					{
						string item = text + ":" + fileName;
						_externalOverrideFolders.Add(item);
						Plugin.Log.LogInfo((object)("[Override] " + text + "/" + fileName + ": 외부 음악 감지됨 → 내장 음악 대체"));
					}
				}
			}
		}

		private static bool IsValidAudioFile(string filePath)
		{
			if (string.IsNullOrEmpty(filePath))
			{
				return false;
			}
			string text = Path.GetExtension(filePath).ToLower();
			return text == ".ogg" || text == ".wav" || text == ".mp3";
		}

		private static void ApplyExternalOverrides()
		{
			foreach (string externalOverrideFolder in _externalOverrideFolders)
			{
				string[] array = externalOverrideFolder.Split(':');
				if (array.Length != 2)
				{
					continue;
				}
				string text = array[0];
				string folderName = array[1];
				Dictionary<string, Dictionary<string, AudioClip>> dictionary = null;
				switch (text)
				{
				case "Music":
					dictionary = Plugin.CustomMusicList;
					break;
				case "Ambient":
					dictionary = Plugin.CustomAmbientList;
					break;
				case "SFX":
					dictionary = Plugin.CustomSFXList;
					break;
				}
				if (dictionary != null)
				{
					string text2 = dictionary.Keys.FirstOrDefault((string k) => string.Equals(k, folderName, StringComparison.OrdinalIgnoreCase));
					if (text2 != null)
					{
						int count = dictionary[text2].Count;
						dictionary[text2].Clear();
						Plugin.Log.LogInfo((object)$"[Override] {text}/{folderName}: 내장 음악 {count}개 제거됨");
					}
				}
			}
		}

		private static void CollectAudioFiles(string path, Dictionary<string, AudioClip> singleDict, Dictionary<string, Dictionary<string, AudioClip>> folderDict)
		{
			string[] files = Directory.GetFiles(path);
			foreach (string path2 in files)
			{
				LoadExternalClip(path2, singleDict);
			}
			string[] directories = Directory.GetDirectories(path);
			foreach (string path3 in directories)
			{
				string fileName = Path.GetFileName(path3);
				folderDict[fileName] = new Dictionary<string, AudioClip>();
				string[] files2 = Directory.GetFiles(path3);
				foreach (string path4 in files2)
				{
					LoadExternalClip(path4, folderDict[fileName]);
				}
			}
		}

		private static void LoadExternalClip(string path, Dictionary<string, AudioClip> dict)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Invalid comparison between Unknown and I4
			if (path.EndsWith(".txt") || !path.Contains("."))
			{
				return;
			}
			string text = Path.GetExtension(path).ToLower();
			if (text != ".ogg" && text != ".wav" && text != ".mp3")
			{
				return;
			}
			string text2 = "file:///" + path.Replace("\\", "/");
			AudioType audioType = GetAudioType(text);
			UnityWebRequest audioClip = UnityWebRequestMultimedia.GetAudioClip(text2, audioType);
			DownloadHandlerAudioClip val = (DownloadHandlerAudioClip)audioClip.downloadHandler;
			val.streamAudio = false;
			val.compressed = false;
			audioClip.SendWebRequest();
			float num = 10f;
			float num2 = 0f;
			while (!audioClip.isDone && num2 < num)
			{
				Thread.Sleep(10);
				num2 += 0.01f;
			}
			if ((int)audioClip.result == 1)
			{
				AudioClip val2 = ((val != null) ? val.audioClip : null);
				if ((Object)(object)val2 != (Object)null && val2.length > 0f && val2.samples > 0)
				{
					string key = (((Object)val2).name = Path.GetFileNameWithoutExtension(path));
					dict[key] = val2;
				}
				else
				{
					Plugin.Log.LogWarning((object)("Invalid external clip: " + path));
				}
			}
			else
			{
				Plugin.Log.LogWarning((object)("Failed to load external: " + path + " - " + audioClip.error));
			}
			audioClip.Dispose();
		}

		private static (string folderName, string clipName) ParseResourcePath(string fileName)
		{
			string item = null;
			string item2 = null;
			if (fileName.Contains("\\"))
			{
				string[] array = fileName.Split('\\');
				if (array.Length >= 2)
				{
					item = array[0];
					item2 = Path.GetFileNameWithoutExtension(array[^1]);
				}
			}
			else
			{
				string[] array2 = fileName.Split('.');
				if (array2.Length >= 3)
				{
					item = array2[0];
					item2 = string.Join(".", array2.Skip(1).Take(array2.Length - 2));
				}
				else if (array2.Length >= 2)
				{
					item = null;
					item2 = array2[0];
				}
			}
			return (item, item2);
		}

		private static string DetectAudioFormat(byte[] audioData)
		{
			if (audioData.Length < 4)
			{
				return ".ogg";
			}
			if (audioData[0] == 82 && audioData[1] == 73 && audioData[2] == 70 && audioData[3] == 70)
			{
				return ".wav";
			}
			if ((audioData[0] == 73 && audioData[1] == 68 && audioData[2] == 51) || (audioData[0] == byte.MaxValue && (audioData[1] & 0xE0) == 224))
			{
				return ".mp3";
			}
			return ".ogg";
		}

		private static AudioType GetAudioType(string extension)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_002d: Unknown result type (might be due to invalid IL or missing references)
			if (!(extension == ".wav"))
			{
				if (extension == ".mp3")
				{
					return (AudioType)13;
				}
				return (AudioType)14;
			}
			return (AudioType)20;
		}
	}
	[HarmonyPatch(typeof(MusicMan), "Awake")]
	public static class MusicMan_Awake_Patch
	{
		private static void Postfix(MusicMan __instance)
		{
			if (!Plugin.ModEnabled.Value)
			{
				return;
			}
			for (int i = 0; i < __instance.m_music.Count; i++)
			{
				NamedMusic instance = __instance.m_music[i];
				string value = ReflectionCache.GetValue<string>(instance, "m_name");
				if (Plugin.CustomMusicList.ContainsKey(value))
				{
					AudioClip[] value2 = Plugin.CustomMusicList[value].Values.ToArray();
					ReflectionCache.SetValue(instance, "m_clips", value2);
					continue;
				}
				string text = StringMatchCache.FindMatch(value);
				if (text != null && Plugin.CustomMusicList.ContainsKey(text))
				{
					AudioClip[] value3 = Plugin.CustomMusicList[text].Values.ToArray();
					ReflectionCache.SetValue(instance, "m_clips", value3);
					continue;
				}
				AudioClip[] value4 = ReflectionCache.GetValue<AudioClip[]>(instance, "m_clips");
				if (value4 == null)
				{
					continue;
				}
				for (int j = 0; j < value4.Length; j++)
				{
					if ((Object)(object)value4[j] != (Object)null && Plugin.CustomMusic.ContainsKey(((Object)value4[j]).name))
					{
						value4[j] = Plugin.CustomMusic[((Object)value4[j]).name];
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(MusicMan), "UpdateMusic")]
	public static class MusicMan_UpdateMusic_Patch
	{
		private static void Prefix(ref object ___m_queuedMusic, AudioSource ___m_musicSource)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Invalid comparison between Unknown and I4
			if (!Plugin.ModEnabled.Value)
			{
				return;
			}
			if (___m_queuedMusic != null)
			{
				ReflectionCache.SetValue(___m_queuedMusic, "m_volume", Plugin.MusicVolume.Value);
			}
			if (!((Object)(object)___m_musicSource != (Object)null) || !___m_musicSource.loop)
			{
				return;
			}
			if (!___m_musicSource.isPlaying)
			{
				___m_musicSource.loop = false;
			}
			else if ((Object)(object)___m_musicSource.clip != (Object)null && (int)___m_musicSource.clip.loadState == 2)
			{
				float num = ___m_musicSource.clip.length - ___m_musicSource.time;
				if (num < 0.5f)
				{
					___m_musicSource.loop = false;
				}
			}
		}
	}
	[HarmonyPatch(typeof(MusicLocation), "Awake")]
	public static class MusicLocation_Awake_Patch
	{
		private static void Postfix(ref AudioSource ___m_audioSource, ref float ___m_baseVolume)
		{
			if (!Plugin.ModEnabled.Value || (Object)(object)___m_audioSource == (Object)null || (Object)(object)___m_audioSource.clip == (Object)null)
			{
				return;
			}
			string name = ((Object)___m_audioSource.clip).name;
			if (!Plugin.CustomMusic.ContainsKey(name))
			{
				return;
			}
			AudioClip val = Plugin.CustomMusic[name];
			if (!((Object)(object)val == (Object)null) && !(val.length <= 0f))
			{
				bool isPlaying = ___m_audioSource.isPlaying;
				if (isPlaying)
				{
					___m_audioSource.Stop();
				}
				___m_audioSource.clip = val;
				___m_audioSource.time = 0f;
				___m_baseVolume *= Plugin.LocationVolMultiplier.Value;
				if (isPlaying)
				{
					___m_audioSource.Play();
				}
			}
		}
	}
	[HarmonyPatch(typeof(AudioMan), "Awake")]
	public static class AudioMan_Awake_Patch
	{
		private static void Postfix(AudioMan __instance, IList ___m_randomAmbients, AudioSource ___m_oceanAmbientSource, AudioSource ___m_windLoopSource)
		{
			if (Plugin.ModEnabled.Value)
			{
				for (int i = 0; i < ___m_randomAmbients.Count; i++)
				{
					object obj = ___m_randomAmbients[i];
					string value = ReflectionCache.GetValue<string>(obj, "m_name");
					ReplaceAmbientClips(obj, "m_randomAmbientClips");
					ReplaceAmbientClips(obj, "m_randomAmbientClipsDay");
					ReplaceAmbientClips(obj, "m_randomAmbientClipsNight");
					ReplaceAmbientList(obj, value, "_day", "m_randomAmbientClipsDay");
					ReplaceAmbientList(obj, value, "_night", "m_randomAmbientClipsNight");
					ReplaceAmbientList(obj, value, "", "m_randomAmbientClips");
				}
				if (Plugin.CustomAmbient.ContainsKey("ocean"))
				{
					___m_oceanAmbientSource.clip = Plugin.CustomAmbient["ocean"];
				}
				if (Plugin.CustomAmbient.ContainsKey("wind"))
				{
					___m_windLoopSource.clip = Plugin.CustomAmbient["wind"];
				}
			}
		}

		private static void ReplaceAmbientClips(object ambient, string fieldName)
		{
			IList value = ReflectionCache.GetValue<IList>(ambient, fieldName);
			if (value == null)
			{
				return;
			}
			for (int i = 0; i < value.Count; i++)
			{
				object? obj = value[i];
				AudioClip val = (AudioClip)((obj is AudioClip) ? obj : null);
				if ((Object)(object)val != (Object)null && Plugin.CustomAmbient.ContainsKey(((Object)val).name))
				{
					value[i] = Plugin.CustomAmbient[((Object)val).name];
				}
			}
		}

		private static void ReplaceAmbientList(object ambient, string ambientName, string suffix, string fieldName)
		{
			string key = ambientName + suffix;
			if (!Plugin.CustomAmbientList.ContainsKey(key))
			{
				return;
			}
			IList value = ReflectionCache.GetValue<IList>(ambient, fieldName);
			if (value == null)
			{
				return;
			}
			List<AudioClip> list = Plugin.CustomAmbientList[key].Values.ToList();
			value.Clear();
			foreach (AudioClip item in list)
			{
				value.Add(item);
			}
		}
	}
	[HarmonyPatch(typeof(AudioMan), "QueueAmbientLoop")]
	public static class AudioMan_QueueAmbientLoop_Patch
	{
		private static void Prefix(ref float ___m_queuedAmbientVol, ref float ___m_ambientVol, ref float vol)
		{
			if (Plugin.ModEnabled.Value)
			{
				vol = Plugin.AmbientVolume.Value;
				___m_ambientVol = Plugin.AmbientVolume.Value;
				___m_queuedAmbientVol = Plugin.AmbientVolume.Value;
			}
		}
	}
	[HarmonyPatch(typeof(ZSFX), "Awake")]
	public static class ZSFX_Awake_Patch
	{
		private static void Postfix(ZSFX __instance)
		{
			if (!Plugin.ModEnabled.Value)
			{
				return;
			}
			string zSFXName = AudioUtils.GetZSFXName(__instance);
			if (Plugin.CustomSFXList.TryGetValue(zSFXName, out var value))
			{
				__instance.m_audioClips = (from x in value
					orderby x.Key
					select x.Value).ToArray();
			}
			else
			{
				if (__instance.m_audioClips == null)
				{
					return;
				}
				for (int i = 0; i < __instance.m_audioClips.Length; i++)
				{
					if ((Object)(object)__instance.m_audioClips[i] != (Object)null && Plugin.CustomSFX.ContainsKey(((Object)__instance.m_audioClips[i]).name))
					{
						__instance.m_audioClips[i] = Plugin.CustomSFX[((Object)__instance.m_audioClips[i]).name];
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(TeleportWorld), "Awake")]
	public static class TeleportWorld_Awake_Patch
	{
		private static void Postfix(TeleportWorld __instance)
		{
			if (!Plugin.ModEnabled.Value || !Plugin.CustomSFX.ContainsKey("portal"))
			{
				return;
			}
			AudioSource componentInChildren = ((Component)__instance).GetComponentInChildren<AudioSource>();
			if (!((Object)(object)componentInChildren != (Object)null))
			{
				return;
			}
			AudioClip val = Plugin.CustomSFX["portal"];
			if (!((Object)(object)val == (Object)null) && !(val.length <= 0f))
			{
				bool isPlaying = componentInChildren.isPlaying;
				componentInChildren.Stop();
				componentInChildren.clip = val;
				componentInChildren.time = 0f;
				if (isPlaying || componentInChildren.playOnAwake)
				{
					componentInChildren.Play();
				}
			}
		}
	}
	[HarmonyPatch(typeof(Fireplace), "Start")]
	public static class Fireplace_Start_Patch
	{
		private static void Postfix(Fireplace __instance)
		{
			if (!Plugin.ModEnabled.Value)
			{
				return;
			}
			string name = ((Object)__instance).name;
			AudioSource val = null;
			AudioClip val2 = null;
			if (name.Contains("groundtorch") && Plugin.CustomSFX.ContainsKey("groundtorch"))
			{
				GameObject enabledObject = __instance.m_enabledObject;
				val = ((enabledObject != null) ? enabledObject.GetComponentInChildren<AudioSource>() : null);
				val2 = Plugin.CustomSFX["groundtorch"];
			}
			else if (name.Contains("walltorch") && Plugin.CustomSFX.ContainsKey("walltorch"))
			{
				GameObject enabledObjectHigh = __instance.m_enabledObjectHigh;
				val = ((enabledObjectHigh != null) ? enabledObjectHigh.GetComponentInChildren<AudioSource>() : null);
				if ((Object)(object)val == (Object)null)
				{
					GameObject enabledObject2 = __instance.m_enabledObject;
					val = ((enabledObject2 != null) ? enabledObject2.GetComponentInChildren<AudioSource>() : null);
				}
				val2 = Plugin.CustomSFX["walltorch"];
			}
			else if (name.Contains("fire_pit") && Plugin.CustomSFX.ContainsKey("fire_pit"))
			{
				GameObject enabledObjectHigh2 = __instance.m_enabledObjectHigh;
				val = ((enabledObjectHigh2 != null) ? enabledObjectHigh2.GetComponentInChildren<AudioSource>() : null);
				val2 = Plugin.CustomSFX["fire_pit"];
			}
			else if (name.Contains("bonfire") && Plugin.CustomSFX.ContainsKey("bonfire"))
			{
				GameObject enabledObjectHigh3 = __instance.m_enabledObjectHigh;
				val = ((enabledObjectHigh3 != null) ? enabledObjectHigh3.GetComponentInChildren<AudioSource>() : null);
				val2 = Plugin.CustomSFX["bonfire"];
			}
			else if (name.Contains("hearth") && Plugin.CustomSFX.ContainsKey("hearth"))
			{
				GameObject enabledObjectHigh4 = __instance.m_enabledObjectHigh;
				val = ((enabledObjectHigh4 != null) ? enabledObjectHigh4.GetComponentInChildren<AudioSource>() : null);
				val2 = Plugin.CustomSFX["hearth"];
			}
			if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null && val2.length > 0f)
			{
				SafeReplaceClip(val, val2);
			}
		}

		private static void SafeReplaceClip(AudioSource source, AudioClip newClip)
		{
			bool isPlaying = source.isPlaying;
			source.Stop();
			source.clip = newClip;
			source.time = 0f;
			if (isPlaying || source.playOnAwake)
			{
				source.Play();
			}
		}
	}
	[HarmonyPatch(typeof(EnvMan), "Awake")]
	public static class EnvMan_Awake_Patch
	{
		private static void Postfix(EnvMan __instance)
		{
			if (!Plugin.ModEnabled.Value)
			{
				return;
			}
			for (int i = 0; i < __instance.m_environments.Count; i++)
			{
				string name = __instance.m_environments[i].m_name;
				foreach (string key in Plugin.CustomMusicList.Keys)
				{
					string text = name.ToLower();
					string text2 = key.ToLower();
					if (text == text2 || text.Contains(text2) || text2.Contains(text))
					{
						string name2 = __instance.m_environments[i].m_name;
						__instance.m_environments[i].m_name = key;
						AddMusicToEnvironment(__instance, i, "");
						__instance.m_environments[i].m_name = name2;
						break;
					}
				}
				AddMusicToEnvironment(__instance, i, "Morning");
				AddMusicToEnvironment(__instance, i, "Day");
				AddMusicToEnvironment(__instance, i, "Evening");
				AddMusicToEnvironment(__instance, i, "Night");
			}
		}

		private static void AddMusicToEnvironment(EnvMan envMan, int index, string timeOfDay)
		{
			string text = envMan.m_environments[index].m_name + timeOfDay;
			if (Plugin.CustomMusicList.ContainsKey(text))
			{
				switch (timeOfDay)
				{
				case "Morning":
					envMan.m_environments[index].m_musicMorning = text;
					break;
				case "Day":
					envMan.m_environments[index].m_musicDay = text;
					break;
				case "Evening":
					envMan.m_environments[index].m_musicEvening = text;
					break;
				case "Night":
					envMan.m_environments[index].m_musicNight = text;
					break;
				}
				Type type = ((object)MusicMan.instance.m_music[0]).GetType();
				object obj = Activator.CreateInstance(type);
				ReflectionCache.SetValue(obj, "m_name", text);
				ReflectionCache.SetValue(obj, "m_clips", (from x in Plugin.CustomMusicList[text]
					orderby x.Key
					select x.Value).ToArray());
				ReflectionCache.SetValue(obj, "m_loop", true);
				ReflectionCache.SetValue(obj, "m_ambientMusic", true);
				ReflectionCache.SetValue(obj, "m_resume", true);
				((IList)MusicMan.instance.m_music).Add(obj);
			}
		}
	}
	[HarmonyPatch(typeof(Terminal), "InputText")]
	public static class Terminal_InputText_Patch
	{
		private static bool Prefix(Terminal __instance)
		{
			if (!Plugin.ModEnabled.Value)
			{
				return true;
			}
			FieldInfo field = ((object)__instance).GetType().GetField("m_input");
			if (field == null)
			{
				return true;
			}
			object value = field.GetValue(__instance);
			if (value == null)
			{
				return true;
			}
			PropertyInfo property = value.GetType().GetProperty("text");
			if (property == null)
			{
				return true;
			}
			string text = (string)property.GetValue(value);
			switch (text.ToLower())
			{
			case "captainaudio reset":
				((BaseUnityPlugin)Plugin.instance).Config.Reload();
				((BaseUnityPlugin)Plugin.instance).Config.Save();
				AddOutput(__instance, text);
				AddOutput(__instance, "Captain Audio config reloaded");
				return false;
			case "captainaudio music":
				AddOutput(__instance, text);
				if ((Object)(object)EnvMan.instance != (Object)null)
				{
					Player localPlayer = Player.m_localPlayer;
					string arg = ((localPlayer != null && localPlayer.IsSafeInHome()) ? "home" : EnvMan.instance.GetAmbientMusic());
					AddOutput(__instance, $"Current: {arg} | Folders: {Plugin.CustomMusicList.Count} | Clips: {Plugin.CustomMusic.Count}");
				}
				return false;
			case "captainaudio env":
				AddOutput(__instance, text);
				if ((Object)(object)EnvMan.instance != (Object)null)
				{
					AddOutput(__instance, "Environment: " + EnvMan.instance.GetCurrentEnvironment().m_name);
				}
				else
				{
					AddOutput(__instance, "Must be called in-game");
				}
				return false;
			default:
				return true;
			}
		}

		private static void AddOutput(Terminal terminal, string text)
		{
			Traverse.Create((object)terminal).Method("AddString", new object[1] { text }).GetValue();
		}
	}
	[BepInPlugin("captain.CaptainAudio", "Captain Audio", "1.3.0")]
	public class Plugin : BaseUnityPlugin
	{
		public static Plugin instance;

		public static ConfigEntry<bool> ModEnabled;

		public static ConfigEntry<float> MusicVolume;

		public static ConfigEntry<float> AmbientVolume;

		public static ConfigEntry<float> LocationVolMultiplier;

		public static ConfigEntry<int> LoadRetryCount;

		public static ConfigEntry<bool> EnableFallback;

		public static Dictionary<string, AudioClip> CustomMusic = new Dictionary<string, AudioClip>();

		public static Dictionary<string, Dictionary<string, AudioClip>> CustomMusicList = new Dictionary<string, Dictionary<string, AudioClip>>();

		public static Dictionary<string, AudioClip> CustomAmbient = new Dictionary<string, AudioClip>();

		public static Dictionary<string, Dictionary<string, AudioClip>> CustomAmbientList = new Dictionary<string, Dictionary<string, AudioClip>>();

		public static Dictionary<string, AudioClip> CustomSFX = new Dictionary<string, AudioClip>();

		public static Dictionary<string, Dictionary<string, AudioClip>> CustomSFXList = new Dictionary<string, Dictionary<string, AudioClip>>();

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable this mod");
			MusicVolume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MusicVolume", 0.6f, "Music volume (0.0 - 1.0)");
			AmbientVolume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "AmbientVolume", 0.3f, "Ambient volume (0.0 - 1.0)");
			LocationVolMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("General", "LocationVolumeMultiplier", 5f, "Location music volume multiplier");
			LoadRetryCount = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced", "LoadRetryCount", 3, "Number of retries when audio loading fails");
			EnableFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced", "EnableFallback", true, "Use fallback to vanilla music on load failure");
			if (!ModEnabled.Value)
			{
				return;
			}
			try
			{
				Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null);
				((MonoBehaviour)this).StartCoroutine(AudioLoader.InitializeAsync());
				Log.LogMessage((object)"Captain Audio v1.2.2 loaded");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Critical error: " + ex.Message));
			}
		}
	}
	public static class AudioUtils
	{
		public static string GetZSFXName(ZSFX zsfx)
		{
			string name = ((Object)zsfx).name;
			char[] anyOf = new char[2] { '(', ' ' };
			int num = name.IndexOfAny(anyOf);
			return (num != -1) ? name.Remove(num) : name;
		}

		public static void SafeSetVolume(AudioSource source, float volume)
		{
			if ((Object)(object)source != (Object)null)
			{
				source.volume = Mathf.Clamp01(volume);
			}
		}
	}
	public static class ReflectionCache
	{
		private static readonly Dictionary<string, FieldInfo> _fieldCache = new Dictionary<string, FieldInfo>();

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

		public static FieldInfo GetField(Type type, string fieldName)
		{
			string key = type.FullName + "." + fieldName;
			if (!_fieldCache.ContainsKey(key))
			{
				_fieldCache[key] = type.GetField(fieldName);
			}
			return _fieldCache[key];
		}

		public static void SetValue(object instance, string fieldName, object value)
		{
			GetField(instance.GetType(), fieldName)?.SetValue(instance, value);
		}

		public static T GetValue<T>(object instance, string fieldName)
		{
			FieldInfo field = GetField(instance.GetType(), fieldName);
			return (field != null) ? ((T)field.GetValue(instance)) : default(T);
		}
	}
	public static class StringMatchCache
	{
		private static readonly Dictionary<string, string> _lowerCaseCache = new Dictionary<string, string>();

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

		private static bool _initialized = false;

		public static string GetLowerCase(string input)
		{
			if (string.IsNullOrEmpty(input))
			{
				return string.Empty;
			}
			if (!_lowerCaseCache.ContainsKey(input))
			{
				_lowerCaseCache[input] = input.ToLower();
			}
			return _lowerCaseCache[input];
		}

		public static void Initialize()
		{
			if (!_initialized)
			{
				AddMapping("menu", "menu", "start", "intro", "mainmenu", "main_menu", "title");
				AddMapping("meadows", "meadow", "meadows");
				AddMapping("blackforest", "forest", "dark", "blackforest");
				AddMapping("swamp", "swamp");
				_initialized = true;
			}
		}

		private static void AddMapping(string target, params string[] sources)
		{
			foreach (string text in sources)
			{
				_musicMappingCache[text + "->" + target] = target;
			}
		}

		public static string FindMatch(string musicName)
		{
			if (string.IsNullOrEmpty(musicName))
			{
				return null;
			}
			if (Plugin.CustomMusicList.ContainsKey(musicName))
			{
				return musicName;
			}
			if (_musicMappingCache.ContainsKey(musicName))
			{
				return _musicMappingCache[musicName];
			}
			string lowerCase = GetLowerCase(musicName);
			foreach (string key in Plugin.CustomMusicList.Keys)
			{
				string lowerCase2 = GetLowerCase(key);
				if (lowerCase.Contains(lowerCase2) || lowerCase2.Contains(lowerCase))
				{
					_musicMappingCache[musicName] = key;
					return key;
				}
			}
			return null;
		}
	}
}