Decompiled source of ModUpdateChecker v1.0.1

BepInEx/plugins/ModUpdateChecker/ModUpdateChecker.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using HowtoFishLocalizationAPI.Api;
using ModUpdateChecker.Configuration;
using ModUpdateChecker.Localization;
using ModUpdateChecker.Runtime;
using ModUpdateChecker.UI;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ModUpdateChecker")]
[assembly: AssemblyDescription("Thunderstore update checker for How to Fish by Ice Box Studio")]
[assembly: AssemblyCompany("Ice Box Studio")]
[assembly: AssemblyProduct("ModUpdateChecker")]
[assembly: AssemblyCopyright("Copyright © 2026 Ice Box Studio All rights reserved.")]
[assembly: ComVisible(false)]
[assembly: Guid("b4d4f8f4-83a7-4ec6-bf6b-7cdaf33f2a52")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ModUpdateChecker
{
	[BepInPlugin("IceBoxStudio.HowToFish.ModUpdateChecker", "ModUpdateChecker", "1.0.0")]
	[BepInDependency("IceBoxStudio.HowToFish.LocalizationAPI", "1.0.0")]
	public sealed class ModUpdateCheckerPlugin : BaseUnityPlugin
	{
		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			try
			{
				Log.LogInfo((object)"=============================================");
				Log.LogInfo((object)("ModUpdateChecker " + I18n.Text("plugin.initializing")));
				Log.LogInfo((object)(I18n.Text("plugin.author_prefix") + "Ice Box Studio(https://steamcommunity.com/id/ibox666/)"));
				ConfigManager.Init(((BaseUnityPlugin)this).Config);
				ModUpdateRuntime.Init((MonoBehaviour)(object)this);
				_harmony = new Harmony("IceBoxStudio.HowToFish.ModUpdateChecker");
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				Log.LogInfo((object)("ModUpdateChecker " + I18n.Text("plugin.initialized")));
				Log.LogInfo((object)"=============================================");
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Failed to initialize ModUpdateChecker: {arg}");
			}
		}

		private void Update()
		{
			ModUpdateRuntime.Update();
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "IceBoxStudio.HowToFish.ModUpdateChecker";

		public const string PLUGIN_NAME = "ModUpdateChecker";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace ModUpdateChecker.UI
{
	internal sealed class ModUpdateUi : MonoBehaviour
	{
		private static readonly BindingFlags PrivateInstance = BindingFlags.Instance | BindingFlags.NonPublic;

		private static ModUpdateUi _current;

		private GameObject _mainMenuButton;

		private GameObject _pauseHolder;

		private GameObject _mainScreen;

		private GameObject _optionsScreen;

		private GameObject _serverScreen;

		private GameObject _root;

		private GameObject _buttonTemplate;

		private Transform _rows;

		private TextMeshProUGUI _title;

		private TextMeshProUGUI _summary;

		private TextMeshProUGUI _checkLabel;

		private TextMeshProUGUI _closeLabel;

		private TextMeshProUGUI _backLabel;

		private Button _checkButton;

		private Button _backButton;

		private bool _open;

		private bool _built;

		private ModUpdateEntry _candidateEntry;

		internal static void Update()
		{
			if ((Object)(object)_current == (Object)null)
			{
				Attach();
			}
			if ((Object)(object)_current == (Object)null)
			{
				return;
			}
			if (Object.op_Implicit((Object)(object)_current._mainMenuButton))
			{
				_current._mainMenuButton.SetActive(MainMenuManager.IsInMenu && !PauseManager.IsPaused);
				_current.RefreshMenuButton();
			}
			if (MainMenuManager.IsInMenu)
			{
				_current.AddMainMenuButton();
				if (_current._open && !PauseManager.IsPaused)
				{
					_current.Hide();
				}
			}
			if ((_current._built || _current.Setup()) && _current._open && Input.GetKeyDown((KeyCode)27))
			{
				if (_current._candidateEntry != null)
				{
					_current.HideCandidates();
				}
				else
				{
					_current.Hide();
				}
			}
		}

		internal static void Attach()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)_current))
			{
				Transform hostParent = GetHostParent();
				if (Object.op_Implicit((Object)(object)hostParent))
				{
					GameObject val = new GameObject("ModUpdateChecker UI");
					val.transform.SetParent(hostParent, false);
					_current = val.AddComponent<ModUpdateUi>();
					_current.Setup();
				}
			}
		}

		internal static void Open()
		{
			Attach();
			if (Object.op_Implicit((Object)(object)_current))
			{
				_current.Show();
			}
		}

		internal static void Toggle()
		{
			if (Object.op_Implicit((Object)(object)_current) && _current._open)
			{
				_current.Hide();
			}
			else
			{
				Open();
			}
		}

		private static Transform GetHostParent()
		{
			CanvasManager val = GetStatic<CanvasManager>(typeof(CanvasManager), "_instance");
			if (Object.op_Implicit((Object)(object)val))
			{
				return ((Component)val).transform;
			}
			return PlayerUI.FXCanvasTrans;
		}

		private void AddMainMenuButton()
		{
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Expected O, but got Unknown
			if (Object.op_Implicit((Object)(object)_mainMenuButton))
			{
				return;
			}
			CanvasManager obj = GetStatic<CanvasManager>(typeof(CanvasManager), "_instance");
			List<GameObject> field = GetField<List<GameObject>>(obj, "_allMainMenuButtons");
			TextMeshProUGUI field2 = GetField<TextMeshProUGUI>(obj, "_versionText");
			if (!Object.op_Implicit((Object)(object)obj) || field == null || field.Count == 0 || !Object.op_Implicit((Object)(object)field[0]) || !Object.op_Implicit((Object)(object)field2))
			{
				return;
			}
			Button val = FindMainMenuButton(field[0]);
			if (Object.op_Implicit((Object)(object)val))
			{
				_mainMenuButton = Object.Instantiate<GameObject>(((Component)val).gameObject, ((TMP_Text)field2).transform.parent, false);
				((Object)_mainMenuButton).name = "ModUpdateCheckerMainMenuButton";
				_mainMenuButton.SetActive(true);
				DisableExtras(_mainMenuButton);
				Button val2 = UseButton(_mainMenuButton);
				TextMeshProUGUI text = GetText(_mainMenuButton);
				if (!Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)text))
				{
					Object.Destroy((Object)(object)_mainMenuButton);
					_mainMenuButton = null;
					return;
				}
				((TMP_Text)text).text = I18n.Text("ui.main_menu_button");
				((UnityEvent)val2.onClick).AddListener(new UnityAction(OpenMenu));
				(_mainMenuButton.GetComponent<LayoutElement>() ?? _mainMenuButton.AddComponent<LayoutElement>()).ignoreLayout = true;
				PlaceMainMenuButton(_mainMenuButton.GetComponent<RectTransform>(), ((TMP_Text)field2).rectTransform);
				_mainMenuButton.transform.SetAsLastSibling();
			}
		}

		private static Button FindMainMenuButton(GameObject page)
		{
			Button[] componentsInChildren = page.GetComponentsInChildren<Button>(true);
			Button val = null;
			foreach (Button val2 in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val2) && ((Component)val2).gameObject.activeInHierarchy)
				{
					if ((Object)(object)val == (Object)null)
					{
						val = val2;
					}
					TextMeshProUGUI text = GetText(((Component)val2).gameObject);
					if ((Object)(object)text != (Object)null && (((TMP_Text)text).text.IndexOf("Options", StringComparison.OrdinalIgnoreCase) >= 0 || ((TMP_Text)text).text.IndexOf("Settings", StringComparison.OrdinalIgnoreCase) >= 0))
					{
						return val2;
					}
				}
			}
			return val;
		}

		private static void PlaceMainMenuButton(RectTransform button, RectTransform version)
		{
			//IL_0020: 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_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: 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)
			if (Object.op_Implicit((Object)(object)button) && Object.op_Implicit((Object)(object)version))
			{
				((Transform)button).SetParent(((Transform)version).parent, false);
				button.anchorMin = version.anchorMin;
				button.anchorMax = version.anchorMax;
				button.pivot = new Vector2(1f, version.pivot.y);
				button.sizeDelta = new Vector2(Mathf.Max(220f, button.sizeDelta.x), Mathf.Max(48f, button.sizeDelta.y));
				float x = version.anchoredPosition.x;
				float num = 1f - version.pivot.x;
				Rect rect = version.rect;
				float num2 = x + num * ((Rect)(ref rect)).width;
				float y = version.anchoredPosition.y;
				float num3 = 1f - version.pivot.y;
				rect = version.rect;
				float num4 = y + num3 * ((Rect)(ref rect)).height;
				float y2 = version.pivot.y;
				rect = button.rect;
				button.anchoredPosition = new Vector2(num2, num4 + y2 * ((Rect)(ref rect)).height + 8f);
			}
		}

		private void OpenMenu()
		{
			Show();
		}

		private bool Setup()
		{
			if (_built)
			{
				return true;
			}
			PauseManager val = Object.FindAnyObjectByType<PauseManager>();
			GameObject val2 = (Object.op_Implicit((Object)(object)val) ? GetObject(val, "_pauseHolder") : null);
			if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val2))
			{
				return false;
			}
			if (Object.op_Implicit((Object)(object)_root))
			{
				Object.Destroy((Object)(object)_root);
				_root = null;
			}
			if (!BuildScreen(val, val2))
			{
				if (Object.op_Implicit((Object)(object)_root))
				{
					Object.Destroy((Object)(object)_root);
					_root = null;
				}
				return false;
			}
			_built = true;
			return true;
		}

		private bool BuildScreen(PauseManager pause, GameObject holder)
		{
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Expected O, but got Unknown
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Expected O, but got Unknown
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Expected O, but got Unknown
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Unknown result type (might be due to invalid IL or missing references)
			//IL_0383: Unknown result type (might be due to invalid IL or missing references)
			//IL_038a: Expected O, but got Unknown
			//IL_043a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0444: Expected O, but got Unknown
			//IL_04c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cd: Expected O, but got Unknown
			_pauseHolder = holder;
			_mainScreen = GetObject(pause, "_mainScreen");
			_optionsScreen = GetObject(pause, "_optionsScreen");
			_serverScreen = GetObject(pause, "_serverSettingsScreen");
			if (!Object.op_Implicit((Object)(object)_serverScreen))
			{
				return false;
			}
			_root = Object.Instantiate<GameObject>(_serverScreen, _pauseHolder.transform, false);
			((Object)_root).name = "ModUpdateCheckerScreen";
			_root.SetActive(false);
			Transform val = _root.transform.Find("PausedText");
			Transform val2 = _root.transform.Find("ServerDisplay");
			Transform val3 = _root.transform.Find("LowerButtonHolder/BackButton");
			if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)val3))
			{
				return false;
			}
			_title = ((Component)val).GetComponent<TextMeshProUGUI>();
			_buttonTemplate = ((Component)val3).gameObject;
			Button val4 = UseButton(_buttonTemplate);
			_closeLabel = GetText(_buttonTemplate);
			if ((Object)(object)_title == (Object)null || (Object)(object)val4 == (Object)null || (Object)(object)_closeLabel == (Object)null)
			{
				return false;
			}
			((UnityEvent)val4.onClick).AddListener(new UnityAction(Hide));
			DisableExtras(((Component)_title).gameObject);
			TextMeshProUGUI componentInChildren = ((Component)val2).GetComponentInChildren<TextMeshProUGUI>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				return false;
			}
			foreach (Transform item in _root.transform)
			{
				Transform val5 = item;
				if ((Object)(object)val5 != (Object)(object)val && (Object)(object)val5 != (Object)(object)val2 && (Object)(object)val5 != (Object)(object)val3.parent)
				{
					((Component)val5).gameObject.SetActive(false);
				}
			}
			foreach (Transform item2 in val2)
			{
				Transform val6 = item2;
				if (((Object)val6).name != "Outline")
				{
					((Component)val6).gameObject.SetActive(false);
				}
			}
			RectTransform component = ((Component)val2).GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.5f, 0.5f);
			component.anchorMax = new Vector2(0.5f, 0.5f);
			component.pivot = new Vector2(0.5f, 0.5f);
			component.anchoredPosition = Vector2.zero;
			component.sizeDelta = new Vector2(860f, 580f);
			GameObject val7 = new GameObject("ModUpdateContent");
			val7.transform.SetParent(val2, false);
			RectTransform val8 = val7.AddComponent<RectTransform>();
			val8.anchorMin = Vector2.zero;
			val8.anchorMax = Vector2.one;
			val8.offsetMin = new Vector2(28f, 24f);
			val8.offsetMax = new Vector2(-28f, -24f);
			((Transform)val8).localScale = Vector3.one;
			((Transform)val8).SetAsLastSibling();
			VerticalLayoutGroup obj = val7.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 8f;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			_summary = AddText(componentInChildren, (Transform)(object)val8, "Summary", 20f, 32f, (TextAlignmentOptions)514);
			((TMP_Text)_summary).richText = true;
			MakeRows((Transform)(object)val8, componentInChildren);
			GameObject val9 = new GameObject("Footer");
			val9.transform.SetParent((Transform)(object)val8, false);
			val9.AddComponent<LayoutElement>().preferredHeight = 48f;
			HorizontalLayoutGroup obj2 = val9.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj2).spacing = 8f;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = false;
			GameObject val10 = MakeButton(_buttonTemplate, val9.transform, "Check");
			_checkButton = GetButton(val10);
			_checkLabel = GetText(val10);
			if ((Object)(object)_checkButton == (Object)null || (Object)(object)_checkLabel == (Object)null)
			{
				return false;
			}
			((UnityEvent)_checkButton.onClick).AddListener(new UnityAction(CheckNow));
			(val10.GetComponent<LayoutElement>() ?? val10.AddComponent<LayoutElement>()).preferredHeight = 48f;
			GameObject val11 = MakeButton(_buttonTemplate, val9.transform, "Back");
			_backButton = GetButton(val11);
			_backLabel = GetText(val11);
			if ((Object)(object)_backButton == (Object)null || (Object)(object)_backLabel == (Object)null)
			{
				return false;
			}
			((UnityEvent)_backButton.onClick).AddListener(new UnityAction(HideCandidates));
			(val11.GetComponent<LayoutElement>() ?? val11.AddComponent<LayoutElement>()).preferredHeight = 48f;
			val11.SetActive(false);
			LocalizationApi.LanguageChanged += OnLanguageChanged;
			ModUpdateRuntime.Changed += Refresh;
			Refresh();
			return true;
		}

		private void MakeRows(Transform parent, TextMeshProUGUI nativeText)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: 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_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("ModUpdateViewport");
			val.transform.SetParent(parent, false);
			val.AddComponent<LayoutElement>().preferredHeight = 390f;
			RectTransform val2 = val.GetComponent<RectTransform>() ?? val.AddComponent<RectTransform>();
			((Transform)val2).localScale = Vector3.one;
			((Graphic)val.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.08f);
			val.AddComponent<RectMask2D>();
			ScrollRect obj = val.AddComponent<ScrollRect>();
			obj.horizontal = false;
			obj.vertical = true;
			obj.movementType = (MovementType)1;
			obj.scrollSensitivity = 30f;
			GameObject val3 = new GameObject("Rows");
			val3.transform.SetParent(val.transform, false);
			RectTransform val4 = val3.GetComponent<RectTransform>() ?? val3.AddComponent<RectTransform>();
			val4.anchorMin = new Vector2(0f, 1f);
			val4.anchorMax = new Vector2(1f, 1f);
			val4.pivot = new Vector2(0.5f, 1f);
			val4.anchoredPosition = Vector2.zero;
			val4.sizeDelta = new Vector2(0f, 0f);
			((Transform)val4).localScale = Vector3.one;
			VerticalLayoutGroup obj2 = val3.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj2).spacing = 4f;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = false;
			ContentSizeFitter obj3 = val3.AddComponent<ContentSizeFitter>();
			obj3.horizontalFit = (FitMode)0;
			obj3.verticalFit = (FitMode)2;
			obj.viewport = val2;
			obj.content = val4;
			_rows = val3.transform;
		}

		private void Show()
		{
			if (!_built && !Setup())
			{
				return;
			}
			if (_open)
			{
				Refresh();
				return;
			}
			_open = true;
			_candidateEntry = null;
			PauseManager.TogglePause(true);
			HideNative();
			_root.SetActive(true);
			Refresh();
			if ((Object)(object)EventSystem.current != (Object)null && (Object)(object)_checkButton != (Object)null)
			{
				EventSystem.current.SetSelectedGameObject(((Component)_checkButton).gameObject);
			}
		}

		private void Hide()
		{
			if (_open)
			{
				_open = false;
				_candidateEntry = null;
				if (Object.op_Implicit((Object)(object)_root))
				{
					_root.SetActive(false);
				}
				PauseManager.TogglePause(false);
			}
		}

		private void CheckNow()
		{
			_candidateEntry = null;
			ModUpdateRuntime.Check(manual: true);
		}

		private void ShowCandidates(ModUpdateEntry entry)
		{
			if (entry != null && entry.Candidates != null && entry.Candidates.Count != 0)
			{
				_candidateEntry = entry;
				Refresh();
				SelectFirstButton();
			}
		}

		private void HideCandidates()
		{
			if (_candidateEntry != null)
			{
				_candidateEntry = null;
				Refresh();
				if ((Object)(object)EventSystem.current != (Object)null && (Object)(object)_checkButton != (Object)null)
				{
					EventSystem.current.SetSelectedGameObject(((Component)_checkButton).gameObject);
				}
			}
		}

		private void HideNative()
		{
			if (Object.op_Implicit((Object)(object)_mainScreen))
			{
				_mainScreen.SetActive(false);
			}
			if (Object.op_Implicit((Object)(object)_optionsScreen))
			{
				_optionsScreen.SetActive(false);
			}
			if (Object.op_Implicit((Object)(object)_serverScreen))
			{
				_serverScreen.SetActive(false);
			}
		}

		private void Refresh()
		{
			if ((Object)(object)_title == (Object)null || (Object)(object)_summary == (Object)null || (Object)(object)_checkLabel == (Object)null || (Object)(object)_closeLabel == (Object)null || (Object)(object)_backLabel == (Object)null || (Object)(object)_rows == (Object)null)
			{
				return;
			}
			if (_candidateEntry != null)
			{
				bool flag = false;
				IReadOnlyList<ModUpdateEntry> results = ModUpdateRuntime.Results;
				for (int i = 0; i < results.Count; i++)
				{
					if (results[i] == _candidateEntry)
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					_candidateEntry = null;
				}
			}
			bool flag2 = _candidateEntry != null;
			((TMP_Text)_title).text = (flag2 ? I18n.Text("ui.choose_package") : I18n.Text("ui.title"));
			((TMP_Text)_checkLabel).text = I18n.Text("ui.check");
			((TMP_Text)_closeLabel).text = I18n.Text("ui.close");
			((TMP_Text)_backLabel).text = I18n.Text("ui.back");
			((Component)_checkButton).gameObject.SetActive(!flag2);
			((Component)_backButton).gameObject.SetActive(flag2);
			((Selectable)_checkButton).interactable = !ModUpdateRuntime.IsChecking;
			IReadOnlyList<ModUpdateEntry> results2 = ModUpdateRuntime.Results;
			int num = 0;
			for (int j = 0; j < results2.Count; j++)
			{
				if (ModUpdateRuntime.HasUpdate(results2[j]))
				{
					num++;
				}
			}
			if (flag2)
			{
				((TMP_Text)_summary).text = I18n.Text("ui.choose_summary", _candidateEntry.Name, _candidateEntry.Candidates.Count);
			}
			else if (ModUpdateRuntime.IsChecking)
			{
				((TMP_Text)_summary).text = I18n.Text("ui.summary.checking");
			}
			else if (results2.Count == 0)
			{
				((TMP_Text)_summary).text = I18n.Text("ui.summary.empty");
			}
			else if (num > 0)
			{
				((TMP_Text)_summary).text = I18n.Text("ui.summary.update_alert", num) + "\n" + I18n.Text("ui.summary.ready", ModUpdateRuntime.LastChecked ?? "-", num);
			}
			else
			{
				((TMP_Text)_summary).text = I18n.Text("ui.summary.ready", ModUpdateRuntime.LastChecked ?? "-", num);
			}
			(((Component)_summary).GetComponent<LayoutElement>() ?? ((Component)_summary).gameObject.AddComponent<LayoutElement>()).preferredHeight = (flag2 ? 48f : ((num > 0 && !ModUpdateRuntime.IsChecking) ? 60f : 32f));
			if (flag2)
			{
				RefreshCandidates(_candidateEntry);
			}
			else
			{
				RefreshRows(results2);
			}
		}

		private void RefreshRows(IReadOnlyList<ModUpdateEntry> entries)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			//IL_03f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fb: Expected O, but got Unknown
			for (int num = _rows.childCount - 1; num >= 0; num--)
			{
				Object.Destroy((Object)(object)((Component)_rows.GetChild(num)).gameObject);
			}
			if (entries.Count == 0)
			{
				((TMP_Text)AddText(GetText(_buttonTemplate), _rows, "Empty", 20f, 44f, (TextAlignmentOptions)514)).text = I18n.Text("ui.none");
				return;
			}
			for (int i = 0; i < entries.Count; i++)
			{
				ModUpdateEntry modUpdateEntry = entries[i];
				GameObject val = new GameObject("ModUpdateRow " + modUpdateEntry.Guid);
				val.transform.SetParent(_rows, false);
				float num2 = (val.AddComponent<LayoutElement>().preferredHeight = ((modUpdateEntry.State == ModUpdateState.Ambiguous) ? Mathf.Max(82f, 58f + (float)modUpdateEntry.Candidates.Count * 28f) : 62f));
				HorizontalLayoutGroup obj = val.AddComponent<HorizontalLayoutGroup>();
				((HorizontalOrVerticalLayoutGroup)obj).spacing = 8f;
				((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
				((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
				((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
				((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
				string text = ModUpdateRuntime.StatusText(modUpdateEntry);
				if (ModUpdateRuntime.HasUpdate(modUpdateEntry))
				{
					text = "<color=#FFD166><b>" + text + "</b></color>";
				}
				string text2 = modUpdateEntry.Name + "\n" + I18n.Text("ui.local", modUpdateEntry.LocalVersion) + " | " + text;
				if (!string.IsNullOrWhiteSpace(modUpdateEntry.Team) && !string.IsNullOrWhiteSpace(modUpdateEntry.Package))
				{
					text2 = text2 + "\n" + I18n.Text("ui.package", modUpdateEntry.Team, modUpdateEntry.Package);
				}
				if (modUpdateEntry.State == ModUpdateState.Ambiguous)
				{
					for (int j = 0; j < modUpdateEntry.Candidates.Count; j++)
					{
						ModUpdateCandidate modUpdateCandidate = modUpdateEntry.Candidates[j];
						string text3 = ModUpdateRuntime.StatusText(modUpdateCandidate);
						if (modUpdateCandidate.State == ModUpdateState.Update)
						{
							text3 = "<color=#FFD166><b>" + text3 + "</b></color>";
						}
						string text4 = (string.IsNullOrWhiteSpace(modUpdateCandidate.LatestVersion) ? string.Empty : (" | " + I18n.Text("ui.latest", modUpdateCandidate.LatestVersion)));
						text2 = text2 + "\n" + I18n.Text("ui.package", modUpdateCandidate.Team, modUpdateCandidate.Package) + text4 + " | " + text3;
					}
				}
				TextMeshProUGUI val2 = AddText(GetText(_buttonTemplate), val.transform, "Details", 16f, num2, (TextAlignmentOptions)513);
				((TMP_Text)val2).text = text2;
				((TMP_Text)val2).richText = true;
				(((Component)val2).GetComponent<LayoutElement>() ?? ((Component)val2).gameObject.AddComponent<LayoutElement>()).flexibleWidth = 1f;
				GameObject val3 = MakeButton(_buttonTemplate, val.transform, "Open");
				Button button = GetButton(val3);
				TextMeshProUGUI text5 = GetText(val3);
				if ((Object)(object)button == (Object)null || (Object)(object)text5 == (Object)null)
				{
					Object.Destroy((Object)(object)val3);
					continue;
				}
				((TMP_Text)text5).text = I18n.Text("ui.open");
				((Selectable)button).interactable = ((modUpdateEntry.State == ModUpdateState.Ambiguous) ? (modUpdateEntry.Candidates.Count > 0) : (!string.IsNullOrWhiteSpace(modUpdateEntry.PackageUrl)));
				LayoutElement obj2 = val3.GetComponent<LayoutElement>() ?? val3.AddComponent<LayoutElement>();
				obj2.preferredWidth = 110f;
				obj2.preferredHeight = num2;
				ModUpdateEntry selected = modUpdateEntry;
				((UnityEvent)button.onClick).AddListener((UnityAction)delegate
				{
					if (selected.State == ModUpdateState.Ambiguous)
					{
						ShowCandidates(selected);
					}
					else
					{
						ModUpdateRuntime.OpenPackage(selected);
					}
				});
			}
		}

		private void RefreshCandidates(ModUpdateEntry entry)
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Expected O, but got Unknown
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Expected O, but got Unknown
			for (int num = _rows.childCount - 1; num >= 0; num--)
			{
				Object.Destroy((Object)(object)((Component)_rows.GetChild(num)).gameObject);
			}
			if (entry == null || entry.Candidates == null || entry.Candidates.Count == 0)
			{
				((TMP_Text)AddText(GetText(_buttonTemplate), _rows, "Empty", 20f, 44f, (TextAlignmentOptions)514)).text = I18n.Text("ui.none");
				return;
			}
			for (int i = 0; i < entry.Candidates.Count; i++)
			{
				ModUpdateCandidate modUpdateCandidate = entry.Candidates[i];
				GameObject val = new GameObject("ModUpdateCandidate " + modUpdateCandidate.Team + " " + modUpdateCandidate.Package);
				val.transform.SetParent(_rows, false);
				val.AddComponent<LayoutElement>().preferredHeight = 84f;
				HorizontalLayoutGroup obj = val.AddComponent<HorizontalLayoutGroup>();
				((HorizontalOrVerticalLayoutGroup)obj).spacing = 8f;
				((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
				((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
				((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
				((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
				string text = ModUpdateRuntime.StatusText(modUpdateCandidate);
				if (modUpdateCandidate.State == ModUpdateState.Update)
				{
					text = "<color=#FFD166><b>" + text + "</b></color>";
				}
				string text2 = I18n.Text("ui.package", modUpdateCandidate.Team, modUpdateCandidate.Package) + "\n" + I18n.Text("ui.local", entry.LocalVersion);
				if (!string.IsNullOrWhiteSpace(modUpdateCandidate.LatestVersion))
				{
					text2 = text2 + " | " + I18n.Text("ui.latest", modUpdateCandidate.LatestVersion);
				}
				text2 = text2 + "\n" + text;
				TextMeshProUGUI val2 = AddText(GetText(_buttonTemplate), val.transform, "Details", 15f, 84f, (TextAlignmentOptions)513);
				((TMP_Text)val2).text = text2;
				((TMP_Text)val2).richText = true;
				(((Component)val2).GetComponent<LayoutElement>() ?? ((Component)val2).gameObject.AddComponent<LayoutElement>()).flexibleWidth = 1f;
				GameObject val3 = MakeButton(_buttonTemplate, val.transform, "Open");
				Button button = GetButton(val3);
				TextMeshProUGUI text3 = GetText(val3);
				if ((Object)(object)button == (Object)null || (Object)(object)text3 == (Object)null)
				{
					Object.Destroy((Object)(object)val3);
					continue;
				}
				((TMP_Text)text3).text = I18n.Text("ui.open");
				((Selectable)button).interactable = !string.IsNullOrWhiteSpace(modUpdateCandidate.PackageUrl);
				LayoutElement obj2 = val3.GetComponent<LayoutElement>() ?? val3.AddComponent<LayoutElement>();
				obj2.preferredWidth = 110f;
				obj2.preferredHeight = 84f;
				ModUpdateCandidate selected = modUpdateCandidate;
				((UnityEvent)button.onClick).AddListener((UnityAction)delegate
				{
					ModUpdateRuntime.OpenPackage(selected);
				});
			}
		}

		private void SelectFirstButton()
		{
			if (!((Object)(object)EventSystem.current == (Object)null) && !((Object)(object)_rows == (Object)null))
			{
				Button componentInChildren = ((Component)_rows).GetComponentInChildren<Button>(true);
				if ((Object)(object)componentInChildren != (Object)null && ((Selectable)componentInChildren).interactable)
				{
					EventSystem.current.SetSelectedGameObject(((Component)componentInChildren).gameObject);
				}
			}
		}

		private void OnLanguageChanged(string language)
		{
			Refresh();
			RefreshMenuButton();
		}

		private void LateUpdate()
		{
			if (MainMenuManager.IsInMenu)
			{
				RefreshMenuButton();
			}
		}

		private void RefreshMenuButton()
		{
			if (!Object.op_Implicit((Object)(object)_mainMenuButton))
			{
				return;
			}
			TextMeshProUGUI text = GetText(_mainMenuButton);
			if (!Object.op_Implicit((Object)(object)text))
			{
				return;
			}
			int num = 0;
			IReadOnlyList<ModUpdateEntry> results = ModUpdateRuntime.Results;
			for (int i = 0; i < results.Count; i++)
			{
				if (ModUpdateRuntime.HasUpdate(results[i]))
				{
					num++;
				}
			}
			((TMP_Text)text).richText = true;
			((TMP_Text)text).text = ((num > 0) ? I18n.Text("ui.main_menu_button_updates", num) : I18n.Text("ui.main_menu_button"));
		}

		private void OnDestroy()
		{
			LocalizationApi.LanguageChanged -= OnLanguageChanged;
			ModUpdateRuntime.Changed -= Refresh;
			if ((Object)(object)_current == (Object)(object)this)
			{
				_current = null;
			}
		}

		private static TextMeshProUGUI AddText(TextMeshProUGUI template, Transform parent, string name, float size, float height, TextAlignmentOptions alignment)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			TextMeshProUGUI val = Object.Instantiate<TextMeshProUGUI>(template, parent, false);
			((Object)((Component)val).gameObject).name = name;
			((Component)val).gameObject.SetActive(true);
			DisableExtras(((Component)val).gameObject);
			((TMP_Text)val).fontSize = size;
			((TMP_Text)val).alignment = alignment;
			((Graphic)val).raycastTarget = false;
			((TMP_Text)val).textWrappingMode = (TextWrappingModes)1;
			(((Component)val).GetComponent<LayoutElement>() ?? ((Component)val).gameObject.AddComponent<LayoutElement>()).preferredHeight = height;
			return val;
		}

		private static GameObject MakeButton(GameObject template, Transform parent, string name)
		{
			GameObject obj = Object.Instantiate<GameObject>(template, parent, false);
			((Object)obj).name = name;
			obj.SetActive(true);
			UseButton(obj);
			TextMeshProUGUI text = GetText(obj);
			if ((Object)(object)text != (Object)null)
			{
				((TMP_Text)text).text = string.Empty;
			}
			return obj;
		}

		private static Button UseButton(GameObject buttonObject)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			Button val = (((Object)(object)buttonObject == (Object)null) ? null : buttonObject.GetComponent<Button>());
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			val.onClick = new ButtonClickedEvent();
			Navigation navigation = default(Navigation);
			((Navigation)(ref navigation)).mode = (Mode)3;
			((Selectable)val).navigation = navigation;
			return val;
		}

		private static Button GetButton(GameObject root)
		{
			Button[] array = (((Object)(object)root == (Object)null) ? Array.Empty<Button>() : root.GetComponents<Button>());
			for (int num = array.Length - 1; num >= 0; num--)
			{
				if (((Behaviour)array[num]).enabled)
				{
					return array[num];
				}
			}
			return null;
		}

		private static TextMeshProUGUI GetText(GameObject root)
		{
			if (!((Object)(object)root == (Object)null))
			{
				return root.GetComponentInChildren<TextMeshProUGUI>(true);
			}
			return null;
		}

		private static void DisableExtras(GameObject root)
		{
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			MonoBehaviour[] componentsInChildren = root.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if (!(val is Graphic) && !(val is Selectable) && !(val is TMP_Text) && !(val is Shadow) && !(val is UIButton) && !(val is LayoutGroup) && !(val is LayoutElement) && !(val is ContentSizeFitter) && !(val is RectMask2D))
				{
					((Behaviour)val).enabled = false;
				}
			}
		}

		private static GameObject GetObject(PauseManager pause, string field)
		{
			return GetField<GameObject>(pause, field);
		}

		private static T GetField<T>(object instance, string field)
		{
			if (instance == null)
			{
				return default(T);
			}
			FieldInfo field2 = instance.GetType().GetField(field, PrivateInstance);
			if (!(field2 == null))
			{
				return (T)field2.GetValue(instance);
			}
			return default(T);
		}

		private static T GetStatic<T>(Type type, string field) where T : class
		{
			FieldInfo field2 = type.GetField(field, BindingFlags.Static | BindingFlags.NonPublic);
			if (!(field2 == null))
			{
				return field2.GetValue(null) as T;
			}
			return null;
		}
	}
}
namespace ModUpdateChecker.Runtime
{
	internal enum ModUpdateState
	{
		Checking,
		Current,
		Update,
		NotFound,
		Ambiguous,
		Error,
		Deprecated,
		NoRelease
	}
	internal sealed class ModUpdateEntry
	{
		internal string Guid;

		internal string Name;

		internal string LocalVersion;

		internal string Team;

		internal string Package;

		internal string LatestVersion;

		internal string PackageUrl;

		internal ModUpdateState State;

		internal string Error;

		internal List<ModUpdateCandidate> Candidates = new List<ModUpdateCandidate>();
	}
	internal sealed class ModUpdateCandidate
	{
		internal string Team;

		internal string Package;

		internal string LatestVersion;

		internal string PackageUrl;

		internal ModUpdateState State;

		internal string Error;
	}
	internal static class ModUpdateRuntime
	{
		private sealed class CandidateScore
		{
			internal ListingPackage Package;

			internal int Score;

			internal int NameScore;
		}

		private sealed class MappingCache
		{
			[JsonProperty("version")]
			public int Version { get; set; }

			public Dictionary<string, string> Mappings { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
		}

		private sealed class VersionParts
		{
			internal readonly int[] Core = new int[3];

			internal readonly List<string> Pre = new List<string>();

			internal static VersionParts Parse(string value)
			{
				VersionParts versionParts = new VersionParts();
				string text = (value ?? string.Empty).Trim().TrimStart('v', 'V');
				int num = text.IndexOf('+');
				if (num >= 0)
				{
					text = text.Substring(0, num);
				}
				string[] array = text.Split(new char[1] { '-' }, 2);
				string[] array2 = array[0].Split(new char[1] { '.' });
				for (int i = 0; i < Math.Min(3, array2.Length); i++)
				{
					if (int.TryParse(array2[i], out var result))
					{
						versionParts.Core[i] = result;
					}
				}
				if (array.Length > 1)
				{
					versionParts.Pre.AddRange(array[1].Split(new char[1] { '.' }));
				}
				return versionParts;
			}
		}

		private sealed class ListingPage
		{
			[JsonProperty("next")]
			public string Next { get; set; }

			[JsonProperty("results")]
			public List<ListingPackage> Results { get; set; }
		}

		private sealed class ListingPackage
		{
			[JsonProperty("community_identifier")]
			public string CommunityIdentifier { get; set; }

			[JsonProperty("namespace")]
			public string Namespace { get; set; }

			[JsonProperty("name")]
			public string Name { get; set; }
		}

		private sealed class PackageDetails
		{
			[JsonProperty("namespace")]
			public string Namespace { get; set; }

			[JsonProperty("name")]
			public string Name { get; set; }

			[JsonProperty("community_listings")]
			public List<CommunityListing> CommunityListings { get; set; }

			[JsonProperty("is_deprecated")]
			public bool IsDeprecated { get; set; }

			[JsonProperty("latest")]
			public PackageVersion Latest { get; set; }
		}

		private sealed class CommunityListing
		{
			[JsonProperty("community")]
			public string Community { get; set; }
		}

		private sealed class PackageVersion
		{
			[JsonProperty("version_number")]
			public string VersionNumber { get; set; }
		}

		private const string Community = "how-to-fish";

		private const string ApiRoot = "https://thunderstore.io/";

		private const string CacheFileName = "IceBoxStudio.HowToFish.ModUpdateChecker.json";

		private const int CacheVersion = 1;

		private static readonly TimeSpan SessionCacheTime = TimeSpan.FromHours(2.0);

		private static readonly List<ModUpdateEntry> Entries = new List<ModUpdateEntry>();

		private static readonly Dictionary<string, string> CachedMappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, ModUpdateEntry> SessionResults = new Dictionary<string, ModUpdateEntry>(StringComparer.OrdinalIgnoreCase);

		private static MonoBehaviour _host;

		private static Coroutine _check;

		private static bool _startupStarted;

		private static float _startupAt;

		private static string _lastChecked;

		private static DateTime _sessionCachedAt;

		internal static IReadOnlyList<ModUpdateEntry> Results => Entries;

		internal static bool IsChecking => _check != null;

		internal static string LastChecked => _lastChecked;

		internal static event Action Changed;

		internal static bool HasUpdate(ModUpdateEntry entry)
		{
			if (entry != null)
			{
				if (entry.State != ModUpdateState.Update)
				{
					return entry.Candidates.Any((ModUpdateCandidate candidate) => candidate.State == ModUpdateState.Update);
				}
				return true;
			}
			return false;
		}

		internal static void Init(MonoBehaviour host)
		{
			_host = host;
			_startupAt = Time.unscaledTime + ConfigManager.StartupDelay;
			LoadMap();
		}

		internal static void Update()
		{
			ModUpdateUi.Update();
			if (!_startupStarted && ConfigManager.CheckOnStartup && Time.unscaledTime >= _startupAt)
			{
				_startupStarted = true;
				Check(manual: false);
			}
		}

		internal static void Check(bool manual)
		{
			if (_check != null || (Object)(object)_host == (Object)null)
			{
				return;
			}
			if (manual)
			{
				_startupStarted = true;
			}
			Scan();
			if (!CacheIsFresh())
			{
				SessionResults.Clear();
			}
			bool flag = true;
			for (int i = 0; i < Entries.Count; i++)
			{
				if (!RestoreSession(Entries[i]))
				{
					flag = false;
				}
			}
			if (flag)
			{
				Notify();
				return;
			}
			for (int j = 0; j < Entries.Count; j++)
			{
				Entries[j].State = ModUpdateState.Checking;
				Entries[j].Error = null;
			}
			Notify();
			_check = _host.StartCoroutine(RunCheck());
		}

		internal static string StatusText(ModUpdateEntry entry)
		{
			return entry.State switch
			{
				ModUpdateState.Checking => I18n.Text("ui.checking"), 
				ModUpdateState.Current => I18n.Text("ui.current"), 
				ModUpdateState.Update => I18n.Text("ui.update", entry.LatestVersion), 
				ModUpdateState.NotFound => I18n.Text("ui.not_found"), 
				ModUpdateState.Ambiguous => I18n.Text("ui.ambiguous", entry.Candidates.Count), 
				ModUpdateState.Deprecated => I18n.Text("ui.deprecated"), 
				ModUpdateState.NoRelease => I18n.Text("ui.no_release"), 
				_ => I18n.Text("ui.error"), 
			};
		}

		internal static string StatusText(ModUpdateCandidate candidate)
		{
			if (candidate == null)
			{
				return I18n.Text("ui.error");
			}
			return candidate.State switch
			{
				ModUpdateState.Checking => I18n.Text("ui.checking"), 
				ModUpdateState.Current => I18n.Text("ui.current"), 
				ModUpdateState.Update => I18n.Text("ui.update", candidate.LatestVersion), 
				ModUpdateState.NotFound => I18n.Text("ui.not_found"), 
				ModUpdateState.Deprecated => I18n.Text("ui.deprecated"), 
				ModUpdateState.NoRelease => I18n.Text("ui.no_release"), 
				_ => I18n.Text("ui.error"), 
			};
		}

		internal static void OpenPackage(ModUpdateEntry entry)
		{
			if (entry != null && !string.IsNullOrWhiteSpace(entry.PackageUrl))
			{
				Application.OpenURL(entry.PackageUrl);
			}
		}

		internal static void OpenPackage(ModUpdateCandidate candidate)
		{
			if (candidate != null && !string.IsNullOrWhiteSpace(candidate.PackageUrl))
			{
				Application.OpenURL(candidate.PackageUrl);
			}
		}

		private static IEnumerator RunCheck()
		{
			for (int i = 0; i < Entries.Count; i++)
			{
				yield return CheckMod(Entries[i]);
				CacheSession(Entries[i]);
			}
			_lastChecked = DateTime.Now.ToString("yyyy-MM-dd HH:mm");
			_sessionCachedAt = DateTime.UtcNow;
			_check = null;
			SaveMap();
			Notify();
		}

		private static IEnumerator CheckMod(ModUpdateEntry entry)
		{
			if (RestoreSession(entry))
			{
				Notify();
				yield break;
			}
			if (!ConfigManager.TryGetMapping(entry.Guid, out var team, out var package) && !GetMapping(entry.Guid, out team, out package))
			{
				List<ListingPackage> candidates = null;
				yield return FindPackages(entry.Name, delegate(List<ListingPackage> result)
				{
					candidates = result;
				}, delegate(string error)
				{
					entry.Error = error;
				});
				ListingPackage listingPackage = ((candidates == null) ? null : PickCandidate(entry, candidates));
				string text = OtherName(entry.Guid, entry.Name);
				if (!string.Equals(text, entry.Name, StringComparison.OrdinalIgnoreCase) && (candidates == null || candidates.Count == 0 || listingPackage == null))
				{
					List<ListingPackage> alternateCandidates = null;
					yield return FindPackages(text, delegate(List<ListingPackage> result)
					{
						alternateCandidates = result;
					}, delegate(string error)
					{
						entry.Error = error;
					});
					if (alternateCandidates != null)
					{
						if (candidates == null)
						{
							candidates = alternateCandidates;
						}
						else
						{
							candidates = (from @group in candidates.Concat(alternateCandidates).GroupBy<ListingPackage, string>((ListingPackage candidate) => candidate.Namespace + "/" + candidate.Name, StringComparer.OrdinalIgnoreCase)
								select @group.First()).ToList();
						}
					}
				}
				if (candidates == null || candidates.Count == 0)
				{
					entry.State = (string.IsNullOrEmpty(entry.Error) ? ModUpdateState.NotFound : ModUpdateState.Error);
					Notify();
					yield break;
				}
				listingPackage = PickCandidate(entry, candidates);
				if (listingPackage == null)
				{
					entry.State = ModUpdateState.Ambiguous;
					entry.Candidates = (from candidate in SortCandidates(entry, candidates)
						select new ModUpdateCandidate
						{
							Team = candidate.Namespace,
							Package = candidate.Name,
							State = ModUpdateState.Checking
						}).ToList();
					Notify();
					for (int i = 0; i < entry.Candidates.Count; i++)
					{
						ModUpdateCandidate modUpdateCandidate = entry.Candidates[i];
						yield return ReadCandidate(modUpdateCandidate, entry.LocalVersion, modUpdateCandidate.Team, modUpdateCandidate.Package);
						Notify();
					}
					yield break;
				}
				team = listingPackage.Namespace;
				package = listingPackage.Name;
				CachedMappings[entry.Guid] = team + "/" + package;
			}
			entry.Team = team;
			entry.Package = package;
			yield return ReadPackage(entry, team, package);
			Notify();
		}

		private static IEnumerator FindPackages(string name, Action<List<ListingPackage>> success, Action<string> failure)
		{
			string text = "https://thunderstore.io/api/cyberstorm/listing/how-to-fish/?page=1&q=" + Uri.EscapeDataString(name ?? string.Empty);
			List<ListingPackage> all = new List<ListingPackage>();
			HashSet<string> visited = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			int pages = 0;
			while (!string.IsNullOrWhiteSpace(text) && pages++ < 100 && visited.Add(text))
			{
				string body = null;
				string error = null;
				yield return Get(text, delegate(string value)
				{
					body = value;
				}, delegate(string value)
				{
					error = value;
				});
				if (body == null)
				{
					failure(error ?? "Thunderstore request failed.");
					yield break;
				}
				ListingPage listingPage;
				try
				{
					listingPage = JsonConvert.DeserializeObject<ListingPage>(body);
				}
				catch (Exception ex)
				{
					failure(ex.Message);
					yield break;
				}
				if (listingPage == null)
				{
					break;
				}
				if (listingPage.Results != null)
				{
					all.AddRange(listingPage.Results.Where(IsHowToFishPackage));
				}
				text = listingPage.Next;
			}
			success(all);
		}

		private static IEnumerator ReadPackage(ModUpdateEntry entry, string team, string package)
		{
			PackageDetails details = null;
			string error = null;
			yield return GetPackage(team, package, delegate(PackageDetails value)
			{
				details = value;
			}, delegate(string value)
			{
				error = value;
			});
			if (details == null)
			{
				entry.State = ModUpdateState.Error;
				entry.Error = error;
				yield break;
			}
			try
			{
				if (details == null || details.CommunityListings == null || !details.CommunityListings.Any((CommunityListing listing) => listing != null && string.Equals(listing.Community, "how-to-fish", StringComparison.OrdinalIgnoreCase)))
				{
					entry.State = ModUpdateState.NotFound;
					yield break;
				}
				if (details.Latest == null || string.IsNullOrWhiteSpace(details.Latest.VersionNumber))
				{
					entry.State = ModUpdateState.NoRelease;
					yield break;
				}
				entry.Team = (string.IsNullOrWhiteSpace(details.Namespace) ? team : details.Namespace);
				entry.Package = (string.IsNullOrWhiteSpace(details.Name) ? package : details.Name);
				entry.LatestVersion = details.Latest.VersionNumber;
				entry.PackageUrl = "https://thunderstore.io/c/how-to-fish/p/" + Uri.EscapeDataString(entry.Team) + "/" + Uri.EscapeDataString(entry.Package) + "/";
				if (details.IsDeprecated)
				{
					entry.State = ModUpdateState.Deprecated;
					yield break;
				}
				int num = Compare(entry.LocalVersion, entry.LatestVersion);
				entry.State = ((num >= 0) ? ModUpdateState.Current : ModUpdateState.Update);
			}
			catch (Exception ex)
			{
				entry.State = ModUpdateState.Error;
				entry.Error = ex.Message;
			}
		}

		private static IEnumerator ReadCandidate(ModUpdateCandidate candidate, string localVersion, string team, string package)
		{
			PackageDetails details = null;
			string error = null;
			yield return GetPackage(team, package, delegate(PackageDetails value)
			{
				details = value;
			}, delegate(string value)
			{
				error = value;
			});
			if (details == null)
			{
				candidate.State = ModUpdateState.Error;
				candidate.Error = error;
				yield break;
			}
			try
			{
				if (details.CommunityListings == null || !details.CommunityListings.Any((CommunityListing listing) => listing != null && string.Equals(listing.Community, "how-to-fish", StringComparison.OrdinalIgnoreCase)))
				{
					candidate.State = ModUpdateState.NotFound;
					yield break;
				}
				candidate.Team = (string.IsNullOrWhiteSpace(details.Namespace) ? team : details.Namespace);
				candidate.Package = (string.IsNullOrWhiteSpace(details.Name) ? package : details.Name);
				candidate.PackageUrl = "https://thunderstore.io/c/how-to-fish/p/" + Uri.EscapeDataString(candidate.Team) + "/" + Uri.EscapeDataString(candidate.Package) + "/";
				if (details.Latest == null || string.IsNullOrWhiteSpace(details.Latest.VersionNumber))
				{
					candidate.State = ModUpdateState.NoRelease;
					yield break;
				}
				candidate.LatestVersion = details.Latest.VersionNumber;
				candidate.State = (details.IsDeprecated ? ModUpdateState.Deprecated : ((Compare(localVersion, candidate.LatestVersion) >= 0) ? ModUpdateState.Current : ModUpdateState.Update));
			}
			catch (Exception ex)
			{
				candidate.State = ModUpdateState.Error;
				candidate.Error = ex.Message;
			}
		}

		private static IEnumerator GetPackage(string team, string package, Action<PackageDetails> success, Action<string> failure)
		{
			string url = "https://thunderstore.io/api/experimental/package/" + Uri.EscapeDataString(team) + "/" + Uri.EscapeDataString(package) + "/";
			string body = null;
			string error = null;
			yield return Get(url, delegate(string value)
			{
				body = value;
			}, delegate(string value)
			{
				error = value;
			});
			if (body == null)
			{
				failure(error ?? "Thunderstore request failed.");
				yield break;
			}
			try
			{
				success(JsonConvert.DeserializeObject<PackageDetails>(body));
			}
			catch (Exception ex)
			{
				failure(ex.Message);
			}
		}

		private static IEnumerator Get(string url, Action<string> success, Action<string> failure)
		{
			if (!Uri.TryCreate(url, UriKind.Absolute, out Uri result) || result.Scheme != Uri.UriSchemeHttps || !string.Equals(result.Host, "thunderstore.io", StringComparison.OrdinalIgnoreCase))
			{
				failure("Invalid Thunderstore URL.");
				yield break;
			}
			UnityWebRequest request = UnityWebRequest.Get(result);
			try
			{
				request.timeout = ConfigManager.RequestTimeout;
				yield return request.SendWebRequest();
				if ((int)request.result == 2 || (int)request.result == 3)
				{
					failure(request.error ?? "Thunderstore request failed.");
					yield break;
				}
				success((request.downloadHandler == null) ? string.Empty : request.downloadHandler.text);
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		private static void Scan()
		{
			Entries.Clear();
			foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
			{
				BepInPlugin val = ((pluginInfo.Value == null) ? null : pluginInfo.Value.Metadata);
				if (val != null && !string.Equals(val.GUID, "IceBoxStudio.HowToFish.ModUpdateChecker", StringComparison.OrdinalIgnoreCase))
				{
					Entries.Add(new ModUpdateEntry
					{
						Guid = (string.IsNullOrWhiteSpace(val.GUID) ? pluginInfo.Key : val.GUID),
						Name = (string.IsNullOrWhiteSpace(val.Name) ? pluginInfo.Key : val.Name),
						LocalVersion = ((val.Version == null) ? "0.0.0" : val.Version.ToString())
					});
				}
			}
			Entries.Sort((ModUpdateEntry left, ModUpdateEntry right) => string.Compare(left.Name, right.Name, StringComparison.OrdinalIgnoreCase));
		}

		private static ListingPackage PickCandidate(ModUpdateEntry entry, List<ListingPackage> candidates)
		{
			List<CandidateScore> list = ScoreCandidates(entry, candidates);
			if (list.Count == 0)
			{
				return null;
			}
			string guidTail = GuidName(entry.Guid);
			List<CandidateScore> list2 = list.Where((CandidateScore value) => string.Equals(Clean(value.Package.Name), guidTail, StringComparison.Ordinal)).ToList();
			if (list2.Count == 1)
			{
				return list2[0].Package;
			}
			if (list[0].NameScore < 55)
			{
				return null;
			}
			if (list.Count > 1)
			{
				CandidateScore candidateScore = list[0];
				CandidateScore candidateScore2 = list[1];
				if (candidateScore.NameScore - candidateScore2.NameScore <= 3 && candidateScore.Score - candidateScore2.Score < 20)
				{
					return null;
				}
			}
			return list[0].Package;
		}

		private static List<ListingPackage> SortCandidates(ModUpdateEntry entry, List<ListingPackage> candidates)
		{
			return (from value in ScoreCandidates(entry, candidates)
				select value.Package).ToList();
		}

		private static List<CandidateScore> ScoreCandidates(ModUpdateEntry entry, List<ListingPackage> candidates)
		{
			return (from value in candidates.GroupBy<ListingPackage, string>((ListingPackage candidate) => candidate.Namespace + "/" + candidate.Name, StringComparer.OrdinalIgnoreCase).Select(delegate(IGrouping<string, ListingPackage> @group)
				{
					ListingPackage listingPackage = @group.First();
					return new CandidateScore
					{
						Package = listingPackage,
						NameScore = Similarity(Clean(entry.Name), Clean(listingPackage.Name)),
						Score = Score(entry, listingPackage)
					};
				})
				orderby value.Score descending, value.NameScore descending
				select value).ToList();
		}

		private static int Score(ModUpdateEntry entry, ListingPackage package)
		{
			string text = Clean(entry.Name);
			string text2 = Clean(package.Name);
			int num = Similarity(text, text2) * 2;
			if (text != text2 && (text2.Contains(text) || text.Contains(text2)))
			{
				num += 20;
			}
			if (GuidName(entry.Guid) == text2)
			{
				num += 25;
			}
			if (Clean(entry.Guid.Substring(0, Math.Max(0, entry.Guid.LastIndexOf('.')))).Contains(Clean(package.Namespace)))
			{
				num += 15;
			}
			return num;
		}

		private static string GuidName(string guid)
		{
			if (string.IsNullOrWhiteSpace(guid))
			{
				return string.Empty;
			}
			int num = guid.LastIndexOf('.');
			return Clean((num < 0) ? guid : guid.Substring(num + 1));
		}

		private static int Similarity(string left, string right)
		{
			if (left.Length == 0 || right.Length == 0)
			{
				return 0;
			}
			int[] array = new int[right.Length + 1];
			int[] array2 = new int[right.Length + 1];
			for (int i = 0; i <= right.Length; i++)
			{
				array[i] = i;
			}
			for (int j = 1; j <= left.Length; j++)
			{
				array2[0] = j;
				for (int k = 1; k <= right.Length; k++)
				{
					int num = ((left[j - 1] != right[k - 1]) ? 1 : 0);
					array2[k] = Math.Min(Math.Min(array2[k - 1] + 1, array[k] + 1), array[k - 1] + num);
				}
				int[] array3 = array;
				array = array2;
				array2 = array3;
			}
			int num2 = Math.Max(left.Length, right.Length);
			return (num2 - array[right.Length]) * 100 / num2;
		}

		private static bool IsHowToFishPackage(ListingPackage package)
		{
			if (package != null && string.Equals(package.CommunityIdentifier, "how-to-fish", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(package.Namespace))
			{
				return !string.IsNullOrWhiteSpace(package.Name);
			}
			return false;
		}

		private static string OtherName(string guid, string name)
		{
			int num = guid?.LastIndexOf('.') ?? (-1);
			if (num >= 0 && num < guid.Length - 1)
			{
				return guid.Substring(num + 1);
			}
			return name ?? string.Empty;
		}

		private static string Clean(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(value.Length);
			for (int i = 0; i < value.Length; i++)
			{
				char c = char.ToLowerInvariant(value[i]);
				if (char.IsLetterOrDigit(c))
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}

		private static bool GetMapping(string guid, out string team, out string package)
		{
			team = null;
			package = null;
			if (!CachedMappings.TryGetValue(guid, out var value))
			{
				return false;
			}
			int num = value.IndexOf('/');
			if (num <= 0 || num >= value.Length - 1)
			{
				return false;
			}
			team = value.Substring(0, num);
			package = value.Substring(num + 1);
			return true;
		}

		private static void LoadMap()
		{
			string path = Path.Combine(Paths.ConfigPath, "IceBoxStudio.HowToFish.ModUpdateChecker.json");
			if (!File.Exists(path))
			{
				return;
			}
			try
			{
				MappingCache mappingCache = JsonConvert.DeserializeObject<MappingCache>(File.ReadAllText(path));
				if (mappingCache == null || mappingCache.Version != 1 || mappingCache.Mappings == null)
				{
					return;
				}
				foreach (KeyValuePair<string, string> mapping in mappingCache.Mappings)
				{
					CachedMappings[mapping.Key] = mapping.Value;
				}
			}
			catch
			{
			}
		}

		private static void SaveMap()
		{
			try
			{
				File.WriteAllText(Path.Combine(Paths.ConfigPath, "IceBoxStudio.HowToFish.ModUpdateChecker.json"), JsonConvert.SerializeObject((object)new MappingCache
				{
					Version = 1,
					Mappings = CachedMappings
				}, (Formatting)1));
			}
			catch
			{
			}
		}

		private static int Compare(string local, string remote)
		{
			VersionParts versionParts = VersionParts.Parse(local);
			VersionParts versionParts2 = VersionParts.Parse(remote);
			for (int i = 0; i < 3; i++)
			{
				if (versionParts.Core[i] != versionParts2.Core[i])
				{
					return versionParts.Core[i].CompareTo(versionParts2.Core[i]);
				}
			}
			if (versionParts.Pre.Count == 0 && versionParts2.Pre.Count > 0)
			{
				return 1;
			}
			if (versionParts.Pre.Count > 0 && versionParts2.Pre.Count == 0)
			{
				return -1;
			}
			int num = Math.Max(versionParts.Pre.Count, versionParts2.Pre.Count);
			for (int j = 0; j < num; j++)
			{
				if (j >= versionParts.Pre.Count)
				{
					return -1;
				}
				if (j >= versionParts2.Pre.Count)
				{
					return 1;
				}
				int result;
				bool flag = int.TryParse(versionParts.Pre[j], out result);
				int result2;
				bool flag2 = int.TryParse(versionParts2.Pre[j], out result2);
				if (flag && flag2 && result != result2)
				{
					return result.CompareTo(result2);
				}
				if (flag != flag2)
				{
					if (!flag)
					{
						return 1;
					}
					return -1;
				}
				int num2 = string.Compare(versionParts.Pre[j], versionParts2.Pre[j], StringComparison.OrdinalIgnoreCase);
				if (num2 != 0)
				{
					return num2;
				}
			}
			return 0;
		}

		private static void Notify()
		{
			ModUpdateRuntime.Changed?.Invoke();
		}

		private static bool RestoreSession(ModUpdateEntry entry)
		{
			if (entry == null || !CacheIsFresh())
			{
				return false;
			}
			if (!SessionResults.TryGetValue(entry.Guid, out var value) || !string.Equals(value.Name, entry.Name, StringComparison.Ordinal) || !string.Equals(value.LocalVersion, entry.LocalVersion, StringComparison.Ordinal))
			{
				return false;
			}
			CopyEntry(entry, value);
			return true;
		}

		private static bool CacheIsFresh()
		{
			if (_sessionCachedAt != default(DateTime))
			{
				return DateTime.UtcNow - _sessionCachedAt < SessionCacheTime;
			}
			return false;
		}

		private static void CacheSession(ModUpdateEntry entry)
		{
			if (entry != null && !string.IsNullOrWhiteSpace(entry.Guid))
			{
				SessionResults[entry.Guid] = CopyEntry(entry);
			}
		}

		private static void CopyEntry(ModUpdateEntry target, ModUpdateEntry source)
		{
			target.Team = source.Team;
			target.Package = source.Package;
			target.LatestVersion = source.LatestVersion;
			target.PackageUrl = source.PackageUrl;
			target.State = source.State;
			target.Error = source.Error;
			target.Candidates = ((source.Candidates == null) ? new List<ModUpdateCandidate>() : source.Candidates.Select(CopyCandidate).ToList());
		}

		private static ModUpdateEntry CopyEntry(ModUpdateEntry source)
		{
			ModUpdateEntry obj = new ModUpdateEntry
			{
				Guid = source.Guid,
				Name = source.Name,
				LocalVersion = source.LocalVersion
			};
			CopyEntry(obj, source);
			return obj;
		}

		private static ModUpdateCandidate CopyCandidate(ModUpdateCandidate source)
		{
			return new ModUpdateCandidate
			{
				Team = source.Team,
				Package = source.Package,
				LatestVersion = source.LatestVersion,
				PackageUrl = source.PackageUrl,
				State = source.State,
				Error = source.Error
			};
		}
	}
}
namespace ModUpdateChecker.Localization
{
	internal static class I18n
	{
		private const string FileName = "ModUpdateChecker.Localization.json";

		private static readonly ModLocalizer _localizer = Load();

		internal static string Text(string key, params object[] args)
		{
			return _localizer.GetLocalizedText(key, args);
		}

		private static ModLocalizer Load()
		{
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			ModLocalizer obj = LocalizationApi.For("IceBoxStudio.HowToFish.ModUpdateChecker");
			obj.RegisterJson(Path.Combine(directoryName, "ModUpdateChecker.Localization.json"));
			return obj;
		}
	}
}
namespace ModUpdateChecker.Configuration
{
	internal static class ConfigManager
	{
		private static ConfigEntry<bool> _checkOnStartup;

		private static ConfigEntry<float> _startupDelay;

		private static ConfigEntry<int> _requestTimeout;

		private static ConfigEntry<string> _mappings;

		internal static bool CheckOnStartup
		{
			get
			{
				if (_checkOnStartup != null)
				{
					return _checkOnStartup.Value;
				}
				return true;
			}
		}

		internal static float StartupDelay => Mathf.Max(0f, (_startupDelay == null) ? 3f : _startupDelay.Value);

		internal static int RequestTimeout => Mathf.Clamp((_requestTimeout == null) ? 10 : _requestTimeout.Value, 3, 60);

		internal static void Init(ConfigFile config)
		{
			_checkOnStartup = config.Bind<bool>("General", "CheckOnStartup", true, I18n.Text("config.check_startup"));
			_startupDelay = config.Bind<float>("General", "StartupDelay", 3f, I18n.Text("config.startup_delay"));
			_requestTimeout = config.Bind<int>("Network", "RequestTimeout", 10, I18n.Text("config.request_timeout"));
			_mappings = config.Bind<string>("Packages", "Mappings", string.Empty, I18n.Text("config.mappings"));
			config.Save();
		}

		internal static bool TryGetMapping(string guid, out string team, out string package)
		{
			team = null;
			package = null;
			string text = ((_mappings == null) ? string.Empty : _mappings.Value);
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string[] array = text.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length == 0 || text2.StartsWith("#", StringComparison.Ordinal))
				{
					continue;
				}
				int num = text2.IndexOf('=');
				if (num > 0 && string.Equals(text2.Substring(0, num).Trim(), guid, StringComparison.OrdinalIgnoreCase))
				{
					string text3 = text2.Substring(num + 1).Trim();
					int num2 = text3.IndexOf('/');
					if (num2 <= 0 || num2 >= text3.Length - 1)
					{
						return false;
					}
					team = text3.Substring(0, num2).Trim();
					package = text3.Substring(num2 + 1).Trim();
					if (team.Length > 0)
					{
						return package.Length > 0;
					}
					return false;
				}
			}
			return false;
		}
	}
}