Decompiled source of CybergrindMusicExplorer v2.3.2

plugins/Jukebox/Jukebox.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
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 BepInEx;
using Discord;
using HarmonyLib;
using Jukebox.Components;
using Jukebox.Components.HUD;
using Jukebox.Components.Player;
using Jukebox.Input;
using Jukebox.Patches;
using Jukebox.RTLTMPro;
using Jukebox.UI;
using Jukebox.UI.Elements;
using Jukebox.UI.Windows;
using Jukebox.Utils;
using JukeboxCore.Collections;
using JukeboxCore.Components;
using JukeboxCore.Events;
using JukeboxCore.Logging;
using JukeboxCore.Models.Preferences;
using JukeboxCore.Playback;
using JukeboxCore.Songs;
using JukeboxCore.Songs.Models;
using JukeboxCore.Subtitles;
using JukeboxCore.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using SettingsMenu.Components;
using SettingsMenu.Components.Pages;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.Networking;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Jukebox")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Jukebox")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("B01B8657-61B6-4BC3-BFAF-F43D4C264B4B")]
[assembly: AssemblyFileVersion("2.3.2.0")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("2.3.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 Jukebox
{
	[BepInProcess("ULTRAKILL.exe")]
	[BepInPlugin("dev.flazhik.jukebox", "Jukebox", "2.3.2")]
	public class JukeboxPlugin : BaseUnityPlugin
	{
		private static GameObject _bootstrap;

		public static string catalogDir;

		private static Harmony _harmony;

		private static bool _motDShown;

		private static bool _init;

		static JukeboxPlugin()
		{
			catalogDir = Path.Combine(PathsUtils.AssemblyPath, "Assets");
		}

		private void Awake()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			JukeboxLogger.Initialize(((BaseUnityPlugin)this).Logger);
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			Addressables.InitializeAsync().WaitForCompletion();
			Addressables.LoadContentCatalogAsync(Path.Combine(catalogDir, "catalog.json"), true, (string)null).WaitForCompletion();
			_harmony = new Harmony("dev.flazhik.jukebox");
			Startup();
		}

		private void Start()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			_bootstrap = Addressables.LoadAssetAsync<GameObject>((object)"Assets/Jukebox/Bootstrap.prefab").WaitForCompletion();
			InputActionsPatch.Load();
			HudFontMaterials.Load();
			JukeboxCustomSongMetadata.Load();
		}

		private void OnDestroy()
		{
			JukeboxLogger.Shutdown();
		}

		private void Startup()
		{
			_harmony.PatchAll();
			FileInfo fileInfo = new FileInfo(Path.Combine(PathsUtils.AssemblyPath, "cookies.txt"));
			if (fileInfo.Exists)
			{
				fileInfo.Delete();
			}
			SceneManager.sceneLoaded += OnSceneLoaded;
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode loadSceneMode)
		{
			//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)
			if (scene != SceneManager.GetActiveScene())
			{
				return;
			}
			switch (SceneHelper.CurrentScene)
			{
			case "Bootstrap":
				SetDefaultPrefsValues();
				SetDefaultGlyphSubstitution();
				break;
			case "Main Menu":
				if (!_init)
				{
					_init = true;
				}
				break;
			case "Endless":
				Object.Instantiate<GameObject>(_bootstrap);
				if (!_motDShown)
				{
					((MonoBehaviour)MonoSingleton<JukeboxManager>.Instance).StartCoroutine(MotD());
				}
				break;
			}
		}

		private static void SetDefaultPrefsValues()
		{
			foreach (KeyValuePair<string, object> item in new Dictionary<string, object>
			{
				{ "jukebox.volumeBoost", 0f },
				{ "jukebox.individualBoostPerTrack", false },
				{ "jukebox.showTrackPanelIndefinitely", false },
				{ "jukebox.alwaysPlayIntro", true },
				{ "jukebox.resumeLastSong", false },
				{ "jukebox.discordAndSteamIntegration", true },
				{ "jukebox.enableTracksPreview", true },
				{ "jukebox.preventDuplicateTracks", false },
				{ "jukebox.nowPlayingHud", true },
				{ "jukebox.enableCalmThemes", true },
				{ "jukebox.songPanelStyle", 0 },
				{ "jukebox.currentPlaylist", "slot1" },
				{ "jukebox.calmThemeEnemiesThreshold", 2 },
				{ "jukebox.displaySubtitles", true },
				{ "jukebox.effects.parry", null },
				{ "jukebox.effects.wavecomplete", null },
				{ "jukebox.effects.gameover", null },
				{ "jukebox.effects.aww", null },
				{ "jukebox.effects.terminal-music", null },
				{ "jukebox.downloader.separateFolderPerPlaylist", true }
			})
			{
				MonoSingleton<PrefsManager>.Instance.defaultValues.Add(item.Key, item.Value);
			}
		}

		private static void SetDefaultGlyphSubstitution()
		{
			TMP_Settings.missingGlyphCharacter = 63;
		}

		private static IEnumerator MotD()
		{
			yield return (object)new WaitForSeconds(1.5f);
			InputAction menu = MonoSingleton<JukeboxInputs>.Instance.Menu;
			string text = GeneralExtensions.Join<InputBinding>((IEnumerable<InputBinding>)(object)menu.bindings, (Func<InputBinding, string>)((InputBinding binding) => InputExtensions.GetBindingDisplayStringWithoutOverride(menu, binding, (DisplayStringOptions)0)), ", ");
			MonoSingleton<HudMessageReceiver>.Instance.SendHudMessage("CGME menu is available by pressing <color=orange>" + text + "</color>", "", "", 0, false, false, true);
			_motDShown = true;
		}
	}
	public static class JukeboxPluginInfo
	{
		public const string Guid = "dev.flazhik.jukebox";

		public const string Name = "Jukebox";

		public const string Version = "2.3.2";
	}
}
namespace Jukebox.Utils
{
	public static class FloatExtensions
	{
		public static string SecondsToHumanReadable(this float value)
		{
			int num = (int)value;
			int num2 = num / 60;
			int num3 = num - num2 * 60;
			return $"{num2:00}:{num3:00}";
		}
	}
	public static class IntExtensions
	{
		public static int Mod(this int num, int mod)
		{
			return (num % mod + mod) % mod;
		}
	}
	public static class ScrollingTextUnscaled
	{
		public static IEnumerator ShowText(TMP_Text text, string message, float secondsBetweenLetters = 0.005f, AudioSource clickAudio = null)
		{
			int currentLetter = 0;
			text.text = "";
			while (currentLetter < message.Length)
			{
				int num;
				if (message[currentLetter] == '<')
				{
					while (currentLetter < message.Length && message[currentLetter] != '>')
					{
						num = currentLetter + 1;
						currentLetter = num;
					}
				}
				else if (currentLetter < message.Length - 1)
				{
					while (currentLetter < message.Length - 1 && message[currentLetter + 1] == ' ')
					{
						num = currentLetter + 1;
						currentLetter = num;
					}
				}
				num = currentLetter + 1;
				currentLetter = num;
				text.text = message.Substring(0, currentLetter);
				if ((Object)(object)clickAudio != (Object)null && message[currentLetter - 1] != '\n' && message[currentLetter - 1] != ' ')
				{
					clickAudio.Play();
				}
				yield return (object)new WaitForSecondsRealtime(secondsBetweenLetters);
			}
		}

		public static IEnumerator HideText(TMP_Text text, float secondsBetweenLetters = 0.005f)
		{
			string message = text.text;
			int currentLetter = message.Length - 1;
			while (currentLetter >= 0)
			{
				int num;
				if (message[currentLetter] == '>')
				{
					while (currentLetter >= 0 && message[currentLetter] != '<')
					{
						num = currentLetter - 1;
						currentLetter = num;
					}
				}
				num = currentLetter - 1;
				currentLetter = num;
				text.text = message.Substring(0, currentLetter + 1);
				yield return (object)new WaitForSecondsRealtime(secondsBetweenLetters);
			}
		}

		public static IEnumerator Reveal(TMP_Text text, float secondsBetweenLetters = 0.005f, AudioSource clickAudio = null)
		{
			text.ForceMeshUpdate(false, false);
			int total = text.textInfo.characterCount;
			int visible = 0;
			text.maxVisibleCharacters = 0;
			while (visible < total)
			{
				visible = (text.maxVisibleCharacters = visible + 1);
				if ((Object)(object)clickAudio != (Object)null)
				{
					clickAudio.Play();
				}
				yield return (object)new WaitForSecondsRealtime(secondsBetweenLetters);
			}
		}

		public static IEnumerator Conceal(TMP_Text text, float secondsBetweenLetters = 0.005f)
		{
			int visible = text.maxVisibleCharacters;
			while (visible > 0)
			{
				visible = (text.maxVisibleCharacters = visible - 1);
				yield return (object)new WaitForSecondsRealtime(secondsBetweenLetters);
			}
		}
	}
	public static class TmpTextExtensions
	{
		public static void SetPreservingRtl(this TMP_Text text, string value)
		{
			RtlUtils.Set(text, value);
		}
	}
}
namespace Jukebox.UI
{
	public class JukeboxCredits : MonoBehaviour
	{
		[SerializeField]
		private TMP_Text[] terminalText;

		[SerializeField]
		private AudioSource click;

		[SerializeField]
		private GameObject firstWindow;

		[SerializeField]
		private GameObject secondWindow;

		[SerializeField]
		private List<GameObject> lastWindows;

		private string[] originalText;

		private void Awake()
		{
			originalText = terminalText.Select((TMP_Text t) => t.text).ToArray();
		}

		private void OnEnable()
		{
			firstWindow.SetActive(false);
			secondWindow.SetActive(false);
			foreach (GameObject lastWindow in lastWindows)
			{
				lastWindow.SetActive(false);
			}
			TMP_Text[] array = terminalText;
			for (int i = 0; i < array.Length; i++)
			{
				array[i].text = string.Empty;
			}
			((MonoBehaviour)this).StartCoroutine(WindowsRoutine());
		}

		private IEnumerator WindowsRoutine()
		{
			yield return TerminalTextRoutine(0);
			firstWindow.SetActive(true);
			yield return TerminalTextRoutine(1);
			yield return TerminalTextRoutine(2);
			secondWindow.SetActive(true);
			yield return TerminalTextRoutine(3);
			yield return TerminalTextRoutine(4);
			foreach (GameObject lastWindow in lastWindows)
			{
				lastWindow.SetActive(true);
			}
		}

		private IEnumerator TerminalTextRoutine(int index)
		{
			return ScrollingTextUnscaled.ShowText(terminalText[index], originalText[index], 0.01f, click);
		}
	}
	public class JukeboxCustomContentButton : CustomContentButton
	{
		[SerializeField]
		public GameObject introAndLoopIcon;

		[SerializeField]
		public GameObject calmThemeIcon;
	}
	public class JukeboxUI : MonoBehaviour
	{
		[SerializeField]
		public GameObject jukeboxMenuTemplate;

		[SerializeField]
		public GameObject playbackMenuTemplate;

		[SerializeField]
		public GameObject whatIsNewMenuTemplate;

		[SerializeField]
		public GameObject nowPlayingHudTemplate;

		[SerializeField]
		public GameObject nowPlayingHudClassicTemplate;

		private GameObject jukeboxMenu;

		private GameObject playbackMenu;

		private GameObject whatIsNew;

		private ReadOnlyCollection<JukeboxWindow> windows;

		protected void Awake()
		{
			InstantiateUI();
			windows = new ReadOnlyCollection<JukeboxWindow>(new List<JukeboxWindow>
			{
				jukeboxMenu.GetComponent<JukeboxWindow>(),
				playbackMenu.GetComponent<JukeboxPlaybackWindow>(),
				whatIsNew.GetComponent<WhatsNew>()
			});
			foreach (JukeboxWindow window in windows)
			{
				((Component)window).gameObject.SetActive(false);
			}
			((MonoBehaviour)this).StartCoroutine(WhatIsNewRoutine());
		}

		protected void Update()
		{
			ManageWindows();
		}

		public void OpenDownloader()
		{
			JukeboxMenuWindow component = jukeboxMenu.GetComponent<JukeboxMenuWindow>();
			component.Open();
			component.OpenDownloader();
		}

		private void ManageWindows()
		{
			if (GameIsPaused())
			{
				return;
			}
			if (MonoSingleton<InputManager>.Instance.InputSource.Pause.WasPerformedThisFrame)
			{
				foreach (JukeboxWindow item in windows.Where((JukeboxWindow window) => window.IsOpened()))
				{
					item.Close();
				}
				return;
			}
			JukeboxWindow jukeboxWindow = windows.FirstOrDefault((JukeboxWindow w) => (Object)(object)w.hotkey != (Object)null && w.hotkey.ToInputAction().WasPerformedThisFrame());
			if ((Object)(object)jukeboxWindow != (Object)null)
			{
				foreach (JukeboxWindow window in windows)
				{
					if ((Object)(object)window.hotkey != (Object)null && InputExtensions.IsActionEqual(window.hotkey.ToInputAction(), jukeboxWindow.hotkey.ToInputAction(), (string)null))
					{
						window.Toggle();
					}
					else
					{
						window.Close();
					}
				}
			}
			if (!windows.Any((JukeboxWindow w) => GameStateManager.Instance.IsStateActive(w.StateKey())) && MonoSingleton<OptionsManager>.Instance.paused)
			{
				MonoSingleton<JukeboxPauseManager>.Instance.UnPause();
			}
		}

		private void InstantiateUI()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject
			{
				name = "JukeboxCanvas"
			};
			val.AddComponent<Canvas>();
			GameObject val2 = GameObject.Find("/Canvas");
			Canvas component = val.GetComponent<Canvas>();
			component.renderMode = (RenderMode)1;
			component.sortingOrder = val2.GetComponent<Canvas>().sortingOrder + 1;
			val.AddComponent<CanvasScaler>();
			val.AddComponent<GraphicRaycaster>();
			jukeboxMenu = Object.Instantiate<GameObject>(jukeboxMenuTemplate, val2.transform);
			playbackMenu = Object.Instantiate<GameObject>(playbackMenuTemplate, val2.transform);
			whatIsNew = Object.Instantiate<GameObject>(whatIsNewMenuTemplate, val2.transform);
			Object.Instantiate<GameObject>(nowPlayingHudTemplate, GameObject.Find("/Player/Main Camera/HUD Camera/HUD/GunCanvas").transform, false);
			Object.Instantiate<GameObject>(nowPlayingHudClassicTemplate, val2.transform.Find("Crosshair Filler"), false);
		}

		private IEnumerator WhatIsNewRoutine()
		{
			yield return (object)new WaitForSeconds(1.4f);
			whatIsNew.GetComponent<WhatsNew>().Open();
		}

		private static bool GameIsPaused()
		{
			return GameStateManager.Instance.IsStateActive("pause");
		}
	}
	public class ScrollUIImageUnscaled : MonoBehaviour
	{
		public float xSpeed;

		public float ySpeed;

		private RawImage img;

		private void Start()
		{
			img = ((Component)this).GetComponent<RawImage>();
		}

		private void Update()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			Rect uvRect = img.uvRect;
			Vector2 val = ((Rect)(ref uvRect)).position + new Vector2(xSpeed, ySpeed) * Time.unscaledDeltaTime;
			while ((double)val.x > 1.0)
			{
				val.x -= 1f;
			}
			while ((double)val.x < -1.0)
			{
				val.x += 1f;
			}
			while ((double)val.y > 1.0)
			{
				val.y -= 1f;
			}
			while ((double)val.y < -1.0)
			{
				val.y += 1f;
			}
			RawImage obj = img;
			uvRect = img.uvRect;
			Vector2 val2 = ((Rect)(ref uvRect)).position + new Vector2(xSpeed, ySpeed) * Time.unscaledDeltaTime;
			uvRect = img.uvRect;
			obj.uvRect = new Rect(val2, ((Rect)(ref uvRect)).size);
		}
	}
}
namespace Jukebox.UI.Windows
{
	public class JukeboxMenuWindow : JukeboxWindow
	{
		[SerializeField]
		protected Button[] buttons;

		[SerializeField]
		protected GameObject activeBgObject;

		[SerializeField]
		protected GameObject generalSettings;

		[SerializeField]
		protected Button downloaderButton;

		[SerializeField]
		protected GameObject downloaderPage;

		private IPlaybackController player;

		public override string StateKey()
		{
			return "jukeboxMenu";
		}

		protected override void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			base.Awake();
			Button[] array = buttons;
			foreach (Button button in array)
			{
				((UnityEvent)button.onClick).AddListener((UnityAction)delegate
				{
					SetActiveButton(button);
				});
			}
			player = (IPlaybackController)(object)MonoSingleton<JukeboxManager>.Instance?.player;
			IPlaybackController obj = player;
			if (obj != null)
			{
				obj.OnNextAudioClip += NextAudioClip;
			}
		}

		protected void OnDestroy()
		{
			player.OnNextAudioClip -= NextAudioClip;
		}

		public void NextAudioClip(ClipChangedArgs clip)
		{
			if (clip.NewSong && clip.Song.IsCustom)
			{
				ReflectionUtils.GetPrivate<Slider>((object)generalSettings.GetComponentInChildren<SettingsSlider>(true), typeof(SettingsSlider), "slider").value = player.State.VolumeBoost;
			}
		}

		public void OpenDownloader()
		{
			SettingsMenu component = ((Component)this).gameObject.GetComponent<SettingsMenu>();
			if (!ReflectionUtils.GetPrivate<bool>((object)component, typeof(SettingsMenu), "initialized"))
			{
				component.Initialize();
			}
			component.SetActivePage(downloaderPage);
			SetActiveButton(downloaderButton);
		}

		private void SetActiveButton(Button button)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			activeBgObject.transform.SetParent(((Component)button).transform);
			activeBgObject.transform.SetSiblingIndex(0);
			activeBgObject.transform.localPosition = Vector3.zero;
		}
	}
	public class JukeboxPlaybackWindow : JukeboxWindow
	{
		public override string StateKey()
		{
			return "jukeboxPlayback";
		}
	}
	public abstract class JukeboxWindow : MonoBehaviour
	{
		[SerializeField]
		public InputActionReference hotkey;

		protected virtual void Awake()
		{
			if ((Object)(object)hotkey != (Object)null)
			{
				InputManager instance = MonoSingleton<InputManager>.Instance;
				hotkey = InputActionReference.Create((instance != null) ? instance.InputSource.Actions.asset.FindAction(hotkey.ToInputAction().id) : null);
			}
		}

		public abstract string StateKey();

		public void Open()
		{
			((Component)this).gameObject.SetActive(true);
			MonoSingleton<JukeboxPauseManager>.Instance?.Pause(StateKey(), ((Component)this).gameObject);
		}

		public void Close()
		{
			GameStateManager.Instance.PopState(StateKey());
			((Component)this).gameObject.SetActive(false);
		}

		public void Toggle()
		{
			if (IsOpened())
			{
				Close();
			}
			else
			{
				Open();
			}
		}

		public bool IsOpened()
		{
			return ((Component)this).gameObject.activeSelf;
		}
	}
	public class WhatsNew : JukeboxWindow
	{
		private const string PrefsKey = "Jukebox.2.3.0-Release";

		private void Start()
		{
			if (PlayerPrefs.GetInt("Jukebox.2.3.0-Release") != 1)
			{
				PlayerPrefs.SetInt("Jukebox.2.3.0-Release", 1);
			}
			else
			{
				Close();
			}
		}

		public override string StateKey()
		{
			return "jukebox.whatisnew";
		}
	}
}
namespace Jukebox.UI.Themes
{
	public class ThemeEnemySettings : MonoBehaviour
	{
		[SerializeField]
		public TMP_Text title;

		[SerializeField]
		public Image icon;

		[SerializeField]
		public GameObject radianceBg;

		[SerializeField]
		public Toggle toggle;

		[SerializeField]
		public Counter counter;
	}
	public class ThemeSettingsMenu : MonoBehaviour
	{
		[SerializeField]
		private Counter threshold;

		[SerializeField]
		private GameObject enemyTemplate;

		[SerializeField]
		private GameObject regularEnemies;

		[SerializeField]
		private GameObject radiantEnemies;

		[SerializeField]
		private Button[] tabButtons;

		[SerializeField]
		private GameObject activeTab;

		[SerializeField]
		private List<AssetReferenceT<SpawnableObject>> enemies;

		private void Awake()
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			JukeboxThemesManager themesManager = MonoSingleton<JukeboxThemesManager>.Instance;
			threshold.SetDefaultValue(MonoSingleton<PrefsManager>.Instance.GetIntLocal("jukebox.calmThemeEnemiesThreshold", 0));
			threshold.OnChanged += OnThresholdChanged;
			Button[] array = tabButtons;
			foreach (Button button in array)
			{
				((UnityEvent)button.onClick).AddListener((UnityAction)delegate
				{
					//IL_0055: Unknown result type (might be due to invalid IL or missing references)
					activeTab.transform.SetParent(((Component)button).transform);
					activeTab.transform.SetSiblingIndex(0);
					activeTab.transform.localPosition = Vector3.zero;
				});
			}
			foreach (AssetReferenceT<SpawnableObject> enemy2 in enemies)
			{
				AsyncOperationHandle<SpawnableObject> val = enemy2.LoadAssetAsync();
				SpawnableObject enemy = val.WaitForCompletion();
				Addressables.Release<SpawnableObject>(val);
				Dictionary<bool, ThemeEnemySettings> respectiveSettings = new Dictionary<bool, ThemeEnemySettings>();
				GameObject[] array2 = (GameObject[])(object)new GameObject[2] { regularEnemies, radiantEnemies };
				foreach (GameObject val2 in array2)
				{
					GameObject val3 = Object.Instantiate<GameObject>(enemyTemplate, val2.transform);
					bool radiant = (Object)(object)val2 == (Object)(object)radiantEnemies;
					Sprite gridIcon = enemy.gridIcon;
					ThemeEnemySettings enemySettings = val3.GetComponent<ThemeEnemySettings>();
					enemySettings.icon.sprite = gridIcon;
					enemySettings.title.text = (radiant ? ("Radiant " + enemy.objectName) : enemy.objectName);
					enemySettings.radianceBg.SetActive(radiant);
					int? thresholdFor = themesManager.GetThresholdFor(enemy.enemyType, radiant);
					if (!thresholdFor.HasValue)
					{
						enemySettings.counter.SetDefaultValue(0);
					}
					else
					{
						enemySettings.counter.SetDefaultValue(thresholdFor.Value);
					}
					enemySettings.toggle.isOn = thresholdFor.HasValue;
					enemySettings.counter.OnChanged += delegate(int value)
					{
						//IL_002c: Unknown result type (might be due to invalid IL or missing references)
						enemySettings.toggle.isOn = true;
						themesManager.SetThresholdFor(enemy.enemyType, value, radiant);
					};
					((UnityEvent<bool>)(object)enemySettings.toggle.onValueChanged).AddListener((UnityAction<bool>)delegate(bool on)
					{
						//IL_005a: Unknown result type (might be due to invalid IL or missing references)
						//IL_001e: Unknown result type (might be due to invalid IL or missing references)
						if (on)
						{
							themesManager.SetThresholdFor(enemy.enemyType, enemySettings.counter.Value, radiant);
						}
						else
						{
							themesManager.DisableThresholdFor(enemy.enemyType, radiant);
						}
					});
					respectiveSettings[radiant] = enemySettings;
					val3.SetActive(true);
				}
				respectiveSettings[false].counter.OnChanged += delegate(int value)
				{
					//IL_0011: Unknown result type (might be due to invalid IL or missing references)
					//IL_0041: Unknown result type (might be due to invalid IL or missing references)
					int? thresholdFor2 = themesManager.GetThresholdFor(enemy.enemyType, radiant: true);
					if (!thresholdFor2.HasValue || thresholdFor2.Value > value)
					{
						themesManager.SetThresholdFor(enemy.enemyType, value, radiant: true);
						respectiveSettings[true].toggle.isOn = true;
						respectiveSettings[true].counter.Value = value;
					}
				};
				((UnityEvent<bool>)(object)respectiveSettings[false].toggle.onValueChanged).AddListener((UnityAction<bool>)delegate(bool on)
				{
					//IL_0015: Unknown result type (might be due to invalid IL or missing references)
					if (on && !themesManager.GetThresholdFor(enemy.enemyType, radiant: true).HasValue)
					{
						respectiveSettings[true].toggle.isOn = true;
						respectiveSettings[true].counter.Value = respectiveSettings[false].counter.Value;
					}
				});
			}
		}

		private void OnDestroy()
		{
			threshold.OnChanged -= OnThresholdChanged;
		}

		private static void OnThresholdChanged(int value)
		{
			PrefsManager instance = MonoSingleton<PrefsManager>.Instance;
			if (instance != null)
			{
				instance.SetIntLocal("jukebox.calmThemeEnemiesThreshold", value);
			}
		}
	}
}
namespace Jukebox.UI.Subtitles
{
	public class SubtitlesPanel : MonoBehaviour
	{
		[SerializeField]
		public CanvasGroup panelGroup;

		[SerializeField]
		public TMP_Text text;

		[SerializeField]
		public float approachTime;

		[SerializeField]
		public Material fontMaterial;

		private bool displaySubtitles;

		private Coroutine displayRoutine;

		private IPlaybackController player;

		public void Awake()
		{
			player = (IPlaybackController)(object)MonoSingleton<JukeboxManager>.Instance?.player;
			if (player == null)
			{
				((Behaviour)this).enabled = false;
				return;
			}
			PrefsManager.onPrefChanged = (Action<string, object>)Delegate.Combine(PrefsManager.onPrefChanged, new Action<string, object>(OnPrefChanged));
			player.OnSubtitleChanged += OnSubtitleChanged;
			player.OnStop += OnStop;
			displaySubtitles = MonoSingleton<PrefsManager>.Instance.GetBoolLocal("jukebox.displaySubtitles", false);
			Stall();
		}

		public void OnDestroy()
		{
			PrefsManager.onPrefChanged = (Action<string, object>)Delegate.Remove(PrefsManager.onPrefChanged, new Action<string, object>(OnPrefChanged));
			if (player != null)
			{
				player.OnSubtitleChanged -= OnSubtitleChanged;
				player.OnStop -= OnStop;
			}
		}

		public void RestoreFontMaterial()
		{
			if ((Object)(object)fontMaterial != (Object)null)
			{
				text.fontMaterial = fontMaterial;
			}
		}

		private void OnSubtitleChanged(string contents)
		{
			if (displaySubtitles)
			{
				if (string.IsNullOrEmpty(contents))
				{
					StartFade(0f);
					return;
				}
				text.text = contents;
				StartFade(1f);
				text.ForceMeshUpdate(false, false);
				LayoutRebuilder.ForceRebuildLayoutImmediate(((Component)((Component)this).transform.parent).GetComponent<RectTransform>());
			}
		}

		private void OnStop()
		{
			Stall();
		}

		private void OnPrefChanged(string key, object value)
		{
			if (key.Equals("jukebox.displaySubtitles"))
			{
				displaySubtitles = (bool)value;
				if (!displaySubtitles)
				{
					Stall();
				}
			}
		}

		private void StartFade(float target)
		{
			if (displayRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(displayRoutine);
			}
			displayRoutine = null;
			if (!Mathf.Approximately(panelGroup.alpha, target))
			{
				displayRoutine = ((MonoBehaviour)this).StartCoroutine(Fade(target));
			}
		}

		private IEnumerator Fade(float target)
		{
			float start = panelGroup.alpha;
			float time = 0f;
			while (time < approachTime)
			{
				time += Time.unscaledDeltaTime;
				panelGroup.alpha = Mathf.Lerp(start, target, time / approachTime);
				yield return null;
			}
			panelGroup.alpha = target;
		}

		private void Stall()
		{
			if (displayRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(displayRoutine);
				displayRoutine = null;
			}
			text.text = string.Empty;
			panelGroup.alpha = 0f;
		}
	}
}
namespace Jukebox.UI.SongPanel
{
	public class ClassicSongPanel : SongPanel
	{
		protected override SongPanelStyle Style => SongPanelStyle.Classic;

		protected override IEnumerator FadeIn(JukeboxSongMetadata metadata)
		{
			float time = 0f;
			while ((double)time < (double)panelApproachTime)
			{
				time += Time.unscaledDeltaTime;
				panelGroup.alpha = (active ? (time / panelApproachTime) : 0f);
				yield return null;
			}
			panelGroup.alpha = (active ? 1 : 0);
		}

		protected override IEnumerator Await()
		{
			float time = panelStayTime;
			while ((double)time > 0.0 || showIndefinitely)
			{
				panelGroup.alpha = (active ? 1 : 0);
				if ((double)time > 0.0)
				{
					time -= Time.unscaledDeltaTime;
				}
				yield return null;
			}
		}

		protected override IEnumerator FadeOut()
		{
			float time = panelApproachTime;
			while ((double)time > 0.0)
			{
				time -= Time.unscaledDeltaTime;
				panelGroup.alpha = (active ? (time / panelApproachTime) : 0f);
				yield return null;
			}
			panelGroup.alpha = 0f;
		}
	}
	public class JukeboxSongPanel : SongPanel
	{
		[SerializeField]
		public Vector2 startDimensions;

		[SerializeField]
		public Vector2 targetDimensions;

		private Coroutine rollingTextRoutine;

		protected override SongPanelStyle Style => SongPanelStyle.Jukebox;

		protected override IEnumerator FadeIn(JukeboxSongMetadata metadata)
		{
			((Graphic)text).color = new Color(1f, 1f, 1f, 1f);
			RectTransform rect = ((Component)panelGroup).GetComponent<RectTransform>();
			rect.sizeDelta = startDimensions;
			if (rollingTextRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(rollingTextRoutine);
			}
			rollingTextRoutine = ((MonoBehaviour)this).StartCoroutine(ScrollingTextUnscaled.Reveal(text, 0.022f));
			float time = 0f;
			while (time < panelApproachTime)
			{
				panelGroup.alpha = (active ? 1 : 0);
				time += Time.unscaledDeltaTime;
				if (time > panelApproachTime)
				{
					time = panelApproachTime;
				}
				rect.sizeDelta = startDimensions + (targetDimensions - startDimensions) * time / panelApproachTime;
				yield return null;
			}
		}

		protected override IEnumerator Await()
		{
			float time = panelStayTime;
			while ((double)time > 0.0 || showIndefinitely)
			{
				panelGroup.alpha = (active ? 1 : 0);
				if ((double)time > 0.0)
				{
					time -= Time.unscaledDeltaTime;
				}
				yield return null;
			}
		}

		protected override IEnumerator FadeOut()
		{
			rollingTextRoutine = ((MonoBehaviour)this).StartCoroutine(ScrollingTextUnscaled.Conceal(text, 0.022f));
			RectTransform rect = ((Component)panelGroup).GetComponent<RectTransform>();
			float time = panelApproachTime;
			yield return (object)new WaitUntil((Func<bool>)(() => text.maxVisibleCharacters <= 20));
			while ((double)time > 0.0)
			{
				panelGroup.alpha = (active ? 1 : 0);
				time -= Time.unscaledDeltaTime;
				if (time < 0f)
				{
					time = 0f;
				}
				rect.sizeDelta = startDimensions + (targetDimensions - startDimensions) * time / panelApproachTime;
				yield return null;
			}
			panelGroup.alpha = 0f;
		}
	}
	public class PlaylistSelectable : DirectoryTreeBrowser<JukeboxSong>
	{
		[SerializeField]
		public GameObject selectPlaylistPanel;

		[SerializeField]
		public string playlistName;

		[SerializeField]
		public Button button;

		[SerializeField]
		public GameObject empty;

		protected override int maxPageLength => 6;

		protected override IDirectoryTree<JukeboxSong> baseDirectory
		{
			get
			{
				JukeboxPlaylist val;
				using (StreamReader streamReader = new StreamReader(File.Open(JukeboxPlaylist.PathForPlaylist(playlistName), FileMode.OpenOrCreate)))
				{
					val = JsonConvert.DeserializeObject<JukeboxPlaylist>(streamReader.ReadToEnd());
				}
				List<SongIdentifier> source = ((val != null) ? ((Playlist)val).ids : new List<SongIdentifier>());
				return (IDirectoryTree<JukeboxSong>)(object)new FakeDirectoryTree<JukeboxSong>("Songs", source.Where((SongIdentifier id) => (int)id.type != 1 || File.Exists(id.path)).Select(delegate(SongIdentifier id)
				{
					JukeboxSongsLoader instance = MonoSingleton<JukeboxSongsLoader>.Instance;
					return (instance == null) ? null : instance.Get(id);
				}), (IEnumerable<IDirectoryTree<JukeboxSong>>)null, (IDirectoryTree<JukeboxSong>)null);
			}
		}

		private void Awake()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			((UnityEvent)button.onClick).AddListener((UnityAction)delegate
			{
				PrefsManager instance = MonoSingleton<PrefsManager>.Instance;
				if (instance != null)
				{
					instance.SetStringLocal("jukebox.currentPlaylist", playlistName);
				}
				MonoSingleton<JukeboxManager>.Instance?.playlistEditor.ReloadPlaylist();
				selectPlaylistPanel.SetActive(false);
			});
		}

		private void OnEnable()
		{
			base.currentDirectory = ((DirectoryTreeBrowser<JukeboxSong>)this).baseDirectory;
			((DirectoryTreeBrowser<JukeboxSong>)this).Rebuild(true);
		}

		protected override Action BuildLeaf(JukeboxSong item, int indexInPage)
		{
			GameObject go = Object.Instantiate<GameObject>(base.itemButtonTemplate, base.itemButtonTemplate.transform.parent);
			go.SetActive(true);
			go.GetComponent<Image>().sprite = item.Metadata.Icon;
			return delegate
			{
				Object.Destroy((Object)(object)go);
			};
		}

		public override void Rebuild(bool setToPageZero = true)
		{
			base.Rebuild(setToPageZero);
			Transform itemParent = base.itemParent;
			LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)(object)((itemParent is RectTransform) ? itemParent : null));
			empty.SetActive(!base.currentDirectory.files.Any());
		}
	}
	public abstract class SongPanel : MonoBehaviour
	{
		[SerializeField]
		public CanvasGroup panelGroup;

		[SerializeField]
		public Sprite defaultIcon;

		[SerializeField]
		public float panelApproachTime;

		[SerializeField]
		public float panelStayTime;

		public TMP_Text text;

		public Image icon;

		protected bool active;

		protected bool showIndefinitely;

		private Coroutine displayRoutine;

		private IPlaybackController player;

		protected abstract SongPanelStyle Style { get; }

		public void Awake()
		{
			PrefsManager.onPrefChanged = (Action<string, object>)Delegate.Combine(PrefsManager.onPrefChanged, new Action<string, object>(OnPrefChanged));
			player = (IPlaybackController)(object)MonoSingleton<JukeboxManager>.Instance.player;
			player.OnNextAudioClip += NextAudioClip;
			player.OnStop += OnStop;
			showIndefinitely = MonoSingleton<PrefsManager>.Instance.GetBoolLocal("jukebox.showTrackPanelIndefinitely", false);
			active = MonoSingleton<PrefsManager>.Instance.GetIntLocal("jukebox.songPanelStyle", 0) == (int)Style;
		}

		public void OnDestroy()
		{
			PrefsManager.onPrefChanged = (Action<string, object>)Delegate.Remove(PrefsManager.onPrefChanged, new Action<string, object>(OnPrefChanged));
			player.OnNextAudioClip -= NextAudioClip;
			player.OnStop -= OnStop;
		}

		protected abstract IEnumerator FadeIn(JukeboxSongMetadata metadata);

		protected abstract IEnumerator Await();

		protected abstract IEnumerator FadeOut();

		private IEnumerator ShowPanelRoutine(JukeboxSongMetadata metadata)
		{
			string text = ((!string.IsNullOrEmpty(metadata.Artist)) ? ("<color=#999>" + metadata.Artist + "</color>") : string.Empty);
			this.text.SetPreservingRtl(metadata.Title.ToUpper() + " " + text);
			icon.sprite = (((Object)(object)metadata.Icon != (Object)null) ? metadata.Icon : defaultIcon);
			yield return FadeIn(metadata);
			yield return Await();
			yield return FadeOut();
		}

		private void NextAudioClip(ClipChangedArgs clip)
		{
			if (clip.NewSong)
			{
				active = MonoSingleton<PrefsManager>.Instance.GetIntLocal("jukebox.songPanelStyle", 0) == (int)Style;
				if (displayRoutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(displayRoutine);
				}
				displayRoutine = ((MonoBehaviour)this).StartCoroutine(ShowPanelRoutine(clip.Song.Metadata));
			}
		}

		private void OnStop()
		{
			active = false;
		}

		private void OnPrefChanged(string key, object value)
		{
			if (key.Equals("jukebox.showTrackPanelIndefinitely"))
			{
				showIndefinitely = (bool)value;
			}
			if (key.Equals("jukebox.songPanelStyle"))
			{
				active = (int)value == (int)Style;
			}
		}
	}
	public class SongPanelOption : MonoBehaviour
	{
		private static readonly Color Active = new Color(0.68f, 0.68f, 0.68f, 0.25f);

		private static readonly Color Inactive = new Color(0f, 0f, 0f, 0.59f);

		public SongPanelStyle style;

		private void Start()
		{
			//IL_0052: 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)
			Image component = ((Component)this).gameObject.GetComponent<Image>();
			PrefsManager instance = MonoSingleton<PrefsManager>.Instance;
			((Graphic)component).color = ((((instance != null) ? new int?(instance.GetIntLocal("jukebox.songPanelStyle", 0)) : ((int?)null)) == (int?)style) ? Active : Inactive);
		}

		public void Select()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			Button[] componentsInChildren = ((Component)((Component)this).transform.parent).GetComponentsInChildren<Button>();
			foreach (Button val in componentsInChildren)
			{
				((Graphic)((Component)val).gameObject.GetComponent<Image>()).color = (((Object)(object)val == (Object)(object)((Component)this).gameObject.GetComponent<Button>()) ? Active : Inactive);
			}
			PrefsManager instance = MonoSingleton<PrefsManager>.Instance;
			if (instance != null)
			{
				instance.SetIntLocal("jukebox.songPanelStyle", (int)style);
			}
		}
	}
	public enum SongPanelStyle
	{
		Jukebox,
		Classic
	}
}
namespace Jukebox.UI.Elements
{
	public class Counter : MonoBehaviour
	{
		[SerializeField]
		private TextMeshProUGUI textValue;

		[SerializeField]
		private CounterButton increase;

		[SerializeField]
		private CounterButton decrease;

		[SerializeField]
		private int minValue;

		[SerializeField]
		private int maxValue;

		private IEnumerator changeValueRoutine;

		private int tmpValue;

		[CompilerGenerated]
		private int <Value>k__BackingField;

		public int Value
		{
			[CompilerGenerated]
			get
			{
				return <Value>k__BackingField;
			}
			set
			{
				<Value>k__BackingField = value;
				((TMP_Text)textValue).text = Value.ToString();
				this.OnChanged?.Invoke(value);
			}
		}

		public event Action<int> OnChanged;

		public void Awake()
		{
			AddEvents(increase, delegate
			{
				tmpValue = ChangeValue((int i) => i + 1);
			});
			AddEvents(decrease, delegate
			{
				tmpValue = ChangeValue((int i) => i - 1);
			});
		}

		public void SetDefaultValue(int defaultValue)
		{
			Value = defaultValue;
			tmpValue = defaultValue;
			((TMP_Text)textValue).text = defaultValue.ToString();
		}

		private void AddEvents(CounterButton button, Action<int> callback)
		{
			button.OnDown += delegate
			{
				changeValueRoutine = ChangeValueRoutine(callback);
				((MonoBehaviour)this).StartCoroutine(changeValueRoutine);
			};
			button.OnUp += SaveValue;
			button.OnExit += SaveValue;
		}

		private void SaveValue()
		{
			if (changeValueRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(changeValueRoutine);
			}
			Value = tmpValue;
			this.OnChanged?.Invoke(Value);
		}

		private IEnumerator ChangeValueRoutine(Action<int> callback)
		{
			callback(tmpValue);
			yield return (object)new WaitForSecondsRealtime(0.3f);
			while (true)
			{
				callback(tmpValue);
				yield return (object)new WaitForSecondsRealtime(0.1f);
			}
		}

		private int ChangeValue(Func<int, int> operation)
		{
			int result = ValidateOperation(operation);
			((TMP_Text)textValue).text = result.ToString();
			return result;
		}

		private int ValidateOperation(Func<int, int> operation)
		{
			int num = operation(tmpValue);
			if (num >= minValue && num <= maxValue)
			{
				return num;
			}
			return tmpValue;
		}
	}
	public class CounterButton : Button
	{
		public bool pressed;

		public event Action OnDown;

		public event Action OnUp;

		public event Action OnExit;

		public override void OnPointerDown(PointerEventData eventData)
		{
			pressed = true;
			((Selectable)this).OnPointerDown(eventData);
			this.OnDown?.Invoke();
		}

		public override void OnPointerUp(PointerEventData eventData)
		{
			pressed = false;
			((Selectable)this).OnPointerUp(eventData);
			this.OnUp?.Invoke();
		}

		public override void OnPointerExit(PointerEventData eventData)
		{
			((Selectable)this).OnPointerExit(eventData);
			if (pressed)
			{
				this.OnExit?.Invoke();
			}
			pressed = false;
		}
	}
	public class RewindSlider : MonoBehaviour, IPointerDownHandler, IEventSystemHandler, IPointerUpHandler
	{
		[SerializeField]
		public Slider slider;

		[NonSerialized]
		public bool beingDragged;

		public event Action<float> OnValueChanged;

		public void OnPointerDown(PointerEventData eventData)
		{
			beingDragged = true;
		}

		public void OnPointerUp(PointerEventData eventData)
		{
			beingDragged = false;
			this.OnValueChanged?.Invoke(slider.value);
		}
	}
}
namespace Jukebox.UI.Effects
{
	public class SoundEffectButton : MonoBehaviour
	{
		[SerializeField]
		private string prefsKey;

		[SerializeField]
		private TMP_Text title;

		[SerializeField]
		private TMP_Text fileNameText;

		[SerializeField]
		private TMP_Text browserWindowTitle;

		protected void Awake()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			PrefsManager instance = MonoSingleton<PrefsManager>.Instance;
			ChangeFileName((instance != null) ? instance.GetStringLocal(prefsKey, (string)null) : null);
			PrefsManager.onPrefChanged = (Action<string, object>)Delegate.Combine(PrefsManager.onPrefChanged, new Action<string, object>(OnPrefChanged));
			((UnityEvent)((Component)this).GetComponent<Button>().onClick).AddListener((UnityAction)delegate
			{
				browserWindowTitle.text = "Select file for " + title.text;
			});
		}

		protected void OnDestroy()
		{
			PrefsManager.onPrefChanged = (Action<string, object>)Delegate.Remove(PrefsManager.onPrefChanged, new Action<string, object>(OnPrefChanged));
		}

		private void OnPrefChanged(string key, object value)
		{
			if (key.Equals(prefsKey))
			{
				ChangeFileName((string)value);
			}
		}

		private void ChangeFileName(string fileName)
		{
			fileNameText.text = ((fileName != null) ? new FileInfo(fileName).Name : "Default");
		}
	}
}
namespace Jukebox.UI.Decorators
{
	public class ControlBindDecorator : MonoBehaviour
	{
		[SerializeField]
		public InputActionReference action;

		protected void Awake()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			InputManager instance = MonoSingleton<InputManager>.Instance;
			InputAction val = ((instance != null) ? instance.InputSource.Actions.asset.FindAction(action.ToInputAction().id) : null);
			((Component)this).GetComponentInChildren<ControlsOptionsKey>().RebuildBindings(val, MonoSingleton<InputManager>.Instance.InputSource.Actions.KeyboardMouseScheme);
		}
	}
	public abstract class UIDecorator<T> : MonoBehaviour where T : MonoBehaviour
	{
		[SerializeField]
		public GameObject hint;

		protected GameObject control;

		public void Start()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			control = Object.Instantiate<GameObject>(Template(), ((Component)this).transform);
			control.transform.position = ((Component)this).transform.position;
			Object.Destroy((Object)(object)hint);
			Init();
			Object.Destroy((Object)(object)((Component)this).GetComponent<T>());
		}

		protected abstract GameObject Template();

		protected abstract void Init();
	}
}
namespace Jukebox.Patches
{
	[HarmonyPatch(typeof(AudioMixerController))]
	public static class AudioMixerControllerPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(AudioMixerController), "Update")]
		public static void AudioMixerController_Update_Postfix(AudioMixerController __instance)
		{
			if (!((Object)(object)MonoSingleton<JukeboxManager>.Instance == (Object)null))
			{
				__instance.musicSound.SetFloat("allVolume", __instance.CalculateVolume(__instance.musicVolume) + MonoSingleton<JukeboxManager>.Instance.player.State.VolumeBoost);
			}
		}
	}
	[HarmonyPatch(typeof(DiscordController))]
	public static class DiscordControllerPatch
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UpdateActivityHandler <>9__0_0;

			internal void <DiscordController_UpdateWave_Prefix>b__0_0(Result _)
			{
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(DiscordController), "UpdateWave")]
		public static bool DiscordController_UpdateWave_Prefix(int wave)
		{
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Expected O, but got Unknown
			JukeboxMusicPlayer jukeboxMusicPlayer = MonoSingleton<JukeboxManager>.Instance?.player;
			if ((Object)(object)jukeboxMusicPlayer == (Object)null || jukeboxMusicPlayer.State.Song == null || !MonoSingleton<PrefsManager>.Instance.GetBoolLocal("jukebox.discordAndSteamIntegration", false))
			{
				return true;
			}
			if ((Object)(object)DiscordController.Instance == (Object)null)
			{
				return false;
			}
			Activity val = ReflectionUtils.GetPrivate<Activity>((object)DiscordController.Instance, typeof(DiscordController), "cachedActivity");
			string details = val.Details;
			try
			{
				bool num = ReflectionUtils.GetPrivate<bool>((object)DiscordController.Instance, typeof(DiscordController), "disabled");
				ActivityManager val2 = ReflectionUtils.GetPrivate<ActivityManager>((object)DiscordController.Instance, typeof(DiscordController), "activityManager");
				Discord val3 = ReflectionUtils.GetPrivate<Discord>((object)DiscordController.Instance, typeof(DiscordController), "discord");
				if (num)
				{
					return false;
				}
				val.Details = $"WAVE: {wave} | \ud83c\udfb5 {jukeboxMusicPlayer.State.Song.ArtistAndTrack}";
				ReflectionUtils.SetPrivate<DiscordController, Activity>(DiscordController.Instance, typeof(DiscordController), "cachedActivity", val);
				if (val3 == null || val2 == null)
				{
					return false;
				}
				Activity val4 = val;
				object obj = <>c.<>9__0_0;
				if (obj == null)
				{
					UpdateActivityHandler val5 = delegate
					{
					};
					<>c.<>9__0_0 = val5;
					obj = (object)val5;
				}
				val2.UpdateActivity(val4, (UpdateActivityHandler)obj);
				return false;
			}
			catch (Exception)
			{
				val.Details = details;
				ReflectionUtils.SetPrivate<DiscordController, Activity>(DiscordController.Instance, typeof(DiscordController), "cachedActivity", val);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(FinalCyberRank))]
	public static class FinalCyberRankPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(FinalCyberRank), "GameOver")]
		public static void FinalCyberRank_GameOver_Prefix()
		{
			JukeboxMusicPlayer jukeboxMusicPlayer = MonoSingleton<JukeboxManager>.Instance?.player;
			if ((Object)(object)jukeboxMusicPlayer != (Object)null && MonoSingleton<PrefsManager>.Instance.GetBoolLocal("jukebox.resumeLastSong", false) && jukeboxMusicPlayer.State.Song != null && (Object)(object)jukeboxMusicPlayer.Source.clip != (Object)null)
			{
				JukeboxPreferencesManager instance = MonoSingleton<JukeboxPreferencesManager>.Instance;
				instance.SetPlaybackPosition(jukeboxMusicPlayer.State.Song.Id, jukeboxMusicPlayer.State.ClipIndex, jukeboxMusicPlayer.Source.time);
				instance.ForceSavePreferences();
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(FinalCyberRank), "Appear")]
		public static void FinalCyberRank_Appear_Postfix(FinalCyberRank __instance)
		{
			JukeboxMusicPlayer jukeboxMusicPlayer = MonoSingleton<JukeboxManager>.Instance?.player;
			if (jukeboxMusicPlayer != null && __instance.toAppear[0].activeSelf && !jukeboxMusicPlayer.State.Stopped)
			{
				MonoSingleton<JukeboxManager>.Instance?.player.Stop();
			}
		}
	}
	[HarmonyPatch(typeof(InputActions))]
	public static class InputActionsPatch
	{
		private const string ActionMapName = "Jukebox";

		private static InputActionAsset _controls;

		public static void Load()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			_controls = Addressables.LoadAssetAsync<InputActionAsset>((object)"Assets/Jukebox/JukeboxControls.inputactions").WaitForCompletion();
		}

		[HarmonyPostfix]
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		public static void InputActions_Constructor_Postfix(InputActions __instance)
		{
			if (__instance.asset.FindActionMap("Jukebox", false) == null)
			{
				MergeInputActionAssets(__instance);
			}
		}

		private static void MergeInputActionAssets(InputActions ukInputActions)
		{
			if (Resources.FindObjectsOfTypeAll(typeof(InputActionAsset)).Length == 0)
			{
				JukeboxLogger.Log.LogError((object)"Couldn't load InputActionAsset");
			}
			else if (ukInputActions.asset.FindActionMap("Jukebox", false) == null)
			{
				ukInputActions.Disable();
				InputActionSetupExtensions.AddActionMap(ukInputActions.asset, InputActionMap.FromJson(_controls.ToJson())[0]);
				ukInputActions.Enable();
			}
		}
	}
	[HarmonyPatch(typeof(InputManager))]
	public static class InputManagerPatch
	{
		private const string ActionMapName = "Jukebox";

		private static FileInfo JukeboxBindingsFile => new FileInfo(Path.Combine(PrefsManager.PrefsPath, "JukeboxBinds.json"));

		[HarmonyPostfix]
		[HarmonyPatch(typeof(InputManager), "Awake")]
		public static void InputManager_Awake_Postfix(InputManager __instance)
		{
			__instance.InputSource.Disable();
			if (JukeboxBindingsFile.Exists)
			{
				JsonConvert.DeserializeObject<JsonBindingMap>(File.ReadAllText(JukeboxBindingsFile.FullName)).ApplyTo(__instance.InputSource.Actions.asset);
			}
			__instance.UpgradeBindings();
			__instance.InputSource.Enable();
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(InputManager), "OnDisable")]
		public static void InputManager_OnDisable_Postfix(InputManager __instance)
		{
			SaveBindings(__instance, __instance.InputSource.Actions.asset);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(InputManager), "Rebind")]
		public static bool InputManager_Rebind_Prefix(InputManager __instance, InputAction action, int? existingIndex, Action onComplete, Action onCancel, InputControlScheme scheme)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if (action.actionMap.name != "Jukebox")
			{
				return true;
			}
			__instance.WaitForButton((Action<string>)delegate(string path)
			{
				//IL_0036: Unknown result type (might be due to invalid IL or missing references)
				//IL_0091: Unknown result type (might be due to invalid IL or missing references)
				//IL_0097: Unknown result type (might be due to invalid IL or missing references)
				//IL_0098: Unknown result type (might be due to invalid IL or missing references)
				//IL_0082: Unknown result type (might be due to invalid IL or missing references)
				//IL_009d: Unknown result type (might be due to invalid IL or missing references)
				//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)
				//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
				if (path == "<Mouse>/leftButton")
				{
					onComplete?.Invoke();
				}
				else if (((IEnumerable<InputBinding>)(object)action.bindings).Any((InputBinding binding) => InputSystem.FindControl(((InputBinding)(ref binding)).path) == InputSystem.FindControl(path)))
				{
					onComplete?.Invoke();
				}
				else
				{
					BindingSyntax val = ((!existingIndex.HasValue) ? InputActionSetupExtensions.AddBinding(action, default(InputBinding)) : InputActionSetupExtensions.ChangeBinding(action, existingIndex.GetValueOrDefault()));
					val = ((BindingSyntax)(ref val)).WithPath(path);
					((BindingSyntax)(ref val)).WithGroup(((InputControlScheme)(ref scheme)).bindingGroup);
					__instance.actionModified?.Invoke(action);
					if (onComplete != null)
					{
						onComplete();
					}
				}
			}, (Action)delegate
			{
				onCancel?.Invoke();
			}, ((IEnumerable<DeviceRequirement>)(object)((InputControlScheme)(ref scheme)).deviceRequirements).Select((DeviceRequirement requirement) => ((DeviceRequirement)(ref requirement)).controlPath).ToList());
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(InputManager), "SaveBindings")]
		public static void InputManager_SaveBindings_Postfix(InputManager __instance, InputActionAsset asset)
		{
			SaveBindings(__instance, asset);
		}

		private static void SaveBindings(InputManager instance, InputActionAsset asset)
		{
			File.WriteAllText(JukeboxBindingsFile.FullName, JsonConvert.SerializeObject((object)BindingMapJson(asset, instance), (Formatting)1));
		}

		private static JsonBindingMap BindingMapJson(InputActionAsset asset, InputManager instance)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			InputControlScheme scheme = instance.InputSource.Actions.KeyboardMouseScheme;
			InputActionAsset baseAsset = instance.defaultActions;
			JsonBindingMap val = new JsonBindingMap
			{
				controlScheme = ((InputControlScheme)(ref scheme)).bindingGroup
			};
			foreach (InputAction item in from a in (IEnumerable<InputAction>)asset
				let action = baseAsset.FindAction(a.id)
				where action.actionMap.name == "Jukebox" && !InputExtensions.IsActionEqual(a, action, ((InputControlScheme)(ref scheme)).bindingGroup)
				select a)
			{
				val.AddAction(item);
			}
			return val;
		}
	}
	[HarmonyPatch(typeof(ScreenZone))]
	public static class ScreenZonePatch
	{
		private static PreviewHelper _helper;

		private static ScreenZone _cgTerminal;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ScreenZone), "Awake")]
		public static void ScreenZone_Awake_Postfix(ScreenZone __instance)
		{
			if (!(((Object)__instance).name != "Cybergrind Shop"))
			{
				PreviewHelper previewHelper = default(PreviewHelper);
				if (!((Component)__instance).TryGetComponent<PreviewHelper>(ref previewHelper))
				{
					((Component)__instance).gameObject.AddComponent<PreviewHelper>();
				}
				_cgTerminal = __instance;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ScreenZone), "Update")]
		public static bool ScreenZone_Update_Prefix(bool ___inZone, ScreenZone __instance)
		{
			if ((Object)(object)_cgTerminal != (Object)(object)__instance)
			{
				return true;
			}
			if ((Object)(object)_helper == (Object)null)
			{
				PreviewHelper helper = default(PreviewHelper);
				if (!((Component)__instance).TryGetComponent<PreviewHelper>(ref helper))
				{
					return true;
				}
				_helper = helper;
			}
			_helper.inZone = ___inZone;
			return true;
		}
	}
	[HarmonyPatch(typeof(SteamController))]
	public static class SteamControllerPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(SteamController), "UpdateWave")]
		public static bool SteamController_UpdateWave_Prefix(int wave)
		{
			JukeboxMusicPlayer jukeboxMusicPlayer = MonoSingleton<JukeboxManager>.Instance?.player;
			if ((Object)(object)jukeboxMusicPlayer == (Object)null || jukeboxMusicPlayer.State.Song == null || !MonoSingleton<PrefsManager>.Instance.GetBoolLocal("jukebox.discordAndSteamIntegration", false))
			{
				return true;
			}
			if (!SteamClient.IsValid)
			{
				return false;
			}
			SteamFriends.SetRichPresence("wave", $"WAVE: {wave} | \ud83c\udfb5 {jukeboxMusicPlayer.State.Song.ArtistAndTrack}");
			return false;
		}
	}
}
namespace Jukebox.Input
{
	[ConfigureSingleton(/*Could not decode attribute arguments.*/)]
	public class JukeboxInputs : MonoSingleton<JukeboxInputs>
	{
		private InputActionMap jukeboxActionMap;

		public InputAction Menu { get; private set; }

		public InputAction Playback { get; private set; }

		public InputAction NextTrack { get; private set; }

		public InputAction RandomTrack { get; private set; }

		public InputAction DisablePlayer { get; private set; }

		public void Awake()
		{
			InputManager instance = MonoSingleton<InputManager>.Instance;
			jukeboxActionMap = ((instance != null) ? instance.InputSource.Actions.asset.FindActionMap("Jukebox", false) : null);
			InputActionMap obj = jukeboxActionMap;
			Menu = ((obj != null) ? obj.FindAction("Jukebox Menu", false) : null);
			InputActionMap obj2 = jukeboxActionMap;
			Playback = ((obj2 != null) ? obj2.FindAction("Playback Menu", false) : null);
			InputActionMap obj3 = jukeboxActionMap;
			NextTrack = ((obj3 != null) ? obj3.FindAction("Next Track", false) : null);
			InputActionMap obj4 = jukeboxActionMap;
			RandomTrack = ((obj4 != null) ? obj4.FindAction("Random Track", false) : null);
			InputActionMap obj5 = jukeboxActionMap;
			DisablePlayer = ((obj5 != null) ? obj5.FindAction("Disable Player", false) : null);
		}
	}
}
namespace Jukebox.Components
{
	public class CybergrindEffectsChanger : MonoBehaviour
	{
		private const string TerminalPath = "/FirstRoom/Room/Cybergrind Shop/Music";

		private const string ParryParam = "jukebox.effects.parry";

		private const string WaveParam = "jukebox.effects.wavecomplete";

		private const string AwwParam = "jukebox.effects.aww";

		private const string GameOverParam = "jukebox.effects.gameover";

		private const string TerminalMusicParam = "jukebox.effects.terminal-music";

		private AudioClip awwDefault;

		private AudioClip cheerDefault;

		private AudioClip cheerLongDefault;

		private AudioClip gameOverDefault;

		private AudioClip terminalMusicDefault;

		private CrowdReactions crowdReactions;

		private PrefsManager prefsManager;

		[CompilerGenerated]
		private AudioSource <EndMusicSource>k__BackingField;

		[CompilerGenerated]
		private AudioSource <MenuMusicSource>k__BackingField;

		[CompilerGenerated]
		private AudioSource <CrowdAudioSource>k__BackingField;

		private AudioSource EndMusicSource
		{
			get
			{
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)<EndMusicSource>k__BackingField == (Object)null)
				{
					Scene activeScene = SceneManager.GetActiveScene();
					<EndMusicSource>k__BackingField = ((Scene)(ref activeScene)).GetRootGameObjects().First((GameObject o) => ((Object)o).name == "EndMusic").GetComponent<AudioSource>();
				}
				return <EndMusicSource>k__BackingField;
			}
		}

		private AudioSource MenuMusicSource
		{
			get
			{
				if ((Object)(object)<MenuMusicSource>k__BackingField == (Object)null)
				{
					<MenuMusicSource>k__BackingField = GameObject.Find("/FirstRoom/Room/Cybergrind Shop/Music").GetComponent<AudioSource>();
				}
				return <MenuMusicSource>k__BackingField;
			}
		}

		private AudioSource CrowdAudioSource
		{
			get
			{
				if ((Object)(object)<CrowdAudioSource>k__BackingField == (Object)null)
				{
					<CrowdAudioSource>k__BackingField = ReflectionUtils.GetPrivate<AudioSource>((object)crowdReactions, typeof(CrowdReactions), "aud");
				}
				return <CrowdAudioSource>k__BackingField;
			}
		}

		private void Awake()
		{
			crowdReactions = MonoSingleton<CrowdReactions>.Instance;
			prefsManager = MonoSingleton<PrefsManager>.Instance;
			awwDefault = Object.Instantiate<AudioClip>(crowdReactions?.aww, ((Component)this).gameObject.transform);
			cheerDefault = Object.Instantiate<AudioClip>(crowdReactions?.cheer, ((Component)this).gameObject.transform);
			cheerLongDefault = Object.Instantiate<AudioClip>(crowdReactions?.cheerLong, ((Component)this).gameObject.transform);
			terminalMusicDefault = Object.Instantiate<AudioClip>(MenuMusicSource.clip, ((Component)this).gameObject.transform);
			gameOverDefault = Object.Instantiate<AudioClip>(EndMusicSource.clip, ((Component)this).gameObject.transform);
			((MonoBehaviour)this).StartCoroutine(Setup());
			PrefsManager.onPrefChanged = (Action<string, object>)Delegate.Combine(PrefsManager.onPrefChanged, new Action<string, object>(OnPrefChanged));
		}

		private void OnDestroy()
		{
			PrefsManager.onPrefChanged = (Action<string, object>)Delegate.Remove(PrefsManager.onPrefChanged, new Action<string, object>(OnPrefChanged));
		}

		private IEnumerator Setup()
		{
			yield return PrepareParrySfx();
			yield return PrepareWaveCompleteSfx();
			yield return PrepareAwwSfx();
			yield return PrepareGameOverSfx();
			yield return PrepareTerminalMusic();
		}

		private IEnumerator PrepareParrySfx()
		{
			return PrepareClip(prefsManager.GetStringLocal("jukebox.effects.parry", (string)null), delegate(AudioClip clip)
			{
				crowdReactions.cheer = clip;
			}, cheerDefault);
		}

		private IEnumerator PrepareWaveCompleteSfx()
		{
			return PrepareClip(prefsManager.GetStringLocal("jukebox.effects.wavecomplete", (string)null), delegate(AudioClip clip)
			{
				crowdReactions.cheerLong = clip;
			}, cheerLongDefault);
		}

		private IEnumerator PrepareAwwSfx()
		{
			return PrepareClip(prefsManager.GetStringLocal("jukebox.effects.aww", (string)null), delegate(AudioClip clip)
			{
				crowdReactions.aww = clip;
			}, awwDefault);
		}

		private IEnumerator PrepareGameOverSfx()
		{
			return PrepareClip(prefsManager.GetStringLocal("jukebox.effects.gameover", (string)null), delegate(AudioClip clip)
			{
				EndMusicSource.clip = clip;
			}, gameOverDefault);
		}

		private IEnumerator PrepareTerminalMusic()
		{
			AudioSource terminalMusic = MenuMusicSource;
			return PrepareClip(prefsManager.GetStringLocal("jukebox.effects.terminal-music", (string)null), delegate(AudioClip clip)
			{
				terminalMusic.Stop();
				terminalMusic.volume = 1f;
				terminalMusic.clip = clip;
				terminalMusic.Play();
			}, terminalMusicDefault);
		}

		private IEnumerator PrepareClip(string path, Action<AudioClip> callback, AudioClip fallback)
		{
			if (string.IsNullOrEmpty(path))
			{
				callback(fallback);
			}
			else
			{
				yield return Download(new FileInfo(path), callback);
			}
		}

		private IEnumerator Download(FileSystemInfo path, Action<AudioClip> callback)
		{
			string absoluteUri = new Uri(path.FullName).AbsoluteUri;
			if (!CustomMusicFileBrowser.extensionTypeDict.TryGetValue(path.Extension.ToLower(), out var value))
			{
				JukeboxLogger.Log.LogWarning((object)("Unsupported audio file extension: " + path.Extension));
				yield break;
			}
			DownloadHandlerAudioClip handler = new DownloadHandlerAudioClip(absoluteUri, value);
			UnityWebRequest wr = new UnityWebRequest(absoluteUri, "GET", (DownloadHandler)(object)handler, (UploadHandler)null);
			try
			{
				yield return wr.SendWebRequest();
				if (wr.responseCode == 200)
				{
					callback(handler.audioClip);
				}
			}
			finally
			{
				((IDisposable)wr)?.Dispose();
			}
		}

		private void OnPrefChanged(string key, object value)
		{
			switch (key)
			{
			case "jukebox.effects.wavecomplete":
				((MonoBehaviour)this).StartCoroutine(PrepareWaveCompleteSfx());
				break;
			case "jukebox.effects.aww":
				((MonoBehaviour)this).StartCoroutine(PrepareAwwSfx());
				break;
			case "jukebox.effects.gameover":
				((MonoBehaviour)this).StartCoroutine(PrepareGameOverSfx());
				break;
			case "jukebox.effects.terminal-music":
				((MonoBehaviour)this).StartCoroutine(PrepareTerminalMusic());
				break;
			case "jukebox.effects.parry":
				CrowdAudioSource.Stop();
				((MonoBehaviour)this).StartCoroutine(PrepareParrySfx());
				break;
			}
		}
	}
	public class DeckShuffled<T> : IEnumerable<T>, IEnumerable
	{
		private List<T> current = Randomize(target).ToList();

		public DeckShuffled(IEnumerable<T> target)
		{
		}

		public IEnumerator<T> GetEnumerator()
		{
			return current.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return current.GetEnumerator();
		}

		public void Reshuffle()
		{
			if (current.Count > 1)
			{
				current = Randomize(current).ToList();
			}
		}

		private static IEnumerable<T> Randomize(IEnumerable<T> source)
		{
			T[] arr = source.ToArray();
			int i = arr.Length - 1;
			while (i > 0)
			{
				int swapIndex = Random.Range(0, i + 1);
				yield return arr[swapIndex];
				arr[swapIndex] = arr[i];
				int num = i - 1;
				i = num;
			}
			yield return arr[0];
		}
	}
	public class JukeboxEffectsBrowser : DirectoryTreeBrowser<FileInfo>
	{
		private static readonly DirectoryInfo EffectsDirectory = new DirectoryInfo(PathsUtils.EffectsPath);

		[SerializeField]
		private JukeboxTerminalPanel navigator;

		[SerializeField]
		private GameObject effectsMenu;

		private string currentPrefKey;

		protected override int maxPageLength => 3;

		protected override IDirectoryTree<FileInfo> baseDirectory => (IDirectoryTree<FileInfo>)new FileDirectoryTree(EffectsDirectory, (IDirectoryTree<FileInfo>)null);

		public void NextPage()
		{
			base.SetPage((base.currentPage != base.maxPages - 1) ? (base.currentPage + 1) : 0);
		}

		public void PreviousPage()
		{
			base.SetPage((base.currentPage == 0) ? (base.maxPages - 1) : (base.currentPage - 1));
		}

		public void SetCurrentPrefKey(string key)
		{
			currentPrefKey = key;
		}

		public void ResetToDefault()
		{
			PrefsManager instance = MonoSingleton<PrefsManager>.Instance;
			if (instance != null)
			{
				instance.SetStringLocal(currentPrefKey, (string)null);
			}
		}

		protected override Action BuildLeaf(FileInfo item, int indexInPage)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			GameObject go = Object.Instantiate<GameObject>(base.itemButtonTemplate, base.itemParent);
			CustomContentButton component = go.GetComponent<CustomContentButton>();
			component.text.text = item.Name;
			go.SetActive(true);
			((UnityEvent)component.button.onClick).AddListener((UnityAction)delegate
			{
				PrefsManager instance = MonoSingleton<PrefsManager>.Instance;
				if (instance != null)
				{
					instance.SetStringLocal(currentPrefKey, item.FullName);
				}
				navigator.GoToNoMenu(effectsMenu);
			});
			return delegate
			{
				Object.Destroy((Object)(object)go);
			};
		}

		public void OpenEffectsFolder()
		{
			Application.OpenURL(new Uri(EffectsDirectory.FullName).AbsoluteUri);
		}

		public override void Rebuild(bool setToPageZero = true)
		{
			Transform itemParent = base.itemParent;
			LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)(object)((itemParent is RectTransform) ? itemParent : null));
			base.Rebuild(setToPageZero);
		}
	}
	public class JukeboxFileBrowser : DirectoryTreeBrowser<JukeboxSong>
	{
		[SerializeField]
		private JukeboxTerminalPanel navigator;

		[SerializeField]
		private JukeboxPlaylistEditor playlistEditorLogic;

		[SerializeField]
		private GameObject playlistEditor;

		[Header("Load all warning")]
		[SerializeField]
		private TMP_Text loadAllWarningText;

		[SerializeField]
		private GameObject yesAndNoButtons;

		[SerializeField]
		private GameObject imADumbassButton;

		[SerializeField]
		private Sprite youtubeIcon;

		protected override int maxPageLength => 4;

		protected override IDirectoryTree<JukeboxSong> baseDirectory => (IDirectoryTree<JukeboxSong>)new JukeboxFileTree(PathsUtils.MusicPath, (IDirectoryTree<JukeboxSong>)null);

		public void NextPage()
		{
			base.SetPage((base.currentPage != base.maxPages - 1) ? (base.currentPage + 1) : 0);
		}

		public void PreviousPage()
		{
			base.SetPage((base.currentPage == 0) ? (base.maxPages - 1) : (base.currentPage - 1));
		}

		public void LoadAllFromCurrentFolder()
		{
			foreach (JukeboxSong file in base.currentDirectory.files)
			{
				playlistEditorLogic.playlist.Add(file.Id);
			}
			((DirectoryTreeBrowser<JukeboxSong>)playlistEditorLogic).Rebuild(true);
		}

		public void SetWarningText()
		{
			if (!base.currentDirectory.files.Any())
			{
				yesAndNoButtons.SetActive(false);
				imADumbassButton.SetActive(true);
				loadAllWarningText.text = ((!base.currentDirectory.children.Any()) ? "Oh, I get it. \"Yes, I would like to add a whopping total of 0 songs\". Very funny." : "This folder contains no songs.");
			}
			else if (base.currentDirectory.files.Count() == 1)
			{
				loadAllWarningText.text = "It's a single song in there, but ok. Would you like me to add it?";
			}
			else
			{
				loadAllWarningText.text = $"Do you want to add <color=green>{base.currentDirectory.files.Count()}</color> tracks " + "from the folder <color=yellow>" + base.currentDirectory.name + "</color> to your playlist?";
			}
		}

		public void Refresh()
		{
			((DirectoryTreeBrowser<JukeboxSong>)this).baseDirectory.Refresh();
			((DirectoryTreeBrowser<JukeboxSong>)this).Rebuild(true);
		}

		protected override Action BuildLeaf(JukeboxSong song, int indexInPage)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			GameObject go = Object.Instantiate<GameObject>(base.itemButtonTemplate, base.itemParent, false);
			JukeboxCustomContentButton component = go.GetComponent<JukeboxCustomContentButton>();
			JukeboxSongMetadata metadata = song.Metadata;
			((UnityEvent)((CustomContentButton)component).button.onClick).AddListener((UnityAction)delegate
			{
				int count = ((Playlist)playlistEditorLogic.playlist).Count;
				int page = ((DirectoryTreeBrowser<JukeboxSong>)playlistEditorLogic).PageOf(count);
				playlistEditorLogic.playlist.Add(song.Id);
				((DirectoryTreeBrowser<JukeboxSong>)playlistEditorLogic).SetPage(page);
				playlistEditorLogic.Select(count);
				navigator.GoToNoMenu(playlistEditor);
			});
			CompositeProperties compositeProperties = metadata.GetCompositeProperties();
			string text = ((!string.IsNullOrEmpty(metadata.Artist)) ? ("<color=#gray>" + metadata.Artist + "</color>") : string.Empty);
			((CustomContentButton)component).text.SetPreservingRtl(metadata.Title + " " + text);
			((CustomContentButton)component).icon.sprite = metadata.Icon;
			component.introAndLoopIcon.SetActive(compositeProperties.GotIntroAndLoop);
			component.calmThemeIcon.SetActive(compositeProperties.GotCalmTheme || compositeProperties.GotCalmIntro);
			go.SetActive(true);
			return delegate
			{
				Object.Destroy((Object)(object)go);
			};
		}

		protected override Action BuildDirectory(IDirectoryTree<JukeboxSong> folder, int indexInPage)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			GameObject btn = Object.Instantiate<GameObject>(base.folderButtonTemplate, base.itemParent, false);
			((UnityEventBase)btn.GetComponent<Button>().onClick).RemoveAllListeners();
			((UnityEvent)btn.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
			{
				base.StepDown(folder);
			});
			btn.GetComponentInChildren<TMP_Text>().SetPreservingRtl(folder.name);
			btn.SetActive(true);
			if (folder.name == "YouTube" && folder.parent?.parent == null)
			{
				((Component)btn.transform.Find("Icon")).GetComponent<Image>().sprite = youtubeIcon;
			}
			return delegate
			{
				Object.Destroy((Object)(object)btn);
			};
		}
	}
	[ConfigureSingleton(/*Could not decode attribute arguments.*/)]
	public class JukeboxManager : MonoSingleton<JukeboxManager>
	{
		private const string CanvasPath = "/Canvas";

		private const string CybergrindShopPath = "/FirstRoom/Room/Cybergrind Shop";

		private const string PanelPath = "/FirstRoom/Room/Cybergrind Shop/Canvas/Background/Main Panel";

		[SerializeField]
		public GameObject songPanel;

		[SerializeField]
		public GameObject subtitlesPanel;

		[SerializeField]
		public JukeboxPlaylistEditor playlistEditor;

		[SerializeField]
		public JukeboxMusicPlayer player;

		[SerializeField]
		private GameObject terminalPanel;

		[SerializeField]
		private GameObject musicLogic;

		[SerializeField]
		private GameObject musicChanger;

		protected void Awake()
		{
			ReplaceOriginalLogic();
			SetupTerminal();
			SetupSongPanel();
			SetupSubtitlesPanel();
		}

		private void SetupTerminal()
		{
			GameObject val = GameObject.Find("/FirstRoom/Room/Cybergrind Shop/Canvas/Background/Main Panel");
			terminalPanel.transform.SetParent(val.transform, false);
		}

		private void ReplaceOriginalLogic()
		{
			Transform val = GameObject.Find("/FirstRoom/Room/Cybergrind Shop").transform.Find("Logic");
			Object.Destroy((Object)(object)((Component)val.Find("Music")).gameObject);
			musicLogic.transform.parent = val;
			CustomMusicPlayer obj = Resources.FindObjectsOfTypeAll<CustomMusicPlayer>()[0];
			Transform parent = ((Component)obj).transform.parent;
			Object.Destroy((Object)(object)((Component)obj).gameObject);
			musicChanger.transform.parent = ((Component)parent).transform;
		}

		private void SetupSongPanel()
		{
			GameObject val = GameObject.Find("/Canvas");
			Object.Destroy((Object)(object)((Component)val.transform.Find("SongPanel")).gameObject);
			songPanel.transform.SetParent(val.transform, false);
		}

		private void SetupSubtitlesPanel()
		{
			GameObject val = GameObject.Find("/Canvas");
			subtitlesPanel.transform.SetParent(val.transform, false);
		}
	}
	public class JukeboxMusicChanger : MonoBehaviour
	{
		private enum ThemeVariation
		{
			Battle,
			Calm
		}

		private const int FadeSpeed = 1;

		public AudioSource clean;

		public AudioSource battle;

		private ThemeVariation currentTheme;

		private JukeboxThemesManager themesManager;

		private Coroutine updateRoutine;

		private static bool CalmThemesAllowed => MonoSingleton<PrefsManager>.Instance.GetBoolLocal("jukebox.enableCalmThemes", false);

		public void Awake()
		{
			themesManager = MonoSingleton<JukeboxThemesManager>.Instance;
		}

		private void Update()
		{
			if (!((Object)(object)clean.clip == (Object)null))
			{
				if (currentTheme == ThemeVariation.Calm)
				{
					clean.volume = Mathf.MoveTowards(clean.volume, 1f, 1f * Time.deltaTime);
					battle.volume = Mathf.MoveTowards(battle.volume, 0f, 1f * Time.deltaTime);
				}
				else
				{
					clean.volume = Mathf.MoveTowards(clean.volume, 0f, 1f * Time.deltaTime);
					battle.volume = Mathf.MoveTowards(battle.volume, 1f, 1f * Time.deltaTime);
				}
			}
		}

		public void ChangeTo(AudioClip battleTheme, AudioClip calmTheme, float startingFrom = 0f)
		{
			if (updateRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(updateRoutine);
			}
			clean.clip = calmTheme;
			battle.clip = battleTheme;
			battle.Play();
			battle.time = startingFrom;
			battle.volume = 1f;
			if ((Object)(object)clean.clip != (Object)null)
			{
				clean.Play();
				clean.time = startingFrom;
				if (CalmThemesAllowed && themesManager.FewEnemies)
				{
					clean.volume = 1f;
					battle.volume = 0f;
				}
			}
			updateRoutine = ((MonoBehaviour)this).StartCoroutine(SlowUpdate());
		}

		public void RewindTo(float position = 0f)
		{
			Seek(clean, position);
			Seek(battle, position);
			static void Seek(AudioSource source, float pos)
			{
				if (!((Object)(object)source == (Object)null) && !((Object)(object)source.clip == (Object)null))
				{
					int num = Mathf.RoundToInt(Mathf.Clamp(pos, 0f, source.clip.length) * (float)source.clip.frequency);
					source.timeSamples = Mathf.Clamp(num, 0, source.clip.samples - 1);
				}
			}
		}

		private IEnumerator SlowUpdate()
		{
			while (true)
			{
				if (CalmThemesAllowed && themesManager.FewEnemies)
				{
					PlayCalmTheme();
				}
				else
				{
					PlayBattleTheme();
				}
				yield return (object)new WaitForSecondsRealtime(0.1f);
			}
		}

		private void PlayBattleTheme()
		{
			if (currentTheme != ThemeVariation.Battle)
			{
				currentTheme = ThemeVariation.Battle;
				if ((Object)(object)clean.clip != (Object)null)
				{
					battle.timeSamples = clean.timeSamples;
				}
			}
		}

		private void PlayCalmTheme()
		{
			if (currentTheme != ThemeVariation.Calm)
			{
				currentTheme = ThemeVariation.Calm;
				if ((Object)(object)clean.clip != (Object)null)
				{
					clean.timeSamples = battle.timeSamples;
				}
			}
		}
	}
	public class JukeboxMusicPlayer : MonoBehaviour, IPlaybackController
	{
		private enum Command
		{
			NextTrack,
			RandomTrack,
			JumpTo,
			None
		}

		[SerializeField]
		private JukeboxPlaylistEditor playlistEditor;

		[SerializeField]
		private JukeboxSongsLoader loader;

		[SerializeField]
		private JukeboxMusicChanger changer;

		private PlaybackOrder playbackOrder;

		private JukeboxTrackPlayer trackPlayer;

		private Coroutine currentPlaylistRoutine;

		private JukeboxSongSession currentSession;

		private Command pendingPlayerCommand;

		private int jumpTo;

		private bool allowVolumeBoost;

		private SubtitleTrack lastSubtitleTrack;

		private int subtitleIndex = -1;

		private Coroutine rewindRoutine;

		public PlaybackState State { get; } = new PlaybackState();

		public AudioSource Source => changer.battle;

		private static float VolumeBoostValue => MonoSingleton<PrefsManager>.Instance.GetFloatLocal("jukebox.volumeBoost", 0f);

		private static bool IndividualBoostPerTrack => MonoSingleton<PrefsManager>.Instance.GetBoolLocal("jukebox.individualBoostPerTrack", false);

		private static bool ResumeLastSong => MonoSingleton<PrefsManager>.Instance.GetBoolLocal("jukebox.resumeLastSong", false);

		public event Action<ClipChangedArgs> OnNextAudioClip;

		public event Action<IEnumerable<SongIdentifier>> OnOrderChange;

		public event Action OnStop;

		public event Action<string> OnSubtitleChanged;

		public void Awake()
		{
			changer = ((Component)this).gameObject.GetComponent<JukeboxMusicChanger>();
			trackPlayer = new JukeboxTrackPlayer(changer, State, NextAudioClip);
			PrefsManager.onPrefChanged = (Action<string, object>)Delegate.Combine(PrefsManager.onPrefChanged, new Action<string, object>(OnPrefChanged));
			MonoSingleton<JukeboxInputs>.Instance.DisablePlayer.performed += OnPlayerDisabled;
			MonoSingleton<JukeboxInputs>.Instance.NextTrack.performed += OnNextTrack;
			MonoSingleton<JukeboxInputs>.Instance.RandomTrack.performed += OnRandomTrack;
		}

		public void OnEnable()
		{
			StartPlaylist();
		}

		private void OnDisable()
		{
			if (ResumeLastSong && State.Song != null)
			{
				JukeboxPreferencesManager instance = MonoSingleton<JukeboxPreferencesManager>.Instance;
				if (!((Object)(object)instance == (Object)null))
				{
					instance.SetPlaybackPosition(State.Song.Id, State.ClipIndex, Source.time);
					instance.ForceSavePreferences();
				}
			}
		}

		public void OnDestroy()
		{
			PrefsManager.onPrefChanged = (Action<string, object>)Delegate.Remove(PrefsManager.onPrefChanged, new Action<string, object>(OnPrefChanged));
			MonoSingleton<JukeboxInputs>.Instance.DisablePlayer.performed -= OnPlayerDisabled;
			MonoSingleton<JukeboxInputs>.Instance.NextTrack.performed -= OnNextTrack;
			MonoSingleton<JukeboxInputs>.Instance.RandomTrack.performed -= OnRandomTrack;
			StopCurrentRoutines();
			State.Song = null;
		}

		private void Update()
		{
			//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)
			SubtitleTrack subtitles = State.Subtitles;
			if (subtitles != lastSubtitleTrack)
			{
				lastSubtitleTrack = subtitles;
				subtitleIndex = -1;
			}
			int num = ((subtitles != null) ? subtitles.IndexAt(Source.time, subtitleIndex) : (-1));
			if (num != subtitleIndex)
			{
				subtitleIndex = num;
				PlaybackState state = State;
				object subtitleLine;
				if (num >= 0)
				{
					SubtitleLine val = subtitles.Lines[num];
					subtitleLine = ((SubtitleLine)(ref val)).Text;
				}
				else
				{
					subtitleLine = null;
				}
				state.SubtitleLine = (string)subtitleLine;
				this.OnSubtitleChanged?.Invoke(State.SubtitleLine);
			}
		}

		public void Stop()
		{
			State.Stopped = true;
		}

		public void ChangeTrack(int index)
		{
			jumpTo = index;
			pendingPlayerCommand = Command.JumpTo;
		}

		public void RewindTo(float position)
		{
			if (rewindRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(rewindRoutine);
			}
			rewindRoutine = ((MonoBehaviour)this).StartCoroutine(RewindRoutine(position));
		}

		private IEnumerator RewindRoutine(float position)
		{
			JukeboxSongSession obj = currentSession;
			JukeboxCustomSongSession val = (JukeboxCustomSongSession)(object)((obj is JukeboxCustomSongSession) ? obj : null);
			if (val == null || State.ClipIndex < 0)
			{
				changer.RewindTo(position);
				rewindRoutine = null;
				yield break;
			}
			if (val.IsFullyLoaded(State.ClipIndex))
			{
				changer.RewindTo(position);
				rewindRoutine = null;
				yield break;
			}
			AudioClip battleClip = null;
			AudioClip calmClip = null;
			yield return val.LoadFullyFor(State.ClipIndex, (Action<AudioClip, AudioClip>)delegate(AudioClip battle, AudioClip calm)
			{
				battleClip = battle;
				calmClip = calm;
			});
			if ((Object)(object)battleClip == (Object)null)
			{
				changer.RewindTo(position);
				rewindRoutine = null;
			}
			else
			{
				changer.ChangeTo(battleClip, calmClip, position);
				rewindRoutine = null;
			}
		}

		public void StartPlaylist()
		{
			if (((Playlist)playlistEditor.playlist).Count < 1)
			{
				JukeboxLogger.Log.LogError((object)"No songs in playlist, somehow. Not starting playlist routine...");
				return;
			}
			StopCurrentRoutines();
			State.Stopped = false;
			pendingPlayerCommand = Command.None;
			playbackOrder = new PlaybackOrder(playlistEditor.playlist);
			currentPlaylistRoutine = ((MonoBehaviour)this).StartCoroutine(PlaylistRoutine());
		}

		public void StopPlaylist()
		{
			Source.Stop();
			StopCurrentRoutines();
			State.Song = null;
			State.Subtitles = null;
			State.Stopped = true;
			pendingPlayerCommand = Command.None;
			State.ClipIndex = -1;
			State.ClipCount = 0;
		}

		private void StopCurrentRoutines()
		{
			if (currentPlaylistRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(currentPlaylistRoutine);
				currentPlaylistRoutine = null;
			}
			if (rewindRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(rewindRoutine);
				rewindRoutine = null;
			}
			JukeboxSongSession obj = currentSession;
			if (obj != null)
			{
				obj.Dispose();
			}
			currentSession = null;
		}

		private IEnumerator PlaylistRoutine()
		{
			JukeboxPreferencesManager instance = MonoSingleton<JukeboxPreferencesManager>.Instance;
			PlayStartingFrom resume = instance.GetPlaybackPosition();
			instance.ResetPlaybackPosition();
			bool flag = true;
			while (!State.Stopped)
			{
				List<SongIdentifier> order = playbackOrder.BuildOrder();
				this.OnOrderChange?.Invoke(order);
				int index = playbackOrder.StartIndex(order);
				PlayStartingFrom trackToResume = null;
				if (flag && ResumeLastSong && resume != null)
				{
					SongIdentifier resumeId = new SongIdentifier(resume.Path, resume.Type);
					int num = order.FindIndex((SongIdentifier id) => object.Equals(id, resumeId));
					if (num >= 0 && loader.Get(resumeId) != null)
					{
						index = num;
						trackToResume = resume;
						resume = null;
					}
				}
				int unplayable = 0;
				while (!State.Stopped)
				{
					State.SongIndex = index;
					JukeboxSongSession val = loader.CreateSession(loader.Get(order[index]));
					if (val == null)
					{
						JukeboxLogger.Log.LogWarning((object)("Unable to load " + order[index].path + ", skipping"));
						int num2 = unplayable + 1;
						unplayable = num2;
						if (num2 >= order.Count)
						{
							JukeboxLogger.Log.LogError((object)"No playable tracks in the playlist, stopping");
							State.Stopped = true;
							break;
						}
						int num3 = ResolveNext(index, order.Count);
						if (num3 < 0)
						{
							break;
						}
						index = num3;
					}
					else
					{
						unplayable = 0;
						currentSession = val;
						PresenceController.UpdateCyberGrindWave(MonoSingleton<EndlessGrid>.Instance.currentWave);
						yield return val.Load(trackPlayer.Play(val, flag, trackToResume, Interrupted, IsLoopOne));
						currentSession = null;
						flag = false;
						trackToResume = null;
						int num4 = ResolveNext(index, order.Count);
						if (num4 < 0)
						{
							break;
						}
						index = num4;
					}
				}
			}
		}

		private int ResolveNext(int cursor, int count)
		{
			int num = pendingPlayerCommand switch
			{
				Command.RandomTrack => PlaybackOrder.RandomIndex(count), 
				Command.JumpTo => jumpTo, 
				_ => cursor + 1, 
			};
			pendingPlayerCommand = Command.None;
			if (num >= count)
			{
				return -1;
			}
			return num;
		}

		private bool Interrupted()
		{
			if (pendingPlayerCommand == Command.None)
			{
				return State.Stopped;
			}
			return true;
		}

		private bool IsLoopOne()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			return (int)((Playlist)playlistEditor.playlist).loopMode == 1;
		}

		private void OnNextTrack(CallbackContext _)
		{
			pendingPlayerCommand = Command.NextTrack;
		}

		private void OnRandomTrack(CallbackContext _)
		{
			pendingPlayerCommand = Command.RandomTrack;
		}

		private void OnPlayerDisabled(CallbackContext _)
		{
			if (!State.Stopped)
			{
				StopPlaylist();
				this.OnStop?.Invoke();
			}
			else
			{
				StartPlaylist();
			}
		}

		private void OnPrefChanged(string key, object value)
		{
			if (!(key == "jukebox.volumeBoost"))
			{
				if (key == "jukebox.individualBoostPerTrack")
				{
					State.VolumeBoost = CalculateBoostFor(State.Song);
				}
				return;
			}
			if (IndividualBoostPerTrack)
			{
				JukeboxSong song = State.Song;
				if (song != null && song.IsCustom)
				{
					MonoSingleton<JukeboxPreferencesManager>.Instance?.SetVolumeBoostFor(State.Song.Id.path, (float)value);
				}
			}
			State.VolumeBoost = (float)value;
		}

		private void NextAudioClip(JukeboxSong song, bool newSong)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			State.Song = song;
			allowVolumeBoost = song.IsCustom;
			State.VolumeBoost = CalculateBoostFor(song);
			this.OnNextAudioClip?.Invoke(new ClipChangedArgs(song, newSong));
		}

		private float CalculateBoostFor(JukeboxSong song)
		{
			if (!allowVolumeBoost)
			{
				return 0f;
			}
			if (IndividualBoostPerTrack && song != null)
			{
				float? volumeBoostFor = MonoSingleton<JukeboxPreferencesManager>.Instance.GetVolumeBoostFor(song.Id.path);
				if (volumeBoostFor.HasValue)
				{
					return volumeBoostFor.GetValueOrDefault();
				}
			}
			return VolumeBoostValue;
		}
	}
	[ConfigureSingleton(/*Could not decode attribute arguments.*/)]
	public class JukeboxPauseManager : MonoSingleton<JukeboxPauseManager>
	{
		public void Pause(string stateKey, GameObject window)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			NewMovement instance = MonoSingleton<NewMovement>.Instance;
			if (instance != null)
			{
				((Behaviour)instance).enabled = false;
			}
			CameraController instance2 = MonoSingleton<CameraController>.Instance;
			if (instance2 != null)
			{
				instance2.activated = false;
			}
			GunControl instance3 = MonoSingleton<GunControl>.Instance;
			if (instance3 != null)
			{
				instance3.activated = false;
			}
			GameStateManager.Instance.RegisterState(new GameState(stateKey, (GameObject[])(object)new GameObject[1] { window })
			{
				cursorLock = (LockMode)2,
				cameraInputLock = (LockMode)1,
				playerInputLock = (LockMode)1
			});
			OptionsManager instance4 = MonoSingleton<OptionsManager>.Instance;
			if (instance4 != null)
			{
				instance4.paused = true;
			}
		}

		public void UnPause()
		{
			Time.timeScale = MonoSingleton<TimeController>.Instance.timeScale * MonoSingleton<TimeController>.Instance.timeScaleModifier;
			MonoSingleton<OptionsManager>.Instance.paused = false;
			MonoSingleton<CameraController>.Instance.activated = true;
			((Behaviour)MonoSingleton<NewMovement>.Instance).enabled = true;
			MonoSingleton<GunControl>.Instance.activated = true;
		}
	}
	public class JukeboxPlayback : DirectoryTreeBrowser<SongIdentifier>
	{
		[SerializeField]
		private Sprite defaultIcon;

		[SerializeField]
		private GameObject disclaimer;

		[SerializeField]
		private GameObject buttonsSection;

		[SerializeField]
		private RewindSlider rewindSlider;

		[SerializeField]
		private TMP_Text currentTimestamp;

		[SerializeField]
		private TMP_Text totalLength;

		private readonly List<Transform> buttons = new List<Transform>();

		private IEnumerable<SongIdentifier> order = new List<SongIdentifier>();

		[NonSerialized]
		private IPlaybackController player;

		protected override int maxPageLength => 5;

		protected override IDirectoryTree<SongIdentifier> baseDirectory => (IDirectoryTree<SongIdentifier>)(object)new FakeDirectoryTree<SongIdentifier>("Playlist", order, (IEnumerable<IDirectoryTree<SongIdentifier>>)null, (IDirectoryTree<SongIdentifier>)null);

		private CustomContentButton CurrentButton
		{
			get
			{
				Transform? obj = buttons.ElementAtOrDefault(player.State.SongIndex % ((DirectoryTreeBrowser<SongIdentifier>)this).maxPageLength);
				if (obj == null)
				{
					return null;
				}
				return ((Component)obj).GetComponent<CustomContentButton>();
			}
		}

		protected void Awake()
		{
			player = (IPlaybackController)(object)MonoSingleton<JukeboxManager>.Instance?.player;
			IPlaybackController obj = player;
			if (obj != null)
			{
				obj.OnOrderChange += OnOrderChange;
			}
			IPlaybackController obj2 = player;
			if (obj2 != null)
			{
				obj2.OnNextAudioClip += NextAudioClip;
			}
			IPlaybackController obj3 = player;
			if (obj3 != null)
			{
				obj3.OnStop += OnStop;
			}
			rewindSlider.OnValueChanged += delegate(float position)
			{
				player.RewindTo(position);
				rewindSlider.slider.value = position;
			};
		}

		private void Update()
		{
			if (!((Object)(object)player.Source == (Object)null) && !((Object)(object)player.Source.clip == (Object)null))
			{
				if (!rewindSlider.beingDragged)
				{
					rewindSlider.slider.value = player.Source.time;
				}
				currentTimestamp.text = player.Source.time.SecondsToHumanReadable();
			}
		}

		protected void OnEnable()
		{
			if (!object.Equals(base.currentDirectory, ((DirectoryTreeBrowser<SongIdentifier>)this).baseDirectory))
			{
				base.currentDirectory = ((DirectoryTreeBrowser<SongIdentifier>)this).baseDirectory;
				((DirectoryTreeBrowser<SongIdentifier>)this).Rebuild(false);
				base.SetPage(Mathf.Clamp(player.State.SongIndex / ((DirectoryTreeBrowser<SongIdentifier>)this).maxPageLength, 0, base.maxPages - 1));
			}
		}

		protected void OnDestroy()
		{
			player.OnStop -= OnStop;
			player.OnOrderChange -= OnOrderChange;
			player.OnNextAudioClip -= NextAudioClip;
		}

		public void NextPage()
		{
			base.SetPage((base.currentPage != base.maxPages - 1) ? (base.currentPage + 1) : 0);
		}

		public void PreviousPage()
		{
			base.SetPage((base.currentPage == 0) ? (base.maxPages - 1) : (base.currentPage - 1));
		}

		protected override Action BuildLeaf(SongIdentifier id, int indexInPage)
		{
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Expected O, but got Unknown
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			JukeboxSong obj = MonoSingleton<JukeboxSongsLoader>.Instance.Get(id);
			JukeboxSongMetadata val = ((obj != null) ? obj.Metadata : null);
			GameObject go = Object.Instantiate<GameObject>(base.itemButtonTemplate, base.itemButtonTemplate.transform.parent);
			CustomContentButton component = go.GetComponent<CustomContentButton>();
			string text = ((!string.IsNullOrEmpty(val.Artist)) ? ("<color=#gray>" + val.Artist + "</color>") : string.Empty);
			component.text.SetPreservingRtl(val.Title + " " + text);
			component.icon.sprite = (((Object)(object)val.Icon != (Object)null) ? val.Icon : defaultIcon);
			go.SetActive(true);
			buttons.Add(go.transform);
			if (base.PageOf(player.State.SongIndex) == base.currentPage && (Object)(object)component == (Object)(object)CurrentButton)
			{
				((Graphic)component.border).color = new Color(0.5f, 0.5f, 0.5f, 0.5f);
				Object.Destroy((Object)(object)go.GetComponent<Button>());
			}
			else
			{
				((UnityEvent)component.button.onClick).AddListener((UnityAction)delegate
				{
					player.ChangeTrack(base.currentPage * ((DirectoryTreeBrowser<SongIdentifier>)this).maxPageLength + indexInPage);
				});
			}
			return delegate
			{
				Object.Destroy((Object)(object)go);
			};
		}

		private void OnOrderChange(IEnumerable<SongIdentifier> songs)
		{
			order = songs;
			((DirectoryTreeBrowser<SongIdentifier>)this).Rebuild(true);
		}

		private void NextAudioClip(ClipChangedArgs clip)
		{
			AudioClip clip2 = player.Source.clip;
			rewindSlider.slider.value = 0f;
			rewindSlider.slider.maxValue = clip2.length;
			totalLength.text = clip2.length.SecondsToHumanReadable();
			buttonsSection.SetActive(true);
			((Selectable)rewindSlider.slider).interactable = true;
			disclaimer.SetActive(false);
			((DirectoryTreeBrowser<SongIdentifier>)this).Rebuild(false);
		}

		private void OnStop()
		{
			((Selectable)rewindSlider.slider).interactable = false;
		}

		public override void Rebuild(bool setToPageZero = true)
		{
			buttons.Clear();
			Transform itemParent = base.itemParent;
			LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)(object)((itemParent is RectTransform) ? itemParent : null));
			base.Rebuild(setToPageZero);
		}
	}
	public class JukeboxPlaylistEditor : DirectoryTreeBrowser<JukeboxSong>
	{
		[SerializeField]
		private JukeboxSongsLoader loader;

		[SerializeField]
		private JukeboxSoundtrackBrowser browser;

		[SerializeField]
		private Sprite defaultIcon;

		[SerializeField]
		private Sprite loopSprite;

		[SerializeField]
		private Sprite loopOnceSprite;

		[Header("UI Elements")]
		[SerializeField]
		private Image loopModeImage;

		[SerializeField]
		private Image shuffleImage;

		[SerializeField]
		private RectTransform selectedControls;

		[SerializeField]
		private List<Transform> anchors;

		public JukeboxPlaylist playlist = new JukeboxPlaylist();

		private readonly List<Transform> buttons = new List<Transform>();

		private readonly Dictionary<Transform, Coroutine> changeAnchorRoutines = new Dictionary<Transform, Coroutine>();

		protected override int maxPageLength => anchors.Count;

		protected override IDirectoryTree<JukeboxSong> baseDirectory => (IDirectoryTree<JukeboxSong>)(object)new FakeDirectoryTree<JukeboxSong>("Songs", ((Playlist)playlist).ids.Select((SongIdentifier id) => loader.Get(id)), (IEnumerable<IDirectoryTree<JukeboxSong>>)null, (IDirectoryTree<JukeboxSong>)null);

		private JukeboxCustomContentButton CurrentButton
		{
			get
			{
				Transform? obj = buttons.ElementAtOrDefault(((Playlist)playlist).selected % ((DirectoryTreeBrowser<JukeboxSong>)this).maxPageLength);
				if (obj == null)
				{
					return null;
				}
				return ((Component)obj).GetComponent<JukeboxCustomContentButton>();
			}
		}

		private void Start()
		{
			ReloadPlaylist();
			PrefsManager.onPrefChanged = (Action<string, object>)Delegate.Combine(PrefsManager.onPrefChanged, new Action<string, object>(OnPrefChange));
		}

		private void OnDestroy()
		{
			((Playlist)playlist).OnChanged -= SavePlaylist;
			PrefsManager.onPrefChanged = (Action<string, object>)Delegate.Remove(PrefsManager.onPrefChanged, new Action<string, object>(OnPrefChange));
		}

		public void ReloadPlaylist()
		{
			//IL_0009: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				LoadPlaylist();
			}
			catch (JsonReaderException ex)
			{
				JsonReaderException ex2 = ex;
				JukeboxLogger.Log.LogError((object)("Error loading Playlist.json: '" + ((Exception)(object)ex2).Message + "'. Recreating file."));
				File.Delete(JukeboxPlaylist.CurrentPath);
				LoadPlaylist();
			}
			Select(((Playlist)playlist).selected);
			SetLoopMode(((Playlist)playlist).loopMode);
			SetShuffle(((Playlist)playlist).shuffled);
			((Playlist)playlist).OnChanged += SavePlaylist;
		}

		public void SavePlaylist()
		{
			File.WriteAllText(JukeboxPlaylist.CurrentPath, JsonConvert.SerializeObject((object)playlist));
		}

		public void LoadPlaylist()
		{
			JukeboxLogger.Log.LogInfo((object)("Loading Playlist " + JukeboxPlaylist.CurrentPath));
			JukeboxPlaylist val;
			using (StreamReader streamReader = new StreamReader(File.Open(JukeboxPlaylist.CurrentPath, FileMode.OpenOrCreate)))
			{
				val = JsonConvert.DeserializeObject<JukeboxPlaylist>(streamReader.ReadToEnd());
			}
			if (val == null)
			{
				CreateEmpty();
			}
			else
			{
				val.RemoveUnplayable();
				if (((Playlist)val).ids.Count == 0)
				{
					CreateEmpty();
				}
				else
				{
					playlist = val;
				}
				base.currentDirectory = ((DirectoryTreeBrowser<JukeboxSong>)this).baseDirectory;
				((DirectoryTreeBrowser<JukeboxSong>)this).Rebuild(true);
			}
			if (MonoSingleton<PrefsManager>.Instance.GetBoolLocal("jukebox.preventDuplicateTracks", false))
			{
				playlist.RemoveDuplicates();
			}
			((DirectoryTreeBrowser<JukeboxSong>)this).Rebuild(true);
			void CreateEmpty()
			{
				JukeboxLogger.Log.LogInfo((object)("No saved playlist found at " + JukeboxPlaylist.CurrentPath + ". Creating default..."));
				CreateDefaultPlaylist();
			}
		}

		public void Remove()
		{
			((Playlist)playlist).Remove(((Playlist)playlist).selected);
			if (((Playlist)playlist).selected >= ((Playlist)playlist).ids.Count)
			{
				Select(((Playlist)playlist).Count - 1);
			}
			((DirectoryTreeBrowser<JukeboxSong>)this).Rebuild(false);
		}

		public void ClearPlaylist()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			((Playlist)playlist).ids.Clear();
			AssetReferenceSoundtrackSong val = browser.rootFolder[0];
			playlist.Add(new SongIdentifier(((AssetReference)val).AssetGUID, (IdentifierType)0));
			((DirectoryTreeBrowser<JukeboxSong>)this).Rebuild(true);
		}

		public void NextPage()
		{
			base.SetPage((base.currentPage !

plugins/Jukebox/Jukebox.ThornExtensions.dll

Decompiled 2 weeks ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using HarmonyLib;
using Jukebox.Components.HUD;
using Jukebox.ThornExtensions.Modules;
using Jukebox.UI.Subtitles;
using Microsoft.CodeAnalysis;
using ThornClient.Core;
using ThornClient.HUD;
using ThornClient.Managers;
using ThornClient.System;
using UnityEngine;
using UnityEngine.AddressableAssets;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 Jukebox.ThornExtensions
{
	[BepInProcess("ULTRAKILL.exe")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("dev.flazhik.jukebox.thorn-extensions", "Jukebox Thorn Extensions", "2.3.2")]
	public class JukeboxThornExtensionPlugin : BaseUnityPlugin
	{
		private static Harmony _harmony;

		private static bool ThornPresent => Chainloader.PluginInfos.ContainsKey("com.github.end-4.thornClient");

		private void Awake()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			if (ThornPresent)
			{
				((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
				_harmony = new Harmony("dev.flazhik.jukebox.thorn-extensions");
				_harmony.PatchAll();
			}
		}

		private void Start()
		{
			if (ThornPresent)
			{
				NowPlayingModule.Load();
				PlaybackTimeModule.Load();
				SubtitlesModule.Load();
			}
		}
	}
	internal static class PluginInfo
	{
		public const string Guid = "dev.flazhik.jukebox.thorn-extensions";

		public const string Name = "Jukebox Thorn Extensions";

		public const string Version = "2.3.2";
	}
	internal static class ThornPluginInfo
	{
		public const string Guid = "com.github.end-4.thornClient";
	}
}
namespace Jukebox.ThornExtensions.Patches
{
	[HarmonyPatch(typeof(HudModule))]
	public static class HudModulePatch
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(HudModule), "Reparent")]
		public static void HudModule_Reparent_Postfix(HudModule __instance)
		{
			if (!(__instance is NowPlayingModule nowPlayingModule))
			{
				if (!(__instance is PlaybackTimeModule playbackTimeModule))
				{
					if (__instance is SubtitlesModule subtitlesModule)
					{
						subtitlesModule.hud.RestoreFontMaterial();
					}
				}
				else if (!((Object)(object)playbackTimeModule.hud == (Object)null))
				{
					((ThornHud)playbackTimeModule.hud).RestoreFontMaterials();
				}
			}
			else if (!((Object)(object)nowPlayingModule.hud == (Object)null))
			{
				((ThornHud)nowPlayingModule.hud).RestoreFontMaterials();
			}
		}
	}
}
namespace Jukebox.ThornExtensions.Modules
{
	public sealed class NowPlayingModule : FramedHudModule
	{
		private static GameObject _nowPlayingHudPrefab;

		private static Sprite _icon;

		public NowPlayingThornHud hud;

		public override Sprite Icon => _icon;

		public override string[] Tags => new string[4] { "music", "cgme", "player", "nowplaying" };

		public override bool HideBackgroundWhenEmpty => true;

		public NowPlayingModule()
			: base("dev.flazhik.jukebox.nowPlaying", "Now Playing", "Shows the currently playing track")
		{
		}

		public static void Load()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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)
			_nowPlayingHudPrefab = Addressables.LoadAssetAsync<GameObject>((object)"Assets/Jukebox/UI/NowPlayingHudThorn.prefab").WaitForCompletion();
			_icon = Addressables.LoadAssetAsync<Sprite>((object)"Assets/Jukebox/Textures/ThornIcons/note.png").WaitForCompletion();
		}

		protected override GameObject CreateContentObject()
		{
			GameObject val = Object.Instantiate<GameObject>(_nowPlayingHudPrefab);
			hud = val.GetComponent<NowPlayingThornHud>();
			return val;
		}
	}
	public sealed class PlaybackTimeModule : FramedHudModule
	{
		private static GameObject _nowPlayingHudPrefab;

		public PlaybackTimeThornHud hud;

		public override Sprite Icon => AssetManager.Get<Sprite>(ClickGUI.BundleKey, "clock");

		public override string[] Tags => new string[6] { "music", "cgme", "player", "nowplaying", "time", "playback" };

		public override bool HideBackgroundWhenEmpty => true;

		public PlaybackTimeModule()
			: base("dev.flazhik.jukebox.playbackTime", "Playback Time", "Displays the elapsed playback time and track duration")
		{
		}

		public static void Load()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			_nowPlayingHudPrefab = Addressables.LoadAssetAsync<GameObject>((object)"Assets/Jukebox/UI/PlaybackTimeHudThorn.prefab").WaitForCompletion();
		}

		protected override GameObject CreateContentObject()
		{
			GameObject val = Object.Instantiate<GameObject>(_nowPlayingHudPrefab);
			hud = val.GetComponent<PlaybackTimeThornHud>();
			return val;
		}
	}
	public sealed class SubtitlesModule : FramedHudModule
	{
		private static GameObject _subtitlesPrefab;

		private static Sprite _icon;

		public SubtitlesPanel hud;

		public override Sprite Icon => _icon;

		public override string[] Tags => new string[5] { "music", "cgme", "player", "subtitles", "subs" };

		public override bool HideBackgroundWhenEmpty => true;

		public SubtitlesModule()
			: base("dev.flazhik.jukebox.subtitles", "Subtitles", "Displays subtitles for Cybergrind songs")
		{
		}

		public static void Load()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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)
			_subtitlesPrefab = Addressables.LoadAssetAsync<GameObject>((object)"Assets/Jukebox/UI/SubtitlesPanelThorn.prefab").WaitForCompletion();
			_icon = Addressables.LoadAssetAsync<Sprite>((object)"Assets/Jukebox/Textures/ThornIcons/subtitles.png").WaitForCompletion();
		}

		protected override GameObject CreateContentObject()
		{
			GameObject val = Object.Instantiate<GameObject>(_subtitlesPrefab);
			hud = val.GetComponent<SubtitlesPanel>();
			return val;
		}
	}
}

plugins/Jukebox/JukeboxCore.dll

Decompiled 2 weeks 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.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using JetBrains.Annotations;
using JukeboxCore.Components;
using JukeboxCore.Events;
using JukeboxCore.Exceptions;
using JukeboxCore.Logging;
using JukeboxCore.Songs;
using JukeboxCore.Songs.Models;
using JukeboxCore.Subtitles;
using JukeboxCore.Themes;
using JukeboxCore.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using SubtitlesParser.Classes;
using SubtitlesParser.Classes.Parsers;
using TagLib;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Networking;
using UnityEngine.ResourceManagement.AsyncOperations;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("JukeboxCore")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("JukeboxCore")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("AB57436D-F3D9-454E-8F64-33F23AFD6690")]
[assembly: AssemblyFileVersion("2.3.2.0")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("2.3.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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;
		}
	}
	internal static class IsExternalInit
	{
	}
}
namespace JukeboxCore
{
	[ConfigureSingleton(/*Could not decode attribute arguments.*/)]
	public class MainThreadDispatcher : MonoSingleton<MainThreadDispatcher>
	{
		private static readonly ConcurrentQueue<Action> ExecutionQueue = new ConcurrentQueue<Action>();

		private void Update()
		{
			Action result;
			while (ExecutionQueue.TryDequeue(out result))
			{
				result();
			}
		}

		public void Enqueue(Action action)
		{
			ExecutionQueue.Enqueue(action);
		}

		public void RunAsync(Func<Task> task, Action<Exception> onError = null)
		{
			Task.Run(task).ContinueWith(delegate(Task t)
			{
				if (t.IsFaulted)
				{
					if (onError != null)
					{
						Enqueue(delegate
						{
							onError(t.Exception);
						});
					}
					else
					{
						Enqueue(delegate
						{
							Debug.LogException((Exception)t.Exception);
						});
					}
				}
			});
		}
	}
	public struct UnscaledTimeSince
	{
		private float time;

		public static implicit operator float(UnscaledTimeSince ts)
		{
			return Time.unscaledTime - ts.time;
		}

		public static implicit operator UnscaledTimeSince(float ts)
		{
			return new UnscaledTimeSince
			{
				time = Time.unscaledTime - ts
			};
		}
	}
}
namespace JukeboxCore.Utils
{
	public static class CompositeSongsUtils
	{
		private const string IntroPostfix = "intro";

		private const string LoopPostfix = "loop";

		private const string CalmPostfix = "calm";

		private const string CalmLoopPostfix = "calmloop";

		private const string CalmIntroPostfix = "calmintro";

		private static readonly List<string> SpecialPostfixes = new List<string>(5) { "calmintro", "calmloop", "loop", "calm", "intro" };

		public static CompositeSongFiles GetTrackSegments(FileInfo baseTrack, IEnumerable<FileInfo> siblings = null)
		{
			string? fileNameWithoutExtension = Path.GetFileNameWithoutExtension(baseTrack.Name);
			string baseName = GetBaseName(fileNameWithoutExtension);
			bool num = fileNameWithoutExtension.Equals(baseName);
			bool flag = baseTrack.Exists && HasValidExtenstion(baseTrack);
			if (!num)
			{
				if (!flag)
				{
					return CompositeSongFiles.Empty;
				}
				return new CompositeSongFiles
				{
					Main = baseTrack
				};
			}
			List<FileInfo> list = (siblings ?? ListSiblings(baseTrack, baseName)).ToList();
			FileInfo fileInfo = (flag ? baseTrack : FindComponent(list, baseName, "loop", baseTrack.Extension));
			if (fileInfo == null)
			{
				return CompositeSongFiles.Empty;
			}
			return new CompositeSongFiles
			{
				Main = fileInfo,
				Intro = FindComponent(list, baseName, "intro", baseTrack.Extension),
				CalmIntro = FindComponent(list, baseName, "calmintro", baseTrack.Extension),
				Calm = (FindComponent(list, baseName, "calm", baseTrack.Extension) ?? FindComponent(list, baseName, "calmloop", baseTrack.Extension))
			};
		}

		public static bool IsPlayableTrack(FileInfo id)
		{
			return GetTrackSegments(id).IsPlayable;
		}

		public static FileInfo WithoutPostfix(FileInfo path)
		{
			string fileNameWithoutExtension = PathsUtils.GetFileNameWithoutExtension(path.Name);
			string baseName = GetBaseName(fileNameWithoutExtension);
			if (!baseName.Equals(fileNameWithoutExtension))
			{
				return new FileInfo(Path.Combine(path.DirectoryName ?? string.Empty, baseName + path.Extension));
			}
			return path;
		}

		public static bool HasValidExtenstion(FileInfo path)
		{
			return CustomMusicFileBrowser.extensionTypeDict.Keys.Any((string extension) => path.Extension.ToLower().Equals(extension));
		}

		public static string GetBaseName(string fileName)
		{
			if (string.IsNullOrEmpty(fileName))
			{
				return fileName;
			}
			using (IEnumerator<string> enumerator = (from postfix in SpecialPostfixes
				select "_" + postfix into suffix
				where fileName.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)
				select suffix).GetEnumerator())
			{
				if (enumerator.MoveNext())
				{
					string current = enumerator.Current;
					string text = fileName;
					int length = current.Length;
					return text.Substring(0, text.Length - length);
				}
			}
			return fileName;
		}

		private static List<FileInfo> ListSiblings(FileInfo requested, string baseName)
		{
			DirectoryInfo directory = requested.Directory;
			if (directory == null || !directory.Exists)
			{
				return new List<FileInfo>();
			}
			return (from file in directory.GetFiles().Where(HasValidExtenstion)
				where GetBaseName(PathsUtils.GetFileNameWithoutExtension(file.Name)).Equals(baseName)
				select file).ToList();
		}

		private static FileInfo FindComponent(IReadOnlyList<FileInfo> group, string baseName, string postfix, string preferredExtension)
		{
			string target = baseName + "_" + postfix;
			return (from file in @group
				where PathsUtils.GetFileNameWithoutExtension(file.Name).Equals(target)
				orderby (!file.Extension.Equals(preferredExtension, StringComparison.OrdinalIgnoreCase)) ? 1 : 0
				select file).ThenBy<FileInfo, string>((FileInfo file) => file.Name, StringComparer.Ordinal).FirstOrDefault();
		}
	}
	public static class ConfigUtils
	{
		public static readonly ConfigFile BepInExConfig = new ConfigFile(PathsUtils.BepInExConfigPath, true);
	}
	public static class GenericUtils
	{
		public static string FirstNonEmpty(params string[] options)
		{
			return options.FirstOrDefault((string option) => !string.IsNullOrEmpty(option));
		}
	}
	public sealed class JsonStore<T> where T : class, new()
	{
		private readonly object @lock = new object();

		private readonly string path;

		private bool dirty;

		public T Value { get; private set; }

		public bool Loaded { get; private set; }

		public JsonStore(string path)
		{
			this.path = path ?? throw new ArgumentNullException("path");
			Value = new T();
			Load();
		}

		public void Load()
		{
			lock (@lock)
			{
				Loaded = false;
				if (!File.Exists(path))
				{
					return;
				}
				try
				{
					string text = File.ReadAllText(path);
					if (!string.IsNullOrWhiteSpace(text))
					{
						T val = JsonConvert.DeserializeObject<T>(text);
						if (val != null)
						{
							Value = val;
							Loaded = true;
						}
					}
				}
				catch (Exception)
				{
				}
			}
		}

		public void SaveIfDirty()
		{
			lock (@lock)
			{
				if (dirty)
				{
					Write();
				}
			}
		}

		public void Save()
		{
			lock (@lock)
			{
				Write();
			}
		}

		public void MarkDirty()
		{
			lock (@lock)
			{
				dirty = true;
			}
		}

		public void Mutate(Action<T> change)
		{
			if (change == null)
			{
				throw new ArgumentNullException("change");
			}
			lock (@lock)
			{
				change(Value);
				dirty = true;
			}
		}

		private void Write()
		{
			string directoryName = Path.GetDirectoryName(path);
			if (!string.IsNullOrEmpty(directoryName))
			{
				Directory.CreateDirectory(directoryName);
			}
			File.WriteAllText(path, JsonConvert.SerializeObject((object)Value));
			dirty = false;
		}
	}
	public static class NetworkUtils
	{
		private const int RetryAttempts = 3;

		private static readonly HttpClient Client;

		static NetworkUtils()
		{
			Client = new HttpClient();
			Client.Timeout = TimeSpan.FromSeconds(5.0);
			Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
			Client.DefaultRequestHeaders.UserAgent.TryParseAdd("com.google.android.youtube/17.36.4 (Linux; U; Android 12; GB) gzip");
		}

		public static async Task<string> GetRaw(string url)
		{
			for (int i = 0; i < 3; i++)
			{
				try
				{
					string text = await (await Client.GetAsync(url)).Content.ReadAsStringAsync();
					if (text == null)
					{
						continue;
					}
					return text;
				}
				catch (Exception)
				{
					if (i == 2)
					{
						throw;
					}
				}
			}
			throw new HttpRequestException();
		}

		public static async Task<Texture2D> DownloadImage(string url)
		{
			UnityWebRequest request = UnityWebRequestTexture.GetTexture(url);
			try
			{
				await request.SendWebRequest();
				if (request.error != null)
				{
					throw new ImageDownloadFailedException(url, request.error);
				}
				return DownloadHandlerTexture.GetContent(request);
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}
	}
	public static class ObjectExtensions
	{
		public static bool IsOneOf(this object v, params object[] options)
		{
			return options.Any((object o) => o.Equals(v));
		}
	}
	public static class PathsUtils
	{
		private static readonly List<string> ReservedWords = new List<string>(25)
		{
			"CON", "PRN", "AUX", "CLOCK$", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4",
			"COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4",
			"LPT5", "LPT6", "LPT7", "LPT8", "LPT9"
		};

		public static readonly string ApplicationPath = Path.Combine(Directory.GetParent(Application.dataPath)?.FullName);

		public static readonly string AssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

		public static readonly string CybergrindPath = Path.Combine(ApplicationPath, "Cybergrind");

		public static readonly string MusicPath = Path.Combine(CybergrindPath, "Music");

		public static readonly string LogsPath = Path.Combine(AssemblyPath, "Logs");

		public static readonly string EffectsPath = Path.Combine(CybergrindPath, "Effects");

		public static readonly string BepInExConfigPath = Path.Combine(Paths.ConfigPath, "Jukebox", "config.cfg");

		public static string CoerceValidFileName(string filename)
		{
			string text = Regex.Escape(new string(Path.GetInvalidFileNameChars()));
			string pattern = "[" + text + "]+";
			string seed = Regex.Replace(filename, pattern, "_");
			return ReservedWords.Select((string reservedWord) => "^" + reservedWord + "\\.").Aggregate(seed, (string current, string reservedWordPattern) => Regex.Replace(current, reservedWordPattern, "_reservedWord_.", RegexOptions.IgnoreCase));
		}

		public static string GetFileNameWithoutExtension(string path)
		{
			if (string.IsNullOrEmpty(path) || path.IndexOf('\0') >= 0)
			{
				return string.Empty;
			}
			int num = Math.Max(path.LastIndexOf('/'), path.LastIndexOf('\\')) + 1;
			int num2 = path.LastIndexOf('.');
			if (num2 > num)
			{
				return path.Substring(num, num2 - num);
			}
			int num3 = num;
			return path.Substring(num3, path.Length - num3);
		}
	}
	public abstract class ReflectionUtils
	{
		private const BindingFlags PrivateFields = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private const BindingFlags StaticFields = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		public static T GetPrivate<T>(object instance, Type classType, string field)
		{
			FieldInfo field2 = classType.GetField(field, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			return (T)((field2 != null) ? field2.GetValue(instance) : null);
		}

		public static void SetPrivate<T, TV>(T instance, Type classType, string field, TV value)
		{
			FieldInfo field2 = classType.GetField(field, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetField);
			if (field2 != null)
			{
				field2.SetValue(instance, value);
			}
		}

		public static List<FieldInfo> GetAssembliesStaticFields(params Assembly[] assemblies)
		{
			return (from field in assemblies.SelectMany((Assembly assembly) => assembly.GetTypes()).SelectMany((Type type) => type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
				where !field.IsInitOnly && !field.FieldType.IsArray && typeof(Object).IsAssignableFrom(field.FieldType)
				select field).ToList();
		}
	}
	public class UnityWebRequestAwaiter : INotifyCompletion
	{
		private readonly UnityWebRequestAsyncOperation asyncOp;

		private Action continuation;

		public bool IsCompleted => ((AsyncOperation)asyncOp).isDone;

		public UnityWebRequestAwaiter(UnityWebRequestAsyncOperation asyncOp)
		{
			this.asyncOp = asyncOp;
			((AsyncOperation)asyncOp).completed += OnRequestCompleted;
		}

		public void OnCompleted(Action action)
		{
			continuation = action;
		}

		public void GetResult()
		{
		}

		private void OnRequestCompleted(AsyncOperation obj)
		{
			continuation();
		}
	}
	public static class UnityWebRequestAwaiterExtensions
	{
		public static UnityWebRequestAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOp)
		{
			return new UnityWebRequestAwaiter(asyncOp);
		}
	}
}
namespace JukeboxCore.Themes
{
	[CreateAssetMenu(menuName = "Jukebox/Calm theme")]
	public class CalmThemeEntry : ScriptableObject
	{
		[SerializeField]
		public AssetReferenceSoundtrackSong reference;

		[SerializeField]
		public List<AssetReferenceT<AudioClip>> calmVariationClips;
	}
	[CreateAssetMenu(menuName = "Jukebox/Calm Themes Data")]
	public class SoundtrackCalmThemes : ScriptableObject
	{
		[SerializeField]
		public CalmThemeEntry[] calmClips;

		public List<AssetReferenceT<AudioClip>> FindCalmClipsFor(string guid)
		{
			CalmThemeEntry calmThemeEntry = calmClips.FirstOrDefault((CalmThemeEntry variation) => ((AssetReference)variation.reference).AssetGUID == guid);
			if (!((Object)(object)calmThemeEntry != (Object)null))
			{
				return null;
			}
			return calmThemeEntry.calmVariationClips;
		}
	}
}
namespace JukeboxCore.Subtitles
{
	public static class SubtitleFiles
	{
		private const string Extension = ".srt";

		private const string IntroSuffix = "_intro";

		public static FileInfo MainSidecar(FileInfo track)
		{
			return Sidecar(track, string.Empty);
		}

		public static FileInfo IntroSidecar(FileInfo track)
		{
			return Sidecar(track, "_intro");
		}

		private static FileInfo Sidecar(FileInfo track, string suffix)
		{
			if (track == null)
			{
				return null;
			}
			string baseName = CompositeSongsUtils.GetBaseName(PathsUtils.GetFileNameWithoutExtension(track.Name));
			return new FileInfo(Path.Combine(track.DirectoryName ?? string.Empty, baseName + suffix + ".srt"));
		}
	}
	public readonly struct SubtitleLine
	{
		public float Start { get; }

		public float End { get; }

		public string Text { get; }

		public SubtitleLine(float start, float end, string text)
		{
			Start = start;
			End = end;
			Text = text;
		}
	}
	public static class SubtitleParser
	{
		public static SubtitleTrack FromFile(FileInfo file)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (file == null || !file.Exists)
			{
				return SubtitleTrack.Empty;
			}
			try
			{
				using FileStream fileStream = File.OpenRead(file.FullName);
				return ToSubtitlesTrack(new SrtParser().ParseStream((Stream)fileStream, Encoding.UTF8));
			}
			catch (Exception arg)
			{
				JukeboxLogger.Log.LogError((object)$"Couldn't read subtitles from {file.FullName}\n{arg}");
				return SubtitleTrack.Empty;
			}
		}

		private static SubtitleTrack ToSubtitlesTrack(List<SubtitleItem> items)
		{
			List<SubtitleLine> list = new List<SubtitleLine>();
			foreach (SubtitleItem item in items)
			{
				string text = string.Join("\n", item.Lines);
				if (text.Length != 0)
				{
					list.Add(new SubtitleLine((float)item.StartTime / 1000f, (float)item.EndTime / 1000f, text));
				}
			}
			if (list.Count == 0)
			{
				return SubtitleTrack.Empty;
			}
			return new SubtitleTrack(list);
		}
	}
	public sealed class SubtitleTrack(List<SubtitleLine> lines)
	{
		public static readonly SubtitleTrack Empty = new SubtitleTrack(new List<SubtitleLine>());

		public IReadOnlyList<SubtitleLine> Lines => lines;

		private bool IsEmpty => lines.Count == 0;

		public int IndexAt(float time, int previous)
		{
			if (IsEmpty)
			{
				return -1;
			}
			int i = previous;
			if (i >= lines.Count)
			{
				i = lines.Count - 1;
			}
			if (i < 0)
			{
				i = 0;
			}
			while (i > 0 && lines[i].Start > time)
			{
				i--;
			}
			for (; i + 1 < lines.Count && lines[i + 1].Start <= time; i++)
			{
			}
			if (!(time >= lines[i].Start) || !(time <= lines[i].End))
			{
				return -1;
			}
			return i;
		}
	}
}
namespace JukeboxCore.Songs
{
	public sealed class JukeboxCustomSongSession : JukeboxSongSession
	{
		private readonly List<UnityWebRequest> requests;

		private readonly List<AudioClip> retired;

		private List<DownloadHandlerAudioClip> handles;

		private int fullyLoadedIndex;

		public JukeboxCustomSongSession(JukeboxSong song, CompositeSongFiles files)
		{
			<files>P = files;
			requests = new List<UnityWebRequest>();
			retired = new List<AudioClip>();
			fullyLoadedIndex = -1;
			base..ctor(song);
		}

		~JukeboxCustomSongSession()
		{
			requests.ForEach(delegate(UnityWebRequest request)
			{
				request.Dispose();
			});
			requests.Clear();
		}

		public IEnumerator LoadFullyFor(int index, Action<AudioClip, AudioClip> onLoaded)
		{
			FileInfo fileInfo = ((index == 0) ? <files>P.Intro : <files>P.Main);
			FileInfo calmFile = ((index == 0) ? <files>P.CalmIntro : <files>P.Calm);
			AudioClip battleClip = null;
			AudioClip calmClip = null;
			if (fileInfo != null)
			{
				yield return Download(fileInfo, stream: false, delegate(AudioClip clip)
				{
					battleClip = clip;
				});
			}
			if (calmFile != null)
			{
				yield return Download(calmFile, stream: false, delegate(AudioClip clip)
				{
					calmClip = clip;
				});
			}
			ReplaceClips(index, battleClip, calmClip);
			fullyLoadedIndex = index;
			onLoaded(battleClip, calmClip);
		}

		public bool IsFullyLoaded(int index)
		{
			return fullyLoadedIndex == index;
		}

		protected override IEnumerator LoadInternal(IEnumerator callback)
		{
			handles = new List<DownloadHandlerAudioClip>();
			if (<files>P.HasIntro)
			{
				yield return Download(<files>P.Intro, stream: true, delegate(AudioClip c)
				{
					base.IntroClip = c;
				});
			}
			yield return Download(<files>P.Main, stream: true, delegate(AudioClip c)
			{
				base.Clips = new List<AudioClip>(1) { c };
			});
			if (<files>P.CalmIntro != null)
			{
				yield return Download(<files>P.CalmIntro, stream: true, delegate(AudioClip c)
				{
					base.CalmIntroClip = c;
				});
			}
			if (<files>P.Calm != null)
			{
				yield return Download(<files>P.Calm, stream: true, delegate(AudioClip c)
				{
					base.CalmClips = new List<AudioClip>(1) { c };
				});
			}
			yield return callback;
		}

		private IEnumerator Download(FileSystemInfo path, bool stream, Action<AudioClip> downloadCallback)
		{
			UnityWebRequest audioClip = UnityWebRequestMultimedia.GetAudioClip(new Uri(path.FullName), CustomMusicFileBrowser.extensionTypeDict[path.Extension.ToLower()]);
			requests.Add(audioClip);
			DownloadHandler downloadHandler = audioClip.downloadHandler;
			DownloadHandlerAudioClip handle = (DownloadHandlerAudioClip)(object)((downloadHandler is DownloadHandlerAudioClip) ? downloadHandler : null);
			if (handle != null)
			{
				if (stream)
				{
					handles.Add(handle);
				}
				handle.streamAudio = stream;
				audioClip.SendWebRequest();
				yield return audioClip;
				downloadCallback(handle.audioClip);
			}
		}

		protected override void DisposeInternal()
		{
			DisposeOfRequests();
			DisposeOfHandlers();
			handles = null;
			DestroyClipIfPresent(base.IntroClip);
			DestroyClipIfPresent(base.CalmIntroClip);
			DestroyClipsIfPresent(base.Clips);
			DestroyClipsIfPresent(base.CalmClips);
			foreach (AudioClip item in retired)
			{
				DestroyClipIfPresent(item);
			}
			retired.Clear();
		}

		private void ReplaceClips(int clipIndex, AudioClip battleClip, AudioClip calmClip)
		{
			if (clipIndex == 0)
			{
				Retire(base.IntroClip);
				Retire(base.CalmIntroClip);
				base.IntroClip = battleClip;
				base.CalmIntroClip = calmClip;
			}
			else
			{
				RetireAll(base.Clips);
				RetireAll(base.CalmClips);
				base.Clips = (((Object)(object)battleClip == (Object)null) ? null : new List<AudioClip>(1) { battleClip });
				base.CalmClips = (((Object)(object)calmClip == (Object)null) ? null : new List<AudioClip>(1) { calmClip });
			}
		}

		private void Retire(AudioClip clip)
		{
			if ((Object)(object)clip != (Object)null)
			{
				retired.Add(clip);
			}
		}

		private void RetireAll(List<AudioClip> clips)
		{
			if (clips == null)
			{
				return;
			}
			foreach (AudioClip clip in clips)
			{
				Retire(clip);
			}
		}

		private void DisposeOfRequests()
		{
			requests.ForEach(delegate(UnityWebRequest request)
			{
				request.Dispose();
			});
			requests.Clear();
		}

		private void DisposeOfHandlers()
		{
			handles.ForEach(delegate(DownloadHandlerAudioClip request)
			{
				((DownloadHandler)request).Dispose();
			});
		}

		private static void DestroyClipsIfPresent(List<AudioClip> clips)
		{
			if (clips == null)
			{
				return;
			}
			foreach (AudioClip clip in clips)
			{
				DestroyClipIfPresent(clip);
			}
		}

		private static void DestroyClipIfPresent(AudioClip clip)
		{
			if ((Object)(object)clip != (Object)null)
			{
				Object.Destroy((Object)(object)clip);
			}
		}
	}
	public abstract class JukeboxSongSession(JukeboxSong song) : IDisposable
	{
		private bool disposed;

		public JukeboxSong Song { get; } = song;

		public AudioClip IntroClip { get; protected set; }

		public AudioClip CalmIntroClip { get; protected set; }

		public List<AudioClip> Clips { get; protected set; }

		public List<AudioClip> CalmClips { get; protected set; }

		public int MaxClipsIfNotRepeating { get; protected set; }

		public SubtitleTrack Subtitles { get; } = (song.Metadata is JukeboxCustomSongMetadata jukeboxCustomSongMetadata) ? jukeboxCustomSongMetadata.MainSubtitles : SubtitleTrack.Empty;

		public SubtitleTrack IntroSubtitles { get; } = (song.Metadata is JukeboxCustomSongMetadata jukeboxCustomSongMetadata2) ? jukeboxCustomSongMetadata2.IntroSubtitles : SubtitleTrack.Empty;

		public void Dispose()
		{
			if (!disposed)
			{
				disposed = true;
				DisposeInternal();
				IntroClip = null;
				CalmIntroClip = null;
				Clips = null;
				CalmClips = null;
				MaxClipsIfNotRepeating = -1;
			}
		}

		public IEnumerator Load(IEnumerator callback)
		{
			try
			{
				yield return LoadInternal(callback);
			}
			finally
			{
				Dispose();
			}
		}

		protected abstract IEnumerator LoadInternal(IEnumerator callback);

		protected abstract void DisposeInternal();
	}
	public static class JukeboxSongSessionFactory
	{
		public static JukeboxSongSession Create(JukeboxSong song)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			IdentifierType type = song.Id.type;
			if ((int)type != 0)
			{
				if ((int)type == 1)
				{
					return CustomSoundtrackSession(song);
				}
				throw new ArgumentOutOfRangeException("song");
			}
			return new JukeboxSoundtrackSongSession(song);
		}

		private static JukeboxSongSession CustomSoundtrackSession(JukeboxSong song)
		{
			if (!(song.Metadata is JukeboxCustomSongMetadata { Files: { IsPlayable: not false } } jukeboxCustomSongMetadata))
			{
				return null;
			}
			return new JukeboxCustomSongSession(song, jukeboxCustomSongMetadata.Files);
		}
	}
	public sealed class JukeboxSoundtrackSongSession : JukeboxSongSession
	{
		private readonly List<AsyncOperationHandle<AudioClip>> calmClipsHandles = new List<AsyncOperationHandle<AudioClip>>();

		private AsyncOperationHandle<SoundtrackSong> handle;

		public JukeboxSoundtrackSongSession(JukeboxSong song)
			: base(song)
		{
		}

		protected override IEnumerator LoadInternal(IEnumerator callback)
		{
			handle = Addressables.LoadAssetAsync<SoundtrackSong>((object)base.Song.Id.path);
			yield return handle;
			SoundtrackSong addressable = handle.Result;
			base.IntroClip = addressable.introClip;
			base.Clips = addressable.clips;
			List<AssetReferenceT<AudioClip>> list = MonoSingleton<JukeboxStaticData>.Instance?.calmThemes.FindCalmClipsFor(base.Song.Id.path);
			if (list != null)
			{
				base.CalmClips = new List<AudioClip>();
				foreach (AsyncOperationHandle<AudioClip> calmClipHandle in list.Select((AssetReferenceT<AudioClip> clip) => Addressables.LoadAssetAsync<AudioClip>((object)((AssetReference)clip).AssetGUID)))
				{
					calmClipsHandles.Add(calmClipHandle);
					yield return calmClipHandle;
					base.CalmClips.Add(calmClipHandle.Result);
				}
			}
			base.MaxClipsIfNotRepeating = addressable.maxClipsIfNotRepeating;
			yield return callback;
		}

		protected override void DisposeInternal()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			Addressables.Release<SoundtrackSong>(handle);
			foreach (AsyncOperationHandle<AudioClip> calmClipsHandle in calmClipsHandles)
			{
				Addressables.Release<AudioClip>(calmClipsHandle);
			}
			calmClipsHandles.Clear();
			handle = default(AsyncOperationHandle<SoundtrackSong>);
		}
	}
}
namespace JukeboxCore.Songs.Models
{
	public class CompositeProperties
	{
		public bool GotIntroAndLoop { get; init; }

		public bool GotCalmIntro { get; init; }

		public bool GotCalmTheme { get; init; }
	}
	public sealed class CompositeSongFiles
	{
		public static readonly CompositeSongFiles Empty = new CompositeSongFiles();

		public FileInfo Main { get; init; }

		public FileInfo Intro { get; init; }

		public FileInfo CalmIntro { get; init; }

		public FileInfo Calm { get; init; }

		public bool IsPlayable => Main != null;

		public bool HasIntro => Intro != null;
	}
	public sealed class JukeboxCustomSongMetadata : JukeboxSongMetadata
	{
		private static Sprite defaultIcon;

		[CompilerGenerated]
		private SubtitleTrack <MainSubtitles>k__BackingField;

		[CompilerGenerated]
		private SubtitleTrack <IntroSubtitles>k__BackingField;

		private static Sprite DefaultIcon => defaultIcon;

		public CompositeSongFiles Files { get; }

		public SubtitleTrack MainSubtitles => <MainSubtitles>k__BackingField ?? (<MainSubtitles>k__BackingField = SubtitleParser.FromFile(SubtitleFiles.MainSidecar(Files.Main)));

		public SubtitleTrack IntroSubtitles => <IntroSubtitles>k__BackingField ?? (<IntroSubtitles>k__BackingField = SubtitleParser.FromFile(SubtitleFiles.IntroSidecar(Files.Intro)));

		private JukeboxCustomSongMetadata(Sprite icon, string title, CompositeSongFiles files, string artist = null)
			: base(icon, title, artist)
		{
			Files = files;
		}

		public override CompositeProperties GetCompositeProperties()
		{
			return new CompositeProperties
			{
				GotIntroAndLoop = Files.HasIntro,
				GotCalmIntro = (Files.CalmIntro != null),
				GotCalmTheme = (Files.Calm != null)
			};
		}

		public static void Load()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			defaultIcon = Addressables.LoadAssetAsync<Sprite>((object)"Assets/Jukebox/Textures/music_note.png").WaitForCompletion();
		}

		public static JukeboxCustomSongMetadata From(FileInfo src)
		{
			try
			{
				CompositeSongFiles trackSegments = CompositeSongsUtils.GetTrackSegments(src);
				string fileNameWithoutExtension = PathsUtils.GetFileNameWithoutExtension(src.Name);
				if (!trackSegments.IsPlayable)
				{
					return new JukeboxCustomSongMetadata(DefaultIcon, fileNameWithoutExtension, trackSegments);
				}
				Tag tag = File.Create(trackSegments.Main.FullName).Tag;
				return new JukeboxCustomSongMetadata(LoadCover(tag) ?? DefaultIcon, (!string.IsNullOrEmpty(tag.Title)) ? tag.Title : fileNameWithoutExtension, trackSegments, GenericUtils.FirstNonEmpty(tag.FirstPerformer, tag.FirstComposer, tag.FirstAlbumArtist));
			}
			catch (Exception arg)
			{
				JukeboxLogger.Log.LogError((object)$"Couldn't retrieve metadata for {src.Name}\n{arg}");
				return null;
			}
		}

		private static Sprite LoadCover(Tag tag)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			if (tag.Pictures.Length == 0)
			{
				return null;
			}
			using MemoryStream memoryStream = new MemoryStream(tag.Pictures[0].Data.Data);
			try
			{
				memoryStream.Seek(0L, SeekOrigin.Begin);
				Texture2D val = new Texture2D(2, 2);
				ImageConversion.LoadImage(val, memoryStream.ToArray());
				return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
			}
			catch (Exception)
			{
				return null;
			}
		}
	}
	public class JukeboxPlaylist : Playlist
	{
		public static string CurrentPath
		{
			get
			{
				string fullName = Playlist.directory.FullName;
				PrefsManager instance = MonoSingleton<PrefsManager>.Instance;
				return Path.Combine(fullName, ((instance != null) ? instance.GetStringLocal("jukebox.currentPlaylist", (string)null) : null) + ".json");
			}
		}

		public JukeboxPlaylist()
		{
		}

		public JukeboxPlaylist(IEnumerable<SongIdentifier> passedIds)
			: base(((Object)(object)MonoSingleton<PrefsManager>.Instance != (Object)null && MonoSingleton<PrefsManager>.Instance.GetBoolLocal("jukebox.preventDuplicateTracks", false)) ? passedIds.Distinct() : passedIds)
		{
		}

		public static string PathForPlaylist(string name)
		{
			return Path.Combine(Playlist.directory.FullName, name + ".json");
		}

		public void Add(SongIdentifier id)
		{
			if (!MonoSingleton<PrefsManager>.Instance.GetBoolLocal("jukebox.preventDuplicateTracks", false) || !((Playlist)this).ids.Contains(id))
			{
				((Playlist)this).Add(id);
			}
		}

		public void RemoveDuplicates()
		{
			List<SongIdentifier> list = ((Playlist)this).ids.Distinct().ToList();
			((Playlist)this).ids.Clear();
			foreach (SongIdentifier item in list)
			{
				Add(item);
			}
		}

		public void RemoveUnplayable()
		{
			List<SongIdentifier> list = ((Playlist)this).ids.Where((SongIdentifier id) => (int)id.type == 0 || CompositeSongsUtils.IsPlayableTrack(new FileInfo(id.path))).ToList();
			((Playlist)this).ids.Clear();
			foreach (SongIdentifier item in list)
			{
				Add(item);
			}
		}
	}
	public sealed class JukeboxSong
	{
		private readonly Lazy<JukeboxSongMetadata> metadata;

		public SongIdentifier Id { get; }

		public JukeboxSongMetadata Metadata => metadata.Value;

		public bool IsCustom => (int)Id.type == 1;

		public string ArtistAndTrack
		{
			get
			{
				if (Metadata.Artist == null)
				{
					return Metadata.Title;
				}
				return Metadata.Artist + " — " + Metadata.Title;
			}
		}

		private JukeboxSong(SongIdentifier id, Func<JukeboxSongMetadata> metadataFactory)
		{
			Id = id;
			metadata = new Lazy<JukeboxSongMetadata>(metadataFactory);
		}

		public static JukeboxSong Create(SongIdentifier id)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Invalid comparison between Unknown and I4
			IdentifierType type = id.type;
			if ((int)type != 0)
			{
				if ((int)type == 1)
				{
					return new JukeboxSong(id, () => JukeboxCustomSongMetadata.From(new FileInfo(id.path)));
				}
				throw new ArgumentOutOfRangeException("id");
			}
			return new JukeboxSong(id, () => JukeboxSoundtrackSongMetadata.From(new AssetReferenceSoundtrackSong(id.path)));
		}
	}
	public abstract class JukeboxSongMetadata(Sprite icon, string title, string artist = null)
	{
		public Sprite Icon { get; } = icon;

		public string Title { get; } = title;

		public string Artist { get; } = artist;

		public abstract CompositeProperties GetCompositeProperties();
	}
	public sealed class JukeboxSoundtrackSongMetadata : JukeboxSongMetadata
	{
		private readonly string guid;

		private readonly SoundtrackSong song;

		private JukeboxSoundtrackSongMetadata(Sprite icon, string title, string guid, SoundtrackSong song, string artist)
			: base(icon, title, artist)
		{
			this.guid = guid;
			this.song = song;
		}

		public static JukeboxSoundtrackSongMetadata From(AssetReferenceSoundtrackSong reference)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			SoundtrackSong val = Addressables.LoadAssetAsync<SoundtrackSong>((object)((AssetReference)reference).AssetGUID).WaitForCompletion();
			return new JukeboxSoundtrackSongMetadata(val.icon, val.songName, ((AssetReference)reference).AssetGUID, val, val.extraLevelBit);
		}

		public override CompositeProperties GetCompositeProperties()
		{
			return new CompositeProperties
			{
				GotIntroAndLoop = ((Object)(object)song.introClip != (Object)null),
				GotCalmTheme = (MonoSingleton<JukeboxStaticData>.Instance?.calmThemes.FindCalmClipsFor(guid) != null)
			};
		}
	}
}
namespace JukeboxCore.Playback
{
	public interface IPlaybackController
	{
		PlaybackState State { get; }

		AudioSource Source { get; }

		event Action<ClipChangedArgs> OnNextAudioClip;

		event Action<IEnumerable<SongIdentifier>> OnOrderChange;

		event Action OnStop;

		event Action<string> OnSubtitleChanged;

		void ChangeTrack(int index);

		void RewindTo(float position);

		void Stop();
	}
	public sealed class PlaybackState
	{
		public JukeboxSong Song { get; set; }

		public int SongIndex { get; set; }

		public int ClipIndex { get; set; } = -1;

		public int ClipCount { get; set; }

		public float VolumeBoost { get; set; }

		public bool Stopped { get; set; }

		public SubtitleTrack Subtitles { get; set; }

		public string SubtitleLine { get; set; }
	}
}
namespace JukeboxCore.Models.Preferences
{
	[JsonObject(/*Could not decode attribute arguments.*/)]
	public class JukeboxPreferences
	{
		[JsonObject(/*Could not decode attribute arguments.*/)]
		public class PlayStartingFrom
		{
			[JsonProperty("path")]
			public string Path { get; private set; }

			[JsonProperty("type")]
			public IdentifierType Type { get; private set; }

			[JsonProperty("clip")]
			public int Clip { get; private set; }

			[JsonProperty("startFrom")]
			public float StartFrom { get; private set; }

			[JsonConstructor]
			protected PlayStartingFrom()
			{
			}

			public PlayStartingFrom(SongIdentifier id, int clipIndex, float startFrom)
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				Path = id.path;
				Type = id.type;
				Clip = clipIndex;
				StartFrom = startFrom;
			}
		}

		[JsonProperty("playStaringFrom")]
		public PlayStartingFrom playTrackStaringFrom;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public Dictionary<string, float> boostSettings = new Dictionary<string, float>();
	}
	[JsonObject(/*Could not decode attribute arguments.*/)]
	public class JukeboxThemesConfig
	{
		public class CalmThemeConfig
		{
			[JsonProperty(/*Could not decode attribute arguments.*/)]
			public Dictionary<EnemyType, int> specialEnemies = new Dictionary<EnemyType, int>
			{
				{
					(EnemyType)19,
					1
				},
				{
					(EnemyType)5,
					1
				},
				{
					(EnemyType)26,
					1
				},
				{
					(EnemyType)2,
					1
				},
				{
					(EnemyType)7,
					2
				}
			};

			[JsonProperty(/*Could not decode attribute arguments.*/)]
			public Dictionary<EnemyType, int> specialRadiantEnemies = new Dictionary<EnemyType, int>
			{
				{
					(EnemyType)19,
					0
				},
				{
					(EnemyType)5,
					0
				},
				{
					(EnemyType)26,
					0
				},
				{
					(EnemyType)2,
					0
				},
				{
					(EnemyType)7,
					1
				}
			};
		}

		[JsonProperty("calmThemeConfig")]
		public CalmThemeConfig calmTheme = new CalmThemeConfig();

		public JukeboxThemesConfig Equalize()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyValuePair<EnemyType, int> specialEnemy in calmTheme.specialEnemies)
			{
				calmTheme.specialRadiantEnemies.TryAdd(specialEnemy.Key, 0);
				if (calmTheme.specialRadiantEnemies[specialEnemy.Key] > specialEnemy.Value)
				{
					calmTheme.specialRadiantEnemies[specialEnemy.Key] = specialEnemy.Value;
				}
			}
			return this;
		}
	}
}
namespace JukeboxCore.Models.Downloader
{
	public class DownloadableEntityState
	{
		public float? progress;

		public TrackDownloadingState? state;

		[CanBeNull]
		public Sprite thumbnail;

		[CanBeNull]
		public string eta;
	}
	public class DownloadableSongMetadata(string id, string url, string thumbnailUrl, string title, string artist, [property: CanBeNull] string playlist)
	{
		public string Id { get; private set; } = id;

		public string Url { get; private set; } = url;

		public string ThumbnailUrl { get; private set; } = thumbnailUrl;

		public string Title { get; private set; } = title;

		public string Artist { get; private set; } = artist;

		[CanBeNull]
		public string Playlist { get; private set; } = playlist;
	}
	public enum TrackDownloadingState
	{
		None,
		Enqueued,
		PreProcessing,
		Downloading,
		PostProcessing,
		Success,
		Failed
	}
}
namespace JukeboxCore.Logging
{
	public class FileLogAppender : ILogListener, IDisposable
	{
		private static readonly TimeSpan FlushInterval = TimeSpan.FromSeconds(3.0);

		private static readonly DateTime LogsLifetimeThreshold = DateTime.UtcNow.AddDays(-3.0);

		private readonly object @lock = new object();

		private readonly ManualLogSource source;

		private readonly StreamWriter writer;

		private readonly Timer flushTimer;

		private bool disposed;

		public FileLogAppender(ManualLogSource source, string logsDirectory)
		{
			this.source = source;
			foreach (FileInfo item in from file in Directory.CreateDirectory(logsDirectory).GetFiles()
				where file.LastWriteTimeUtc < LogsLifetimeThreshold
				select file)
			{
				try
				{
					item.Delete();
				}
				catch (Exception)
				{
				}
			}
			string path = Path.Combine(logsDirectory, $"{source.SourceName}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log");
			writer = new StreamWriter(path, append: true, Encoding.UTF8);
			flushTimer = new Timer(delegate(object state)
			{
				((FileLogAppender)state).Flush();
			}, this, FlushInterval, FlushInterval);
		}

		public void LogEvent(object sender, LogEventArgs eventArgs)
		{
			if ((object)eventArgs.Source != source)
			{
				return;
			}
			lock (@lock)
			{
				if (!disposed)
				{
					writer.WriteLine($"[{DateTime.Now.ToUniversalTime():O}] {eventArgs}");
				}
			}
		}

		private void Flush()
		{
			lock (@lock)
			{
				if (!disposed)
				{
					writer.Flush();
				}
			}
		}

		public void Dispose()
		{
			flushTimer.Dispose();
			lock (@lock)
			{
				if (!disposed)
				{
					disposed = true;
					writer.Flush();
					writer.Dispose();
				}
			}
		}
	}
	public static class JukeboxLogger
	{
		private static FileLogAppender _appender;

		public static ManualLogSource Log { get; private set; }

		public static void Initialize(ManualLogSource source)
		{
			Log = source;
			_appender = new FileLogAppender(source, Path.Combine(PathsUtils.LogsPath, source.SourceName));
			Logger.Listeners.Add((ILogListener)(object)_appender);
		}

		public static void Shutdown()
		{
			if (_appender != null)
			{
				Logger.Listeners.Remove((ILogListener)(object)_appender);
				_appender.Dispose();
				_appender = null;
			}
		}
	}
}
namespace JukeboxCore.Exceptions
{
	public class CookiesRequiredException : JukeboxException
	{
		public override string Message => "Cookies required";
	}
	public class ImageDownloadFailedException(string url, string reason) : JukeboxException()
	{
		public override string Message => "An error has occured while downloading image " + url + ": " + reason;
	}
	public class InvalidCookiesFileException : JukeboxException
	{
		public override string Message => "Invalid cookies.txt file";
	}
	public class InvalidVideoUrlException : JukeboxException
	{
		public override string Message => "Invalid URL or track is not available";
	}
	public abstract class JukeboxException : ArgumentException
	{
	}
	public class PlaylistIsUnviewableException : JukeboxException
	{
		public override string Message => "This type of playlist cannot be downloaded";
	}
}
namespace JukeboxCore.Events
{
	public class ClipChangedArgs(JukeboxSong song, bool newSong)
	{
		public JukeboxSong Song { get; private set; } = song;

		public bool NewSong { get; private set; } = newSong;
	}
	public abstract class EventManager<T>
	{
		private readonly Dictionary<string, T> states = new Dictionary<string, T>();

		private readonly Dictionary<string, Action<T>> listeners = new Dictionary<string, Action<T>>();

		private readonly object @lock = new object();

		public void Register(string id)
		{
			if (!states.ContainsKey(id))
			{
				states.Add(id, DefaultState(id));
			}
		}

		public void Subscribe(string id, Action<T> listener)
		{
			lock (@lock)
			{
				if (!listeners.ContainsKey(id))
				{
					listeners[id] = delegate
					{
					};
				}
				Dictionary<string, Action<T>> dictionary = listeners;
				dictionary[id] = (Action<T>)Delegate.Combine(dictionary[id], listener);
			}
		}

		public void Unsubscribe(string id, Action<T> listener)
		{
			lock (@lock)
			{
				if (listeners.ContainsKey(id))
				{
					Dictionary<string, Action<T>> dictionary = listeners;
					dictionary[id] = (Action<T>)Delegate.Remove(dictionary[id], listener);
				}
			}
		}

		public void Update(string id, Action<T> mutate)
		{
			if (states.TryGetValue(id, out var value))
			{
				mutate(value);
				Action<T> value2;
				lock (@lock)
				{
					listeners.TryGetValue(id, out value2);
				}
				value2?.Invoke(value);
			}
		}

		public void Map(Action<KeyValuePair<string, T>> action)
		{
			foreach (KeyValuePair<string, T> state in states)
			{
				action(state);
			}
		}

		public T StateOf(string id)
		{
			return states.GetValueOrDefault(id);
		}

		public void Clear()
		{
			states.Clear();
			lock (@lock)
			{
				listeners.Clear();
			}
		}

		protected abstract T DefaultState(string id);
	}
}
namespace JukeboxCore.Components
{
	[ConfigureSingleton(/*Could not decode attribute arguments.*/)]
	public class JukeboxSongsLoader : MonoSingleton<JukeboxSongsLoader>
	{
		private readonly Dictionary<SongIdentifier, JukeboxSong> cache = new Dictionary<SongIdentifier, JukeboxSong>();

		public JukeboxSong Get(SongIdentifier id)
		{
			if (cache.TryGetValue(id, out var value))
			{
				return value;
			}
			value = JukeboxSong.Create(id);
			cache.Add(id, value);
			return value;
		}

		public JukeboxSongSession CreateSession(JukeboxSong song)
		{
			if (song == null)
			{
				return null;
			}
			return JukeboxSongSessionFactory.Create(song);
		}
	}
	[ConfigureSingleton(/*Could not decode attribute arguments.*/)]
	public class JukeboxStaticData : MonoSingleton<JukeboxStaticData>
	{
		[SerializeField]
		public SoundtrackCalmThemes calmThemes;
	}
	public class RingPlaceholder : MonoBehaviour
	{
		private UnscaledTimeSince sinceEnabled;

		private void Awake()
		{
			Random.Range(0, 1);
		}

		private void Update()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			((Component)this).transform.localRotation = Quaternion.Euler(0f, 0f, (float)sinceEnabled * -360f);
		}

		private void OnEnable()
		{
			sinceEnabled = 0f;
		}
	}
}
namespace JukeboxCore.Collections
{
	public class JukeboxFileTree : IDirectoryTree<JukeboxSong>, IDirectoryTree
	{
		private DirectoryInfo RealDirectory { get; }

		public string name { get; private set; }

		public IDirectoryTree<JukeboxSong> parent { get; set; }

		public IEnumerable<IDirectoryTree<JukeboxSong>> children { get; private set; }

		public IEnumerable<JukeboxSong> files { get; private set; }

		public JukeboxFileTree(string path, IDirectoryTree<JukeboxSong> parent = null)
		{
			RealDirectory = new DirectoryInfo(path);
			this.parent = parent;
			Refresh();
		}

		private JukeboxFileTree(DirectoryInfo realDirectory, IDirectoryTree<JukeboxSong> parent = null)
		{
			RealDirectory = realDirectory;
			this.parent = parent;
			Refresh();
		}

		public void Refresh()
		{
			RealDirectory.Create();
			name = RealDirectory.Name;
			DirectoryInfo[] youTubeFolderOnTop = GetYouTubeFolderOnTop(RealDirectory.GetDirectories());
			children = youTubeFolderOnTop.Select((DirectoryInfo dir) => new JukeboxFileTree(dir, this));
			files = (from file in RealDirectory.GetFiles().Where(CompositeSongsUtils.HasValidExtenstion)
				group file by CompositeSongsUtils.WithoutPostfix(file).FullName into @group
				select BuildSong(@group.ToList())).ToList();
			static JukeboxSong BuildSong(List<FileInfo> group)
			{
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Expected O, but got Unknown
				FileInfo fileInfo = CompositeSongsUtils.WithoutPostfix(group[0]);
				if (!CompositeSongsUtils.GetTrackSegments(fileInfo, group).IsPlayable)
				{
					return null;
				}
				return MonoSingleton<JukeboxSongsLoader>.Instance?.Get(new SongIdentifier(fileInfo.FullName, (IdentifierType)1));
			}
		}

		public IEnumerable<JukeboxSong> GetFilesRecursive()
		{
			return children.SelectMany((IDirectoryTree<JukeboxSong> child) => child.GetFilesRecursive()).Concat(files);
		}

		public override bool Equals(object obj)
		{
			if (obj != null && !(GetType() != obj.GetType()))
			{
				return string.Equals(RealDirectory.FullName, (obj as DirectoryInfo)?.FullName, StringComparison.InvariantCultureIgnoreCase);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return RealDirectory.GetHashCode();
		}

		private DirectoryInfo[] GetYouTubeFolderOnTop(DirectoryInfo[] directories)
		{
			if (parent == null && directories.Length > 1)
			{
				DirectoryInfo ytFolder = directories.FirstOrDefault((DirectoryInfo folder) => folder.Name.Equals("YouTube"));
				if (ytFolder != null)
				{
					return directories.Where((DirectoryInfo e) => !e.Equals(ytFolder)).Prepend(ytFolder).ToArray();
				}
			}
			return directories;
		}
	}
}

plugins/Jukebox/RTLTMPro.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using Jukebox.RTLTMPro.Types;
using Microsoft.CodeAnalysis;
using TMPro;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 Jukebox.RTLTMPro
{
	internal static class Char32Utils
	{
		public static bool IsUnicode16Char(int ch)
		{
			return ch < 65535;
		}

		public static bool IsRTLCharacter(int ch)
		{
			if (!IsUnicode16Char(ch))
			{
				return false;
			}
			return TextUtils.IsRTLCharacter((char)ch);
		}

		public static bool IsEnglishLetter(int ch)
		{
			if (!IsUnicode16Char(ch))
			{
				return false;
			}
			return TextUtils.IsEnglishLetter((char)ch);
		}

		public static bool IsNumber(int ch, bool preserveNumbers, bool farsi)
		{
			if (!IsUnicode16Char(ch))
			{
				return false;
			}
			return TextUtils.IsNumber((char)ch, preserveNumbers, farsi);
		}

		public static bool IsSymbol(int ch)
		{
			if (!IsUnicode16Char(ch))
			{
				return false;
			}
			return char.IsSymbol((char)ch);
		}

		public static bool IsLetter(int ch)
		{
			if (!IsUnicode16Char(ch))
			{
				return false;
			}
			return char.IsLetter((char)ch);
		}

		public static bool IsPunctuation(int ch)
		{
			if (!IsUnicode16Char(ch))
			{
				return false;
			}
			return char.IsPunctuation((char)ch);
		}

		public static bool IsWhiteSpace(int ch)
		{
			if (!IsUnicode16Char(ch))
			{
				return false;
			}
			return char.IsWhiteSpace((char)ch);
		}
	}
	internal class FastStringBuilder
	{
		private int length;

		private int[] array;

		private int capacity;

		public int Length
		{
			get
			{
				return length;
			}
			set
			{
				if (value <= length)
				{
					length = value;
				}
			}
		}

		public FastStringBuilder(int capacity)
		{
			if (capacity < 0)
			{
				throw new ArgumentOutOfRangeException("capacity");
			}
			this.capacity = capacity;
			array = new int[capacity];
		}

		public FastStringBuilder(string text)
			: this(text, text.Length)
		{
		}

		public FastStringBuilder(string text, int capacity)
			: this(capacity)
		{
			SetValue(text);
		}

		public static implicit operator string(FastStringBuilder x)
		{
			return x.ToString();
		}

		public static implicit operator FastStringBuilder(string x)
		{
			return new FastStringBuilder(x);
		}

		public int Get(int index)
		{
			return array[index];
		}

		public void Set(int index, int ch)
		{
			array[index] = ch;
		}

		public void SetValue(string text)
		{
			int num = 0;
			length = text.Length;
			EnsureCapacity(length, keepValues: false);
			for (int i = 0; i < text.Length; i++)
			{
				int num2 = char.ConvertToUtf32(text, i);
				if (num2 > 65535)
				{
					i++;
				}
				array[num++] = num2;
			}
			length = num;
		}

		public void SetValue(FastStringBuilder other)
		{
			EnsureCapacity(other.length, keepValues: false);
			Copy(other.array, array);
			length = other.length;
		}

		public void Append(int ch)
		{
			length++;
			if (capacity < length)
			{
				EnsureCapacity(length, keepValues: true);
			}
			array[length - 1] = ch;
		}

		public void Append(char ch)
		{
			length++;
			if (capacity < length)
			{
				EnsureCapacity(length, keepValues: true);
			}
			array[length - 1] = ch;
		}

		public void Insert(int pos, FastStringBuilder str, int offset, int count)
		{
			if (str == this)
			{
				throw new InvalidOperationException("You cannot pass the same string builder to insert");
			}
			if (count != 0)
			{
				length += count;
				EnsureCapacity(length, keepValues: true);
				for (int num = length - count - 1; num >= pos; num--)
				{
					array[num + count] = array[num];
				}
				for (int i = 0; i < count; i++)
				{
					array[pos + i] = str.array[offset + i];
				}
			}
		}

		public void Insert(int pos, FastStringBuilder str)
		{
			Insert(pos, str, 0, str.length);
		}

		public void Insert(int pos, int ch)
		{
			length++;
			EnsureCapacity(length, keepValues: true);
			for (int num = length - 2; num >= pos; num--)
			{
				array[num + 1] = array[num];
			}
			array[pos] = ch;
		}

		public void RemoveAll(int character)
		{
			int num = 0;
			for (int i = 0; i < length; i++)
			{
				if (array[i] != character)
				{
					array[num] = array[i];
					num++;
				}
			}
			length = num;
		}

		public void Remove(int start, int length)
		{
			for (int i = start; i < this.length - length; i++)
			{
				array[i] = array[i + length];
			}
			this.length -= length;
		}

		public void Reverse(int startIndex, int length)
		{
			for (int i = 0; i < length / 2; i++)
			{
				int num = startIndex + i;
				int num2 = startIndex + length - i - 1;
				int num3 = array[num];
				int num4 = array[num2];
				array[num] = num4;
				array[num2] = num3;
			}
		}

		public void Reverse()
		{
			Reverse(0, length);
		}

		public void Substring(FastStringBuilder output, int start, int length)
		{
			output.length = 0;
			for (int i = 0; i < length; i++)
			{
				output.Append(array[start + i]);
			}
		}

		public override string ToString()
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < length; i++)
			{
				stringBuilder.Append(char.ConvertFromUtf32(array[i]));
			}
			return stringBuilder.ToString();
		}

		public string ToDebugString()
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < length; i++)
			{
				stringBuilder.Append("\\");
				stringBuilder.Append(array[i].ToString("X"));
			}
			return stringBuilder.ToString();
		}

		public void Replace(int oldChar, int newChar)
		{
			for (int i = 0; i < length; i++)
			{
				if (array[i] == oldChar)
				{
					array[i] = newChar;
				}
			}
		}

		public void Replace(FastStringBuilder oldStr, FastStringBuilder newStr)
		{
			for (int i = 0; i < length; i++)
			{
				bool flag = true;
				for (int j = 0; j < oldStr.Length; j++)
				{
					if (array[i + j] != oldStr.Get(j))
					{
						flag = false;
						break;
					}
				}
				if (!flag)
				{
					continue;
				}
				if (oldStr.Length == newStr.Length)
				{
					for (int k = 0; k < oldStr.Length; k++)
					{
						array[i + k] = newStr.Get(k);
					}
				}
				else if (oldStr.Length < newStr.Length)
				{
					int num = newStr.Length - oldStr.Length;
					length += num;
					EnsureCapacity(length, keepValues: true);
					for (int num2 = length - num - 1; num2 >= i + oldStr.Length; num2--)
					{
						array[num2 + num] = array[num2];
					}
					for (int l = 0; l < newStr.Length; l++)
					{
						array[i + l] = newStr.Get(l);
					}
				}
				else
				{
					int num3 = oldStr.Length - newStr.Length;
					for (int m = i + num3; m < length - num3; m++)
					{
						array[m] = array[m + num3];
					}
					for (int n = 0; n < newStr.Length; n++)
					{
						array[i + n] = newStr.Get(n);
					}
					length -= num3;
				}
				i += newStr.Length;
			}
		}

		public void Clear()
		{
			length = 0;
		}

		private void EnsureCapacity(int cap, bool keepValues)
		{
			if (capacity < cap)
			{
				if (capacity == 0)
				{
					capacity = 1;
				}
				while (capacity < cap)
				{
					capacity *= 2;
				}
				if (keepValues)
				{
					int[] dst = new int[capacity];
					Copy(array, dst);
					array = dst;
				}
				else
				{
					array = new int[capacity];
				}
			}
		}

		private static void Copy(int[] src, int[] dst)
		{
			for (int i = 0; i < src.Length; i++)
			{
				dst[i] = src[i];
			}
		}
	}
	internal static class GlyphFixer
	{
		public static Dictionary<char, char> EnglishToFarsiNumberMap = new Dictionary<char, char>
		{
			['0'] = '۰',
			['1'] = '۱',
			['2'] = '۲',
			['3'] = '۳',
			['4'] = '۴',
			['5'] = '۵',
			['6'] = '۶',
			['7'] = '۷',
			['8'] = '۸',
			['9'] = '۹'
		};

		public static Dictionary<char, char> EnglishToHinduNumberMap = new Dictionary<char, char>
		{
			['0'] = '٠',
			['1'] = '١',
			['2'] = '٢',
			['3'] = '٣',
			['4'] = '٤',
			['5'] = '٥',
			['6'] = '٦',
			['7'] = '٧',
			['8'] = '٨',
			['9'] = '٩'
		};

		public static void Fix(FastStringBuilder input, FastStringBuilder output, bool preserveNumbers, bool farsi, bool fixTextTags)
		{
			FixYah(input, farsi);
			output.SetValue(input);
			for (int i = 0; i < input.Length; i++)
			{
				int num = input.Get(i);
				if (num == 1604 && i < input.Length - 1 && HandleSpecialLam(input, output, i))
				{
					if (i > 0)
					{
						char c = (char)output.Get(i);
						if (CanCharacterBeConnectedToNext(input.Get(i - 1)))
						{
							output.Set(i, (ushort)(c + 1));
						}
					}
					i++;
				}
				else if (TextUtils.IsGlyphFixedArabicCharacter((char)num))
				{
					char c2 = GlyphTable.Convert((char)num);
					if (IsMiddleLetter(input, i))
					{
						output.Set(i, (ushort)(c2 + 3));
					}
					else if (IsFinishingLetter(input, i))
					{
						output.Set(i, (ushort)(c2 + 1));
					}
					else if (IsLeadingLetter(input, i))
					{
						output.Set(i, (ushort)(c2 + 2));
					}
					else
					{
						output.Set(i, c2);
					}
				}
			}
			if (!preserveNumbers)
			{
				if (fixTextTags)
				{
					FixNumbersOutsideOfTags(output, farsi);
				}
				else
				{
					FixNumbers(output, farsi);
				}
			}
		}

		public static void FixYah(FastStringBuilder text, bool farsi)
		{
			for (int i = 0; i < text.Length; i++)
			{
				if (farsi && text.Get(i) == 1610)
				{
					text.Set(i, 1740);
				}
				else if (!farsi && text.Get(i) == 1740)
				{
					text.Set(i, 1610);
				}
			}
		}

		private static bool HandleSpecialLam(FastStringBuilder input, FastStringBuilder output, int i)
		{
			bool flag;
			switch (input.Get(i + 1))
			{
			case 1573:
				output.Set(i, 65273);
				flag = true;
				break;
			case 1575:
				output.Set(i, 65275);
				flag = true;
				break;
			case 1571:
				output.Set(i, 65271);
				flag = true;
				break;
			case 1570:
				output.Set(i, 65269);
				flag = true;
				break;
			default:
				flag = false;
				break;
			}
			if (flag)
			{
				output.Set(i + 1, 65535);
			}
			return flag;
		}

		public static void FixNumbers(FastStringBuilder text, bool farsi)
		{
			text.Replace(48, farsi ? 1776 : 1632);
			text.Replace(49, farsi ? 1777 : 1633);
			text.Replace(50, farsi ? 1778 : 1634);
			text.Replace(51, farsi ? 1779 : 1635);
			text.Replace(52, farsi ? 1780 : 1636);
			text.Replace(53, farsi ? 1781 : 1637);
			text.Replace(54, farsi ? 1782 : 1638);
			text.Replace(55, farsi ? 1783 : 1639);
			text.Replace(56, farsi ? 1784 : 1640);
			text.Replace(57, farsi ? 1785 : 1641);
		}

		public static void FixNumbersOutsideOfTags(FastStringBuilder text, bool farsi)
		{
			HashSet<char> hashSet = new HashSet<char>(EnglishToFarsiNumberMap.Keys);
			for (int i = 0; i < text.Length; i++)
			{
				int num = text.Get(i);
				if (num == 60)
				{
					bool flag = false;
					for (int j = i + 1; j < text.Length; j++)
					{
						int num2 = text.Get(j);
						if (j != i + 1 || num2 != 32)
						{
							switch (num2)
							{
							case 62:
								i = j;
								flag = true;
								break;
							default:
								continue;
							case 60:
								break;
							}
						}
						break;
					}
					if (flag)
					{
						continue;
					}
				}
				if (hashSet.Contains((char)num))
				{
					text.Set(i, farsi ? EnglishToFarsiNumberMap[(char)num] : EnglishToHinduNumberMap[(char)num]);
				}
			}
		}

		private static bool IsLeadingLetter(FastStringBuilder letters, int index)
		{
			if (index == letters.Length - 1)
			{
				return false;
			}
			int character = letters.Get(index);
			int character2 = ((index != 0) ? letters.Get(index - 1) : 0);
			int character3 = letters.Get(index + 1);
			if ((index == 0 || !CanCharacterBeConnectedToNext(character2) || !CanCharacterBeConnectedToPrevious(character)) && CanCharacterBeConnectedToNext(character))
			{
				return CanCharacterBeConnectedToPrevious(character3);
			}
			return false;
		}

		private static bool IsFinishingLetter(FastStringBuilder letters, int index)
		{
			if (index == 0)
			{
				return false;
			}
			int character = letters.Get(index);
			int character2 = letters.Get(index - 1);
			int character3 = ((index < letters.Length - 1) ? letters.Get(index + 1) : 0);
			if (CanCharacterBeConnectedToNext(character2) && CanCharacterBeConnectedToPrevious(character))
			{
				if (index != letters.Length - 1 && CanCharacterBeConnectedToPrevious(character3))
				{
					return !CanCharacterBeConnectedToNext(character);
				}
				return true;
			}
			return false;
		}

		private static bool IsMiddleLetter(FastStringBuilder letters, int index)
		{
			if (index == 0 || index == letters.Length - 1)
			{
				return false;
			}
			int character = letters.Get(index);
			int character2 = letters.Get(index - 1);
			int character3 = letters.Get(index + 1);
			if (CanCharacterBeConnectedToNext(character2) && CanCharacterBeConnectedToPrevious(character) && CanCharacterBeConnectedToNext(character))
			{
				return CanCharacterBeConnectedToPrevious(character3);
			}
			return false;
		}

		private static bool CanCharacterBeConnectedToNext(int character)
		{
			if (character != 1569 && character != 1570 && character != 1571 && character != 1573 && character != 1572 && character != 1575 && character != 1583 && character != 1584 && character != 1585 && character != 1586 && character != 1688 && character != 1608)
			{
				return TextUtils.IsGlyphFixedArabicCharacter((char)character);
			}
			return false;
		}

		private static bool CanCharacterBeConnectedToPrevious(int character)
		{
			if (character != 1569)
			{
				return TextUtils.IsGlyphFixedArabicCharacter((char)character);
			}
			return false;
		}
	}
	internal static class GlyphTable
	{
		private static readonly Dictionary<char, char> MapList;

		static GlyphTable()
		{
			string[] names = Enum.GetNames(typeof(ArabicIsolatedLetters));
			MapList = new Dictionary<char, char>(names.Length);
			string[] array = names;
			foreach (string value in array)
			{
				MapList.Add((char)(int)Enum.Parse(typeof(ArabicGeneralLetters), value), (char)(int)Enum.Parse(typeof(ArabicIsolatedLetters), value));
			}
		}

		public static char Convert(char toBeConverted)
		{
			if (!MapList.TryGetValue(toBeConverted, out var value))
			{
				return toBeConverted;
			}
			return value;
		}
	}
	internal static class LigatureFixer
	{
		private static readonly List<int> LtrTextHolder = new List<int>(512);

		private static readonly List<int> TagTextHolder = new List<int>(512);

		private static readonly Dictionary<char, char> MirroredCharsMap = new Dictionary<char, char>
		{
			['('] = ')',
			[')'] = '(',
			['»'] = '«',
			['«'] = '»',
			['{'] = '}',
			['}'] = '{',
			['['] = ']',
			[']'] = '['
		};

		private static readonly HashSet<char> MirroredCharsSet = new HashSet<char>(MirroredCharsMap.Keys);

		private static void FlushBufferToOutput(List<int> buffer, FastStringBuilder output)
		{
			for (int i = 0; i < buffer.Count; i++)
			{
				output.Append(buffer[buffer.Count - 1 - i]);
			}
			buffer.Clear();
		}

		public static void Fix(FastStringBuilder input, FastStringBuilder output, bool farsi, bool fixTextTags, bool preserveNumbers)
		{
			LtrTextHolder.Clear();
			TagTextHolder.Clear();
			for (int num = input.Length - 1; num >= 0; num--)
			{
				bool flag = num > 0 && num < input.Length - 1;
				bool flag2 = num == 0;
				bool flag3 = num == input.Length - 1;
				int num2 = input.Get(num);
				int ch = 0;
				if (!flag3)
				{
					ch = input.Get(num + 1);
				}
				int ch2 = 0;
				if (!flag2)
				{
					ch2 = input.Get(num - 1);
				}
				if (fixTextTags && num2 == 62)
				{
					bool flag4 = false;
					int num3 = num;
					TagTextHolder.Add(num2);
					for (int num4 = num - 1; num4 >= 0; num4--)
					{
						int num5 = input.Get(num4);
						TagTextHolder.Add(num5);
						if (num5 == 60)
						{
							if (input.Get(num4 + 1) != 32)
							{
								flag4 = true;
								num3 = num4;
							}
							break;
						}
					}
					if (flag4)
					{
						FlushBufferToOutput(LtrTextHolder, output);
						FlushBufferToOutput(TagTextHolder, output);
						num = num3;
						continue;
					}
					TagTextHolder.Clear();
				}
				if (Char32Utils.IsPunctuation(num2) || Char32Utils.IsSymbol(num2))
				{
					if (MirroredCharsSet.Contains((char)num2))
					{
						bool num6 = Char32Utils.IsRTLCharacter(ch2);
						bool flag5 = Char32Utils.IsRTLCharacter(ch);
						if (num6 || flag5)
						{
							num2 = MirroredCharsMap[(char)num2];
						}
					}
					if (flag)
					{
						bool flag6 = Char32Utils.IsRTLCharacter(ch2);
						bool flag7 = Char32Utils.IsRTLCharacter(ch);
						bool flag8 = Char32Utils.IsWhiteSpace(ch);
						bool flag9 = Char32Utils.IsWhiteSpace(ch2);
						bool flag10 = num2 == 95;
						bool flag11 = num2 == 46 || num2 == 1548 || num2 == 1563;
						if ((flag7 && flag6) || (flag9 && flag11) || (flag8 && flag6) || (flag7 && flag9) || ((flag7 || flag6) && flag10))
						{
							FlushBufferToOutput(LtrTextHolder, output);
							output.Append(num2);
						}
						else
						{
							LtrTextHolder.Add(num2);
						}
					}
					else if (flag3)
					{
						LtrTextHolder.Add(num2);
					}
					else if (flag2)
					{
						output.Append(num2);
					}
					continue;
				}
				if (flag)
				{
					bool flag12 = Char32Utils.IsEnglishLetter(ch2);
					bool flag13 = Char32Utils.IsEnglishLetter(ch);
					bool flag14 = Char32Utils.IsNumber(ch2, preserveNumbers, farsi);
					bool flag15 = Char32Utils.IsNumber(ch, preserveNumbers, farsi);
					bool flag16 = Char32Utils.IsSymbol(ch2);
					bool flag17 = Char32Utils.IsSymbol(ch);
					if (num2 == 32 && (flag13 || flag15 || flag17) && (flag12 || flag14 || flag16))
					{
						LtrTextHolder.Add(num2);
						continue;
					}
				}
				if (Char32Utils.IsEnglishLetter(num2) || Char32Utils.IsNumber(num2, preserveNumbers, farsi))
				{
					LtrTextHolder.Add(num2);
					continue;
				}
				if ((num2 >= 55296 && num2 <= 56319) || (num2 >= 56320 && num2 <= 57343))
				{
					LtrTextHolder.Add(num2);
					continue;
				}
				FlushBufferToOutput(LtrTextHolder, output);
				if (num2 != 65535 && num2 != 8204)
				{
					output.Append(num2);
				}
			}
			FlushBufferToOutput(LtrTextHolder, output);
		}
	}
	internal static class RichTextFixer
	{
		public enum TagType
		{
			None,
			Opening,
			Closing,
			SelfContained
		}

		public struct Tag
		{
			public int Start;

			public int End;

			public int HashCode;

			public TagType Type;

			public Tag(int start, int end, TagType type, int hashCode)
			{
				Type = type;
				Start = start;
				End = end;
				HashCode = hashCode;
			}
		}

		public static void Fix(FastStringBuilder text)
		{
			int num = 0;
			while (num < text.Length)
			{
				FindTag(text, num, out var tag);
				if (tag.Type != TagType.None)
				{
					text.Reverse(tag.Start, tag.End - tag.Start + 1);
					num = tag.End;
					num++;
					continue;
				}
				break;
			}
		}

		public static void FindTag(FastStringBuilder str, int start, out Tag tag)
		{
			int num = start;
			while (num < str.Length)
			{
				if (str.Get(num) != 60)
				{
					num++;
					continue;
				}
				bool flag = true;
				tag.HashCode = 0;
				for (int i = num + 1; i < str.Length; i++)
				{
					int num2 = str.Get(i);
					if (flag)
					{
						if (Char32Utils.IsLetter(num2))
						{
							if (tag.HashCode == 0)
							{
								tag.HashCode = num2.GetHashCode();
							}
							else
							{
								tag.HashCode = (tag.HashCode * 397) ^ num2.GetHashCode();
							}
						}
						else if (tag.HashCode != 0)
						{
							flag = false;
						}
					}
					if (i == num + 1 && num2 == 32)
					{
						break;
					}
					switch (num2)
					{
					case 62:
						tag.Start = num;
						tag.End = i;
						if (str.Get(i - 1) == 47)
						{
							tag.Type = TagType.SelfContained;
						}
						else if (str.Get(num + 1) == 47)
						{
							tag.Type = TagType.Closing;
						}
						else
						{
							tag.Type = TagType.Opening;
						}
						return;
					default:
						continue;
					case 60:
						break;
					}
					break;
				}
				num++;
			}
			tag.Start = 0;
			tag.End = 0;
			tag.Type = TagType.None;
			tag.HashCode = 0;
		}
	}
	internal static class RTLSupport
	{
		public const int DefaultBufferSize = 2048;

		private static FastStringBuilder inputBuilder;

		private static FastStringBuilder glyphFixerOutput;

		static RTLSupport()
		{
			inputBuilder = new FastStringBuilder(2048);
			glyphFixerOutput = new FastStringBuilder(2048);
		}

		public static void FixRTL(string input, FastStringBuilder output, bool farsi = true, bool fixTextTags = true, bool preserveNumbers = false)
		{
			inputBuilder.SetValue(input);
			TashkeelFixer.RemoveTashkeel(inputBuilder);
			GlyphFixer.Fix(inputBuilder, glyphFixerOutput, preserveNumbers, farsi, fixTextTags);
			TashkeelFixer.RestoreTashkeel(glyphFixerOutput);
			TashkeelFixer.FixShaddaCombinations(glyphFixerOutput);
			LigatureFixer.Fix(glyphFixerOutput, output, farsi, fixTextTags, preserveNumbers);
			if (fixTextTags)
			{
				RichTextFixer.Fix(output);
			}
			inputBuilder.Clear();
		}
	}
	public static class RtlUtils
	{
		private enum RtlLanguage
		{
			None,
			Arabic,
			Farsi,
			Hebrew
		}

		public static string Set(TMP_Text component, string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			RtlLanguage rtlLanguage = DetectLanguage(text);
			string text2 = Fix(text, rtlLanguage);
			component.isRightToLeftText = rtlLanguage != RtlLanguage.None;
			component.text = text2;
			return text2;
		}

		private static string Fix(string text, RtlLanguage language)
		{
			if (language == RtlLanguage.None)
			{
				return text;
			}
			bool farsi = language == RtlLanguage.Farsi;
			if (language == RtlLanguage.Hebrew)
			{
				text = MoveHebrewMarksBeforeBase(text);
			}
			FastStringBuilder fastStringBuilder = new FastStringBuilder(2048);
			RTLSupport.FixRTL(text, fastStringBuilder, farsi, fixTextTags: true, preserveNumbers: true);
			fastStringBuilder.Reverse();
			return fastStringBuilder.ToString();
		}

		private static string MoveHebrewMarksBeforeBase(string text)
		{
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			for (int i = 0; i < text.Length; i++)
			{
				char c = text[i];
				if (!IsHebrewLetter(c))
				{
					stringBuilder.Append(c);
					continue;
				}
				int j;
				for (j = i + 1; j < text.Length && IsHebrewMark(text[j]); j++)
				{
				}
				for (int k = i + 1; k < j; k++)
				{
					stringBuilder.Append(text[k]);
				}
				stringBuilder.Append(c);
				i = j - 1;
			}
			return stringBuilder.ToString();
		}

		private static RtlLanguage DetectLanguage(string text)
		{
			bool flag = false;
			bool flag2 = false;
			bool flag3 = false;
			foreach (char c in text)
			{
				if (c >= '\u0590' && c <= '\u05ff')
				{
					flag = true;
				}
				else if (IsFarsiSpecific(c))
				{
					flag2 = true;
				}
				else if (IsArabicCharacter(c))
				{
					flag3 = true;
				}
			}
			if (flag)
			{
				return RtlLanguage.Hebrew;
			}
			if (flag2)
			{
				return RtlLanguage.Farsi;
			}
			if (flag3)
			{
				return RtlLanguage.Arabic;
			}
			return RtlLanguage.None;
		}

		private static bool IsHebrewLetter(char c)
		{
			switch (c)
			{
			case 'א':
			case 'ב':
			case 'ג':
			case 'ד':
			case 'ה':
			case 'ו':
			case 'ז':
			case 'ח':
			case 'ט':
			case 'י':
			case 'ך':
			case 'כ':
			case 'ל':
			case 'ם':
			case 'מ':
			case 'ן':
			case 'נ':
			case 'ס':
			case 'ע':
			case 'ף':
			case 'פ':
			case 'ץ':
			case 'צ':
			case 'ק':
			case 'ר':
			case 'ש':
			case 'ת':
			case 'װ':
			case 'ױ':
			case 'ײ':
				return true;
			default:
				return false;
			}
		}

		private static bool IsHebrewMark(char c)
		{
			switch (c)
			{
			case '\u0591':
			case '\u0592':
			case '\u0593':
			case '\u0594':
			case '\u0595':
			case '\u0596':
			case '\u0597':
			case '\u0598':
			case '\u0599':
			case '\u059a':
			case '\u059b':
			case '\u059c':
			case '\u059d':
			case '\u059e':
			case '\u059f':
			case '\u05a0':
			case '\u05a1':
			case '\u05a2':
			case '\u05a3':
			case '\u05a4':
			case '\u05a5':
			case '\u05a6':
			case '\u05a7':
			case '\u05a8':
			case '\u05a9':
			case '\u05aa':
			case '\u05ab':
			case '\u05ac':
			case '\u05ad':
			case '\u05ae':
			case '\u05af':
			case '\u05b0':
			case '\u05b1':
			case '\u05b2':
			case '\u05b3':
			case '\u05b4':
			case '\u05b5':
			case '\u05b6':
			case '\u05b7':
			case '\u05b8':
			case '\u05b9':
			case '\u05ba':
			case '\u05bb':
			case '\u05bc':
			case '\u05bd':
			case '\u05bf':
			case '\u05c1':
			case '\u05c2':
			case '\u05c4':
			case '\u05c5':
			case '\u05c7':
				return true;
			default:
				return false;
			}
		}

		private static bool IsFarsiSpecific(char c)
		{
			switch (c)
			{
			case 'پ':
			case 'چ':
			case 'ژ':
			case 'ک':
			case 'گ':
			case 'ی':
				return true;
			default:
				return false;
			}
		}

		private static bool IsArabicCharacter(char c)
		{
			if (c >= 'ﭐ')
			{
				if (c >= 'ﹰ')
				{
					if (c <= '\ufeff')
					{
						goto IL_0062;
					}
				}
				else if (c <= '﷿')
				{
					goto IL_0062;
				}
			}
			else if (c <= 'ݿ')
			{
				if (c <= 'ۿ')
				{
					if (c >= '\u0600')
					{
						goto IL_0062;
					}
				}
				else if (c >= 'ݐ')
				{
					goto IL_0062;
				}
			}
			else if (c <= '\u089f')
			{
				if (c >= 'ࡰ')
				{
					goto IL_0062;
				}
			}
			else if (c <= '\u08ff')
			{
				goto IL_0062;
			}
			return false;
			IL_0062:
			return true;
		}
	}
	internal static class TashkeelFixer
	{
		private static readonly List<TashkeelLocation> TashkeelLocations = new List<TashkeelLocation>(100);

		private static readonly string ShaddaDammatan = new string(new char[2] { '\u0651', '\u064c' });

		private static readonly string ShaddaKasratan = new string(new char[2] { '\u0651', '\u064d' });

		private static readonly string ShaddaSuperscriptAlef = new string(new char[2] { '\u0651', '\u0670' });

		private static readonly string ShaddaFatha = new string(new char[2] { '\u0651', '\u064e' });

		private static readonly string ShaddaDamma = new string(new char[2] { '\u0651', '\u064f' });

		private static readonly string ShaddaKasra = new string(new char[2] { '\u0651', '\u0650' });

		private static readonly string ShaddaWithFathaIsolatedForm = 'ﱠ'.ToString();

		private static readonly string ShaddaWithDammaIsolatedForm = 'ﱡ'.ToString();

		private static readonly string ShaddaWithKasraIsolatedForm = 'ﱢ'.ToString();

		private static readonly string ShaddaWithDammatanIsolatedForm = 'ﱞ'.ToString();

		private static readonly string ShaddaWithKasratanIsolatedForm = 'ﱟ'.ToString();

		private static readonly string ShaddaWithSuperscriptAlefIsolatedForm = 'ﱣ'.ToString();

		private static readonly HashSet<char> TashkeelCharactersSet = new HashSet<char>
		{
			'\u064b', '\u064c', '\u064d', '\u064e', '\u064f', '\u0650', '\u0651', '\u0652', '\u0653', '\u0670',
			'ﱞ', 'ﱟ', 'ﱠ', 'ﱡ', 'ﱢ', 'ﱣ'
		};

		private static readonly Dictionary<char, char> ShaddaCombinationMap = new Dictionary<char, char>
		{
			['\u064c'] = 'ﱞ',
			['\u064d'] = 'ﱟ',
			['\u064e'] = 'ﱠ',
			['\u064f'] = 'ﱡ',
			['\u0650'] = 'ﱢ',
			['\u0670'] = 'ﱣ'
		};

		public static void RemoveTashkeel(FastStringBuilder input)
		{
			TashkeelLocations.Clear();
			int num = 0;
			for (int i = 0; i < input.Length; i++)
			{
				int num2 = input.Get(i);
				if (Char32Utils.IsUnicode16Char(num2) && TashkeelCharactersSet.Contains((char)num2))
				{
					TashkeelLocations.Add(new TashkeelLocation((TashkeelCharacters)num2, i));
					continue;
				}
				input.Set(num, num2);
				num++;
			}
			input.Length = num;
		}

		public static void RestoreTashkeel(FastStringBuilder letters)
		{
			foreach (TashkeelLocation tashkeelLocation in TashkeelLocations)
			{
				letters.Insert(tashkeelLocation.Position, tashkeelLocation.Tashkeel);
			}
		}

		public static void FixShaddaCombinations(FastStringBuilder input)
		{
			int num = 0;
			int num2 = 0;
			while (num2 < input.Length)
			{
				int num3 = input.Get(num2);
				int num4 = ((num2 < input.Length - 1) ? input.Get(num2 + 1) : 0);
				if (num3 == 1617 && ShaddaCombinationMap.ContainsKey((char)num4))
				{
					input.Set(num, ShaddaCombinationMap[(char)num4]);
					num++;
					num2 += 2;
				}
				else
				{
					input.Set(num, num3);
					num++;
					num2++;
				}
			}
			input.Length = num;
		}
	}
	public struct TashkeelLocation
	{
		public char Tashkeel { get; set; }

		public int Position { get; set; }

		public TashkeelLocation(TashkeelCharacters tashkeel, int position)
		{
			this = default(TashkeelLocation);
			Tashkeel = (char)tashkeel;
			Position = position;
		}
	}
	internal static class TextUtils
	{
		private const char LowerCaseA = 'a';

		private const char UpperCaseA = 'A';

		private const char LowerCaseZ = 'z';

		private const char UpperCaseZ = 'Z';

		private const char HebrewLow = '\u0591';

		private const char HebrewHigh = '״';

		private const char ArabicBaseBlockLow = '\u0600';

		private const char ArabicBaseBlockHigh = 'ۿ';

		private const char ArabicExtendedABlockLow = 'ࢠ';

		private const char ArabicExtendedABlockHigh = '\u08ff';

		private const char ArabicExtendedBBlockLow = 'ࡰ';

		private const char ArabicExtendedBBlockHigh = '\u089f';

		private const char ArabicPresentationFormsABlockLow = 'ﭐ';

		private const char ArabicPresentationFormsABlockHigh = '﷿';

		private const char ArabicPresentationFormsBBlockLow = 'ﹰ';

		private const char ArabicPresentationFormsBBlockHigh = '\ufeff';

		public static bool IsArabicPresentationForm(char ch)
		{
			if (ch < 'ﭐ' || ch > '﷿')
			{
				if (ch >= 'ﹰ')
				{
					return ch <= '\ufeff';
				}
				return false;
			}
			return true;
		}

		public static bool IsPunctuation(char ch)
		{
			throw new NotImplementedException();
		}

		public static bool IsNumber(char ch, bool preserveNumbers, bool farsi)
		{
			if (preserveNumbers)
			{
				return IsEnglishNumber(ch);
			}
			if (farsi)
			{
				return IsFarsiNumber(ch);
			}
			return IsHinduNumber(ch);
		}

		public static bool IsEnglishNumber(char ch)
		{
			if (ch >= '0')
			{
				return ch <= '9';
			}
			return false;
		}

		public static bool IsFarsiNumber(char ch)
		{
			if (ch >= '۰')
			{
				return ch <= '۹';
			}
			return false;
		}

		public static bool IsHinduNumber(char ch)
		{
			if (ch >= '٠')
			{
				return ch <= '٩';
			}
			return false;
		}

		public static bool IsEnglishLetter(char ch)
		{
			if (ch < 'A' || ch > 'Z')
			{
				if (ch >= 'a')
				{
					return ch <= 'z';
				}
				return false;
			}
			return true;
		}

		public static bool IsHebrewCharacter(char ch)
		{
			if (ch >= '\u0591')
			{
				return ch <= '״';
			}
			return false;
		}

		public static bool IsArabicCharacter(char ch)
		{
			if ((ch < '\u0600' || ch > 'ۿ') && (ch < 'ࢠ' || ch > '\u08ff') && (ch < 'ࡰ' || ch > '\u089f') && (ch < 'ﭐ' || ch > '﷿'))
			{
				if (ch >= 'ﹰ')
				{
					return ch <= '\ufeff';
				}
				return false;
			}
			return true;
		}

		public static bool IsRTLCharacter(char ch)
		{
			if (IsHebrewCharacter(ch))
			{
				return true;
			}
			if (IsArabicCharacter(ch))
			{
				return true;
			}
			return false;
		}

		public static bool IsGlyphFixedArabicCharacter(char ch)
		{
			switch (ch)
			{
			case 'ء':
			case 'آ':
			case 'أ':
			case 'ؤ':
			case 'إ':
			case 'ئ':
			case 'ا':
			case 'ب':
			case 'ة':
			case 'ت':
			case 'ث':
			case 'ج':
			case 'ح':
			case 'خ':
			case 'د':
			case 'ذ':
			case 'ر':
			case 'ز':
			case 'س':
			case 'ش':
			case 'ص':
			case 'ض':
			case 'ط':
			case 'ظ':
			case 'ع':
			case 'غ':
			case 'ف':
			case 'ق':
			case 'ك':
			case 'ل':
			case 'م':
			case 'ن':
			case 'ه':
			case 'و':
			case 'ى':
			case 'ي':
			case 'پ':
			case 'چ':
			case 'ژ':
			case 'ک':
			case 'گ':
			case 'ی':
				return true;
			default:
				return false;
			}
		}

		public static bool IsRTLInput(string input)
		{
			bool flag = false;
			foreach (char c in input)
			{
				switch (c)
				{
				case '<':
					flag = true;
					continue;
				case '>':
					flag = false;
					continue;
				}
				if (!flag && char.IsLetter(c))
				{
					return IsRTLCharacter(c);
				}
			}
			return false;
		}
	}
}
namespace Jukebox.RTLTMPro.Types
{
	public enum ArabicGeneralLetters
	{
		Hamza = 1569,
		AlefMaddaAbove = 1570,
		AlefHamzaAbove = 1571,
		WawHamzaAbove = 1572,
		AlefHamzaBelow = 1573,
		YehHamzaAbove = 1574,
		Alef = 1575,
		Beh = 1576,
		TehMarbuta = 1577,
		Teh = 1578,
		Theh = 1579,
		Jeem = 1580,
		Hah = 1581,
		Khah = 1582,
		Dal = 1583,
		Thal = 1584,
		Reh = 1585,
		Zain = 1586,
		Seen = 1587,
		Sheen = 1588,
		Sad = 1589,
		Dad = 1590,
		Tah = 1591,
		Zah = 1592,
		Ain = 1593,
		Ghain = 1594,
		Feh = 1601,
		Qaf = 1602,
		Kaf = 1603,
		Lam = 1604,
		Meem = 1605,
		Noon = 1606,
		Heh = 1607,
		Waw = 1608,
		AlefMaksura = 1609,
		Yeh = 1610,
		FarsiYeh = 1740,
		Peh = 1662,
		TCheh = 1670,
		Jeh = 1688,
		Keheh = 1705,
		Gaf = 1711,
		Tatweel = 1600
	}
	internal enum ArabicIsolatedLetters
	{
		Hamza = 65152,
		AlefMaddaAbove = 65153,
		AlefHamzaAbove = 65155,
		WawHamzaAbove = 65157,
		AlefHamzaBelow = 65159,
		YehHamzaAbove = 65161,
		Alef = 65165,
		Beh = 65167,
		TehMarbuta = 65171,
		Teh = 65173,
		Theh = 65177,
		Jeem = 65181,
		Hah = 65185,
		Khah = 65189,
		Dal = 65193,
		Thal = 65195,
		Reh = 65197,
		Zain = 65199,
		Seen = 65201,
		Sheen = 65205,
		Sad = 65209,
		Dad = 65213,
		Tah = 65217,
		Zah = 65221,
		Ain = 65225,
		Ghain = 65229,
		Feh = 65233,
		Qaf = 65237,
		Kaf = 65241,
		Lam = 65245,
		Meem = 65249,
		Noon = 65253,
		Heh = 65257,
		Waw = 65261,
		AlefMaksura = 65263,
		Yeh = 65265,
		FarsiYeh = 64508,
		Peh = 64342,
		TCheh = 64378,
		Jeh = 64394,
		Keheh = 64398,
		Gaf = 64402
	}
	public enum EnglishNumbers
	{
		Zero = 48,
		One,
		Two,
		Three,
		Four,
		Five,
		Six,
		Seven,
		Eight,
		Nine
	}
	public enum FarsiNumbers
	{
		Zero = 1776,
		One,
		Two,
		Three,
		Four,
		Five,
		Six,
		Seven,
		Eight,
		Nine
	}
	public enum HinduNumbers
	{
		Zero = 1632,
		One,
		Two,
		Three,
		Four,
		Five,
		Six,
		Seven,
		Eight,
		Nine
	}
	public enum SpecialCharacters
	{
		ZeroWidthNoJoiner = 8204
	}
	public enum TashkeelCharacters
	{
		Fathan = 1611,
		Dammatan = 1612,
		Kasratan = 1613,
		Fatha = 1614,
		Damma = 1615,
		Kasra = 1616,
		Shadda = 1617,
		Sukun = 1618,
		MaddahAbove = 1619,
		SuperscriptAlef = 1648,
		ShaddaWithDammatanIsolatedForm = 64606,
		ShaddaWithKasratanIsolatedForm = 64607,
		ShaddaWithFathaIsolatedForm = 64608,
		ShaddaWithDammaIsolatedForm = 64609,
		ShaddaWithKasraIsolatedForm = 64610,
		ShaddaWithSuperscriptAlefIsolatedForm = 64611
	}
}

plugins/Jukebox/SubtitlesParser.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using SubtitlesParser.Classes.Utils;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("AlexPoint")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Universal subtitles parser")]
[assembly: AssemblyFileVersion("1.5.1.0")]
[assembly: AssemblyInformationalVersion("1.5.1")]
[assembly: AssemblyProduct("SubtitlesParser")]
[assembly: AssemblyTitle("SubtitlesParser")]
[assembly: AssemblyVersion("1.5.1.0")]
namespace SubtitlesParser.Classes
{
	internal static class StreamHelpers
	{
		public static Stream CopyStream(Stream inputStream)
		{
			MemoryStream memoryStream = new MemoryStream();
			int num;
			do
			{
				byte[] buffer = new byte[1024];
				num = inputStream.Read(buffer, 0, 1024);
				memoryStream.Write(buffer, 0, num);
			}
			while (inputStream.CanRead && num > 0);
			memoryStream.ToArray();
			return memoryStream;
		}
	}
	public class SubtitleItem
	{
		public int StartTime { get; set; }

		public int EndTime { get; set; }

		public List<string> Lines { get; set; }

		public List<string> PlaintextLines { get; set; }

		public SubtitleItem()
		{
			Lines = new List<string>();
			PlaintextLines = new List<string>();
		}

		public override string ToString()
		{
			TimeSpan timeSpan = new TimeSpan(0, 0, 0, 0, StartTime);
			TimeSpan timeSpan2 = new TimeSpan(0, 0, 0, 0, EndTime);
			return string.Format("{0} --> {1}: {2}", timeSpan.ToString("G"), timeSpan2.ToString("G"), string.Join(Environment.NewLine, Lines));
		}
	}
	public class SubtitlesFormat
	{
		public static SubtitlesFormat SubRipFormat = new SubtitlesFormat
		{
			Name = "SubRip",
			Extension = "\\.srt"
		};

		public static SubtitlesFormat MicroDvdFormat = new SubtitlesFormat
		{
			Name = "MicroDvd",
			Extension = "\\.sub"
		};

		public static SubtitlesFormat SubViewerFormat = new SubtitlesFormat
		{
			Name = "SubViewer",
			Extension = "\\.sub"
		};

		public static SubtitlesFormat SubStationAlphaFormat = new SubtitlesFormat
		{
			Name = "SubStationAlpha",
			Extension = "\\.ssa"
		};

		public static SubtitlesFormat TtmlFormat = new SubtitlesFormat
		{
			Name = "TTML",
			Extension = "\\.ttml"
		};

		public static SubtitlesFormat WebVttFormat = new SubtitlesFormat
		{
			Name = "WebVTT",
			Extension = "\\.vtt"
		};

		public static SubtitlesFormat YoutubeXmlFormat = new SubtitlesFormat
		{
			Name = "YoutubeXml"
		};

		public static List<SubtitlesFormat> SupportedSubtitlesFormats = new List<SubtitlesFormat> { SubRipFormat, MicroDvdFormat, SubViewerFormat, SubStationAlphaFormat, TtmlFormat, WebVttFormat, YoutubeXmlFormat };

		public string Name { get; set; }

		public string Extension { get; set; }

		private SubtitlesFormat()
		{
		}
	}
}
namespace SubtitlesParser.Classes.Writers
{
	public interface ISubtitlesWriter
	{
		void WriteStream(Stream stream, IEnumerable<SubtitleItem> subtitleItems, bool includeFormatting = true);

		Task WriteStreamAsync(Stream stream, IEnumerable<SubtitleItem> subtitleItems, bool includeFormatting = true);
	}
	public class SrtWriter : ISubtitlesWriter
	{
		private IEnumerable<string> SubtitleItemToSubtitleEntry(SubtitleItem subtitleItem, int subtitleEntryNumber, bool includeFormatting)
		{
			List<string> list = new List<string>();
			list.Add(subtitleEntryNumber.ToString());
			list.Add(formatTimecodeLine());
			if (!includeFormatting && subtitleItem.PlaintextLines != null)
			{
				list.AddRange(subtitleItem.PlaintextLines);
			}
			else
			{
				list.AddRange(subtitleItem.Lines);
			}
			return list;
			string formatTimecodeLine()
			{
				TimeSpan timeSpan = TimeSpan.FromMilliseconds(subtitleItem.StartTime);
				TimeSpan timeSpan2 = TimeSpan.FromMilliseconds(subtitleItem.EndTime);
				return $"{timeSpan:hh\\:mm\\:ss\\,fff} --> {timeSpan2:hh\\:mm\\:ss\\,fff}";
			}
		}

		public void WriteStream(Stream stream, IEnumerable<SubtitleItem> subtitleItems, bool includeFormatting = true)
		{
			using TextWriter textWriter = new StreamWriter(stream);
			List<SubtitleItem> list = subtitleItems.ToList();
			for (int i = 0; i < list.Count; i++)
			{
				SubtitleItem subtitleItem = list[i];
				IEnumerable<string> enumerable = SubtitleItemToSubtitleEntry(subtitleItem, i + 1, includeFormatting);
				foreach (string item in enumerable)
				{
					textWriter.WriteLine(item);
				}
				textWriter.WriteLine();
			}
		}

		public async Task WriteStreamAsync(Stream stream, IEnumerable<SubtitleItem> subtitleItems, bool includeFormatting = true)
		{
			await using TextWriter writer = new StreamWriter(stream);
			List<SubtitleItem> items = subtitleItems.ToList();
			for (int i = 0; i < items.Count; i++)
			{
				SubtitleItem subtitleItem = items[i];
				IEnumerable<string> lines = SubtitleItemToSubtitleEntry(subtitleItem, i + 1, includeFormatting);
				foreach (string line in lines)
				{
					await writer.WriteLineAsync(line);
				}
				await writer.WriteLineAsync();
			}
		}
	}
	public class SsaWriter : ISubtitlesWriter
	{
		private void WriteHeader(TextWriter writer, SsaWrapStyle wrapStyle = SsaWrapStyle.None)
		{
			writer.WriteLine("[Script Info]");
			writer.WriteLine($"{';'} Script generated by SubtitlesParser v{GetType().Assembly.GetName().Version}");
			writer.WriteLine($"{';'} https://github.com/AlexPoint/SubtitlesParser");
			writer.WriteLine("ScriptType: v4.00");
			writer.WriteLine(string.Format("{0}{1}", "WrapStyle: ", wrapStyle));
			writer.WriteLine();
			writer.Flush();
		}

		private async Task WriteHeaderAsync(TextWriter writer, SsaWrapStyle wrapStyle = SsaWrapStyle.None)
		{
			await writer.WriteLineAsync("[Script Info]");
			await writer.WriteLineAsync($"{';'} Script generated by SubtitlesParser v{GetType().Assembly.GetName().Version}");
			await writer.WriteLineAsync($"{';'} https://github.com/AlexPoint/SubtitlesParser");
			await writer.WriteLineAsync("ScriptType: v4.00");
			await writer.WriteLineAsync(string.Format("{0}{1}", "WrapStyle: ", wrapStyle));
			await writer.WriteLineAsync();
			await writer.FlushAsync();
		}

		private string SubtitleItemToDialogueLine(SubtitleItem subtitleItem, bool includeFormatting)
		{
			string[] array = new string[10]
			{
				"0",
				TimeSpan.FromMilliseconds(subtitleItem.StartTime).ToString("h\\:mm\\:ss\\.fff"),
				TimeSpan.FromMilliseconds(subtitleItem.EndTime).ToString("h\\:mm\\:ss\\.fff"),
				null,
				null,
				"0",
				"0",
				"0",
				null,
				null
			};
			List<string> source = ((!includeFormatting && subtitleItem.PlaintextLines != null) ? subtitleItem.PlaintextLines : subtitleItem.Lines);
			array[9] = source.Aggregate(string.Empty, (string current, string line) => current + line + "\\N").TrimEnd('\\', 'N');
			StringBuilder stringBuilder = new StringBuilder("Dialogue: ");
			return stringBuilder.AppendJoin(',', array).ToString();
		}

		public void WriteStream(Stream stream, IEnumerable<SubtitleItem> subtitleItems, bool includeFormatting = true)
		{
			using TextWriter textWriter = new StreamWriter(stream);
			WriteHeader(textWriter);
			textWriter.WriteLine("[Events]");
			textWriter.WriteLine("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
			foreach (SubtitleItem subtitleItem in subtitleItems)
			{
				textWriter.WriteLine(SubtitleItemToDialogueLine(subtitleItem, includeFormatting));
			}
		}

		public async Task WriteStreamAsync(Stream stream, IEnumerable<SubtitleItem> subtitleItems, bool includeFormatting = true)
		{
			await using TextWriter writer = new StreamWriter(stream);
			await WriteHeaderAsync(writer);
			await writer.WriteLineAsync("[Events]");
			await writer.WriteLineAsync("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
			foreach (SubtitleItem item in subtitleItems)
			{
				await writer.WriteLineAsync(SubtitleItemToDialogueLine(item, includeFormatting));
			}
		}
	}
}
namespace SubtitlesParser.Classes.Utils
{
	public static class SsaFormatConstants
	{
		public const string SCRIPT_INFO_LINE = "[Script Info]";

		public const string EVENT_LINE = "[Events]";

		public const char SEPARATOR = ',';

		public const char COMMENT = ';';

		public const string WRAP_STYLE_PREFIX = "WrapStyle: ";

		public const string DIALOGUE_PREFIX = "Dialogue: ";

		public const string START_COLUMN = "Start";

		public const string END_COLUMN = "End";

		public const string TEXT_COLUMN = "Text";
	}
	public enum SsaWrapStyle
	{
		Smart,
		EndOfLine,
		None,
		SmartWideLowerLine
	}
	public static class SsaWrapStyleExtensions
	{
		public static SsaWrapStyle FromString(this string rawString)
		{
			int result;
			return (!int.TryParse(rawString, out result)) ? SsaWrapStyle.None : result.FromInt();
		}

		public static SsaWrapStyle FromInt(this int rawInt)
		{
			if (1 == 0)
			{
			}
			SsaWrapStyle result = rawInt switch
			{
				0 => SsaWrapStyle.Smart, 
				1 => SsaWrapStyle.EndOfLine, 
				2 => SsaWrapStyle.None, 
				3 => SsaWrapStyle.SmartWideLowerLine, 
				_ => SsaWrapStyle.None, 
			};
			if (1 == 0)
			{
			}
			return result;
		}
	}
}
namespace SubtitlesParser.Classes.Parsers
{
	public interface ISubtitlesParser
	{
		List<SubtitleItem> ParseStream(Stream stream, Encoding encoding);
	}
	public class MicroDvdParser : ISubtitlesParser
	{
		private readonly float defaultFrameRate = 25f;

		private readonly char[] _lineSeparators = new char[1] { '|' };

		private const string LineRegex = "^[{\\[](-?\\d+)[}\\]][{\\[](-?\\d+)[}\\]](.*)";

		public MicroDvdParser()
		{
		}

		public MicroDvdParser(float defaultFrameRate)
		{
			this.defaultFrameRate = defaultFrameRate;
		}

		public List<SubtitleItem> ParseStream(Stream subStream, Encoding encoding)
		{
			if (!subStream.CanRead || !subStream.CanSeek)
			{
				string message = $"Stream must be seekable and readable in a subtitles parser. Operation interrupted; isSeekable: {subStream.CanSeek} - isReadable: {subStream.CanSeek}";
				throw new ArgumentException(message);
			}
			subStream.Position = 0L;
			StreamReader streamReader = new StreamReader(subStream, encoding, detectEncodingFromByteOrderMarks: true);
			List<SubtitleItem> list = new List<SubtitleItem>();
			string text = streamReader.ReadLine();
			while (text != null && !IsMicroDvdLine(text))
			{
				text = streamReader.ReadLine();
			}
			if (text != null)
			{
				SubtitleItem subtitleItem = ParseLine(text, defaultFrameRate);
				float frameRate;
				if (subtitleItem.Lines != null && subtitleItem.Lines.Any())
				{
					if (!TryExtractFrameRate(subtitleItem.Lines[0], out frameRate))
					{
						Console.WriteLine("Couldn't extract frame rate of sub file with first line {0}. We use the default frame rate: {1}", text, defaultFrameRate);
						frameRate = defaultFrameRate;
						list.Add(subtitleItem);
					}
				}
				else
				{
					frameRate = defaultFrameRate;
				}
				for (text = streamReader.ReadLine(); text != null; text = streamReader.ReadLine())
				{
					if (!string.IsNullOrEmpty(text))
					{
						SubtitleItem item = ParseLine(text, frameRate);
						list.Add(item);
					}
				}
			}
			if (list.Any())
			{
				return list;
			}
			throw new ArgumentException("Stream is not in a valid MicroDVD format");
		}

		private bool IsMicroDvdLine(string line)
		{
			return Regex.IsMatch(line, "^[{\\[](-?\\d+)[}\\]][{\\[](-?\\d+)[}\\]](.*)");
		}

		private SubtitleItem ParseLine(string line, float frameRate)
		{
			Match match = Regex.Match(line, "^[{\\[](-?\\d+)[}\\]][{\\[](-?\\d+)[}\\]](.*)");
			if (match.Success && match.Groups.Count > 2)
			{
				string value = match.Groups[1].Value;
				int startTime = (int)(1000.0 * double.Parse(value) / (double)frameRate);
				string value2 = match.Groups[2].Value;
				int endTime = (int)(1000.0 * double.Parse(value2) / (double)frameRate);
				string value3 = match.Groups[match.Groups.Count - 1].Value;
				string[] source = value3.Split(_lineSeparators);
				List<string> lines = source.Where((string l) => !string.IsNullOrEmpty(l)).ToList();
				return new SubtitleItem
				{
					Lines = lines,
					StartTime = startTime,
					EndTime = endTime
				};
			}
			string message = $"The subtitle file line {line} is not in the micro dvd format. We stop the process.";
			throw new InvalidDataException(message);
		}

		private bool TryExtractFrameRate(string text, out float frameRate)
		{
			if (!string.IsNullOrEmpty(text))
			{
				return float.TryParse(text, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out frameRate);
			}
			frameRate = defaultFrameRate;
			return false;
		}
	}
	public class SrtParser : ISubtitlesParser
	{
		private readonly string[] _delimiters = new string[3] { "-->", "- >", "->" };

		public List<SubtitleItem> ParseStream(Stream srtStream, Encoding encoding)
		{
			if (!srtStream.CanRead || !srtStream.CanSeek)
			{
				string message = $"Stream must be seekable and readable in a subtitles parser. Operation interrupted; isSeekable: {srtStream.CanSeek} - isReadable: {srtStream.CanSeek}";
				throw new ArgumentException(message);
			}
			srtStream.Position = 0L;
			StreamReader reader = new StreamReader(srtStream, encoding, detectEncodingFromByteOrderMarks: true);
			List<SubtitleItem> list = new List<SubtitleItem>();
			List<string> list2 = GetSrtSubTitleParts(reader).ToList();
			if (list2.Any())
			{
				foreach (string item in list2)
				{
					List<string> list3 = (from s in item.Split(new string[1] { Environment.NewLine }, StringSplitOptions.None)
						select s.Trim() into l
						where !string.IsNullOrEmpty(l)
						select l).ToList();
					SubtitleItem subtitleItem = new SubtitleItem();
					foreach (string item2 in list3)
					{
						if (subtitleItem.StartTime == 0 && subtitleItem.EndTime == 0)
						{
							if (TryParseTimecodeLine(item2, out var startTc, out var endTc))
							{
								subtitleItem.StartTime = startTc;
								subtitleItem.EndTime = endTc;
							}
						}
						else
						{
							subtitleItem.Lines.Add(item2);
							subtitleItem.PlaintextLines.Add(Regex.Replace(item2, "\\{.*?\\}|<.*?>", string.Empty));
						}
					}
					if ((subtitleItem.StartTime != 0 || subtitleItem.EndTime != 0) && subtitleItem.Lines.Any())
					{
						list.Add(subtitleItem);
					}
				}
				if (list.Any())
				{
					return list;
				}
				throw new ArgumentException("Stream is not in a valid Srt format");
			}
			throw new FormatException("Parsing as srt returned no srt part.");
		}

		private IEnumerable<string> GetSrtSubTitleParts(TextReader reader)
		{
			StringBuilder sb = new StringBuilder();
			while (true)
			{
				string text;
				string line = (text = reader.ReadLine());
				if (text == null)
				{
					break;
				}
				if (string.IsNullOrEmpty(line.Trim()))
				{
					string res = sb.ToString().TrimEnd();
					if (!string.IsNullOrEmpty(res))
					{
						yield return res;
					}
					sb = new StringBuilder();
				}
				else
				{
					sb.AppendLine(line);
				}
			}
			if (sb.Length > 0)
			{
				yield return sb.ToString();
			}
		}

		private bool TryParseTimecodeLine(string line, out int startTc, out int endTc)
		{
			string[] array = line.Split(_delimiters, StringSplitOptions.None);
			if (array.Length != 2)
			{
				startTc = -1;
				endTc = -1;
				return false;
			}
			startTc = ParseSrtTimecode(array[0]);
			endTc = ParseSrtTimecode(array[1]);
			return true;
		}

		private static int ParseSrtTimecode(string s)
		{
			Match match = Regex.Match(s, "[0-9]+:[0-9]+:[0-9]+([,\\.][0-9]+)?");
			if (match.Success)
			{
				s = match.Value;
				if (TimeSpan.TryParse(s.Replace(',', '.'), out var result))
				{
					return (int)result.TotalMilliseconds;
				}
			}
			return -1;
		}
	}
	public class SsaParser : ISubtitlesParser
	{
		public List<SubtitleItem> ParseStream(Stream ssaStream, Encoding encoding)
		{
			if (!ssaStream.CanRead || !ssaStream.CanSeek)
			{
				string message = $"Stream must be seekable and readable in a subtitles parser. Operation interrupted; isSeekable: {ssaStream.CanSeek} - isReadable: {ssaStream.CanRead}";
				throw new ArgumentException(message);
			}
			ssaStream.Position = 0L;
			StreamReader streamReader = new StreamReader(ssaStream, encoding, detectEncodingFromByteOrderMarks: true);
			SsaWrapStyle ssaWrapStyle = SsaWrapStyle.None;
			string text = streamReader.ReadLine();
			int num = 1;
			while (text != null && text != "[Events]")
			{
				if (text.StartsWith("WrapStyle: "))
				{
					ssaWrapStyle = text.Split(':')[1].TrimStart().FromString();
				}
				text = streamReader.ReadLine();
				num++;
			}
			if (text != null)
			{
				string text2 = streamReader.ReadLine();
				if (!string.IsNullOrEmpty(text2))
				{
					List<string> list = (from head in text2.Split(',')
						select head.Trim()).ToList();
					int num2 = list.IndexOf("Start");
					int num3 = list.IndexOf("End");
					int num4 = list.IndexOf("Text");
					if (num2 > 0 && num3 > 0 && num4 > 0)
					{
						List<SubtitleItem> list2 = new List<SubtitleItem>();
						for (text = streamReader.ReadLine(); text != null; text = streamReader.ReadLine())
						{
							if (!string.IsNullOrEmpty(text))
							{
								string[] array = text.Split(',');
								string s = array[num2];
								string s2 = array[num3];
								string text3 = string.Join(",", array.Skip(num4));
								int num5 = ParseSsaTimecode(s);
								int num6 = ParseSsaTimecode(s2);
								if (num5 > 0 && num6 > 0 && !string.IsNullOrEmpty(text3))
								{
									List<string> source;
									switch (ssaWrapStyle)
									{
									case SsaWrapStyle.Smart:
									case SsaWrapStyle.EndOfLine:
									case SsaWrapStyle.SmartWideLowerLine:
										source = text3.Split("\\N").ToList();
										break;
									case SsaWrapStyle.None:
										source = Regex.Split(text3, "(?:\\\\n)|(?:\\\\N)").ToList();
										break;
									default:
										throw new ArgumentOutOfRangeException();
									}
									source = source.Select((string line) => line.TrimStart()).ToList();
									SubtitleItem item = new SubtitleItem
									{
										StartTime = num5,
										EndTime = num6,
										Lines = source,
										PlaintextLines = source.Select((string subtitleLine) => Regex.Replace(subtitleLine, "\\{.*?\\}", string.Empty)).ToList()
									};
									list2.Add(item);
								}
							}
						}
						if (list2.Any())
						{
							return list2;
						}
						throw new ArgumentException("Stream is not in a valid Ssa format");
					}
					string message2 = string.Format("Couldn't find all the necessary columns headers ({0}, {1}, {2}) in header line {3}", "Start", "End", "Text", text2);
					throw new ArgumentException(message2);
				}
				string message3 = $"The header line after the line '{text}' was null -> no need to continue parsing";
				throw new ArgumentException(message3);
			}
			string message4 = string.Format("We reached line '{0}' with line number #{1} without finding to Event section ({2})", text, num, "[Events]");
			throw new ArgumentException(message4);
		}

		private int ParseSsaTimecode(string s)
		{
			if (TimeSpan.TryParse(s, out var result))
			{
				return (int)result.TotalMilliseconds;
			}
			return -1;
		}
	}
	public class SubParser
	{
		private readonly Dictionary<SubtitlesFormat, ISubtitlesParser> _subFormatToParser = new Dictionary<SubtitlesFormat, ISubtitlesParser>
		{
			{
				SubtitlesFormat.SubRipFormat,
				new SrtParser()
			},
			{
				SubtitlesFormat.MicroDvdFormat,
				new MicroDvdParser()
			},
			{
				SubtitlesFormat.SubViewerFormat,
				new SubViewerParser()
			},
			{
				SubtitlesFormat.SubStationAlphaFormat,
				new SsaParser()
			},
			{
				SubtitlesFormat.TtmlFormat,
				new TtmlParser()
			},
			{
				SubtitlesFormat.WebVttFormat,
				new VttParser()
			},
			{
				SubtitlesFormat.YoutubeXmlFormat,
				new YtXmlFormatParser()
			}
		};

		public SubtitlesFormat GetMostLikelyFormat(string fileName)
		{
			string extension = Path.GetExtension(fileName);
			if (!string.IsNullOrEmpty(extension))
			{
				foreach (SubtitlesFormat supportedSubtitlesFormat in SubtitlesFormat.SupportedSubtitlesFormats)
				{
					if (supportedSubtitlesFormat.Extension != null && Regex.IsMatch(extension, supportedSubtitlesFormat.Extension, RegexOptions.IgnoreCase))
					{
						return supportedSubtitlesFormat;
					}
				}
			}
			return null;
		}

		public List<SubtitleItem> ParseStream(Stream stream)
		{
			return ParseStream(stream, Encoding.UTF8);
		}

		public List<SubtitleItem> ParseStream(Stream stream, Encoding encoding, SubtitlesFormat subFormat = null)
		{
			Dictionary<SubtitlesFormat, ISubtitlesParser> subFormatDictionary = ((subFormat != null) ? _subFormatToParser.OrderBy((KeyValuePair<SubtitlesFormat, ISubtitlesParser> dic) => Math.Abs(string.Compare(dic.Key.Name, subFormat.Name, StringComparison.Ordinal))).ToDictionary((KeyValuePair<SubtitlesFormat, ISubtitlesParser> entry) => entry.Key, (KeyValuePair<SubtitlesFormat, ISubtitlesParser> entry) => entry.Value) : _subFormatToParser);
			return ParseStream(stream, encoding, subFormatDictionary);
		}

		public List<SubtitleItem> ParseStream(Stream stream, Encoding encoding, Dictionary<SubtitlesFormat, ISubtitlesParser> subFormatDictionary)
		{
			if (!stream.CanRead)
			{
				throw new ArgumentException("Cannot parse a non-readable stream");
			}
			Stream stream2 = stream;
			if (!stream.CanSeek)
			{
				stream2 = StreamHelpers.CopyStream(stream);
				stream2.Seek(0L, SeekOrigin.Begin);
			}
			subFormatDictionary = subFormatDictionary ?? _subFormatToParser;
			foreach (KeyValuePair<SubtitlesFormat, ISubtitlesParser> item in subFormatDictionary)
			{
				try
				{
					ISubtitlesParser value = item.Value;
					return value.ParseStream(stream2, encoding);
				}
				catch (Exception)
				{
				}
			}
			string arg = LogFirstCharactersOfStream(stream, 500, encoding);
			string message = $"All the subtitles parsers failed to parse the following stream:{arg}";
			throw new ArgumentException(message);
		}

		private string LogFirstCharactersOfStream(Stream stream, int nbOfCharactersToPrint, Encoding encoding)
		{
			string text = "";
			if (stream.CanRead)
			{
				if (stream.CanSeek)
				{
					stream.Position = 0L;
				}
				StreamReader streamReader = new StreamReader(stream, encoding, detectEncodingFromByteOrderMarks: true);
				char[] array = new char[nbOfCharactersToPrint];
				streamReader.ReadBlock(array, 0, nbOfCharactersToPrint);
				return string.Format("Parsing of subtitle stream failed. Beginning of sub stream:\n{0}", string.Join("", array));
			}
			return $"Tried to log the first {nbOfCharactersToPrint} characters of a closed stream";
		}
	}
	public class SubViewerParser : ISubtitlesParser
	{
		private const string FirstLine = "[INFORMATION]";

		private const short MaxLineNumberForItems = 20;

		private readonly Regex _timestampRegex = new Regex("\\d{2}:\\d{2}:\\d{2}\\.\\d{2},\\d{2}:\\d{2}:\\d{2}\\.\\d{2}", RegexOptions.Compiled);

		private const char TimecodeSeparator = ',';

		public List<SubtitleItem> ParseStream(Stream subStream, Encoding encoding)
		{
			subStream.Position = 0L;
			StreamReader streamReader = new StreamReader(subStream, encoding, detectEncodingFromByteOrderMarks: true);
			string text = streamReader.ReadLine();
			if (text == "[INFORMATION]")
			{
				string text2 = streamReader.ReadLine();
				int num = 2;
				while (text2 != null && num <= 20 && !IsTimestampLine(text2))
				{
					text2 = streamReader.ReadLine();
					num++;
				}
				if (text2 != null && num <= 20 && IsTimestampLine(text2))
				{
					List<SubtitleItem> list = new List<SubtitleItem>();
					string line = text2;
					List<string> list2 = new List<string>();
					while (text2 != null)
					{
						text2 = streamReader.ReadLine();
						if (IsTimestampLine(text2))
						{
							Tuple<int, int> tuple = ParseTimecodeLine(line);
							int item = tuple.Item1;
							int item2 = tuple.Item2;
							if (item > 0 && item2 > 0 && list2.Any())
							{
								list.Add(new SubtitleItem
								{
									StartTime = item,
									EndTime = item2,
									Lines = list2
								});
							}
							line = text2;
							list2 = new List<string>();
						}
						else if (!string.IsNullOrEmpty(text2))
						{
							list2.Add(text2);
						}
					}
					Tuple<int, int> tuple2 = ParseTimecodeLine(line);
					int item3 = tuple2.Item1;
					int item4 = tuple2.Item2;
					if (item3 > 0 && item4 > 0 && list2.Any())
					{
						list.Add(new SubtitleItem
						{
							StartTime = item3,
							EndTime = item4,
							Lines = list2
						});
					}
					if (list.Any())
					{
						return list;
					}
					throw new ArgumentException("Stream is not in a valid SubViewer format");
				}
				string message = $"Couldn't find the first timestamp line in the current sub file. Last line read: '{text2}', line number #{num}";
				throw new ArgumentException(message);
			}
			throw new ArgumentException("Stream is not in a valid SubViewer format");
		}

		private Tuple<int, int> ParseTimecodeLine(string line)
		{
			string[] array = line.Split(',');
			if (array.Length == 2)
			{
				int item = ParseTimecode(array[0]);
				int item2 = ParseTimecode(array[1]);
				return new Tuple<int, int>(item, item2);
			}
			string message = $"Couldn't parse the timecodes in line '{line}'";
			throw new ArgumentException(message);
		}

		private int ParseTimecode(string s)
		{
			if (TimeSpan.TryParse(s, out var result))
			{
				return (int)result.TotalMilliseconds;
			}
			return -1;
		}

		private bool IsTimestampLine(string line)
		{
			if (string.IsNullOrEmpty(line))
			{
				return false;
			}
			return _timestampRegex.IsMatch(line);
		}
	}
	public class TtmlParser : ISubtitlesParser
	{
		public List<SubtitleItem> ParseStream(Stream xmlStream, Encoding encoding)
		{
			xmlStream.Position = 0L;
			List<SubtitleItem> list = new List<SubtitleItem>();
			XElement xElement = XElement.Load(xmlStream);
			XNamespace xNamespace = xElement.GetNamespaceOfPrefix("tt") ?? xElement.GetDefaultNamespace();
			List<XElement> list2 = xElement.Descendants(xNamespace + "p").ToList();
			foreach (XElement item2 in list2)
			{
				try
				{
					XmlReader xmlReader = item2.CreateReader();
					xmlReader.MoveToContent();
					string s = item2.Attribute("begin").Value.Replace("t", "");
					long num = ParseTimecode(s);
					string s2 = item2.Attribute("end").Value.Replace("t", "");
					long num2 = ParseTimecode(s2);
					string item = xmlReader.ReadInnerXml().Replace("<tt:", "<").Replace("</tt:", "</")
						.Replace($" xmlns:tt=\"{xNamespace}\"", "")
						.Replace($" xmlns=\"{xNamespace}\"", "");
					list.Add(new SubtitleItem
					{
						StartTime = (int)num,
						EndTime = (int)num2,
						Lines = new List<string> { item }
					});
				}
				catch (Exception arg)
				{
					Console.WriteLine("Exception raised when parsing xml node {0}: {1}", item2, arg);
				}
			}
			if (list.Any())
			{
				return list;
			}
			throw new ArgumentException("Stream is not in a valid TTML format, or represents empty subtitles");
		}

		private static long ParseTimecode(string s)
		{
			if (TimeSpan.TryParse(s, out var result))
			{
				return (long)result.TotalMilliseconds;
			}
			if (long.TryParse(s.TrimEnd('t'), out var result2))
			{
				return result2 / 10000;
			}
			return -1L;
		}
	}
	public class VttParser : ISubtitlesParser
	{
		private readonly string[] _delimiters = new string[3] { "-->", "- >", "->" };

		public List<SubtitleItem> ParseStream(Stream vttStream, Encoding encoding)
		{
			if (!vttStream.CanRead || !vttStream.CanSeek)
			{
				string message = $"Stream must be seekable and readable in a subtitles parser. Operation interrupted; isSeekable: {vttStream.CanSeek} - isReadable: {vttStream.CanSeek}";
				throw new ArgumentException(message);
			}
			vttStream.Position = 0L;
			StreamReader reader = new StreamReader(vttStream, encoding, detectEncodingFromByteOrderMarks: true);
			List<SubtitleItem> list = new List<SubtitleItem>();
			List<string> list2 = GetVttSubTitleParts(reader).ToList();
			if (list2.Any())
			{
				foreach (string item in list2)
				{
					List<string> list3 = (from s in item.Split(new string[1] { Environment.NewLine }, StringSplitOptions.None)
						select s.Trim() into l
						where !string.IsNullOrEmpty(l)
						select l).ToList();
					SubtitleItem subtitleItem = new SubtitleItem();
					foreach (string item2 in list3)
					{
						if (subtitleItem.StartTime == 0 && subtitleItem.EndTime == 0)
						{
							if (TryParseTimecodeLine(item2, out var startTc, out var endTc))
							{
								subtitleItem.StartTime = startTc;
								subtitleItem.EndTime = endTc;
							}
						}
						else
						{
							subtitleItem.Lines.Add(item2);
						}
					}
					if ((subtitleItem.StartTime != 0 || subtitleItem.EndTime != 0) && subtitleItem.Lines.Any())
					{
						list.Add(subtitleItem);
					}
				}
				return list;
			}
			throw new FormatException("Parsing as VTT returned no VTT part.");
		}

		private IEnumerable<string> GetVttSubTitleParts(TextReader reader)
		{
			StringBuilder sb = new StringBuilder();
			while (true)
			{
				string text;
				string line = (text = reader.ReadLine());
				if (text == null)
				{
					break;
				}
				if (string.IsNullOrEmpty(line.Trim()))
				{
					string res = sb.ToString().TrimEnd();
					if (!string.IsNullOrEmpty(res))
					{
						yield return res;
					}
					sb = new StringBuilder();
				}
				else
				{
					sb.AppendLine(line);
				}
			}
			if (sb.Length > 0)
			{
				yield return sb.ToString();
			}
		}

		private bool TryParseTimecodeLine(string line, out int startTc, out int endTc)
		{
			string[] array = line.Split(_delimiters, StringSplitOptions.None);
			if (array.Length != 2)
			{
				startTc = -1;
				endTc = -1;
				return false;
			}
			startTc = ParseVttTimecode(array[0]);
			endTc = ParseVttTimecode(array[1]);
			return true;
		}

		private int ParseVttTimecode(string s)
		{
			int hours = 0;
			int minutes = 0;
			int seconds = 0;
			int num = -1;
			Match match = Regex.Match(s, "([0-9]+):([0-9]+):([0-9]+)[,\\.]([0-9]+)");
			if (match.Success)
			{
				hours = int.Parse(match.Groups[1].Value);
				minutes = int.Parse(match.Groups[2].Value);
				seconds = int.Parse(match.Groups[3].Value);
				num = int.Parse(match.Groups[4].Value);
			}
			else
			{
				match = Regex.Match(s, "([0-9]+):([0-9]+)[,\\.]([0-9]+)");
				if (match.Success)
				{
					minutes = int.Parse(match.Groups[1].Value);
					seconds = int.Parse(match.Groups[2].Value);
					num = int.Parse(match.Groups[3].Value);
				}
			}
			if (num >= 0)
			{
				return (int)new TimeSpan(0, hours, minutes, seconds, num).TotalMilliseconds;
			}
			return -1;
		}
	}
	public class YtXmlFormatParser : ISubtitlesParser
	{
		public List<SubtitleItem> ParseStream(Stream xmlStream, Encoding encoding)
		{
			xmlStream.Position = 0L;
			List<SubtitleItem> list = new List<SubtitleItem>();
			XmlDocument xmlDocument = new XmlDocument();
			xmlDocument.Load(xmlStream);
			if (xmlDocument.DocumentElement != null)
			{
				XmlNodeList xmlNodeList = xmlDocument.DocumentElement.SelectNodes("//text");
				if (xmlNodeList != null)
				{
					for (int i = 0; i < xmlNodeList.Count; i++)
					{
						XmlNode xmlNode = xmlNodeList[i];
						try
						{
							string value = xmlNode.Attributes["start"].Value;
							float num = float.Parse(value, CultureInfo.InvariantCulture);
							string value2 = xmlNode.Attributes["dur"].Value;
							float num2 = float.Parse(value2, CultureInfo.InvariantCulture);
							string innerText = xmlNode.InnerText;
							list.Add(new SubtitleItem
							{
								StartTime = (int)(num * 1000f),
								EndTime = (int)((num + num2) * 1000f),
								Lines = new List<string> { innerText }
							});
						}
						catch (Exception arg)
						{
							Console.WriteLine("Exception raised when parsing xml node {0}: {1}", xmlNode, arg);
						}
					}
				}
			}
			if (list.Any())
			{
				return list;
			}
			throw new ArgumentException("Stream is not in a valid Youtube XML format");
		}
	}
}

plugins/Jukebox/TagLibSharp.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Text;
using System.Xml;
using Microsoft.CodeAnalysis;
using TagLib.Aac;
using TagLib.Aiff;
using TagLib.Ape;
using TagLib.Asf;
using TagLib.Audible;
using TagLib.Dsf;
using TagLib.Flac;
using TagLib.Gif;
using TagLib.IFD;
using TagLib.IFD.Entries;
using TagLib.IFD.Makernotes;
using TagLib.IFD.Tags;
using TagLib.IIM;
using TagLib.Id3v1;
using TagLib.Id3v2;
using TagLib.Image;
using TagLib.Image.NoMetadata;
using TagLib.Jpeg;
using TagLib.Matroska;
using TagLib.Mpeg;
using TagLib.Mpeg4;
using TagLib.MusePack;
using TagLib.NonContainer;
using TagLib.Ogg;
using TagLib.Ogg.Codecs;
using TagLib.Png;
using TagLib.Riff;
using TagLib.Tiff;
using TagLib.Tiff.Arw;
using TagLib.Tiff.Cr2;
using TagLib.Tiff.Dng;
using TagLib.Tiff.Nef;
using TagLib.Tiff.Pef;
using TagLib.Tiff.Rw2;
using TagLib.WavPack;
using TagLib.Xmp;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("Brian Nickel, Gabriel Burt, Stephen Shaw, etc")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2006-2007 Brian Nickel.  Copyright (c) 2009-2020 Other contributors")]
[assembly: AssemblyDescription("A library for for reading and writing metadata in media files, including video, audio, and photo formats.")]
[assembly: AssemblyFileVersion("2.3.0.0")]
[assembly: AssemblyInformationalVersion("2.3.0.0")]
[assembly: AssemblyProduct("TagLib#")]
[assembly: AssemblyTitle("TagLibSharp")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/mono/taglib-sharp")]
[assembly: AssemblyVersion("2.3.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace TagLib
{
	public enum StringType
	{
		Latin1,
		UTF16,
		UTF16BE,
		UTF8,
		UTF16LE
	}
	public class ByteVector : IList<byte>, ICollection<byte>, IEnumerable<byte>, IEnumerable, IComparable<ByteVector>
	{
		private static readonly uint[] crc_table = new uint[256]
		{
			0u, 79764919u, 159529838u, 222504665u, 319059676u, 398814059u, 445009330u, 507990021u, 638119352u, 583659535u,
			797628118u, 726387553u, 890018660u, 835552979u, 1015980042u, 944750013u, 1276238704u, 1221641927u, 1167319070u, 1095957929u,
			1595256236u, 1540665371u, 1452775106u, 1381403509u, 1780037320u, 1859660671u, 1671105958u, 1733955601u, 2031960084u, 2111593891u,
			1889500026u, 1952343757u, 2552477408u, 2632100695u, 2443283854u, 2506133561u, 2334638140u, 2414271883u, 2191915858u, 2254759653u,
			3190512472u, 3135915759u, 3081330742u, 3009969537u, 2905550212u, 2850959411u, 2762807018u, 2691435357u, 3560074640u, 3505614887u,
			3719321342u, 3648080713u, 3342211916u, 3287746299u, 3467911202u, 3396681109u, 4063920168u, 4143685023u, 4223187782u, 4286162673u,
			3779000052u, 3858754371u, 3904687514u, 3967668269u, 881225847u, 809987520u, 1023691545u, 969234094u, 662832811u, 591600412u,
			771767749u, 717299826u, 311336399u, 374308984u, 453813921u, 533576470u, 25881363u, 88864420u, 134795389u, 214552010u,
			2023205639u, 2086057648u, 1897238633u, 1976864222u, 1804852699u, 1867694188u, 1645340341u, 1724971778u, 1587496639u, 1516133128u,
			1461550545u, 1406951526u, 1302016099u, 1230646740u, 1142491917u, 1087903418u, 2896545431u, 2825181984u, 2770861561u, 2716262478u,
			3215044683u, 3143675388u, 3055782693u, 3001194130u, 2326604591u, 2389456536u, 2200899649u, 2280525302u, 2578013683u, 2640855108u,
			2418763421u, 2498394922u, 3769900519u, 3832873040u, 3912640137u, 3992402750u, 4088425275u, 4151408268u, 4197601365u, 4277358050u,
			3334271071u, 3263032808u, 3476998961u, 3422541446u, 3585640067u, 3514407732u, 3694837229u, 3640369242u, 1762451694u, 1842216281u,
			1619975040u, 1682949687u, 2047383090u, 2127137669u, 1938468188u, 2001449195u, 1325665622u, 1271206113u, 1183200824u, 1111960463u,
			1543535498u, 1489069629u, 1434599652u, 1363369299u, 622672798u, 568075817u, 748617968u, 677256519u, 907627842u, 853037301u,
			1067152940u, 995781531u, 51762726u, 131386257u, 177728840u, 240578815u, 269590778u, 349224269u, 429104020u, 491947555u,
			4046411278u, 4126034873u, 4172115296u, 4234965207u, 3794477266u, 3874110821u, 3953728444u, 4016571915u, 3609705398u, 3555108353u,
			3735388376u, 3664026991u, 3290680682u, 3236090077u, 3449943556u, 3378572211u, 3174993278u, 3120533705u, 3032266256u, 2961025959u,
			2923101090u, 2868635157u, 2813903052u, 2742672763u, 2604032198u, 2683796849u, 2461293480u, 2524268063u, 2284983834u, 2364738477u,
			2175806836u, 2238787779u, 1569362073u, 1498123566u, 1409854455u, 1355396672u, 1317987909u, 1246755826u, 1192025387u, 1137557660u,
			2072149281u, 2135122070u, 1912620623u, 1992383480u, 1753615357u, 1816598090u, 1627664531u, 1707420964u, 295390185u, 358241886u,
			404320391u, 483945776u, 43990325u, 106832002u, 186451547u, 266083308u, 932423249u, 861060070u, 1041341759u, 986742920u,
			613929101u, 542559546u, 756411363u, 701822548u, 3316196985u, 3244833742u, 3425377559u, 3370778784u, 3601682597u, 3530312978u,
			3744426955u, 3689838204u, 3819031489u, 3881883254u, 3928223919u, 4007849240u, 4037393693u, 4100235434u, 4180117107u, 4259748804u,
			2310601993u, 2373574846u, 2151335527u, 2231098320u, 2596047829u, 2659030626u, 2470359227u, 2550115596u, 2947551409u, 2876312838u,
			2788305887u, 2733848168u, 3165939309u, 3094707162u, 3040238851u, 2985771188u
		};

		private static bool use_broken_latin1;

		private static readonly ReadOnlyByteVector td1 = new ReadOnlyByteVector(1);

		private static readonly ReadOnlyByteVector td2 = new ReadOnlyByteVector(2);

		private static Encoding last_utf16_encoding = Encoding.Unicode;

		private readonly List<byte> data = new List<byte>();

		public byte[] Data => data.ToArray();

		public bool IsEmpty => data.Count == 0;

		public uint Checksum
		{
			get
			{
				uint num = 0u;
				foreach (byte datum in data)
				{
					num = (num << 8) ^ crc_table[((num >> 24) & 0xFF) ^ datum];
				}
				return num;
			}
		}

		public static bool UseBrokenLatin1Behavior
		{
			get
			{
				return use_broken_latin1;
			}
			set
			{
				use_broken_latin1 = value;
			}
		}

		public int Count => data.Count;

		public bool IsSynchronized => false;

		public object SyncRoot => this;

		public virtual bool IsReadOnly => false;

		public virtual bool IsFixedSize => false;

		public byte this[int index]
		{
			get
			{
				return data[index];
			}
			set
			{
				if (IsReadOnly)
				{
					throw new NotSupportedException("Cannot edit readonly objects.");
				}
				data[index] = value;
			}
		}

		public ByteVector()
		{
		}

		public ByteVector(ByteVector vector)
		{
			if (vector != null)
			{
				data.AddRange(vector);
			}
		}

		public ByteVector(params byte[] data)
		{
			if (data != null)
			{
				this.data.AddRange(data);
			}
		}

		public ByteVector(byte[] data, int length)
		{
			if (length > data.Length)
			{
				throw new ArgumentOutOfRangeException("length", "Length exceeds size of data.");
			}
			if (length < 0)
			{
				throw new ArgumentOutOfRangeException("length", "Length is less than zero.");
			}
			if (length == data.Length)
			{
				this.data.AddRange(data);
				return;
			}
			byte[] array = new byte[length];
			Array.Copy(data, 0, array, 0, length);
			this.data.AddRange(array);
		}

		public ByteVector(int size)
			: this(size, 0)
		{
		}

		public ByteVector(int size, byte value)
		{
			if (size < 0)
			{
				throw new ArgumentOutOfRangeException("size", "Size is less than zero.");
			}
			if (size != 0)
			{
				byte[] array = new byte[size];
				for (int i = 0; i < size; i++)
				{
					array[i] = value;
				}
				data.AddRange(array);
			}
		}

		public ByteVector Mid(int startIndex, int length)
		{
			if (startIndex < 0 || startIndex > Count)
			{
				throw new ArgumentOutOfRangeException("startIndex");
			}
			if (length < 0 || startIndex + length > Count)
			{
				throw new ArgumentOutOfRangeException("length");
			}
			if (length == 0)
			{
				return new ByteVector();
			}
			if (startIndex + length > data.Count)
			{
				length = data.Count - startIndex;
			}
			byte[] array = new byte[length];
			data.CopyTo(startIndex, array, 0, length);
			return array;
		}

		public ByteVector Mid(int index)
		{
			return Mid(index, Count - index);
		}

		public int Find(ByteVector pattern, int offset, int byteAlign)
		{
			if (pattern == null)
			{
				throw new ArgumentNullException("pattern");
			}
			if (offset < 0)
			{
				throw new ArgumentOutOfRangeException("offset", "offset must be at least 0.");
			}
			if (byteAlign < 1)
			{
				throw new ArgumentOutOfRangeException("byteAlign", "byteAlign must be at least 1.");
			}
			if (pattern.Count > Count - offset)
			{
				return -1;
			}
			if (pattern.Count == 1)
			{
				byte b = pattern[0];
				for (int i = offset; i < data.Count; i += byteAlign)
				{
					if (data[i] == b)
					{
						return i;
					}
				}
				return -1;
			}
			int[] array = new int[256];
			for (int j = 0; j < 256; j++)
			{
				array[j] = pattern.Count;
			}
			for (int k = 0; k < pattern.Count - 1; k++)
			{
				array[pattern[k]] = pattern.Count - k - 1;
			}
			for (int l = pattern.Count - 1 + offset; l < data.Count; l += array[data[l]])
			{
				int num = l;
				int num2 = pattern.Count - 1;
				while (num2 >= 0 && data[num] == pattern[num2])
				{
					num--;
					num2--;
				}
				if (-1 == num2 && (num + 1 - offset) % byteAlign == 0)
				{
					return num + 1;
				}
			}
			return -1;
		}

		public int Find(ByteVector pattern, int offset)
		{
			return Find(pattern, offset, 1);
		}

		public int Find(ByteVector pattern)
		{
			return Find(pattern, 0, 1);
		}

		public int RFind(ByteVector pattern, int offset, int byteAlign)
		{
			if (pattern == null)
			{
				throw new ArgumentNullException("pattern");
			}
			if (offset < 0)
			{
				throw new ArgumentOutOfRangeException("offset");
			}
			if (pattern.Count == 0 || pattern.Count > Count - offset)
			{
				return -1;
			}
			if (pattern.Count == 1)
			{
				byte b = pattern[0];
				for (int num = Count - offset - 1; num >= 0; num -= byteAlign)
				{
					if (data[num] == b)
					{
						return num;
					}
				}
				return -1;
			}
			int[] array = new int[256];
			for (int i = 0; i < 256; i++)
			{
				array[i] = pattern.Count;
			}
			for (int num2 = pattern.Count - 1; num2 > 0; num2--)
			{
				array[pattern[num2]] = num2;
			}
			for (int num3 = Count - offset - pattern.Count; num3 >= 0; num3 -= array[data[num3]])
			{
				if ((offset - num3) % byteAlign == 0 && ContainsAt(pattern, num3))
				{
					return num3;
				}
			}
			return -1;
		}

		public int RFind(ByteVector pattern, int offset)
		{
			return RFind(pattern, offset, 1);
		}

		public int RFind(ByteVector pattern)
		{
			return RFind(pattern, 0, 1);
		}

		public bool ContainsAt(ByteVector pattern, int offset, int patternOffset, int patternLength)
		{
			if (pattern == null)
			{
				throw new ArgumentNullException("pattern");
			}
			if (pattern.Count < patternLength)
			{
				patternLength = pattern.Count;
			}
			if (patternLength > data.Count || offset >= data.Count || patternOffset >= pattern.Count || patternLength <= 0 || offset < 0)
			{
				return false;
			}
			for (int i = 0; i < patternLength - patternOffset; i++)
			{
				if (data[i + offset] != pattern[i + patternOffset])
				{
					return false;
				}
			}
			return true;
		}

		public bool ContainsAt(ByteVector pattern, int offset, int patternOffset)
		{
			return ContainsAt(pattern, offset, patternOffset, int.MaxValue);
		}

		public bool ContainsAt(ByteVector pattern, int offset)
		{
			return ContainsAt(pattern, offset, 0);
		}

		public bool StartsWith(ByteVector pattern)
		{
			return ContainsAt(pattern, 0);
		}

		public bool EndsWith(ByteVector pattern)
		{
			if (pattern == null)
			{
				throw new ArgumentNullException("pattern");
			}
			return ContainsAt(pattern, data.Count - pattern.Count);
		}

		public int EndsWithPartialMatch(ByteVector pattern)
		{
			if (pattern == null)
			{
				throw new ArgumentNullException("pattern");
			}
			if (pattern.Count > data.Count)
			{
				return -1;
			}
			int num = data.Count - pattern.Count;
			for (int i = 1; i < pattern.Count; i++)
			{
				if (ContainsAt(pattern, num + i, 0, pattern.Count - i))
				{
					return num + i;
				}
			}
			return -1;
		}

		public void Add(ByteVector data)
		{
			if (IsReadOnly)
			{
				throw new NotSupportedException("Cannot edit readonly objects.");
			}
			if (data != null)
			{
				this.data.AddRange(data);
			}
		}

		public void Add(byte[] data)
		{
			if (IsReadOnly)
			{
				throw new NotSupportedException("Cannot edit readonly objects.");
			}
			if (data != null)
			{
				this.data.AddRange(data);
			}
		}

		public void Insert(int index, ByteVector data)
		{
			if (IsReadOnly)
			{
				throw new NotSupportedException("Cannot edit readonly objects.");
			}
			if (data != null)
			{
				this.data.InsertRange(index, data);
			}
		}

		public void Insert(int index, byte[] data)
		{
			if (IsReadOnly)
			{
				throw new NotSupportedException("Cannot edit readonly objects.");
			}
			if (data != null)
			{
				this.data.InsertRange(index, data);
			}
		}

		public ByteVector Resize(int size, byte padding)
		{
			if (IsReadOnly)
			{
				throw new NotSupportedException("Cannot edit readonly objects.");
			}
			if (data.Count > size)
			{
				data.RemoveRange(size, data.Count - size);
			}
			while (data.Count < size)
			{
				data.Add(padding);
			}
			return this;
		}

		public ByteVector Resize(int size)
		{
			return Resize(size, 0);
		}

		public void RemoveRange(int index, int count)
		{
			if (IsReadOnly)
			{
				throw new NotSupportedException("Cannot edit readonly objects.");
			}
			data.RemoveRange(index, count);
		}

		public int ToInt(bool mostSignificantByteFirst)
		{
			int num = 0;
			int num2 = ((Count > 4) ? 3 : (Count - 1));
			for (int i = 0; i <= num2; i++)
			{
				int num3 = (mostSignificantByteFirst ? (num2 - i) : i);
				num |= this[i] << num3 * 8;
			}
			return num;
		}

		public int ToInt()
		{
			return ToInt(mostSignificantByteFirst: true);
		}

		public uint ToUInt(bool mostSignificantByteFirst)
		{
			uint num = 0u;
			int num2 = ((Count > 4) ? 3 : (Count - 1));
			for (int i = 0; i <= num2; i++)
			{
				int num3 = (mostSignificantByteFirst ? (num2 - i) : i);
				num |= (uint)(this[i] << num3 * 8);
			}
			return num;
		}

		public uint ToUInt()
		{
			return ToUInt(mostSignificantByteFirst: true);
		}

		public short ToShort(bool mostSignificantByteFirst)
		{
			short num = 0;
			int num2 = ((Count > 2) ? 1 : (Count - 1));
			for (int i = 0; i <= num2; i++)
			{
				int num3 = (mostSignificantByteFirst ? (num2 - i) : i);
				num |= (short)(this[i] << num3 * 8);
			}
			return num;
		}

		public short ToShort()
		{
			return ToShort(mostSignificantByteFirst: true);
		}

		public ushort ToUShort(bool mostSignificantByteFirst)
		{
			ushort num = 0;
			int num2 = ((Count > 2) ? 1 : (Count - 1));
			for (int i = 0; i <= num2; i++)
			{
				int num3 = (mostSignificantByteFirst ? (num2 - i) : i);
				num |= (ushort)(this[i] << num3 * 8);
			}
			return num;
		}

		public ushort ToUShort()
		{
			return ToUShort(mostSignificantByteFirst: true);
		}

		public long ToLong(bool mostSignificantByteFirst)
		{
			long num = 0L;
			int num2 = ((Count > 8) ? 7 : (Count - 1));
			for (int i = 0; i <= num2; i++)
			{
				int num3 = (mostSignificantByteFirst ? (num2 - i) : i);
				num |= (long)((ulong)this[i] << num3 * 8);
			}
			return num;
		}

		public long ToLong()
		{
			return ToLong(mostSignificantByteFirst: true);
		}

		public ulong ToULong(bool mostSignificantByteFirst)
		{
			ulong num = 0uL;
			int num2 = ((Count > 8) ? 7 : (Count - 1));
			for (int i = 0; i <= num2; i++)
			{
				int num3 = (mostSignificantByteFirst ? (num2 - i) : i);
				num |= (ulong)this[i] << num3 * 8;
			}
			return num;
		}

		public ulong ToULong()
		{
			return ToULong(mostSignificantByteFirst: true);
		}

		public float ToFloat(bool mostSignificantByteFirst)
		{
			byte[] array = (byte[])Data.Clone();
			if (mostSignificantByteFirst)
			{
				Array.Reverse((Array)array);
			}
			return BitConverter.ToSingle(array, 0);
		}

		public float ToFloat()
		{
			return ToFloat(mostSignificantByteFirst: true);
		}

		public double ToDouble(bool mostSignificantByteFirst)
		{
			byte[] array = (byte[])Data.Clone();
			if (mostSignificantByteFirst)
			{
				Array.Reverse((Array)array);
			}
			return BitConverter.ToDouble(array, 0);
		}

		public double ToDouble()
		{
			return ToDouble(mostSignificantByteFirst: true);
		}

		public double ToExtendedPrecision()
		{
			int num = ((this[0] & 0x7F) << 8) | this[1];
			ulong num2 = ((ulong)this[2] << 24) | ((ulong)this[3] << 16) | ((ulong)this[4] << 8) | this[5];
			ulong num3 = ((ulong)this[6] << 24) | ((ulong)this[7] << 16) | ((ulong)this[8] << 8) | this[9];
			double num4;
			if (num == 0 && num2 == 0L && num3 == 0L)
			{
				num4 = 0.0;
			}
			else if (num == 32767)
			{
				num4 = double.PositiveInfinity;
			}
			else
			{
				num -= 16383;
				num4 = (double)num2 * Math.Pow(2.0, num -= 31);
				num4 += (double)num3 * Math.Pow(2.0, num -= 32);
			}
			if ((this[0] & 0x80) == 0)
			{
				return num4;
			}
			return 0.0 - num4;
		}

		public string ToString(StringType type, int offset, int count)
		{
			if (offset < 0 || offset > Count)
			{
				throw new ArgumentOutOfRangeException("offset");
			}
			if (count < 0 || count + offset > Count)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			ByteVector bom = ((type == StringType.UTF16 && data.Count - offset > 1) ? Mid(offset, 2) : null);
			string @string = StringTypeToEncoding(type, bom).GetString(Data, offset, count);
			if (@string.Length != 0 && (@string[0] == '\ufffe' || @string[0] == '\ufeff'))
			{
				return @string.Substring(1);
			}
			return @string;
		}

		[Obsolete("Use ToString(StringType,int,int)")]
		public string ToString(StringType type, int offset)
		{
			return ToString(type, offset, Count - offset);
		}

		public string ToString(StringType type)
		{
			return ToString(type, 0, Count);
		}

		public override string ToString()
		{
			return ToString(StringType.UTF8, 0, Count);
		}

		public string[] ToStrings(StringType type, int offset)
		{
			return ToStrings(type, offset, int.MaxValue);
		}

		public string[] ToStrings(StringType type, int offset, int count)
		{
			int num = 0;
			int num2 = offset;
			List<string> list = new List<string>();
			ByteVector byteVector = TextDelimiter(type);
			int count2 = byteVector.Count;
			while (num < count && num2 < Count)
			{
				int num3 = num2;
				if (num + 1 == count)
				{
					num2 = offset + count;
				}
				else
				{
					num2 = Find(byteVector, num3, count2);
					if (num2 < 0)
					{
						num2 = Count;
					}
				}
				int num4 = num2 - num3;
				if (num4 == 0)
				{
					list.Add(string.Empty);
				}
				else
				{
					string text = ToString(type, num3, num4);
					if (text.Length != 0 && (text[0] == '\ufffe' || text[0] == '\ufeff'))
					{
						text = text.Substring(1);
					}
					list.Add(text);
				}
				num2 += count2;
			}
			return list.ToArray();
		}

		public static bool operator ==(ByteVector first, ByteVector second)
		{
			bool flag = (object)first == null;
			bool flag2 = (object)second == null;
			if (flag && flag2)
			{
				return true;
			}
			if (flag || flag2)
			{
				return false;
			}
			return first.Equals(second);
		}

		public static bool operator !=(ByteVector first, ByteVector second)
		{
			return !(first == second);
		}

		public static bool operator <(ByteVector first, ByteVector second)
		{
			if (first == null)
			{
				throw new ArgumentNullException("first");
			}
			if (second == null)
			{
				throw new ArgumentNullException("second");
			}
			return first.CompareTo(second) < 0;
		}

		public static bool operator <=(ByteVector first, ByteVector second)
		{
			if (first == null)
			{
				throw new ArgumentNullException("first");
			}
			if (second == null)
			{
				throw new ArgumentNullException("second");
			}
			return first.CompareTo(second) <= 0;
		}

		public static bool operator >(ByteVector first, ByteVector second)
		{
			if (first == null)
			{
				throw new ArgumentNullException("first");
			}
			if (second == null)
			{
				throw new ArgumentNullException("second");
			}
			return first.CompareTo(second) > 0;
		}

		public static bool operator >=(ByteVector first, ByteVector second)
		{
			if (first == null)
			{
				throw new ArgumentNullException("first");
			}
			if (second == null)
			{
				throw new ArgumentNullException("second");
			}
			return first.CompareTo(second) >= 0;
		}

		public static ByteVector operator +(ByteVector first, ByteVector second)
		{
			return new ByteVector(first) { second };
		}

		public static implicit operator ByteVector(byte value)
		{
			return new ByteVector(value);
		}

		public static implicit operator ByteVector(byte[] value)
		{
			return new ByteVector(value);
		}

		public static implicit operator ByteVector(string value)
		{
			return FromString(value, StringType.UTF8);
		}

		public static ByteVector FromInt(int value, bool mostSignificantByteFirst)
		{
			ByteVector byteVector = new ByteVector();
			for (int i = 0; i < 4; i++)
			{
				int num = (mostSignificantByteFirst ? (3 - i) : i);
				byteVector.Add((byte)((uint)(value >> num * 8) & 0xFFu));
			}
			return byteVector;
		}

		public static ByteVector FromInt(int value)
		{
			return FromInt(value, mostSignificantByteFirst: true);
		}

		public static ByteVector FromUInt(uint value, bool mostSignificantByteFirst)
		{
			ByteVector byteVector = new ByteVector();
			for (int i = 0; i < 4; i++)
			{
				int num = (mostSignificantByteFirst ? (3 - i) : i);
				byteVector.Add((byte)((value >> num * 8) & 0xFFu));
			}
			return byteVector;
		}

		public static ByteVector FromUInt(uint value)
		{
			return FromUInt(value, mostSignificantByteFirst: true);
		}

		public static ByteVector FromShort(short value, bool mostSignificantByteFirst)
		{
			ByteVector byteVector = new ByteVector();
			for (int i = 0; i < 2; i++)
			{
				int num = (mostSignificantByteFirst ? (1 - i) : i);
				byteVector.Add((byte)((uint)(value >> num * 8) & 0xFFu));
			}
			return byteVector;
		}

		public static ByteVector FromShort(short value)
		{
			return FromShort(value, mostSignificantByteFirst: true);
		}

		public static ByteVector FromUShort(ushort value, bool mostSignificantByteFirst)
		{
			ByteVector byteVector = new ByteVector();
			for (int i = 0; i < 2; i++)
			{
				int num = (mostSignificantByteFirst ? (1 - i) : i);
				byteVector.Add((byte)((uint)(value >> num * 8) & 0xFFu));
			}
			return byteVector;
		}

		public static ByteVector FromUShort(ushort value)
		{
			return FromUShort(value, mostSignificantByteFirst: true);
		}

		public static ByteVector FromLong(long value, bool mostSignificantByteFirst)
		{
			ByteVector byteVector = new ByteVector();
			for (int i = 0; i < 8; i++)
			{
				int num = (mostSignificantByteFirst ? (7 - i) : i);
				byteVector.Add((byte)((value >> num * 8) & 0xFF));
			}
			return byteVector;
		}

		public static ByteVector FromLong(long value)
		{
			return FromLong(value, mostSignificantByteFirst: true);
		}

		public static ByteVector FromULong(ulong value, bool mostSignificantByteFirst)
		{
			ByteVector byteVector = new ByteVector();
			for (int i = 0; i < 8; i++)
			{
				int num = (mostSignificantByteFirst ? (7 - i) : i);
				byteVector.Add((byte)((value >> num * 8) & 0xFF));
			}
			return byteVector;
		}

		public static ByteVector FromULong(ulong value)
		{
			return FromULong(value, mostSignificantByteFirst: true);
		}

		public static ByteVector FromString(string text, StringType type, int length)
		{
			ByteVector byteVector = new ByteVector();
			if (type == StringType.UTF16)
			{
				byteVector.Add(new byte[2] { 255, 254 });
			}
			if (text == null || text.Length == 0)
			{
				return byteVector;
			}
			if (text.Length > length)
			{
				text = text.Substring(0, length);
			}
			byteVector.Add(StringTypeToEncoding(type, byteVector).GetBytes(text));
			return byteVector;
		}

		public static ByteVector FromString(string text, StringType type)
		{
			return FromString(text, type, int.MaxValue);
		}

		public static ByteVector FromString(string text, int length)
		{
			return FromString(text, StringType.UTF8, length);
		}

		[Obsolete("Use FromString(string,StringType)")]
		public static ByteVector FromString(string text)
		{
			return FromString(text, StringType.UTF8);
		}

		public static ByteVector FromPath(string path)
		{
			byte[] firstChunk;
			return FromPath(path, out firstChunk, copyFirstChunk: false);
		}

		internal static ByteVector FromPath(string path, out byte[] firstChunk, bool copyFirstChunk)
		{
			if (path == null)
			{
				throw new ArgumentNullException("path");
			}
			return FromFile(new File.LocalFileAbstraction(path), out firstChunk, copyFirstChunk);
		}

		public static ByteVector FromFile(File.IFileAbstraction abstraction)
		{
			byte[] firstChunk;
			return FromFile(abstraction, out firstChunk, copyFirstChunk: false);
		}

		internal static ByteVector FromFile(File.IFileAbstraction abstraction, out byte[] firstChunk, bool copyFirstChunk)
		{
			if (abstraction == null)
			{
				throw new ArgumentNullException("abstraction");
			}
			Stream readStream = abstraction.ReadStream;
			ByteVector result = FromStream(readStream, out firstChunk, copyFirstChunk);
			abstraction.CloseStream(readStream);
			return result;
		}

		public static ByteVector FromStream(Stream stream)
		{
			byte[] firstChunk;
			return FromStream(stream, out firstChunk, copyFirstChunk: false);
		}

		internal static ByteVector FromStream(Stream stream, out byte[] firstChunk, bool copyFirstChunk)
		{
			ByteVector byteVector = new ByteVector();
			byte[] array = new byte[4096];
			int num = array.Length;
			int num2 = 0;
			bool flag = false;
			firstChunk = null;
			int num3;
			do
			{
				Array.Clear(array, 0, array.Length);
				num3 = stream.Read(array, 0, num);
				byteVector.Add(array);
				num2 += num3;
				if (flag)
				{
					continue;
				}
				if (copyFirstChunk)
				{
					if (firstChunk == null || firstChunk.Length != num)
					{
						firstChunk = new byte[num];
					}
					Array.Copy(array, 0, firstChunk, 0, num3);
				}
				flag = true;
			}
			while ((num2 != stream.Length || stream.Length <= 0) && (num3 >= num || stream.Length > 0));
			if (stream.Length > 0 && byteVector.Count != stream.Length)
			{
				byteVector.Resize((int)stream.Length);
			}
			return byteVector;
		}

		public static ByteVector TextDelimiter(StringType type)
		{
			if (type != StringType.UTF16 && type != StringType.UTF16BE && type != StringType.UTF16LE)
			{
				return td1;
			}
			return td2;
		}

		private static Encoding StringTypeToEncoding(StringType type, ByteVector bom)
		{
			switch (type)
			{
			case StringType.UTF16:
				if (bom == null)
				{
					return last_utf16_encoding;
				}
				if (bom[0] == byte.MaxValue && bom[1] == 254)
				{
					return last_utf16_encoding = Encoding.Unicode;
				}
				if (bom[1] == byte.MaxValue && bom[0] == 254)
				{
					return last_utf16_encoding = Encoding.BigEndianUnicode;
				}
				return last_utf16_encoding;
			case StringType.UTF16BE:
				return Encoding.BigEndianUnicode;
			case StringType.UTF8:
				return Encoding.UTF8;
			case StringType.UTF16LE:
				return Encoding.Unicode;
			default:
				if (use_broken_latin1)
				{
					return Encoding.Default;
				}
				try
				{
					return Encoding.GetEncoding("latin1");
				}
				catch (ArgumentException)
				{
					return Encoding.UTF8;
				}
			}
		}

		public override bool Equals(object other)
		{
			if (!(other is ByteVector))
			{
				return false;
			}
			return Equals((ByteVector)other);
		}

		public bool Equals(ByteVector other)
		{
			return CompareTo(other) == 0;
		}

		public override int GetHashCode()
		{
			return (int)Checksum;
		}

		public int CompareTo(ByteVector other)
		{
			if ((object)other == null)
			{
				throw new ArgumentNullException("other");
			}
			int num = Count - other.Count;
			int num2 = 0;
			while (num == 0 && num2 < Count)
			{
				num = this[num2] - other[num2];
				num2++;
			}
			return num;
		}

		public IEnumerator<byte> GetEnumerator()
		{
			return data.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return data.GetEnumerator();
		}

		public void Clear()
		{
			if (IsReadOnly)
			{
				throw new NotSupportedException("Cannot edit readonly objects.");
			}
			data.Clear();
		}

		public void Add(byte item)
		{
			if (IsReadOnly)
			{
				throw new NotSupportedException("Cannot edit readonly objects.");
			}
			data.Add(item);
		}

		public bool Remove(byte item)
		{
			if (IsReadOnly)
			{
				throw new NotSupportedException("Cannot edit readonly objects.");
			}
			return data.Remove(item);
		}

		public void CopyTo(byte[] array, int arrayIndex)
		{
			data.CopyTo(array, arrayIndex);
		}

		public bool Contains(byte item)
		{
			return data.Contains(item);
		}

		public void RemoveAt(int index)
		{
			if (IsReadOnly)
			{
				throw new NotSupportedException("Cannot edit readonly objects.");
			}
			data.RemoveAt(index);
		}

		public void Insert(int index, byte item)
		{
			if (IsReadOnly)
			{
				throw new NotSupportedException("Cannot edit readonly objects.");
			}
			data.Insert(index, item);
		}

		public int IndexOf(byte item)
		{
			return data.IndexOf(item);
		}
	}
	[ComVisible(false)]
	public class ByteVectorCollection : ListBase<ByteVector>
	{
		public ByteVectorCollection()
		{
		}

		public ByteVectorCollection(IEnumerable<ByteVector> list)
		{
			if (list != null)
			{
				Add(list);
			}
		}

		public ByteVectorCollection(params ByteVector[] list)
		{
			if (list != null)
			{
				Add(list);
			}
		}

		public override void SortedInsert(ByteVector item, bool unique)
		{
			if (item == null)
			{
				throw new ArgumentNullException("item");
			}
			int i;
			for (i = 0; i < base.Count; i++)
			{
				if (item == base[i] && unique)
				{
					return;
				}
				if (item >= base[i])
				{
					break;
				}
			}
			Insert(i + 1, item);
		}

		public ByteVector ToByteVector(ByteVector separator)
		{
			if (separator == null)
			{
				throw new ArgumentNullException("separator");
			}
			ByteVector byteVector = new ByteVector();
			for (int i = 0; i < base.Count; i++)
			{
				if (i != 0 && separator.Count > 0)
				{
					byteVector.Add(separator);
				}
				byteVector.Add(base[i]);
			}
			return byteVector;
		}

		public static ByteVectorCollection Split(ByteVector vector, ByteVector pattern, int byteAlign, int max)
		{
			if (vector == null)
			{
				throw new ArgumentNullException("vector");
			}
			if (pattern == null)
			{
				throw new ArgumentNullException("pattern");
			}
			if (byteAlign < 1)
			{
				throw new ArgumentOutOfRangeException("byteAlign", "byteAlign must be at least 1.");
			}
			ByteVectorCollection byteVectorCollection = new ByteVectorCollection();
			int num = 0;
			int num2 = vector.Find(pattern, 0, byteAlign);
			while (num2 != -1 && (max < 1 || max > byteVectorCollection.Count + 1))
			{
				byteVectorCollection.Add(vector.Mid(num, num2 - num));
				num = num2 + pattern.Count;
				num2 = vector.Find(pattern, num2 + pattern.Count, byteAlign);
			}
			if (num < vector.Count)
			{
				byteVectorCollection.Add(vector.Mid(num, vector.Count - num));
			}
			return byteVectorCollection;
		}

		public static ByteVectorCollection Split(ByteVector vector, ByteVector pattern, int byteAlign)
		{
			return Split(vector, pattern, byteAlign, 0);
		}

		public static ByteVectorCollection Split(ByteVector vector, ByteVector pattern)
		{
			return Split(vector, pattern, 1);
		}
	}
	public class CombinedTag : Tag
	{
		private readonly List<Tag> tags;

		public virtual Tag[] Tags => tags.ToArray();

		public override TagTypes TagTypes
		{
			get
			{
				TagTypes tagTypes = TagTypes.None;
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tagTypes |= tag.TagTypes;
					}
				}
				return tagTypes;
			}
		}

		public override string Title
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string title = tag.Title;
						if (title != null)
						{
							return title;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Title = value;
					}
				}
			}
		}

		public override string Subtitle
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string subtitle = tag.Subtitle;
						if (subtitle != null)
						{
							return subtitle;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Subtitle = value;
					}
				}
			}
		}

		public override string Description
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string description = tag.Description;
						if (description != null)
						{
							return description;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Description = value;
					}
				}
			}
		}

		public override string[] Performers
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string[] performers = tag.Performers;
						if (performers != null && performers.Length != 0)
						{
							return performers;
						}
					}
				}
				return new string[0];
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Performers = value;
					}
				}
			}
		}

		public override string[] PerformersSort
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string[] performersSort = tag.PerformersSort;
						if (performersSort != null && performersSort.Length != 0)
						{
							return performersSort;
						}
					}
				}
				return new string[0];
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.PerformersSort = value;
					}
				}
			}
		}

		public override string[] PerformersRole
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string[] performersRole = tag.PerformersRole;
						if (performersRole != null && performersRole.Length != 0)
						{
							return performersRole;
						}
					}
				}
				return new string[0];
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.PerformersRole = value;
					}
				}
			}
		}

		public override string[] AlbumArtistsSort
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string[] albumArtistsSort = tag.AlbumArtistsSort;
						if (albumArtistsSort != null && albumArtistsSort.Length != 0)
						{
							return albumArtistsSort;
						}
					}
				}
				return new string[0];
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.AlbumArtistsSort = value;
					}
				}
			}
		}

		public override string[] AlbumArtists
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string[] albumArtists = tag.AlbumArtists;
						if (albumArtists != null && albumArtists.Length != 0)
						{
							return albumArtists;
						}
					}
				}
				return new string[0];
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.AlbumArtists = value;
					}
				}
			}
		}

		public override string[] Composers
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string[] composers = tag.Composers;
						if (composers != null && composers.Length != 0)
						{
							return composers;
						}
					}
				}
				return new string[0];
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Composers = value;
					}
				}
			}
		}

		public override string[] ComposersSort
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string[] composersSort = tag.ComposersSort;
						if (composersSort != null && composersSort.Length != 0)
						{
							return composersSort;
						}
					}
				}
				return new string[0];
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.ComposersSort = value;
					}
				}
			}
		}

		public override string TitleSort
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string titleSort = tag.TitleSort;
						if (titleSort != null && titleSort.Length > 0)
						{
							return titleSort;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.TitleSort = value;
					}
				}
			}
		}

		public override string AlbumSort
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string albumSort = tag.AlbumSort;
						if (albumSort != null && albumSort.Length > 0)
						{
							return albumSort;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.AlbumSort = value;
					}
				}
			}
		}

		public override string Album
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string album = tag.Album;
						if (album != null)
						{
							return album;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Album = value;
					}
				}
			}
		}

		public override string Comment
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string comment = tag.Comment;
						if (comment != null)
						{
							return comment;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Comment = value;
					}
				}
			}
		}

		public override string[] Genres
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string[] genres = tag.Genres;
						if (genres != null && genres.Length != 0)
						{
							return genres;
						}
					}
				}
				return new string[0];
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Genres = value;
					}
				}
			}
		}

		public override uint Year
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						uint year = tag.Year;
						if (year != 0)
						{
							return year;
						}
					}
				}
				return 0u;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Year = value;
					}
				}
			}
		}

		public override uint Track
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						uint track = tag.Track;
						if (track != 0)
						{
							return track;
						}
					}
				}
				return 0u;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Track = value;
					}
				}
			}
		}

		public override uint TrackCount
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						uint trackCount = tag.TrackCount;
						if (trackCount != 0)
						{
							return trackCount;
						}
					}
				}
				return 0u;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.TrackCount = value;
					}
				}
			}
		}

		public override uint Disc
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						uint disc = tag.Disc;
						if (disc != 0)
						{
							return disc;
						}
					}
				}
				return 0u;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Disc = value;
					}
				}
			}
		}

		public override uint DiscCount
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						uint discCount = tag.DiscCount;
						if (discCount != 0)
						{
							return discCount;
						}
					}
				}
				return 0u;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.DiscCount = value;
					}
				}
			}
		}

		public override string Lyrics
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string lyrics = tag.Lyrics;
						if (lyrics != null)
						{
							return lyrics;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Lyrics = value;
					}
				}
			}
		}

		public override string Grouping
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string grouping = tag.Grouping;
						if (grouping != null)
						{
							return grouping;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Grouping = value;
					}
				}
			}
		}

		public override uint BeatsPerMinute
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						uint beatsPerMinute = tag.BeatsPerMinute;
						if (beatsPerMinute != 0)
						{
							return beatsPerMinute;
						}
					}
				}
				return 0u;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.BeatsPerMinute = value;
					}
				}
			}
		}

		public override string Conductor
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string conductor = tag.Conductor;
						if (conductor != null)
						{
							return conductor;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Conductor = value;
					}
				}
			}
		}

		public override string Copyright
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string copyright = tag.Copyright;
						if (copyright != null)
						{
							return copyright;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Copyright = value;
					}
				}
			}
		}

		public override DateTime? DateTagged
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						DateTime? dateTagged = tag.DateTagged;
						if (dateTagged.HasValue)
						{
							return dateTagged;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.DateTagged = value;
					}
				}
			}
		}

		public override string MusicBrainzArtistId
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string musicBrainzArtistId = tag.MusicBrainzArtistId;
						if (musicBrainzArtistId != null)
						{
							return musicBrainzArtistId;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.MusicBrainzArtistId = value;
					}
				}
			}
		}

		public override string MusicBrainzReleaseGroupId
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string musicBrainzReleaseGroupId = tag.MusicBrainzReleaseGroupId;
						if (musicBrainzReleaseGroupId != null)
						{
							return musicBrainzReleaseGroupId;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.MusicBrainzReleaseGroupId = value;
					}
				}
			}
		}

		public override string MusicBrainzReleaseId
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string musicBrainzReleaseId = tag.MusicBrainzReleaseId;
						if (musicBrainzReleaseId != null)
						{
							return musicBrainzReleaseId;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.MusicBrainzReleaseId = value;
					}
				}
			}
		}

		public override string MusicBrainzReleaseArtistId
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string musicBrainzReleaseArtistId = tag.MusicBrainzReleaseArtistId;
						if (musicBrainzReleaseArtistId != null)
						{
							return musicBrainzReleaseArtistId;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.MusicBrainzReleaseArtistId = value;
					}
				}
			}
		}

		public override string MusicBrainzTrackId
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string musicBrainzTrackId = tag.MusicBrainzTrackId;
						if (musicBrainzTrackId != null)
						{
							return musicBrainzTrackId;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.MusicBrainzTrackId = value;
					}
				}
			}
		}

		public override string MusicBrainzDiscId
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string musicBrainzDiscId = tag.MusicBrainzDiscId;
						if (musicBrainzDiscId != null)
						{
							return musicBrainzDiscId;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.MusicBrainzDiscId = value;
					}
				}
			}
		}

		public override string MusicIpId
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string musicIpId = tag.MusicIpId;
						if (musicIpId != null)
						{
							return musicIpId;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.MusicIpId = value;
					}
				}
			}
		}

		public override string AmazonId
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string amazonId = tag.AmazonId;
						if (amazonId != null)
						{
							return amazonId;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.AmazonId = value;
					}
				}
			}
		}

		public override string MusicBrainzReleaseStatus
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string musicBrainzReleaseStatus = tag.MusicBrainzReleaseStatus;
						if (musicBrainzReleaseStatus != null)
						{
							return musicBrainzReleaseStatus;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.MusicBrainzReleaseStatus = value;
					}
				}
			}
		}

		public override string MusicBrainzReleaseType
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string musicBrainzReleaseType = tag.MusicBrainzReleaseType;
						if (musicBrainzReleaseType != null)
						{
							return musicBrainzReleaseType;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.MusicBrainzReleaseType = value;
					}
				}
			}
		}

		public override string MusicBrainzReleaseCountry
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string musicBrainzReleaseCountry = tag.MusicBrainzReleaseCountry;
						if (musicBrainzReleaseCountry != null)
						{
							return musicBrainzReleaseCountry;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.MusicBrainzReleaseCountry = value;
					}
				}
			}
		}

		public override IPicture[] Pictures
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						IPicture[] pictures = tag.Pictures;
						if (pictures != null && pictures.Length != 0)
						{
							return pictures;
						}
					}
				}
				return base.Pictures;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Pictures = value;
					}
				}
			}
		}

		public override double ReplayGainTrackGain
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						double replayGainTrackGain = tag.ReplayGainTrackGain;
						if (!double.IsNaN(replayGainTrackGain))
						{
							return replayGainTrackGain;
						}
					}
				}
				return double.NaN;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.ReplayGainTrackGain = value;
					}
				}
			}
		}

		public override double ReplayGainTrackPeak
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						double replayGainTrackPeak = tag.ReplayGainTrackPeak;
						if (!double.IsNaN(replayGainTrackPeak))
						{
							return replayGainTrackPeak;
						}
					}
				}
				return double.NaN;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.ReplayGainTrackPeak = value;
					}
				}
			}
		}

		public override double ReplayGainAlbumGain
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						double replayGainAlbumGain = tag.ReplayGainAlbumGain;
						if (!double.IsNaN(replayGainAlbumGain))
						{
							return replayGainAlbumGain;
						}
					}
				}
				return double.NaN;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.ReplayGainAlbumGain = value;
					}
				}
			}
		}

		public override double ReplayGainAlbumPeak
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						double replayGainAlbumPeak = tag.ReplayGainAlbumPeak;
						if (!double.IsNaN(replayGainAlbumPeak))
						{
							return replayGainAlbumPeak;
						}
					}
				}
				return double.NaN;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.ReplayGainAlbumPeak = value;
					}
				}
			}
		}

		public override string InitialKey
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string initialKey = tag.InitialKey;
						if (initialKey != null)
						{
							return initialKey;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.InitialKey = value;
					}
				}
			}
		}

		public override string RemixedBy
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string remixedBy = tag.RemixedBy;
						if (remixedBy != null)
						{
							return remixedBy;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.RemixedBy = value;
					}
				}
			}
		}

		public override string Publisher
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string publisher = tag.Publisher;
						if (publisher != null)
						{
							return publisher;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Publisher = value;
					}
				}
			}
		}

		public override string ISRC
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string iSRC = tag.ISRC;
						if (iSRC != null)
						{
							return iSRC;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.ISRC = value;
					}
				}
			}
		}

		public override string Length
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						string length = tag.Length;
						if (length != null)
						{
							return length;
						}
					}
				}
				return null;
			}
			set
			{
				foreach (Tag tag in tags)
				{
					if (tag != null)
					{
						tag.Length = value;
					}
				}
			}
		}

		public override bool IsEmpty
		{
			get
			{
				foreach (Tag tag in tags)
				{
					if (tag.IsEmpty)
					{
						return true;
					}
				}
				return false;
			}
		}

		public CombinedTag()
		{
			tags = new List<Tag>();
		}

		public CombinedTag(params Tag[] tags)
		{
			this.tags = new List<Tag>(tags);
		}

		public void SetTags(params Tag[] tags)
		{
			this.tags.Clear();
			this.tags.AddRange(tags);
		}

		protected void InsertTag(int index, Tag tag)
		{
			tags.Insert(index, tag);
		}

		protected void AddTag(Tag tag)
		{
			tags.Add(tag);
		}

		protected void RemoveTag(Tag tag)
		{
			tags.Remove(tag);
		}

		protected void ClearTags()
		{
			tags.Clear();
		}

		public override void Clear()
		{
			foreach (Tag tag in tags)
			{
				tag.Clear();
			}
		}
	}
	[Serializable]
	public class CorruptFileException : Exception
	{
		public CorruptFileException(string message)
			: base(message)
		{
		}

		public CorruptFileException()
		{
		}

		public CorruptFileException(string message, Exception innerException)
			: base(message, innerException)
		{
		}

		protected CorruptFileException(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{
		}
	}
	internal static class Debugger
	{
		public delegate void DebugMessageSentHandler(string message);

		private struct DebugTimeData
		{
			public TimeSpan time;

			public long occurances;

			public DebugTimeData(TimeSpan time, int occurances)
			{
				this.time = time;
				this.occurances = occurances;
			}
		}

		private static readonly string allowed = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`~!@#$%^&*()_+-={}[];:'\",.<>?/\\|";

		private static readonly Dictionary<object, Dictionary<object, DebugTimeData>> debug_times = new Dictionary<object, Dictionary<object, DebugTimeData>>();

		public static event DebugMessageSentHandler DebugMessageSent;

		public static void Debug(string message)
		{
			Debugger.DebugMessageSent?.Invoke(message);
		}

		public static void DumpHex(ByteVector data)
		{
			DumpHex(data.Data);
		}

		public static void DumpHex(byte[] data)
		{
			int num = 16;
			int num2 = data.Length / num + ((data.Length % num != 0) ? 1 : 0);
			for (int i = 0; i < num2; i++)
			{
				for (int j = 0; j < num; j++)
				{
					if (i == num2 - 1 && data.Length % num != 0 && j >= data.Length % num)
					{
						Console.Write("   ");
					}
					else
					{
						Console.Write(" {0:x2}", data[i * num + j]);
					}
				}
				Console.Write(" | ");
				for (int k = 0; k < num; k++)
				{
					if (i == num2 - 1 && data.Length % num != 0 && k >= data.Length % num)
					{
						Console.Write(" ");
					}
					else
					{
						WriteByte2(data[i * num + k]);
					}
				}
				Console.WriteLine();
			}
			Console.WriteLine();
		}

		private static void WriteByte2(byte data)
		{
			string text = allowed;
			foreach (char c in text)
			{
				if (c == data)
				{
					Console.Write(c);
					return;
				}
			}
			Console.Write(".");
		}

		public static void AddDebugTime(object o1, object o2, DateTime start)
		{
			DebugTimeData value = new DebugTimeData(DateTime.Now - start, 1);
			if (debug_times.ContainsKey(o1) && debug_times[o1].ContainsKey(o2))
			{
				value.time += debug_times[o1][o2].time;
				value.occurances += debug_times[o1][o2].occurances;
			}
			if (!debug_times.ContainsKey(o1))
			{
				debug_times.Add(o1, new Dictionary<object, DebugTimeData>());
			}
			if (!debug_times[o1].ContainsKey(o2))
			{
				debug_times[o1].Add(o2, value);
			}
			else
			{
				debug_times[o1][o2] = value;
			}
		}

		public static void DumpDebugTime(object o1)
		{
			Console.WriteLine(o1.ToString());
			if (!debug_times.ContainsKey(o1))
			{
				return;
			}
			foreach (KeyValuePair<object, DebugTimeData> item in debug_times[o1])
			{
				Console.WriteLine("  {0}", item.Key);
				Console.WriteLine("    Objects: {0}", item.Value.time);
				Console.WriteLine("    Total:   {0}", item.Value.occurances);
				Console.WriteLine("    Average: {0}", new TimeSpan(item.Value.time.Ticks / item.Value.occurances));
				Console.WriteLine();
			}
			debug_times.Remove(o1);
		}
	}
	[Flags]
	public enum ReadStyle
	{
		None = 0,
		Average = 2,
		PictureLazy = 4
	}
	public abstract class File : IDisposable
	{
		public enum AccessMode
		{
			Read,
			Write,
			Closed
		}

		public delegate File FileTypeResolver(IFileAbstraction abstraction, string mimetype, ReadStyle style);

		public class LocalFileAbstraction : IFileAbstraction
		{
			private readonly string name;

			public string Name => name;

			public Stream ReadStream => System.IO.File.Open(Name, FileMode.Open, FileAccess.Read, FileShare.Read);

			public Stream WriteStream => System.IO.File.Open(Name, FileMode.Open, FileAccess.ReadWrite);

			public LocalFileAbstraction(string path)
			{
				if (path == null)
				{
					throw new ArgumentNullException("path");
				}
				name = path;
			}

			public void CloseStream(Stream stream)
			{
				if (stream == null)
				{
					throw new ArgumentNullException("stream");
				}
				stream.Close();
			}
		}

		public interface IFileAbstraction
		{
			string Name { get; }

			Stream ReadStream { get; }

			Stream WriteStream { get; }

			void CloseStream(Stream stream);
		}

		private Stream file_stream;

		protected IFileAbstraction file_abstraction;

		private static readonly int buffer_size = 1024;

		private static readonly List<FileTypeResolver> file_type_resolvers = new List<FileTypeResolver>();

		private List<string> corruption_reasons;

		public static uint BufferSize => (uint)buffer_size;

		public abstract Tag Tag { get; }

		public abstract Properties Properties { get; }

		public TagTypes TagTypesOnDisk { get; protected set; }

		public TagTypes TagTypes => Tag?.TagTypes ?? TagTypes.None;

		public string Name => file_abstraction.Name;

		public string MimeType { get; internal set; }

		public long Tell
		{
			get
			{
				if (Mode != AccessMode.Closed)
				{
					return file_stream.Position;
				}
				return 0L;
			}
		}

		public long Length
		{
			get
			{
				if (Mode != AccessMode.Closed)
				{
					return file_stream.Length;
				}
				return 0L;
			}
		}

		public long InvariantStartPosition { get; protected set; } = -1L;


		public long InvariantEndPosition { get; protected set; } = -1L;


		public AccessMode Mode
		{
			get
			{
				if (file_stream == null)
				{
					return AccessMode.Closed;
				}
				if (file_stream.CanWrite)
				{
					return AccessMode.Write;
				}
				return AccessMode.Read;
			}
			set
			{
				if (Mode != value && (Mode != AccessMode.Write || value != 0))
				{
					if (file_stream != null)
					{
						file_abstraction.CloseStream(file_stream);
					}
					file_stream = null;
					switch (value)
					{
					case AccessMode.Read:
						file_stream = file_abstraction.ReadStream;
						break;
					case AccessMode.Write:
						file_stream = file_abstraction.WriteStream;
						break;
					}
					Mode = value;
				}
			}
		}

		public IFileAbstraction FileAbstraction => file_abstraction;

		public virtual bool Writeable => !PossiblyCorrupt;

		public bool PossiblyCorrupt => corruption_reasons != null;

		public IEnumerable<string> CorruptionReasons => corruption_reasons;

		protected File(string path)
		{
			if (path == null)
			{
				throw new ArgumentNullException("path");
			}
			file_abstraction = new LocalFileAbstraction(path);
		}

		protected File(IFileAbstraction abstraction)
		{
			if (abstraction == null)
			{
				throw new ArgumentNullException("abstraction");
			}
			file_abstraction = abstraction;
		}

		internal void MarkAsCorrupt(string reason)
		{
			if (corruption_reasons == null)
			{
				corruption_reasons = new List<string>();
			}
			corruption_reasons.Add(reason);
		}

		public void Dispose()
		{
			Mode = AccessMode.Closed;
		}

		public abstract void Save();

		public abstract void RemoveTags(TagTypes types);

		public abstract Tag GetTag(TagTypes type, bool create);

		public Tag GetTag(TagTypes type)
		{
			return GetTag(type, create: false);
		}

		public ByteVector ReadBlock(int length)
		{
			if (length < 0)
			{
				throw new ArgumentException("Length must be non-negative", "length");
			}
			if (length == 0)
			{
				return new ByteVector();
			}
			Mode = AccessMode.Read;
			byte[] array = new byte[length];
			int num = 0;
			int num2 = 0;
			int num3 = length;
			do
			{
				num = file_stream.Read(array, num2, num3);
				num2 += num;
				num3 -= num;
			}
			while (num3 > 0 && num != 0);
			return new ByteVector(array, num2);
		}

		public void WriteBlock(ByteVector data)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			Mode = AccessMode.Write;
			file_stream.Write(data.Data, 0, data.Count);
		}

		public long Find(ByteVector pattern, long startPosition, ByteVector before)
		{
			if (pattern == null)
			{
				throw new ArgumentNullException("pattern");
			}
			Mode = AccessMode.Read;
			if (pattern.Count > buffer_size)
			{
				return -1L;
			}
			long num = startPosition;
			long position = file_stream.Position;
			try
			{
				file_stream.Position = startPosition;
				ByteVector byteVector = ReadBlock(buffer_size);
				while (byteVector.Count > 0)
				{
					int num2 = byteVector.Find(pattern);
					if (before != null && byteVector.Find(before) < num2)
					{
						return -1L;
					}
					if (num2 >= 0)
					{
						return num + num2;
					}
					num += buffer_size - pattern.Count;
					if (before != null && before.Count > pattern.Count)
					{
						num -= before.Count - pattern.Count;
					}
					file_stream.Position = num;
					byteVector = ReadBlock(buffer_size);
				}
				return -1L;
			}
			finally
			{
				file_stream.Position = position;
			}
		}

		public long Find(ByteVector pattern, long startPosition)
		{
			return Find(pattern, startPosition, null);
		}

		public long Find(ByteVector pattern)
		{
			return Find(pattern, 0L);
		}

		private long RFind(ByteVector pattern, long startPosition, ByteVector after)
		{
			if (pattern == null)
			{
				throw new ArgumentNullException("pattern");
			}
			Mode = AccessMode.Read;
			if (pattern.Count > buffer_size)
			{
				return -1L;
			}
			long position = file_stream.Position;
			long num = Length - startPosition;
			int num2 = buffer_size;
			num2 = (int)Math.Min(num, buffer_size);
			num -= num2;
			file_stream.Position = num;
			ByteVector byteVector = ReadBlock(num2);
			while (byteVector.Count > 0)
			{
				long num3 = byteVector.RFind(pattern);
				if (num3 >= 0)
				{
					file_stream.Position = position;
					return num + num3;
				}
				if (after != null && byteVector.RFind(after) >= 0)
				{
					file_stream.Position = position;
					return -1L;
				}
				num2 = (int)Math.Min(num, buffer_size);
				num -= num2;
				if (num2 + pattern.Count > buffer_size)
				{
					num += pattern.Count;
				}
				file_stream.Position = num;
				byteVector = ReadBlock(num2);
			}
			file_stream.Position = position;
			return -1L;
		}

		public long RFind(ByteVector pattern, long startPosition)
		{
			return RFind(pattern, startPosition, null);
		}

		public long RFind(ByteVector pattern)
		{
			return RFind(pattern, 0L);
		}

		public void Insert(ByteVector data, long start, long replace)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			Insert(data, data.Count, start, replace);
		}

		public void Insert(ByteVector data, long start)
		{
			Insert(data, start, 0L);
		}

		public void Insert(long size, long start)
		{
			Insert(null, size, start, 0L);
		}

		public void RemoveBlock(long start, long length)
		{
			if (length > 0)
			{
				Mode = AccessMode.Write;
				int length2 = buffer_size;
				long num = start + length;
				long num2 = start;
				ByteVector byteVector = (byte)1;
				while (byteVector.Count != 0)
				{
					file_stream.Position = num;
					byteVector = ReadBlock(length2);
					num += byteVector.Count;
					file_stream.Position = num2;
					WriteBlock(byteVector);
					num2 += byteVector.Count;
				}
				Truncate(num2);
			}
		}

		public void Seek(long offset, SeekOrigin origin)
		{
			if (Mode != AccessMode.Closed)
			{
				file_stream.Seek(offset, origin);
			}
		}

		public void Seek(long offset)
		{
			Seek(offset, SeekOrigin.Begin);
		}

		public static File Create(string path)
		{
			return Create(path, null, ReadStyle.Average);
		}

		public static File Create(IFileAbstraction abstraction)
		{
			return Create(abstraction, null, ReadStyle.Average);
		}

		public static File Create(string path, ReadStyle propertiesStyle)
		{
			return Create(path, null, propertiesStyle);
		}

		public static File Create(IFileAbstraction abstraction, ReadStyle propertiesStyle)
		{
			return Create(abstraction, null, propertiesStyle);
		}

		public static File Create(string path, string mimetype, ReadStyle propertiesStyle)
		{
			return Create(new LocalFileAbstraction(path), mimetype, propertiesStyle);
		}

		public static File Create(IFileAbstraction abstraction, string mimetype, ReadStyle propertiesStyle)
		{
			if (mimetype == null)
			{
				string text = string.Empty;
				int num = abstraction.Name.LastIndexOf(".") + 1;
				if (num >= 1 && num < abstraction.Name.Length)
				{
					text = abstraction.Name.Substring(num, abstraction.Name.Length - num);
				}
				mimetype = "taglib/" + text.ToLower(CultureInfo.InvariantCulture);
			}
			foreach (FileTypeResolver file_type_resolver in file_type_resolvers)
			{
				File file = file_type_resolver(abstraction, mimetype, propertiesStyle);
				if (file != null)
				{
					return file;
				}
			}
			if (!FileTypes.AvailableTypes.ContainsKey(mimetype))
			{
				throw new UnsupportedFormatException(string.Format(CultureInfo.InvariantCulture, "{0} ({1})", abstraction.Name, mimetype));
			}
			Type type = FileTypes.AvailableTypes[mimetype];
			try
			{
				File obj = (File)Activator.CreateInstance(type, abstraction, propertiesStyle);
				obj.MimeType = mimetype;
				return obj;
			}
			catch (TargetInvocationException ex)
			{
				PrepareExceptionForRethrow(ex.InnerException);
				throw ex.InnerException;
			}
		}

		public static void AddFileTypeResolver(FileTypeResolver resolver)
		{
			if (resolver != null)
			{
				file_type_resolvers.Insert(0, resolver);
			}
		}

		protected void PreSave()
		{
			if (!Writeable)
			{
				throw new InvalidOperationException("File not writeable.");
			}
			if (PossiblyCorrupt)
			{
				throw new CorruptFileException("Corrupted file cannot be saved.");
			}
			if (Tag?.Pictures == null)
			{
				return;
			}
			IPicture[] pictures = Tag.Pictures;
			for (int i = 0; i < pictures.Length; i++)
			{
				if (pictures[i] is ILazy lazy)
				{
					lazy.Load();
				}
			}
		}

		private void Insert(ByteVector data, long size, long start, long replace)
		{
			Mode = AccessMode.Write;
			if (size == replace)
			{
				if (data != null)
				{
					file_stream.Position = start;
					WriteBlock(data);
				}
				return;
			}
			if (size < replace)
			{
				if (data != null)
				{
					file_stream.Position = start;
					WriteBlock(data);
				}
				RemoveBlock(start + size, replace - size);
				return;
			}
			int num = (int)(size - replace);
			int num2 = num % buffer_size;
			if (num2 != 0)
			{
				num += buffer_size - num2;
			}
			long num3 = start + replace;
			long num4 = start;
			file_stream.Position = num3;
			byte[] data2 = ReadBlock(num).Data;
			num3 += num;
			if (data != null)
			{
				file_stream.Position = num4;
				WriteBlock(data);
			}
			else if (start + size > Length)
			{
				file_stream.SetLength(start + size);
			}
			num4 += size;
			byte[] array = new byte[data2.Length];
			Array.Copy(data2, 0, array, 0, data2.Length);
			while (num != 0)
			{
				file_stream.Position = num3;
				int num5 = file_stream.Read(data2, 0, (num < data2.Length) ? num : data2.Length);
				num3 += num;
				file_stream.Position = num4;
				file_stream.Write(array, 0, (num < array.Length) ? num : array.Length);
				num4 += num;
				Array.Copy(data2, 0, array, 0, num5);
				num = num5;
			}
		}

		protected void Truncate(long length)
		{
			AccessMode mode = Mode;
			Mode = AccessMode.Write;
			file_stream.SetLength(length);
			Mode = mode;
		}

		private static void PrepareExceptionForRethrow(Exception ex)
		{
			StreamingContext context = new StreamingContext(StreamingContextStates.CrossAppDomain);
			ObjectManager objectManager = new ObjectManager(null, context);
			SerializationInfo info = new SerializationInfo(ex.GetType(), new FormatterConverter());
			ex.GetObjectData(info, context);
			objectManager.RegisterObject(ex, 1L, info);
			objectManager.DoFixups();
		}
	}
	public static class FileTypes
	{
		private static Dictionary<string, Type> file_types;

		private static readonly Type[] static_file_types;

		public static IDictionary<string, Type> AvailableTypes => file_types;

		static FileTypes()
		{
			static_file_types = new Type[26]
			{
				typeof(TagLib.Aac.File),
				typeof(TagLib.Aiff.File),
				typeof(TagLib.Ape.File),
				typeof(TagLib.Asf.File),
				typeof(TagLib.Audible.File),
				typeof(TagLib.Dsf.File),
				typeof(TagLib.Flac.File),
				typeof(TagLib.Matroska.File),
				typeof(TagLib.Gif.File),
				typeof(TagLib.Image.NoMetadata.File),
				typeof(TagLib.Jpeg.File),
				typeof(TagLib.Mpeg4.File),
				typeof(AudioFile),
				typeof(TagLib.Mpeg.File),
				typeof(TagLib.MusePack.File),
				typeof(TagLib.Ogg.File),
				typeof(TagLib.Png.File),
				typeof(TagLib.Riff.File),
				typeof(TagLib.Tiff.Arw.File),
				typeof(TagLib.Tiff.Cr2.File),
				typeof(TagLib.Tiff.Dng.File),
				typeof(TagLib.Tiff.File),
				typeof(TagLib.Tiff.Nef.File),
				typeof(TagLib.Tiff.Pef.File),
				typeof(TagLib.Tiff.Rw2.File),
				typeof(TagLib.WavPack.File)
			};
			Init();
		}

		internal static void Init()
		{
			if (file_types == null)
			{
				file_types = new Dictionary<string, Type>();
				Type[] array = static_file_types;
				for (int i = 0; i < array.Length; i++)
				{
					Register(array[i]);
				}
			}
		}

		public static void Register(Type type)
		{
			Attribute[] customAttributes = Attribute.GetCustomAttributes(type, typeof(SupportedMimeType), inherit: false);
			if (customAttributes.Length != 0)
			{
				Attribute[] array = customAttributes;
				for (int i = 0; i < array.Length; i++)
				{
					SupportedMimeType supportedMimeType = (SupportedMimeType)array[i];
					file_types.Add(supportedMimeType.MimeType, type);
				}
			}
		}
	}
	public static class Genres
	{
		private static readonly string[] audio = new string[148]
		{
			"Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal",
			"New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial",
			"Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
			"Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise",
			"Alternative Rock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic",
			"Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
			"Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychedelic", "Rave", "Showtunes",
			"Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock",
			"Folk", "Folk/Rock", "National Folk", "Swing", "Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass",
			"Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic",
			"Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove",
			"Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
			"Duet", "Punk Rock", "Drum Solo", "A Cappella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore",
			"Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover",
			"Contemporary Christian", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "Jpop", "Synthpop"
		};

		private static readonly string[] video = new string[40]
		{
			"Action", "Action/Adventure", "Adult", "Adventure", "Catastrophe", "Child's", "Claymation", "Comedy", "Concert", "Documentary",
			"Drama", "Eastern", "Entertaining", "Erotic", "Extremal Sport", "Fantasy", "Fashion", "Historical", "Horror", "Horror/Mystic",
			"Humor", "Indian", "Informercial", "Melodrama", "Military & War", "Music Video", "Musical", "Mystery", "Nature", "Political Satire",
			"Popular Science", "Psychological Thriller", "Religion", "Science Fiction", "Scifi Action", "Slapstick", "Splatter", "Sports", "Thriller", "Western"
		};

		public static string[] Audio => (string[])audio.Clone();

		public static string[] Video => (string[])video.Clone();

		public static byte AudioToIndex(string name)
		{
			for (byte b = 0; b < audio.Length; b++)
			{
				if (name == audio[b])
				{
					return b;
				}
			}
			return byte.MaxValue;
		}

		public static byte VideoToIndex(string name)
		{
			for (byte b = 0; b < video.Length; b++)
			{
				if (name == video[b])
				{
					return b;
				}
			}
			return byte.MaxValue;
		}

		public static string IndexToAudio(byte index)
		{
			if (index >= audio.Length)
			{
				return null;
			}
			return audio[index];
		}

		public static string IndexToVideo(byte index)
		{
			if (index >= video.Length)
			{
				return null;
			}
			return video[index];
		}

		public static string IndexToAudio(string text)
		{
			return IndexToAudio(StringToByte(text));
		}

		public static string IndexToVideo(string text)
		{
			return IndexToVideo(StringToByte(text));
		}

		private static byte StringToByte(string text)
		{
			int num;
			if (text != null && text.Length > 2 && text[0] == '(' && (num = text.IndexOf(')')) != -1 && byte.TryParse(text.Substring(1, num - 1), out var result))
			{
				return result;
			}
			if (text != null && byte.TryParse(text, out result))
			{
				return result;
			}
			return byte.MaxValue;
		}
	}
	[Flags]
	public enum MediaTypes
	{
		None = 0,
		Audio = 1,
		Video = 2,
		Photo = 4,
		Text = 8
	}
	public interface ICodec
	{
		TimeSpan Duration { get; }

		MediaTypes MediaTypes { get; }

		string Description { get; }
	}
	public interface IAudioCodec : ICodec
	{
		int AudioBitrate { get; }

		int AudioSampleRate { get; }

		int AudioChannels { get; }
	}
	public interface ILosslessAudioCodec
	{
		int BitsPerSample { get; }
	}
	public interface IVideoCodec : ICodec
	{
		int VideoWidth { get; }

		int VideoHeight { get; }
	}
	public interface IPhotoCodec : ICodec
	{
		int PhotoWidth { get; }

		int PhotoHeight { get; }

		int PhotoQuality { get; }
	}
	public interface ILazy
	{
		bool IsLoaded { get; }

		void Load();
	}
	public class ListBase<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable where T : IComparable<T>
	{
		private readonly List<T> data = new List<T>();

		public bool IsEmpty => Count == 0;

		public bool IsReadOnly => false;

		public bool IsFixedSize => false;

		public T this[int index]
		{
			get
			{
				return data[index];
			}
			set
			{
				data[index] = value;
			}
		}

		public int Count => data.Count;

		public bool IsSynchronized => false;

		public object SyncRoot => this;

		public ListBase()
		{
		}

		public ListBase(ListBase<T> list)
		{
			if (list != null)
			{
				Add(list);
			}
		}

		public ListBase(params T[] list)
		{
			if (list != null)
			{
				Add(list);
			}
		}

		public void Add(ListBase<T> list)
		{
			if (list != null)
			{
				data.AddRange(list);
			}
		}

		public void Add(IEnumerable<T> list)
		{
			if (list != null)
			{
				data.AddRange(list);
			}
		}

		public void Add(T[] list)
		{
			if (list != null)
			{
				data.AddRange(list);
			}
		}

		public virtual void SortedInsert(T item, bool unique)
		{
			if (item == null)
			{
				throw new ArgumentNullException("item");
			}
			int i;
			for (i = 0; i < data.Count; i++)
			{
				if (item.CompareTo(data[i]) == 0 && unique)
				{
					return;
				}
				if (item.CompareTo(data[i]) <= 0)
				{
					break;
				}
			}
			Insert(i, item);
		}

		public void SortedInsert(T item)
		{
			if (item == null)
			{
				throw new ArgumentNullException("item");
			}
			SortedInsert(item, unique: false);
		}

		public T[] ToArray()
		{
			return data.ToArray();
		}

		public void Add(T item)
		{
			data.Add(item);
		}

		public void Clear()
		{
			data.Clear();
		}

		public bool Contains(T item)
		{
			return data.Contains(item);
		}

		public int IndexOf(T item)
		{
			return data.IndexOf(item);
		}

		public void Insert(int index, T item)
		{
			data.Insert(index, item);
		}

		public bool Remove(T item)
		{
			return data.Remove(item);
		}

		public void RemoveAt(int index)
		{
			data.RemoveAt(index);
		}

		public string ToString(string separator)
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < Count; i++)
			{
				if (i != 0)
				{
					stringBuilder.Append(separator);
				}
				stringBuilder.Append(this[i]);
			}
			return stringBuilder.ToString();
		}

		public override string ToString()
		{
			return ToString(", ");
		}

		public void CopyTo(T[] array, int arrayIndex)
		{
			data.CopyTo(array, arrayIndex);
		}

		public IEnumerator<T> GetEnumerator()
		{
			return data.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return data.GetEnumerator();
		}
	}
	public class TextBox : Box
	{
		private ByteVector data;

		public override ByteVector Data
		{
			get
			{
				return data;
			}
			set
			{
				data = value;
			}
		}

		public TextBox(BoxHeader header, File file, IsoHandlerBox handler)
			: base(header, handler)
		{
			if (file == null)
			{
				throw new ArgumentNullException("file");
			}
			data = LoadData(file);
		}
	}
	public class UrlBox : Box
	{
		private ByteVector data;

		public override ByteVector Data
		{
			get
			{
				return data;
			}
			set
			{
				data = value;
			}
		}

		public UrlBox(BoxHeader header, File file, IsoHandlerBox handler)
			: base(header, handler)
		{
			if (file == null)
			{
				throw new ArgumentNullException("file");
			}
			data = LoadData(file);
		}
	}
	public enum PictureType
	{
		Other = 0,
		FileIcon = 1,
		OtherFileIcon = 2,
		FrontCover = 3,
		BackCover = 4,
		LeafletPage = 5,
		Media = 6,
		LeadArtist = 7,
		Artist = 8,
		Conductor = 9,
		Band = 10,
		Composer = 11,
		Lyricist = 12,
		RecordingLocation = 13,
		DuringRecording = 14,
		DuringPerformance = 15,
		MovieScreenCapture = 16,
		ColoredFish = 17,
		Illustration = 18,
		BandLogo = 19,
		PublisherLogo = 20,
		NotAPicture = 255
	}
	public interface IPicture
	{
		string MimeType { get; set; }

		PictureType Type { get; set; }

		string Filename { get; set; }

		string Description { get; set; }

		ByteVector Data { get; set; }
	}
	public class Picture : IPicture
	{
		private static readonly string[] lutExtensionMime = new string[150]
		{
			"aac", "audio/aac", "abw", "application/x-abiword", "arc", "application/octet-stream", "avi", "video/x-msvideo", "azw", "application/vnd.amazon.ebook",
			"bin", "application/octet-stream", "bmp", "image/bmp", "bmp", "image/x-windows-bmp", "bm", "image/bmp", "bz", "application/x-bzip",
			"bz2", "application/x-bzip2", "csh", "application/x-csh", "css", "text/css", "csv", "text/csv", "doc", "application/msword",
			"eot", "application/vnd.ms-fontobject", "epub", "application/epub+zip", "gif", "image/gif", "htm", "text/html", "html", "text/html",
			"ico", "image/x-icon", "ics", "text/calendar", "jar", "application/java-archive", "jpg", "image/jpeg", "jpeg", "image/jpeg",
			"js", "application/javascript", "json", "application/json", "mid", "audio/midi", "midi", "audio/midi", "mp3", "audio/mpeg",
			"mp1", "audio/mpeg", "mp2", "audio/mpeg", "mpg", "video/mpeg", "mpeg", "video/mpeg", "m4a", "audio/mp4",
			"mp4", "video/mp4", "m4v", "video/mp4", "mpkg", "application/vnd.apple.installer+xml", "odp", "application/vnd.oasis.opendocument.presentation", "ods", "application/vnd.oasis.opendocument.spreadsheet",
			"odt", "application/vnd.oasis.opendocument.text", "oga", "audio/ogg", "ogg", "audio/ogg", "ogx", "application/ogg", "ogv", "video/ogg",
			"otf", "font/otf", "png", "image/png", "pdf", "application/pdf", "ppt", "application/vnd.ms-powerpoint", "rar", "application/x-rar-compressed",
			"rtf", "application/rtf", "sh", "application/x-sh", "svg", "image/svg+xml", "swf", "application/x-shockwave-flash", "tar", "application/x-tar",
			"tif", "image/tiff", "tiff", "image/tiff", "ts", "video/vnd.dlna.mpeg-tts", "ttf", "font/ttf", "vsd", "application/vnd.visio",
			"wav", "audio/x-wav", "weba", "audio/webm", "webm", "video/webm", "webp", "image/webp", "woff", "font/woff",
			"woff2", "font/woff2", "xhtml", "application/xhtml+xml", "xls", "application/vnd.ms", "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xml", "application/xml",
			"xul", "application/vnd.mozilla.xul+xml", "zip", "application/zip", "3gp", "video/3gpp", "3g2", "video/3gpp2", "7z", "application/x-7z-compressed"
		};

		public string MimeType { get; set; }

		public PictureType Type { get; set; }

		public string Filename { get; set; }

		public string Description { get; set; }

		public ByteVector Data { get; set; }

		public Picture()
		{
		}

		public Picture(string path)
		{
			if (path == null)
			{
				throw new ArgumentNullException("path");
			}
			Data = ByteVector.FromPath(path);
			Filename = Path.GetFileName(path);
			Description = Filename;
			MimeType = GetMimeFromExtension(Filename);
			Type = (MimeType.StartsWith("image/") ? PictureType.FrontCover : PictureType.NotAPicture);
		}

		public Picture(File.IFileAbstraction abstraction)
		{
			if (abstraction == null)
			{
				throw new ArgumentNullException("abstraction");
			}
			Data = ByteVector.FromFile(abstraction);
			Filename = abstraction.Name;
			Description = abstraction.Name;
			if (!string.IsNullOrEmpty(Filename) && Filename.Contains("."))
			{
				MimeType = GetMimeFromExtension(Filename);
				Type = (MimeType.StartsWith("image/") ? PictureType.FrontCover : PictureType.NotAPicture);
				return;
			}
			string extensionFromData = GetExtensionFromData(Data);
			MimeType = GetMimeFromExtension(extensionFromData);
			if (extensionFromData != null)
			{
				Type = PictureType.FrontCover;
				Filename = (Description = "cover" + extensionFromData);
			}
			else
			{
				Type = PictureType.NotAPicture;
				Filename = "UnknownType";
			}
		}

		public Picture(ByteVector data)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			Data = new ByteVector(data);
			string extensionFromData = GetExtensionFromData(data);
			MimeType = GetMimeFromExtension(extensionFromData);
			if (extensionFromData != null)
			{
				Type = PictureType.FrontCover;
				Filename = (Description = "cover" + extensionFromData);
			}
			else
			{
				Type = PictureType.NotAPicture;
				Filename = "UnknownType";
			}
		}

		public Picture(IPicture picture)
		{
			MimeType = picture.MimeType;
			Type = picture.Type;
			Filename = picture.Filename;
			Description = picture.Description;
			Data = picture.Data;
		}

		[Obsolete("Use Picture(string filename) constructor instead.")]
		public static Picture CreateFromPath(string filename)
		{
			return new Picture(filename);
		}

		[Obsolete("Use Picture(File.IFileAbstraction abstraction) constructor instead.")]
		public static Picture CreateFromFile(File.IFileAbstraction abstraction)
		{
			return new Picture(abstraction);
		}

		public static string GetExtensionFromData(ByteVector data)
		{
			string result = null;
			if (data.Count >= 4)
			{
				if (data[1] == 80 && data[2] == 78 && data[3] == 71)
				{
					result = ".png";
				}
				else if (data[0] == 71 && data[1] == 73 && data[2] == 70)
				{
					result = ".gif";
				}
				else if (data[0] == 66 && data[1] == 77)
				{
					result = ".bmp";
				}
				else if (data[0] == byte.MaxValue && data[1] == 216 && data[data.Count - 2] == byte.MaxValue && data[data.Count - 1] == 217)
				{
					result = ".jpg";
				}
			}
			return result;
		}

		public static string GetExtensionFromMime(string mime)
		{
			string result = null;
			for (int i = 1; i < lutExtensionMime.Length; i += 2)
			{
				if (lutExtensionMime[i] == mime)
				{
					result = lutExtensionMime[i - 1];
					break;
				}
			}
			return result;
		}

		public static string GetMimeFromExtension(string name)
		{
			string result = "application/octet-stream";
			if (string.IsNullOrEmpty(name))
			{
				return result;
			}
			string extension = Path.GetExtension(name);
			extension = ((!string.IsNullOrEmpty(extension)) ? extension.Substring(1) : name);
			extension = extension.ToLower();
			for (int i = 0; i < lutExtensionMime.Length; i += 2)
			{
				if (lutExtensionMime[i] == extension)
				{
					result = lutExtensionMime[i + 1];
					break;
				}
			}
			return result;
		}
	}
	public class PictureLazy : IPicture, ILazy
	{
		private string mime_type;

		private PictureType type;

		private string filename;

		private ByteVector data;

		private File.IFileAbstraction file;

		private readonly long stream_offset;

		private readonly long stream_size = -1L;

		public string MimeType
		{
			get
			{
				if (mime_type == null)
				{
					Load();
				}
				return mime_type;
			}
			set
			{
				mime_type = value;
			}
		}

		public PictureType Type
		{
			get
			{
				if (type == PictureType.Other && mime_type == null)
				{
					Load();
				}
				return type;
			}
			set
			{
				type = value;
			}
		}

		public string Filename
		{
			get
			{
				if (filename == null)
				{
					Load();
				}
				return filename;
			}
			set
			{
				filename = value;
			}
		}

		public string Description { get; set; }

		public ByteVector Data
		{
			get
			{
				if (data == null)
				{
					Load();
				}
				return data;
			}
			set
			{
				data = value;
			}
		}

		public bool IsLoaded => data != null;

		public PictureLazy()
		{
		}

		public PictureLazy(string path)
		{
			if (path == null)
			{
				throw new ArgumentNullException("path");
			}
			file = new File.LocalFileAbstraction(path);
			filename = Path.GetFileName(path);
			Description = filename;
			mime_type = Picture.GetMimeFromExtension(filename);
			type = (mime_type.StartsWith("image/") ? PictureType.FrontCover : PictureType.NotAPicture);
		}

		public PictureLazy(File.IFileAbstraction abstraction, long offset = 0L, long size = -1L)
		{
			if (abstraction == null)
			{
				throw new ArgumentNullException("abstraction");
			}
			file = abstraction;
			stream_offset = offset;
			stream_size = size;
			filename = abstraction.Name;
			Description = abstraction.Name;
			if (!string.IsNullOrEmpty(filename) && filename.Contains("."))
			{
				mime_type = Picture.GetMimeFromExtension(filename);
				type = (mime_type.StartsWith("image/") ? PictureType.FrontCover : PictureType.NotAPicture);
			}
		}

		public PictureLazy(ByteVector data)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			Data = new ByteVector(data);
			string extensionFromData = Picture.GetExtensionFromData(data);
			MimeType = Picture.GetMimeFromExtension(extensionFromData);
			if (extensionFromData != null)
			{
				type = PictureType.FrontCover;
				filename = (Description = "cover" + extensionFromData);
			}
			else
			{
				type = PictureType.NotAPicture;
				filename = "UnknownType";
			}
		}

		public PictureLazy(IPicture picture)
		{
			mime_type = picture.MimeType;
			type = picture.Type;
			filename = picture.Filename;
			Description = picture.Description;
			data = picture.Data;
		}

		public void Load()
		{
			if (data != null)
			{
				return;
			}
			Stream stream = null;
			try
			{
				if (stream_size == 0L)
				{
					data = new ByteVector();
				}
				else if (stream_size > 0)
				{
					stream = file.ReadStream;
					stream.Seek(stream_offset, SeekOrigin.Begin);
					int num = 0;
					int num2 = 0;
					int num3 = (int)stream_size;
					byte[] buffer = new byte[num3];
					do
					{
						num = stream.Read(buffer, num2, num3);
						num2 += num;
						num3 -= num;
					}
					while (num3 > 0 && num != 0);
					data = new ByteVector(buffer, num2);
				}
				else
				{
					stream = file.ReadStream;
					stream.Seek(stream_offset, SeekOrigin.Begin);
					data = ByteVector.FromStream(stream);
				}
			}
			finally
			{
				if (stream != null && file != null)
				{
					file.CloseStream(stream);
				}
				file = null;
			}
			if (mime_type != null)
			{
				return;
			}
			string extensionFromData = Picture.GetExtensionFromData(data);
			MimeType = Picture.GetMimeFromExtension(extensionFromData);
			if (extensionFromData != null)
			{
				type = PictureType.FrontCover;
				if (filename == null)
				{
					string text2 = (Description = "cover" + extensionFromData);
					filename = text2;
				}
			}
			else
			{
				type = PictureType.NotAPicture;
				if (filename == null)
				{
					filename = "UnknownType";
				}
			}
		}
	}
	public class Properties : IAudioCodec, ICodec, IVideoCodec, IPhotoCodec
	{
		private readonly ICodec[] codecs = new ICodec[0];

		private TimeSpan duration = TimeSpan.Zero;

		public IEnumerable<ICodec> Codecs => codecs;

		public TimeSpan Duration
		{
			get
			{
				TimeSpan timeSpan = duration;
				if (timeSpan != TimeSpan.Zero)
				{
					return timeSpan;
				}
				ICodec[] array = codecs;
				foreach (ICodec codec in array)
				{
					if (codec != null && codec.Duration > timeSpan)
					{
						timeSpan = codec.Duration;
					}
				}
				return timeSpan;
			}
		}

		public MediaTypes MediaTypes
		{
			get
			{
				MediaTypes mediaTypes = MediaTypes.None;
				ICodec[] array = codecs;
				foreach (ICodec codec in array)
				{
					if (codec != null)
					{
						mediaTypes |= codec.MediaTypes;
					}
				}
				return mediaTypes;
			}
		}

		public string Description
		{
			get
			{
				StringBuilder stringBuilder = new StringBuilder();
				ICodec[] array = codecs;
				foreach (ICodec codec in array)
				{
					if (codec != null)
					{
						if (stringBuilder.Length != 0)
						{
							stringBuilder.Append("; ");
						}
						stringBuilder.Append(codec.Description);
					}
				}
				return stringBuilder.ToString();
			}
		}

		public int AudioBitrate
		{
			get
			{
				ICodec[] array = codecs;
				foreach (ICodec codec in array)
				{
					if (codec != null && (codec.MediaTypes & MediaTypes.Audio) != 0 && codec is IAudioCodec audioCodec && audioCodec.AudioBitrate != 0)
					{
						return audioCodec.AudioBitrate;
					}
				}
				return 0;
			}
		}

		public int AudioSampleRate
		{
			get
			{
				ICodec[] array = codecs;
				foreach (ICodec codec in array)
				{
					if (codec != null && (codec.MediaTypes & MediaTypes.Audio) != 0 && codec is IAudioCodec audioCodec && audioCodec.AudioSampleRate != 0)
					{
						return audioCodec.AudioSampleRate;
					}
				}
				return 0;
			}
		}

		public int BitsPerSample
		{
			get
			{
				ICodec[] array = codecs;
				foreach (ICodec codec in array)
				{
					if (codec != null && (codec.MediaTypes & MediaTypes.Audio) != 0 && codec is ILosslessAudioCodec losslessAudioCodec && losslessAudioCodec.BitsPerSample != 0)
					{
						return losslessAudioCodec.BitsPerSample;
					}
				}
				return 0;
			}
		}

		public int AudioChannels
		{
			get
			{
				ICodec[] array = codecs;
				foreach (ICodec codec in array)
				{
					if (codec != null && (codec.MediaTypes & MediaTypes.Audio) != 0 && codec is IAudioCodec audioCodec && audioCodec.AudioChannels != 0)
					{
						return audioCodec.AudioChannels;
					}
				}
				return 0;
			}
		}

		public int VideoWidth
		{
			get
			{
				ICodec[] array = codecs;
				foreach (ICodec codec in array)
				{
					if (codec != null && (codec.MediaTypes & MediaTypes.Video) != 0 && codec is IVideoCodec videoCodec && videoCodec.VideoWidth != 0)
					{
						return videoCodec.VideoWidth;
					}
				}
				return 0;
			}
		}

		public int VideoHeight
		{
			get
			{
				ICodec[] array = codecs;
				foreach (ICodec codec in array)
				{
					if (codec != null && (codec.MediaTypes & MediaTypes.Video) != 0 && codec is IVideoCodec videoCodec && videoCodec.VideoHeight != 0)
					{
						return videoCodec.VideoHeight;
					}
				}
				return 0;
			}
		}

		public int PhotoWidth
		{
			get
			{
				ICodec[] array = codecs;
				foreach (ICodec codec in array)
				{
					if (codec != null && (codec.MediaTypes & MediaTypes.Photo) != 0 && codec is IPhotoCodec photoCodec && photoCodec.PhotoWidth != 0)
					{
						return photoCodec.PhotoWidth;
					}
				}
				return 0;
			}
		}

		public int PhotoHeight
		{
			get
			{
				ICodec[] array = codecs;
				foreach (ICodec codec in array)
				{
					if (codec != null && (codec.MediaTypes & MediaTypes.Photo) != 0 && codec is IPhotoCodec photoCodec && photoCodec.PhotoHeight != 0)
					{
						return photoCodec.PhotoHeight;
					}
				}
				return 0;
			}
		}

		public int PhotoQuality
		{
			get
			{
				ICodec[] array = codecs;
				foreach (ICodec codec in array)
				{
					if (codec != null && (codec.MediaTypes & MediaTypes.Photo) != 0 && codec is IPhotoCodec photoCodec && photoCodec.PhotoQuality != 0)
					{
						return photoCodec.PhotoQuality;
					}
				}
				return 0;
			}
		}

		public Properties()
		{
		}

		public Properties(TimeSpan duration, params ICodec[] codecs)
		{
			this.duration = duration;
			if (codecs != null)
			{
				this.codecs = codecs;
			}
		}

		public Properties(TimeSpan duration, IEnumerable<ICodec> codecs)
		{
			this.duration = duration;
			if (codecs != null)
			{
				this.codecs = new List<ICodec>(codecs).ToArray();
			}
		}
	}
	public sealed class ReadOnlyByteVector : ByteVector
	{
		public override bool IsReadOnly => true;

		public override bool IsFixedSize => true;

		public ReadOnlyByteVector()
		{
		}

		public ReadOnlyByteVector(int size, byte value)
			: base(size, value)
		{
		}

		public ReadOnlyByteVector(int size)
			: this(size, 0)
		{
		}

		public ReadOnlyByteVector(ByteVector vector)
			: base(vector)
		{
		}

		public ReadOnlyByteVector(byte[] data, int length)
			: base(data, length)
		{
		}

		public ReadOnlyByteVector(params byte[] data)
			: base(data)
		{
		}

		public static implicit operator ReadOnlyByteVector(byte value)
		{
			return new ReadOnlyByteVector(value);
		}

		public static implicit operator ReadOnlyByteVector(byte[] value)
		{
			return new ReadOnlyByteVector(value);
		}

		public static implicit operator ReadOnlyByteVector(string value)
		{
			return new ReadOnlyByteVector(ByteVector.FromString(value, StringType.UTF8));
		}
	}
	[ComVisible(false)]
	public class StringCollection : ListBase<string>
	{
		public StringCollection()
		{
		}

		public StringCollection(StringCollection values)
		{
			Add(values);
		}

		public StringCollection(params string[] values)
		{
			Add(values);
		}

		public StringCollection(ByteVectorCollection vectorList, StringType type)
		{
			foreach (ByteVector vector in vectorList)
			{
				Add(vector.ToString(type));
			}
		}

		public StringCollection(ByteVectorCollection vectorList)
			: this(vectorList, StringType.UTF8)
		{
		}

		public static StringCollection Split(string value, string pattern)
		{
			if (value == null)
			{
				throw new ArgumentNullException("value");
			}
			if (pattern == null)
			{
				throw new ArgumentNullException("pattern");
			}
			StringCollection stringCollection = new StringCollection();
			int num = 0;
			int num2 = value.IndexOf(pattern, 0);
			int length = pattern.Length;
			while (num2 != -1)
			{
				stringCollection.Add(value.Substring(num, num2 - num));
				num = num2 + length;
				num2 = value.IndexOf(pattern, num);
			}
			stringCollection.Add(value.Substring(num));
			return stringCollection;
		}
	}
	[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
	public sealed class SupportedMimeType : Attribute
	{
		private static readonly List<SupportedMimeType> mimetypes;

		public string MimeType { get; private set; }

		public string Extension { get; private set; }

		public static IEnumerable<string> AllMimeTypes
		{
			get
			{
				foreach (SupportedMimeType mimetype in mimetypes)
				{
					yield return mimetype.MimeType;
				}
			}
		}

		public static IEnumerable<string> AllExtensions
		{
			get
			{
				foreach (SupportedMimeType mimetype in mimetypes)
				{
					if (mimetype.Extension != null)
					{
						yield return mimetype.Extension;
					}
				}
			}
		}

		static SupportedMimeType()
		{
			mimetypes = new List<SupportedMimeType>();
			FileTypes.Init();
		}

		public SupportedMimeType(string mimetype)
		{
			MimeType = mimetype;
			mimetypes.Add(this);
		}

		public SupportedMimeType(string mimetype, string extension)
			: this(mimetype)
		{
			Extension = extension;
		}
	}
	[Flags]
	public enum TagTypes : uint
	{
		None = 0u,
		Xiph = 1u,
		Id3v1 = 2u,
		Id3v2 = 4u,
		Ape = 8u,
		Apple = 0x10u,
		Asf = 0x20u,
		RiffInfo = 0x40u,
		MovieId = 0x80u,
		DivX = 0x100u,
		FlacMetadata = 0x200u,
		TiffIFD = 0x400u,
		XMP = 0x800u,
		JpegComment = 0x1000u,
		GifComment = 0x2000u,
		Png = 0x4000u,
		IPTCIIM = 0x8000u,
		AudibleMetadata = 0x10000u,
		Matroska = 0x20000u,
		AllTags = uint.MaxValue
	}
	public abstract class Tag
	{
		public abstract TagTypes TagTypes { get; }

		public virtual string Title
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string TitleSort
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string Subtitle
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string Description
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string[] Performers
		{
			get
			{
				return new string[0];
			}
			set
			{
			}
		}

		public virtual string[] PerformersSort
		{
			get
			{
				return new string[0];
			}
			set
			{
			}
		}

		public virtual string[] PerformersRole
		{
			get
			{
				return new string[0];
			}
			set
			{
			}
		}

		public virtual string[] AlbumArtists
		{
			get
			{
				return new string[0];
			}
			set
			{
			}
		}

		public virtual string[] AlbumArtistsSort
		{
			get
			{
				return new string[0];
			}
			set
			{
			}
		}

		public virtual string[] Composers
		{
			get
			{
				return new string[0];
			}
			set
			{
			}
		}

		public virtual string[] ComposersSort
		{
			get
			{
				return new string[0];
			}
			set
			{
			}
		}

		public virtual string Album
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string AlbumSort
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string Comment
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string[] Genres
		{
			get
			{
				return new string[0];
			}
			set
			{
			}
		}

		public virtual uint Year
		{
			get
			{
				return 0u;
			}
			set
			{
			}
		}

		public virtual uint Track
		{
			get
			{
				return 0u;
			}
			set
			{
			}
		}

		public virtual uint TrackCount
		{
			get
			{
				return 0u;
			}
			set
			{
			}
		}

		public virtual uint Disc
		{
			get
			{
				return 0u;
			}
			set
			{
			}
		}

		public virtual uint DiscCount
		{
			get
			{
				return 0u;
			}
			set
			{
			}
		}

		public virtual string Lyrics
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string Grouping
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual uint BeatsPerMinute
		{
			get
			{
				return 0u;
			}
			set
			{
			}
		}

		public virtual string Conductor
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string Copyright
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual DateTime? DateTagged
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string MusicBrainzArtistId
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string MusicBrainzReleaseGroupId
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string MusicBrainzReleaseId
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string MusicBrainzReleaseArtistId
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string MusicBrainzTrackId
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string MusicBrainzDiscId
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string MusicIpId
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string AmazonId
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string MusicBrainzReleaseStatus
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string MusicBrainzReleaseType
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string MusicBrainzReleaseCountry
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual double ReplayGainTrackGain
		{
			get
			{
				return double.NaN;
			}
			set
			{
			}
		}

		public virtual double ReplayGainTrackPeak
		{
			get
			{
				return double.NaN;
			}
			set
			{
			}
		}

		public virtual double ReplayGainAlbumGain
		{
			get
			{
				return double.NaN;
			}
			set
			{
			}
		}

		public virtual double ReplayGainAlbumPeak
		{
			get
			{
				return double.NaN;
			}
			set
			{
			}
		}

		public virtual string InitialKey
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string RemixedBy
		{
			get
			{
				return null;
			}
			set
			{
			}
		}

		public virtual string Publisher
		{
			get