Decompiled source of BoomboxCart v1.0.1

BoomBoxCartMod.dll

Decompiled 10 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Logging;
using BoomBoxCartMod.Patches;
using BoomBoxCartMod.Util;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
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("BoomBoxCartMod")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BoomBoxCartMod")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("37e852e0-b511-4315-8182-68c0a54e1ba9")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[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 BoomBoxCartMod
{
	public class Boombox : MonoBehaviourPunCallbacks
	{
		[CompilerGenerated]
		private sealed class <DownloadTimeoutCoroutine>d__35 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public string requestId;

			public string url;

			public Boombox <>4__this;

			private Player[] <>s__1;

			private int <>s__2;

			private Player <player>5__3;

			private string <timeoutMessage>5__4;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>s__1 = null;
				<player>5__3 = null;
				<timeoutMessage>5__4 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(40f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if (currentRequestId == requestId && isDownloadInProgress)
					{
						Logger.LogWarning((object)("Download timeout for url: " + url));
						if (PhotonNetwork.IsMasterClient)
						{
							Logger.LogInfo((object)"Master client initiating timeout recovery");
							<>4__this.isTimeoutRecovery = true;
							<>s__1 = PhotonNetwork.PlayerList;
							for (<>s__2 = 0; <>s__2 < <>s__1.Length; <>s__2++)
							{
								<player>5__3 = <>s__1[<>s__2];
								if (!downloadsReady[url].Contains(<player>5__3.ActorNumber))
								{
									if (!downloadErrors.ContainsKey(url))
									{
										downloadErrors[url] = new HashSet<int>();
									}
									downloadErrors[url].Add(<player>5__3.ActorNumber);
									Logger.LogWarning((object)$"Player {<player>5__3.ActorNumber} timed out during download");
								}
								<player>5__3 = null;
							}
							<>s__1 = null;
							if (downloadsReady.ContainsKey(url) && downloadsReady[url].Count > 0)
							{
								<timeoutMessage>5__4 = $"Some players timed out. Continuing playback for {downloadsReady[url].Count} players.";
								<>4__this.photonView.RPC("NotifyPlayersOfErrors", (RpcTarget)0, new object[1] { <timeoutMessage>5__4 });
								<>4__this.photonView.RPC("SyncPlayback", (RpcTarget)0, new object[2]
								{
									url,
									PhotonNetwork.LocalPlayer.ActorNumber
								});
								<timeoutMessage>5__4 = null;
							}
							else
							{
								<>4__this.photonView.RPC("NotifyPlayersOfErrors", (RpcTarget)0, new object[1] { "Download timed out for all players." });
							}
							isDownloadInProgress = false;
							currentDownloadUrl = "";
							currentRequestId = "";
							<>4__this.isTimeoutRecovery = false;
						}
					}
					<>4__this.timeoutCoroutines.Remove(requestId);
					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 BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		public PhotonView photonView;

		public AudioSource audioSource;

		public float maxVolumeLimit = 0.8f;

		private float minDistance = 3f;

		private float maxDistanceBase = 10f;

		private float maxDistanceAddition = 20f;

		private static bool isDownloadInProgress = false;

		private static string currentDownloadUrl = "";

		private static string currentRequestId = "";

		public string currentSongUrl = "";

		public string currentSongTitle = "No song playing";

		private bool isAwaitingSyncPlayback = false;

		public bool isPlaying = false;

		private bool isTimeoutRecovery = false;

		private AudioLowPassFilter lowPassFilter;

		public int qualityLevel = 3;

		private static Dictionary<string, AudioClip> downloadedClips = new Dictionary<string, AudioClip>();

		private static Dictionary<string, string> songTitles = new Dictionary<string, string>();

		private static Dictionary<string, HashSet<int>> downloadsReady = new Dictionary<string, HashSet<int>>();

		private static Dictionary<string, HashSet<int>> downloadErrors = new Dictionary<string, HashSet<int>>();

		private const float DOWNLOAD_TIMEOUT = 40f;

		private Dictionary<string, Coroutine> timeoutCoroutines = new Dictionary<string, Coroutine>();

		private static readonly Regex youtubeUrlRegex = new Regex("^https?:\\/\\/(www\\.)?(youtube\\.com\\/watch\\?v=|youtu\\.be\\/)[a-zA-Z0-9_-]+(\\?|&|$)", RegexOptions.IgnoreCase | RegexOptions.Compiled);

		private static ManualLogSource Logger => Instance.logger;

		private void Awake()
		{
			audioSource = ((Component)this).gameObject.AddComponent<AudioSource>();
			audioSource.volume = 0.15f;
			audioSource.spatialBlend = 1f;
			audioSource.playOnAwake = false;
			audioSource.rolloffMode = (AudioRolloffMode)2;
			audioSource.spread = 90f;
			audioSource.dopplerLevel = 0f;
			audioSource.reverbZoneMix = 1f;
			audioSource.spatialize = true;
			lowPassFilter = ((Component)this).gameObject.AddComponent<AudioLowPassFilter>();
			((Behaviour)lowPassFilter).enabled = false;
			UpdateAudioRangeBasedOnVolume(audioSource.volume);
			photonView = ((Component)this).GetComponent<PhotonView>();
			isAwaitingSyncPlayback = false;
			if ((Object)(object)photonView == (Object)null)
			{
				Logger.LogError((object)"PhotonView not found on Boombox object.");
				return;
			}
			if ((Object)(object)((Component)this).GetComponent<BoomboxController>() == (Object)null)
			{
				((Component)this).gameObject.AddComponent<BoomboxController>();
			}
			Logger.LogInfo((object)$"Boombox initialized on this cart. AudioSource: {audioSource}, PhotonView: {photonView}");
		}

		private void Update()
		{
			if (isAwaitingSyncPlayback && audioSource.isPlaying)
			{
				audioSource.Stop();
				isPlaying = false;
			}
		}

		private void OnDestroy()
		{
			foreach (Coroutine value in timeoutCoroutines.Values)
			{
				if (value != null)
				{
					((MonoBehaviour)this).StopCoroutine(value);
				}
			}
			timeoutCoroutines.Clear();
		}

		public bool IsDownloadInProgress()
		{
			return isDownloadInProgress;
		}

		public string GetCurrentDownloadUrl()
		{
			return currentDownloadUrl;
		}

		public static bool IsValidYoutubeUrl(string url)
		{
			return !string.IsNullOrWhiteSpace(url) && youtubeUrlRegex.IsMatch(url);
		}

		[PunRPC]
		public async void RequestSong(string url, int requesterId)
		{
			if (!IsValidYoutubeUrl(url))
			{
				Logger.LogError((object)("Invalid YouTube URL: " + url));
				if (requesterId == PhotonNetwork.LocalPlayer.ActorNumber)
				{
					UpdateUIStatus("Error: Invalid YouTube URL.");
				}
				return;
			}
			string requestId = Guid.NewGuid().ToString();
			if (isDownloadInProgress && requesterId != PhotonNetwork.MasterClient.ActorNumber)
			{
				if (requesterId == PhotonNetwork.LocalPlayer.ActorNumber)
				{
					UpdateUIStatus("Please wait for the current download to complete.");
				}
				return;
			}
			isDownloadInProgress = true;
			isAwaitingSyncPlayback = true;
			currentDownloadUrl = url;
			currentRequestId = requestId;
			CleanupCurrentPlayback();
			timeoutCoroutines[requestId] = ((MonoBehaviour)this).StartCoroutine(DownloadTimeoutCoroutine(requestId, url));
			if (requesterId == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				UpdateUIStatus("Downloading audio from " + url + "...");
			}
			if (downloadsReady.ContainsKey(url))
			{
				downloadsReady[url].Clear();
			}
			else
			{
				downloadsReady[url] = new HashSet<int>();
			}
			if (downloadErrors.ContainsKey(url))
			{
				downloadErrors[url].Clear();
			}
			else
			{
				downloadErrors[url] = new HashSet<int>();
			}
			if (!downloadedClips.ContainsKey(url))
			{
				try
				{
					var (filePath, title) = await YoutubeDL.DownloadAudioWithTitleAsync(url);
					songTitles[url] = title;
					photonView.RPC("SetSongTitle", (RpcTarget)3, new object[2] { url, title });
					if (requesterId == PhotonNetwork.LocalPlayer.ActorNumber)
					{
						UpdateUIStatus("Processing audio: " + title);
					}
					AudioClip clip = await GetAudioClipAsync(filePath);
					downloadedClips[url] = clip;
					Logger.LogInfo((object)("Downloaded and cached clip for url: " + url));
				}
				catch (Exception ex2)
				{
					Exception ex = ex2;
					if (requesterId == PhotonNetwork.LocalPlayer.ActorNumber)
					{
						UpdateUIStatus("Error: " + ex.Message);
					}
					photonView.RPC("ReportDownloadError", (RpcTarget)0, new object[3]
					{
						PhotonNetwork.LocalPlayer.ActorNumber,
						url,
						ex.Message
					});
					isAwaitingSyncPlayback = false;
					return;
				}
			}
			photonView.RPC("ReportDownloadComplete", (RpcTarget)0, new object[2]
			{
				PhotonNetwork.LocalPlayer.ActorNumber,
				url
			});
			if (requesterId == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				UpdateUIStatus("Waiting for all players to be ready...");
			}
			if (requesterId == PhotonNetwork.LocalPlayer.ActorNumber || (PhotonNetwork.IsMasterClient && (requesterId != PhotonNetwork.LocalPlayer.ActorNumber || isTimeoutRecovery)))
			{
				await WaitForPlayersReadyOrFailed(url);
				if (currentRequestId != requestId)
				{
					Logger.LogWarning((object)("Request ID mismatch. Current: " + currentRequestId + ", This request: " + requestId));
					return;
				}
				if (downloadErrors.ContainsKey(url) && downloadErrors[url].Count > 0)
				{
					string errorMessage = $"Some players had download errors. Continuing playback for {downloadsReady[url].Count} players.";
					Logger.LogWarning((object)errorMessage);
					photonView.RPC("NotifyPlayersOfErrors", (RpcTarget)0, new object[1] { errorMessage });
				}
				Logger.LogInfo((object)$"All players ready for playback. Initiating sync playback for {downloadsReady[url].Count} players.");
				photonView.RPC("SyncPlayback", (RpcTarget)0, new object[2] { url, requesterId });
			}
			if (timeoutCoroutines.TryGetValue(requestId, out var coroutine) && coroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(coroutine);
				timeoutCoroutines.Remove(requestId);
			}
			if (currentRequestId == requestId)
			{
				isDownloadInProgress = false;
				currentDownloadUrl = "";
				currentRequestId = "";
			}
		}

		[PunRPC]
		public void NotifyPlayersOfErrors(string message)
		{
			Logger.LogWarning((object)message);
			UpdateUIStatus(message);
		}

		[PunRPC]
		public void ReportDownloadError(int actorNumber, string url, string errorMessage)
		{
			Logger.LogError((object)$"Player {actorNumber} reported download error for {url}: {errorMessage}");
			if (!downloadErrors.ContainsKey(url))
			{
				downloadErrors[url] = new HashSet<int>();
			}
			downloadErrors[url].Add(actorNumber);
			if (actorNumber == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				UpdateUIStatus("Error: " + errorMessage);
			}
		}

		[IteratorStateMachine(typeof(<DownloadTimeoutCoroutine>d__35))]
		private IEnumerator DownloadTimeoutCoroutine(string requestId, string url)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DownloadTimeoutCoroutine>d__35(0)
			{
				<>4__this = this,
				requestId = requestId,
				url = url
			};
		}

		[PunRPC]
		public void SetSongTitle(string url, string title)
		{
			songTitles[url] = title;
			if (currentSongUrl == url)
			{
				currentSongTitle = title;
				UpdateUIStatus("Now playing: " + title);
			}
		}

		[PunRPC]
		public void ReportDownloadComplete(int actorNumber, string url)
		{
			if (!downloadsReady.ContainsKey(url))
			{
				downloadsReady[url] = new HashSet<int>();
			}
			downloadsReady[url].Add(actorNumber);
		}

		private async Task WaitForPlayersReadyOrFailed(string url)
		{
			int totalPlayers = PhotonNetwork.PlayerList.Length;
			int readyCount;
			int errorCount;
			while (true)
			{
				readyCount = (downloadsReady.ContainsKey(url) ? downloadsReady[url].Count : 0);
				errorCount = (downloadErrors.ContainsKey(url) ? downloadErrors[url].Count : 0);
				bool flag = readyCount + errorCount >= totalPlayers;
				bool flag2 = flag;
				if (!flag2)
				{
					bool flag3 = readyCount > 0 && errorCount > 0;
					bool flag4 = flag3;
					if (flag4)
					{
						flag4 = await Task.Delay(5000).ContinueWith((Task _) => true);
					}
					flag2 = flag4;
				}
				if (flag2)
				{
					break;
				}
				await Task.Delay(100);
			}
			Logger.LogInfo((object)$"Ready to proceed with playback. Ready: {readyCount}, Errors: {errorCount}, Total: {totalPlayers}");
		}

		[PunRPC]
		public void SyncPlayback(string url, int requesterId)
		{
			if (isPlaying && currentSongUrl == url && audioSource.isPlaying)
			{
				return;
			}
			if (!downloadedClips.ContainsKey(url))
			{
				Logger.LogError((object)("Clip not found for url: " + url));
				return;
			}
			CleanupCurrentPlayback();
			currentSongUrl = url;
			if (songTitles.TryGetValue(url, out var value))
			{
				currentSongTitle = value;
			}
			isAwaitingSyncPlayback = false;
			audioSource.clip = downloadedClips[url];
			SetQuality(qualityLevel);
			UpdateAudioRangeBasedOnVolume(audioSource.volume);
			audioSource.Play();
			isPlaying = true;
			UpdateUIStatus("Now playing: " + currentSongTitle);
		}

		private void CleanupCurrentPlayback()
		{
			if (audioSource.isPlaying)
			{
				audioSource.Stop();
				isPlaying = false;
			}
			audioSource.clip = null;
		}

		public void RemoveFromCache(string url)
		{
			if (downloadedClips.ContainsKey(url))
			{
				AudioClip val = downloadedClips[url];
				downloadedClips.Remove(url);
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
			}
			if (songTitles.ContainsKey(url))
			{
				songTitles.Remove(url);
			}
			if (downloadsReady.ContainsKey(url))
			{
				downloadsReady.Remove(url);
			}
			if (downloadErrors.ContainsKey(url))
			{
				downloadErrors.Remove(url);
			}
		}

		public void SetQuality(int level)
		{
			qualityLevel = Mathf.Clamp(level, 0, 4);
			switch (qualityLevel)
			{
			case 0:
				((Behaviour)lowPassFilter).enabled = true;
				lowPassFilter.cutoffFrequency = 1500f;
				break;
			case 1:
				((Behaviour)lowPassFilter).enabled = true;
				lowPassFilter.cutoffFrequency = 3000f;
				break;
			case 2:
				((Behaviour)lowPassFilter).enabled = true;
				lowPassFilter.cutoffFrequency = 4500f;
				break;
			case 3:
				((Behaviour)lowPassFilter).enabled = true;
				lowPassFilter.cutoffFrequency = 6000f;
				break;
			case 4:
				((Behaviour)lowPassFilter).enabled = false;
				break;
			}
		}

		[PunRPC]
		public void UpdateQuality(int level, int requesterId)
		{
			BoomboxController component = ((Component)this).GetComponent<BoomboxController>();
			SetQuality(level);
		}

		[PunRPC]
		public void UpdateVolume(float volume, int requesterId)
		{
			BoomboxController component = ((Component)this).GetComponent<BoomboxController>();
			float volume2 = volume * maxVolumeLimit;
			audioSource.volume = volume2;
			UpdateAudioRangeBasedOnVolume(volume2);
		}

		private void UpdateAudioRangeBasedOnVolume(float volume)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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_007a: 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_0085: Expected O, but got Unknown
			float num = Mathf.Lerp(maxDistanceBase, maxDistanceBase + maxDistanceAddition, volume);
			audioSource.minDistance = minDistance;
			audioSource.maxDistance = num;
			AnimationCurve val = new AnimationCurve((Keyframe[])(object)new Keyframe[3]
			{
				new Keyframe(0f, 1f),
				new Keyframe(minDistance, 0.9f),
				new Keyframe(num, 0f)
			});
			audioSource.SetCustomCurve((AudioSourceCurveType)0, val);
		}

		[PunRPC]
		public void PausePlayback(int requesterId)
		{
			if (audioSource.isPlaying)
			{
				audioSource.Pause();
				isPlaying = false;
				UpdateUIStatus("Paused: " + currentSongTitle);
			}
		}

		[PunRPC]
		public void StopPlayback(int requesterId)
		{
			if (audioSource.isPlaying)
			{
				audioSource.Stop();
				isPlaying = false;
				UpdateUIStatus("Stopped: " + currentSongTitle);
			}
		}

		private void UpdateUIStatus(string message)
		{
			BoomboxUI component = ((Component)this).GetComponent<BoomboxUI>();
			if ((Object)(object)component != (Object)null && component.IsUIVisible())
			{
				component.UpdateStatus(message);
			}
		}

		public override void OnPlayerLeftRoom(Player otherPlayer)
		{
			if (isDownloadInProgress && !string.IsNullOrEmpty(currentDownloadUrl) && downloadsReady.ContainsKey(currentDownloadUrl) && !downloadsReady[currentDownloadUrl].Contains(otherPlayer.ActorNumber))
			{
				if (!downloadErrors.ContainsKey(currentDownloadUrl))
				{
					downloadErrors[currentDownloadUrl] = new HashSet<int>();
				}
				downloadErrors[currentDownloadUrl].Add(otherPlayer.ActorNumber);
				Logger.LogInfo((object)$"Player {otherPlayer.ActorNumber} left during download - marking as error");
			}
			((MonoBehaviourPunCallbacks)this).OnPlayerLeftRoom(otherPlayer);
		}

		public override void OnPlayerEnteredRoom(Player newPlayer)
		{
			((MonoBehaviourPunCallbacks)this).OnPlayerEnteredRoom(newPlayer);
			if (PhotonNetwork.IsMasterClient && isPlaying && !string.IsNullOrEmpty(currentSongUrl))
			{
				Logger.LogInfo((object)$"New player {newPlayer.ActorNumber} joined - syncing current playback state");
				if (songTitles.ContainsKey(currentSongUrl))
				{
					photonView.RPC("SetSongTitle", newPlayer, new object[2]
					{
						currentSongUrl,
						songTitles[currentSongUrl]
					});
				}
				photonView.RPC("SyncPlayback", newPlayer, new object[2]
				{
					currentSongUrl,
					PhotonNetwork.LocalPlayer.ActorNumber
				});
				photonView.RPC("UpdateQuality", newPlayer, new object[2]
				{
					qualityLevel,
					PhotonNetwork.LocalPlayer.ActorNumber
				});
			}
		}

		public static async Task<AudioClip> GetAudioClipAsync(string filePath)
		{
			string uri = "file://" + filePath;
			UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(uri, (AudioType)13);
			try
			{
				TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
				UnityWebRequestAsyncOperation operation = www.SendWebRequest();
				((AsyncOperation)operation).completed += delegate
				{
					//IL_000c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0012: Invalid comparison between Unknown and I4
					if ((int)www.result != 1)
					{
						tcs.SetException(new Exception("Failed to load audio file: " + www.error));
					}
					else
					{
						tcs.SetResult(result: true);
					}
				};
				await tcs.Task;
				AudioClip clip = DownloadHandlerAudioClip.GetContent(www);
				try
				{
					if (Directory.Exists(Path.GetDirectoryName(filePath)))
					{
						Directory.Delete(Path.GetDirectoryName(filePath), recursive: true);
					}
				}
				catch (Exception ex2)
				{
					Exception ex = ex2;
					Logger.LogWarning((object)("Failed to clean up temp directory: " + ex.Message));
				}
				return clip;
			}
			finally
			{
				if (www != null)
				{
					((IDisposable)www).Dispose();
				}
			}
		}
	}
	public class BoomboxController : MonoBehaviourPun
	{
		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		private PhysGrabCart cart;

		private BoomboxUI boomboxUI;

		private Boombox boombox;

		private int currentControllerId = -1;

		private static ManualLogSource Logger => Instance.logger;

		private void Awake()
		{
			cart = ((Component)this).GetComponent<PhysGrabCart>();
			boombox = ((Component)this).GetComponent<Boombox>();
			if ((Object)(object)cart == (Object)null)
			{
				Logger.LogError((object)"BoomboxController: PhysGrabCart component not found!");
			}
			else if ((Object)(object)boombox == (Object)null)
			{
				Logger.LogError((object)"BoomboxController: Boombox component not found!");
			}
		}

		private bool IsLocalPlayerGrabbingCart()
		{
			return PlayerGrabbingTracker.IsLocalPlayerGrabbingCart(((Component)this).gameObject);
		}

		public void RequestBoomboxControl()
		{
			if (IsLocalPlayerGrabbingCart())
			{
				int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
				((MonoBehaviourPun)this).photonView.RPC("RequestControl", (RpcTarget)2, new object[1] { actorNumber });
			}
		}

		[PunRPC]
		private void RequestControl(int requesterId)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				bool flag = true;
				if (requesterId == PhotonNetwork.LocalPlayer.ActorNumber)
				{
					flag = PlayerGrabbingTracker.IsLocalPlayerGrabbingCart(((Component)this).gameObject);
				}
				if (currentControllerId == -1 && flag)
				{
					((MonoBehaviourPun)this).photonView.RPC("SetController", (RpcTarget)0, new object[1] { requesterId });
				}
			}
		}

		[PunRPC]
		private void SetController(int controllerId)
		{
			try
			{
				currentControllerId = controllerId;
				if (controllerId == PhotonNetwork.LocalPlayer.ActorNumber)
				{
					EnsureBoomboxUIExists();
					if ((Object)(object)boomboxUI != (Object)null)
					{
						boomboxUI.ShowUI();
					}
					else
					{
						Logger.LogError((object)"Failed to create BoomboxUI component");
					}
				}
				else if ((Object)(object)boomboxUI != (Object)null && boomboxUI.IsUIVisible())
				{
					boomboxUI.HideUI();
				}
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("Error in SetController: " + ex.Message + "\n" + ex.StackTrace));
			}
		}

		private void EnsureBoomboxUIExists()
		{
			if ((Object)(object)boomboxUI == (Object)null)
			{
				boomboxUI = ((Component)this).gameObject.GetComponent<BoomboxUI>();
				if ((Object)(object)boomboxUI == (Object)null)
				{
					boomboxUI = ((Component)this).gameObject.AddComponent<BoomboxUI>();
				}
			}
		}

		public void ReleaseControl()
		{
			if (currentControllerId == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				((MonoBehaviourPun)this).photonView.RPC("RequestRelease", (RpcTarget)2, new object[1] { PhotonNetwork.LocalPlayer.ActorNumber });
			}
		}

		[PunRPC]
		private void RequestRelease(int releaserId)
		{
			if (PhotonNetwork.IsMasterClient && currentControllerId == releaserId)
			{
				((MonoBehaviourPun)this).photonView.RPC("SetController", (RpcTarget)0, new object[1] { -1 });
			}
		}

		private void OnPlayerReleasedCart(int playerActorNumber)
		{
			if (playerActorNumber == currentControllerId && PhotonNetwork.IsMasterClient)
			{
				((MonoBehaviourPun)this).photonView.RPC("SetController", (RpcTarget)0, new object[1] { -1 });
			}
		}

		public void LocalPlayerReleasedCart()
		{
			int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
			if (currentControllerId == actorNumber)
			{
				ReleaseControl();
			}
		}
	}
	public class BoomboxUI : MonoBehaviourPun
	{
		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		public PhotonView photonView;

		private bool showUI = false;

		private string urlInput = "";

		private float normalizedVolume = 0.3f;

		private float lastSentNormalizedVolume = 0.3f;

		private bool isSliderBeingDragged = false;

		private int qualityLevel = 3;

		private string[] qualityLabels = new string[5] { "REALLY Low (You Freak)", "Low", "Medium-Low", "Medium-High (Recommended)", "High" };

		private bool isQualitySliderBeingDragged = false;

		private int lastSentQualityLevel = 3;

		private Rect windowRect = new Rect(100f, 100f, 400f, 700f);

		private Boombox boombox;

		private BoomboxController controller;

		private GUIStyle windowStyle;

		private GUIStyle headerStyle;

		private GUIStyle buttonStyle;

		private GUIStyle textFieldStyle;

		private GUIStyle labelStyle;

		private GUIStyle sliderStyle;

		private GUIStyle statusStyle;

		private Texture2D backgroundTexture;

		private Texture2D buttonTexture;

		private Texture2D sliderBackgroundTexture;

		private Texture2D sliderThumbTexture;

		private string errorMessage = "";

		private float errorMessageTime = 0f;

		private string statusMessage = "";

		private CursorLockMode previousLockMode;

		private bool previousCursorVisible;

		private bool stylesInitialized = false;

		private Vector2 scrollPosition = Vector2.zero;

		private bool shouldClearFocus = false;

		private static ManualLogSource Logger => Instance.logger;

		private void Awake()
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				boombox = ((Component)this).GetComponent<Boombox>();
				if ((Object)(object)boombox != (Object)null)
				{
					photonView = boombox.photonView;
				}
				else
				{
					Logger.LogError((object)"BoomboxUI: Failed to find Boombox component");
					photonView = ((Component)this).GetComponent<PhotonView>();
				}
				controller = ((Component)this).GetComponent<BoomboxController>();
				if ((Object)(object)photonView == (Object)null)
				{
					Logger.LogError((object)"BoomboxUI: Failed to find PhotonView component");
				}
				windowRect = new Rect((float)(Screen.width / 2 - 200), (float)(Screen.height / 2 - 175), 400f, 500f);
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("Error in BoomboxUI.Awake: " + ex.Message + "\n" + ex.StackTrace));
			}
		}

		private void Update()
		{
			if (Time.time > errorMessageTime && !string.IsNullOrEmpty(errorMessage))
			{
				errorMessage = "";
			}
			if (showUI && Keyboard.current != null && ((ButtonControl)Keyboard.current.escapeKey).wasPressedThisFrame)
			{
				if ((Object)(object)controller != (Object)null)
				{
					controller.ReleaseControl();
				}
				else
				{
					HideUI();
				}
			}
			if (isSliderBeingDragged && Input.GetMouseButtonUp(0))
			{
				isSliderBeingDragged = false;
				SendVolumeUpdate();
			}
			if (isQualitySliderBeingDragged && Input.GetMouseButtonUp(0))
			{
				isQualitySliderBeingDragged = false;
				SendQualityUpdate();
			}
		}

		public void ShowUI()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if (showUI)
			{
				return;
			}
			if ((Object)(object)boombox == (Object)null || (Object)(object)photonView == (Object)null)
			{
				Logger.LogError((object)"Cannot show UI - boombox or photonView is null");
				return;
			}
			showUI = true;
			previousLockMode = Cursor.lockState;
			previousCursorVisible = Cursor.visible;
			Cursor.visible = true;
			Cursor.lockState = (CursorLockMode)0;
			if ((Object)(object)boombox != (Object)null)
			{
				normalizedVolume = boombox.audioSource.volume / boombox.maxVolumeLimit;
				lastSentNormalizedVolume = normalizedVolume;
				qualityLevel = boombox.qualityLevel;
				lastSentQualityLevel = qualityLevel;
			}
			UpdateStatusFromBoombox();
		}

		public void UpdateStatusFromBoombox()
		{
			if ((Object)(object)boombox != (Object)null)
			{
				if (boombox.IsDownloadInProgress())
				{
					statusMessage = "Downloading audio from " + boombox.GetCurrentDownloadUrl() + "...";
				}
				else if (boombox.isPlaying)
				{
					statusMessage = "Now playing: " + boombox.currentSongTitle;
				}
				else if (!string.IsNullOrEmpty(boombox.currentSongUrl))
				{
					statusMessage = "Ready to play: " + boombox.currentSongTitle;
				}
				else
				{
					statusMessage = "Ready to play music! Enter a YouTube URL";
				}
			}
		}

		private void SendVolumeUpdate()
		{
			if (normalizedVolume != lastSentNormalizedVolume)
			{
				lastSentNormalizedVolume = normalizedVolume;
				if ((Object)(object)boombox.audioSource != (Object)null)
				{
					float volume = normalizedVolume * boombox.maxVolumeLimit;
					boombox.audioSource.volume = volume;
				}
				if ((Object)(object)photonView != (Object)null)
				{
					photonView.RPC("UpdateVolume", (RpcTarget)3, new object[2]
					{
						normalizedVolume,
						PhotonNetwork.LocalPlayer.ActorNumber
					});
				}
			}
		}

		private void SendQualityUpdate()
		{
			if (qualityLevel != lastSentQualityLevel)
			{
				lastSentQualityLevel = qualityLevel;
				if ((Object)(object)boombox != (Object)null)
				{
					boombox.SetQuality(qualityLevel);
				}
				if ((Object)(object)photonView != (Object)null)
				{
					photonView.RPC("UpdateQuality", (RpcTarget)3, new object[2]
					{
						qualityLevel,
						PhotonNetwork.LocalPlayer.ActorNumber
					});
				}
			}
		}

		public void HideUI()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (showUI)
			{
				showUI = false;
				Cursor.lockState = previousLockMode;
				Cursor.visible = previousCursorVisible;
			}
		}

		public bool IsUIVisible()
		{
			return showUI;
		}

		public void UpdateStatus(string message)
		{
			statusMessage = message;
		}

		private void InitializeStyles()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Expected O, but got Unknown
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Expected O, but got Unknown
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Expected O, but got Unknown
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Expected O, but got Unknown
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Expected O, but got Unknown
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Expected O, but got Unknown
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Expected O, but got Unknown
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Expected O, but got Unknown
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_032d: Expected O, but got Unknown
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Expected O, but got Unknown
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_037c: Expected O, but got Unknown
			//IL_0387: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cf: Expected O, but got Unknown
			if (!stylesInitialized)
			{
				backgroundTexture = CreateColorTexture(new Color(0.1f, 0.1f, 0.1f, 0.9f));
				buttonTexture = CreateColorTexture(new Color(0.2f, 0.2f, 0.3f, 1f));
				sliderBackgroundTexture = CreateColorTexture(new Color(0.15f, 0.15f, 0.2f, 1f));
				sliderThumbTexture = CreateColorTexture(new Color(0.7f, 0.7f, 0.8f, 1f));
				windowStyle = new GUIStyle(GUI.skin.window);
				windowStyle.normal.background = backgroundTexture;
				windowStyle.onNormal.background = backgroundTexture;
				windowStyle.border = new RectOffset(10, 10, 10, 10);
				windowStyle.padding = new RectOffset(15, 15, 20, 15);
				headerStyle = new GUIStyle(GUI.skin.label);
				headerStyle.fontSize = 18;
				headerStyle.fontStyle = (FontStyle)1;
				headerStyle.normal.textColor = Color.white;
				headerStyle.alignment = (TextAnchor)4;
				headerStyle.margin = new RectOffset(0, 0, 10, 20);
				buttonStyle = new GUIStyle(GUI.skin.button);
				buttonStyle.normal.background = buttonTexture;
				buttonStyle.hover.background = CreateColorTexture(new Color(0.3f, 0.3f, 0.4f, 1f));
				buttonStyle.active.background = CreateColorTexture(new Color(0.4f, 0.4f, 0.5f, 1f));
				buttonStyle.normal.textColor = Color.white;
				buttonStyle.hover.textColor = Color.white;
				buttonStyle.active.textColor = Color.white;
				buttonStyle.fontSize = 14;
				buttonStyle.padding = new RectOffset(15, 15, 8, 8);
				buttonStyle.margin = new RectOffset(5, 5, 5, 5);
				buttonStyle.alignment = (TextAnchor)4;
				textFieldStyle = new GUIStyle(GUI.skin.textField);
				textFieldStyle.normal.background = CreateColorTexture(new Color(0.2f, 0.2f, 0.2f, 1f));
				textFieldStyle.normal.textColor = Color.white;
				textFieldStyle.fontSize = 14;
				textFieldStyle.padding = new RectOffset(10, 10, 8, 8);
				labelStyle = new GUIStyle(GUI.skin.label);
				labelStyle.normal.textColor = Color.white;
				labelStyle.fontSize = 14;
				labelStyle.margin = new RectOffset(0, 0, 10, 5);
				statusStyle = new GUIStyle(GUI.skin.label);
				statusStyle.normal.textColor = Color.cyan;
				statusStyle.fontSize = 14;
				statusStyle.wordWrap = true;
				statusStyle.alignment = (TextAnchor)4;
				sliderStyle = new GUIStyle(GUI.skin.horizontalSlider);
				sliderStyle.normal.background = sliderBackgroundTexture;
				stylesInitialized = true;
			}
		}

		private Texture2D CreateColorTexture(Color color)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Expected O, but got Unknown
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(1, 1);
			val.SetPixel(0, 0, color);
			val.Apply();
			return val;
		}

		private void OnGUI()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (showUI)
			{
				if (!stylesInitialized)
				{
					InitializeStyles();
				}
				windowRect = GUILayout.Window(0, windowRect, new WindowFunction(DrawUI), "Boombox Controller", windowStyle, Array.Empty<GUILayoutOption>());
			}
		}

		private void DrawUI(int windowID)
		{
			//IL_0019: 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)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_031a: Unknown result type (might be due to invalid IL or missing references)
			//IL_033c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0599: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Label("Control The Boombox In The Cart", headerStyle, Array.Empty<GUILayoutOption>());
			scrollPosition = GUILayout.BeginScrollView(scrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			GUILayout.Space(10f);
			GUILayout.Label("Enter YouTube URL:", labelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			urlInput = GUILayout.TextField(urlInput, 200, textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) });
			GUILayout.EndHorizontal();
			GUILayout.Space(15f);
			float num = normalizedVolume * 100f;
			GUILayout.Label($"Volume: {Mathf.Round(num)}%", labelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			Rect lastRect;
			if ((int)Event.current.type == 0)
			{
				lastRect = GUILayoutUtility.GetLastRect();
				if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition))
				{
					isSliderBeingDragged = true;
				}
			}
			float num2 = GUILayout.HorizontalSlider(normalizedVolume, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>());
			if (num2 != normalizedVolume && !isSliderBeingDragged)
			{
				isSliderBeingDragged = true;
			}
			normalizedVolume = num2;
			if ((Object)(object)boombox != (Object)null && (Object)(object)boombox.audioSource != (Object)null)
			{
				float volume = normalizedVolume * boombox.maxVolumeLimit;
				boombox.audioSource.volume = volume;
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(15f);
			GUILayout.Label("Audio Quality: " + qualityLabels[qualityLevel], labelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if ((int)Event.current.type == 0)
			{
				lastRect = GUILayoutUtility.GetLastRect();
				if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition))
				{
					isQualitySliderBeingDragged = true;
				}
			}
			float num3 = GUILayout.HorizontalSlider((float)qualityLevel, 0f, 4f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>());
			int num4 = Mathf.RoundToInt(num3);
			if (num4 != qualityLevel && !isQualitySliderBeingDragged)
			{
				isQualitySliderBeingDragged = true;
			}
			qualityLevel = num4;
			if ((Object)(object)boombox != (Object)null)
			{
				boombox.SetQuality(qualityLevel);
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(15f);
			if (!string.IsNullOrEmpty(statusMessage))
			{
				GUILayout.Label(statusMessage, statusStyle, Array.Empty<GUILayoutOption>());
				GUILayout.Space(5f);
			}
			if (!string.IsNullOrEmpty(errorMessage))
			{
				GUI.color = Color.red;
				GUILayout.Label(errorMessage, labelStyle, Array.Empty<GUILayoutOption>());
				GUI.color = Color.white;
				GUILayout.Space(5f);
			}
			GUILayout.Space(10f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUI.enabled = (Object)(object)boombox != (Object)null && !boombox.IsDownloadInProgress();
			if (GUILayout.Button("▶ PLAY", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) }))
			{
				if (IsValidYoutubeUrl(urlInput))
				{
					photonView.RPC("RequestSong", (RpcTarget)0, new object[2]
					{
						urlInput,
						PhotonNetwork.LocalPlayer.ActorNumber
					});
					GUI.FocusControl((string)null);
				}
				else
				{
					ShowErrorMessage("Invalid YouTube URL!");
				}
			}
			GUI.enabled = true;
			GUI.enabled = (Object)(object)boombox != (Object)null && boombox.isPlaying;
			if (GUILayout.Button("■ STOP", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) }))
			{
				photonView.RPC("StopPlayback", (RpcTarget)0, new object[1] { PhotonNetwork.LocalPlayer.ActorNumber });
				GUI.FocusControl((string)null);
			}
			GUI.enabled = true;
			GUILayout.EndHorizontal();
			if ((Object)(object)boombox != (Object)null && boombox.IsDownloadInProgress())
			{
				GUILayout.Space(10f);
				GUILayout.Label("Download in progress...", statusStyle, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndScrollView();
			GUILayout.Space(10f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			if (GUILayout.Button("Close", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(100f),
				GUILayout.Height(30f)
			}))
			{
				if ((Object)(object)controller != (Object)null)
				{
					controller.ReleaseControl();
				}
				else
				{
					HideUI();
				}
			}
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
			GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref windowRect)).width, 30f));
		}

		private bool IsValidYoutubeUrl(string url)
		{
			return Boombox.IsValidYoutubeUrl(url);
		}

		private void ShowErrorMessage(string message)
		{
			Debug.LogError((object)message);
			errorMessage = message;
			errorMessageTime = Time.time + 3f;
		}

		private void OnDestroy()
		{
			if ((Object)(object)backgroundTexture != (Object)null)
			{
				Object.Destroy((Object)(object)backgroundTexture);
			}
			if ((Object)(object)buttonTexture != (Object)null)
			{
				Object.Destroy((Object)(object)buttonTexture);
			}
			if ((Object)(object)sliderBackgroundTexture != (Object)null)
			{
				Object.Destroy((Object)(object)sliderBackgroundTexture);
			}
			if ((Object)(object)sliderThumbTexture != (Object)null)
			{
				Object.Destroy((Object)(object)sliderThumbTexture);
			}
		}
	}
	[BepInPlugin("ColtG5.BoomboxCart", "BoomboxCart", "1.0.1")]
	public class BoomBoxCartMod : BaseUnityPlugin
	{
		private const string modGUID = "ColtG5.BoomboxCart";

		private const string modName = "BoomboxCart";

		private const string modVersion = "1.0.1";

		private readonly Harmony harmony = new Harmony("ColtG5.BoomboxCart");

		internal static BoomBoxCartMod instance;

		internal ManualLogSource logger;

		private void Awake()
		{
			if ((Object)(object)instance == (Object)null)
			{
				instance = this;
			}
			logger = Logger.CreateLogSource("ColtG5.BoomboxCart");
			logger.LogInfo((object)"BoomBoxCartMod loaded!");
			Task.Run(delegate
			{
				YoutubeDL.InitializeAsync().Wait();
			});
			harmony.PatchAll();
		}
	}
}
namespace BoomBoxCartMod.Util
{
	public static class YoutubeDL
	{
		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		private static readonly string baseFolder = Path.Combine(Directory.GetCurrentDirectory(), "BoomboxedCart");

		private const string YtDLP_URL = "https://github.com/yt-dlp/yt-dlp/releases/download/2025.02.19/yt-dlp.exe";

		private const string FFMPEG_URL = "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip";

		private static readonly string ytDlpPath = Path.Combine(baseFolder, "yt-dlp.exe");

		private static readonly string ffmpegFolder = Path.Combine(baseFolder, "ffmpeg");

		private static readonly string ffmpegBinPath = Path.Combine(ffmpegFolder, "ffmpeg-master-latest-win64-gpl", "bin", "ffmpeg.exe");

		private static ManualLogSource Logger => Instance.logger;

		public static async Task InitializeAsync()
		{
			if (!Directory.Exists(baseFolder))
			{
				Directory.CreateDirectory(baseFolder);
			}
			if (!File.Exists(ytDlpPath))
			{
				Console.WriteLine("yt-dlp not found. Downloading...");
				await DownloadFileAsync("https://github.com/yt-dlp/yt-dlp/releases/download/2025.02.19/yt-dlp.exe", ytDlpPath);
			}
			if (!Directory.Exists(ffmpegFolder))
			{
				Console.WriteLine("ffmpeg not found. Downloading and extracting...");
				await DownloadAndExtractFFmpegAsync();
			}
			if (!File.Exists(ffmpegBinPath))
			{
				throw new Exception("ffmpeg executable was not found after extraction.");
			}
			Console.WriteLine("Initialization complete.");
		}

		private static async Task DownloadFileAsync(string url, string destinationPath)
		{
			HttpClient client = new HttpClient();
			try
			{
				File.WriteAllBytes(destinationPath, await client.GetByteArrayAsync(url));
			}
			finally
			{
				((IDisposable)client)?.Dispose();
			}
		}

		private static async Task DownloadAndExtractFFmpegAsync()
		{
			string zipPath = Path.Combine(baseFolder, "ffmpeg.zip");
			await DownloadFileAsync("https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip", zipPath);
			ZipFile.ExtractToDirectory(zipPath, ffmpegFolder);
			File.Delete(zipPath);
		}

		public static async Task<(string filePath, string title)> DownloadAudioWithTitleAsync(string videoUrl)
		{
			await InitializeAsync();
			string tempFolder = Path.Combine(baseFolder, Guid.NewGuid().ToString());
			Directory.CreateDirectory(tempFolder);
			return await Task.Run(async delegate
			{
				try
				{
					string title = await GetVideoTitleInternalAsync(videoUrl);
					if (string.IsNullOrEmpty(title))
					{
						title = "Unknown Title";
					}
					string command = "-x --audio-format mp3 --ffmpeg-location \"" + ffmpegBinPath + "\" --output \"" + Path.Combine(tempFolder, "%(title)s.%(ext)s") + "\" " + videoUrl;
					ProcessStartInfo processInfo = new ProcessStartInfo
					{
						FileName = ytDlpPath,
						Arguments = command,
						RedirectStandardOutput = true,
						RedirectStandardError = true,
						UseShellExecute = false,
						CreateNoWindow = true
					};
					using (Process process = Process.Start(processInfo))
					{
						if (process == null)
						{
							throw new Exception("Failed to start yt-dlp process.");
						}
						process.WaitForExit();
						if (process.ExitCode != 0)
						{
							string error = process.StandardError.ReadToEnd();
							throw new Exception("yt-dlp error: " + error);
						}
					}
					string audioFilePath = Directory.GetFiles(tempFolder, "*.mp3").FirstOrDefault();
					if (audioFilePath == null)
					{
						throw new Exception("Audio download failed.");
					}
					return (audioFilePath, title);
				}
				catch (Exception ex2)
				{
					Exception ex = ex2;
					if (Directory.Exists(tempFolder))
					{
						Directory.Delete(tempFolder, recursive: true);
					}
					throw new Exception("Error downloading audio: " + ex.Message);
				}
			});
		}

		private static async Task<string> GetVideoTitleInternalAsync(string url)
		{
			try
			{
				await InitializeAsync();
				ProcessStartInfo psi = new ProcessStartInfo
				{
					FileName = ytDlpPath,
					Arguments = "--get-title " + url,
					UseShellExecute = false,
					RedirectStandardOutput = true,
					RedirectStandardError = true,
					CreateNoWindow = true
				};
				Process process = new Process
				{
					StartInfo = psi
				};
				try
				{
					new TaskCompletionSource<string>();
					process.Start();
					string title2 = await process.StandardOutput.ReadToEndAsync();
					await process.StandardError.ReadToEndAsync();
					Task timeoutTask = Task.Delay(10000);
					Task processExitTask = Task.Run(delegate
					{
						process.WaitForExit();
					});
					if (await Task.WhenAny(new Task[2] { processExitTask, timeoutTask }) == timeoutTask)
					{
						try
						{
							process.Kill();
						}
						catch
						{
						}
						Logger.LogWarning((object)"yt-dlp title fetch timed out");
						return "Unknown Title (Timeout)";
					}
					if (process.ExitCode != 0)
					{
						Logger.LogError((object)$"yt-dlp error code: {process.ExitCode}");
						return "Unknown Title";
					}
					title2 = title2.Trim();
					return string.IsNullOrEmpty(title2) ? "Unknown Title" : title2;
				}
				finally
				{
					if (process != null)
					{
						((IDisposable)process).Dispose();
					}
				}
			}
			catch (Exception ex2)
			{
				Exception ex = ex2;
				Logger.LogError((object)("Error getting video title: " + ex.Message));
				return "Unknown Title";
			}
		}
	}
}
namespace BoomBoxCartMod.Patches
{
	[HarmonyPatch(typeof(PhysGrabCart))]
	internal class PhysGrabCartPatch
	{
		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		private static ManualLogSource Logger => Instance.logger;

		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		private static void PatchPhysGrabCartStart(PhysGrabCart __instance)
		{
			if (!((Object)(object)RunManager.instance.levelCurrent == (Object)(object)RunManager.instance.levelShop))
			{
				if ((Object)(object)((Component)__instance).GetComponent<Boombox>() == (Object)null)
				{
					((Component)__instance).gameObject.AddComponent<Boombox>();
				}
				if ((Object)(object)((Component)__instance).GetComponent<BoomboxController>() == (Object)null)
				{
					((Component)__instance).gameObject.AddComponent<BoomboxController>();
				}
			}
		}
	}
	public static class PlayerGrabbingTracker
	{
		public static Dictionary<int, GameObject> playerGrabbingMap = new Dictionary<int, GameObject>();

		public static bool IsLocalPlayerGrabbingCart(GameObject cart)
		{
			int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
			return playerGrabbingMap.ContainsKey(actorNumber) && (Object)(object)playerGrabbingMap[actorNumber] == (Object)(object)cart;
		}

		public static void SetLocalPlayerGrabbing(GameObject obj)
		{
			int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
			if ((Object)(object)obj != (Object)null)
			{
				playerGrabbingMap[actorNumber] = obj;
			}
			else if (playerGrabbingMap.ContainsKey(actorNumber))
			{
				playerGrabbingMap.Remove(actorNumber);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerController))]
	internal class PlayerControllerPatch
	{
		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		private static ManualLogSource Logger => Instance.logger;

		[HarmonyPatch("Update")]
		[HarmonyPostfix]
		private static void PatchPlayerControllerUpdate(PlayerController __instance)
		{
			if ((Object)(object)__instance.physGrabObject != (Object)null)
			{
				PlayerGrabbingTracker.SetLocalPlayerGrabbing(__instance.physGrabObject.gameObject);
				if (Keyboard.current != null && ((ButtonControl)Keyboard.current.yKey).wasPressedThisFrame && (Object)(object)__instance.physGrabObject.GetComponent<Boombox>() != (Object)null)
				{
					BoomboxController component = __instance.physGrabObject.GetComponent<BoomboxController>();
					if ((Object)(object)component != (Object)null)
					{
						component.RequestBoomboxControl();
					}
				}
			}
			else
			{
				PlayerGrabbingTracker.SetLocalPlayerGrabbing(null);
			}
		}
	}
}