Decompiled source of ChineseInputPlus v2.1.0

BepInEx/plugins/ChineseInputPlus/ChineseInputPlus.dll

Decompiled 9 hours ago
using System;
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 Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ChineseInputPlus
{
	internal static class ImeState
	{
		private static Type _keyboardType;

		private static PropertyInfo _currentProperty;

		private static MethodInfo _setImeEnabled;

		private static object _keyboard;

		private static bool? _lastState;

		private static FieldInfo _chatActiveField;

		private static FieldInfo _chatStateField;

		private static FieldInfo _chatInstanceField;

		public static void Init()
		{
			try
			{
				_keyboardType = Type.GetType("UnityEngine.InputSystem.Keyboard, Unity.InputSystem");
				if (_keyboardType != null)
				{
					_currentProperty = _keyboardType.GetProperty("current", BindingFlags.Static | BindingFlags.Public);
					_setImeEnabled = _keyboardType.GetMethod("SetIMEEnabled", BindingFlags.Instance | BindingFlags.Public);
				}
			}
			catch (Exception ex)
			{
				ChineseInputPlugin.Log.LogDebug((object)("[中文输入] 初始化 InputSystem IME 失败:" + ex.Message));
			}
			try
			{
				Type typeFromHandle = typeof(ChatManager);
				_chatActiveField = typeFromHandle.GetField("chatActive", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_chatStateField = typeFromHandle.GetField("chatState", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_chatInstanceField = typeFromHandle.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			}
			catch (Exception ex2)
			{
				ChineseInputPlugin.Log.LogDebug((object)("[中文输入] 初始化聊天状态失败:" + ex2.Message));
			}
		}

		public static bool ChatActive()
		{
			try
			{
				ChatManager val = (ChatManager)((_chatInstanceField != null) ? ((object)/*isinst with value type is only supported in some contexts*/) : ((object)ChatManager.instance));
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				bool flag = default(bool);
				int num;
				if (_chatActiveField != null)
				{
					object value = _chatActiveField.GetValue(val);
					if (value is bool)
					{
						flag = (bool)value;
						num = 1;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0)
				{
					return true;
				}
				if (_chatStateField != null)
				{
					object value2 = _chatStateField.GetValue(val);
					if (value2 != null && value2.ToString() == "Active")
					{
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		public static void Tick(bool menuInputActive)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if (ChineseInputPlugin.Cfg == null)
			{
				return;
			}
			bool flag = ChineseInputPlugin.Cfg.ChatImeSupport.Value && ChatActive();
			bool flag2 = menuInputActive || flag;
			try
			{
				IMECompositionMode val = (IMECompositionMode)(flag2 ? 1 : 0);
				if (Input.imeCompositionMode != val)
				{
					Input.imeCompositionMode = val;
				}
			}
			catch
			{
			}
			ApplyInputSystemIme(flag2);
		}

		private static void ApplyInputSystemIme(bool enabled)
		{
			if (_setImeEnabled == null || _currentProperty == null)
			{
				return;
			}
			try
			{
				if (_keyboard == null || _lastState != enabled)
				{
					_keyboard = _currentProperty.GetValue(null);
				}
				if (_keyboard != null && _lastState != enabled)
				{
					_setImeEnabled.Invoke(_keyboard, new object[1] { enabled });
					_lastState = enabled;
				}
			}
			catch (Exception ex)
			{
				ChineseInputPlugin.Log.LogDebug((object)("[中文输入] 切换 InputSystem IME 失败:" + ex.Message));
			}
		}
	}
	internal static class MenuInputHandler
	{
		private static readonly Dictionary<int, string> BaseTexts = new Dictionary<int, string>();

		private static readonly Dictionary<int, bool> Composing = new Dictionary<int, bool>();

		public static bool TryHandle(MenuTextInput input)
		{
			if ((Object)(object)input == (Object)null || !ChineseInputPlugin.Cfg.Enabled.Value)
			{
				return false;
			}
			MenuManager instance = MenuManager.instance;
			if ((Object)(object)instance == (Object)null || !IsTextInputActive(instance))
			{
				return false;
			}
			try
			{
				instance.TextInputActive();
			}
			catch
			{
			}
			if (ChineseInputPlugin.Cfg.RemoveUpperCase.Value && MenuFields.UpperOnly != null)
			{
				MenuFields.UpperOnly.SetValue(input, false);
			}
			if (ChineseInputPlugin.Cfg.ImeComposition.Value)
			{
				Input.imeCompositionMode = (IMECompositionMode)1;
				MoveCompositionCursor(input);
				string compositionString = Input.compositionString;
				int instanceID = ((Object)input).GetInstanceID();
				if (!string.IsNullOrEmpty(compositionString))
				{
					if (!Composing.TryGetValue(instanceID, out var value) || !value)
					{
						BaseTexts[instanceID] = CurrentText(input);
						Composing[instanceID] = true;
					}
					string text = Clamp(BaseTexts[instanceID] + compositionString, MaxLength(input));
					SetText(input, text);
					return true;
				}
				if (Composing.TryGetValue(instanceID, out var value2) && value2)
				{
					Composing[instanceID] = false;
					SetText(input, BaseTexts[instanceID] ?? string.Empty);
					return false;
				}
			}
			if (ChineseInputPlugin.Cfg.EnablePaste.Value && PastePressed())
			{
				string text2 = ClipboardText();
				if (!string.IsNullOrEmpty(text2))
				{
					string text3 = Clamp(CurrentText(input) + text2, MaxLength(input));
					SetText(input, text3);
					return true;
				}
			}
			return false;
		}

		public static bool IsMenuInputActive()
		{
			if (ChineseInputPlugin.Cfg == null || !ChineseInputPlugin.Cfg.Enabled.Value)
			{
				return false;
			}
			MenuManager instance = MenuManager.instance;
			return (Object)(object)instance != (Object)null && IsTextInputActive(instance);
		}

		private static bool IsTextInputActive(MenuManager manager)
		{
			try
			{
				int result;
				if (!(MenuFields.TextInputActive == null))
				{
					object value = MenuFields.TextInputActive.GetValue(manager);
					bool flag = default(bool);
					int num;
					if (value is bool)
					{
						flag = (bool)value;
						num = 1;
					}
					else
					{
						num = 0;
					}
					result = num & (flag ? 1 : 0);
				}
				else
				{
					result = 1;
				}
				return (byte)result != 0;
			}
			catch
			{
				return true;
			}
		}

		private static void MoveCompositionCursor(MenuTextInput input)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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)
			try
			{
				object? obj = MenuFields.TextMain?.GetValue(input);
				Component val = (Component)((obj is Component) ? obj : null);
				if (!((Object)(object)val == (Object)null))
				{
					Vector3 position = val.transform.position;
					Input.compositionCursorPos = new Vector2(position.x, (float)Screen.height - position.y);
				}
			}
			catch
			{
			}
		}

		private static string CurrentText(MenuTextInput input)
		{
			try
			{
				return (MenuFields.TextCurrent?.GetValue(input) as string) ?? string.Empty;
			}
			catch
			{
				return string.Empty;
			}
		}

		private static int MaxLength(MenuTextInput input)
		{
			try
			{
				return (MenuFields.MaxLength?.GetValue(input) is int num && num > 0) ? num : 0;
			}
			catch
			{
				return 0;
			}
		}

		private static void SetText(MenuTextInput input, string text)
		{
			try
			{
				MenuFields.TextCurrent?.SetValue(input, text);
				MenuFields.InputTextSet?.Invoke(input, null);
			}
			catch (Exception ex)
			{
				ChineseInputPlugin.Log.LogDebug((object)("[中文输入] 写入失败:" + ex.Message));
			}
		}

		private static string Clamp(string text, int maxLength)
		{
			if (string.IsNullOrEmpty(text))
			{
				return string.Empty;
			}
			text = text.Replace("\r", string.Empty).Replace("\n", string.Empty).Replace("\t", string.Empty);
			if (maxLength > 0 && text.Length > maxLength)
			{
				text = text.Substring(0, maxLength);
			}
			return text;
		}

		private static bool PastePressed()
		{
			return (Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305) || Input.GetKey((KeyCode)310)) && Input.GetKeyDown((KeyCode)118);
		}

		private static string ClipboardText()
		{
			try
			{
				string systemCopyBuffer = GUIUtility.systemCopyBuffer;
				if (string.IsNullOrEmpty(systemCopyBuffer))
				{
					return string.Empty;
				}
				StringBuilder stringBuilder = new StringBuilder(systemCopyBuffer.Length);
				string text = systemCopyBuffer;
				foreach (char c in text)
				{
					if (!char.IsControl(c))
					{
						stringBuilder.Append(c);
					}
				}
				return stringBuilder.ToString().Trim();
			}
			catch
			{
				return string.Empty;
			}
		}
	}
	internal static class MenuFields
	{
		private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		public static readonly FieldInfo TextCurrent = typeof(MenuTextInput).GetField("textCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		public static readonly FieldInfo UpperOnly = typeof(MenuTextInput).GetField("upperOnly", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		public static readonly FieldInfo MaxLength = typeof(MenuTextInput).GetField("maxLength", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		public static readonly FieldInfo TextMain = typeof(MenuTextInput).GetField("textMain", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		public static readonly MethodInfo InputTextSet = typeof(MenuTextInput).GetMethod("InputTextSet", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		public static readonly FieldInfo TextInputActive = typeof(MenuManager).GetField("textInputActive", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		public static readonly FieldInfo Instance = typeof(MenuManager).GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

		public static void LogAvailability()
		{
			ChineseInputPlugin.Log.LogInfo((object)("[中文输入] 字段探测:textCurrent=" + Mark(TextCurrent) + ", upperOnly=" + Mark(UpperOnly) + ", maxLength=" + Mark(MaxLength) + ", textMain=" + Mark(TextMain) + ", InputTextSet=" + Mark(InputTextSet) + ", textInputActive=" + Mark(TextInputActive)));
		}

		private static string Mark(MemberInfo member)
		{
			return (member == null) ? "缺失" : "OK";
		}
	}
	[BepInPlugin("xingKong.ChineseInputPlus", "REPO Chinese Input Plus", "2.1.0")]
	[BepInProcess("REPO.exe")]
	public class ChineseInputPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "xingKong.ChineseInputPlus";

		public const string PluginName = "REPO Chinese Input Plus";

		public const string PluginVersion = "2.1.0";

		internal static ManualLogSource Log;

		internal static PluginConfig Cfg;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = new PluginConfig(((BaseUnityPlugin)this).Config);
			MenuFields.LogAvailability();
			ImeState.Init();
			LogChatCapability();
			_harmony = new Harmony("xingKong.ChineseInputPlus");
			try
			{
				_harmony.PatchAll(typeof(MenuTextInputPatches));
				Log.LogInfo((object)"[中文输入] 已挂接 MenuTextInput.Start / Update。");
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"[中文输入] 挂接失败:{arg}");
			}
			Log.LogInfo((object)"REPO Chinese Input Plus v2.1.0 已加载(0.5.1 适配版,菜单输入框)。");
		}

		private void Update()
		{
			bool menuInputActive = MenuInputHandler.IsMenuInputActive();
			ImeState.Tick(menuInputActive);
		}

		private void OnDestroy()
		{
			try
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch
			{
			}
		}

		private void LogChatCapability()
		{
			try
			{
				Type typeFromHandle = typeof(ChatManager);
				bool flag = typeFromHandle.GetMethod("OnCompositionChange", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null;
				bool flag2 = typeFromHandle.GetMethod("ChatPasteLogic", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null;
				Log.LogInfo((object)$"[中文输入] 聊天框原生支持:IME={flag}, 粘贴={flag2};插件会在聊天激活时强制开启 IME。");
			}
			catch (Exception ex)
			{
				Log.LogDebug((object)("[中文输入] 检测聊天能力失败:" + ex.Message));
			}
		}
	}
	internal static class MenuTextInputPatches
	{
		[HarmonyPatch(typeof(MenuTextInput), "Start")]
		[HarmonyPostfix]
		private static void StartPostfix(MenuTextInput __instance)
		{
			if (ChineseInputPlugin.Cfg != null && ChineseInputPlugin.Cfg.RemoveUpperCase.Value && MenuFields.UpperOnly != null)
			{
				MenuFields.UpperOnly.SetValue(__instance, false);
			}
		}

		[HarmonyPatch(typeof(MenuTextInput), "Update")]
		[HarmonyPrefix]
		private static bool UpdatePrefix(MenuTextInput __instance)
		{
			return !MenuInputHandler.TryHandle(__instance);
		}
	}
	internal sealed class PluginConfig
	{
		public readonly ConfigEntry<bool> Enabled;

		public readonly ConfigEntry<bool> ImeComposition;

		public readonly ConfigEntry<bool> EnablePaste;

		public readonly ConfigEntry<bool> RemoveUpperCase;

		public readonly ConfigEntry<bool> ChatImeSupport;

		public PluginConfig(ConfigFile cfg)
		{
			Enabled = cfg.Bind<bool>("1. 通用", "启用", true, "总开关。只影响菜单输入框(房间名/玩家名/存档名/密码)。");
			ImeComposition = cfg.Bind<bool>("2. 输入", "启用输入法组合", true, "支持拼音/日文/韩文输入法的组合显示与提交。关闭后只能粘贴中文。");
			EnablePaste = cfg.Bind<bool>("2. 输入", "Ctrl+V 粘贴", true, "在菜单输入框里粘贴剪贴板文本(自动去掉换行与控制字符,并遵守长度上限)。");
			RemoveUpperCase = cfg.Bind<bool>("2. 输入", "去掉强制大写", true, "菜单输入框原本会把字母强制转大写,开启后保留原始大小写,也才能输入汉字。");
			ChatImeSupport = cfg.Bind<bool>("2. 输入", "聊天框输入法支持", true, "聊天框激活时强制开启 IME,支持中文、日文、韩文等任意输入法语言的组合与提交(0.5.1 原生只在启动时开一次,之后聊天框拿不到 IME)。");
		}
	}
}