Decompiled source of SongOrganizer v1.3.10

SongOrganizer.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using BaboonAPI.Hooks.Tracks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.FSharp.Collections;
using Microsoft.FSharp.Core;
using Newtonsoft.Json;
using SongOrganizer.Data;
using SongOrganizer.Utils;
using TMPro;
using TrombLoader.CustomTracks;
using TrombLoader.Helpers;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Localization.Components;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("SongOrganizer")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Trombone Champ: Searching, Filtering, and Sorting")]
[assembly: AssemblyFileVersion("1.3.10.0")]
[assembly: AssemblyInformationalVersion("1.3.10")]
[assembly: AssemblyProduct("SongOrganizer")]
[assembly: AssemblyTitle("SongOrganizer")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.10.0")]
[module: UnverifiableCode]
namespace SongOrganizer
{
	[BepInPlugin("SongOrganizer", "SongOrganizer", "1.3.10")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public static Plugin Instance;

		public static ManualLogSource Log;

		public static Options Options;

		public static TrackLoaded TrackLoaded;

		public static Toggle Toggle;

		public static Button Button;

		public static TMP_InputField InputFieldPrefab;

		public static TMP_InputField SearchInput;

		public static Image Star;

		public static Dictionary<string, Track> TrackDict = new Dictionary<string, Track>();

		public const int TRACK_SCORE_LENGTH = 5;

		private const string FILTER_SECTION = "Filter";

		private const string SORT_SECTION = "Sort";

		private const string INDEX_SECTION = "Index";

		private const string SEARCH_SECTION = "Search";

		private void Awake()
		{
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Options = new Options
			{
				ShowDefault = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "ShowDefault", true, (ConfigDescription)null),
				ShowCustom = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "ShowCustom", true, (ConfigDescription)null),
				ShowUnplayed = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "ShowUnplayed", true, (ConfigDescription)null),
				ShowPlayed = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "ShowPlayed", true, (ConfigDescription)null),
				ShowNotSRank = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "ShowNotSRank", true, (ConfigDescription)null),
				ShowSRank = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "ShowSRank", true, (ConfigDescription)null),
				ShowUnrated = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "ShowUnrated", true, (ConfigDescription)null),
				ShowRated = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "ShowRated", true, (ConfigDescription)null),
				SortMode = ((BaseUnityPlugin)this).Config.Bind<string>("Sort", "SortMode", "default", (ConfigDescription)null),
				LastIndex = ((BaseUnityPlugin)this).Config.Bind<int>("Index", "LastIndex", 0, (ConfigDescription)null),
				SearchValue = ((BaseUnityPlugin)this).Config.Bind<string>("Search", "SearchValue", "", (ConfigDescription)null)
			};
			((MonoBehaviour)this).StartCoroutine((IEnumerator)TootTally.GetRatedTracks());
			SceneManager.sceneUnloaded += UnloadModule;
			new Harmony("SongOrganizer").PatchAll();
		}

		public static void UnloadModule(Scene scene)
		{
			UnloadModule();
		}

		public static void UnloadModule()
		{
			if (TrackLoaded != null)
			{
				TracksLoadedEvent.EVENT.Unregister((Listener)(object)TrackLoaded);
				TrackLoaded = null;
			}
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "SongOrganizer";

		public const string PLUGIN_NAME = "SongOrganizer";

		public const string PLUGIN_VERSION = "1.3.10";
	}
}
namespace SongOrganizer.Utils
{
	public class TrackLoaded : Listener
	{
		private LevelSelectController __instance;

		private IEnumerable<TootTally.SearchTrackResult> ratedTracks;

		public TrackLoaded(LevelSelectController instance, IEnumerable<TootTally.SearchTrackResult> ratedTracks)
		{
			__instance = instance;
			this.ratedTracks = ratedTracks;
		}

		public void OnTracksLoaded(FSharpList<TromboneTrack> tracks)
		{
			AddTracks(ratedTracks, __instance);
			FilterTracks(__instance);
		}

		public static void AddTracks(IEnumerable<TootTally.SearchTrackResult> ratedTracks, LevelSelectController __instance)
		{
			DateTime now = DateTime.Now;
			Plugin.Log.LogDebug((object)$"Loading tracks: {__instance.alltrackslist.Count} total");
			Dictionary<string, TootTally.SearchTrackResult> dictionary = ratedTracks.ToDictionary((TootTally.SearchTrackResult i) => i.file_hash);
			HashSet<string> hashSet = new HashSet<string>(ratedTracks.Select((TootTally.SearchTrackResult i) => i.track_ref));
			HashSet<string> foundRatedTrackNoteHashes = new HashSet<string>();
			Plugin.TrackDict.Clear();
			foreach (SingleTrackData item in __instance.alltrackslist)
			{
				bool flag = false;
				if (hashSet.Contains(item.trackref))
				{
					if (Globals.IsCustomTrack(item.trackref))
					{
						TromboneTrack obj = TrackLookup.lookup(item.trackref);
						CustomTrack val = (CustomTrack)(object)((obj is CustomTrack) ? obj : null);
						string fileLocation = Path.Combine(val.folderPath, Globals.defaultChartName);
						string key = CalcFileHash(fileLocation);
						flag = dictionary.ContainsKey(key);
						if (flag)
						{
							foundRatedTrackNoteHashes.Add(dictionary[key].note_hash);
						}
					}
					else
					{
						flag = true;
					}
				}
				Track track2 = new Track(item)
				{
					custom = Globals.IsCustomTrack(item.trackref),
					rated = flag
				};
				FSharpOption<SavedTrackScore> val2 = TrackLookup.lookupScore(item.trackref);
				track2.letterScore = ((val2 != null) ? val2.Value.highestRank : "-");
				track2.scores = ((val2 != null) ? ((IEnumerable<int>)val2.Value.highScores).ToArray() : new int[5]);
				Plugin.TrackDict.TryAdd(item.trackref, track2);
			}
			List<TootTally.SearchTrackResult> list = ratedTracks.Where((TootTally.SearchTrackResult track) => !foundRatedTrackNoteHashes.Contains(track.note_hash) && !track.is_official).ToList();
			Plugin.Log.LogInfo((object)$"Rated tracks: {ratedTracks.Count()} total. {list.Count} missing");
			foreach (TootTally.SearchTrackResult item2 in list.OrderBy((TootTally.SearchTrackResult i) => i.short_name))
			{
				Plugin.Log.LogDebug((object)(item2.short_name + " - " + ((item2.mirror == null) ? item2.download : item2.mirror)));
			}
			Plugin.Log.LogDebug((object)$"Elapsed {DateTime.Now - now}");
		}

		public static string CalcFileHash(string fileLocation)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(File.ReadAllText(fileLocation));
			using SHA256 sHA = SHA256.Create();
			StringBuilder stringBuilder = new StringBuilder();
			byte[] array = sHA.ComputeHash(bytes);
			byte[] array2 = array;
			foreach (byte b in array2)
			{
				stringBuilder.Append(b.ToString("x2"));
			}
			return stringBuilder.ToString();
		}

		public static void FilterTracks(LevelSelectController __instance)
		{
			List<string[]> list = new List<string[]>();
			List<SingleTrackData> list2 = new List<SingleTrackData>();
			int num = 0;
			foreach (Track value in Plugin.TrackDict.Values)
			{
				if (ShowTrack(value))
				{
					((SingleTrackData)value).trackindex = num;
					list.Add(new string[10]
					{
						((SingleTrackData)value).trackname_long,
						((SingleTrackData)value).trackname_short,
						((SingleTrackData)value).trackref,
						((SingleTrackData)value).year,
						((SingleTrackData)value).artist,
						((SingleTrackData)value).genre,
						((SingleTrackData)value).desc,
						((SingleTrackData)value).difficulty.ToString(),
						((SingleTrackData)value).length.ToString(),
						((SingleTrackData)value).tempo.ToString()
					});
					list2.Add((SingleTrackData)(object)value);
					num++;
				}
			}
			if (list.Count > 0)
			{
				GlobalVariables.data_tracktitles = list.ToArray();
				__instance.alltrackslist.Clear();
				__instance.alltrackslist.AddRange(list2);
			}
			if (GlobalVariables.levelselect_index >= GlobalVariables.data_tracktitles.Length)
			{
				GlobalVariables.levelselect_index = 0;
			}
			Plugin.Log.LogDebug((object)$"Filter result: {__instance.alltrackslist.Count} found of {Plugin.TrackDict.Count}");
			__instance.sortTracks(Plugin.Options.SortMode.Value.ToLower(), false);
		}

		private static bool ShowTrack(Track track)
		{
			return ShowTrack(Plugin.Options.ShowCustom.Value, Plugin.Options.ShowDefault.Value, track.custom) && ShowTrack(Plugin.Options.ShowPlayed.Value, Plugin.Options.ShowUnplayed.Value, track.letterScore != "-") && ShowTrack(Plugin.Options.ShowSRank.Value, Plugin.Options.ShowNotSRank.Value, track.letterScore == "S") && ShowTrack(Plugin.Options.ShowRated.Value, Plugin.Options.ShowUnrated.Value, track.rated) && ShowTrack(Plugin.Options.SearchValue.Value, track);
		}

		private static bool ShowTrack(bool optionToggle, bool oppositeOptionToggle, bool option)
		{
			return optionToggle == oppositeOptionToggle || optionToggle == option;
		}

		private static bool ShowTrack(string searchVal, Track track)
		{
			if (Utility.IsNullOrWhiteSpace(searchVal))
			{
				return true;
			}
			string value = searchVal.ToLower().Trim();
			return ((SingleTrackData)track).trackname_long.ToLower().Contains(value) || ((SingleTrackData)track).trackname_short.ToLower().Contains(value) || ((SingleTrackData)track).artist.ToLower().Contains(value) || ((SingleTrackData)track).genre.ToLower().Contains(value) || ((SingleTrackData)track).desc.ToLower().Contains(value);
		}
	}
}
namespace SongOrganizer.Patches
{
	[HarmonyPatch(typeof(HomeController), "Start")]
	public class HomeControllerStartPatch : MonoBehaviour
	{
		private static void Postfix(HomeController __instance)
		{
			Plugin.Toggle = Object.Instantiate<Toggle>(__instance.set_tog_vsync);
			Object.DontDestroyOnLoad((Object)(object)Plugin.Toggle);
			Plugin.Button = Object.Instantiate<Button>(__instance.graphicspanel.GetComponentInChildren<Button>());
			((UnityEventBase)Plugin.Button.onClick).RemoveAllListeners();
			Object.DontDestroyOnLoad((Object)(object)Plugin.Button);
			SetInputFieldPrefab();
		}

		private static void SetInputFieldPrefab()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			float num = 250f;
			GameObject val = new GameObject("InputFieldHolderSongOrganizer");
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchoredPosition = Vector2.zero;
			val2.sizeDelta = new Vector2(num, 50f);
			GameObject val3 = Object.Instantiate<GameObject>(val, val.transform);
			GameObject val4 = Object.Instantiate<GameObject>(val3, val.transform);
			((Object)val3).name = "Image";
			((Object)val4).name = "Text";
			Plugin.InputFieldPrefab = val.AddComponent<TMP_InputField>();
			Vector2 anchorMax = (val2.anchorMin = Vector2.zero);
			val2.anchorMax = anchorMax;
			((Selectable)Plugin.InputFieldPrefab).image = val3.AddComponent<Image>();
			RectTransform component = val3.GetComponent<RectTransform>();
			Vector2 val5 = (component.pivot = Vector2.zero);
			anchorMax = (component.anchorMax = val5);
			component.anchorMin = anchorMax;
			component.anchoredPosition = new Vector2(0f, 2f);
			component.sizeDelta = new Vector2(num, 2f);
			RectTransform component2 = val4.GetComponent<RectTransform>();
			Vector2 val7 = (component2.anchorMin = Vector2.zero);
			val5 = (component2.anchoredPosition = val7);
			anchorMax = (component2.anchorMax = val5);
			component2.pivot = anchorMax;
			component2.sizeDelta = new Vector2(num, 50f);
			Plugin.InputFieldPrefab.textComponent = CreateSingleText(val4.transform, "TextLabel", "", Color.white);
			Plugin.InputFieldPrefab.textComponent.rectTransform.pivot = new Vector2(0f, 0.7f);
			Plugin.InputFieldPrefab.textComponent.alignment = (TextAlignmentOptions)513;
			Plugin.InputFieldPrefab.textComponent.margin = new Vector4(2f, 0f, 0f, 0f);
			Plugin.InputFieldPrefab.textComponent.enableWordWrapping = true;
			Plugin.InputFieldPrefab.textViewport = Plugin.InputFieldPrefab.textComponent.rectTransform;
			Object.DontDestroyOnLoad((Object)(object)Plugin.InputFieldPrefab);
		}

		public static TMP_Text CreateSingleText(Transform canvasTransform, string name, string text, Color color)
		{
			//IL_000e: 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_001e: Unknown result type (might be due to invalid IL or missing references)
			return CreateSingleText(canvasTransform, name, text, new Vector2(0f, 1f), ((Component)canvasTransform).GetComponent<RectTransform>().sizeDelta, color);
		}

		public static TMP_Text CreateSingleText(Transform canvasTransform, string name, string text, Vector2 pivot, Vector2 size, Color color)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			GameObject gameObject = GameObject.Find("MainCanvas").gameObject;
			GameObject gameObject2 = ((Component)gameObject.transform.Find("AdvancedInfoPanel/primary-content/intro/copy")).gameObject;
			GameObject val = Object.Instantiate<GameObject>(gameObject2);
			((Object)val).name = "ComfortaaTextPrefab";
			val.SetActive(true);
			Object.DestroyImmediate((Object)(object)val.GetComponent<Text>());
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val2).fontSize = 13f;
			((TMP_Text)val2).text = "DefaultText";
			((TMP_Text)val2).font = TMP_FontAsset.CreateFontAsset(gameObject2.GetComponent<Text>().font);
			((TMP_Text)val2).fontMaterial.SetFloat(ShaderUtilities.ID_FaceDilate, 0.25f);
			((TMP_Text)val2).fontMaterial.EnableKeyword(ShaderUtilities.Keyword_Outline);
			((TMP_Text)val2).fontMaterial.SetFloat(ShaderUtilities.ID_OutlineWidth, 0.25f);
			((TMP_Text)val2).fontMaterial.SetColor(ShaderUtilities.ID_OutlineColor, Color.black);
			((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
			((Component)val2).GetComponent<RectTransform>().sizeDelta = val.GetComponent<RectTransform>().sizeDelta;
			((Component)val2).GetComponent<RectTransform>().anchoredPosition = Vector2.zero;
			((TMP_Text)val2).richText = true;
			((TMP_Text)val2).enableWordWrapping = false;
			Object.DontDestroyOnLoad((Object)(object)val2);
			TMP_Text val3 = (TMP_Text)(object)Object.Instantiate<TextMeshProUGUI>(val2, canvasTransform);
			((Object)val3).name = name;
			val3.text = text;
			((Graphic)val3).color = color;
			((Component)val3).gameObject.GetComponent<RectTransform>().pivot = pivot;
			((Component)val3).gameObject.GetComponent<RectTransform>().sizeDelta = size;
			val3.enableWordWrapping = true;
			((Component)val3).gameObject.SetActive(true);
			return val3;
		}
	}
	[HarmonyPatch(typeof(LevelSelectController), "sortTracks")]
	public class LevelSelectControllerSortTracksPatch : MonoBehaviour
	{
		private static bool Prefix(LevelSelectController __instance, string sortcriteria, bool anim)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Options.SortMode.Value = sortcriteria;
			GlobalVariables.sortmode = sortcriteria;
			__instance.sortlabel.text = "Sort: " + sortcriteria;
			if (anim)
			{
				__instance.clipPlayer.cancelCrossfades();
				__instance.doSfx(__instance.sfx_click);
				__instance.closeSortDropdown();
				__instance.btnspanel.transform.localScale = new Vector3(0.001f, 1f, 1f);
				LeanTween.scaleX(__instance.btnspanel, 1f, 0.2f).setEaseOutQuart();
			}
			switch (sortcriteria)
			{
			case "default":
				__instance.alltrackslist.Sort((SingleTrackData t1, SingleTrackData t2) => t1.trackindex.CompareTo(t2.trackindex));
				break;
			case "difficulty":
				__instance.alltrackslist.Sort((SingleTrackData t1, SingleTrackData t2) => t1.difficulty.CompareTo(t2.difficulty));
				break;
			case "alpha":
				__instance.alltrackslist.Sort((SingleTrackData t1, SingleTrackData t2) => (t1.trackname_short == null) ? (-1) : t1.trackname_short.Trim().CompareTo(t2.trackname_short.Trim()));
				break;
			case "long name":
				__instance.alltrackslist.Sort((SingleTrackData t1, SingleTrackData t2) => (t1.trackname_long == null) ? (-1) : t1.trackname_long.Trim().CompareTo(t2.trackname_long.Trim()));
				break;
			case "length":
				__instance.alltrackslist.Sort((SingleTrackData t1, SingleTrackData t2) => t1.length.CompareTo(t2.length));
				break;
			case "artist":
				__instance.alltrackslist.Sort((SingleTrackData t1, SingleTrackData t2) => (t1.artist == null) ? (-1) : t1.artist.Trim().CompareTo(t2.artist.Trim()));
				break;
			}
			__instance.songindex = ((!anim) ? GlobalVariables.levelselect_index : 0);
			__instance.populateSongNames(true);
			return false;
		}
	}
	[HarmonyPatch(typeof(LevelSelectController), "updateGraph")]
	public class LevelSelectControllerUpdateGraphPatch : MonoBehaviour
	{
		private static bool Prefix(LevelSelectController __instance)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < 5; i++)
			{
				__instance.graphline.SetPosition(i, __instance.getGraphVector(i, (float)Mathf.FloorToInt(Random.value * 100f)));
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(LevelSelectController), "populateSongNames")]
	public class LevelSelectControllerPopulateSongNamesPatch : MonoBehaviour
	{
		private static void Prefix(ref int ___songindex)
		{
			GlobalVariables.levelselect_index = ___songindex;
			Plugin.Options.LastIndex.Value = ___songindex;
		}
	}
	[HarmonyPatch(typeof(LevelSelectController), "searchForSongName")]
	public class LevelSelectControllerSearchFirstLetterPatch : MonoBehaviour
	{
		private static bool Prefix(string startingletter)
		{
			return false;
		}
	}
	[HarmonyPatch(typeof(LevelSelectController), "Update")]
	public class LevelSelectControllerUpdatePatch : MonoBehaviour
	{
		private static void Postfix(LevelSelectController __instance, ref int ___songindex, ref List<SingleTrackData> ___alltrackslist)
		{
			if ((Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown((KeyCode)102))
			{
				((Selectable)Plugin.SearchInput).Select();
			}
			else
			{
				if (Plugin.SearchInput.isFocused || !Input.anyKeyDown || Input.inputString.Length <= 0)
				{
					return;
				}
				char c = Input.inputString.ToLower()[0];
				int num = 1;
				int num2 = ___songindex + 1;
				while (num2 < ___alltrackslist.Count)
				{
					if (___alltrackslist[num2].trackname_short.ToLower().Trim()[0] == c)
					{
						__instance.advanceSongs(num, true);
						return;
					}
					num2++;
					num++;
				}
				int num3 = 0;
				while (num3 < ___songindex)
				{
					if (___alltrackslist[num3].trackname_short.ToLower().Trim()[0] == c)
					{
						__instance.advanceSongs(num, true);
						break;
					}
					num3++;
					num++;
				}
			}
		}
	}
	[HarmonyPatch(typeof(LevelSelectController), "clickBack")]
	public class LevelSelectControllerBackPatch : MonoBehaviour
	{
		private static void Postfix()
		{
			Plugin.UnloadModule();
		}
	}
	[HarmonyPatch(typeof(LevelSelectController), "clickPlay")]
	public class LevelSelectControllerPlayPatch : MonoBehaviour
	{
		private static void Postfix()
		{
			Plugin.UnloadModule();
		}
	}
	[HarmonyPatch(typeof(LevelSelectController), "Start")]
	public class LevelSelectControllerStartPatch : MonoBehaviour
	{
		private const string FULLSCREENPANEL = "MainCanvas/FullScreenPanel";

		private const string LEADERBOARD_PATH = "MainCanvas/FullScreenPanel/Leaderboard";

		private const string SORT_DROPDOWN_PATH = "MainCanvas/FullScreenPanel/sort-dropdown";

		private const string SORT_DROPDROPDOWN_FACE_PATH = "MainCanvas/FullScreenPanel/sort-dropdown/face";

		private const string SORT_BUTTON_PATH = "MainCanvas/FullScreenPanel/sort-dropdown/face/btn_sort_length";

		private const string COMPOSER_NAME_PATH = "MainCanvas/FullScreenPanel/capsules/composername";

		private const string TITLE_BAR = "MainCanvas/FullScreenPanel/title bar";

		private static void Prefix()
		{
			GlobalVariables.sortmode = Plugin.Options.SortMode.Value;
			GlobalVariables.levelselect_index = Plugin.Options.LastIndex.Value;
			if (GlobalVariables.levelselect_index >= GlobalVariables.data_tracktitles.Length)
			{
				GlobalVariables.levelselect_index = 0;
			}
		}

		private static void Postfix(LevelSelectController __instance)
		{
			IEnumerable<TootTally.SearchTrackResult> ratedTracks = TootTally.ReadRatedTracks();
			if (Plugin.TrackLoaded == null)
			{
				Plugin.TrackLoaded = new TrackLoaded(__instance, ratedTracks);
				TracksLoadedEvent.EVENT.Register((Listener)(object)Plugin.TrackLoaded);
				Plugin.TrackLoaded.OnTracksLoaded(null);
			}
			AddOptions(__instance);
			AddSearchBar(__instance);
		}

		private static void AddOptions(LevelSelectController __instance)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: 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)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GameObject.Find("MainCanvas/FullScreenPanel/sort-dropdown");
			val.transform.SetAsLastSibling();
			GameObject val2 = GameObject.Find("MainCanvas/FullScreenPanel/sort-dropdown/face");
			RectTransform component = __instance.sortdrop.GetComponent<RectTransform>();
			RectTransform component2 = val2.GetComponent<RectTransform>();
			CreateSortOption(__instance, val2, "artist", -75f);
			CreateSortOption(__instance, val2, "long name", -105f);
			int num = 430;
			component2.sizeDelta = new Vector2(180f, (float)num);
			component.sizeDelta = new Vector2(180f, (float)num);
			Button[] componentsInChildren = val2.GetComponentsInChildren<Button>();
			foreach (Button val3 in componentsInChildren)
			{
				RectTransform component3 = ((Component)val3).GetComponent<RectTransform>();
				component3.anchoredPosition = new Vector2(component3.anchoredPosition.x, component3.anchoredPosition.y + 90f);
			}
			int num2 = -180;
			foreach (FilterOption value in Enum.GetValues(typeof(FilterOption)))
			{
				Toggle val4 = CreateFilterOption(val2, value, new Vector2(242f, (float)(num2 -= 30)));
				ConfigEntry<bool> configEntry = GetConfigEntry(value);
				if (configEntry != null)
				{
					val4.isOn = configEntry.Value;
					((UnityEvent<bool>)(object)val4.onValueChanged).AddListener((UnityAction<bool>)delegate(bool b)
					{
						ToggleListener(configEntry, b, __instance);
					});
				}
			}
			Button[] componentsInChildren2 = val2.GetComponentsInChildren<Button>();
			foreach (Button val5 in componentsInChildren2)
			{
				RectTransform component4 = ((Component)val5).GetComponent<RectTransform>();
				component4.anchoredPosition = new Vector2(component4.anchoredPosition.x, component4.anchoredPosition.y + 60f);
			}
		}

		private static void ToggleListener(ConfigEntry<bool> configEntry, bool b, LevelSelectController __instance)
		{
			configEntry.Value = b;
			TrackLoaded.FilterTracks(__instance);
		}

		private static ConfigEntry<bool> GetConfigEntry(FilterOption filterOption)
		{
			if (1 == 0)
			{
			}
			ConfigEntry<bool> result = (ConfigEntry<bool>)(filterOption switch
			{
				FilterOption.DEFAULT => Plugin.Options.ShowDefault, 
				FilterOption.CUSTOM => Plugin.Options.ShowCustom, 
				FilterOption.PLAYED => Plugin.Options.ShowPlayed, 
				FilterOption.UNPLAYED => Plugin.Options.ShowUnplayed, 
				FilterOption.NOT_S_RANK => Plugin.Options.ShowNotSRank, 
				FilterOption.S_RANK => Plugin.Options.ShowSRank, 
				FilterOption.UNRATED => Plugin.Options.ShowUnrated, 
				FilterOption.RATED => Plugin.Options.ShowRated, 
				_ => null, 
			});
			if (1 == 0)
			{
			}
			return result;
		}

		private static void CreateSortOption(LevelSelectController __instance, GameObject face, string sortOption, float y)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			GameObject val = GameObject.Find("MainCanvas/FullScreenPanel/sort-dropdown/face/btn_sort_length");
			Button val2 = Object.Instantiate<Button>(val.GetComponent<Button>(), ((Component)face.transform).transform);
			Object.Destroy((Object)(object)((Component)val2).GetComponentInChildren<LocalizeStringEvent>());
			((Object)val2).name = sortOption;
			RectTransform component = ((Component)val2).GetComponent<RectTransform>();
			component.anchoredPosition = new Vector2(0f, y);
			Text componentInChildren = ((Component)val2).GetComponentInChildren<Text>();
			componentInChildren.text = sortOption;
			Button component2 = ((Component)val2).GetComponent<Button>();
			((UnityEventBase)component2.onClick).RemoveAllListeners();
			((UnityEvent)component2.onClick).AddListener((UnityAction)delegate
			{
				__instance.sortTracks(sortOption, true);
			});
		}

		private static Toggle CreateFilterOption(GameObject face, FilterOption filter, Vector2 position)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			Toggle val = Object.Instantiate<Toggle>(Plugin.Toggle, ((Component)face.transform).transform);
			string text = (((Object)val).name = Enums.GetDescription(filter));
			RectTransform component = ((Component)val).GetComponent<RectTransform>();
			component.anchoredPosition = position;
			component.sizeDelta = new Vector2(180f, 30f);
			Text componentInChildren = ((Component)val).GetComponentInChildren<Text>();
			componentInChildren.text = text + " tracks";
			componentInChildren.fontSize = 13;
			((Graphic)componentInChildren).color = new Color(0.024f, 0.294f, 0.302f);
			Image[] componentsInChildren = ((Component)val).GetComponentsInChildren<Image>();
			Image[] array = componentsInChildren;
			foreach (Image val2 in array)
			{
				if (((Object)val2).name == "Background")
				{
					((Graphic)val2).rectTransform.sizeDelta = new Vector2(0f, 30f);
					((Graphic)val2).color = new Color(13f / 85f, 1f, 1f);
				}
				else if (((Object)val2).name == "Checkmark")
				{
					((Graphic)val2).rectTransform.anchoredPosition = new Vector2(-70f, 20f);
					((Graphic)val2).rectTransform.sizeDelta = new Vector2(20f, 20f);
				}
			}
			Text componentInChildren2 = ((Component)val).GetComponentInChildren<Text>();
			((Graphic)componentInChildren2).rectTransform.sizeDelta = new Vector2(180f, ((Graphic)componentInChildren2).rectTransform.sizeDelta.y);
			return val;
		}

		private static void AddSearchBar(LevelSelectController __instance)
		{
			//IL_0063: 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_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Expected O, but got Unknown
			GameObject val = GameObject.Find("MainCanvas/FullScreenPanel");
			Object.Destroy((Object)(object)__instance.scenetitle);
			Plugin.SearchInput = Object.Instantiate<TMP_InputField>(Plugin.InputFieldPrefab, val.transform);
			((Object)Plugin.SearchInput).name = "SearchInput";
			((Component)Plugin.SearchInput).GetComponent<RectTransform>().anchoredPosition = new Vector2(190f, 420f);
			((Component)Plugin.SearchInput).GetComponent<RectTransform>().sizeDelta = new Vector2(80f, 14f);
			Plugin.SearchInput.text = Plugin.Options.SearchValue.Value;
			((UnityEvent<string>)(object)Plugin.SearchInput.onEndEdit).AddListener((UnityAction<string>)delegate(string text)
			{
				Plugin.SearchInput.text = text;
			});
			((UnityEvent<string>)(object)Plugin.SearchInput.onValueChanged).AddListener((UnityAction<string>)delegate(string text)
			{
				Plugin.Options.SearchValue.Value = text;
				TrackLoaded.FilterTracks(__instance);
			});
			Button val2 = AddDeleteButton(Plugin.SearchInput.textComponent);
			((Object)val2).name = "clear search";
			((UnityEvent)val2.onClick).AddListener((UnityAction)delegate
			{
				Plugin.SearchInput.text = "";
				Plugin.SearchInput.textComponent.text = "";
				Plugin.Options.SearchValue.Value = "";
				TrackLoaded.FilterTracks(__instance);
			});
			RectTransform component = ((Component)val2).GetComponent<RectTransform>();
		}

		private static Button AddDeleteButton(TMP_Text scoreText)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = ((Component)scoreText).GetComponent<RectTransform>();
			Button val = Object.Instantiate<Button>(Plugin.Button, (Transform)(object)component);
			RectTransform component2 = ((Component)val).GetComponent<RectTransform>();
			((UnityEventBase)val.onClick).RemoveAllListeners();
			component2.sizeDelta = new Vector2(15f, 15f);
			((Transform)component2).position = ((Transform)component).position;
			component2.anchoredPosition = new Vector2(-20f, 15f);
			Text componentInChildren = ((Component)val).GetComponentInChildren<Text>();
			componentInChildren.text = "X";
			componentInChildren.fontSize = 12;
			return val;
		}
	}
}
namespace SongOrganizer.Data
{
	public class Enums
	{
		public static string GetDescription(Enum value)
		{
			FieldInfo field = value.GetType().GetField(value.ToString());
			return (field.GetCustomAttributes(typeof(DescriptionAttribute), inherit: false) is DescriptionAttribute[] source && source.Any()) ? source.First().Description : value.ToString();
		}
	}
	public enum FilterOption
	{
		DEFAULT,
		CUSTOM,
		UNPLAYED,
		PLAYED,
		[Description("Non S rank")]
		NOT_S_RANK,
		[Description("S rank")]
		S_RANK,
		UNRATED,
		RATED
	}
	public class Options
	{
		public ConfigEntry<bool> ShowDefault { get; set; }

		public ConfigEntry<bool> ShowCustom { get; set; }

		public ConfigEntry<bool> ShowUnplayed { get; set; }

		public ConfigEntry<bool> ShowPlayed { get; set; }

		public ConfigEntry<bool> ShowNotSRank { get; set; }

		public ConfigEntry<bool> ShowSRank { get; set; }

		public ConfigEntry<bool> ShowUnrated { get; set; }

		public ConfigEntry<bool> ShowRated { get; set; }

		public ConfigEntry<string> SortMode { get; set; }

		public ConfigEntry<int> LastIndex { get; set; }

		public ConfigEntry<string> SearchValue { get; set; }
	}
	public class TootTally
	{
		[Serializable]
		public class SearchResponse
		{
			public List<SearchTrackResult> results;
		}

		[Serializable]
		public class SearchTrackResult
		{
			public string track_ref;

			public string short_name;

			public string name;

			public string note_hash;

			public string file_hash;

			public decimal difficulty;

			public string author;

			public string download;

			public string mirror;

			public bool is_official;

			public bool is_rated;
		}

		public static string RatedTracksPath = Paths.ConfigPath + "/rated.json";

		public const string API_URL = "https://toottally.com";

		public const string RATED_ENDPOINT = "/api/search/?rated=1&page=1&page_size=100000&fields=track_ref,short_name,name,note_hash,file_hash,difficulty,author,download,mirror,is_official,is_rated";

		public static IEnumerator<UnityWebRequestAsyncOperation> GetRatedTracks()
		{
			string apiLink = "https://toottally.com/api/search/?rated=1&page=1&page_size=100000&fields=track_ref,short_name,name,note_hash,file_hash,difficulty,author,download,mirror,is_official,is_rated";
			Plugin.Log.LogDebug((object)("Getting rated tracks " + apiLink));
			UnityWebRequest webRequest = UnityWebRequest.Get(apiLink);
			yield return webRequest.SendWebRequest();
			if (!webRequest.isNetworkError && !webRequest.isHttpError)
			{
				File.WriteAllText(RatedTracksPath, webRequest.downloadHandler.GetText());
				Plugin.Log.LogDebug((object)("Finished writing rated tracks to " + RatedTracksPath));
			}
			else
			{
				Plugin.Log.LogError((object)("ERROR: " + webRequest.error));
			}
		}

		public static IEnumerable<SearchTrackResult> ReadRatedTracks()
		{
			try
			{
				string text = File.ReadAllText(RatedTracksPath);
				SearchResponse searchResponse = JsonConvert.DeserializeObject<SearchResponse>(text);
				return searchResponse.results.Where((SearchTrackResult i) => i.is_rated);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Error reading rated.json\n" + ex.Message));
			}
			return new List<SearchTrackResult>();
		}
	}
	public class Track : SingleTrackData
	{
		public bool custom { get; set; }

		public bool rated { get; set; }

		public string letterScore { get; set; }

		public int[] scores { get; set; }

		public Track()
		{
		}

		public Track(SingleTrackData other)
		{
			base.trackname_long = other.trackname_long;
			base.trackname_short = other.trackname_short;
			base.year = other.year;
			base.artist = other.artist;
			base.desc = other.desc;
			base.genre = other.genre;
			base.difficulty = other.difficulty;
			base.tempo = other.tempo;
			base.length = other.length;
			base.trackindex = other.trackindex;
			base.trackref = other.trackref;
		}
	}
}