Decompiled source of MyCustomRadio v0.8.5

HowToFishCustomRadio.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Logging;
using FishNet;
using FishNet.Broadcast;
using FishNet.Connection;
using FishNet.Managing.Object;
using FishNet.Object;
using FishNet.Serializing;
using FishNet.Transporting;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("HowToFishCustomRadio")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("HowToFishCustomRadio")]
[assembly: AssemblyTitle("HowToFishCustomRadio")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace CustomRadioFramework;

public struct RadioUploadChunkMsg : IBroadcast
{
	public int trackId;

	public int chunkIndex;

	public int totalChunks;

	public byte[] data;
}
public struct RadioPlayChunkMsg : IBroadcast
{
	public int trackId;

	public int chunkIndex;

	public int totalChunks;

	public byte[] data;
}
public struct RadioControlMsg : IBroadcast
{
	public bool isPaused;

	public float timePosition;
}
public struct RadioVolumeMsg : IBroadcast
{
	public float volume;
}
public struct SyncRolesMsg : IBroadcast
{
	public int[] modIds;
}
public static class CustomSerializers
{
	public static void WriteUploadChunk(this Writer writer, RadioUploadChunkMsg msg)
	{
		writer.Write<int>(msg.trackId);
		writer.Write<int>(msg.chunkIndex);
		writer.Write<int>(msg.totalChunks);
		writer.Write<byte[]>(msg.data);
	}

	public static RadioUploadChunkMsg ReadUploadChunk(this Reader reader)
	{
		return new RadioUploadChunkMsg
		{
			trackId = reader.Read<int>(),
			chunkIndex = reader.Read<int>(),
			totalChunks = reader.Read<int>(),
			data = reader.Read<byte[]>()
		};
	}

	public static void WritePlayChunk(this Writer writer, RadioPlayChunkMsg msg)
	{
		writer.Write<int>(msg.trackId);
		writer.Write<int>(msg.chunkIndex);
		writer.Write<int>(msg.totalChunks);
		writer.Write<byte[]>(msg.data);
	}

	public static RadioPlayChunkMsg ReadPlayChunk(this Reader reader)
	{
		return new RadioPlayChunkMsg
		{
			trackId = reader.Read<int>(),
			chunkIndex = reader.Read<int>(),
			totalChunks = reader.Read<int>(),
			data = reader.Read<byte[]>()
		};
	}

	public static void WriteControl(this Writer writer, RadioControlMsg msg)
	{
		writer.Write<bool>(msg.isPaused);
		writer.Write<float>(msg.timePosition);
	}

	public static RadioControlMsg ReadControl(this Reader reader)
	{
		return new RadioControlMsg
		{
			isPaused = reader.Read<bool>(),
			timePosition = reader.Read<float>()
		};
	}

	public static void WriteVolume(this Writer writer, RadioVolumeMsg msg)
	{
		writer.Write<float>(msg.volume);
	}

	public static RadioVolumeMsg ReadVolume(this Reader reader)
	{
		return new RadioVolumeMsg
		{
			volume = reader.Read<float>()
		};
	}

	public static void WriteSyncRoles(this Writer writer, SyncRolesMsg msg)
	{
		if (msg.modIds == null)
		{
			writer.Write<int>(0);
			return;
		}
		writer.Write<int>(msg.modIds.Length);
		int[] modIds = msg.modIds;
		foreach (int num in modIds)
		{
			writer.Write<int>(num);
		}
	}

	public static SyncRolesMsg ReadSyncRoles(this Reader reader)
	{
		int num = reader.Read<int>();
		int[] array = new int[num];
		for (int i = 0; i < num; i++)
		{
			array[i] = reader.Read<int>();
		}
		return new SyncRolesMsg
		{
			modIds = array
		};
	}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class OpenFileName
{
	public int structSize;

	public IntPtr hwndOwner = IntPtr.Zero;

	public IntPtr hInstance = IntPtr.Zero;

	public string filter;

	public string customFilter;

	public int maxCustFilter;

	public int filterIndex;

	public string file;

	public int maxFile;

	public string fileTitle;

	public int maxFileTitle;

	public string initialDir;

	public string title;

	public int flags;

	public short fileOffset;

	public short fileExtension;

	public string defExt;

	public IntPtr custData = IntPtr.Zero;

	public IntPtr hook = IntPtr.Zero;

	public string templateName;

	public IntPtr reservedPtr = IntPtr.Zero;

	public int reservedInt;

	public int flagsEx;
}
public class LocalDialog
{
	[DllImport("Comdlg32.dll", CharSet = CharSet.Auto, SetLastError = true, ThrowOnUnmappableChar = true)]
	public static extern bool GetOpenFileName([In][Out] OpenFileName ofn);
}
[BepInPlugin("com.customradio.howtofish", "Custom Radio Framework", "1.1.0")]
public class Plugin : BaseUnityPlugin
{
	private const string _coreWatermark = "Architecture & NetCode by Stasochec";

	public static ManualLogSource Log;

	public static float LocalIgnoreNetworkTime;

	private CustomRadioNetworkService networkService;

	private CustomRadioAudioController audioController;

	private CustomRadioUI uiManager;

	private void Awake()
	{
		Log = ((BaseUnityPlugin)this).Logger;
		Log.LogInfo((object)"Custom Radio v1.1.0 (Safe & Fast Edition) loaded. [Architecture & NetCode by Stasochec]");
		CleanupOldTempFiles();
		audioController = new CustomRadioAudioController(this);
		networkService = new CustomRadioNetworkService(this, audioController);
		uiManager = new CustomRadioUI(this, networkService, audioController);
		((MonoBehaviour)this).StartCoroutine(WaitForNetwork());
	}

	private IEnumerator WaitForNetwork()
	{
		while ((Object)(object)InstanceFinder.NetworkManager == (Object)null || (Object)(object)InstanceFinder.ClientManager == (Object)null)
		{
			yield return null;
		}
		networkService.RegisterCallbacks();
	}

	private void Update()
	{
		uiManager.HandleInput();
		networkService.ProcessTimeouts();
	}

	private void OnGUI()
	{
		uiManager.DrawUI();
	}

	private void OnDestroy()
	{
		networkService?.UnregisterCallbacks();
		audioController?.Dispose();
		CleanupOldTempFiles();
	}

	private void CleanupOldTempFiles()
	{
		try
		{
			string[] files = Directory.GetFiles(Application.temporaryCachePath, "radio_sync_*.mp3");
			string[] files2 = Directory.GetFiles(Application.temporaryCachePath, "moderation_*.mp3");
			string[] array = files;
			for (int i = 0; i < array.Length; i++)
			{
				File.Delete(array[i]);
			}
			array = files2;
			for (int i = 0; i < array.Length; i++)
			{
				File.Delete(array[i]);
			}
		}
		catch
		{
		}
	}
}
public class CustomRadioAudioController : IDisposable
{
	private Plugin plugin;

	private int _mainLoadToken;

	private int _previewLoadToken;

	public AudioSource Source { get; private set; }

	public AudioSource PreviewSource { get; private set; }

	public bool IsPaused { get; private set; }

	public float CurrentVolume { get; set; } = 1f;

	public CustomRadioAudioController(Plugin plugin)
	{
		this.plugin = plugin;
	}

	public void EnsureRadioExists()
	{
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)Source != (Object)null)
		{
			return;
		}
		GameObject val = null;
		GameObject val2 = GameObject.Find("LocalPlayer");
		float num = float.MaxValue;
		if ((Object)(object)InstanceFinder.ClientManager != (Object)null)
		{
			foreach (NetworkObject value in ((ManagedObjects)InstanceFinder.ClientManager.Objects).Spawned.Values)
			{
				if (((Object)((Component)value).gameObject).name.IndexOf("Radio", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					if (!((Object)(object)val2 != (Object)null))
					{
						val = ((Component)value).gameObject;
						break;
					}
					float num2 = Vector3.Distance(val2.transform.position, ((Component)value).transform.position);
					if (num2 < num)
					{
						num = num2;
						val = ((Component)value).gameObject;
					}
				}
			}
		}
		if ((Object)(object)val == (Object)null && (Object)(object)val2 != (Object)null)
		{
			val = val2;
		}
		if ((Object)(object)val != (Object)null)
		{
			Transform val3 = val.transform.Find("CustomRadioSource");
			GameObject val4 = (GameObject)(((Object)(object)val3 != (Object)null) ? ((object)((Component)val3).gameObject) : ((object)new GameObject("CustomRadioSource")));
			val4.transform.SetParent(val.transform);
			val4.transform.localPosition = Vector3.zero;
			Source = val4.GetComponent<AudioSource>() ?? val4.AddComponent<AudioSource>();
			Source.spatialBlend = 1f;
			Source.minDistance = 3f;
			Source.maxDistance = 40f;
			Source.rolloffMode = (AudioRolloffMode)1;
			Source.volume = CurrentVolume;
		}
	}

	private void EnsurePreviewExists()
	{
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)PreviewSource != (Object)null))
		{
			GameObject val = GameObject.Find("LocalPlayer");
			if ((Object)(object)val != (Object)null)
			{
				Transform val2 = val.transform.Find("RadioPreviewSource");
				GameObject val3 = (GameObject)(((Object)(object)val2 != (Object)null) ? ((object)((Component)val2).gameObject) : ((object)new GameObject("RadioPreviewSource")));
				val3.transform.SetParent(val.transform);
				PreviewSource = val3.GetComponent<AudioSource>() ?? val3.AddComponent<AudioSource>();
				PreviewSource.spatialBlend = 0f;
				PreviewSource.volume = 0.5f;
			}
		}
	}

	public void PlayMain(string path)
	{
		EnsureRadioExists();
		int currentToken = ++_mainLoadToken;
		string url = "file:///" + path.Replace("\\", "/");
		((MonoBehaviour)plugin).StartCoroutine(LoadAndPlayAudio(Source, url, isMain: true, () => _mainLoadToken == currentToken));
	}

	public void PlayPreview(string path)
	{
		EnsurePreviewExists();
		int currentToken = ++_previewLoadToken;
		string url = "file:///" + path.Replace("\\", "/");
		((MonoBehaviour)plugin).StartCoroutine(LoadAndPlayAudio(PreviewSource, url, isMain: false, () => _previewLoadToken == currentToken));
	}

	public void StopPreview()
	{
		_previewLoadToken++;
		if ((Object)(object)PreviewSource != (Object)null)
		{
			PreviewSource.Stop();
		}
	}

	private IEnumerator LoadAndPlayAudio(AudioSource targetSource, string url, bool isMain, Func<bool> isLoadValid)
	{
		UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(url, (AudioType)13);
		try
		{
			yield return www.SendWebRequest();
			if ((Object)(object)targetSource == (Object)null || !isLoadValid())
			{
				yield break;
			}
			if ((int)www.result == 1)
			{
				AudioClip content = DownloadHandlerAudioClip.GetContent(www);
				if ((Object)(object)targetSource.clip != (Object)null)
				{
					Object.Destroy((Object)(object)targetSource.clip);
				}
				targetSource.clip = content;
				if (isMain)
				{
					IsPaused = false;
				}
				targetSource.Play();
			}
		}
		finally
		{
			((IDisposable)www)?.Dispose();
		}
	}

	public void ApplyControlState(bool pauseState, float timePosition)
	{
		if (!((Object)(object)Source == (Object)null) && !float.IsNaN(timePosition) && !float.IsInfinity(timePosition))
		{
			IsPaused = pauseState;
			Source.time = timePosition;
			if (IsPaused)
			{
				Source.Pause();
			}
			else
			{
				Source.Play();
			}
		}
	}

	public void ApplyVolume(float vol)
	{
		if (!float.IsNaN(vol) && !float.IsInfinity(vol))
		{
			CurrentVolume = Mathf.Clamp(vol, 0f, 1f);
			if ((Object)(object)Source != (Object)null)
			{
				Source.volume = CurrentVolume;
			}
		}
	}

	public void Dispose()
	{
		if ((Object)(object)Source != (Object)null && (Object)(object)Source.clip != (Object)null)
		{
			Object.Destroy((Object)(object)Source.clip);
		}
		if ((Object)(object)PreviewSource != (Object)null && (Object)(object)PreviewSource.clip != (Object)null)
		{
			Object.Destroy((Object)(object)PreviewSource.clip);
		}
	}
}
public class CustomRadioNetworkService
{
	public class PendingTrack
	{
		public int TrackId;

		public string Path;
	}

	private class DownloadContext
	{
		public FileStream Stream;

		public int ExpectedChunks;

		public HashSet<int> ReceivedChunks;

		public float LastActivityTime;

		public string TempPath;
	}

	public const int CHUNK_SIZE = 12288;

	private const float TARGET_BPS = 204800f;

	private const float DOWNLOAD_TIMEOUT = 120f;

	private const int MAX_FILE_SIZE = 15728640;

	private const int MAX_CHUNKS = 2000;

	private Plugin plugin;

	private CustomRadioAudioController audio;

	private bool isRegistered;

	private Dictionary<int, DownloadContext> activeDownloads = new Dictionary<int, DownloadContext>();

	private Dictionary<int, DownloadContext> activeUploads = new Dictionary<int, DownloadContext>();

	private List<PendingTrack> moderationQueue = new List<PendingTrack>();

	public HashSet<int> ServerModerators = new HashSet<int>();

	public bool IsLocalModerator;

	public float UploadProgress;

	public float DownloadProgress;

	public CustomRadioNetworkService(Plugin plugin, CustomRadioAudioController audio)
	{
		this.plugin = plugin;
		this.audio = audio;
	}

	public List<PendingTrack> GetModerationQueue()
	{
		return moderationQueue;
	}

	public void RegisterCallbacks()
	{
		if (isRegistered)
		{
			return;
		}
		GenericWriter<RadioUploadChunkMsg>.SetWrite((Action<Writer, RadioUploadChunkMsg>)CustomSerializers.WriteUploadChunk);
		GenericReader<RadioUploadChunkMsg>.SetRead((Func<Reader, RadioUploadChunkMsg>)CustomSerializers.ReadUploadChunk);
		GenericWriter<RadioPlayChunkMsg>.SetWrite((Action<Writer, RadioPlayChunkMsg>)CustomSerializers.WritePlayChunk);
		GenericReader<RadioPlayChunkMsg>.SetRead((Func<Reader, RadioPlayChunkMsg>)CustomSerializers.ReadPlayChunk);
		GenericWriter<RadioControlMsg>.SetWrite((Action<Writer, RadioControlMsg>)CustomSerializers.WriteControl);
		GenericReader<RadioControlMsg>.SetRead((Func<Reader, RadioControlMsg>)CustomSerializers.ReadControl);
		GenericWriter<RadioVolumeMsg>.SetWrite((Action<Writer, RadioVolumeMsg>)CustomSerializers.WriteVolume);
		GenericReader<RadioVolumeMsg>.SetRead((Func<Reader, RadioVolumeMsg>)CustomSerializers.ReadVolume);
		GenericWriter<SyncRolesMsg>.SetWrite((Action<Writer, SyncRolesMsg>)CustomSerializers.WriteSyncRoles);
		GenericReader<SyncRolesMsg>.SetRead((Func<Reader, SyncRolesMsg>)CustomSerializers.ReadSyncRoles);
		InstanceFinder.ClientManager.RegisterBroadcast<RadioPlayChunkMsg>((Action<RadioPlayChunkMsg, Channel>)OnClientReceivePlayChunk);
		InstanceFinder.ClientManager.RegisterBroadcast<RadioControlMsg>((Action<RadioControlMsg, Channel>)OnClientReceiveControl);
		InstanceFinder.ClientManager.RegisterBroadcast<RadioVolumeMsg>((Action<RadioVolumeMsg, Channel>)OnClientReceiveVolume);
		InstanceFinder.ClientManager.RegisterBroadcast<SyncRolesMsg>((Action<SyncRolesMsg, Channel>)OnClientReceiveRoles);
		InstanceFinder.ServerManager.RegisterBroadcast<RadioUploadChunkMsg>((Action<NetworkConnection, RadioUploadChunkMsg, Channel>)OnServerReceiveUploadChunk, true);
		InstanceFinder.ServerManager.RegisterBroadcast<RadioControlMsg>((Action<NetworkConnection, RadioControlMsg, Channel>)OnServerReceiveControl, true);
		InstanceFinder.ServerManager.RegisterBroadcast<RadioVolumeMsg>((Action<NetworkConnection, RadioVolumeMsg, Channel>)OnServerReceiveVolume, true);
		InstanceFinder.ServerManager.OnRemoteConnectionState += delegate(NetworkConnection conn, RemoteConnectionStateArgs args)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if ((int)args.ConnectionState == 2)
			{
				BroadcastRoles();
			}
			else if ((int)args.ConnectionState == 0)
			{
				ToggleModerator(conn.ClientId, state: false);
			}
		};
		isRegistered = true;
	}

	public void UnregisterCallbacks()
	{
		if (isRegistered)
		{
			if ((Object)(object)InstanceFinder.ClientManager != (Object)null)
			{
				InstanceFinder.ClientManager.UnregisterBroadcast<RadioPlayChunkMsg>((Action<RadioPlayChunkMsg, Channel>)OnClientReceivePlayChunk);
				InstanceFinder.ClientManager.UnregisterBroadcast<RadioControlMsg>((Action<RadioControlMsg, Channel>)OnClientReceiveControl);
				InstanceFinder.ClientManager.UnregisterBroadcast<RadioVolumeMsg>((Action<RadioVolumeMsg, Channel>)OnClientReceiveVolume);
				InstanceFinder.ClientManager.UnregisterBroadcast<SyncRolesMsg>((Action<SyncRolesMsg, Channel>)OnClientReceiveRoles);
			}
			if ((Object)(object)InstanceFinder.ServerManager != (Object)null)
			{
				InstanceFinder.ServerManager.UnregisterBroadcast<RadioUploadChunkMsg>((Action<NetworkConnection, RadioUploadChunkMsg, Channel>)OnServerReceiveUploadChunk);
				InstanceFinder.ServerManager.UnregisterBroadcast<RadioControlMsg>((Action<NetworkConnection, RadioControlMsg, Channel>)OnServerReceiveControl);
				InstanceFinder.ServerManager.UnregisterBroadcast<RadioVolumeMsg>((Action<NetworkConnection, RadioVolumeMsg, Channel>)OnServerReceiveVolume);
			}
		}
	}

	public void ToggleModerator(int clientId, bool state)
	{
		if (InstanceFinder.IsServerStarted)
		{
			if (state)
			{
				ServerModerators.Add(clientId);
			}
			else
			{
				ServerModerators.Remove(clientId);
			}
			BroadcastRoles();
		}
	}

	private void BroadcastRoles()
	{
		if (InstanceFinder.IsServerStarted)
		{
			int[] modIds = ServerModerators.ToArray();
			InstanceFinder.ServerManager.Broadcast<SyncRolesMsg>(new SyncRolesMsg
			{
				modIds = modIds
			}, true, (Channel)0);
		}
	}

	private void OnClientReceiveRoles(SyncRolesMsg msg, Channel channel)
	{
		if (!((Object)(object)InstanceFinder.ClientManager == (Object)null) && !(InstanceFinder.ClientManager.Connection == (NetworkConnection)null))
		{
			int clientId = InstanceFinder.ClientManager.Connection.ClientId;
			IsLocalModerator = msg.modIds != null && msg.modIds.Contains(clientId);
		}
	}

	public void SendFile(string path)
	{
		((MonoBehaviour)plugin).StartCoroutine(ClientUploadCoroutine(path));
	}

	private IEnumerator ClientUploadCoroutine(string path)
	{
		if ((Object)(object)InstanceFinder.ClientManager == (Object)null)
		{
			yield break;
		}
		FileInfo fileInfo = new FileInfo(path);
		if (fileInfo.Length > 15728640)
		{
			yield break;
		}
		int trackId = Random.Range(1000, 999999);
		int totalChunks = Mathf.CeilToInt((float)fileInfo.Length / 12288f);
		UploadProgress = 0.01f;
		using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
		{
			byte[] buffer = new byte[12288];
			int chunkIndex = 0;
			float allowance = 12288f;
			float lastTime = Time.realtimeSinceStartup;
			while (true)
			{
				int num;
				int bytesRead = (num = fs.Read(buffer, 0, buffer.Length));
				if (num <= 0)
				{
					break;
				}
				UploadProgress = (float)chunkIndex / (float)totalChunks;
				while (allowance < (float)bytesRead)
				{
					yield return null;
					float realtimeSinceStartup = Time.realtimeSinceStartup;
					allowance += (realtimeSinceStartup - lastTime) * 204800f;
					lastTime = realtimeSinceStartup;
				}
				if (allowance > 49152f)
				{
					allowance = 49152f;
				}
				byte[] array;
				if (bytesRead < 12288)
				{
					array = new byte[bytesRead];
					Buffer.BlockCopy(buffer, 0, array, 0, bytesRead);
				}
				else
				{
					array = buffer;
				}
				InstanceFinder.ClientManager.Broadcast<RadioUploadChunkMsg>(new RadioUploadChunkMsg
				{
					trackId = trackId,
					chunkIndex = chunkIndex,
					totalChunks = totalChunks,
					data = array
				}, (Channel)0);
				allowance -= (float)bytesRead;
				chunkIndex++;
				yield return null;
			}
		}
		UploadProgress = 0f;
	}

	private void OnServerReceiveUploadChunk(NetworkConnection conn, RadioUploadChunkMsg msg, Channel channel)
	{
		if (msg.totalChunks <= 0 || msg.totalChunks > 2000 || msg.chunkIndex < 0 || msg.chunkIndex >= msg.totalChunks || msg.data == null || msg.data.Length > 13312)
		{
			return;
		}
		int num = Mathf.Abs(msg.trackId);
		if (!activeUploads.TryGetValue(num, out var value))
		{
			if (activeUploads.Count >= 5)
			{
				return;
			}
			string text = Path.Combine(Application.temporaryCachePath, $"moderation_{num}.mp3");
			value = new DownloadContext
			{
				TempPath = text,
				Stream = new FileStream(text, FileMode.Create, FileAccess.Write, FileShare.None),
				ExpectedChunks = msg.totalChunks,
				ReceivedChunks = new HashSet<int>(),
				LastActivityTime = Time.realtimeSinceStartup
			};
			activeUploads[num] = value;
		}
		value.Stream.Seek((long)msg.chunkIndex * 12288L, SeekOrigin.Begin);
		value.Stream.Write(msg.data, 0, msg.data.Length);
		value.ReceivedChunks.Add(msg.chunkIndex);
		value.LastActivityTime = Time.realtimeSinceStartup;
		if (value.ReceivedChunks.Count < value.ExpectedChunks)
		{
			return;
		}
		value.Stream.Dispose();
		activeUploads.Remove(num);
		if (moderationQueue.Count >= 10)
		{
			if (File.Exists(value.TempPath))
			{
				File.Delete(value.TempPath);
			}
		}
		else
		{
			moderationQueue.Add(new PendingTrack
			{
				TrackId = num,
				Path = value.TempPath
			});
		}
	}

	public void ApproveTrack(PendingTrack track, float initialVolume)
	{
		moderationQueue.Remove(track);
		InstanceFinder.ServerManager.Broadcast<RadioVolumeMsg>(new RadioVolumeMsg
		{
			volume = initialVolume
		}, true, (Channel)0);
		((MonoBehaviour)plugin).StartCoroutine(ServerStreamCoroutine(track.Path, track.TrackId));
	}

	public void DenyTrack(PendingTrack track)
	{
		moderationQueue.Remove(track);
		if (File.Exists(track.Path))
		{
			File.Delete(track.Path);
		}
	}

	private IEnumerator ServerStreamCoroutine(string path, int trackId)
	{
		if ((Object)(object)InstanceFinder.ServerManager == (Object)null)
		{
			yield break;
		}
		FileInfo fileInfo = new FileInfo(path);
		int totalChunks = Mathf.CeilToInt((float)fileInfo.Length / 12288f);
		using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
		{
			byte[] buffer = new byte[12288];
			int chunkIndex = 0;
			float allowance = 12288f;
			float lastTime = Time.realtimeSinceStartup;
			while (true)
			{
				int num;
				int bytesRead = (num = fs.Read(buffer, 0, buffer.Length));
				if (num <= 0)
				{
					break;
				}
				while (allowance < (float)bytesRead)
				{
					yield return null;
					float realtimeSinceStartup = Time.realtimeSinceStartup;
					allowance += (realtimeSinceStartup - lastTime) * 204800f;
					lastTime = realtimeSinceStartup;
				}
				if (allowance > 49152f)
				{
					allowance = 49152f;
				}
				byte[] array;
				if (bytesRead < 12288)
				{
					array = new byte[bytesRead];
					Buffer.BlockCopy(buffer, 0, array, 0, bytesRead);
				}
				else
				{
					array = buffer;
				}
				InstanceFinder.ServerManager.Broadcast<RadioPlayChunkMsg>(new RadioPlayChunkMsg
				{
					trackId = trackId,
					chunkIndex = chunkIndex,
					totalChunks = totalChunks,
					data = array
				}, true, (Channel)0);
				allowance -= (float)bytesRead;
				chunkIndex++;
				yield return null;
			}
		}
		if (File.Exists(path))
		{
			File.Delete(path);
		}
	}

	private void OnClientReceivePlayChunk(RadioPlayChunkMsg msg, Channel channel)
	{
		if (msg.totalChunks > 0 && msg.totalChunks <= 2000 && msg.chunkIndex >= 0 && msg.chunkIndex < msg.totalChunks)
		{
			int num = Mathf.Abs(msg.trackId);
			if (!activeDownloads.TryGetValue(num, out var value))
			{
				string text = Path.Combine(Application.temporaryCachePath, $"radio_sync_{num}.mp3");
				value = new DownloadContext
				{
					TempPath = text,
					Stream = new FileStream(text, FileMode.Create, FileAccess.Write, FileShare.None),
					ExpectedChunks = msg.totalChunks,
					ReceivedChunks = new HashSet<int>(),
					LastActivityTime = Time.realtimeSinceStartup
				};
				activeDownloads[num] = value;
			}
			value.Stream.Seek((long)msg.chunkIndex * 12288L, SeekOrigin.Begin);
			value.Stream.Write(msg.data, 0, msg.data.Length);
			value.ReceivedChunks.Add(msg.chunkIndex);
			value.LastActivityTime = Time.realtimeSinceStartup;
			DownloadProgress = (float)value.ReceivedChunks.Count / (float)value.ExpectedChunks;
			if (value.ReceivedChunks.Count >= value.ExpectedChunks)
			{
				DownloadProgress = 0f;
				value.Stream.Dispose();
				activeDownloads.Remove(num);
				audio.PlayMain(value.TempPath);
			}
		}
	}

	public void SendControl(bool isPaused, float time)
	{
		if ((Object)(object)InstanceFinder.ClientManager != (Object)null)
		{
			InstanceFinder.ClientManager.Broadcast<RadioControlMsg>(new RadioControlMsg
			{
				isPaused = isPaused,
				timePosition = time
			}, (Channel)0);
		}
	}

	public void SendVolume(float vol)
	{
		if ((Object)(object)InstanceFinder.ClientManager != (Object)null)
		{
			InstanceFinder.ClientManager.Broadcast<RadioVolumeMsg>(new RadioVolumeMsg
			{
				volume = vol
			}, (Channel)0);
		}
	}

	private void OnServerReceiveControl(NetworkConnection conn, RadioControlMsg msg, Channel channel)
	{
		if (!float.IsNaN(msg.timePosition) && !float.IsInfinity(msg.timePosition) && !(msg.timePosition < 0f) && (conn == InstanceFinder.ClientManager.Connection || ServerModerators.Contains(conn.ClientId)))
		{
			InstanceFinder.ServerManager.Broadcast<RadioControlMsg>(msg, true, (Channel)0);
		}
	}

	private void OnServerReceiveVolume(NetworkConnection conn, RadioVolumeMsg msg, Channel channel)
	{
		if (!float.IsNaN(msg.volume) && !float.IsInfinity(msg.volume) && !(msg.volume < 0f) && !(msg.volume > 1f) && (conn == InstanceFinder.ClientManager.Connection || ServerModerators.Contains(conn.ClientId)))
		{
			InstanceFinder.ServerManager.Broadcast<RadioVolumeMsg>(msg, true, (Channel)0);
		}
	}

	private void OnClientReceiveControl(RadioControlMsg msg, Channel channel)
	{
		if (!(Time.realtimeSinceStartup - Plugin.LocalIgnoreNetworkTime < 0.8f))
		{
			audio.ApplyControlState(msg.isPaused, msg.timePosition);
		}
	}

	private void OnClientReceiveVolume(RadioVolumeMsg msg, Channel channel)
	{
		if (!(Time.realtimeSinceStartup - Plugin.LocalIgnoreNetworkTime < 0.8f))
		{
			audio.ApplyVolume(msg.volume);
		}
	}

	public void ProcessTimeouts()
	{
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		List<int> list = null;
		foreach (KeyValuePair<int, DownloadContext> activeUpload in activeUploads)
		{
			if (realtimeSinceStartup - activeUpload.Value.LastActivityTime > 120f)
			{
				if (list == null)
				{
					list = new List<int>();
				}
				list.Add(activeUpload.Key);
			}
		}
		if (list != null)
		{
			foreach (int item in list)
			{
				DownloadContext downloadContext = activeUploads[item];
				downloadContext.Stream.Dispose();
				if (File.Exists(downloadContext.TempPath))
				{
					File.Delete(downloadContext.TempPath);
				}
				activeUploads.Remove(item);
			}
		}
		List<int> list2 = null;
		foreach (KeyValuePair<int, DownloadContext> activeDownload in activeDownloads)
		{
			if (realtimeSinceStartup - activeDownload.Value.LastActivityTime > 120f)
			{
				if (list2 == null)
				{
					list2 = new List<int>();
				}
				list2.Add(activeDownload.Key);
			}
		}
		if (list2 != null)
		{
			foreach (int item2 in list2)
			{
				DownloadContext downloadContext2 = activeDownloads[item2];
				downloadContext2.Stream.Dispose();
				if (File.Exists(downloadContext2.TempPath))
				{
					File.Delete(downloadContext2.TempPath);
				}
				activeDownloads.Remove(item2);
			}
		}
		if (activeDownloads.Count == 0)
		{
			DownloadProgress = 0f;
		}
		if (activeUploads.Count == 0)
		{
			UploadProgress = 0f;
		}
	}
}
public class CustomRadioUI
{
	private Plugin plugin;

	private CustomRadioNetworkService network;

	private CustomRadioAudioController audio;

	private bool showUI;

	private bool showRolesUI;

	private bool isDialogOpen;

	private ConcurrentQueue<string> pendingFiles = new ConcurrentQueue<string>();

	private float lastSeekSendTime;

	private float lastVolumeSendTime;

	private GUIStyle windowStyle;

	private GUIStyle btnStyle;

	private GUIStyle labelStyle;

	private GUIStyle sliderTrackStyle;

	private GUIStyle sliderThumbStyle;

	private bool stylesInitialized;

	private bool isRu;

	private Dictionary<int, string> _nameCache = new Dictionary<int, string>();

	public CustomRadioUI(Plugin plugin, CustomRadioNetworkService network, CustomRadioAudioController audio)
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Invalid comparison between Unknown and I4
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Invalid comparison between Unknown and I4
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Invalid comparison between Unknown and I4
		this.plugin = plugin;
		this.network = network;
		this.audio = audio;
		isRu = (int)Application.systemLanguage == 30 || (int)Application.systemLanguage == 38 || (int)Application.systemLanguage == 3;
	}

	private string T(string ru, string en)
	{
		if (!isRu)
		{
			return en;
		}
		return ru;
	}

	private void InitStyles()
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Expected O, but got Unknown
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Expected O, but got Unknown
		//IL_0186: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01be: Expected O, but got Unknown
		//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_020a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0214: Expected O, but got Unknown
		//IL_0236: Unknown result type (might be due to invalid IL or missing references)
		//IL_0253: Unknown result type (might be due to invalid IL or missing references)
		if (!stylesInitialized)
		{
			windowStyle = new GUIStyle(GUI.skin.box);
			windowStyle.normal.background = MakeTex(2, 2, new Color(0.08f, 0.08f, 0.08f, 0.95f));
			windowStyle.normal.textColor = Color.white;
			windowStyle.alignment = (TextAnchor)1;
			windowStyle.fontStyle = (FontStyle)1;
			windowStyle.fontSize = 14;
			btnStyle = new GUIStyle(GUI.skin.button);
			btnStyle.normal.background = MakeTex(2, 2, new Color(0.18f, 0.18f, 0.18f, 1f));
			btnStyle.hover.background = MakeTex(2, 2, new Color(0.35f, 0.35f, 0.35f, 1f));
			btnStyle.active.background = MakeTex(2, 2, new Color(0.5f, 0.5f, 0.5f, 1f));
			btnStyle.normal.textColor = Color.white;
			btnStyle.fontStyle = (FontStyle)1;
			labelStyle = new GUIStyle(GUI.skin.label);
			labelStyle.normal.textColor = new Color(0.85f, 0.85f, 0.85f, 1f);
			labelStyle.alignment = (TextAnchor)4;
			labelStyle.fontSize = 13;
			sliderTrackStyle = new GUIStyle(GUI.skin.horizontalSlider);
			sliderTrackStyle.normal.background = MakeTex(2, 2, new Color(0.2f, 0.2f, 0.2f, 1f));
			sliderTrackStyle.fixedHeight = 6f;
			sliderThumbStyle = new GUIStyle(GUI.skin.horizontalSliderThumb);
			sliderThumbStyle.normal.background = MakeTex(2, 2, new Color(0.8f, 0.8f, 0.8f, 1f));
			sliderThumbStyle.hover.background = MakeTex(2, 2, Color.white);
			sliderThumbStyle.fixedWidth = 14f;
			sliderThumbStyle.fixedHeight = 14f;
			stylesInitialized = true;
		}
	}

	private Texture2D MakeTex(int width, int height, Color col)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: 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_0034: Expected O, but got Unknown
		Color[] array = (Color[])(object)new Color[width * height];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = col;
		}
		Texture2D val = new Texture2D(width, height);
		val.SetPixels(array);
		val.Apply();
		return val;
	}

	public void HandleInput()
	{
		if (Input.GetKeyDown((KeyCode)121))
		{
			showUI = !showUI;
			audio.EnsureRadioExists();
		}
		if (pendingFiles.TryDequeue(out var result))
		{
			network.SendFile(result);
		}
	}

	private string GetPlayerName(NetworkConnection conn)
	{
		if (_nameCache.TryGetValue(conn.ClientId, out var value))
		{
			return value;
		}
		string text = string.Format("{0} #{1}", T("Игрок", "Player"), conn.ClientId);
		if ((Object)(object)conn.FirstObject != (Object)null)
		{
			MonoBehaviour[] componentsInChildren = ((Component)conn.FirstObject).GetComponentsInChildren<MonoBehaviour>();
			string[] array = new string[10] { "Username", "Nickname", "PlayerName", "playerName", "nickName", "_username", "_nickname", "_playerName", "steamName", "SteamName" };
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			MonoBehaviour[] array2 = componentsInChildren;
			foreach (MonoBehaviour val in array2)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Type type = ((object)val).GetType();
				if (type.Namespace != null && (type.Namespace.StartsWith("UnityEngine") || type.Namespace.StartsWith("FishNet")))
				{
					continue;
				}
				string[] array3 = array;
				foreach (string name in array3)
				{
					PropertyInfo property = type.GetProperty(name, bindingAttr);
					if (property != null && property.PropertyType == typeof(string))
					{
						string text2 = (string)property.GetValue(val);
						if (!string.IsNullOrEmpty(text2) && !text2.Contains("(Clone)"))
						{
							text = $"{text2} (ID: {conn.ClientId})";
							break;
						}
					}
					FieldInfo field = type.GetField(name, bindingAttr);
					if (field != null && field.FieldType == typeof(string))
					{
						string text3 = (string)field.GetValue(val);
						if (!string.IsNullOrEmpty(text3) && !text3.Contains("(Clone)"))
						{
							text = $"{text3} (ID: {conn.ClientId})";
							break;
						}
					}
				}
				if (text != string.Format("{0} #{1}", T("Игрок", "Player"), conn.ClientId))
				{
					break;
				}
			}
		}
		_nameCache[conn.ClientId] = text;
		return text;
	}

	public void DrawUI()
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_0171: Unknown result type (might be due to invalid IL or missing references)
		//IL_0553: Unknown result type (might be due to invalid IL or missing references)
		//IL_023b: Unknown result type (might be due to invalid IL or missing references)
		//IL_026c: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_05bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0601: Unknown result type (might be due to invalid IL or missing references)
		//IL_0389: Unknown result type (might be due to invalid IL or missing references)
		//IL_0730: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0468: Unknown result type (might be due to invalid IL or missing references)
		//IL_04b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_077a: Unknown result type (might be due to invalid IL or missing references)
		//IL_07c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0697: Unknown result type (might be due to invalid IL or missing references)
		//IL_090c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0834: Unknown result type (might be due to invalid IL or missing references)
		//IL_0877: Unknown result type (might be due to invalid IL or missing references)
		//IL_0960: Unknown result type (might be due to invalid IL or missing references)
		//IL_09c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a31: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a85: Unknown result type (might be due to invalid IL or missing references)
		if (!showUI)
		{
			return;
		}
		InitStyles();
		bool flag = InstanceFinder.IsServerStarted || network.IsLocalModerator;
		GUI.Box(new Rect(20f, 20f, 320f, 200f), "\nC U S T O M   R A D I O", windowStyle);
		if (GUI.Button(new Rect(285f, 25f, 45f, 20f), isRu ? "RU" : "EN", btnStyle))
		{
			isRu = !isRu;
		}
		string text = ((network.UploadProgress > 0f) ? T($"Отправка... {Mathf.RoundToInt(network.UploadProgress * 100f)}%", $"Uploading... {Mathf.RoundToInt(network.UploadProgress * 100f)}%") : T("Загрузить MP3 на сервер", "Upload MP3 to Server"));
		if (GUI.Button(new Rect(40f, 55f, 280f, 25f), text, btnStyle) && network.UploadProgress == 0f)
		{
			OpenWindowsExplorerSafe();
		}
		if (network.DownloadProgress > 0f)
		{
			GUI.Label(new Rect(40f, 110f, 280f, 20f), T($"⬇ Скачивание трека... {Mathf.RoundToInt(network.DownloadProgress * 100f)}%", $"⬇ Downloading track... {Mathf.RoundToInt(network.DownloadProgress * 100f)}%"), labelStyle);
		}
		else
		{
			AudioSource source = audio.Source;
			if ((Object)(object)source != (Object)null && (Object)(object)source.clip != (Object)null)
			{
				string text2 = FormatTime(source.time) + "  /  " + FormatTime(source.clip.length);
				GUI.Label(new Rect(40f, 85f, 280f, 20f), text2, labelStyle);
				GUI.enabled = flag;
				GUI.changed = false;
				float time = GUI.HorizontalSlider(new Rect(40f, 105f, 280f, 15f), source.time, 0f, source.clip.length, sliderTrackStyle, sliderThumbStyle);
				if (GUI.changed && flag)
				{
					source.time = time;
					Plugin.LocalIgnoreNetworkTime = Time.realtimeSinceStartup;
					if (Time.realtimeSinceStartup - lastSeekSendTime > 0.1f)
					{
						lastSeekSendTime = Time.realtimeSinceStartup;
						network.SendControl(audio.IsPaused, time);
					}
				}
				if (GUI.Button(new Rect(100f, 130f, 45f, 25f), "<<", btnStyle) && flag)
				{
					float time2 = (source.time = Mathf.Max(0f, source.time - 15f));
					Plugin.LocalIgnoreNetworkTime = Time.realtimeSinceStartup;
					network.SendControl(audio.IsPaused, time2);
				}
				string text3 = (audio.IsPaused ? "Play" : "Pause");
				if (GUI.Button(new Rect(155f, 130f, 50f, 25f), text3, btnStyle) && flag)
				{
					bool flag2 = !audio.IsPaused;
					audio.ApplyControlState(flag2, source.time);
					Plugin.LocalIgnoreNetworkTime = Time.realtimeSinceStartup;
					network.SendControl(flag2, source.time);
				}
				if (GUI.Button(new Rect(215f, 130f, 45f, 25f), ">>", btnStyle) && flag)
				{
					float time3 = (source.time = Mathf.Min(source.clip.length, source.time + 15f));
					Plugin.LocalIgnoreNetworkTime = Time.realtimeSinceStartup;
					network.SendControl(audio.IsPaused, time3);
				}
				GUI.Label(new Rect(40f, 165f, 70f, 20f), $"Vol: {Mathf.RoundToInt(audio.CurrentVolume * 100f)}%", labelStyle);
				GUI.changed = false;
				float vol = GUI.HorizontalSlider(new Rect(110f, 170f, 210f, 15f), audio.CurrentVolume, 0f, 1f, sliderTrackStyle, sliderThumbStyle);
				if (GUI.changed && flag)
				{
					audio.ApplyVolume(vol);
					Plugin.LocalIgnoreNetworkTime = Time.realtimeSinceStartup;
					if (Time.realtimeSinceStartup - lastVolumeSendTime > 0.1f)
					{
						lastVolumeSendTime = Time.realtimeSinceStartup;
						network.SendVolume(vol);
					}
				}
				GUI.enabled = true;
			}
		}
		if (!InstanceFinder.IsServerStarted)
		{
			return;
		}
		GUI.Box(new Rect(360f, 20f, 320f, 290f), "\n" + T("М О Д Е Р А Ц И Я   ( Х О С Т )", "M O D E R A T I O N   ( H O S T )"), windowStyle);
		string text4 = (showRolesUI ? T("Вернуться к трекам", "Back to tracks") : T("Игроки и роли", "Players & Roles"));
		if (GUI.Button(new Rect(380f, 55f, 280f, 25f), text4, btnStyle))
		{
			showRolesUI = !showRolesUI;
		}
		if (showRolesUI)
		{
			int num3 = 90;
			GUI.Label(new Rect(380f, (float)num3, 280f, 20f), T("Выдача прав на управление радио:", "Radio management permissions:"), labelStyle);
			num3 += 25;
			{
				foreach (NetworkConnection value in InstanceFinder.ServerManager.Clients.Values)
				{
					if (!(value == InstanceFinder.ClientManager.Connection))
					{
						string playerName = GetPlayerName(value);
						bool flag3 = network.ServerModerators.Contains(value.ClientId);
						bool flag4 = GUI.Toggle(new Rect(380f, (float)num3, 280f, 20f), flag3, " " + playerName + " - " + T("Модератор", "Mod"));
						if (flag4 != flag3)
						{
							network.ToggleModerator(value.ClientId, flag4);
						}
						num3 += 25;
					}
				}
				return;
			}
		}
		List<CustomRadioNetworkService.PendingTrack> moderationQueue = network.GetModerationQueue();
		if (moderationQueue.Count == 0)
		{
			GUI.Label(new Rect(370f, 90f, 300f, 30f), T("Очередь треков пуста", "Track queue is empty"), labelStyle);
			return;
		}
		int num4 = 90;
		for (int i = 0; i < Mathf.Min(moderationQueue.Count, 3); i++)
		{
			CustomRadioNetworkService.PendingTrack pendingTrack = moderationQueue[i];
			GUI.Label(new Rect(370f, (float)num4, 120f, 25f), string.Format("{0} #{1}", T("Трек", "Track"), pendingTrack.TrackId), labelStyle);
			if (GUI.Button(new Rect(490f, (float)num4, 55f, 25f), T("Слушать", "Preview"), btnStyle))
			{
				audio.PlayPreview(pendingTrack.Path);
			}
			float initialVolume = (((Object)(object)audio.PreviewSource != (Object)null) ? audio.PreviewSource.volume : 0.5f);
			if (GUI.Button(new Rect(555f, (float)num4, 40f, 25f), "OK", btnStyle))
			{
				audio.StopPreview();
				network.ApproveTrack(pendingTrack, initialVolume);
			}
			if (GUI.Button(new Rect(605f, (float)num4, 40f, 25f), "Del", btnStyle))
			{
				audio.StopPreview();
				network.DenyTrack(pendingTrack);
			}
			num4 += 35;
		}
		if (!((Object)(object)audio.PreviewSource != (Object)null) || !((Object)(object)audio.PreviewSource.clip != (Object)null))
		{
			return;
		}
		GUI.Label(new Rect(380f, 195f, 280f, 20f), T("Настройка перед запуском у всех:", "Settings before playing for everyone:"), labelStyle);
		string text5 = (audio.PreviewSource.isPlaying ? "Pause" : "Play");
		if (GUI.Button(new Rect(380f, 215f, 60f, 20f), text5, btnStyle))
		{
			if (audio.PreviewSource.isPlaying)
			{
				audio.PreviewSource.Pause();
			}
			else
			{
				audio.PreviewSource.Play();
			}
		}
		GUI.changed = false;
		float time4 = GUI.HorizontalSlider(new Rect(450f, 218f, 210f, 15f), audio.PreviewSource.time, 0f, audio.PreviewSource.clip.length, sliderTrackStyle, sliderThumbStyle);
		if (GUI.changed)
		{
			audio.PreviewSource.time = time4;
		}
		GUI.Label(new Rect(380f, 245f, 60f, 20f), $"Vol: {Mathf.RoundToInt(audio.PreviewSource.volume * 100f)}%", labelStyle);
		GUI.changed = false;
		float volume = GUI.HorizontalSlider(new Rect(450f, 248f, 210f, 15f), audio.PreviewSource.volume, 0f, 1f, sliderTrackStyle, sliderThumbStyle);
		if (GUI.changed)
		{
			audio.PreviewSource.volume = volume;
		}
	}

	private string FormatTime(float timeInSeconds)
	{
		int num = Mathf.FloorToInt(timeInSeconds / 60f);
		int num2 = Mathf.FloorToInt(timeInSeconds % 60f);
		return $"{num:00}:{num2:00}";
	}

	private void OpenWindowsExplorerSafe()
	{
		if (isDialogOpen)
		{
			return;
		}
		isDialogOpen = true;
		new Thread((ThreadStart)delegate
		{
			OpenFileName openFileName = new OpenFileName();
			openFileName.structSize = Marshal.SizeOf(openFileName);
			openFileName.filter = "MP3 Files (*.mp3)\0*.mp3\0";
			openFileName.file = new string(new char[1024]);
			openFileName.maxFile = openFileName.file.Length;
			openFileName.title = T("Выберите песню для радио", "Select a song for the radio");
			openFileName.flags = 528392;
			if (LocalDialog.GetOpenFileName(openFileName))
			{
				pendingFiles.Enqueue(openFileName.file.TrimEnd(new char[1]));
			}
			isDialogOpen = false;
		}).Start();
	}
}