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 b__0(string name) { return name.StartsWith(resourcePrefix); } } [CompilerGenerated] private sealed class <>c__DisplayClass6_0 { public AudioClip clip; public bool success; internal void b__0(AudioClip loadedClip) { clip = loadedClip; success = (Object)(object)loadedClip != (Object)null; } } [CompilerGenerated] private sealed class d__12 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string path; public Dictionary singleDict; public Dictionary> folderDict; private string[] <>s__1; private int <>s__2; private string 5__3; private string[] <>s__4; private int <>s__5; private string 5__6; private string 5__7; private string[] <>s__8; private int <>s__9; private string 5__10; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__12(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>s__1 = null; 5__3 = null; <>s__4 = null; 5__6 = null; 5__7 = null; <>s__8 = null; 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; 5__3 = null; <>s__2++; goto IL_00a4; case 2: { <>1__state = -1; 5__10 = null; <>s__9++; goto IL_0196; } IL_00a4: if (<>s__2 < <>s__1.Length) { 5__3 = <>s__1[<>s__2]; <>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(LoadExternalClipAsync(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) { 5__6 = <>s__4[<>s__5]; 5__7 = Path.GetFileName(5__6); folderDict[5__7] = new Dictionary(); <>s__8 = Directory.GetFiles(5__6); <>s__9 = 0; goto IL_0196; } <>s__4 = null; return false; IL_0196: if (<>s__9 < <>s__8.Length) { 5__10 = <>s__8[<>s__9]; <>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(LoadExternalClipAsync(5__10, folderDict[5__7])); <>1__state = 2; return true; } <>s__8 = null; 5__7 = null; 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 d__4 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public 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 d__7 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public byte[] audioData; public string clipName; public Action onComplete; private string 5__1; private string 5__2; private AudioType 5__3; private string 5__4; private UnityWebRequest 5__5; private DownloadHandlerAudioClip 5__6; private AudioClip 5__7; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public 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(); } } 5__1 = null; 5__2 = null; 5__4 = null; 5__5 = null; 5__6 = null; 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; 5__1 = DetectAudioFormat(audioData); 5__2 = Path.Combine(Application.temporaryCachePath, $"temp_audio_{clipName}_{Guid.NewGuid()}{5__1}"); try { Directory.CreateDirectory(Path.GetDirectoryName(5__2)); File.WriteAllBytes(5__2, audioData); } catch { onComplete?.Invoke(null); return false; } 5__3 = GetAudioType(5__1); 5__4 = new Uri(5__2).AbsoluteUri; 5__5 = UnityWebRequestMultimedia.GetAudioClip(5__4, 5__3); <>1__state = -3; 5__6 = (DownloadHandlerAudioClip)5__5.downloadHandler; 5__6.streamAudio = false; 5__6.compressed = false; 5__5.timeout = 15; <>2__current = 5__5.SendWebRequest(); <>1__state = 1; return true; case 1: <>1__state = -3; if ((int)5__5.result == 1) { 5__7 = DownloadHandlerAudioClip.GetContent(5__5); if ((Object)(object)5__7 != (Object)null && 5__7.length > 0f && 5__7.samples > 0 && (int)5__7.loadState == 2) { ((Object)5__7).name = clipName; onComplete?.Invoke(5__7); } else { ManualLogSource log = Plugin.Log; object[] obj = new object[4] { clipName, null, null, null }; AudioClip obj2 = 5__7; obj[1] = ((obj2 != null) ? new float?(obj2.length) : null); AudioClip obj3 = 5__7; obj[2] = ((obj3 != null) ? new int?(obj3.samples) : null); AudioClip obj4 = 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); } 5__7 = null; } else { Plugin.Log.LogWarning((object)("Failed to load: " + clipName + " - " + 5__5.error)); onComplete?.Invoke(null); } 5__6 = null; <>m__Finally1(); 5__5 = null; if (File.Exists(5__2)) { try { File.Delete(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 (5__5 != null) { ((IDisposable)5__5).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__5 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string resourcePrefix; public Dictionary singleDict; public Dictionary> folderDict; private <>c__DisplayClass5_0 <>8__1; private Assembly 5__2; private string[] 5__3; private List 5__4; private string[] <>s__5; private int <>s__6; private string 5__7; private Stream 5__8; private byte[] 5__9; private string 5__10; private string 5__11; private string 5__12; private LoadTask 5__13; private List.Enumerator <>s__14; private LoadTask 5__15; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__5(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; 5__2 = null; 5__3 = null; 5__4 = null; <>s__5 = null; 5__7 = null; 5__8 = null; 5__9 = null; 5__10 = null; 5__11 = null; 5__12 = null; 5__13 = null; <>s__14 = default(List.Enumerator); 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; 5__2 = Assembly.GetExecutingAssembly(); 5__3 = (from name in 5__2.GetManifestResourceNames() where name.StartsWith(<>8__1.resourcePrefix) select name).ToArray(); if (5__3.Length == 0) { return false; } 5__4 = new List(); <>s__5 = 5__3; for (<>s__6 = 0; <>s__6 < <>s__5.Length; <>s__6++) { 5__7 = <>s__5[<>s__6]; 5__8 = 5__2.GetManifestResourceStream(5__7); if (5__8 != null) { 5__9 = new byte[5__8.Length]; 5__8.Read(5__9, 0, 5__9.Length); 5__8.Dispose(); 5__10 = 5__7.Substring(<>8__1.resourcePrefix.Length + 1); (5__11, 5__12) = ParseResourcePath(5__10); if (!string.IsNullOrEmpty(5__12)) { 5__13 = new LoadTask { FolderName = 5__11, ClipName = 5__12, AudioData = 5__9, IsCompleted = false, LoadedClip = null }; 5__4.Add(5__13); ((MonoBehaviour)Plugin.instance).StartCoroutine(LoadSingleClipWithRetry(5__13)); 5__8 = null; 5__9 = null; 5__10 = null; 5__11 = null; 5__12 = null; 5__13 = null; 5__7 = null; } } } <>s__5 = null; break; case 1: <>1__state = -1; break; } if (5__4.Any((LoadTask t) => !t.IsCompleted)) { <>2__current = null; <>1__state = 1; return true; } <>s__14 = 5__4.GetEnumerator(); try { while (<>s__14.MoveNext()) { 5__15 = <>s__14.Current; if ((Object)(object)5__15.LoadedClip == (Object)null) { continue; } if (!string.IsNullOrEmpty(5__15.FolderName)) { if (!folderDict.ContainsKey(5__15.FolderName)) { folderDict[5__15.FolderName] = new Dictionary(); } folderDict[5__15.FolderName][5__15.ClipName] = 5__15.LoadedClip; } else { singleDict[5__15.ClipName] = 5__15.LoadedClip; } 5__15 = null; } } finally { ((IDisposable)<>s__14).Dispose(); } <>s__14 = default(List.Enumerator); 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 d__8 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private string 5__1; private string 5__2; private string 5__3; private string 5__4; private bool 5__5; private string 5__6; private string 5__7; private string 5__8; private string 5__9; private string[] <>s__10; private int <>s__11; private string 5__12; private string 5__13; private string[] <>s__14; private int <>s__15; private string 5__16; private string 5__17; private string[] <>s__18; private int <>s__19; private string 5__20; private string 5__21; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__8(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__1 = null; 5__2 = null; 5__3 = null; 5__4 = null; 5__6 = null; 5__7 = null; 5__8 = null; 5__9 = null; <>s__10 = null; 5__12 = null; 5__13 = null; <>s__14 = null; 5__16 = null; 5__17 = null; <>s__18 = null; 5__20 = null; 5__21 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = Path.Combine(Paths.PluginPath, "korCaptain-CaptainAudio"); 5__2 = Path.Combine(5__1, "music"); 5__3 = Path.Combine(5__1, "sfx"); 5__4 = Path.Combine(5__1, "ambient"); 5__5 = Directory.Exists(5__2) || Directory.Exists(5__3) || Directory.Exists(5__4); if (5__5) { _externalOverrideFolders.Clear(); if (Directory.Exists(5__2)) { <>s__10 = Directory.GetDirectories(5__2); for (<>s__11 = 0; <>s__11 < <>s__10.Length; <>s__11++) { 5__12 = <>s__10[<>s__11]; 5__13 = Path.GetFileName(5__12); if (Directory.GetFiles(5__12).Any((string f) => IsValidAudioFile(f))) { _externalOverrideFolders.Add("Music:" + 5__13); Plugin.Log.LogInfo((object)("[Override] Music/" + 5__13 + ": 외부 음악 감지됨 → 내장 음악 대체")); } 5__13 = null; 5__12 = null; } <>s__10 = null; } if (Directory.Exists(5__3)) { <>s__14 = Directory.GetDirectories(5__3); for (<>s__15 = 0; <>s__15 < <>s__14.Length; <>s__15++) { 5__16 = <>s__14[<>s__15]; 5__17 = Path.GetFileName(5__16); if (Directory.GetFiles(5__16).Any((string f) => IsValidAudioFile(f))) { _externalOverrideFolders.Add("SFX:" + 5__17); Plugin.Log.LogInfo((object)("[Override] SFX/" + 5__17 + ": 외부 SFX 감지됨 → 내장 SFX 대체")); } 5__17 = null; 5__16 = null; } <>s__14 = null; } if (Directory.Exists(5__4)) { <>s__18 = Directory.GetDirectories(5__4); for (<>s__19 = 0; <>s__19 < <>s__18.Length; <>s__19++) { 5__20 = <>s__18[<>s__19]; 5__21 = Path.GetFileName(5__20); if (Directory.GetFiles(5__20).Any((string f) => IsValidAudioFile(f))) { _externalOverrideFolders.Add("Ambient:" + 5__21); Plugin.Log.LogInfo((object)("[Override] Ambient/" + 5__21 + ": 외부 Ambient 감지됨 → 내장 Ambient 대체")); } 5__21 = null; 5__20 = null; } <>s__18 = null; } ApplyExternalOverrides(); if (Directory.Exists(5__2)) { <>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(CollectAudioFilesAsync(5__2, Plugin.CustomMusic, Plugin.CustomMusicList)); <>1__state = 1; return true; } goto IL_0441; } 5__6 = Path.Combine(Paths.PluginPath, "CaptainAudio"); if (!Directory.Exists(5__6)) { return false; } _externalOverrideFolders.Clear(); ScanExternalFolders(5__6); ApplyExternalOverrides(); 5__7 = Path.Combine(5__6, "Music"); 5__8 = Path.Combine(5__6, "SFX"); 5__9 = Path.Combine(5__6, "Ambient"); if (Directory.Exists(5__7)) { <>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(CollectAudioFilesAsync(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(5__4)) { <>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(CollectAudioFilesAsync(5__4, Plugin.CustomAmbient, Plugin.CustomAmbientList)); <>1__state = 3; return true; } goto IL_04cd; IL_059f: if (Directory.Exists(5__8)) { <>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(CollectAudioFilesAsync(5__8, Plugin.CustomSFX, Plugin.CustomSFXList)); <>1__state = 5; return true; } goto IL_05e5; IL_04cd: return false; IL_0441: if (Directory.Exists(5__3)) { <>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(CollectAudioFilesAsync(5__3, Plugin.CustomSFX, Plugin.CustomSFXList)); <>1__state = 2; return true; } goto IL_0487; IL_05e5: if (Directory.Exists(5__9)) { <>2__current = ((MonoBehaviour)Plugin.instance).StartCoroutine(CollectAudioFilesAsync(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 d__13 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string path; public Dictionary dict; private string 5__1; private string 5__2; private AudioType 5__3; private UnityWebRequest 5__4; private DownloadHandlerAudioClip 5__5; private AudioClip 5__6; private string 5__7; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public 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(); } } 5__1 = null; 5__2 = null; 5__4 = null; 5__5 = null; 5__6 = null; 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; } 5__1 = Path.GetExtension(path).ToLower(); if (5__1 != ".ogg" && 5__1 != ".wav" && 5__1 != ".mp3") { return false; } 5__2 = new Uri(path).AbsoluteUri; 5__3 = GetAudioType(5__1); 5__4 = UnityWebRequestMultimedia.GetAudioClip(5__2, 5__3); <>1__state = -3; 5__5 = (DownloadHandlerAudioClip)5__4.downloadHandler; 5__5.streamAudio = false; 5__5.compressed = false; 5__4.timeout = 15; <>2__current = 5__4.SendWebRequest(); <>1__state = 1; return true; case 1: <>1__state = -3; if ((int)5__4.result == 1) { DownloadHandlerAudioClip obj = 5__5; 5__6 = ((obj != null) ? obj.audioClip : null); if ((Object)(object)5__6 != (Object)null && 5__6.length > 0f && 5__6.samples > 0) { 5__7 = Path.GetFileNameWithoutExtension(path); ((Object)5__6).name = 5__7; dict[5__7] = 5__6; 5__7 = null; } else { Plugin.Log.LogWarning((object)("Invalid external clip: " + path)); } 5__6 = null; } else { Plugin.Log.LogWarning((object)("Failed to load external: " + path + " - " + 5__4.error)); } 5__5 = null; <>m__Finally1(); 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 (5__4 != null) { ((IDisposable)5__4).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__6 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public LoadTask task; private int 5__1; private int 5__2; private <>c__DisplayClass6_0 <>8__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public 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; 5__1 = Plugin.LoadRetryCount.Value; 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 (5__2 < 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; 5__2++; break; } if (5__2 < 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 _externalOverrideFolders = new HashSet(StringComparer.OrdinalIgnoreCase); [IteratorStateMachine(typeof(d__4))] public static IEnumerator InitializeAsync() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__4(0); } [IteratorStateMachine(typeof(d__5))] private static IEnumerator LoadEmbeddedResourcesParallel(string resourcePrefix, Dictionary singleDict, Dictionary> folderDict) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__5(0) { resourcePrefix = resourcePrefix, singleDict = singleDict, folderDict = folderDict }; } [IteratorStateMachine(typeof(d__6))] private static IEnumerator LoadSingleClipWithRetry(LoadTask task) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__6(0) { task = task }; } [IteratorStateMachine(typeof(d__7))] private static IEnumerator LoadAudioClipFromBytes(byte[] audioData, string clipName, Action onComplete) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__7(0) { audioData = audioData, clipName = clipName, onComplete = onComplete }; } [IteratorStateMachine(typeof(d__8))] private static IEnumerator LoadExternalAudioFilesAsync() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new 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> 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(d__12))] private static IEnumerator CollectAudioFilesAsync(string path, Dictionary singleDict, Dictionary> folderDict) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__12(0) { path = path, singleDict = singleDict, folderDict = folderDict }; } [IteratorStateMachine(typeof(d__13))] private static IEnumerator LoadExternalClipAsync(string path, Dictionary dict) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new 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(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(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(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(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(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(ambient, fieldName); if (value == null) { return; } List 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(); 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() : null); val2 = Plugin.CustomSFX["groundtorch"]; } else if (name.Contains("walltorch") && Plugin.CustomSFX.ContainsKey("walltorch")) { GameObject enabledObjectHigh = __instance.m_enabledObjectHigh; val = ((enabledObjectHigh != null) ? enabledObjectHigh.GetComponentInChildren() : null); if ((Object)(object)val == (Object)null) { GameObject enabledObject2 = __instance.m_enabledObject; val = ((enabledObject2 != null) ? enabledObject2.GetComponentInChildren() : 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() : 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() : null); val2 = Plugin.CustomSFX["bonfire"]; } else if (name.Contains("hearth") && Plugin.CustomSFX.ContainsKey("hearth")) { GameObject enabledObjectHigh4 = __instance.m_enabledObjectHigh; val = ((enabledObjectHigh4 != null) ? enabledObjectHigh4.GetComponentInChildren() : 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(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 ModEnabled; public static ConfigEntry MusicVolume; public static ConfigEntry AmbientVolume; public static ConfigEntry LocationVolMultiplier; public static ConfigEntry LoadRetryCount; public static ConfigEntry EnableFallback; public static Dictionary CustomMusic = new Dictionary(); public static Dictionary> CustomMusicList = new Dictionary>(); public static Dictionary CustomAmbient = new Dictionary(); public static Dictionary> CustomAmbientList = new Dictionary>(); public static Dictionary CustomSFX = new Dictionary(); public static Dictionary> CustomSFXList = new Dictionary>(); public static ManualLogSource Log { get; private set; } private void Awake() { instance = this; Log = ((BaseUnityPlugin)this).Logger; ModEnabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Enable this mod"); MusicVolume = ((BaseUnityPlugin)this).Config.Bind("General", "MusicVolume", 0.6f, "Music volume (0.0 - 1.0)"); AmbientVolume = ((BaseUnityPlugin)this).Config.Bind("General", "AmbientVolume", 0.3f, "Ambient volume (0.0 - 1.0)"); LocationVolMultiplier = ((BaseUnityPlugin)this).Config.Bind("General", "LocationVolumeMultiplier", 5f, "Location music volume multiplier"); LoadRetryCount = ((BaseUnityPlugin)this).Config.Bind("Advanced", "LoadRetryCount", 3, "Number of retries when audio loading fails"); EnableFallback = ((BaseUnityPlugin)this).Config.Bind("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 _fieldCache = new Dictionary(); private static readonly Dictionary _propertyCache = new Dictionary(); 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(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 _lowerCaseCache = new Dictionary(); private static readonly Dictionary _musicMappingCache = new Dictionary(); 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; } } }