Decompiled source of ItemRotationControl v1.0.2

Zichen-ItemRotationControl-1.0.2.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using Zichen_ItemRotationControl.I18N;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Zichen-ItemRotationControl-1.0.2")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+edf53df903579000416037543c35c2d7b4f23cc6")]
[assembly: AssemblyProduct("Zichen-ItemRotationControl-1.0.2")]
[assembly: AssemblyTitle("Zichen-ItemRotationControl-1.0.2")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
[BepInPlugin("zichen.itemrotationcontrol", "A.Item Rotation Control", "1.0.2")]
public sealed class ZichenItemRotationControlPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "zichen.itemrotationcontrol";

	public const string PluginVersion = "1.0.2";

	public const string PluginName = "A.Item Rotation Control";

	private const string ContactConfigSection = "模组信息";

	private const string RotationConfigSection = "A.物品旋转控制";

	private ConfigEntry<string> moduleNameInfo;

	private ConfigEntry<string> moduleVersionInfo;

	private ConfigEntry<string> contactInfo;

	private static ConfigEntry<bool> rotationControlEnabled;

	private static ConfigEntry<bool> invertItemRotationHorizontal;

	private static ConfigEntry<bool> invertItemRotationVertical;

	private static ConfigEntry<float> itemRotationSensitivity;

	private static int itemRotationInputDepth;

	private Harmony harmony;

	private I18NRegistry _i18n;

	private I18NSwitcher _i18nSwitcher;

	private bool _autoLocaleChecked;

	public static ZichenItemRotationControlPlugin Instance { get; private set; }

	public static bool UseChinese()
	{
		return Instance?._i18n?.UseChinese == true;
	}

	private void Awake()
	{
		DetachFromManager();
		MakePluginHostPersistent();
		ResetConfigIfVersionChanged();
		Instance = this;
		bool defaultChinese = LanguageDetector.DetectDefault();
		_i18n = new I18NRegistry(((BaseUnityPlugin)this).Config);
		_i18n.BindLanguage(defaultChinese);
		moduleNameInfo = _i18n.BindReadOnly("Mod Info", "模组信息", "Mod Name", "模组名称", "物品旋转控制", "物品旋转控制");
		moduleVersionInfo = _i18n.BindReadOnly("Mod Info", "模组信息", "Mod Version", "模组版本号", "1.0.2", "1.0.2");
		contactInfo = _i18n.BindReadOnly("Mod Info", "模组信息", "Contact QQ", "REPO交流QQ群", "824639225", "824639225");
		rotationControlEnabled = _i18n.Bind("A.物品旋转控制", "A.物品旋转控制", "Enabled", "启用", "When enabled, mouse input while holding right-click to rotate items is modified.", "开启后才会修改按住右键旋转物品时的鼠标输入。", defaultValue: true);
		invertItemRotationHorizontal = _i18n.Bind("A.物品旋转控制", "A.物品旋转控制", "Invert Horizontal", "反转左右旋转", "When enabled, the horizontal mouse direction is reversed while rotating items with right-click held.", "开启后按住右键旋转物品时,鼠标左右方向会反过来。", defaultValue: true);
		invertItemRotationVertical = _i18n.Bind("A.物品旋转控制", "A.物品旋转控制", "Invert Vertical", "反转上下旋转", "When enabled, the vertical mouse direction is reversed while rotating items with right-click held.", "开启后按住右键旋转物品时,鼠标上下方向会反过来。", defaultValue: true);
		itemRotationSensitivity = _i18n.Bind("A.物品旋转控制", "A.物品旋转控制", "Rotation Sensitivity", "旋转灵敏度/倍率", "Mouse sensitivity multiplier while rotating items with right-click held. 1.0 is the vanilla speed.", "按住右键旋转物品时的鼠标灵敏度倍率。1.0 为原版速度。", 1f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f));
		ApplyItemRotationPatch();
		if (harmony != null)
		{
			_i18nSwitcher = new I18NSwitcher(_i18n, harmony, "A.Item Rotation Control", ((BaseUnityPlugin)this).Logger);
			_i18nSwitcher.InstallRepoConfigHook();
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)"zichen-item-rotation-control loaded.");
		if (!_autoLocaleChecked)
		{
			((MonoBehaviour)this).StartCoroutine(AutoFollowGameLocaleOnce());
		}
	}

	private IEnumerator AutoFollowGameLocaleOnce()
	{
		float waited = 0f;
		while (waited < 20f)
		{
			waited += 0.2f;
			yield return (object)new WaitForSecondsRealtime(0.2f);
			if (LanguageDetector.TryReadGameLocaleCode(out var code))
			{
				if (LanguageDetector.IsChineseCode(code) && _i18n?.LanguageEntry != null && _i18n.LanguageEntry.Value != I18NRegistry.DisplayLanguage.中文)
				{
					_i18n.LanguageEntry.Value = I18NRegistry.DisplayLanguage.中文;
					((BaseUnityPlugin)this).Logger.LogInfo((object)"已根据游戏内语言设置自动切换为中文。");
				}
				_autoLocaleChecked = true;
				yield break;
			}
		}
		_autoLocaleChecked = true;
	}

	private void DetachFromManager()
	{
		((Component)this).gameObject.transform.parent = null;
		((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
		Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
	}

	private void OnDestroy()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
			harmony = null;
		}
	}

	private void ApplyItemRotationPatch()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Expected O, but got Unknown
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Expected O, but got Unknown
		//IL_00be: Expected O, but got Unknown
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Expected O, but got Unknown
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Expected O, but got Unknown
		try
		{
			harmony = new Harmony("zichen.itemrotationcontrol.patch");
			MethodInfo methodInfo = AccessTools.Method(typeof(PhysGrabber), "ObjectTurning", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(SemiFunc), "InputMouseX", (Type[])null, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.Method(typeof(SemiFunc), "InputMouseY", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null || methodInfo3 == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Failed to find item rotation input methods.");
				return;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ZichenItemRotationControlPlugin), "BeginItemRotationInputPatch", (Type[])null), new HarmonyMethod(typeof(ZichenItemRotationControlPlugin), "EndItemRotationInputPatch", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(ZichenItemRotationControlPlugin), "InputMouseXPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(typeof(ZichenItemRotationControlPlugin), "InputMouseYPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Item rotation controls patched (v0.5.0 input system).");
		}
		catch (Exception arg)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)$"Failed to patch item rotation controls: {arg}");
		}
	}

	private static void BeginItemRotationInputPatch()
	{
		itemRotationInputDepth++;
	}

	private static void EndItemRotationInputPatch()
	{
		itemRotationInputDepth = Math.Max(0, itemRotationInputDepth - 1);
	}

	private static void InputMouseXPostfix(ref float __result)
	{
		if (itemRotationInputDepth > 0 && rotationControlEnabled != null && rotationControlEnabled.Value)
		{
			__result = AdjustItemRotationAxis(__result, horizontal: true);
		}
	}

	private static void InputMouseYPostfix(ref float __result)
	{
		if (itemRotationInputDepth > 0 && rotationControlEnabled != null && rotationControlEnabled.Value)
		{
			__result = AdjustItemRotationAxis(__result, horizontal: false);
		}
	}

	internal static float AdjustItemRotationAxis(float value, bool horizontal)
	{
		float num = ((itemRotationSensitivity == null) ? 1f : Mathf.Clamp(itemRotationSensitivity.Value, 0.1f, 3f));
		float num2 = value * num;
		if (horizontal && invertItemRotationHorizontal != null && invertItemRotationHorizontal.Value)
		{
			num2 = 0f - num2;
		}
		if (!horizontal && invertItemRotationVertical != null && invertItemRotationVertical.Value)
		{
			num2 = 0f - num2;
		}
		return num2;
	}

	private void MakePluginHostPersistent()
	{
		try
		{
			((Component)this).transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to mark plugin host persistent: " + ex.GetType().Name + ": " + ex.Message));
		}
	}

	private void ResetConfigIfVersionChanged()
	{
		try
		{
			string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
			string text = ReadConfigPluginVersion(configFilePath);
			if (!string.IsNullOrWhiteSpace(text) && !(text == "1.0.2"))
			{
				ResetConfigFileToDefaults(configFilePath);
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Config version changed. Old config was reset to defaults.");
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to reset config by version: " + ex.Message));
		}
	}

	private static string ReadConfigPluginVersion(string configPath)
	{
		if (!File.Exists(configPath))
		{
			return null;
		}
		Match match = Regex.Match(File.ReadAllText(configPath), "(?m)^模组版本号\\s*=\\s*(.+?)\\s*$");
		if (!match.Success)
		{
			return null;
		}
		return match.Groups[1].Value.Trim();
	}

	private void ResetConfigFileToDefaults(string configPath)
	{
		((BaseUnityPlugin)this).Config.Clear();
		if (File.Exists(configPath))
		{
			File.Delete(configPath);
		}
		((BaseUnityPlugin)this).Config.Reload();
	}
}
namespace Zichen_ItemRotationControl.I18N
{
	internal static class LanguageDetector
	{
		public static readonly string[] DefaultChineseKeywords = new string[6] { "chinese", "简体", "繁體", "繁体", "汉化", "中文" };

		public static readonly string[] DefaultChineseEnvironmentPluginGuids = new string[1] { "gravydevsupreme.xunity.autotranslator" };

		private const string ChineseLocaleCodePrefix = "zh";

		private static bool _reflectionResolved;

		private static Type _localizationSettingsType;

		private static PropertyInfo _selectedLocaleProp;

		private static PropertyInfo _localeIdentifierProp;

		private static PropertyInfo _identifierCodeProp;

		public static bool DetectDefault()
		{
			if (DetectFromGameLocale())
			{
				return true;
			}
			return DetectDefault(DefaultChineseKeywords, DefaultChineseEnvironmentPluginGuids);
		}

		public static bool IsGameLocaleChinese()
		{
			return DetectFromGameLocale();
		}

		public static bool TryReadGameLocaleCode(out string code)
		{
			code = null;
			try
			{
				code = TryReadGameActiveLocaleCode();
				if (!string.IsNullOrEmpty(code))
				{
					return true;
				}
				code = TryReadSelectedLocaleCode();
				return !string.IsNullOrEmpty(code);
			}
			catch
			{
				code = null;
				return false;
			}
		}

		public static bool IsChineseCode(string code)
		{
			if (!string.IsNullOrEmpty(code))
			{
				return code.StartsWith("zh", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static bool DetectFromGameLocale()
		{
			if (TryReadGameLocaleCode(out var code))
			{
				return IsChineseCode(code);
			}
			return false;
		}

		private static string TryReadGameActiveLocaleCode()
		{
			try
			{
				Type type = Type.GetType("LocalizationManager, Assembly-CSharp");
				if (type == null)
				{
					return null;
				}
				object obj = type.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				if (obj == null)
				{
					return null;
				}
				object obj2 = type.GetField("ActiveLocale")?.GetValue(obj);
				if (obj2 == null)
				{
					return null;
				}
				return ReadLocaleCode(obj2.GetType().GetField("LocaleReference")?.GetValue(obj2));
			}
			catch
			{
				return null;
			}
		}

		private static string TryReadSelectedLocaleCode()
		{
			try
			{
				ResolveReflectionCache();
				if (_localizationSettingsType == null || _selectedLocaleProp == null)
				{
					return null;
				}
				return ReadLocaleCode(_selectedLocaleProp.GetValue(null, null));
			}
			catch
			{
				return null;
			}
		}

		private static string ReadLocaleCode(object locale)
		{
			if (locale == null)
			{
				return null;
			}
			try
			{
				object obj = _localeIdentifierProp?.GetValue(locale, null);
				if (obj == null)
				{
					return null;
				}
				return _identifierCodeProp?.GetValue(obj, null) as string;
			}
			catch
			{
				return null;
			}
		}

		private static void ResolveReflectionCache()
		{
			if (_reflectionResolved)
			{
				return;
			}
			_reflectionResolved = true;
			try
			{
				_localizationSettingsType = Type.GetType("UnityEngine.Localization.Settings.LocalizationSettings, Unity.Localization");
				if (_localizationSettingsType != null)
				{
					_selectedLocaleProp = _localizationSettingsType.GetProperty("SelectedLocale");
					if (_selectedLocaleProp != null)
					{
						_localeIdentifierProp = _selectedLocaleProp.PropertyType?.GetProperty("Identifier");
						_identifierCodeProp = (_localeIdentifierProp?.PropertyType)?.GetProperty("Code");
					}
				}
			}
			catch
			{
				_localizationSettingsType = null;
				_selectedLocaleProp = null;
			}
		}

		public static bool DetectDefault(IEnumerable<string> chineseKeywords, IEnumerable<string> chineseEnvironmentPluginGuids)
		{
			string[] array = ToFilteredArray(chineseKeywords) ?? DefaultChineseKeywords;
			string[] array2 = ToFilteredArray(chineseEnvironmentPluginGuids) ?? DefaultChineseEnvironmentPluginGuids;
			try
			{
				foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
				{
					string text = pluginInfo.Key ?? string.Empty;
					PluginInfo value = pluginInfo.Value;
					object obj;
					if (value == null)
					{
						obj = null;
					}
					else
					{
						BepInPlugin metadata = value.Metadata;
						obj = ((metadata != null) ? metadata.Name : null);
					}
					if (obj == null)
					{
						obj = string.Empty;
					}
					string text2 = (string)obj;
					foreach (string value2 in array)
					{
						if (text.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0)
						{
							return true;
						}
					}
					for (int j = 0; j < array2.Length; j++)
					{
						if (string.Equals(text, array2[j], StringComparison.OrdinalIgnoreCase))
						{
							return true;
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static string[] ToFilteredArray(IEnumerable<string> source)
		{
			if (source == null)
			{
				return null;
			}
			List<string> list = new List<string>();
			foreach (string item in source)
			{
				if (!string.IsNullOrEmpty(item))
				{
					list.Add(item);
				}
			}
			if (list.Count != 0)
			{
				return list.ToArray();
			}
			return null;
		}
	}
	internal sealed class I18NRegistry
	{
		public sealed class CfgI18N
		{
			public string SectionEN;

			public string SectionCN;

			public string KeyEN;

			public string KeyCN;

			public string DescEN;

			public string DescCN;

			public ConfigurationManagerAttributes Attrs;

			public string ValueEN;

			public string ValueCN;

			public ConfigEntry<string> ReadOnlyEntry;
		}

		public enum DisplayLanguage
		{
			中文,
			English
		}

		private readonly ConfigFile _config;

		private readonly ManualLogSource _logger;

		private readonly List<CfgI18N> _all = new List<CfgI18N>(20);

		private readonly Dictionary<string, CfgI18N> _bySection = new Dictionary<string, CfgI18N>(StringComparer.Ordinal);

		private readonly Dictionary<string, Dictionary<string, CfgI18N>> _bySectionAndKey = new Dictionary<string, Dictionary<string, CfgI18N>>(StringComparer.Ordinal);

		public ConfigEntry<DisplayLanguage> LanguageEntry { get; private set; }

		public bool UseChinese
		{
			get
			{
				if (LanguageEntry != null)
				{
					return LanguageEntry.Value == DisplayLanguage.中文;
				}
				return false;
			}
		}

		public int RegisteredCount => _all.Count;

		public IReadOnlyList<CfgI18N> All => _all;

		public event Action LanguageChanged;

		public I18NRegistry(ConfigFile config, ManualLogSource logger = null)
		{
			_config = config ?? throw new ArgumentNullException("config");
			_logger = logger;
		}

		public ConfigEntry<DisplayLanguage> BindLanguage(bool defaultChinese, string sectionEN = "Mod Info", string sectionCN = "模组信息")
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			DisplayLanguage displayLanguage = ((!defaultChinese) ? DisplayLanguage.English : DisplayLanguage.中文);
			CfgI18N cfgI18N = NewI18N(sectionEN, sectionCN, "Language", "语言", "Switch display language.", "切换显示语言。", readOnly: false);
			LanguageEntry = _config.Bind<DisplayLanguage>(sectionEN, "Language", displayLanguage, new ConfigDescription(cfgI18N.DescEN, (AcceptableValueBase)null, new object[1] { cfgI18N.Attrs }));
			LanguageEntry.SettingChanged += delegate
			{
				RefreshAttrs();
				InvokeLanguageChanged();
			};
			return LanguageEntry;
		}

		public ConfigEntry<T> Bind<T>(string sectEN, string sectCN, string keyEN, string keyCN, string descEN, string descCN, T defaultValue, AcceptableValueBase acceptableValues = null, int order = 0, Action<ConfigEntryBase> customDrawer = null, bool? showRangeAsPercent = null, bool? isAdvanced = null, bool? browsable = null)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			CfgI18N cfgI18N = NewI18N(sectEN, sectCN, keyEN, keyCN, descEN, descCN, readOnly: false);
			if (order != 0)
			{
				cfgI18N.Attrs.Order = order;
			}
			if (customDrawer != null)
			{
				cfgI18N.Attrs.CustomDrawer = customDrawer;
			}
			if (showRangeAsPercent.HasValue)
			{
				cfgI18N.Attrs.ShowRangeAsPercent = showRangeAsPercent.Value;
			}
			if (isAdvanced.HasValue)
			{
				cfgI18N.Attrs.IsAdvanced = isAdvanced.Value;
			}
			if (browsable.HasValue)
			{
				cfgI18N.Attrs.Browsable = browsable.Value;
			}
			return _config.Bind<T>(sectEN, keyEN, defaultValue, new ConfigDescription(descEN, acceptableValues, new object[1] { cfgI18N.Attrs }));
		}

		public ConfigEntry<string> BindReadOnly(string sectEN, string sectCN, string keyEN, string keyCN, string valueEN, string valueCN, int order = 0, Action<ConfigEntryBase> customDrawer = null)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			CfgI18N cfgI18N = NewI18N(sectEN, sectCN, keyEN, keyCN, "", "", readOnly: true);
			cfgI18N.ValueEN = valueEN;
			cfgI18N.ValueCN = valueCN;
			if (order != 0)
			{
				cfgI18N.Attrs.Order = order;
			}
			if (customDrawer != null)
			{
				cfgI18N.Attrs.CustomDrawer = customDrawer;
			}
			cfgI18N.ReadOnlyEntry = _config.Bind<string>(sectEN, keyEN, valueEN, new ConfigDescription("", (AcceptableValueBase)null, new object[1] { cfgI18N.Attrs }));
			string text = (UseChinese ? valueCN : valueEN);
			if (cfgI18N.ReadOnlyEntry.Value != text)
			{
				cfgI18N.ReadOnlyEntry.Value = text;
			}
			cfgI18N.ReadOnlyEntry.SettingChanged += MakeReadOnlyValueGuard(cfgI18N);
			return cfgI18N.ReadOnlyEntry;
		}

		private EventHandler MakeReadOnlyValueGuard(CfgI18N i18n)
		{
			return delegate
			{
				if (i18n.ReadOnlyEntry != null)
				{
					string text = (UseChinese ? i18n.ValueCN : i18n.ValueEN);
					if (i18n.ReadOnlyEntry.Value != text)
					{
						i18n.ReadOnlyEntry.Value = text;
						InvokeLanguageChanged();
					}
				}
			};
		}

		private void InvokeLanguageChanged()
		{
			Action action = this.LanguageChanged;
			if (action == null)
			{
				return;
			}
			Delegate[] invocationList = action.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action action2 = (Action)invocationList[i];
				try
				{
					action2();
				}
				catch (Exception ex)
				{
					ManualLogSource logger = _logger;
					if (logger != null)
					{
						logger.LogWarning((object)("LanguageChanged 订阅方异常:" + ex.Message));
					}
				}
			}
		}

		private CfgI18N NewI18N(string sectEN, string sectCN, string keyEN, string keyCN, string descEN, string descCN, bool readOnly)
		{
			CfgI18N cfgI18N = new CfgI18N
			{
				SectionEN = sectEN,
				SectionCN = sectCN,
				KeyEN = keyEN,
				KeyCN = keyCN,
				DescEN = descEN,
				DescCN = descCN,
				Attrs = (readOnly ? new ConfigurationManagerAttributes
				{
					ReadOnly = true,
					HideDefaultButton = true
				} : new ConfigurationManagerAttributes())
			};
			ApplyI18N(cfgI18N);
			_all.Add(cfgI18N);
			_bySection[sectEN] = cfgI18N;
			if (!_bySectionAndKey.TryGetValue(sectEN, out var value))
			{
				value = new Dictionary<string, CfgI18N>(StringComparer.Ordinal);
				_bySectionAndKey[sectEN] = value;
			}
			value[keyEN] = cfgI18N;
			return cfgI18N;
		}

		private void ApplyI18N(CfgI18N i18n)
		{
			bool useChinese = UseChinese;
			i18n.Attrs.Category = (useChinese ? i18n.SectionCN : i18n.SectionEN);
			i18n.Attrs.DispName = (useChinese ? i18n.KeyCN : i18n.KeyEN);
			i18n.Attrs.Description = (useChinese ? i18n.DescCN : i18n.DescEN);
			if (i18n.ReadOnlyEntry != null)
			{
				string text = (useChinese ? i18n.ValueCN : i18n.ValueEN);
				if (i18n.ReadOnlyEntry.Value != text)
				{
					i18n.ReadOnlyEntry.Value = text;
				}
			}
		}

		public void RefreshAttrs()
		{
			for (int i = 0; i < _all.Count; i++)
			{
				ApplyI18N(_all[i]);
			}
		}

		public void RefreshNow()
		{
			RefreshAttrs();
			InvokeLanguageChanged();
		}

		public bool TryGetBySection(string sectEN, out CfgI18N i18n)
		{
			return _bySection.TryGetValue(sectEN, out i18n);
		}

		public bool TryGetByKey(string keyEN, out CfgI18N i18n)
		{
			i18n = null;
			foreach (KeyValuePair<string, Dictionary<string, CfgI18N>> item in _bySectionAndKey)
			{
				if (item.Value.TryGetValue(keyEN, out i18n))
				{
					return true;
				}
			}
			return false;
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public bool? ShowRangeAsPercent;

		public Action<ConfigEntryBase> CustomDrawer;

		public bool? Browsable;

		public string Category;

		public object DefaultValue;

		public bool? HideDefaultButton;

		public bool? HideSettingName;

		public string Description;

		public string DispName;

		public int? Order;

		public bool? ReadOnly;

		public bool? IsAdvanced;
	}
	internal sealed class I18NSwitcher
	{
		private sealed class LiveLabel
		{
			public WeakReference<TextMeshProUGUI> Tmp;

			public I18NRegistry.CfgI18N I18N;

			public bool IsSection;
		}

		private sealed class LiveValue
		{
			public WeakReference<Object> InputField;

			public I18NRegistry.CfgI18N I18N;
		}

		private sealed class LiveToggleButtons
		{
			public WeakReference<TextMeshProUGUI> LeftTmp;

			public WeakReference<TextMeshProUGUI> RightTmp;

			public bool IsLanguage;
		}

		private readonly I18NRegistry _registry;

		private readonly Harmony _harmony;

		private readonly ManualLogSource _logger;

		private readonly string _pluginName;

		private readonly string _pluginNameFixedForm;

		private readonly List<LiveLabel> _liveLabels = new List<LiveLabel>(64);

		private readonly List<LiveValue> _liveValues = new List<LiveValue>(8);

		private readonly List<LiveToggleButtons> _liveToggleButtons = new List<LiveToggleButtons>(16);

		private static readonly Dictionary<Type, FieldInfo> _labelTmpFieldCache = new Dictionary<Type, FieldInfo>(8);

		private static readonly Dictionary<Type, FieldInfo> _inputStringSystemFieldCache = new Dictionary<Type, FieldInfo>(2);

		private static readonly Dictionary<Type, MethodInfo> _inputStringSystemSetValueCache = new Dictionary<Type, MethodInfo>(2);

		private static readonly Dictionary<Type, FieldInfo> _toggleLeftTmpCache = new Dictionary<Type, FieldInfo>(4);

		private static readonly Dictionary<Type, FieldInfo> _toggleRightTmpCache = new Dictionary<Type, FieldInfo>(4);

		private static readonly object _reflectionCacheLock = new object();

		private static readonly object _globalInstancesLock = new object();

		private static readonly List<WeakReference<I18NSwitcher>> _globalInstances = new List<WeakReference<I18NSwitcher>>(2);

		public int LiveUiCount => _liveLabels.Count + _liveValues.Count + _liveToggleButtons.Count;

		public I18NSwitcher(I18NRegistry registry, Harmony harmony, string pluginName, ManualLogSource logger = null)
		{
			_registry = registry ?? throw new ArgumentNullException("registry");
			_harmony = harmony ?? throw new ArgumentNullException("harmony");
			_pluginName = pluginName ?? throw new ArgumentNullException("pluginName");
			_pluginNameFixedForm = FixNamingForOwnership(pluginName);
			_logger = logger;
			_registry.LanguageChanged += RefreshLiveUi;
		}

		private static string FixNamingForOwnership(string input)
		{
			if (string.IsNullOrEmpty(input))
			{
				return input;
			}
			input = Regex.Replace(input, "([a-z])([A-Z])", "$1 $2");
			input = Regex.Replace(input, "([A-Z])([A-Z][a-z])", "$1 $2");
			input = Regex.Replace(input, "\\s+", " ");
			input = Regex.Replace(input, "([A-Z]\\.)\\s([A-Z]\\.)", "$1$2");
			return input.Trim();
		}

		public void InstallRepoConfigHook()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			try
			{
				Type type = AccessTools.TypeByName("MenuLib.MenuAPI");
				if (type == null)
				{
					ManualLogSource logger = _logger;
					if (logger != null)
					{
						logger.LogInfo((object)"MenuLib 未安装,跳过 REPOConfig 双语劫持。");
					}
					return;
				}
				HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(I18NSwitcher), "PostfixDispatch", (Type[])null, (Type[])null))
				{
					priority = 0
				};
				HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal) { "CreateREPOLabel", "CreateREPOToggle", "CreateREPOSlider", "CreateREPOInputField", "CreateREPOButton" };
				int num = 0;
				foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
				{
					if (!hashSet.Contains(declaredMethod.Name))
					{
						continue;
					}
					ParameterInfo[] parameters = declaredMethod.GetParameters();
					if (parameters.Length != 0 && !(parameters[0].ParameterType != typeof(string)))
					{
						try
						{
							_harmony.Patch((MethodBase)declaredMethod, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
							num++;
						}
						catch
						{
						}
					}
				}
				if (num > 0)
				{
					ManualLogSource logger2 = _logger;
					if (logger2 != null)
					{
						logger2.LogInfo((object)$"REPOConfig 双语显示已挂载({num} 个 UI 创建方法)。");
					}
				}
				else
				{
					ManualLogSource logger3 = _logger;
					if (logger3 != null)
					{
						logger3.LogInfo((object)"REPOConfig 没找到可劫持的 UI 方法。");
					}
				}
				RegisterGlobalInstance(this);
			}
			catch (Exception ex)
			{
				ManualLogSource logger4 = _logger;
				if (logger4 != null)
				{
					logger4.LogInfo((object)("REPOConfig 劫持失败:" + ex.Message));
				}
			}
		}

		private static void RegisterGlobalInstance(I18NSwitcher inst)
		{
			lock (_globalInstancesLock)
			{
				_globalInstances.Add(new WeakReference<I18NSwitcher>(inst));
			}
		}

		public static void PostfixDispatch(object[] __args, object __result)
		{
			try
			{
				if (__args == null || __args.Length == 0 || __result == null || !(__args[0] is string text) || string.IsNullOrEmpty(text))
				{
					return;
				}
				List<I18NSwitcher> list = null;
				lock (_globalInstancesLock)
				{
					for (int num = _globalInstances.Count - 1; num >= 0; num--)
					{
						if (!_globalInstances[num].TryGetTarget(out var target))
						{
							_globalInstances.RemoveAt(num);
						}
						else
						{
							(list ?? (list = new List<I18NSwitcher>(_globalInstances.Count))).Add(target);
						}
					}
				}
				if (list != null)
				{
					for (int i = 0; i < list.Count; i++)
					{
						list[i].TryRegisterLiveLabel(text, __result);
					}
				}
			}
			catch
			{
			}
		}

		private static string GetOwningPluginName(object uiElement)
		{
			try
			{
				object obj = ((uiElement is Component) ? uiElement : null);
				GameObject val = (GameObject)(((obj != null) ? ((Component)obj).gameObject : null) ?? ((uiElement is GameObject) ? uiElement : null));
				if ((Object)(object)val == (Object)null)
				{
					return null;
				}
				Transform val2 = val.transform;
				while ((Object)(object)val2 != (Object)null)
				{
					string name = ((Object)((Component)val2).gameObject).name;
					if (name != null && name.StartsWith("Menu Page ", StringComparison.Ordinal))
					{
						return name.Substring("Menu Page ".Length);
					}
					val2 = val2.parent;
				}
			}
			catch
			{
			}
			return null;
		}

		private void TryRegisterLiveLabel(string label, object uiElement)
		{
			string owningPluginName = GetOwningPluginName(uiElement);
			if ((owningPluginName != null && !string.Equals(owningPluginName, _pluginNameFixedForm, StringComparison.Ordinal)) || (!_registry.TryGetBySection(label, out var i18n) && !_registry.TryGetByKey(label, out i18n)))
			{
				return;
			}
			bool flag = string.Equals(label, i18n.SectionEN, StringComparison.Ordinal);
			TextMeshProUGUI labelTmp = GetLabelTmp(uiElement);
			if ((Object)(object)labelTmp == (Object)null)
			{
				return;
			}
			bool useChinese = _registry.UseChinese;
			string text = ((!flag) ? (useChinese ? i18n.KeyCN : i18n.KeyEN) : (useChinese ? i18n.SectionCN : i18n.SectionEN));
			if (!string.IsNullOrEmpty(text))
			{
				try
				{
					((TMP_Text)labelTmp).text = text;
				}
				catch
				{
				}
			}
			_liveLabels.Add(new LiveLabel
			{
				Tmp = new WeakReference<TextMeshProUGUI>(labelTmp),
				I18N = i18n,
				IsSection = flag
			});
			if (!flag && i18n.ReadOnlyEntry != null)
			{
				Object val = (Object)((uiElement is Object) ? uiElement : null);
				if (val != null && HasInputStringSystemField(((object)val).GetType()))
				{
					_liveValues.Add(new LiveValue
					{
						InputField = new WeakReference<Object>(val),
						I18N = i18n
					});
					string text2 = (useChinese ? i18n.ValueCN : i18n.ValueEN);
					if (i18n.ReadOnlyEntry.Value != text2)
					{
						i18n.ReadOnlyEntry.Value = text2;
					}
					TrySetInputFieldValue(val, text2);
				}
			}
			if (flag)
			{
				return;
			}
			Type type = uiElement.GetType();
			FieldInfo value;
			FieldInfo value2;
			lock (_reflectionCacheLock)
			{
				if (!_toggleLeftTmpCache.TryGetValue(type, out value))
				{
					value = AccessTools.Field(type, "leftButtonTMP");
					_toggleLeftTmpCache[type] = value;
				}
				if (!_toggleRightTmpCache.TryGetValue(type, out value2))
				{
					value2 = AccessTools.Field(type, "rightButtonTMP");
					_toggleRightTmpCache[type] = value2;
				}
			}
			if (value != null && value2 != null)
			{
				object? value3 = value.GetValue(uiElement);
				TextMeshProUGUI val2 = (TextMeshProUGUI)((value3 is TextMeshProUGUI) ? value3 : null);
				object? value4 = value2.GetValue(uiElement);
				TextMeshProUGUI val3 = (TextMeshProUGUI)((value4 is TextMeshProUGUI) ? value4 : null);
				if ((Object)(object)val2 != (Object)null && (Object)(object)val3 != (Object)null)
				{
					_liveToggleButtons.Add(new LiveToggleButtons
					{
						LeftTmp = new WeakReference<TextMeshProUGUI>(val2),
						RightTmp = new WeakReference<TextMeshProUGUI>(val3),
						IsLanguage = false
					});
					ApplyToggleButtonText(val2, val3, useChinese, isLanguage: false);
				}
			}
		}

		private static TextMeshProUGUI GetLabelTmp(object uiElement)
		{
			try
			{
				Type type = uiElement.GetType();
				FieldInfo value;
				lock (_reflectionCacheLock)
				{
					if (!_labelTmpFieldCache.TryGetValue(type, out value))
					{
						value = AccessTools.Field(type, "labelTMP");
						_labelTmpFieldCache[type] = value;
					}
				}
				object? obj = value?.GetValue(uiElement);
				return (TextMeshProUGUI)((obj is TextMeshProUGUI) ? obj : null);
			}
			catch
			{
				return null;
			}
		}

		private static bool HasInputStringSystemField(Type t)
		{
			lock (_reflectionCacheLock)
			{
				if (!_inputStringSystemFieldCache.TryGetValue(t, out var value))
				{
					value = AccessTools.Field(t, "inputStringSystem");
					_inputStringSystemFieldCache[t] = value;
				}
				return value != null;
			}
		}

		private static bool TrySetInputFieldValue(Object ifObj, string newVal)
		{
			try
			{
				Type type = ((object)ifObj).GetType();
				FieldInfo value;
				lock (_reflectionCacheLock)
				{
					if (!_inputStringSystemFieldCache.TryGetValue(type, out value))
					{
						value = AccessTools.Field(type, "inputStringSystem");
						_inputStringSystemFieldCache[type] = value;
					}
				}
				if (value == null)
				{
					return false;
				}
				object value2 = value.GetValue(ifObj);
				if (value2 == null)
				{
					return false;
				}
				MethodInfo value3;
				lock (_reflectionCacheLock)
				{
					Type type2 = value2.GetType();
					if (!_inputStringSystemSetValueCache.TryGetValue(type2, out value3))
					{
						value3 = AccessTools.Method(type2, "SetValue", new Type[2]
						{
							typeof(string),
							typeof(bool)
						}, (Type[])null);
						_inputStringSystemSetValueCache[type2] = value3;
					}
				}
				if (value3 == null)
				{
					return false;
				}
				value3.Invoke(value2, new object[2]
				{
					newVal ?? string.Empty,
					false
				});
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static void ApplyToggleButtonText(TextMeshProUGUI leftTmp, TextMeshProUGUI rightTmp, bool cn, bool isLanguage)
		{
			try
			{
				if (isLanguage)
				{
					((TMP_Text)leftTmp).text = (cn ? "中文" : "Chinese");
					((TMP_Text)rightTmp).text = (cn ? "英文" : "English");
				}
				else
				{
					((TMP_Text)leftTmp).text = (cn ? "开" : "ON");
					((TMP_Text)rightTmp).text = (cn ? "关" : "OFF");
				}
			}
			catch
			{
			}
		}

		private void RefreshLiveUi()
		{
			bool useChinese = _registry.UseChinese;
			for (int num = _liveLabels.Count - 1; num >= 0; num--)
			{
				LiveLabel liveLabel = _liveLabels[num];
				if (!liveLabel.Tmp.TryGetTarget(out var target) || (Object)(object)target == (Object)null || (Object)(object)((Component)target).gameObject == (Object)null)
				{
					_liveLabels.RemoveAt(num);
				}
				else
				{
					string text = ((!liveLabel.IsSection) ? (useChinese ? liveLabel.I18N.KeyCN : liveLabel.I18N.KeyEN) : (useChinese ? liveLabel.I18N.SectionCN : liveLabel.I18N.SectionEN));
					try
					{
						((TMP_Text)target).text = text;
					}
					catch
					{
						_liveLabels.RemoveAt(num);
					}
				}
			}
			for (int num2 = _liveValues.Count - 1; num2 >= 0; num2--)
			{
				LiveValue liveValue = _liveValues[num2];
				if (!liveValue.InputField.TryGetTarget(out var target2) || target2 == (Object)null)
				{
					_liveValues.RemoveAt(num2);
				}
				else
				{
					string newVal = (useChinese ? liveValue.I18N.ValueCN : liveValue.I18N.ValueEN);
					if (!TrySetInputFieldValue(target2, newVal))
					{
						_liveValues.RemoveAt(num2);
					}
				}
			}
			for (int num3 = _liveToggleButtons.Count - 1; num3 >= 0; num3--)
			{
				LiveToggleButtons liveToggleButtons = _liveToggleButtons[num3];
				if (!liveToggleButtons.LeftTmp.TryGetTarget(out var target3) || (Object)(object)target3 == (Object)null || !liveToggleButtons.RightTmp.TryGetTarget(out var target4) || (Object)(object)target4 == (Object)null)
				{
					_liveToggleButtons.RemoveAt(num3);
				}
				else
				{
					ApplyToggleButtonText(target3, target4, useChinese, liveToggleButtons.IsLanguage);
				}
			}
		}
	}
}