using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using HarmonyLib;
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: AssemblyTitle("CustomMusic")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CustomMusic")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("257ad1fd-38e8-48d3-93e0-190ceb523928")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
[BepInPlugin("com.custommusic.mod", "Custom Music", "3.1.0")]
public class CustomMusicPlugin : BaseUnityPlugin
{
private AudioSource audioSource;
private AudioClip currentClip;
private Coroutine loadCoroutine;
private Harmony harmony;
private readonly List<string> songs = new List<string>();
private bool showUI = false;
private bool isPaused = false;
private bool gamePausedMusic = false;
private int selectedSong = -1;
private Vector2 uiScroll;
private string status = "Ready";
private Rect windowRect = new Rect(20f, 20f, 430f, 470f);
private const int WindowId = 563821;
private const float PauseThreshold = 0.001f;
private const float SlowMotionThreshold = 0.95f;
private const float SlowMotionPitch = 0.35f;
public static CustomMusicPlugin Instance { get; private set; }
private string MusicFolder => Path.Combine(Paths.PluginPath, "CustomMusic");
private void Awake()
{
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Expected O, but got Unknown
Instance = this;
audioSource = ((Component)this).gameObject.AddComponent<AudioSource>();
audioSource.playOnAwake = false;
audioSource.loop = true;
audioSource.volume = 1f;
audioSource.pitch = 1f;
try
{
harmony = new Harmony("com.custommusic.mod.inputguard");
harmony.PatchAll(typeof(CustomMusicPlugin).Assembly);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Mouse input guard enabled.");
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("Could not install mouse input guard: " + ex));
((BaseUnityPlugin)this).Logger.LogWarning((object)"The music UI will work, but clicks may still reach the game.");
}
RefreshSongs();
((BaseUnityPlugin)this).Logger.LogInfo((object)("Custom Music 3.1 loaded. Music folder: " + MusicFolder));
}
private void Update()
{
if (Input.GetKeyDown((KeyCode)291))
{
showUI = !showUI;
}
SyncWithGameTime();
}
public static bool ShouldBlockGameMouseInput(int button)
{
if (button != 0 || (Object)(object)Instance == (Object)null || !Instance.showUI)
{
return false;
}
return Instance.IsPointerOverWindow();
}
private bool IsPointerOverWindow()
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
Vector3 mousePosition = Input.mousePosition;
Vector2 val = default(Vector2);
((Vector2)(ref val))..ctor(mousePosition.x, (float)Screen.height - mousePosition.y);
return ((Rect)(ref windowRect)).Contains(val);
}
private void SyncWithGameTime()
{
if ((Object)(object)audioSource == (Object)null || (Object)(object)audioSource.clip == (Object)null)
{
return;
}
float timeScale = Time.timeScale;
if (timeScale <= 0.001f)
{
if (audioSource.isPlaying)
{
audioSource.Pause();
gamePausedMusic = true;
}
return;
}
if (gamePausedMusic)
{
gamePausedMusic = false;
if (!isPaused)
{
audioSource.UnPause();
}
}
audioSource.pitch = ((timeScale < 0.95f) ? 0.35f : 1f);
}
private void RefreshSongs()
{
try
{
if (!Directory.Exists(MusicFolder))
{
Directory.CreateDirectory(MusicFolder);
}
songs.Clear();
string[] files = Directory.GetFiles(MusicFolder);
foreach (string text in files)
{
if (Path.GetExtension(text).Equals(".ogg", StringComparison.OrdinalIgnoreCase))
{
songs.Add(text);
}
}
songs.Sort(StringComparer.OrdinalIgnoreCase);
if (songs.Count > 0)
{
selectedSong = Mathf.Clamp(selectedSong, 0, songs.Count - 1);
}
else
{
selectedSong = -1;
}
status = "Found " + songs.Count + " song(s).";
((BaseUnityPlugin)this).Logger.LogInfo((object)status);
}
catch (Exception ex)
{
status = "Error scanning music folder.";
((BaseUnityPlugin)this).Logger.LogError((object)ex);
}
}
private void PlaySelectedSong()
{
if (selectedSong < 0 || selectedSong >= songs.Count)
{
status = "Please select a song first.";
return;
}
if (loadCoroutine != null)
{
((MonoBehaviour)this).StopCoroutine(loadCoroutine);
loadCoroutine = null;
}
isPaused = false;
gamePausedMusic = false;
loadCoroutine = ((MonoBehaviour)this).StartCoroutine(LoadAndPlayCoroutine(songs[selectedSong]));
}
private IEnumerator LoadAndPlayCoroutine(string filePath)
{
if (!File.Exists(filePath))
{
status = "File not found.";
loadCoroutine = null;
yield break;
}
string fileUri;
try
{
fileUri = new Uri(filePath).AbsoluteUri;
}
catch (Exception ex)
{
Exception ex2 = ex;
status = "Invalid music file path.";
((BaseUnityPlugin)this).Logger.LogError((object)ex2);
loadCoroutine = null;
yield break;
}
UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(fileUri, (AudioType)14);
try
{
yield return request.SendWebRequest();
if (request.isNetworkError || request.isHttpError)
{
status = "Load failed: " + request.error;
((BaseUnityPlugin)this).Logger.LogError((object)status);
loadCoroutine = null;
yield break;
}
AudioClip loadedClip = DownloadHandlerAudioClip.GetContent(request);
if ((Object)(object)loadedClip == (Object)null)
{
status = "Could not create AudioClip.";
((BaseUnityPlugin)this).Logger.LogError((object)status);
loadCoroutine = null;
yield break;
}
if ((Object)(object)audioSource != (Object)null)
{
audioSource.Stop();
audioSource.clip = null;
}
if ((Object)(object)currentClip != (Object)null)
{
Object.Destroy((Object)(object)currentClip);
currentClip = null;
}
currentClip = loadedClip;
audioSource.clip = currentClip;
audioSource.loop = true;
audioSource.volume = 1f;
audioSource.pitch = 1f;
if (Time.timeScale <= 0.001f)
{
gamePausedMusic = true;
status = "Loaded; waiting for game to resume.";
}
else
{
audioSource.Play();
SyncWithGameTime();
status = "Playing: " + Path.GetFileName(filePath);
}
((BaseUnityPlugin)this).Logger.LogInfo((object)status);
}
finally
{
((IDisposable)request)?.Dispose();
}
loadCoroutine = null;
}
private void PauseMusic()
{
if ((Object)(object)audioSource == (Object)null || (Object)(object)audioSource.clip == (Object)null)
{
status = "No music loaded.";
return;
}
audioSource.Pause();
isPaused = true;
gamePausedMusic = false;
status = "Music paused.";
}
private void ResumeMusic()
{
if ((Object)(object)audioSource == (Object)null || (Object)(object)audioSource.clip == (Object)null)
{
status = "No music loaded.";
return;
}
isPaused = false;
if (Time.timeScale <= 0.001f)
{
gamePausedMusic = true;
status = "Waiting for game to resume.";
return;
}
gamePausedMusic = false;
audioSource.UnPause();
SyncWithGameTime();
status = "Music resumed.";
}
private void StopMusic()
{
if ((Object)(object)audioSource != (Object)null)
{
audioSource.Stop();
audioSource.pitch = 1f;
}
isPaused = false;
gamePausedMusic = false;
status = "Music stopped.";
}
private void KeepWindowOnScreen()
{
float num = Mathf.Max(280f, (float)Screen.width - 16f);
float num2 = Mathf.Max(220f, (float)Screen.height - 16f);
((Rect)(ref windowRect)).width = Mathf.Clamp(((Rect)(ref windowRect)).width, Mathf.Min(280f, num), num);
((Rect)(ref windowRect)).height = Mathf.Clamp(((Rect)(ref windowRect)).height, Mathf.Min(220f, num2), num2);
((Rect)(ref windowRect)).x = Mathf.Clamp(((Rect)(ref windowRect)).x, 0f, Mathf.Max(0f, (float)Screen.width - ((Rect)(ref windowRect)).width));
((Rect)(ref windowRect)).y = Mathf.Clamp(((Rect)(ref windowRect)).y, 0f, Mathf.Max(0f, (float)Screen.height - ((Rect)(ref windowRect)).height));
}
private void OnGUI()
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Expected O, but got Unknown
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
if (showUI)
{
KeepWindowOnScreen();
windowRect = GUI.Window(563821, windowRect, new WindowFunction(DrawWindow), "Custom Music 3.1");
}
}
private void DrawWindow(int windowID)
{
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_0387: Unknown result type (might be due to invalid IL or missing references)
Rect val = default(Rect);
((Rect)(ref val))..ctor(8f, 22f, ((Rect)(ref windowRect)).width - 16f, ((Rect)(ref windowRect)).height - 52f);
float num = 350f + (float)songs.Count * 28f;
Rect val2 = default(Rect);
((Rect)(ref val2))..ctor(0f, 0f, Mathf.Max(100f, ((Rect)(ref val)).width - 20f), num);
uiScroll = GUI.BeginScrollView(val, uiScroll, val2);
GUILayout.BeginArea(val2);
GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
GUILayout.Label("Press F10 to hide/show this window.", Array.Empty<GUILayoutOption>());
GUILayout.Label("Music folder: BepInEx/plugins/CustomMusic", Array.Empty<GUILayoutOption>());
GUILayout.Label("Status: " + status, Array.Empty<GUILayoutOption>());
GUILayout.Label("Game time scale: " + Time.timeScale.ToString("0.00"), Array.Empty<GUILayoutOption>());
string text = ((Time.timeScale <= 0.001f) ? "Paused" : ((Time.timeScale < 0.95f) ? "Slow Motion" : "Normal"));
GUILayout.Label("Game mode: " + text, Array.Empty<GUILayoutOption>());
GUILayout.Space(6f);
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
if (GUILayout.Button("Refresh Songs", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }))
{
RefreshSongs();
}
if (GUILayout.Button("Hide", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }))
{
showUI = false;
}
GUILayout.EndHorizontal();
GUILayout.Space(6f);
GUILayout.Label("Available OGG Songs:", Array.Empty<GUILayoutOption>());
if (songs.Count == 0)
{
GUILayout.Label("No .ogg files found. Add music to the CustomMusic folder.", Array.Empty<GUILayoutOption>());
}
else
{
for (int i = 0; i < songs.Count; i++)
{
bool flag = selectedSong == i;
string fileName = Path.GetFileName(songs[i]);
if (GUILayout.Toggle(flag, fileName, GUI.skin.button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) }))
{
selectedSong = i;
}
}
}
GUILayout.Space(6f);
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
if (GUILayout.Button("Play Selected", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
{
PlaySelectedSong();
}
if (GUILayout.Button("Pause", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
{
PauseMusic();
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
if (GUILayout.Button("Resume", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
{
ResumeMusic();
}
if (GUILayout.Button("Stop", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
{
StopMusic();
}
GUILayout.EndHorizontal();
GUILayout.Space(5f);
GUILayout.Label("Slow-motion pitch: 0.35 | Normal pitch: 1.00", Array.Empty<GUILayoutOption>());
GUILayout.EndVertical();
GUILayout.EndArea();
GUI.EndScrollView();
GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref windowRect)).width, 18f));
}
private void OnDestroy()
{
if (loadCoroutine != null)
{
((MonoBehaviour)this).StopCoroutine(loadCoroutine);
loadCoroutine = null;
}
if ((Object)(object)audioSource != (Object)null)
{
audioSource.Stop();
audioSource.clip = null;
}
if ((Object)(object)currentClip != (Object)null)
{
Object.Destroy((Object)(object)currentClip);
currentClip = null;
}
if (harmony != null)
{
try
{
harmony.UnpatchSelf();
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Harmony cleanup warning: " + ex.Message));
}
}
if ((Object)(object)Instance == (Object)(object)this)
{
Instance = null;
}
}
}
[HarmonyPatch(typeof(Input), "GetMouseButtonDown", new Type[] { typeof(int) })]
internal static class CustomMusicBlockMouseDownPatch
{
private static bool Prefix(int button, ref bool __result)
{
if (!CustomMusicPlugin.ShouldBlockGameMouseInput(button))
{
return true;
}
__result = false;
return false;
}
}
[HarmonyPatch(typeof(Input), "GetMouseButton", new Type[] { typeof(int) })]
internal static class CustomMusicBlockMouseHeldPatch
{
private static bool Prefix(int button, ref bool __result)
{
if (!CustomMusicPlugin.ShouldBlockGameMouseInput(button))
{
return true;
}
__result = false;
return false;
}
}
[HarmonyPatch(typeof(Input), "GetMouseButtonUp", new Type[] { typeof(int) })]
internal static class CustomMusicBlockMouseUpPatch
{
private static bool Prefix(int button, ref bool __result)
{
if (!CustomMusicPlugin.ShouldBlockGameMouseInput(button))
{
return true;
}
__result = false;
return false;
}
}