Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of BetterJukebox v1.0.0
Mods/BetterJukebox.dll
Decompiled 2 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading.Tasks; using BetterJukebox; using BetterJukebox.Config; using BetterJukebox.Models; using BetterJukebox.Patches; using BetterJukebox.Services; using BetterJukebox.Utils; using HarmonyLib; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using ScheduleOne.ObjectScripts; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(Core), "BetterJukebox", "1.0.0", "Bars", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BetterJukebox")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("BetterJukebox")] [assembly: AssemblyTitle("BetterJukebox")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.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 BetterJukebox { public class Core : MelonMod { [CompilerGenerated] private sealed class <CheckSavedJukeboxes>d__20 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Core <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <CheckSavedJukeboxes>d__20(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>4__this._config.LogDebug("Waiting for scene to stabilize before checking for saved jukeboxes..."); <>2__current = (object)new WaitForSeconds(0.5f); <>1__state = 1; return true; case 1: <>1__state = -1; <>4__this._config.LogDebug("Beginning saved jukebox detection process..."); <>2__current = <>4__this._jukeboxPatcher.CheckForSavedJukeboxes(); <>1__state = 2; return true; case 2: <>1__state = -1; <>2__current = <>4__this._paginationManager.CheckForSavedJukeboxInterfaces(); <>1__state = 3; return true; case 3: <>1__state = -1; MelonCoroutines.Start(<>4__this.FinalJukeboxCheck()); 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 <FinalJukeboxCheck>d__21 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Core <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <FinalJukeboxCheck>d__21(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(5f); <>1__state = 1; return true; case 1: <>1__state = -1; <>4__this._config.LogDebug("Performing final jukebox check..."); <>2__current = <>4__this._jukeboxPatcher.ScanForJukeboxes(repeat: false); <>1__state = 2; return true; case 2: <>1__state = -1; <>4__this._paginationManager.CheckForUnpatchedInterfaces(); 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 <InitializationSequence>d__15 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Core <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <InitializationSequence>d__15(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; <>4__this._config.LogDebug("Starting initialization sequence"); MelonPreferences_Entry<bool> showLoadingIndicator = <>4__this._config.ShowLoadingIndicator; if (showLoadingIndicator != null && showLoadingIndicator.Value) { <>4__this.CreateLoadingIndicator(); } <>2__current = <>4__this._trackManager.LoadAudioFiles(); <>1__state = 1; return true; } case 1: <>1__state = -1; if ((Object)(object)<>4__this._loadingIndicator != (Object)null) { Object.Destroy((Object)(object)<>4__this._loadingIndicator); <>4__this._loadingIndicator = null; <>4__this._loadingText = null; } if (<>4__this._trackManager.TracksLoaded) { <>4__this._harmonyPatches.SetupPatches(); <>4__this._patchesApplied = true; <>4__this._config.LogDebug("Harmony patches applied"); } <>4__this._initialLoadComplete = true; <>4__this._config.LogDebug("Initial loading sequence complete"); <>2__current = <>4__this.ScanCurrentScene(); <>1__state = 2; return true; case 2: <>1__state = -1; 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 <ScanCurrentScene>d__18 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Core <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ScanCurrentScene>d__18(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 (<>4__this._trackManager.TracksLoaded) { <>4__this._config.LogDebug("Scanning current scene for jukeboxes"); <>2__current = <>4__this._jukeboxPatcher.ScanForJukeboxes(repeat: false); <>1__state = 1; return true; } break; case 1: <>1__state = -1; <>4__this._paginationManager.CheckForUnpatchedInterfaces(); 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 <UpdateLoadingProgress>d__17 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Core <>4__this; private int <percentage>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <UpdateLoadingProgress>d__17(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; break; } if ((Object)(object)<>4__this._loadingIndicator != (Object)null && (Object)(object)<>4__this._loadingText != (Object)null) { if (<>4__this._trackManager != null) { <percentage>5__1 = Mathf.RoundToInt(<>4__this._trackManager.LoadingProgress * 100f); ((TMP_Text)<>4__this._loadingText).text = $"BetterJukebox: Loading songs... {<percentage>5__1}%"; } <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 1; return 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 Core _instance; private ModConfig _config; private TrackManager _trackManager; private JukeboxPatcher _jukeboxPatcher; private PaginationManager _paginationManager; private HarmonyPatches _harmonyPatches; private object _scanCoroutine; private bool _patchesApplied; private bool _initialLoadComplete; private GameObject _loadingIndicator; private TextMeshProUGUI _loadingText; private string CustomSongsFolder => Path.Combine(MelonEnvironment.ModsDirectory, "BetterJukebox"); public override void OnInitializeMelon() { _instance = this; InitializeServices(); MelonCoroutines.Start(InitializationSequence()); } private void InitializeServices() { _config = new ModConfig(); _config.Initialize(); _trackManager = new TrackManager(_config, CustomSongsFolder); _jukeboxPatcher = new JukeboxPatcher(_config, _trackManager); _paginationManager = new PaginationManager(_config, _jukeboxPatcher); _harmonyPatches = new HarmonyPatches(_jukeboxPatcher, _paginationManager); } [IteratorStateMachine(typeof(<InitializationSequence>d__15))] private IEnumerator InitializationSequence() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <InitializationSequence>d__15(0) { <>4__this = this }; } private void CreateLoadingIndicator() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Expected O, but got Unknown //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) try { _loadingIndicator = new GameObject("BetterJukeboxLoadingIndicator"); Canvas val = _loadingIndicator.AddComponent<Canvas>(); val.renderMode = (RenderMode)0; val.sortingOrder = 9999; CanvasScaler val2 = _loadingIndicator.AddComponent<CanvasScaler>(); val2.uiScaleMode = (ScaleMode)1; val2.referenceResolution = new Vector2(1920f, 1080f); GameObject val3 = new GameObject("Panel"); val3.transform.SetParent(((Component)val).transform, false); RectTransform val4 = val3.AddComponent<RectTransform>(); val4.anchorMin = new Vector2(1f, 0f); val4.anchorMax = new Vector2(1f, 0f); val4.pivot = new Vector2(1f, 0f); val4.anchoredPosition = new Vector2(-10f, 10f); val4.sizeDelta = new Vector2(300f, 40f); Image val5 = val3.AddComponent<Image>(); ((Graphic)val5).color = new Color(0f, 0f, 0f, 0.7f); GameObject val6 = new GameObject("LoadingText"); val6.transform.SetParent(val3.transform, false); RectTransform val7 = val6.AddComponent<RectTransform>(); val7.anchorMin = Vector2.zero; val7.anchorMax = Vector2.one; val7.offsetMin = new Vector2(10f, 5f); val7.offsetMax = new Vector2(-10f, -5f); _loadingText = val6.AddComponent<TextMeshProUGUI>(); ((TMP_Text)_loadingText).text = "BetterJukebox: Loading songs..."; ((TMP_Text)_loadingText).fontSize = 14f; ((TMP_Text)_loadingText).alignment = (TextAlignmentOptions)513; ((Graphic)_loadingText).color = Color.white; MelonCoroutines.Start(UpdateLoadingProgress()); } catch (Exception ex) { _config.LogDebug("Failed to create loading indicator: " + ex.Message); } } [IteratorStateMachine(typeof(<UpdateLoadingProgress>d__17))] private IEnumerator UpdateLoadingProgress() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <UpdateLoadingProgress>d__17(0) { <>4__this = this }; } [IteratorStateMachine(typeof(<ScanCurrentScene>d__18))] private IEnumerator ScanCurrentScene() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <ScanCurrentScene>d__18(0) { <>4__this = this }; } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { _config.LogDebug($"Scene Initialized: {sceneName} (index: {buildIndex}). Scanning for jukeboxes..."); if (_scanCoroutine != null) { MelonCoroutines.Stop(_scanCoroutine); } if (_initialLoadComplete) { _scanCoroutine = MelonCoroutines.Start(CheckSavedJukeboxes()); } } [IteratorStateMachine(typeof(<CheckSavedJukeboxes>d__20))] private IEnumerator CheckSavedJukeboxes() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <CheckSavedJukeboxes>d__20(0) { <>4__this = this }; } [IteratorStateMachine(typeof(<FinalJukeboxCheck>d__21))] private IEnumerator FinalJukeboxCheck() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <FinalJukeboxCheck>d__21(0) { <>4__this = this }; } public override void OnLevelWasLoaded(int level) { if (_trackManager.TracksLoaded) { _config.LogDebug($"Level {level} was loaded. Running additional jukebox scan..."); MelonCoroutines.Start(_jukeboxPatcher.ScanForJukeboxes(repeat: false)); } } public override void OnUpdate() { if (_trackManager.TracksLoaded && !_patchesApplied) { _harmonyPatches.SetupPatches(); _patchesApplied = true; _config.LogDebug("Harmony patches applied during update"); MelonCoroutines.Start(ScanCurrentScene()); } if (_trackManager.TracksLoaded && Input.GetKeyDown((KeyCode)291)) { MelonLogger.Msg("Manually refreshing jukeboxes"); MelonCoroutines.Start(_jukeboxPatcher.ScanForJukeboxes(repeat: false)); _paginationManager.CheckForUnpatchedInterfaces(); } } public static void OnJukeboxAwake(Jukebox jukebox) { if (_instance != null && _instance._trackManager.TracksLoaded && (Object)(object)jukebox != (Object)null) { _instance._config.LogDebug($"Jukebox created/initialized: {((Object)jukebox).GetInstanceID()}"); _instance._jukeboxPatcher.PatchJukebox(jukebox); } } public static void OnSetupSongEntries(JukeboxInterface jukeboxInterface) { if (_instance != null && _instance._trackManager.TracksLoaded) { _instance._paginationManager.SetupPagination(jukeboxInterface); } } public static void OnJukeboxInterfaceOpen(JukeboxInterface jukeboxInterface) { if (_instance != null && _instance._trackManager.TracksLoaded) { _instance._paginationManager.UpdatePaginationUI(jukeboxInterface); } } public static bool OnRefreshSongEntries(JukeboxInterface jukeboxInterface) { if (_instance != null && _instance._trackManager.TracksLoaded) { _instance._paginationManager.RefreshPagination(jukeboxInterface); return false; } return true; } } } namespace BetterJukebox.Utils { public static class AudioUtils { public static AudioType GetAudioType(string filePath) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_002f: Unknown result type (might be due to invalid IL or missing references) if (filePath.EndsWith(".mp3", StringComparison.OrdinalIgnoreCase)) { return (AudioType)13; } if (filePath.EndsWith(".wav", StringComparison.OrdinalIgnoreCase)) { return (AudioType)20; } return (AudioType)0; } } } namespace BetterJukebox.Services { public class JukeboxPatcher { [CompilerGenerated] private sealed class <CheckForSavedJukeboxes>d__7 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public JukeboxPatcher <>4__this; private Jukebox[] <finalScan>5__1; private int <finalPatched>5__2; private int <attempt>5__3; private Jukebox[] <jukeboxes>5__4; private int <patchedThisAttempt>5__5; private Jukebox[] <>s__6; private int <>s__7; private Jukebox <jukebox>5__8; private Jukebox[] <>s__9; private int <>s__10; private Jukebox <jukebox>5__11; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <CheckForSavedJukeboxes>d__7(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <finalScan>5__1 = null; <jukeboxes>5__4 = null; <>s__6 = null; <jukebox>5__8 = null; <>s__9 = null; <jukebox>5__11 = null; <>1__state = -2; } private bool MoveNext() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Expected O, but got Unknown //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Expected O, but got Unknown //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(3f); <>1__state = 1; return true; case 1: <>1__state = -1; <attempt>5__3 = 0; goto IL_025d; case 2: <>1__state = -1; goto IL_0245; case 3: <>1__state = -1; goto IL_0245; case 4: { <>1__state = -1; <finalScan>5__1 = Object.FindObjectsOfType<Jukebox>(true); <finalPatched>5__2 = 0; <>s__9 = <finalScan>5__1; for (<>s__10 = 0; <>s__10 < <>s__9.Length; <>s__10++) { <jukebox>5__11 = <>s__9[<>s__10]; if ((Object)(object)<jukebox>5__11 != (Object)null && !<>4__this._patchedJukeboxIds.Contains(((Object)<jukebox>5__11).GetInstanceID())) { <>4__this.PatchJukebox(<jukebox>5__11); <finalPatched>5__2++; } <jukebox>5__11 = null; } <>s__9 = null; if (<finalPatched>5__2 > 0) { MelonLogger.Msg($"Final scan patched {<finalPatched>5__2} additional jukeboxes"); } return false; } IL_025d: if (<attempt>5__3 < 3) { <>4__this._config.LogDebug($"Checking for saved jukeboxes (attempt {<attempt>5__3 + 1}/3)..."); <jukeboxes>5__4 = Object.FindObjectsOfType<Jukebox>(true); <patchedThisAttempt>5__5 = 0; <>4__this._config.LogDebug($"Found {<jukeboxes>5__4.Length} jukeboxes in the scene"); <>s__6 = <jukeboxes>5__4; for (<>s__7 = 0; <>s__7 < <>s__6.Length; <>s__7++) { <jukebox>5__8 = <>s__6[<>s__7]; if ((Object)(object)<jukebox>5__8 != (Object)null && !<>4__this._patchedJukeboxIds.Contains(((Object)<jukebox>5__8).GetInstanceID())) { <>4__this.PatchJukebox(<jukebox>5__8); <patchedThisAttempt>5__5++; } <jukebox>5__8 = null; } <>s__6 = null; if (<patchedThisAttempt>5__5 > 0) { MelonLogger.Msg($"Patched {<patchedThisAttempt>5__5} saved jukeboxes in attempt {<attempt>5__3 + 1}"); } if (<patchedThisAttempt>5__5 == 0 && <attempt>5__3 < 2) { <>4__this._config.LogDebug("No new jukeboxes found, waiting to try again..."); <>2__current = (object)new WaitForSeconds(2f); <>1__state = 2; return true; } if (<patchedThisAttempt>5__5 > 0) { <>2__current = (object)new WaitForSeconds(1f); <>1__state = 3; return true; } } MelonLogger.Msg($"Finished checking for saved jukeboxes, total patched: {<>4__this._patchedJukeboxIds.Count}"); <>2__current = (object)new WaitForSeconds(5f); <>1__state = 4; return true; IL_0245: <jukeboxes>5__4 = null; <attempt>5__3++; goto IL_025d; } } 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 <ScanForJukeboxes>d__5 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public bool repeat; public JukeboxPatcher <>4__this; private int <scanAttempts>5__1; private int <maxScanAttempts>5__2; private int <jukeboxesFound>5__3; private int <jukeboxesPatched>5__4; private Jukebox[] <jukeboxes>5__5; private Jukebox[] <>s__6; private int <>s__7; private Jukebox <jukebox>5__8; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ScanForJukeboxes>d__5(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <jukeboxes>5__5 = null; <>s__6 = null; <jukebox>5__8 = null; <>1__state = -2; } private bool MoveNext() { //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (<>4__this._trackManager.CustomTracks.Count == 0) { return false; } <scanAttempts>5__1 = 0; <maxScanAttempts>5__2 = ((!repeat) ? 1 : 10); <jukeboxesFound>5__3 = 0; <jukeboxesPatched>5__4 = 0; break; case 1: <>1__state = -1; <jukeboxes>5__5 = null; break; } if (<scanAttempts>5__1 < <maxScanAttempts>5__2) { <scanAttempts>5__1++; <>4__this._config.LogDebug($"Scanning for jukeboxes (attempt {<scanAttempts>5__1}/{<maxScanAttempts>5__2})..."); <jukeboxes>5__5 = Object.FindObjectsOfType<Jukebox>(true); <jukeboxesFound>5__3 = <jukeboxes>5__5.Length; if (<jukeboxes>5__5.Length != 0) { <>4__this._config.LogDebug($"Found {<jukeboxes>5__5.Length} jukeboxes"); <>s__6 = <jukeboxes>5__5; for (<>s__7 = 0; <>s__7 < <>s__6.Length; <>s__7++) { <jukebox>5__8 = <>s__6[<>s__7]; if ((Object)(object)<jukebox>5__8 != (Object)null && !<>4__this._patchedJukeboxIds.Contains(((Object)<jukebox>5__8).GetInstanceID())) { <>4__this.PatchJukebox(<jukebox>5__8); <jukeboxesPatched>5__4++; } <jukebox>5__8 = null; } <>s__6 = null; if (<jukeboxesPatched>5__4 > 0) { MelonLogger.Msg($"Patched {<jukeboxesPatched>5__4} new jukeboxes"); } } else { <>4__this._config.LogDebug("No jukeboxes found in the current scene"); } if (<jukeboxesFound>5__3 <= 0 || <jukeboxesPatched>5__4 != <jukeboxesFound>5__3) { <>2__current = (object)new WaitForSeconds(5f); <>1__state = 1; return true; } } if ((<jukeboxesFound>5__3 == 0) & repeat) { MelonLogger.Warning("No jukeboxes found after multiple attempts. Try pressing F10 when near a jukebox for manual refresh."); } 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 readonly ModConfig _config; private readonly TrackManager _trackManager; private readonly HashSet<int> _patchedJukeboxIds = new HashSet<int>(); public JukeboxPatcher(ModConfig config, TrackManager trackManager) { _config = config; _trackManager = trackManager; } public bool IsJukeboxPatched(int jukeboxId) { return _patchedJukeboxIds.Contains(jukeboxId); } [IteratorStateMachine(typeof(<ScanForJukeboxes>d__5))] public IEnumerator ScanForJukeboxes(bool repeat = true) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <ScanForJukeboxes>d__5(0) { <>4__this = this, repeat = repeat }; } public void PatchJukebox(Jukebox jukebox) { //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Expected O, but got Unknown if ((Object)(object)jukebox == (Object)null || _trackManager.CustomTracks.Count == 0) { return; } int instanceID = ((Object)jukebox).GetInstanceID(); if (_patchedJukeboxIds.Contains(instanceID)) { _config.LogDebug($"Jukebox {instanceID} already patched, skipping"); return; } try { Track[] trackList = jukebox.TrackList; if (trackList == null) { MelonLogger.Warning($"Jukebox {instanceID} has null TrackList, skipping"); return; } Track[] array = (Track[])(object)new Track[trackList.Length + _trackManager.CustomTracks.Count]; Array.Copy(trackList, array, trackList.Length); for (int i = 0; i < _trackManager.CustomTracks.Count; i++) { array[trackList.Length + i] = _trackManager.CustomTracks[i]; } jukebox.TrackList = array; FieldInfo field = typeof(Jukebox).GetField("_jukeboxState", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { JukeboxState val = (JukeboxState)field.GetValue(jukebox); if (val != null && val.TrackOrder != null) { int[] array2 = new int[array.Length]; for (int j = 0; j < array2.Length; j++) { array2[j] = j; } val.TrackOrder = array2; } } _patchedJukeboxIds.Add(instanceID); MelonLogger.Msg($"Successfully patched jukebox {instanceID} with {_trackManager.CustomTracks.Count} custom tracks"); } catch (Exception ex) { MelonLogger.Error($"Failed to patch jukebox {instanceID}: {ex.Message}"); MelonLogger.Error(ex.StackTrace); } } [IteratorStateMachine(typeof(<CheckForSavedJukeboxes>d__7))] public IEnumerator CheckForSavedJukeboxes() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <CheckForSavedJukeboxes>d__7(0) { <>4__this = this }; } } public class PaginationManager { [CompilerGenerated] private sealed class <AnimateButtonScale>d__10 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Transform buttonTransform; public PaginationManager <>4__this; private Vector3 <originalScale>5__1; private float <duration>5__2; private float <elapsed>5__3; private float <t>5__4; private float <t>5__5; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <AnimateButtonScale>d__10(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; <originalScale>5__1 = buttonTransform.localScale; <duration>5__2 = 0.1f; <elapsed>5__3 = 0f; goto IL_00c5; case 1: <>1__state = -1; goto IL_00c5; case 2: { <>1__state = -1; break; } IL_00c5: if (<elapsed>5__3 < <duration>5__2) { <t>5__4 = <elapsed>5__3 / <duration>5__2; buttonTransform.localScale = Vector3.Lerp(<originalScale>5__1, <originalScale>5__1 * 0.9f, <t>5__4); <elapsed>5__3 += Time.deltaTime; <>2__current = null; <>1__state = 1; return true; } <elapsed>5__3 = 0f; break; } if (<elapsed>5__3 < <duration>5__2) { <t>5__5 = <elapsed>5__3 / <duration>5__2; buttonTransform.localScale = Vector3.Lerp(<originalScale>5__1 * 0.9f, <originalScale>5__1, <t>5__5); <elapsed>5__3 += Time.deltaTime; <>2__current = null; <>1__state = 2; return true; } buttonTransform.localScale = <originalScale>5__1; 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 <CheckForSavedJukeboxInterfaces>d__13 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public PaginationManager <>4__this; private JukeboxInterface[] <finalInterfaces>5__1; private int <finalCount>5__2; private int <attempt>5__3; private JukeboxInterface[] <interfaces>5__4; private int <patchedCount>5__5; private JukeboxInterface[] <>s__6; private int <>s__7; private JukeboxInterface <jukeboxInterface>5__8; private int <jukeboxId>5__9; private JukeboxInterface[] <>s__10; private int <>s__11; private JukeboxInterface <jukeboxInterface>5__12; private int <jukeboxId>5__13; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <CheckForSavedJukeboxInterfaces>d__13(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <finalInterfaces>5__1 = null; <interfaces>5__4 = null; <>s__6 = null; <jukeboxInterface>5__8 = null; <>s__10 = null; <jukeboxInterface>5__12 = null; <>1__state = -2; } private bool MoveNext() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Expected O, but got Unknown //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Expected O, but got Unknown //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(2f); <>1__state = 1; return true; case 1: <>1__state = -1; <attempt>5__3 = 0; goto IL_0317; case 2: <>1__state = -1; goto IL_02fd; case 3: <>1__state = -1; goto IL_02fd; case 4: { <>1__state = -1; <finalInterfaces>5__1 = Object.FindObjectsOfType<JukeboxInterface>(true); <finalCount>5__2 = 0; <>s__10 = <finalInterfaces>5__1; for (<>s__11 = 0; <>s__11 < <>s__10.Length; <>s__11++) { <jukeboxInterface>5__12 = <>s__10[<>s__11]; if ((Object)(object)<jukeboxInterface>5__12 != (Object)null && (Object)(object)<jukeboxInterface>5__12.Jukebox != (Object)null && !<>4__this._paginationData.ContainsKey(<jukeboxInterface>5__12)) { <jukeboxId>5__13 = ((Object)<jukeboxInterface>5__12.Jukebox).GetInstanceID(); if (<>4__this._jukeboxPatcher.IsJukeboxPatched(<jukeboxId>5__13)) { <>4__this.RecreateSongEntries(<jukeboxInterface>5__12); <>4__this.SetupPagination(<jukeboxInterface>5__12); <finalCount>5__2++; } } <jukeboxInterface>5__12 = null; } <>s__10 = null; if (<finalCount>5__2 > 0) { MelonLogger.Msg($"Final scan applied pagination to {<finalCount>5__2} additional interfaces"); } return false; } IL_0317: if (<attempt>5__3 < 3) { <>4__this._config.LogDebug($"Checking for saved jukebox interfaces (attempt {<attempt>5__3 + 1}/3)..."); <interfaces>5__4 = Object.FindObjectsOfType<JukeboxInterface>(true); <patchedCount>5__5 = 0; <>4__this._config.LogDebug($"Found {<interfaces>5__4.Length} jukebox interfaces in the scene"); <>s__6 = <interfaces>5__4; for (<>s__7 = 0; <>s__7 < <>s__6.Length; <>s__7++) { <jukeboxInterface>5__8 = <>s__6[<>s__7]; if ((Object)(object)<jukeboxInterface>5__8 != (Object)null && (Object)(object)<jukeboxInterface>5__8.Jukebox != (Object)null) { <jukeboxId>5__9 = ((Object)<jukeboxInterface>5__8.Jukebox).GetInstanceID(); if (<>4__this._paginationData.ContainsKey(<jukeboxInterface>5__8)) { <>4__this._config.LogDebug($"Interface for jukebox {<jukeboxId>5__9} already has pagination"); continue; } if (<>4__this._jukeboxPatcher.IsJukeboxPatched(<jukeboxId>5__9)) { <>4__this._config.LogDebug($"Setting up pagination for patched jukebox {<jukeboxId>5__9}"); <>4__this.RecreateSongEntries(<jukeboxInterface>5__8); <>4__this.SetupPagination(<jukeboxInterface>5__8); <patchedCount>5__5++; } else { <>4__this._config.LogDebug($"Found interface for jukebox {<jukeboxId>5__9} but it's not patched yet"); } } <jukeboxInterface>5__8 = null; } <>s__6 = null; if (<patchedCount>5__5 > 0) { MelonLogger.Msg($"Applied pagination to {<patchedCount>5__5} jukebox interfaces in attempt {<attempt>5__3 + 1}"); <>2__current = (object)new WaitForSeconds(1f); <>1__state = 2; return true; } if (<attempt>5__3 < 2) { <>4__this._config.LogDebug("No interfaces to update, waiting to try again..."); <>2__current = (object)new WaitForSeconds(2f); <>1__state = 3; return true; } } <>2__current = (object)new WaitForSeconds(4f); <>1__state = 4; return true; IL_02fd: <interfaces>5__4 = null; <attempt>5__3++; goto IL_0317; } } 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 const int TRACKS_PER_PAGE = 27; private readonly ModConfig _config; private readonly JukeboxPatcher _jukeboxPatcher; private readonly Dictionary<JukeboxInterface, PaginationData> _paginationData = new Dictionary<JukeboxInterface, PaginationData>(); public PaginationManager(ModConfig config, JukeboxPatcher jukeboxPatcher) { _config = config; _jukeboxPatcher = jukeboxPatcher; } public void SetupPagination(JukeboxInterface jukeboxInterface) { //IL_0379: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Expected O, but got Unknown //IL_03c4: Unknown result type (might be due to invalid IL or missing references) //IL_03ce: Expected O, but got Unknown //IL_03fb: Unknown result type (might be due to invalid IL or missing references) //IL_0429: Unknown result type (might be due to invalid IL or missing references) //IL_04a2: Unknown result type (might be due to invalid IL or missing references) //IL_04a7: Unknown result type (might be due to invalid IL or missing references) //IL_04bf: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Unknown result type (might be due to invalid IL or missing references) //IL_0501: Unknown result type (might be due to invalid IL or missing references) //IL_0522: Unknown result type (might be due to invalid IL or missing references) //IL_0543: Unknown result type (might be due to invalid IL or missing references) //IL_056a: Unknown result type (might be due to invalid IL or missing references) //IL_05a4: Unknown result type (might be due to invalid IL or missing references) //IL_05ab: Expected O, but got Unknown //IL_05cf: Unknown result type (might be due to invalid IL or missing references) //IL_05dc: Unknown result type (might be due to invalid IL or missing references) //IL_05e9: Unknown result type (might be due to invalid IL or missing references) //IL_063a: Unknown result type (might be due to invalid IL or missing references) //IL_0677: Unknown result type (might be due to invalid IL or missing references) //IL_067e: Expected O, but got Unknown //IL_06a3: Unknown result type (might be due to invalid IL or missing references) //IL_0704: Unknown result type (might be due to invalid IL or missing references) //IL_074f: Unknown result type (might be due to invalid IL or missing references) //IL_0759: Expected O, but got Unknown //IL_0786: Unknown result type (might be due to invalid IL or missing references) //IL_07b4: Unknown result type (might be due to invalid IL or missing references) //IL_082d: Unknown result type (might be due to invalid IL or missing references) //IL_0832: Unknown result type (might be due to invalid IL or missing references) //IL_084a: Unknown result type (might be due to invalid IL or missing references) //IL_086b: Unknown result type (might be due to invalid IL or missing references) //IL_088c: Unknown result type (might be due to invalid IL or missing references) //IL_08ad: Unknown result type (might be due to invalid IL or missing references) //IL_08ce: Unknown result type (might be due to invalid IL or missing references) //IL_08f5: Unknown result type (might be due to invalid IL or missing references) //IL_092f: Unknown result type (might be due to invalid IL or missing references) //IL_0936: Expected O, but got Unknown //IL_095a: Unknown result type (might be due to invalid IL or missing references) //IL_0967: Unknown result type (might be due to invalid IL or missing references) //IL_0974: Unknown result type (might be due to invalid IL or missing references) //IL_09c5: Unknown result type (might be due to invalid IL or missing references) //IL_0a06: Unknown result type (might be due to invalid IL or missing references) //IL_0a10: Expected O, but got Unknown //IL_0a23: Unknown result type (might be due to invalid IL or missing references) //IL_0a2d: Expected O, but got Unknown //IL_0458: Unknown result type (might be due to invalid IL or missing references) //IL_046f: Unknown result type (might be due to invalid IL or missing references) //IL_07e3: Unknown result type (might be due to invalid IL or missing references) //IL_07fa: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Expected O, but got Unknown //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)jukeboxInterface == (Object)null || (Object)(object)jukeboxInterface.Jukebox == (Object)null || (Object)(object)jukeboxInterface.EntryContainer == (Object)null) { _config.LogDebug("Cannot setup pagination, interface or required components are null"); return; } int num = jukeboxInterface.Jukebox.TrackList.Length; if (num <= 27) { _config.LogDebug($"No pagination needed, track count ({num}) <= tracks per page ({27})"); return; } if (_paginationData.ContainsKey(jukeboxInterface)) { if ((Object)(object)_paginationData[jukeboxInterface].PaginationUI != (Object)null) { Object.Destroy((Object)(object)_paginationData[jukeboxInterface].PaginationUI); } _paginationData.Remove(jukeboxInterface); } PaginationData paginationData = new PaginationData { CurrentPage = 0, TotalPages = Mathf.CeilToInt((float)num / 27f), OriginalSongEntries = new List<RectTransform>() }; FieldInfo fieldInfo = AccessTools.Field(typeof(JukeboxInterface), "songEntries"); if (fieldInfo != null) { paginationData.OriginalSongEntries = new List<RectTransform>(fieldInfo.GetValue(jukeboxInterface) as List<RectTransform>); } Canvas canvas = jukeboxInterface.Canvas; if ((Object)(object)canvas == (Object)null) { _config.LogDebug("Canvas is null, cannot create pagination UI"); return; } GameObject val = new GameObject("PaginationContainer", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(((Component)canvas).transform, false); RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0.5f, 0f); component.anchorMax = new Vector2(0.5f, 0f); component.pivot = new Vector2(0.5f, 0f); component.anchoredPosition = new Vector2(0f, 445f); component.sizeDelta = new Vector2(300f, 40f); Image val2 = val.AddComponent<Image>(); ((Graphic)val2).color = new Color(0.1f, 0.1f, 0.1f, 0.8f); try { Type type = Type.GetType("UnityEngine.UI.Extensions.UICornerCut, Unity.UI.Extensions"); if (type != null) { Component obj = val.AddComponent(type); MonoBehaviour obj2 = (MonoBehaviour)(object)((obj is MonoBehaviour) ? obj : null); FieldInfo field = type.GetField("cornerRadius"); if (field != null) { field.SetValue(obj2, 10f); } } } catch (Exception) { _config.LogDebug("Couldn't add rounded corners to pagination UI"); } HorizontalLayoutGroup val3 = val.AddComponent<HorizontalLayoutGroup>(); ((LayoutGroup)val3).childAlignment = (TextAnchor)4; ((HorizontalOrVerticalLayoutGroup)val3).spacing = 10f; ((LayoutGroup)val3).padding = new RectOffset(10, 10, 5, 5); GameObject prevButton = new GameObject("PrevButton", new Type[4] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button) }); prevButton.transform.SetParent((Transform)(object)component, false); RectTransform component2 = prevButton.GetComponent<RectTransform>(); component2.sizeDelta = new Vector2(40f, 40f); Image component3 = prevButton.GetComponent<Image>(); ((Graphic)component3).color = new Color(0.15f, 0.15f, 0.15f, 0.9f); try { Outline val4 = prevButton.AddComponent<Outline>(); ((Shadow)val4).effectColor = new Color(0.3f, 0.3f, 0.3f, 0.5f); ((Shadow)val4).effectDistance = new Vector2(1f, -1f); } catch (Exception) { _config.LogDebug("Couldn't add rounded style to pagination buttons"); } Button component4 = prevButton.GetComponent<Button>(); ColorBlock colors = ((Selectable)component4).colors; ((ColorBlock)(ref colors)).normalColor = new Color(0.15f, 0.15f, 0.15f, 0.9f); ((ColorBlock)(ref colors)).highlightedColor = new Color(0.25f, 0.25f, 0.25f, 1f); ((ColorBlock)(ref colors)).pressedColor = new Color(0.1f, 0.1f, 0.1f, 1f); ((ColorBlock)(ref colors)).selectedColor = new Color(0.25f, 0.25f, 0.25f, 1f); ((ColorBlock)(ref colors)).disabledColor = new Color(0.15f, 0.15f, 0.15f, 0.5f); ((ColorBlock)(ref colors)).colorMultiplier = 1.2f; ((ColorBlock)(ref colors)).fadeDuration = 0.1f; ((Selectable)component4).colors = colors; GameObject val5 = new GameObject("Text", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI) }); val5.transform.SetParent(prevButton.transform, false); RectTransform component5 = val5.GetComponent<RectTransform>(); component5.anchorMin = Vector2.zero; component5.anchorMax = Vector2.one; component5.sizeDelta = Vector2.zero; TextMeshProUGUI component6 = val5.GetComponent<TextMeshProUGUI>(); ((TMP_Text)component6).text = "<"; ((TMP_Text)component6).fontSize = 24f; ((TMP_Text)component6).alignment = (TextAlignmentOptions)514; ((Graphic)component6).color = new Color(0.8f, 0.8f, 0.8f, 1f); GameObject val6 = new GameObject("PageText", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI) }); val6.transform.SetParent((Transform)(object)component, false); RectTransform component7 = val6.GetComponent<RectTransform>(); component7.sizeDelta = new Vector2(150f, 40f); TextMeshProUGUI component8 = val6.GetComponent<TextMeshProUGUI>(); ((TMP_Text)component8).text = $"Page 1/{paginationData.TotalPages}"; ((TMP_Text)component8).fontSize = 18f; ((TMP_Text)component8).alignment = (TextAlignmentOptions)514; ((Graphic)component8).color = new Color(0.8f, 0.8f, 0.8f, 1f); GameObject nextButton = new GameObject("NextButton", new Type[4] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button) }); nextButton.transform.SetParent((Transform)(object)component, false); RectTransform component9 = nextButton.GetComponent<RectTransform>(); component9.sizeDelta = new Vector2(40f, 40f); Image component10 = nextButton.GetComponent<Image>(); ((Graphic)component10).color = new Color(0.15f, 0.15f, 0.15f, 0.9f); try { Outline val7 = nextButton.AddComponent<Outline>(); ((Shadow)val7).effectColor = new Color(0.3f, 0.3f, 0.3f, 0.5f); ((Shadow)val7).effectDistance = new Vector2(1f, -1f); } catch (Exception) { _config.LogDebug("Couldn't add rounded style to next button"); } Button component11 = nextButton.GetComponent<Button>(); ColorBlock colors2 = ((Selectable)component11).colors; ((ColorBlock)(ref colors2)).normalColor = new Color(0.15f, 0.15f, 0.15f, 0.9f); ((ColorBlock)(ref colors2)).highlightedColor = new Color(0.25f, 0.25f, 0.25f, 1f); ((ColorBlock)(ref colors2)).pressedColor = new Color(0.1f, 0.1f, 0.1f, 1f); ((ColorBlock)(ref colors2)).selectedColor = new Color(0.25f, 0.25f, 0.25f, 1f); ((ColorBlock)(ref colors2)).disabledColor = new Color(0.15f, 0.15f, 0.15f, 0.5f); ((ColorBlock)(ref colors2)).colorMultiplier = 1.2f; ((ColorBlock)(ref colors2)).fadeDuration = 0.1f; ((Selectable)component11).colors = colors2; GameObject val8 = new GameObject("Text", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI) }); val8.transform.SetParent(nextButton.transform, false); RectTransform component12 = val8.GetComponent<RectTransform>(); component12.anchorMin = Vector2.zero; component12.anchorMax = Vector2.one; component12.sizeDelta = Vector2.zero; TextMeshProUGUI component13 = val8.GetComponent<TextMeshProUGUI>(); ((TMP_Text)component13).text = ">"; ((TMP_Text)component13).fontSize = 24f; ((TMP_Text)component13).alignment = (TextAlignmentOptions)514; ((Graphic)component13).color = new Color(0.8f, 0.8f, 0.8f, 1f); paginationData.PaginationUI = val; paginationData.PrevButton = component4; paginationData.NextButton = component11; paginationData.PageText = component8; ((UnityEvent)paginationData.PrevButton.onClick).AddListener((UnityAction)delegate { StartButtonAnimation(prevButton.transform); ChangePage(jukeboxInterface, -1); }); ((UnityEvent)paginationData.NextButton.onClick).AddListener((UnityAction)delegate { StartButtonAnimation(nextButton.transform); ChangePage(jukeboxInterface, 1); }); _paginationData[jukeboxInterface] = paginationData; UpdatePaginationUI(jukeboxInterface); MelonLogger.Msg($"Pagination setup completed for jukebox: {((Object)jukeboxInterface.Jukebox).GetInstanceID()} with {paginationData.TotalPages} pages"); } catch (Exception ex4) { MelonLogger.Error("Failed to setup pagination: " + ex4.Message); MelonLogger.Error(ex4.StackTrace); } } public void ChangePage(JukeboxInterface jukeboxInterface, int delta) { if (_paginationData.ContainsKey(jukeboxInterface)) { PaginationData paginationData = _paginationData[jukeboxInterface]; int num = paginationData.CurrentPage + delta; if (num >= 0 && num < paginationData.TotalPages) { paginationData.CurrentPage = num; UpdatePaginationUI(jukeboxInterface); RefreshPagination(jukeboxInterface); } } } public void UpdatePaginationUI(JukeboxInterface jukeboxInterface) { if (_paginationData.ContainsKey(jukeboxInterface)) { PaginationData paginationData = _paginationData[jukeboxInterface]; ((TMP_Text)paginationData.PageText).text = $"Page {paginationData.CurrentPage + 1}/{paginationData.TotalPages}"; ((Selectable)paginationData.PrevButton).interactable = paginationData.CurrentPage > 0; ((Selectable)paginationData.NextButton).interactable = paginationData.CurrentPage < paginationData.TotalPages - 1; } } public void RefreshPagination(JukeboxInterface jukeboxInterface) { if (!_paginationData.ContainsKey(jukeboxInterface)) { return; } PaginationData paginationData = _paginationData[jukeboxInterface]; FieldInfo fieldInfo = AccessTools.Field(typeof(JukeboxInterface), "songEntries"); if (fieldInfo == null || !(fieldInfo.GetValue(jukeboxInterface) is List<RectTransform> list)) { return; } Track[] trackList = jukeboxInterface.Jukebox.TrackList; int num = paginationData.CurrentPage * 27; int num2 = Mathf.Min(num + 27, trackList.Length); foreach (RectTransform item in list) { if ((Object)(object)item != (Object)null) { ((Component)item).gameObject.SetActive(false); } } for (int i = num; i < num2; i++) { if (i < list.Count && (Object)(object)list[i] != (Object)null) { ((Component)list[i]).gameObject.SetActive(true); Track val = trackList[i]; if (jukeboxInterface.Jukebox.currentTrack == val && jukeboxInterface.Jukebox.IsPlaying) { ((Component)((Transform)list[i]).Find("PlayPause/Icon")).GetComponent<Image>().sprite = jukeboxInterface.SongEntryPauseSprite; } else { ((Component)((Transform)list[i]).Find("PlayPause/Icon")).GetComponent<Image>().sprite = jukeboxInterface.SongEntryPlaySprite; } } } } private void StartButtonAnimation(Transform buttonTransform) { MelonCoroutines.Start(AnimateButtonScale(buttonTransform)); } [IteratorStateMachine(typeof(<AnimateButtonScale>d__10))] private IEnumerator AnimateButtonScale(Transform buttonTransform) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <AnimateButtonScale>d__10(0) { <>4__this = this, buttonTransform = buttonTransform }; } public void RecreateSongEntries(JukeboxInterface jukeboxInterface) { //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Expected O, but got Unknown if ((Object)(object)jukeboxInterface == (Object)null || (Object)(object)jukeboxInterface.Jukebox == (Object)null || (Object)(object)jukeboxInterface.EntryContainer == (Object)null) { return; } try { FieldInfo fieldInfo = AccessTools.Field(typeof(JukeboxInterface), "songEntries"); if (fieldInfo == null) { _config.LogDebug("Cannot access songEntries field"); return; } if (!(fieldInfo.GetValue(jukeboxInterface) is List<RectTransform> list)) { _config.LogDebug("Song entries list is null"); return; } Track[] trackList = jukeboxInterface.Jukebox.TrackList; int count = list.Count; int num = trackList.Length; _config.LogDebug($"Recreating song entries: Current UI entries: {count}, Total tracks: {num}"); foreach (RectTransform item in list) { if ((Object)(object)item != (Object)null) { Object.Destroy((Object)(object)((Component)item).gameObject); } } list.Clear(); for (int i = 0; i < trackList.Length; i++) { Track val = trackList[i]; if (val == null) { continue; } GameObject entry = Object.Instantiate<GameObject>(jukeboxInterface.SongEntryPrefab, (Transform)(object)jukeboxInterface.EntryContainer); ((TMP_Text)((Component)entry.transform.Find("Name")).GetComponent<TextMeshProUGUI>()).text = val.TrackName; ((TMP_Text)((Component)entry.transform.Find("Artist")).GetComponent<TextMeshProUGUI>()).text = val.ArtistName; entry.transform.SetAsLastSibling(); int num2 = i; ((UnityEvent)((Component)entry.transform.Find("PlayPause")).GetComponent<Button>().onClick).AddListener((UnityAction)delegate { MethodInfo method = typeof(JukeboxInterface).GetMethod("SongEntryClicked", BindingFlags.Instance | BindingFlags.Public); if (method != null) { method.Invoke(jukeboxInterface, new object[1] { entry.GetComponent<RectTransform>() }); } }); list.Add(entry.GetComponent<RectTransform>()); } fieldInfo.SetValue(jukeboxInterface, list); MelonLogger.Msg($"Successfully recreated {list.Count} song entries for jukebox {((Object)jukeboxInterface.Jukebox).GetInstanceID()}"); } catch (Exception ex) { MelonLogger.Error("Failed to recreate song entries: " + ex.Message); MelonLogger.Error(ex.StackTrace); } } public void CheckForUnpatchedInterfaces() { JukeboxInterface[] array = Object.FindObjectsOfType<JukeboxInterface>(); bool flag = false; JukeboxInterface[] array2 = array; foreach (JukeboxInterface val in array2) { if ((Object)(object)val != (Object)null && !_paginationData.ContainsKey(val)) { Jukebox jukebox = val.Jukebox; int num = ((jukebox != null) ? ((Object)jukebox).GetInstanceID() : (-1)); if (num != -1 && _jukeboxPatcher.IsJukeboxPatched(num)) { _config.LogDebug($"Found JukeboxInterface for already patched jukebox {num}, recreating song entries and setting up pagination"); RecreateSongEntries(val); SetupPagination(val); flag = true; } } } if (flag) { MelonLogger.Msg("Applied pagination to newly found jukebox interfaces from saved game"); } } [IteratorStateMachine(typeof(<CheckForSavedJukeboxInterfaces>d__13))] public IEnumerator CheckForSavedJukeboxInterfaces() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <CheckForSavedJukeboxInterfaces>d__13(0) { <>4__this = this }; } } public class TrackManager { [CompilerGenerated] private sealed class <LoadAudioFiles>d__16 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public TrackManager <>4__this; private string[] <audioFiles>5__1; private int <totalFiles>5__2; private int <completedFiles>5__3; private int <batchSize>5__4; private int <batchStart>5__5; private int <currentBatchSize>5__6; private List<Task<Track>> <batchTasks>5__7; private int <i>5__8; private int <fileIndex>5__9; private string <audioFile>5__10; private List<Task<Track>>.Enumerator <>s__11; private Task<Track> <task>5__12; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <LoadAudioFiles>d__16(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <audioFiles>5__1 = null; <batchTasks>5__7 = null; <audioFile>5__10 = null; <>s__11 = default(List<Task<Track>>.Enumerator); <task>5__12 = null; <>1__state = -2; } private bool MoveNext() { //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (!<>4__this._config.EnableCustomTracks.Value) { MelonLogger.Msg("Custom tracks are disabled in settings"); <>4__this.TracksLoaded = true; return false; } if (!Directory.Exists(<>4__this._customSongsFolder)) { Directory.CreateDirectory(<>4__this._customSongsFolder); MelonLogger.Msg("Created custom songs folder at " + <>4__this._customSongsFolder); MelonLogger.Msg("Place your .mp3 or .wav files in this folder"); <>4__this.TracksLoaded = true; return false; } <audioFiles>5__1 = (from file in Directory.GetFiles(<>4__this._customSongsFolder) where file.EndsWith(".mp3", StringComparison.OrdinalIgnoreCase) || file.EndsWith(".wav", StringComparison.OrdinalIgnoreCase) select file).ToArray(); if (<audioFiles>5__1.Length == 0) { MelonLogger.Msg("No audio files found in " + <>4__this._customSongsFolder); <>4__this.TracksLoaded = true; return false; } MelonLogger.Msg($"Found {<audioFiles>5__1.Length} audio files to load"); <totalFiles>5__2 = <audioFiles>5__1.Length; <completedFiles>5__3 = 0; <batchSize>5__4 = Mathf.Clamp(<>4__this._config.LoadingBatchSize?.Value ?? 5, 1, 10); <batchStart>5__5 = 0; goto IL_038f; case 1: <>1__state = -1; goto IL_0266; case 2: { <>1__state = -1; <batchTasks>5__7 = null; <batchStart>5__5 += <batchSize>5__4; goto IL_038f; } IL_0266: if (<batchTasks>5__7.Any((Task<Track> t) => !t.IsCompleted)) { <>2__current = null; <>1__state = 1; return true; } <>s__11 = <batchTasks>5__7.GetEnumerator(); try { while (<>s__11.MoveNext()) { <task>5__12 = <>s__11.Current; if (<task>5__12.Result != null) { <>4__this.CustomTracks.Add(<task>5__12.Result); } <completedFiles>5__3++; <>4__this.LoadingProgress = (float)<completedFiles>5__3 / (float)<totalFiles>5__2; <task>5__12 = null; } } finally { ((IDisposable)<>s__11).Dispose(); } <>s__11 = default(List<Task<Track>>.Enumerator); <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 2; return true; IL_038f: if (<batchStart>5__5 < <totalFiles>5__2) { <currentBatchSize>5__6 = Math.Min(<batchSize>5__4, <totalFiles>5__2 - <batchStart>5__5); <batchTasks>5__7 = new List<Task<Track>>(); <i>5__8 = 0; while (<i>5__8 < <currentBatchSize>5__6) { <fileIndex>5__9 = <batchStart>5__5 + <i>5__8; <audioFile>5__10 = <audioFiles>5__1[<fileIndex>5__9]; <batchTasks>5__7.Add(<>4__this.LoadTrackAsync(<audioFile>5__10)); <audioFile>5__10 = null; <i>5__8++; } goto IL_0266; } <>4__this.TracksLoaded = true; MelonLogger.Msg($"Successfully loaded {<>4__this.CustomTracks.Count} custom tracks"); 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 readonly ModConfig _config; private readonly string _customSongsFolder; private const float BATCH_INTERVAL = 0.1f; public List<Track> CustomTracks { get; private set; } = new List<Track>(); public bool TracksLoaded { get; private set; } public float LoadingProgress { get; private set; } public TrackManager(ModConfig config, string customSongsFolder) { _config = config; _customSongsFolder = customSongsFolder; } [IteratorStateMachine(typeof(<LoadAudioFiles>d__16))] public IEnumerator LoadAudioFiles() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <LoadAudioFiles>d__16(0) { <>4__this = this }; } private async Task<Track> LoadTrackAsync(string audioFile) { try { string fileName = Path.GetFileNameWithoutExtension(audioFile); UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip("file://" + audioFile, AudioUtils.GetAudioType(audioFile)); try { UnityWebRequestAsyncOperation operation = www.SendWebRequest(); while (!((AsyncOperation)operation).isDone) { await Task.Delay(10); } if ((int)www.result != 1) { MelonLogger.Error("Failed to load " + fileName + ": " + www.error); return null; } AudioClip clip = DownloadHandlerAudioClip.GetContent(www); if ((Object)(object)clip != (Object)null) { ((Object)clip).name = fileName; Track track = new Track { TrackName = fileName, ArtistName = "Custom", Clip = clip }; MelonLogger.Msg("Loaded track: " + fileName); return track; } } finally { ((IDisposable)www)?.Dispose(); } } catch (Exception ex2) { Exception ex = ex2; MelonLogger.Error("Error loading track " + Path.GetFileName(audioFile) + ": " + ex.Message); } return null; } } } namespace BetterJukebox.Patches { public class HarmonyPatches { private readonly JukeboxPatcher _jukeboxPatcher; private readonly PaginationManager _paginationManager; public HarmonyPatches(JukeboxPatcher jukeboxPatcher, PaginationManager paginationManager) { _jukeboxPatcher = jukeboxPatcher; _paginationManager = paginationManager; } public void SetupPatches() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Expected O, but got Unknown //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Expected O, but got Unknown try { Harmony val = new Harmony("com.bars.BetterJukebox"); MethodInfo methodInfo = AccessTools.Method(typeof(Jukebox), "Awake", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(HarmonyPatches), "JukeboxAwakePostfix", (Type[])null, (Type[])null); if (methodInfo != null && methodInfo2 != null) { val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MelonLogger.Msg("Successfully patched Jukebox.Awake method"); } else { MelonLogger.Warning("Failed to patch Jukebox.Awake method. Will use fallback scanning instead."); } MethodInfo methodInfo3 = AccessTools.Method(typeof(JukeboxInterface), "SetupSongEntries", (Type[])null, (Type[])null); MethodInfo methodInfo4 = AccessTools.Method(typeof(HarmonyPatches), "SetupSongEntriesPostfix", (Type[])null, (Type[])null); if (methodInfo3 != null && methodInfo4 != null) { val.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(methodInfo4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MelonLogger.Msg("Successfully patched JukeboxInterface.SetupSongEntries method"); } else { MelonLogger.Warning("Failed to patch JukeboxInterface.SetupSongEntries method"); } MethodInfo methodInfo5 = AccessTools.Method(typeof(JukeboxInterface), "Open", (Type[])null, (Type[])null); MethodInfo methodInfo6 = AccessTools.Method(typeof(HarmonyPatches), "OpenPostfix", (Type[])null, (Type[])null); if (methodInfo5 != null && methodInfo6 != null) { val.Patch((MethodBase)methodInfo5, (HarmonyMethod)null, new HarmonyMethod(methodInfo6), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo7 = AccessTools.Method(typeof(JukeboxInterface), "RefreshSongEntries", (Type[])null, (Type[])null); MethodInfo methodInfo8 = AccessTools.Method(typeof(HarmonyPatches), "RefreshSongEntriesPrefix", (Type[])null, (Type[])null); if (methodInfo7 != null && methodInfo8 != null) { val.Patch((MethodBase)methodInfo7, new HarmonyMethod(methodInfo8), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { MelonLogger.Error("Failed to setup Harmony patches: " + ex.Message); MelonLogger.Error(ex.StackTrace); } } public static void JukeboxAwakePostfix(Jukebox __instance) { Core.OnJukeboxAwake(__instance); } public static void SetupSongEntriesPostfix(JukeboxInterface __instance) { Core.OnSetupSongEntries(__instance); } public static void OpenPostfix(JukeboxInterface __instance) { Core.OnJukeboxInterfaceOpen(__instance); } public static bool RefreshSongEntriesPrefix(JukeboxInterface __instance) { return Core.OnRefreshSongEntries(__instance); } } } namespace BetterJukebox.Models { public class PaginationData { public int CurrentPage { get; set; } = 0; public int TotalPages { get; set; } = 1; public GameObject PaginationUI { get; set; } public Button PrevButton { get; set; } public Button NextButton { get; set; } public TextMeshProUGUI PageText { get; set; } public List<RectTransform> OriginalSongEntries { get; set; } = new List<RectTransform>(); } } namespace BetterJukebox.Config { public class ModConfig { private MelonPreferences_Category _category; public MelonPreferences_Entry<bool> EnableCustomTracks { get; private set; } public MelonPreferences_Entry<bool> EnableDebugLogs { get; private set; } public MelonPreferences_Entry<bool> ShowLoadingIndicator { get; private set; } public MelonPreferences_Entry<int> LoadingBatchSize { get; private set; } public void Initialize() { _category = MelonPreferences.CreateCategory("BetterJukebox"); EnableCustomTracks = _category.CreateEntry<bool>("EnableCustomTracks", true, "Enable Custom Tracks", "Enable loading custom tracks from the mod folder", false, false, (ValueValidator)null, (string)null); EnableDebugLogs = _category.CreateEntry<bool>("EnableDebugLogs", false, "Enable Debug Logs", "Enable detailed debug logs", false, false, (ValueValidator)null, (string)null); ShowLoadingIndicator = _category.CreateEntry<bool>("ShowLoadingIndicator", true, "Show Loading Indicator", "Show a progress indicator when loading custom tracks", false, false, (ValueValidator)null, (string)null); LoadingBatchSize = _category.CreateEntry<int>("LoadingBatchSize", 5, "Loading Batch Size", "Number of tracks to load simultaneously (1-10, higher values may cause lag)", false, false, (ValueValidator)null, (string)null); MelonLogger.Msg("Config initialized"); } public void LogDebug(string message) { if (EnableDebugLogs.Value) { MelonLogger.Msg("[Debug] " + message); } } } }