Decompiled source of BaseModelSearch v1.0.1

BaseModelSearch.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace UCMSearch;

internal static class CnAliases
{
	private const string PluginTypeName = "CN_AllInOne.AllInOnePatchPlugin";

	private const string DataTypeName = "CN_AllInOne.TranslationData";

	private const string FieldName = "Translations";

	private const string MethodName = "GetTranslations";

	private static bool _resolved;

	private static bool _warned;

	private static DateTime _nextAttempt = DateTime.MinValue;

	private static Dictionary<string, string> _exact;

	private static Dictionary<string, string> _normalized;

	internal static int Count
	{
		get
		{
			if (_normalized != null)
			{
				return _normalized.Count;
			}
			return 0;
		}
	}

	internal static void EnsureLoaded()
	{
		if (_resolved || DateTime.UtcNow < _nextAttempt)
		{
			return;
		}
		_nextAttempt = DateTime.UtcNow.AddSeconds(3.0);
		try
		{
			IDictionary dictionary = ReadTable();
			if (dictionary == null || dictionary.Count == 0)
			{
				if (!_warned)
				{
					_warned = true;
					Plugin.LogInfo("[CN] no Chinese translation table yet - search stays English only for now");
				}
				return;
			}
			_exact = new Dictionary<string, string>(StringComparer.Ordinal);
			_normalized = new Dictionary<string, string>(StringComparer.Ordinal);
			foreach (DictionaryEntry item in dictionary)
			{
				string text = item.Key as string;
				string value = item.Value as string;
				if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(value))
				{
					if (!_exact.ContainsKey(text))
					{
						_exact[text] = value;
					}
					string text2 = Normalize(text);
					if (text2.Length > 0 && !_normalized.ContainsKey(text2))
					{
						_normalized[text2] = value;
					}
				}
			}
			_resolved = true;
			Plugin.LogInfo("[CN] Chinese aliases loaded: " + _normalized.Count + " keys (" + dictionary.Count + " raw entries)");
		}
		catch (Exception ex)
		{
			Plugin.LogWarn("[CN] could not read the Chinese translation table: " + ex.Message);
		}
	}

	private static IDictionary ReadTable()
	{
		Type type = AccessTools.TypeByName("CN_AllInOne.AllInOnePatchPlugin");
		if (type != null)
		{
			FieldInfo fieldInfo = AccessTools.Field(type, "Translations");
			if (fieldInfo != null && fieldInfo.GetValue(null) is IDictionary { Count: >0 } dictionary)
			{
				return dictionary;
			}
		}
		Type type2 = AccessTools.TypeByName("CN_AllInOne.TranslationData");
		if (type2 != null)
		{
			MethodInfo methodInfo = AccessTools.Method(type2, "GetTranslations", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				return methodInfo.Invoke(null, null) as IDictionary;
			}
		}
		return null;
	}

	private static string Normalize(string s)
	{
		if (string.IsNullOrEmpty(s))
		{
			return string.Empty;
		}
		StringBuilder stringBuilder = new StringBuilder(s.Length);
		foreach (char c in s)
		{
			if (char.IsLetterOrDigit(c))
			{
				stringBuilder.Append(char.ToUpperInvariant(c));
			}
		}
		return stringBuilder.ToString();
	}

	internal static string ChineseFor(string english)
	{
		EnsureLoaded();
		if (string.IsNullOrEmpty(english))
		{
			return null;
		}
		if (_exact != null && _exact.TryGetValue(english, out var value))
		{
			return value;
		}
		if (_normalized != null && _normalized.TryGetValue(Normalize(english), out var value2))
		{
			return value2;
		}
		return null;
	}

	internal static string WithAliases(string haystack, params string[] candidates)
	{
		if (candidates == null || candidates.Length == 0)
		{
			return haystack;
		}
		EnsureLoaded();
		if (_normalized == null && _exact == null)
		{
			return haystack;
		}
		for (int i = 0; i < candidates.Length; i++)
		{
			string text = ChineseFor(candidates[i]);
			if (!string.IsNullOrEmpty(text))
			{
				haystack = haystack + " " + text.ToLowerInvariant();
			}
		}
		return haystack;
	}
}
[BepInPlugin("saucein.ucm.modelsearch", "UCM Model Search", "1.2.1")]
[BepInProcess("TotallyAccurateBattleSimulator.exe")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "saucein.ucm.modelsearch";

	public const string PluginName = "UCM Model Search";

	public const string PluginVersion = "1.2.1";

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> CfgUcmDropdown;

	internal static ConfigEntry<bool> CfgAutoApply;

	internal static ConfigEntry<bool> CfgBilingual;

	internal static ConfigEntry<float> CfgUcmBoxWidth;

	internal static ConfigEntry<float> CfgUcmBoxHeight;

	internal static ConfigEntry<float> CfgUcmBoxGap;

	internal static ConfigEntry<bool> CfgVerbose;

	private void Awake()
	{
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		CfgUcmDropdown = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SearchInUcmDropdown", true, "Add a search box above UCM's 'uc tools' window that filters the 'LOAD THE UC!' dropdown.");
		CfgAutoApply = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AutoApplyWhenPicked", false, "When picking a model from the filtered UCM dropdown, immediately run UCM's 'update custom base' instead of waiting for you to click the toggle.");
		CfgBilingual = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "BilingualSearch", true, "Also match models by their Chinese names taken from the CN_AllInOne Chinese localization patch (CN_All.dll). English names keep working either way; with the patch missing this option does nothing.");
		CfgUcmBoxWidth = ((BaseUnityPlugin)this).Config.Bind<float>("General", "UcmSearchBoxWidth", 320f, "Width (in UI units) of the search box that sits above UCM's 'uc tools' window.");
		CfgUcmBoxHeight = ((BaseUnityPlugin)this).Config.Bind<float>("General", "UcmSearchBoxHeight", 30f, "Height (in UI units) of that search box.");
		CfgUcmBoxGap = ((BaseUnityPlugin)this).Config.Bind<float>("General", "UcmSearchBoxGap", 6f, "Gap (in UI units) between the search box and the top of UCM's 'uc tools' window.");
		CfgVerbose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "VerboseLogging", true, "Log extra information to the BepInEx console/log file (useful when reporting a problem).");
		GameObject val = new GameObject("UCMSearch");
		Object.DontDestroyOnLoad((Object)val);
		val.AddComponent<UcmDropdownSearch>();
		if (CfgBilingual.Value)
		{
			CnAliases.EnsureLoaded();
		}
		LogInfo("UCM Model Search 1.2.1 loaded. Waiting for the game UI.");
	}

	internal static void LogInfo(string message)
	{
		if (Log != null)
		{
			if (CfgVerbose != null && !CfgVerbose.Value)
			{
				Log.LogDebug((object)message);
			}
			else
			{
				Log.LogInfo((object)message);
			}
		}
	}

	internal static void LogWarn(string message)
	{
		if (Log != null)
		{
			Log.LogWarning((object)message);
		}
	}
}
internal abstract class SearchOverlay : MonoBehaviour
{
	protected Rect BoxRect;

	protected bool BoxValid;

	protected bool Focused;

	protected string Query = string.Empty;

	private GUIStyle _style;

	private GUIStyle _rightStyle;

	private Texture2D _white;

	private float _styleHeight = -1f;

	private static readonly Color BoxColor = new Color(0.129f, 0.145f, 0.2f, 0.96f);

	private static readonly Color BorderColor = new Color(0.239f, 0.263f, 0.333f, 1f);

	private static readonly Color TextColor = new Color(0.98f, 0.98f, 0.98f, 1f);

	private static readonly Color HintColor = new Color(0.72f, 0.74f, 0.8f, 1f);

	private static readonly Color CaretColor = new Color(1f, 1f, 1f, 0.9f);

	private static Font _guiFont;

	private static bool _guiFontTried;

	protected abstract string Hint { get; }

	protected virtual string Status => string.Empty;

	protected abstract bool ComputeBox(out Rect rect);

	protected abstract void ApplyQuery(string query);

	protected virtual void Commit()
	{
	}

	protected void TickOverlay()
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		BoxValid = ComputeBox(out var rect);
		if (!BoxValid)
		{
			SetFocus(focused: false);
			return;
		}
		BoxRect = rect;
		HandleInput();
	}

	protected void DrawOverlay()
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bd: 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_0191: Unknown result type (might be due to invalid IL or missing references)
		//IL_029b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0201: Expected O, but got Unknown
		//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0259: Unknown result type (might be due to invalid IL or missing references)
		//IL_026b: Unknown result type (might be due to invalid IL or missing references)
		if (!BoxValid)
		{
			return;
		}
		try
		{
			EnsureResources();
			GUI.depth = -10000;
			float num = Mathf.Clamp(((Rect)(ref BoxRect)).height * 0.3f, 4f, 12f);
			GUI.DrawTexture(BoxRect, (Texture)(object)_white, (ScaleMode)0, true, 0f, BorderColor, 0f, num);
			GUI.DrawTexture(new Rect(((Rect)(ref BoxRect)).x + 1f, ((Rect)(ref BoxRect)).y + 1f, ((Rect)(ref BoxRect)).width - 2f, ((Rect)(ref BoxRect)).height - 2f), (Texture)(object)_white, (ScaleMode)0, true, 0f, BoxColor, 0f, Mathf.Max(2f, num - 1f));
			float num2 = Mathf.Clamp(((Rect)(ref BoxRect)).height * 0.3f, 5f, 18f);
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(((Rect)(ref BoxRect)).x + num2, ((Rect)(ref BoxRect)).y + 2f, ((Rect)(ref BoxRect)).width - num2 * 2f, ((Rect)(ref BoxRect)).height - 4f);
			string text = string.Empty;
			if (Focused)
			{
				try
				{
					text = Input.compositionString;
				}
				catch
				{
					text = string.Empty;
				}
			}
			string text2 = Query + text;
			if (text2.Length == 0 && !Focused)
			{
				_style.normal.textColor = HintColor;
				GUI.Label(val, Hint, _style);
			}
			else
			{
				_style.normal.textColor = TextColor;
				GUI.Label(val, text2, _style);
				if (Focused && Mathf.PingPong(Time.unscaledTime, 1f) < 0.55f)
				{
					float x = _style.CalcSize(new GUIContent(text2)).x;
					float num3 = Mathf.Min(((Rect)(ref val)).x + x + 1f, ((Rect)(ref BoxRect)).xMax - num2 - 2f);
					GUI.DrawTexture(new Rect(num3, ((Rect)(ref BoxRect)).y + num2 * 0.5f, 2f, ((Rect)(ref BoxRect)).height - num2), (Texture)(object)_white, (ScaleMode)0, true, 0f, CaretColor, 0f, 1f);
				}
			}
			string status = Status;
			if (!string.IsNullOrEmpty(status))
			{
				_rightStyle.normal.textColor = HintColor;
				GUI.Label(val, status, _rightStyle);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("[search] could not draw the search box: " + ex.Message));
			BoxValid = false;
		}
	}

	protected string DisplayText()
	{
		string text = string.Empty;
		if (Focused)
		{
			try
			{
				text = Input.compositionString;
			}
			catch
			{
				text = string.Empty;
			}
		}
		if (Query.Length == 0 && text.Length == 0 && !Focused)
		{
			return Hint;
		}
		return Query + text;
	}

	private void HandleInput()
	{
		if (Input.GetMouseButtonDown(0))
		{
			SetFocus(ContainsMouse());
		}
		if (!Focused)
		{
			return;
		}
		bool flag = false;
		string inputString = Input.inputString;
		if (!string.IsNullOrEmpty(inputString))
		{
			for (int i = 0; i < inputString.Length; i++)
			{
				char c = inputString[i];
				if (c == '\b' || c == '\u007f')
				{
					if (Query.Length > 0)
					{
						Query = Query.Substring(0, Query.Length - 1);
						flag = true;
					}
					continue;
				}
				if (c == '\n' || c == '\r')
				{
					Commit();
					SetFocus(focused: false);
					return;
				}
				if (c == '\u001b')
				{
					Query = string.Empty;
					SetFocus(focused: false);
					ApplyQuery(Query);
					return;
				}
				if (c >= ' ')
				{
					Query += c;
					flag = true;
				}
			}
		}
		if (flag)
		{
			ApplyQuery(Query);
		}
		if (Input.GetKeyDown((KeyCode)27))
		{
			Query = string.Empty;
			SetFocus(focused: false);
			ApplyQuery(Query);
		}
	}

	private bool ContainsMouse()
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		Vector2 val = Vector2.op_Implicit(Input.mousePosition);
		return ((Rect)(ref BoxRect)).Contains(new Vector2(val.x, (float)Screen.height - val.y));
	}

	protected void SetFocus(bool focused)
	{
		if (Focused == focused)
		{
			return;
		}
		Focused = focused;
		try
		{
			Input.imeCompositionMode = (IMECompositionMode)(focused ? 1 : 0);
		}
		catch
		{
		}
	}

	protected void ClearQuery()
	{
		Query = string.Empty;
		SetFocus(focused: false);
		ApplyQuery(Query);
	}

	private void EnsureResources()
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Expected O, but got Unknown
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Expected O, but got Unknown
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Expected O, but got Unknown
		if ((Object)(object)_white == (Object)null)
		{
			_white = new Texture2D(1, 1);
			_white.SetPixel(0, 0, Color.white);
			_white.Apply();
			((Object)_white).hideFlags = (HideFlags)61;
		}
		float height = ((Rect)(ref BoxRect)).height;
		if (_style == null)
		{
			_style = new GUIStyle(GUI.skin.label);
			_style.alignment = (TextAnchor)3;
			_style.fontStyle = (FontStyle)1;
			_style.richText = false;
			_style.wordWrap = false;
			_style.clipping = (TextClipping)1;
			_rightStyle = new GUIStyle(_style);
			_rightStyle.fontStyle = (FontStyle)1;
			_rightStyle.alignment = (TextAnchor)5;
			Font guiFont = GetGuiFont();
			if ((Object)(object)guiFont != (Object)null)
			{
				_style.font = guiFont;
				_rightStyle.font = guiFont;
			}
		}
		if (!Mathf.Approximately(_styleHeight, height))
		{
			_styleHeight = height;
			int fontSize = Mathf.RoundToInt(Mathf.Clamp(height * 0.45f, 10f, 26f));
			_style.fontSize = fontSize;
			_rightStyle.fontSize = fontSize;
		}
	}

	private static Font GetGuiFont()
	{
		if (_guiFontTried)
		{
			return _guiFont;
		}
		_guiFontTried = true;
		try
		{
			_guiFont = Font.CreateDynamicFontFromOSFont(new string[7] { "Microsoft YaHei UI", "Microsoft YaHei", "SimHei", "SimSun", "Arial Unicode MS", "Segoe UI", "Arial" }, 16);
			Plugin.LogInfo("[search] overlay font: " + (((Object)(object)_guiFont != (Object)null) ? ((Object)_guiFont).name : "<built-in>"));
		}
		catch (Exception ex)
		{
			Plugin.LogWarn("[search] could not create an OS font, using the built-in one: " + ex.Message);
			_guiFont = null;
		}
		return _guiFont;
	}

	protected static bool ScreenRectOf(RectTransform rt, Canvas canvas, out Rect rect, out float scale)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: 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_0025: 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_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: 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_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		rect = default(Rect);
		scale = 1f;
		if ((Object)(object)rt == (Object)null)
		{
			return false;
		}
		Camera val = null;
		if ((Object)(object)canvas != (Object)null && (int)canvas.renderMode != 0)
		{
			val = canvas.worldCamera;
		}
		Vector3[] array = (Vector3[])(object)new Vector3[4];
		rt.GetWorldCorners(array);
		Vector2 val2 = RectTransformUtility.WorldToScreenPoint(val, array[0]);
		Vector2 val3 = RectTransformUtility.WorldToScreenPoint(val, array[2]);
		float num = Mathf.Min(val2.x, val3.x);
		float num2 = Mathf.Max(val2.x, val3.x);
		float num3 = Mathf.Min(val2.y, val3.y);
		float num4 = Mathf.Max(val2.y, val3.y);
		if (num2 - num < 2f || num4 - num3 < 2f)
		{
			return false;
		}
		Rect rect2 = rt.rect;
		float num5;
		if (!(((Rect)(ref rect2)).width > 1f))
		{
			num5 = num2 - num;
		}
		else
		{
			rect2 = rt.rect;
			num5 = ((Rect)(ref rect2)).width;
		}
		float num6 = num5;
		scale = (num2 - num) / Mathf.Max(1f, num6);
		rect = new Rect(num, (float)Screen.height - num4, num2 - num, num4 - num3);
		return true;
	}

	protected static float ScaleOf(RectTransform rt)
	{
		if (!ScreenRectOf(rt, null, out var _, out var scale))
		{
			return 1f;
		}
		return scale;
	}
}
internal class UcmDropdownSearch : SearchOverlay
{
	private const string NoMatchText = "-- no match --";

	private const float ScanInterval = 0.5f;

	private Transform _ucmRoot;

	private Dropdown _dropdown;

	private Canvas _canvas;

	private readonly List<string> _master = new List<string>();

	private readonly List<string> _lastApplied = new List<string>();

	private readonly List<string> _scratch = new List<string>();

	private readonly List<string> _filtered = new List<string>();

	private string _appliedQuery;

	private int _lastSyncedValue = -1;

	private float _nextScan;

	private Vector2 _lastLoggedSize;

	private static Type _mainType;

	private static FieldInfo _customBaseField;

	private static FieldInfo _instanceField;

	private static MethodInfo _buttonsMethod;

	private static bool _reflectionResolved;

	protected override string Hint => "Search Base";

	protected override string Status
	{
		get
		{
			int num = 0;
			for (int i = 0; i < _filtered.Count; i++)
			{
				if (_filtered[i] != "-- no match --")
				{
					num++;
				}
			}
			if (_master.Count <= 0)
			{
				return string.Empty;
			}
			return num + "/" + _master.Count;
		}
	}

	private void Update()
	{
		try
		{
			if (!Plugin.CfgUcmDropdown.Value)
			{
				return;
			}
			if ((Object)(object)_dropdown == (Object)null)
			{
				if (Time.unscaledTime < _nextScan)
				{
					return;
				}
				_nextScan = Time.unscaledTime + 0.5f;
				EnsureUi();
				if ((Object)(object)_dropdown == (Object)null)
				{
					return;
				}
			}
			TickOverlay();
			if (BoxValid)
			{
				SyncOptions();
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("[UCM search] " + ex));
		}
	}

	private void OnGUI()
	{
		if (Plugin.CfgUcmDropdown.Value)
		{
			DrawOverlay();
		}
	}

	private void EnsureUi()
	{
		if ((Object)(object)_ucmRoot == (Object)null)
		{
			GameObject val = GameObject.Find("UCMUI(Clone)");
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			_ucmRoot = val.transform;
			Plugin.LogInfo("[UCM] UCM UI found");
		}
		if (!((Object)(object)_dropdown == (Object)null))
		{
			return;
		}
		Transform val2 = UiFactory.FindDeep(_ucmRoot, "custombasedropdown");
		if (!((Object)(object)val2 == (Object)null))
		{
			_dropdown = ((Component)val2).GetComponent<Dropdown>();
			if (!((Object)(object)_dropdown == (Object)null))
			{
				_canvas = ((Component)_dropdown).GetComponentInParent<Canvas>();
				Plugin.LogInfo("[UCM] custom base dropdown located");
			}
		}
	}

	protected override bool ComputeBox(out Rect rect)
	{
		//IL_0001: 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_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: 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)
		rect = default(Rect);
		if ((Object)(object)_dropdown == (Object)null)
		{
			return false;
		}
		Transform parent = ((Component)_dropdown).transform.parent;
		RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
		if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy)
		{
			return false;
		}
		if (!SearchOverlay.ScreenRectOf(val, _canvas, out var rect2, out var scale))
		{
			return false;
		}
		float num = Mathf.Clamp(Plugin.CfgUcmBoxWidth.Value * scale, 160f, Mathf.Max(160f, ((Rect)(ref rect2)).width));
		float num2 = Mathf.Max(22f, Plugin.CfgUcmBoxHeight.Value * scale);
		float num3 = Mathf.Max(2f, Plugin.CfgUcmBoxGap.Value * scale);
		float num4 = Mathf.Clamp(((Rect)(ref rect2)).center.x - num * 0.5f, 0f, Mathf.Max(0f, (float)Screen.width - num));
		float num5 = Mathf.Clamp(((Rect)(ref rect2)).y - num3 - num2, 0f, Mathf.Max(0f, (float)Screen.height - num2));
		rect = new Rect(num4, num5, num, num2);
		Vector2 val2 = default(Vector2);
		((Vector2)(ref val2))..ctor(num, num2);
		if (val2 != _lastLoggedSize)
		{
			_lastLoggedSize = val2;
			Plugin.LogInfo($"[UCM] search box above the window at ({num4:F0},{num5:F0}) size {num:F0}x{num2:F0} px (scale {scale:F2})");
		}
		return true;
	}

	protected override void ApplyQuery(string query)
	{
		Apply(query);
	}

	protected override void Commit()
	{
		string text = null;
		for (int i = 0; i < _filtered.Count; i++)
		{
			if (!(_filtered[i] == "-- no match --"))
			{
				text = _filtered[i];
				break;
			}
		}
		if (text != null)
		{
			SetCustomBase(text);
			if (Plugin.CfgAutoApply.Value)
			{
				InvokeUpdateCustomBase();
			}
		}
	}

	private void SyncOptions()
	{
		List<string> list = Snapshot(_dropdown, _scratch);
		if (!SameList(list, _lastApplied))
		{
			_master.Clear();
			_master.AddRange(list);
			_appliedQuery = null;
			Plugin.LogInfo("[UCM] custom base dropdown has " + _master.Count + " entries");
		}
		if (_appliedQuery != Query)
		{
			Apply(Query);
		}
		else if (_dropdown.value != _lastSyncedValue)
		{
			_lastSyncedValue = _dropdown.value;
			string text = SelectedText();
			if (Plugin.CfgAutoApply.Value && !string.IsNullOrEmpty(text) && text != "-- no match --")
			{
				SetCustomBase(text);
				InvokeUpdateCustomBase();
			}
		}
	}

	private void Apply(string query)
	{
		_appliedQuery = query;
		if ((Object)(object)_dropdown == (Object)null || _master.Count == 0)
		{
			return;
		}
		List<string> list = UiFactory.SplitQuery(query);
		_filtered.Clear();
		if (list.Count == 0)
		{
			_filtered.AddRange(_master);
		}
		else
		{
			for (int i = 0; i < _master.Count; i++)
			{
				if (!(_master[i] == "-- no match --"))
				{
					string haystack = _master[i].ToLowerInvariant();
					if (Plugin.CfgBilingual.Value)
					{
						haystack = CnAliases.WithAliases(haystack, _master[i]);
					}
					if (UiFactory.Matches(list, haystack))
					{
						_filtered.Add(_master[i]);
					}
				}
			}
		}
		if (_filtered.Count == 0)
		{
			_filtered.Add("-- no match --");
		}
		string text = SelectedText();
		_dropdown.ClearOptions();
		_dropdown.AddOptions(_filtered);
		int num = ((text != null) ? _filtered.IndexOf(text) : (-1));
		if (num < 0)
		{
			num = 0;
		}
		_dropdown.value = num;
		_dropdown.RefreshShownValue();
		_lastSyncedValue = num;
		SetCustomBase(_filtered[num]);
		_lastApplied.Clear();
		_lastApplied.AddRange(_filtered);
		Plugin.LogInfo("[UCM] filter '" + query + "' -> " + ((_filtered.Count != 1 || !(_filtered[0] == "-- no match --")) ? _filtered.Count : 0) + " option(s)");
	}

	private string SelectedText()
	{
		if ((Object)(object)_dropdown == (Object)null)
		{
			return null;
		}
		List<OptionData> options = _dropdown.options;
		int value = _dropdown.value;
		if (options == null || value < 0 || value >= options.Count)
		{
			return null;
		}
		return options[value].text;
	}

	private static List<string> Snapshot(Dropdown dropdown, List<string> into)
	{
		into.Clear();
		if ((Object)(object)dropdown == (Object)null || dropdown.options == null)
		{
			return into;
		}
		for (int i = 0; i < dropdown.options.Count; i++)
		{
			into.Add(dropdown.options[i].text);
		}
		return into;
	}

	private static bool SameList(List<string> a, List<string> b)
	{
		if (a.Count != b.Count)
		{
			return false;
		}
		for (int i = 0; i < a.Count; i++)
		{
			if (!string.Equals(a[i], b[i], StringComparison.Ordinal))
			{
				return false;
			}
		}
		return true;
	}

	private static void ResolveReflection()
	{
		if (_reflectionResolved)
		{
			return;
		}
		_reflectionResolved = true;
		try
		{
			_mainType = AccessTools.TypeByName("UCU.Main");
			if (_mainType == null)
			{
				Plugin.LogWarn("[UCM] type UCU.Main not found - base sync disabled");
				return;
			}
			_customBaseField = AccessTools.Field(_mainType, "custombase");
			_instanceField = AccessTools.Field(_mainType, "instance");
			_buttonsMethod = AccessTools.Method(_mainType, "Buttons", new Type[1] { typeof(string) }, (Type[])null);
		}
		catch (Exception ex)
		{
			Plugin.LogWarn("[UCM] reflection setup failed: " + ex.Message);
		}
	}

	private static void SetCustomBase(string modelName)
	{
		ResolveReflection();
		if (_customBaseField == null)
		{
			return;
		}
		try
		{
			_customBaseField.SetValue(null, modelName);
		}
		catch (Exception ex)
		{
			Plugin.LogWarn("[UCM] could not set custombase: " + ex.Message);
		}
	}

	private static void InvokeUpdateCustomBase()
	{
		ResolveReflection();
		if (_buttonsMethod == null || _instanceField == null)
		{
			return;
		}
		try
		{
			object value = _instanceField.GetValue(null);
			if (value != null)
			{
				_buttonsMethod.Invoke(value, new object[1] { "UpdateCustom" });
				Plugin.LogInfo("[UCM] applied the picked model");
			}
		}
		catch (Exception ex)
		{
			Plugin.LogWarn("[UCM] could not apply the picked model: " + ex.Message);
		}
	}
}
internal static class UiFactory
{
	internal static Transform FindDeep(Transform root, string name)
	{
		if ((Object)(object)root == (Object)null)
		{
			return null;
		}
		if (((Object)root).name == name)
		{
			return root;
		}
		for (int i = 0; i < root.childCount; i++)
		{
			Transform val = FindDeep(root.GetChild(i), name);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
		}
		return null;
	}

	internal static List<Transform> FindAllDeep(Transform root, string name)
	{
		List<Transform> result = new List<Transform>();
		FindAllDeep(root, name, result);
		return result;
	}

	private static void FindAllDeep(Transform root, string name, List<Transform> result)
	{
		if (!((Object)(object)root == (Object)null))
		{
			if (((Object)root).name == name)
			{
				result.Add(root);
			}
			for (int i = 0; i < root.childCount; i++)
			{
				FindAllDeep(root.GetChild(i), name, result);
			}
		}
	}

	internal static bool IsSceneObject(GameObject go)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)go == (Object)null)
		{
			return false;
		}
		try
		{
			Scene scene = go.scene;
			return ((Scene)(ref scene)).IsValid();
		}
		catch
		{
			return false;
		}
	}

	internal static List<string> SplitQuery(string query)
	{
		List<string> list = new List<string>();
		if (string.IsNullOrEmpty(query))
		{
			return list;
		}
		string[] array = query.ToLowerInvariant().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
		for (int i = 0; i < array.Length; i++)
		{
			if (array[i].Length > 0)
			{
				list.Add(array[i]);
			}
		}
		return list;
	}

	internal static bool Matches(List<string> tokens, string haystack)
	{
		if (tokens.Count == 0)
		{
			return true;
		}
		if (string.IsNullOrEmpty(haystack))
		{
			return false;
		}
		for (int i = 0; i < tokens.Count; i++)
		{
			if (haystack.IndexOf(tokens[i], StringComparison.Ordinal) < 0)
			{
				return false;
			}
		}
		return true;
	}
}