Decompiled source of ResoniteBetterIMESupport v3.0.2

Renderer/BepInEx/plugins/blhsrwznrghfzpr-ResoniteBetterIMESupport/ResoniteBetterIMESupport.Renderer.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using InterprocessLib;
using Microsoft.CodeAnalysis;
using Renderite.Shared;
using ResoniteBetterIMESupport.Shared;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.LowLevel;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("blhsrwznrghfzpr")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("3.0.2.0")]
[assembly: AssemblyInformationalVersion("3.0.2+bb501d764477df30bdab478a08569af8258d00e0")]
[assembly: AssemblyProduct("ResoniteBetterIMESupport.Renderer")]
[assembly: AssemblyTitle("ResoniteBetterIMESupport.Renderer")]
[assembly: AssemblyVersion("3.0.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 ResoniteBetterIMESupport.Shared
{
	internal static class ImeKeys
	{
		public static readonly Key[] TextEditorEditingKeys;

		public static readonly Key[] RendererEditingKeys;

		public static readonly Key[] CaretKeys;

		static ImeKeys()
		{
			Key[] array = new Key[8];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			TextEditorEditingKeys = (Key[])(object)array;
			Key[] array2 = new Key[10];
			RuntimeHelpers.InitializeArray(array2, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			RendererEditingKeys = (Key[])(object)array2;
			Key[] array3 = new Key[4];
			RuntimeHelpers.InitializeArray(array3, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			CaretKeys = (Key[])(object)array3;
		}
	}
	internal static class ImeInterprocessChannel
	{
		public const string OwnerId = "ResoniteBetterIMESupport";

		public const string MessageId = "ImeComposition";
	}
	internal enum ImeEditAction
	{
		None,
		Backspace,
		Delete
	}
	internal sealed class ImeInterprocessMessage : IMemoryPackable
	{
		public string Composition = string.Empty;

		public string CommittedText = string.Empty;

		public int CaretOffset = -1;

		public ImeEditAction EditAction;

		public void Pack(ref MemoryPacker packer)
		{
			((MemoryPacker)(ref packer)).Write(Composition);
			((MemoryPacker)(ref packer)).Write(CommittedText);
			((MemoryPacker)(ref packer)).Write<int>(CaretOffset);
			((MemoryPacker)(ref packer)).Write<int>((int)EditAction);
		}

		public void Unpack(ref MemoryUnpacker unpacker)
		{
			((MemoryUnpacker)(ref unpacker)).Read(ref Composition);
			((MemoryUnpacker)(ref unpacker)).Read(ref CommittedText);
			((MemoryUnpacker)(ref unpacker)).Read<int>(ref CaretOffset);
			int num = 0;
			((MemoryUnpacker)(ref unpacker)).Read<int>(ref num);
			EditAction = (Enum.IsDefined(typeof(ImeEditAction), num) ? ((ImeEditAction)num) : ImeEditAction.None);
		}

		public override string ToString()
		{
			return $"ImeInterprocessMessage:CompositionLength={Composition.Length},CommittedLength={CommittedText.Length},CaretOffset={CaretOffset},EditAction={EditAction}";
		}
	}
	internal static class ImeInterprocessQueue
	{
		public static string GetQueueName()
		{
			if (TryGetArgumentValue("--bepinex-target", out string value) && !string.IsNullOrWhiteSpace(value))
			{
				return "ResoniteBetterIMESupport." + HashForQueue(value) + "-ResoniteBetterIMESupport";
			}
			if (TryGetArgumentValue("-shmprefix", out string value2) && !string.IsNullOrWhiteSpace(value2))
			{
				return value2 + "-ResoniteBetterIMESupport";
			}
			if (TryGetArgumentValue("-QueueName", out string value3) && !string.IsNullOrWhiteSpace(value3))
			{
				int num = value3.IndexOf('_');
				return ((num > 0) ? value3.Substring(0, num) : value3) + "-ResoniteBetterIMESupport";
			}
			return "ResoniteBetterIMESupport.Fallback-ResoniteBetterIMESupport";
		}

		private static bool TryGetArgumentValue(string argumentName, out string value)
		{
			string[] commandLineArgs = Environment.GetCommandLineArgs();
			for (int i = 0; i < commandLineArgs.Length - 1; i++)
			{
				if (string.Equals(commandLineArgs[i], argumentName, StringComparison.InvariantCultureIgnoreCase))
				{
					value = commandLineArgs[i + 1];
					return true;
				}
			}
			value = string.Empty;
			return false;
		}

		private static string HashForQueue(string value)
		{
			string s = value.Trim().Replace('/', '\\').ToLowerInvariant();
			using SHA256 sHA = SHA256.Create();
			byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(s));
			StringBuilder stringBuilder = new StringBuilder(16);
			for (int i = 0; i < 8 && i < array.Length; i++)
			{
				stringBuilder.Append(array[i].ToString("X2"));
			}
			return stringBuilder.ToString();
		}
	}
}
namespace ResoniteBetterIMESupport.Renderer
{
	internal static class KeyboardDriverIMEPatch
	{
		public sealed class DriverState
		{
			public string ImeComposition = string.Empty;

			public int CompositionCaretOffset = -1;

			public int LastUpdateTypeDeltaLength = -1;

			public long SuppressEmptyCompositionEndUntilTimestamp;

			public bool KeyboardInputActive;

			public bool IgnoreNextEmptyCompositionCommit;

			public Action<IMECompositionString>? CompositionHandler;

			public readonly HashSet<Key> PreviousHeldIMEEditingKeys = new HashSet<Key>();
		}

		private static readonly ConditionalWeakTable<object, DriverState> States = new ConditionalWeakTable<object, DriverState>();

		private static readonly HashSet<object> ActiveDrivers = new HashSet<object>();

		private static FieldInfo? _typeDeltaField;

		private static bool _inputUpdateHooked;

		private static bool _messengerIdentityLogged;

		private static Messenger? _messenger;

		public static Type KeyboardDriverType => AccessTools.TypeByName("KeyboardDriver") ?? throw new InvalidOperationException("KeyboardDriver type was not found.");

		public static FieldInfo TypeDeltaField
		{
			get
			{
				object obj = _typeDeltaField;
				if (obj == null)
				{
					obj = AccessTools.Field(KeyboardDriverType, "typeDelta") ?? throw new InvalidOperationException("KeyboardDriver.typeDelta field was not found.");
					_typeDeltaField = (FieldInfo?)obj;
				}
				return (FieldInfo)obj;
			}
		}

		public static DriverState GetState(object driver)
		{
			return States.GetOrCreateValue(driver);
		}

		public static StringBuilder? GetTypeDelta(object driver)
		{
			return (StringBuilder)TypeDeltaField.GetValue(driver);
		}

		public static void InitializeMessaging()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			if (_messenger == null)
			{
				string queueName = ImeInterprocessQueue.GetQueueName();
				_messenger = new Messenger("ResoniteBetterIMESupport", false, queueName, (IMemoryPackerEntityPool)null, 1048576L);
				LogMessengerIdentityOnce();
			}
		}

		public static void DisposeMessaging()
		{
			Messenger? messenger = _messenger;
			if (messenger != null)
			{
				messenger.Dispose();
			}
			_messenger = null;
		}

		public static void Subscribe(object driver)
		{
			object driver2 = driver;
			Keyboard current = Keyboard.current;
			if (current == null)
			{
				RendererPlugin.Logger.LogWarning((object)"Keyboard.current is null. IME composition support was not attached.");
				return;
			}
			DriverState state = GetState(driver2);
			if (state.CompositionHandler == null)
			{
				InitializeMessaging();
				state.CompositionHandler = delegate(IMECompositionString composition)
				{
					//IL_0006: Unknown result type (might be due to invalid IL or missing references)
					OnIMECompositionChange(driver2, composition);
				};
				current.onIMECompositionChange += state.CompositionHandler;
				ActiveDrivers.Add(driver2);
				HookInputUpdate();
			}
		}

		private static void HookInputUpdate()
		{
			if (!_inputUpdateHooked)
			{
				InputSystem.onAfterUpdate += OnAfterInputUpdate;
				_inputUpdateHooked = true;
			}
		}

		private static void LogMessengerIdentityOnce()
		{
			if (!_messengerIdentityLogged)
			{
				_messengerIdentityLogged = true;
				DebugLog("Renderer IME InterprocessLib sender: ownerId=\"ResoniteBetterIMESupport\", messageId=\"ImeComposition\", queueName=\"" + ImeInterprocessQueue.GetQueueName() + "\"");
			}
		}

		public static void Unsubscribe(object driver)
		{
			DriverState state = GetState(driver);
			if (state.CompositionHandler != null)
			{
				Keyboard current = Keyboard.current;
				if (current != null)
				{
					current.onIMECompositionChange -= state.CompositionHandler;
				}
				ActiveDrivers.Remove(driver);
				state.CompositionHandler = null;
				state.ImeComposition = string.Empty;
				state.CompositionCaretOffset = -1;
				state.SuppressEmptyCompositionEndUntilTimestamp = 0L;
				state.PreviousHeldIMEEditingKeys.Clear();
			}
		}

		public static void ClearComposition(object driver)
		{
			DriverState state = GetState(driver);
			state.ImeComposition = string.Empty;
			state.CompositionCaretOffset = -1;
			state.SuppressEmptyCompositionEndUntilTimestamp = 0L;
			state.PreviousHeldIMEEditingKeys.Clear();
		}

		public static void HandleKeyboardInputActive(object driver, bool keyboardInputActive)
		{
			DriverState state = GetState(driver);
			bool keyboardInputActive2 = state.KeyboardInputActive;
			state.KeyboardInputActive = keyboardInputActive;
			if (keyboardInputActive)
			{
				state.IgnoreNextEmptyCompositionCommit = false;
			}
			else if (keyboardInputActive2)
			{
				CancelCompositionForInactiveKeyboard(driver);
			}
		}

		public static void CancelCompositionForInactiveKeyboard(object driver)
		{
			DriverState state = GetState(driver);
			if (state.ImeComposition.Length == 0)
			{
				ClearComposition(driver);
				return;
			}
			DebugLog($"CancelCompositionForInactiveKeyboard: composition=\"{EscapeForLog(state.ImeComposition)}\", caretOffset={state.CompositionCaretOffset}");
			if (!TrySendComposition(string.Empty, string.Empty, -1))
			{
				DebugLog("CancelCompositionForInactiveKeyboard InterprocessLib send failed.");
			}
			ClearPendingTypeDelta(driver);
			ClearComposition(driver);
			state.IgnoreNextEmptyCompositionCommit = true;
		}

		public static bool HasComposition(object driver)
		{
			return !string.IsNullOrEmpty(GetState(driver).ImeComposition);
		}

		private static void OnAfterInputUpdate()
		{
			Keyboard current = Keyboard.current;
			if (current == null || ActiveDrivers.Count == 0)
			{
				return;
			}
			foreach (object activeDriver in ActiveDrivers)
			{
				SynchronizeCompositionCaret(activeDriver, current);
			}
		}

		private static void OnIMECompositionChange(object driver, IMECompositionString composition)
		{
			string text = ((object)(IMECompositionString)(ref composition)).ToString();
			DriverState state = GetState(driver);
			StringBuilder typeDelta = GetTypeDelta(driver);
			string text2 = typeDelta?.ToString() ?? string.Empty;
			bool flag = IsCompositionBackspaceKeyActive();
			bool flag2 = IsCompositionDeleteKeyActive();
			ImeEditAction imeEditAction = GetImeEditAction(flag, flag2);
			DebugLog($"OnIMECompositionChange begin: composition=\"{EscapeForLog(text)}\", committed=\"{EscapeForLog(text2)}\", previousComposition=\"{EscapeForLog(state.ImeComposition)}\", caretOffset={state.CompositionCaretOffset}, typeDeltaLength={typeDelta?.Length ?? (-1)}, backspaceKeyActive={flag}, deleteKeyActive={flag2}");
			if (text.Length == 0 && state.IgnoreNextEmptyCompositionCommit)
			{
				DebugLog("OnIMECompositionChange ignoring empty composition commit after keyboard focus loss: committed=\"" + EscapeForLog(text2) + "\"");
				state.IgnoreNextEmptyCompositionCommit = false;
				ClearPendingTypeDelta(driver);
				return;
			}
			if (text.Length > 0)
			{
				state.IgnoreNextEmptyCompositionCommit = false;
			}
			if (text.Length == 0 && state.ImeComposition.Length > 0 && IsLikelyFocusLossAccumulatedCommit(text2, state.ImeComposition))
			{
				DebugLog($"OnIMECompositionChange treating accumulated focus-loss TypeDelta as cancel: committedLength={text2.Length}, previousComposition=\"{EscapeForLog(state.ImeComposition)}\"");
				if (!TrySendComposition(string.Empty, string.Empty, -1))
				{
					DebugLog("OnIMECompositionChange focus-loss cancel send failed.");
				}
				ClearPendingTypeDelta(driver);
				ClearComposition(driver);
				state.IgnoreNextEmptyCompositionCommit = true;
				return;
			}
			if ((flag || flag2) && state.ImeComposition.Length > 1)
			{
				state.SuppressEmptyCompositionEndUntilTimestamp = Stopwatch.GetTimestamp() + Stopwatch.Frequency / 10;
			}
			if (text.Length == 0 && state.ImeComposition.Length > 1 && IsSuppressingCompositionEndAfterDeletion(state) && (text2.Length == 0 || text2 == state.ImeComposition))
			{
				DebugLog("OnIMECompositionChange suppressing transient empty composition after deletion");
				state.SuppressEmptyCompositionEndUntilTimestamp = 0L;
				ClearPendingTypeDelta(driver);
				return;
			}
			if (text.Length > 0)
			{
				state.CompositionCaretOffset = GetNextCompositionCaretOffset(state.ImeComposition, text, state.CompositionCaretOffset, flag);
			}
			if (TrySendComposition(text, text2, state.CompositionCaretOffset, imeEditAction))
			{
				DebugLog($"OnIMECompositionChange sent via InterprocessLib: composition=\"{EscapeForLog(text)}\", committed=\"{EscapeForLog(text2)}\", caretOffset={state.CompositionCaretOffset}");
				ClearPendingTypeDelta(driver);
				state.ImeComposition = text;
				if (text.Length == 0)
				{
					state.CompositionCaretOffset = -1;
					state.SuppressEmptyCompositionEndUntilTimestamp = 0L;
				}
				return;
			}
			DebugLog("OnIMECompositionChange InterprocessLib send failed, falling back to typeDelta.");
			if (text == state.ImeComposition)
			{
				return;
			}
			if (typeDelta == null)
			{
				RendererPlugin.Logger.LogWarning((object)"KeyboardDriver.typeDelta is null. IME composition update was skipped.");
				return;
			}
			string text3 = TakeTypeDeltaSuffix(driver, state.LastUpdateTypeDeltaLength);
			if (text.Length == 0)
			{
				if (text3.Length == 0)
				{
					text3 = TakeFullTypeDelta(driver);
				}
				if (text3.Length > 0 && text3 != state.ImeComposition)
				{
					ReplaceComposition(typeDelta, state.ImeComposition, text3);
				}
				state.ImeComposition = string.Empty;
				state.CompositionCaretOffset = -1;
				state.SuppressEmptyCompositionEndUntilTimestamp = 0L;
			}
			else
			{
				ReplaceComposition(typeDelta, state.ImeComposition, text);
				state.ImeComposition = text;
				state.CompositionCaretOffset = text.Length;
			}
		}

		private static ImeEditAction GetImeEditAction(bool backspaceKeyActive, bool deleteKeyActive)
		{
			if (deleteKeyActive)
			{
				return ImeEditAction.Delete;
			}
			if (backspaceKeyActive)
			{
				return ImeEditAction.Backspace;
			}
			return ImeEditAction.None;
		}

		private static bool IsCompositionBackspaceKeyActive()
		{
			Keyboard current = Keyboard.current;
			if (current == null)
			{
				return false;
			}
			if (!((ButtonControl)current.backspaceKey).wasPressedThisFrame)
			{
				return ((ButtonControl)current.backspaceKey).isPressed;
			}
			return true;
		}

		private static bool IsCompositionDeleteKeyActive()
		{
			Keyboard current = Keyboard.current;
			if (current == null)
			{
				return false;
			}
			if (!((ButtonControl)current.deleteKey).wasPressedThisFrame)
			{
				return ((ButtonControl)current.deleteKey).isPressed;
			}
			return true;
		}

		private static bool IsSuppressingCompositionEndAfterDeletion(DriverState state)
		{
			if (state.SuppressEmptyCompositionEndUntilTimestamp == 0L)
			{
				return false;
			}
			if (Stopwatch.GetTimestamp() <= state.SuppressEmptyCompositionEndUntilTimestamp)
			{
				return true;
			}
			state.SuppressEmptyCompositionEndUntilTimestamp = 0L;
			return false;
		}

		private static bool IsLikelyFocusLossAccumulatedCommit(string committedText, string previousComposition)
		{
			if (committedText.Length == 0 || previousComposition.Length == 0)
			{
				return false;
			}
			if (committedText.Length <= Math.Max(previousComposition.Length * 2, previousComposition.Length + 8))
			{
				return false;
			}
			if (CountOccurrences(committedText, previousComposition) >= 2)
			{
				return true;
			}
			int length = Math.Min(previousComposition.Length, committedText.Length);
			if (string.CompareOrdinal(committedText, 0, previousComposition, 0, length) == 0)
			{
				return committedText.Length > previousComposition.Length * 3;
			}
			return false;
		}

		private static int CountOccurrences(string value, string pattern)
		{
			int num = 0;
			int num2;
			for (num2 = 0; num2 < value.Length; num2 += pattern.Length)
			{
				num2 = value.IndexOf(pattern, num2, StringComparison.Ordinal);
				if (num2 < 0)
				{
					return num;
				}
				num++;
			}
			return num;
		}

		private static int GetNextCompositionCaretOffset(string previousComposition, string nextComposition, int previousCaretOffset, bool backspaceKeyActive)
		{
			if (nextComposition.Length == 0)
			{
				return -1;
			}
			if (previousComposition.Length == 0 || previousCaretOffset < 0 || previousCaretOffset == previousComposition.Length)
			{
				return nextComposition.Length;
			}
			int num = TryGetBackspaceDeletionOffset(previousComposition, nextComposition, previousCaretOffset, backspaceKeyActive);
			if (num >= 0)
			{
				return num;
			}
			int num2 = TryGetCaretInsertionOffset(previousComposition, nextComposition, previousCaretOffset);
			if (num2 >= 0)
			{
				return num2;
			}
			int sharedPrefixLength = GetSharedPrefixLength(previousComposition, nextComposition);
			int sharedSuffixLength = GetSharedSuffixLength(previousComposition, nextComposition, sharedPrefixLength);
			int num3 = previousComposition.Length - sharedSuffixLength;
			int val = nextComposition.Length - sharedSuffixLength;
			if (previousCaretOffset < sharedPrefixLength)
			{
				return Math.Min(previousCaretOffset, nextComposition.Length);
			}
			if (previousCaretOffset <= num3)
			{
				return Math.Max(sharedPrefixLength, Math.Min(val, nextComposition.Length));
			}
			int num4 = nextComposition.Length - previousComposition.Length;
			return Math.Max(0, Math.Min(previousCaretOffset + num4, nextComposition.Length));
		}

		private static int TryGetBackspaceDeletionOffset(string previousComposition, string nextComposition, int previousCaretOffset, bool backspaceKeyActive)
		{
			if (!backspaceKeyActive)
			{
				return -1;
			}
			int num = previousComposition.Length - nextComposition.Length;
			if (num <= 0 || previousCaretOffset < num)
			{
				return -1;
			}
			int num2 = previousCaretOffset - num;
			if (!string.Equals(previousComposition.Remove(num2, num), nextComposition, StringComparison.Ordinal))
			{
				return -1;
			}
			return num2;
		}

		private static int TryGetCaretInsertionOffset(string previousComposition, string nextComposition, int previousCaretOffset)
		{
			int num = nextComposition.Length - previousComposition.Length;
			if (num <= 0)
			{
				return -1;
			}
			string value = previousComposition.Substring(0, previousCaretOffset);
			string value2 = previousComposition.Substring(previousCaretOffset);
			if (!nextComposition.StartsWith(value, StringComparison.Ordinal) || !nextComposition.EndsWith(value2, StringComparison.Ordinal))
			{
				return -1;
			}
			return Math.Min(previousCaretOffset + num, nextComposition.Length);
		}

		private static int GetSharedPrefixLength(string first, string second)
		{
			int num = Math.Min(first.Length, second.Length);
			for (int i = 0; i < num; i++)
			{
				if (first[i] != second[i])
				{
					return i;
				}
			}
			return num;
		}

		private static int GetSharedSuffixLength(string first, string second, int prefixLength)
		{
			int num = Math.Min(first.Length, second.Length) - prefixLength;
			for (int i = 0; i < num; i++)
			{
				if (first[first.Length - 1 - i] != second[second.Length - 1 - i])
				{
					return i;
				}
			}
			return num;
		}

		private static void ClearPendingTypeDelta(object driver)
		{
			StringBuilder typeDelta = GetTypeDelta(driver);
			if (typeDelta != null && typeDelta.Length != 0)
			{
				typeDelta.Length = 0;
			}
		}

		private static void ReplaceComposition(StringBuilder typeDelta, string previousComposition, string nextComposition)
		{
			for (int i = 0; i < previousComposition.Length; i++)
			{
				typeDelta.Append('\b');
			}
			typeDelta.Append(nextComposition);
		}

		public static void RemoveIMEEditingKeys(KeyboardState state)
		{
			//IL_0016: 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)
			if (state.heldKeys != null)
			{
				Key[] rendererEditingKeys = ImeKeys.RendererEditingKeys;
				foreach (Key item in rendererEditingKeys)
				{
					state.heldKeys.Remove(item);
				}
			}
		}

		public static void SynchronizeCompositionCaret(object driver, Keyboard keyboard)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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_0044: 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_0067: 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)
			DriverState state = GetState(driver);
			if (state.ImeComposition.Length == 0)
			{
				state.PreviousHeldIMEEditingKeys.Clear();
				return;
			}
			Key[] caretKeys = ImeKeys.CaretKeys;
			foreach (Key val in caretKeys)
			{
				bool flag = IsIMECaretKeyPressed(val, keyboard);
				bool num = IsIMECaretKeyPressedThisFrame(val, keyboard);
				bool flag2 = state.PreviousHeldIMEEditingKeys.Contains(val);
				if ((num || flag) && !flag2)
				{
					MoveCompositionCaret(driver, state, val);
				}
				if (flag)
				{
					state.PreviousHeldIMEEditingKeys.Add(val);
				}
				else
				{
					state.PreviousHeldIMEEditingKeys.Remove(val);
				}
			}
		}

		private static bool IsIMECaretKeyPressedThisFrame(Key key, Keyboard keyboard)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			KeyControl? iMECaretKeyControl = GetIMECaretKeyControl(key, keyboard);
			if (iMECaretKeyControl == null)
			{
				return false;
			}
			return ((ButtonControl)iMECaretKeyControl).wasPressedThisFrame;
		}

		private static bool IsIMECaretKeyPressed(Key key, Keyboard keyboard)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			KeyControl? iMECaretKeyControl = GetIMECaretKeyControl(key, keyboard);
			if (iMECaretKeyControl == null)
			{
				return false;
			}
			return ((ButtonControl)iMECaretKeyControl).isPressed;
		}

		private static KeyControl? GetIMECaretKeyControl(Key key, Keyboard keyboard)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected I4, but got Unknown
			if ((int)key != 8)
			{
				if ((int)key != 127)
				{
					return (KeyControl?)((key - 275) switch
					{
						1 => keyboard.leftArrowKey, 
						0 => keyboard.rightArrowKey, 
						3 => keyboard.homeKey, 
						4 => keyboard.endKey, 
						_ => null, 
					});
				}
				return keyboard.deleteKey;
			}
			return keyboard.backspaceKey;
		}

		private static void MoveCompositionCaret(object driver, DriverState state, Key key)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Invalid comparison between Unknown and I4
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected I4, but got Unknown
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			int num = ((state.CompositionCaretOffset < 0) ? state.ImeComposition.Length : state.CompositionCaretOffset);
			int num2 = num;
			if ((int)key != 8)
			{
				if ((int)key == 127)
				{
					goto IL_0050;
				}
				switch (key - 275)
				{
				case 1:
					break;
				case 0:
					goto IL_0050;
				case 3:
					num2 = 0;
					goto IL_0066;
				case 4:
					num2 = state.ImeComposition.Length;
					goto IL_0066;
				default:
					goto IL_0066;
				}
			}
			num2--;
			goto IL_0066;
			IL_0066:
			num2 = Math.Max(0, Math.Min(num2, state.ImeComposition.Length));
			if (num2 == num)
			{
				DebugLog($"MoveCompositionCaret no-op: key={key}, composition=\"{EscapeForLog(state.ImeComposition)}\", offset={num}");
				return;
			}
			state.CompositionCaretOffset = num2;
			DebugLog($"MoveCompositionCaret send: key={key}, composition=\"{EscapeForLog(state.ImeComposition)}\", previousOffset={num}, nextOffset={num2}");
			if (!TrySendComposition(state.ImeComposition, string.Empty, state.CompositionCaretOffset))
			{
				DebugLog("MoveCompositionCaret send failed.");
			}
			return;
			IL_0050:
			num2++;
			goto IL_0066;
		}

		private static bool TrySendComposition(string composition, string committedText, int caretOffset, ImeEditAction editAction = ImeEditAction.None)
		{
			try
			{
				InitializeMessaging();
				_messenger.SendObject<ImeInterprocessMessage>("ImeComposition", new ImeInterprocessMessage
				{
					Composition = composition,
					CommittedText = committedText,
					CaretOffset = caretOffset,
					EditAction = editAction
				});
				return true;
			}
			catch (Exception ex)
			{
				DebugLog("InterprocessLib send threw " + ex.GetType().Name + ": " + EscapeForLog(ex.Message));
				DisposeMessaging();
				return false;
			}
		}

		public static void TrimTypeDelta(object driver, int length)
		{
			if (length >= 0)
			{
				StringBuilder typeDelta = GetTypeDelta(driver);
				if (typeDelta != null && typeDelta.Length > length)
				{
					typeDelta.Length = length;
				}
			}
		}

		private static string TakeTypeDeltaSuffix(object driver, int length)
		{
			if (length < 0)
			{
				return string.Empty;
			}
			StringBuilder typeDelta = GetTypeDelta(driver);
			if (typeDelta == null || typeDelta.Length <= length)
			{
				return string.Empty;
			}
			string result = typeDelta.ToString(length, typeDelta.Length - length);
			typeDelta.Length = length;
			return result;
		}

		private static string TakeFullTypeDelta(object driver)
		{
			StringBuilder typeDelta = GetTypeDelta(driver);
			if (typeDelta == null || typeDelta.Length == 0)
			{
				return string.Empty;
			}
			string result = typeDelta.ToString();
			typeDelta.Length = 0;
			return result;
		}

		private static string EscapeForLog(string value)
		{
			return value.Replace("\\", "\\\\").Replace("\r", "\\r").Replace("\n", "\\n")
				.Replace("\t", "\\t");
		}

		private static void DebugLog(string message)
		{
			RendererPlugin.LogDebugIme(message);
		}
	}
	internal static class LegacyPluginWarning
	{
		public static void WarnIfLoaded(ManualLogSource logger)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				if (string.Equals(assemblies[i].GetName().Name, "NeosBetterIMESupport", StringComparison.OrdinalIgnoreCase))
				{
					logger.LogWarning((object)"Legacy NeosBetterIMESupport is also loaded. It attaches its own IME handler and can cause duplicated composition text. Remove or disable the old NeosBetterIMESupport plugin folder from BepInEx/plugins.");
					break;
				}
			}
		}
	}
	[BepInPlugin("dev.blhsrwznrghfzpr.ResoniteBetterIMESupport.Renderer", "ResoniteBetterIMESupport.Renderer", "3.0.2")]
	public sealed class RendererPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "dev.blhsrwznrghfzpr.ResoniteBetterIMESupport.Renderer";

		public const string PluginName = "ResoniteBetterIMESupport.Renderer";

		public const string PluginVersion = "3.0.2";

		internal static ManualLogSource Logger;

		private static ConfigEntry<bool> _enableDebugLogging;

		private void Awake()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			Logger = ((BaseUnityPlugin)this).Logger;
			_enableDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableDebugLogging", false, "Enable verbose IME debug logging.");
			LegacyPluginWarning.WarnIfLoaded(Logger);
			KeyboardDriverIMEPatch.InitializeMessaging();
			new Harmony("dev.blhsrwznrghfzpr.ResoniteBetterIMESupport.Renderer").PatchAll(Assembly.GetExecutingAssembly());
			Logger.LogInfo((object)"ResoniteBetterIMESupport.Renderer loaded.");
		}

		private void OnDestroy()
		{
			KeyboardDriverIMEPatch.DisposeMessaging();
		}

		internal static void LogDebugIme(string message)
		{
			if (_enableDebugLogging.Value)
			{
				Logger.LogInfo((object)("[IME debug] " + message));
			}
		}
	}
}
namespace ResoniteBetterIMESupport.Renderer.Patches
{
	[HarmonyPatch]
	internal static class KeyboardDriverStartPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(KeyboardDriverIMEPatch.KeyboardDriverType, "Start", (Type[])null, (Type[])null);
		}

		private static void Postfix(object __instance)
		{
			KeyboardDriverIMEPatch.Subscribe(__instance);
		}
	}
	[HarmonyPatch]
	internal static class KeyboardDriverUpdateStatePatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(KeyboardDriverIMEPatch.KeyboardDriverType, "UpdateState", (Type[])null, (Type[])null);
		}

		private static void Prefix(object __instance, out int __state)
		{
			__state = KeyboardDriverIMEPatch.GetTypeDelta(__instance)?.Length ?? (-1);
			KeyboardDriverIMEPatch.GetState(__instance).LastUpdateTypeDeltaLength = __state;
		}

		private static void Postfix(object __instance, KeyboardState state, int __state)
		{
			if (KeyboardDriverIMEPatch.HasComposition(__instance))
			{
				if (__state >= 0)
				{
					KeyboardDriverIMEPatch.TrimTypeDelta(__instance, __state);
				}
				KeyboardDriverIMEPatch.RemoveIMEEditingKeys(state);
			}
		}
	}
	[HarmonyPatch]
	internal static class KeyboardDriverHandleOutputStatePatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(KeyboardDriverIMEPatch.KeyboardDriverType, "HandleOutputState", (Type[])null, (Type[])null);
		}

		private static void Postfix(object __instance, OutputState output)
		{
			KeyboardDriverIMEPatch.HandleKeyboardInputActive(__instance, output.keyboardInputActive);
		}
	}
}