Decompiled source of CaptainAudio v1.2.1

CaptainAudio.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Concurrent;
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 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__DisplayClass2_0
		{
			public string resourcePrefix;

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

		[CompilerGenerated]
		private sealed class <>c__DisplayClass3_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 <CollectAudioFiles>d__6 : 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 <CollectAudioFiles>d__6(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_009a;
				case 1:
					<>1__state = -1;
					<file>5__3 = null;
					<>s__2++;
					goto IL_009a;
				case 2:
					{
						<>1__state = -1;
						<file>5__10 = null;
						<>s__9++;
						goto IL_0182;
					}
					IL_009a:
					if (<>s__2 < <>s__1.Length)
					{
						<file>5__3 = <>s__1[<>s__2];
						<>2__current = LoadExternalClip(<file>5__3, singleDict);
						<>1__state = 1;
						return true;
					}
					<>s__1 = null;
					<>s__4 = Directory.GetDirectories(path);
					<>s__5 = 0;
					goto IL_01b6;
					IL_01b6:
					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_0182;
					}
					<>s__4 = null;
					return false;
					IL_0182:
					if (<>s__9 < <>s__8.Length)
					{
						<file>5__10 = <>s__8[<>s__9];
						<>2__current = LoadExternalClip(<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_01b6;
				}
			}

			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__1 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			private int <totalMusic>5__1;

			private int <totalAmbient>5__2;

			private int <totalSFX>5__3;

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

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

			[DebuggerHidden]
			public <InitializeAsync>d__1(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;
					}
					CleanupOldTempFiles();
					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 = LoadExternalAudioFiles();
					<>1__state = 4;
					return true;
				case 4:
					<>1__state = -1;
					StringMatchCache.Initialize();
					_isInitialized = true;
					<totalMusic>5__1 = Plugin.CustomMusic.Count + Plugin.CustomMusicList.Values.Sum((Dictionary<string, AudioClip> d) => d.Count);
					<totalAmbient>5__2 = Plugin.CustomAmbient.Count + Plugin.CustomAmbientList.Values.Sum((Dictionary<string, AudioClip> d) => d.Count);
					<totalSFX>5__3 = Plugin.CustomSFX.Count + Plugin.CustomSFXList.Values.Sum((Dictionary<string, AudioClip> d) => d.Count);
					Plugin.Log.LogWarning((object)"=== 오디오 로딩 완료 ===");
					Plugin.Log.LogInfo((object)$"  음악: {Plugin.CustomMusicList.Count}개 폴더, {<totalMusic>5__1}개 클립");
					Plugin.Log.LogInfo((object)$"  환경음: {Plugin.CustomAmbientList.Count}개 폴더, {<totalAmbient>5__2}개 클립");
					Plugin.Log.LogInfo((object)$"  효과음: {Plugin.CustomSFXList.Count}개 폴더, {<totalSFX>5__3}개 클립");
					Plugin.Log.LogInfo((object)$"  총 {<totalMusic>5__1 + <totalAmbient>5__2 + <totalSFX>5__3}개 클립 로딩 완료");
					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__4 : 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 Exception <ex>5__5;

			private UnityWebRequest <www>5__6;

			private DownloadHandlerAudioClip <handler>5__7;

			private AudioClip <clip>5__8;

			private Exception <ex>5__9;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if ((uint)(num - -4) <= 1u || num == 1)
				{
					try
					{
						if (num == -4 || num == 1)
						{
							try
							{
							}
							finally
							{
								<>m__Finally2();
							}
						}
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<fileExtension>5__1 = null;
				<tempPath>5__2 = null;
				<uri>5__4 = null;
				<ex>5__5 = null;
				<www>5__6 = null;
				<handler>5__7 = null;
				<clip>5__8 = null;
				<ex>5__9 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00e5: 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_0125: Unknown result type (might be due to invalid IL or missing references)
				//IL_0149: Unknown result type (might be due to invalid IL or missing references)
				//IL_0153: Expected O, but got Unknown
				//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a9: Invalid comparison between Unknown and I4
				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 (Exception ex)
						{
							<ex>5__5 = ex;
							Plugin.Log.LogWarning((object)("임시 파일 쓰기 실패 (" + clipName + "): " + <ex>5__5.Message));
							onComplete?.Invoke(null);
							return false;
						}
						<audioType>5__3 = GetAudioType(<fileExtension>5__1);
						<uri>5__4 = "file:///" + <tempPath>5__2.Replace("\\", "/");
						<>1__state = -3;
						<www>5__6 = UnityWebRequestMultimedia.GetAudioClip(<uri>5__4, <audioType>5__3);
						<>1__state = -4;
						<handler>5__7 = (DownloadHandlerAudioClip)<www>5__6.downloadHandler;
						<handler>5__7.streamAudio = true;
						<www>5__6.timeout = Plugin.LoadTimeout.Value;
						<>2__current = <www>5__6.SendWebRequest();
						<>1__state = 1;
						return true;
					case 1:
						<>1__state = -4;
						if ((int)<www>5__6.result == 1)
						{
							<clip>5__8 = DownloadHandlerAudioClip.GetContent(<www>5__6);
							if ((Object)(object)<clip>5__8 != (Object)null && <clip>5__8.length > 0f)
							{
								((Object)<clip>5__8).name = clipName;
								onComplete?.Invoke(<clip>5__8);
							}
							else
							{
								Plugin.Log.LogWarning((object)("오디오 클립 로딩 실패 (" + clipName + "): 클립이 null이거나 길이가 0입니다."));
								onComplete?.Invoke(null);
							}
							<clip>5__8 = null;
						}
						else
						{
							Plugin.Log.LogWarning((object)("오디오 클립 로딩 실패 (" + clipName + "): " + <www>5__6.error));
							onComplete?.Invoke(null);
						}
						<handler>5__7 = null;
						<>m__Finally2();
						<www>5__6 = null;
						<>m__Finally1();
						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 (File.Exists(<tempPath>5__2))
				{
					try
					{
						File.Delete(<tempPath>5__2);
					}
					catch (Exception ex)
					{
						<ex>5__9 = ex;
						Plugin.Log.LogWarning((object)("임시 파일 삭제 실패: " + <tempPath>5__2 + " - " + <ex>5__9.Message));
					}
				}
			}

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

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

		[CompilerGenerated]
		private sealed class <LoadEmbeddedResourcesParallel>d__2 : 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__DisplayClass2_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__2(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__DisplayClass2_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 <LoadExternalAudioFiles>d__5 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			private string <externalPath>5__1;

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

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

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

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

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<externalPath>5__1 = Path.Combine(Paths.PluginPath, "CaptainAudio");
					if (!Directory.Exists(<externalPath>5__1))
					{
						return false;
					}
					if (Directory.Exists(Path.Combine(<externalPath>5__1, "Music")))
					{
						<>2__current = CollectAudioFiles(Path.Combine(<externalPath>5__1, "Music"), Plugin.CustomMusic, Plugin.CustomMusicList);
						<>1__state = 1;
						return true;
					}
					goto IL_00b3;
				case 1:
					<>1__state = -1;
					goto IL_00b3;
				case 2:
					<>1__state = -1;
					goto IL_0103;
				case 3:
					{
						<>1__state = -1;
						break;
					}
					IL_00b3:
					if (Directory.Exists(Path.Combine(<externalPath>5__1, "SFX")))
					{
						<>2__current = CollectAudioFiles(Path.Combine(<externalPath>5__1, "SFX"), Plugin.CustomSFX, Plugin.CustomSFXList);
						<>1__state = 2;
						return true;
					}
					goto IL_0103;
					IL_0103:
					if (Directory.Exists(Path.Combine(<externalPath>5__1, "Ambient")))
					{
						<>2__current = CollectAudioFiles(Path.Combine(<externalPath>5__1, "Ambient"), Plugin.CustomAmbient, Plugin.CustomAmbientList);
						<>1__state = 3;
						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 <LoadExternalClip>d__7 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string path;

			public Dictionary<string, AudioClip> dict;

			private string <uri>5__1;

			private UnityWebRequest <www>5__2;

			private DownloadHandlerAudioClip <handler>5__3;

			private AudioClip <clip>5__4;

			private string <clipName>5__5;

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

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

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

			private bool MoveNext()
			{
				//IL_00de: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e4: Invalid comparison between Unknown and I4
				//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
				//IL_0104: 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;
						}
						<uri>5__1 = "file:///" + path.Replace("\\", "/");
						<www>5__2 = UnityWebRequestMultimedia.GetAudioClip(<uri>5__1, (AudioType)0);
						<>1__state = -3;
						<www>5__2.timeout = Plugin.LoadTimeout.Value;
						<>2__current = <www>5__2.SendWebRequest();
						<>1__state = 1;
						return true;
					case 1:
						<>1__state = -3;
						if ((int)<www>5__2.result == 1)
						{
							<handler>5__3 = (DownloadHandlerAudioClip)<www>5__2.downloadHandler;
							DownloadHandlerAudioClip obj = <handler>5__3;
							<clip>5__4 = ((obj != null) ? obj.audioClip : null);
							if ((Object)(object)<clip>5__4 != (Object)null)
							{
								<clipName>5__5 = Path.GetFileNameWithoutExtension(path);
								((Object)<clip>5__4).name = <clipName>5__5;
								dict[<clipName>5__5] = <clip>5__4;
								Plugin.Log.LogInfo((object)("외부 오디오 로딩 성공: " + <clipName>5__5));
								<clipName>5__5 = null;
							}
							else
							{
								Plugin.Log.LogWarning((object)("외부 오디오 로딩 실패 (클립 null): " + path));
							}
							<handler>5__3 = null;
							<clip>5__4 = null;
						}
						else
						{
							Plugin.Log.LogWarning((object)("외부 오디오 로딩 실패: " + path + " - " + <www>5__2.error));
						}
						<>m__Finally1();
						<www>5__2 = null;
						return false;
					}
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

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

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

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

		[CompilerGenerated]
		private sealed class <LoadSingleClipWithRetry>d__3 : 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__DisplayClass3_0 <>8__3;

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

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

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

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

			private bool MoveNext()
			{
				//IL_013f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0149: 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)
					{
						Plugin.Log.LogInfo((object)$"오디오 로딩 재시도 중... ({<attempt>5__2 + 1}/{<retryCount>5__1}): {task.ClipName}");
						<>2__current = (object)new WaitForSeconds(Plugin.LoadRetryDelay.Value);
						<>1__state = 2;
						return true;
					}
					goto IL_015a;
				case 2:
					{
						<>1__state = -1;
						goto IL_015a;
					}
					IL_015a:
					<>8__3 = null;
					<attempt>5__2++;
					break;
				}
				if (<attempt>5__2 < <retryCount>5__1)
				{
					<>8__3 = new <>c__DisplayClass3_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;

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

		[IteratorStateMachine(typeof(<LoadEmbeddedResourcesParallel>d__2))]
		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__2(0)
			{
				resourcePrefix = resourcePrefix,
				singleDict = singleDict,
				folderDict = folderDict
			};
		}

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

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

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

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

		[IteratorStateMachine(typeof(<LoadExternalClip>d__7))]
		private static IEnumerator LoadExternalClip(string path, Dictionary<string, AudioClip> dict)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <LoadExternalClip>d__7(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 void CleanupOldTempFiles()
		{
			try
			{
				string temporaryCachePath = Application.temporaryCachePath;
				if (!Directory.Exists(temporaryCachePath))
				{
					return;
				}
				string[] files = Directory.GetFiles(temporaryCachePath, "temp_audio_*");
				int num = 0;
				string[] array = files;
				foreach (string text in array)
				{
					try
					{
						FileInfo fileInfo = new FileInfo(text);
						if ((DateTime.Now - fileInfo.LastWriteTime).TotalHours > 1.0)
						{
							File.Delete(text);
							num++;
						}
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)("임시 파일 정리 실패: " + text + " - " + ex.Message));
					}
				}
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogWarning((object)("임시 파일 정리 중 오류: " + ex2.Message));
			}
		}

		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)
		{
			if (Plugin.ModEnabled.Value)
			{
				if (___m_queuedMusic != null)
				{
					ReflectionCache.SetValue(___m_queuedMusic, "m_volume", Plugin.MusicVolume.Value);
				}
				if (!___m_musicSource.isPlaying && ___m_musicSource.loop)
				{
					___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))
			{
				string name = ((Object)___m_audioSource.clip).name;
				if (Plugin.CustomMusic.ContainsKey(name))
				{
					___m_audioSource.clip = Plugin.CustomMusic[name];
					___m_baseVolume *= Plugin.LocationVolMultiplier.Value;
				}
			}
		}
	}
	[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"))
			{
				AudioSource componentInChildren = ((Component)__instance).GetComponentInChildren<AudioSource>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.clip = Plugin.CustomSFX["portal"];
					((Component)componentInChildren).gameObject.SetActive(false);
					((Component)componentInChildren).gameObject.SetActive(true);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Fireplace), "Start")]
	public static class Fireplace_Start_Patch
	{
		private static readonly Dictionary<string, string> FireplaceSFXMapping = new Dictionary<string, string>
		{
			{ "groundtorch", "groundtorch" },
			{ "walltorch", "walltorch" },
			{ "fire_pit", "fire_pit" },
			{ "bonfire", "bonfire" },
			{ "hearth", "hearth" }
		};

		private static void Postfix(Fireplace __instance)
		{
			if (!Plugin.ModEnabled.Value)
			{
				return;
			}
			string name = ((Object)__instance).name;
			foreach (KeyValuePair<string, string> item in FireplaceSFXMapping)
			{
				if (name.Contains(item.Key) && Plugin.CustomSFX.ContainsKey(item.Value))
				{
					AudioSource fireplaceAudioSource = GetFireplaceAudioSource(__instance, item.Key);
					if ((Object)(object)fireplaceAudioSource != (Object)null)
					{
						fireplaceAudioSource.clip = Plugin.CustomSFX[item.Value];
					}
					break;
				}
			}
		}

		private static AudioSource GetFireplaceAudioSource(Fireplace fireplace, string keyword)
		{
			if (keyword == "groundtorch")
			{
				GameObject enabledObject = fireplace.m_enabledObject;
				return (enabledObject != null) ? enabledObject.GetComponentInChildren<AudioSource>() : null;
			}
			if (keyword == "walltorch")
			{
				GameObject enabledObjectHigh = fireplace.m_enabledObjectHigh;
				AudioSource val = ((enabledObjectHigh != null) ? enabledObjectHigh.GetComponentInChildren<AudioSource>() : null);
				object obj = val;
				if (obj == null)
				{
					GameObject enabledObject2 = fireplace.m_enabledObject;
					obj = ((enabledObject2 != null) ? enabledObject2.GetComponentInChildren<AudioSource>() : null);
				}
				return (AudioSource)obj;
			}
			GameObject enabledObjectHigh2 = fireplace.m_enabledObjectHigh;
			return (enabledObjectHigh2 != null) ? enabledObjectHigh2.GetComponentInChildren<AudioSource>() : null;
		}
	}
	[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;
					}
				}
				string[] array = new string[4] { "Morning", "Day", "Evening", "Night" };
				string[] array2 = array;
				foreach (string timeOfDay in array2)
				{
					AddMusicToEnvironment(__instance, i, timeOfDay);
				}
			}
		}

		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, "<color=#00BFFF>Captain Audio config reloaded</color>");
				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, $"<color=#00BFFF>Current: {arg} | Folders: {Plugin.CustomMusicList.Count} | Clips: {Plugin.CustomMusic.Count}</color>");
				}
				return false;
			case "captainaudio env":
				AddOutput(__instance, text);
				if ((Object)(object)EnvMan.instance != (Object)null)
				{
					AddOutput(__instance, "<color=#00BFFF>Environment: " + EnvMan.instance.GetCurrentEnvironment().m_name + "</color>");
				}
				else
				{
					AddOutput(__instance, "<color=#00BFFF>Must be called in-game</color>");
				}
				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.2.1")]
	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<float> LoadRetryDelay;

		public static ConfigEntry<int> LoadTimeout;

		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");
			LoadRetryDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced", "LoadRetryDelay", 0.5f, "Delay between retries in seconds");
			LoadTimeout = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced", "LoadTimeout", 15, "Timeout for audio loading in seconds");
			EnableFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced", "EnableFallback", true, "Use fallback to vanilla music on load failure");
			if (!ModEnabled.Value)
			{
				return;
			}
			SetupConfigEvents();
			try
			{
				Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null);
				((MonoBehaviour)this).StartCoroutine(AudioLoader.InitializeAsync());
				Log.LogInfo((object)"Captain Audio v1.2.1 loaded");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Critical error: " + ex.Message));
			}
		}

		private void SetupConfigEvents()
		{
			MusicVolume.SettingChanged += delegate
			{
				Log.LogInfo((object)$"음악 볼륨 변경: {MusicVolume.Value:F2} (다음 음악부터 적용됨)");
			};
			AmbientVolume.SettingChanged += delegate
			{
				Log.LogInfo((object)$"환경음 볼륨 변경: {AmbientVolume.Value:F2} (다음 환경음부터 적용됨)");
			};
			LocationVolMultiplier.SettingChanged += delegate
			{
				Log.LogInfo((object)$"Location 음악 볼륨 배율 변경: {LocationVolMultiplier.Value:F2} (다음 음악부터 적용됨)");
			};
			LoadRetryCount.SettingChanged += delegate
			{
				Log.LogInfo((object)$"오디오 로딩 재시도 횟수 변경: {LoadRetryCount.Value}");
			};
			LoadRetryDelay.SettingChanged += delegate
			{
				Log.LogInfo((object)$"재시도 지연 시간 변경: {LoadRetryDelay.Value}초");
			};
			LoadTimeout.SettingChanged += delegate
			{
				Log.LogInfo((object)$"로딩 타임아웃 변경: {LoadTimeout.Value}초");
			};
		}
	}
	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 ConcurrentDictionary<string, FieldInfo> _fieldCache = new ConcurrentDictionary<string, FieldInfo>();

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

		public static FieldInfo GetField(Type type, string fieldName)
		{
			string key = type.FullName + "." + fieldName;
			return _fieldCache.GetOrAdd(key, (string _) => type.GetField(fieldName));
		}

		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;
		}
	}
}