Decompiled source of CaptainAudio v1.3.51

CaptainAudio.dll

Decompiled 2 weeks 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.Text;
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 <CollectAudioFilesAsync>d__12 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string path;

			public Dictionary<string, AudioClip> singleDict;

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

			private string[] <>s__1;

			private int <>s__2;

			private string <file>5__3;

			private string[] <>s__4;

			private int <>s__5;

			private string <folder>5__6;

			private string <folderName>5__7;

			private string[] <>s__8;

			private int <>s__9;

			private string <file>5__10;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>s__1 = null;
				<file>5__3 = null;
				<>s__4 = null;
				<folder>5__6 = null;
				<folderName>5__7 = null;
				<>s__8 = null;
				<file>5__10 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>s__1 = Directory.GetFiles(path);
					<>s__2 = 0;
					goto IL_00a4;
				case 1:
					<>1__state = -1;
					<file>5__3 = null;
					<>s__2++;
					goto IL_00a4;
				case 2:
					{
						<>1__state = -1;
						<file>5__10 = null;
						<>s__9++;
						goto IL_0196;
					}
					IL_00a4:
					if (<>s__2 < <>s__1.Length)
					{
						<file>5__3 = <>s__1[<>s__2];
						<>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(LoadExternalClipAsync(<file>5__3, singleDict));
						<>1__state = 1;
						return true;
					}
					<>s__1 = null;
					<>s__4 = Directory.GetDirectories(path);
					<>s__5 = 0;
					goto IL_01ca;
					IL_01ca:
					if (<>s__5 < <>s__4.Length)
					{
						<folder>5__6 = <>s__4[<>s__5];
						<folderName>5__7 = Path.GetFileName(<folder>5__6);
						folderDict[<folderName>5__7] = new Dictionary<string, AudioClip>();
						<>s__8 = Directory.GetFiles(<folder>5__6);
						<>s__9 = 0;
						goto IL_0196;
					}
					<>s__4 = null;
					return false;
					IL_0196:
					if (<>s__9 < <>s__8.Length)
					{
						<file>5__10 = <>s__8[<>s__9];
						<>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(LoadExternalClipAsync(<file>5__10, folderDict[<folderName>5__7]));
						<>1__state = 2;
						return true;
					}
					<>s__8 = null;
					<folderName>5__7 = null;
					<folder>5__6 = null;
					<>s__5++;
					goto IL_01ca;
				}
			}

			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 <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;
					<>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(LoadExternalAudioFilesAsync());
					<>1__state = 4;
					return true;
				case 4:
					<>1__state = -1;
					StringMatchCache.Initialize();
					_isInitialized = true;
					Plugin.Log.LogInfo((object)$"Loaded: {Plugin.CustomMusicList.Count} folders, {Plugin.CustomMusic.Count} clips");
					if ((Object)(object)MusicMan.instance != (Object)null)
					{
						MusicMan_Awake_Patch.ApplyBossMusic(MusicMan.instance);
						Plugin.Log.LogInfo((object)"[CaptainAudio] 보스 음악 재적용 완료");
					}
					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_00db: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
				//IL_0109: Expected O, but got Unknown
				//IL_015e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0164: Invalid comparison between Unknown and I4
				//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b9: Invalid comparison between Unknown and I4
				//IL_0275: 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 = new Uri(<tempPath>5__2).AbsoluteUri;
						<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);
				<tasks>5__4.Clear();
				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 <LoadExternalAudioFilesAsync>d__8 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			private string <newBasePath>5__1;

			private string <newMusicPath>5__2;

			private string <newSfxPath>5__3;

			private string <newAmbientPath>5__4;

			private bool <hasNewPath>5__5;

			private string <externalPath>5__6;

			private string <musicPath>5__7;

			private string <sfxPath>5__8;

			private string <ambientPath>5__9;

			private string[] <>s__10;

			private int <>s__11;

			private string <subFolder>5__12;

			private string <folderName>5__13;

			private string[] <>s__14;

			private int <>s__15;

			private string <subFolder>5__16;

			private string <folderName>5__17;

			private string[] <>s__18;

			private int <>s__19;

			private string <subFolder>5__20;

			private string <folderName>5__21;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<newBasePath>5__1 = null;
				<newMusicPath>5__2 = null;
				<newSfxPath>5__3 = null;
				<newAmbientPath>5__4 = null;
				<externalPath>5__6 = null;
				<musicPath>5__7 = null;
				<sfxPath>5__8 = null;
				<ambientPath>5__9 = null;
				<>s__10 = null;
				<subFolder>5__12 = null;
				<folderName>5__13 = null;
				<>s__14 = null;
				<subFolder>5__16 = null;
				<folderName>5__17 = null;
				<>s__18 = null;
				<subFolder>5__20 = null;
				<folderName>5__21 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<newBasePath>5__1 = Path.Combine(Paths.PluginPath, "korCaptain-CaptainAudio");
					<newMusicPath>5__2 = Path.Combine(<newBasePath>5__1, "music");
					<newSfxPath>5__3 = Path.Combine(<newBasePath>5__1, "sfx");
					<newAmbientPath>5__4 = Path.Combine(<newBasePath>5__1, "ambient");
					<hasNewPath>5__5 = Directory.Exists(<newMusicPath>5__2) || Directory.Exists(<newSfxPath>5__3) || Directory.Exists(<newAmbientPath>5__4);
					if (<hasNewPath>5__5)
					{
						_externalOverrideFolders.Clear();
						if (Directory.Exists(<newMusicPath>5__2))
						{
							<>s__10 = Directory.GetDirectories(<newMusicPath>5__2);
							for (<>s__11 = 0; <>s__11 < <>s__10.Length; <>s__11++)
							{
								<subFolder>5__12 = <>s__10[<>s__11];
								<folderName>5__13 = Path.GetFileName(<subFolder>5__12);
								if (Directory.GetFiles(<subFolder>5__12).Any((string f) => IsValidAudioFile(f)))
								{
									_externalOverrideFolders.Add("Music:" + <folderName>5__13);
									Plugin.Log.LogInfo((object)("[Override] Music/" + <folderName>5__13 + ": 외부 음악 감지됨 → 내장 음악 대체"));
								}
								<folderName>5__13 = null;
								<subFolder>5__12 = null;
							}
							<>s__10 = null;
						}
						if (Directory.Exists(<newSfxPath>5__3))
						{
							<>s__14 = Directory.GetDirectories(<newSfxPath>5__3);
							for (<>s__15 = 0; <>s__15 < <>s__14.Length; <>s__15++)
							{
								<subFolder>5__16 = <>s__14[<>s__15];
								<folderName>5__17 = Path.GetFileName(<subFolder>5__16);
								if (Directory.GetFiles(<subFolder>5__16).Any((string f) => IsValidAudioFile(f)))
								{
									_externalOverrideFolders.Add("SFX:" + <folderName>5__17);
									Plugin.Log.LogInfo((object)("[Override] SFX/" + <folderName>5__17 + ": 외부 SFX 감지됨 → 내장 SFX 대체"));
								}
								<folderName>5__17 = null;
								<subFolder>5__16 = null;
							}
							<>s__14 = null;
						}
						if (Directory.Exists(<newAmbientPath>5__4))
						{
							<>s__18 = Directory.GetDirectories(<newAmbientPath>5__4);
							for (<>s__19 = 0; <>s__19 < <>s__18.Length; <>s__19++)
							{
								<subFolder>5__20 = <>s__18[<>s__19];
								<folderName>5__21 = Path.GetFileName(<subFolder>5__20);
								if (Directory.GetFiles(<subFolder>5__20).Any((string f) => IsValidAudioFile(f)))
								{
									_externalOverrideFolders.Add("Ambient:" + <folderName>5__21);
									Plugin.Log.LogInfo((object)("[Override] Ambient/" + <folderName>5__21 + ": 외부 Ambient 감지됨 → 내장 Ambient 대체"));
								}
								<folderName>5__21 = null;
								<subFolder>5__20 = null;
							}
							<>s__18 = null;
						}
						ApplyExternalOverrides();
						if (Directory.Exists(<newMusicPath>5__2))
						{
							<>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(CollectAudioFilesAsync(<newMusicPath>5__2, Plugin.CustomMusic, Plugin.CustomMusicList));
							<>1__state = 1;
							return true;
						}
						goto IL_0441;
					}
					<externalPath>5__6 = Path.Combine(Paths.PluginPath, "CaptainAudio");
					if (!Directory.Exists(<externalPath>5__6))
					{
						return false;
					}
					_externalOverrideFolders.Clear();
					ScanExternalFolders(<externalPath>5__6);
					ApplyExternalOverrides();
					<musicPath>5__7 = Path.Combine(<externalPath>5__6, "Music");
					<sfxPath>5__8 = Path.Combine(<externalPath>5__6, "SFX");
					<ambientPath>5__9 = Path.Combine(<externalPath>5__6, "Ambient");
					if (Directory.Exists(<musicPath>5__7))
					{
						<>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(CollectAudioFilesAsync(<musicPath>5__7, Plugin.CustomMusic, Plugin.CustomMusicList));
						<>1__state = 4;
						return true;
					}
					goto IL_059f;
				case 1:
					<>1__state = -1;
					goto IL_0441;
				case 2:
					<>1__state = -1;
					goto IL_0487;
				case 3:
					<>1__state = -1;
					goto IL_04cd;
				case 4:
					<>1__state = -1;
					goto IL_059f;
				case 5:
					<>1__state = -1;
					goto IL_05e5;
				case 6:
					{
						<>1__state = -1;
						break;
					}
					IL_0487:
					if (Directory.Exists(<newAmbientPath>5__4))
					{
						<>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(CollectAudioFilesAsync(<newAmbientPath>5__4, Plugin.CustomAmbient, Plugin.CustomAmbientList));
						<>1__state = 3;
						return true;
					}
					goto IL_04cd;
					IL_059f:
					if (Directory.Exists(<sfxPath>5__8))
					{
						<>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(CollectAudioFilesAsync(<sfxPath>5__8, Plugin.CustomSFX, Plugin.CustomSFXList));
						<>1__state = 5;
						return true;
					}
					goto IL_05e5;
					IL_04cd:
					return false;
					IL_0441:
					if (Directory.Exists(<newSfxPath>5__3))
					{
						<>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(CollectAudioFilesAsync(<newSfxPath>5__3, Plugin.CustomSFX, Plugin.CustomSFXList));
						<>1__state = 2;
						return true;
					}
					goto IL_0487;
					IL_05e5:
					if (Directory.Exists(<ambientPath>5__9))
					{
						<>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(CollectAudioFilesAsync(<ambientPath>5__9, Plugin.CustomAmbient, Plugin.CustomAmbientList));
						<>1__state = 6;
						return true;
					}
					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 <LoadExternalClipAsync>d__13 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string path;

			public Dictionary<string, AudioClip> dict;

			private string <extension>5__1;

			private string <uri>5__2;

			private AudioType <audioType>5__3;

			private UnityWebRequest <www>5__4;

			private DownloadHandlerAudioClip <handler>5__5;

			private AudioClip <clip>5__6;

			private string <clipName>5__7;

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

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

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

			private bool MoveNext()
			{
				//IL_0165: Unknown result type (might be due to invalid IL or missing references)
				//IL_016b: Invalid comparison between Unknown and I4
				//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e2: 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_0110: Expected O, but got Unknown
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						if (path.EndsWith(".txt") || !path.Contains("."))
						{
							return false;
						}
						<extension>5__1 = Path.GetExtension(path).ToLower();
						if (<extension>5__1 != ".ogg" && <extension>5__1 != ".wav" && <extension>5__1 != ".mp3")
						{
							return false;
						}
						<uri>5__2 = new Uri(path).AbsoluteUri;
						<audioType>5__3 = GetAudioType(<extension>5__1);
						<www>5__4 = UnityWebRequestMultimedia.GetAudioClip(<uri>5__2, <audioType>5__3);
						<>1__state = -3;
						<handler>5__5 = (DownloadHandlerAudioClip)<www>5__4.downloadHandler;
						<handler>5__5.streamAudio = false;
						<handler>5__5.compressed = false;
						<www>5__4.timeout = 15;
						<>2__current = <www>5__4.SendWebRequest();
						<>1__state = 1;
						return true;
					case 1:
						<>1__state = -3;
						if ((int)<www>5__4.result == 1)
						{
							DownloadHandlerAudioClip obj = <handler>5__5;
							<clip>5__6 = ((obj != null) ? obj.audioClip : null);
							if ((Object)(object)<clip>5__6 != (Object)null && <clip>5__6.length > 0f && <clip>5__6.samples > 0)
							{
								<clipName>5__7 = Path.GetFileNameWithoutExtension(path);
								((Object)<clip>5__6).name = <clipName>5__7;
								dict[<clipName>5__7] = <clip>5__6;
								<clipName>5__7 = null;
							}
							else
							{
								Plugin.Log.LogWarning((object)("Invalid external clip: " + path));
							}
							<clip>5__6 = null;
						}
						else
						{
							Plugin.Log.LogWarning((object)("Failed to load external: " + path + " - " + <www>5__4.error));
						}
						<handler>5__5 = null;
						<>m__Finally1();
						<www>5__4 = 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__4 != null)
				{
					((IDisposable)<www>5__4).Dispose();
				}
			}

			[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_010e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0118: 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.AudioData = null;
						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_0129;
				case 2:
					{
						<>1__state = -1;
						goto IL_0129;
					}
					IL_0129:
					<>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
			};
		}

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

		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}개 제거됨");
					}
				}
			}
		}

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

		[IteratorStateMachine(typeof(<LoadExternalClipAsync>d__13))]
		private static IEnumerator LoadExternalClipAsync(string path, Dictionary<string, AudioClip> dict)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <LoadExternalClipAsync>d__13(0)
			{
				path = path,
				dict = dict
			};
		}

		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
	{
		public static void Apply(MusicMan instance)
		{
			for (int i = 0; i < instance.m_music.Count; i++)
			{
				NamedMusic instance2 = instance.m_music[i];
				string value = ReflectionCache.GetValue<string>(instance2, "m_name");
				if (Plugin.CustomMusicList.ContainsKey(value))
				{
					AudioClip[] value2 = Plugin.CustomMusicList[value].Values.ToArray();
					ReflectionCache.SetValue(instance2, "m_clips", value2);
					ReflectionCache.SetValue(instance2, "m_resume", false);
					continue;
				}
				string text = StringMatchCache.FindMatch(value);
				if (text != null && Plugin.CustomMusicList.ContainsKey(text))
				{
					AudioClip[] value3 = Plugin.CustomMusicList[text].Values.ToArray();
					ReflectionCache.SetValue(instance2, "m_clips", value3);
					ReflectionCache.SetValue(instance2, "m_resume", false);
					continue;
				}
				AudioClip[] value4 = ReflectionCache.GetValue<AudioClip[]>(instance2, "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];
					}
				}
			}
		}

		public static void ApplyBossMusic(MusicMan instance)
		{
			for (int i = 0; i < instance.m_music.Count; i++)
			{
				NamedMusic instance2 = instance.m_music[i];
				string value = ReflectionCache.GetValue<string>(instance2, "m_name");
				if (value != null)
				{
					string text = (Plugin.CustomMusicList.ContainsKey(value) ? value : StringMatchCache.FindMatch(value));
					if (text != null && text.StartsWith("Boss_") && Plugin.CustomMusicList.ContainsKey(text))
					{
						AudioClip[] value2 = Plugin.CustomMusicList[text].Values.ToArray();
						ReflectionCache.SetValue(instance2, "m_clips", value2);
						ReflectionCache.SetValue(instance2, "m_resume", false);
					}
				}
			}
		}

		private static void Postfix(MusicMan __instance)
		{
			if (Plugin.ModEnabled.Value)
			{
				Apply(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(MusicMan), "UpdateMusic")]
	public static class MusicMan_UpdateMusic_Patch
	{
		private static void Prefix(ref object ___m_queuedMusic, object ___m_currentMusic, 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
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: 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)
			{
				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 < 1.5f)
					{
						___m_musicSource.loop = false;
					}
				}
			}
			if (___m_currentMusic != null)
			{
				ReflectionCache.SetValue(___m_currentMusic, "m_resume", false);
			}
			if (___m_queuedMusic != null)
			{
				ReflectionCache.SetValue(___m_queuedMusic, "m_resume", false);
			}
			if ((Object)(object)___m_musicSource != (Object)null && (Object)(object)___m_musicSource.clip != (Object)null && (int)___m_musicSource.clip.loadState == 2 && ___m_musicSource.time >= ___m_musicSource.clip.length)
			{
				___m_musicSource.time = 0f;
			}
		}

		private static void Postfix(AudioSource ___m_musicSource)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Invalid comparison between Unknown and I4
			if (Plugin.ModEnabled.Value && (Object)(object)___m_musicSource != (Object)null && (Object)(object)___m_musicSource.clip != (Object)null && (int)___m_musicSource.clip.loadState == 2 && ___m_musicSource.clip.length > 0f && ___m_musicSource.time >= ___m_musicSource.clip.length)
			{
				___m_musicSource.time = 0f;
			}
		}
	}
	[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", false);
				((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;
			case "captainaudio bosses":
				AddOutput(__instance, text);
				if ((Object)(object)MusicMan.instance != (Object)null)
				{
					StringBuilder stringBuilder = new StringBuilder("Boss entries: ");
					bool flag = false;
					foreach (NamedMusic item in MusicMan.instance.m_music)
					{
						string value2 = ReflectionCache.GetValue<string>(item, "m_name");
						if (value2 != null && value2.ToLower().Contains("boss"))
						{
							stringBuilder.Append(value2).Append(" | ");
							flag = true;
						}
					}
					AddOutput(__instance, flag ? stringBuilder.ToString() : "No boss entries found in MusicMan");
				}
				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.51")]
	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());
				try
				{
					Type type = typeof(BaseUnityPlugin).Assembly.GetType("BepInEx.ConsoleManager");
					MethodInfo methodInfo = type?.GetMethod("SetConsoleColor", BindingFlags.Static | BindingFlags.Public);
					TextWriter textWriter = type?.GetProperty("StandardOutStream", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) as TextWriter;
					if (methodInfo != null && textWriter != null)
					{
						methodInfo.Invoke(null, new object[1] { ConsoleColor.Cyan });
						textWriter.WriteLine("[CaptainAudio] CaptainAudio 1.3.51 Loaded!");
						methodInfo.Invoke(null, new object[1] { ConsoleColor.Gray });
					}
					else
					{
						Log.LogMessage((object)"[CaptainAudio] CaptainAudio 1.3.51 Loaded!");
					}
				}
				catch
				{
					Log.LogMessage((object)"[CaptainAudio] CaptainAudio 1.3.51 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("Boss_TheElder", "boss_gdking", "gdking");
				AddMapping("Boss_Yagluth", "boss_goblinking", "goblinking");
				_initialized = true;
			}
		}

		private static void AddMapping(string target, params string[] sources)
		{
			foreach (string key in sources)
			{
				_musicMappingCache[key] = 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;
		}
	}
}