Decompiled source of MageArenaChineseVoice v1.1.1

BepInEx/plugins/MageArenaChinese.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Dissonance;
using HarmonyLib;
using MageArenaChineseVoice.Config;
using MageArenaChineseVoice.Patches;
using Microsoft.CodeAnalysis;
using Recognissimo;
using Recognissimo.Components;
using UnityEngine;
using UnityEngine.Events;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("MageArenaChinese")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0+0c07918a4bae9ee46babceb7f8e4983536154756")]
[assembly: AssemblyProduct("MageArenaChinese")]
[assembly: AssemblyTitle("MageArenaChinese")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.0.0")]
[module: UnverifiableCode]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MageArenaChinese
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "MageArenaChinese";

		public const string PLUGIN_NAME = "MageArenaChinese";

		public const string PLUGIN_VERSION = "1.2.0";
	}
}
namespace MageArenaChineseVoice
{
	[BepInPlugin("com.xofelttil.MageArenaChineseVoice", "MageArenaChineseVoice", "2.0.0")]
	public class MageArenaChineseVoice : BaseUnityPlugin
	{
		private Harmony _harmony;

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			VoiceCommandConfig.Init(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("com.xofelttil.MageArenaChineseVoice");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			GlobalNreGuardPatch.Apply(_harmony, enabled: true, 0.8f, logDetail: true, null, 20000, ((BaseUnityPlugin)this).Logger);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"MageArenaChineseVoice loaded (NRE guard enabled).");
		}
	}
}
namespace MageArenaChineseVoice.Patches
{
	public static class GlobalNreGuardPatch
	{
		private class NreGuardRunner : MonoBehaviour
		{
			private static NreGuardRunner _inst;

			public static NreGuardRunner Ensure()
			{
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0023: Expected O, but got Unknown
				if ((Object)(object)_inst != (Object)null)
				{
					return _inst;
				}
				GameObject val = new GameObject("MA_GlobalNreGuard_Runner");
				Object.DontDestroyOnLoad((Object)(object)val);
				_inst = val.AddComponent<NreGuardRunner>();
				return _inst;
			}
		}

		[CompilerGenerated]
		private sealed class <ReEnableSoon>d__13 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public Behaviour b;

			public float sec;

			private int <id>5__1;

			private float <now>5__2;

			private object <>s__3;

			private bool <>s__4;

			private float <until>5__5;

			private float <remain>5__6;

			private Exception <e>5__7;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ReEnableSoon>d__13(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>s__3 = null;
				<e>5__7 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(sec);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if ((Object)(object)b == (Object)null)
					{
						return false;
					}
					<id>5__1 = ((Object)b).GetInstanceID();
					<now>5__2 = Time.realtimeSinceStartup;
					<>s__3 = _lock;
					<>s__4 = false;
					try
					{
						Monitor.Enter(<>s__3, ref <>s__4);
						if (_cooldownUntil.TryGetValue(<id>5__1, out <until>5__5) && <until>5__5 > <now>5__2)
						{
							<remain>5__6 = <until>5__5 - <now>5__2;
							((MonoBehaviour)NreGuardRunner.Ensure()).StartCoroutine(ReEnableSoon(b, <remain>5__6));
							return false;
						}
					}
					finally
					{
						if (<>s__4)
						{
							Monitor.Exit(<>s__3);
						}
					}
					<>s__3 = null;
					try
					{
						b.enabled = true;
						if (_logDetail)
						{
							ManualLogSource log = _log;
							if (log != null)
							{
								Behaviour obj = b;
								log.LogInfo((object)("[GlobalNreGuardPatch] Re-enabled: " + GetPath((obj != null) ? ((Component)obj).transform : null)));
							}
						}
					}
					catch (Exception ex)
					{
						<e>5__7 = ex;
						ManualLogSource log2 = _log;
						if (log2 != null)
						{
							log2.LogWarning((object)("[GlobalNreGuardPatch] Re-enable failed: " + <e>5__7.Message));
						}
					}
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static bool _enabled = true;

		private static float _cooldownSec = 0.8f;

		private static bool _logDetail = true;

		private static int _maxPatches = 10000;

		private static string[] _hookMethods = new string[6] { "Update", "LateUpdate", "FixedUpdate", "Start", "Awake", "OnGUI" };

		private static Harmony _harmony;

		private static ManualLogSource _log;

		private static readonly Dictionary<int, float> _cooldownUntil = new Dictionary<int, float>();

		private static readonly object _lock = new object();

		public static void Apply(Harmony harmony = null, bool enabled = true, float cooldownSec = 0.8f, bool logDetail = true, string[] hookMethods = null, int maxPatches = 10000, ManualLogSource logger = null)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			_enabled = enabled;
			_cooldownSec = Mathf.Max(0f, cooldownSec);
			_logDetail = logDetail;
			_maxPatches = Mathf.Max(1, maxPatches);
			if (hookMethods != null && hookMethods.Length != 0)
			{
				_hookMethods = hookMethods;
			}
			_log = logger ?? Logger.CreateLogSource("MA-GlobalNreGuardPatch");
			if (_harmony == null)
			{
				_harmony = (Harmony)(((object)harmony) ?? ((object)new Harmony("com.xofelttil.MA.GlobalNreGuardPatch")));
			}
			NreGuardRunner.Ensure();
			if (!_enabled)
			{
				_log.LogInfo((object)"[GlobalNreGuardPatch] Disabled (not installing patches).");
				return;
			}
			try
			{
				int num = InstallGlobalFinalizers();
				_log.LogInfo((object)$"[GlobalNreGuardPatch] Installed finalizers on {num} method(s). Cooldown={_cooldownSec:0.###}s");
			}
			catch (Exception arg)
			{
				_log.LogError((object)$"[GlobalNreGuardPatch] Install failed: {arg}");
			}
		}

		private static int InstallGlobalFinalizers()
		{
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			int num = 0;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (SkipAssembly(assembly))
				{
					continue;
				}
				Type[] types;
				try
				{
					types = assembly.GetTypes();
				}
				catch
				{
					continue;
				}
				Type[] array = types;
				foreach (Type type in array)
				{
					if (type == null || !typeof(MonoBehaviour).IsAssignableFrom(type) || type.IsAbstract)
					{
						continue;
					}
					string[] hookMethods = _hookMethods;
					foreach (string text in hookMethods)
					{
						MethodInfo methodInfo;
						try
						{
							methodInfo = AccessTools.Method(type, text, Type.EmptyTypes, (Type[])null);
						}
						catch
						{
							continue;
						}
						if (methodInfo == null || methodInfo.GetParameters().Length != 0 || methodInfo.ReturnType != typeof(void))
						{
							continue;
						}
						try
						{
							HarmonyMethod val = new HarmonyMethod(typeof(GlobalNreGuardPatch).GetMethod("Finalizer", BindingFlags.Static | BindingFlags.NonPublic));
							PatchProcessor val2 = _harmony.CreateProcessor((MethodBase)methodInfo);
							val2.AddFinalizer(val);
							val2.Patch();
							num++;
							if (num >= _maxPatches)
							{
								return num;
							}
						}
						catch (Exception ex)
						{
							if (_logDetail)
							{
								ManualLogSource log = _log;
								if (log != null)
								{
									log.LogWarning((object)("[GlobalNreGuardPatch] Patch fail " + type.FullName + "." + text + ": " + ex.Message));
								}
							}
						}
					}
				}
			}
			return num;
			static bool SkipAssembly(Assembly a)
			{
				string text2 = a.GetName().Name ?? "";
				if (text2.StartsWith("System") || text2.StartsWith("mscorlib") || text2.StartsWith("netstandard"))
				{
					return true;
				}
				if (text2.StartsWith("Unity") || text2.StartsWith("UnityEngine") || text2.StartsWith("UnityEditor"))
				{
					return true;
				}
				if (text2.StartsWith("BepInEx") || text2.StartsWith("mono"))
				{
					return true;
				}
				if (text2.StartsWith("Harmony") || text2.StartsWith("0Harmony"))
				{
					return true;
				}
				return false;
			}
		}

		private static Exception Finalizer(object __instance, Exception __exception)
		{
			if (!_enabled)
			{
				return __exception;
			}
			if (__exception is NullReferenceException)
			{
				Behaviour val = (Behaviour)((__instance is Behaviour) ? __instance : null);
				if ((Object)(object)val != (Object)null)
				{
					float realtimeSinceStartup = Time.realtimeSinceStartup;
					int instanceID = ((Object)val).GetInstanceID();
					bool flag = false;
					lock (_lock)
					{
						if (_cooldownUntil.TryGetValue(instanceID, out var value) && value > realtimeSinceStartup)
						{
							flag = true;
						}
						else
						{
							_cooldownUntil[instanceID] = realtimeSinceStartup + _cooldownSec;
						}
					}
					if (!flag && val.isActiveAndEnabled)
					{
						if (_logDetail)
						{
							ManualLogSource log = _log;
							if (log != null)
							{
								log.LogWarning((object)$"[GlobalNreGuardPatch] NRE swallowed: {GetPath(((Component)val).transform)} -> disable {_cooldownSec:0.###}s");
							}
						}
						((MonoBehaviour)NreGuardRunner.Ensure()).StartCoroutine(ReEnableSoon(val, _cooldownSec));
						val.enabled = false;
					}
					return null;
				}
				if (_logDetail)
				{
					ManualLogSource log2 = _log;
					if (log2 != null)
					{
						log2.LogWarning((object)"[GlobalNreGuardPatch] NRE swallowed on non-Behaviour instance.");
					}
				}
				return null;
			}
			return __exception;
		}

		[IteratorStateMachine(typeof(<ReEnableSoon>d__13))]
		private static IEnumerator ReEnableSoon(Behaviour b, float sec)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ReEnableSoon>d__13(0)
			{
				b = b,
				sec = sec
			};
		}

		private static string GetPath(Transform t)
		{
			if ((Object)(object)t == (Object)null)
			{
				return "<null>";
			}
			string text = ((Object)t).name;
			while ((Object)(object)t.parent != (Object)null)
			{
				t = t.parent;
				text = ((Object)t.parent).name + "/" + text;
			}
			return text;
		}

		internal static void Apply(object harmony, bool enabled, float cooldownSec, bool logDetail, object hookMethods, int maxPatches, ManualLogSource logger)
		{
			throw new NotImplementedException();
		}
	}
	internal sealed class FastKeywordMatcher
	{
		private sealed class Node
		{
			public readonly Dictionary<char, Node> Next = new Dictionary<char, Node>();

			public List<string> Ends;
		}

		private readonly Node _root = new Node();

		public FastKeywordMatcher(IEnumerable<string> keywords)
		{
			foreach (string keyword in keywords)
			{
				if (string.IsNullOrEmpty(keyword))
				{
					continue;
				}
				Node node = _root;
				string text = keyword;
				foreach (char key in text)
				{
					if (!node.Next.TryGetValue(key, out var value))
					{
						value = (node.Next[key] = new Node());
					}
					node = value;
				}
				Node node3 = node;
				(node3.Ends ?? (node3.Ends = new List<string>())).Add(keyword);
			}
		}

		public List<(string kw, int s, int e)> MatchAll(string text)
		{
			List<(string, int, int)> list = new List<(string, int, int)>();
			if (string.IsNullOrEmpty(text))
			{
				return list;
			}
			for (int i = 0; i < text.Length; i++)
			{
				Node value = _root;
				for (int j = i; j < text.Length && value.Next.TryGetValue(text[j], out value); j++)
				{
					if (value.Ends == null)
					{
						continue;
					}
					foreach (string end in value.Ends)
					{
						list.Add((end, i, j));
					}
				}
			}
			return list;
		}
	}
	[HarmonyPatch(typeof(SetUpModelProvider), "Setup")]
	public static class SetUpModelProviderPatch
	{
		private static ManualLogSource _log;

		private const string ProviderTag = "MA-CN-ExternalModelProvider";

		[HarmonyPrefix]
		public static bool Prefix(SetUpModelProvider __instance)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0327: Unknown result type (might be due to invalid IL or missing references)
			if (_log == null)
			{
				_log = Logger.CreateLogSource("MA-CN-SetupProvider");
			}
			try
			{
				string text = VoiceCommandConfig.ModelRelativePath?.Value ?? string.Empty;
				ConfigEntry<SystemLanguage> modelLanguage = VoiceCommandConfig.ModelLanguage;
				SystemLanguage val = (SystemLanguage)((modelLanguage == null) ? 6 : ((int)modelLanguage.Value));
				if (string.IsNullOrWhiteSpace(text))
				{
					_log.LogInfo((object)"[Setup] Model.RelativePath is empty → use game's original provider.");
					return true;
				}
				List<string> list = new List<string>();
				if (Path.IsPathRooted(text))
				{
					list.Add(text);
				}
				else
				{
					if (!string.IsNullOrEmpty(Paths.PluginPath))
					{
						list.Add(Path.Combine(Paths.PluginPath, text));
					}
					if (!string.IsNullOrEmpty(Paths.GameRootPath))
					{
						list.Add(Path.Combine(Paths.GameRootPath, text));
					}
					string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
					if (!string.IsNullOrEmpty(directoryName))
					{
						list.Add(Path.Combine(directoryName, text));
					}
				}
				list = list.Where((string p) => !string.IsNullOrWhiteSpace(p)).Select(NormalizePath).Distinct<string>(StringComparer.OrdinalIgnoreCase)
					.ToList();
				if (list.Count == 0)
				{
					_log.LogWarning((object)"[Setup] No valid path candidates can be built. Fallback to original.");
					return true;
				}
				string text2 = list.FirstOrDefault(Directory.Exists);
				if (string.IsNullOrEmpty(text2))
				{
					_log.LogWarning((object)("[Setup] None of candidate model paths exist: " + string.Join(" | ", list) + " → fallback to original."));
					return true;
				}
				if (!File.Exists(Path.Combine(text2, "model.conf")) && !Directory.EnumerateFileSystemEntries(text2).Any())
				{
					_log.LogWarning((object)("[Setup] Path looks empty or not a model folder: " + text2 + " → fallback to original."));
					return true;
				}
				GameObject gameObject = ((Component)__instance).gameObject;
				if ((Object)(object)gameObject == (Object)null)
				{
					_log.LogWarning((object)"[Setup] Target GameObject is null. Fallback to original.");
					return true;
				}
				StreamingAssetsLanguageModelProvider val2 = gameObject.GetComponent<StreamingAssetsLanguageModelProvider>();
				if ((Object)(object)val2 == (Object)null)
				{
					val2 = gameObject.AddComponent<StreamingAssetsLanguageModelProvider>();
					TagComponent((Component)(object)val2, "MA-CN-ExternalModelProvider");
					_log.LogDebug((object)"[Setup] Added new StreamingAssetsLanguageModelProvider (tagged).");
				}
				else
				{
					_log.LogDebug((object)"[Setup] Reusing existing StreamingAssetsLanguageModelProvider.");
				}
				val2.language = val;
				val2.languageModels = new List<StreamingAssetsLanguageModel>
				{
					new StreamingAssetsLanguageModel
					{
						language = val,
						path = text2
					}
				};
				SpeechRecognizer component = ((Component)__instance).GetComponent<SpeechRecognizer>();
				if ((Object)(object)component == (Object)null)
				{
					_log.LogWarning((object)"[Setup] SpeechRecognizer not found on target GameObject. Let original handle.");
					return true;
				}
				((SpeechProcessor)component).LanguageModelProvider = (LanguageModelProvider)(object)val2;
				_log.LogInfo((object)$"[Setup] Using external language model: \"{text2}\" (lang={val})");
				_log.LogDebug((object)("[Setup] Candidates tried: " + string.Join(" | ", list)));
				return false;
			}
			catch (Exception arg)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogError((object)$"[Setup] Exception → fallback to original. {arg}");
				}
				return true;
			}
		}

		private static string NormalizePath(string path)
		{
			try
			{
				string text = Path.GetFullPath(path);
				string text2 = text;
				char directorySeparatorChar = Path.DirectorySeparatorChar;
				if (!text2.EndsWith(directorySeparatorChar.ToString()))
				{
					string text3 = text;
					directorySeparatorChar = Path.AltDirectorySeparatorChar;
					if (!text3.EndsWith(directorySeparatorChar.ToString()))
					{
						goto IL_0057;
					}
				}
				text = text.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
				goto IL_0057;
				IL_0057:
				return text;
			}
			catch
			{
				return path;
			}
		}

		private static void TagComponent(Component c, string tag)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)c == (Object)null)
			{
				return;
			}
			try
			{
				((Object)c).hideFlags = (HideFlags)(((Object)c).hideFlags | 0x14);
				if ((Object)(object)c != (Object)null && !((Object)c).name.Contains(tag))
				{
					((Object)c).name = ((Object)c).name + " [" + tag + "]";
				}
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(VoiceControlListener))]
	public static class VoiceControlListenerPatch
	{
		[CompilerGenerated]
		private sealed class <>c__DisplayClass31_0
		{
			public VoiceControlListener instance;

			internal void <ModifiedWaitGetPlayer>b__0(PartialResult p)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				string text = SafeGetText<PartialResult>(p);
				if (_dbgEnabled && _dbgPartial && !string.IsNullOrWhiteSpace(text))
				{
					_log.LogInfo((object)("[Partial] " + text));
				}
				if (!string.IsNullOrWhiteSpace(text))
				{
					_lastMicActivityTs = Time.realtimeSinceStartup;
					TryMatchAndCast(instance, text, "partial");
				}
			}

			internal void <ModifiedWaitGetPlayer>b__1(Result res)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				string text = SafeGetText<Result>(res);
				if (_dbgEnabled && _dbgFinal && !string.IsNullOrWhiteSpace(text))
				{
					_log.LogInfo((object)("[Final] " + text));
				}
				if (!string.IsNullOrWhiteSpace(text))
				{
					_lastMicActivityTs = Time.realtimeSinceStartup;
				}
				TryMatchAndCast(instance, text, "final");
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass36_0
		{
			public VoiceControlListener instance;

			internal void <ModifiedResetMicLong>b__0(PartialResult p)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				string text = SafeGetText<PartialResult>(p);
				if (_dbgEnabled && _dbgPartial && !string.IsNullOrWhiteSpace(text))
				{
					_log.LogInfo((object)("[Partial:reset] " + text));
				}
				if (!string.IsNullOrWhiteSpace(text))
				{
					TryMatchAndCast(instance, text, "partial-reset");
				}
			}

			internal void <ModifiedResetMicLong>b__1(Result r)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				string text = SafeGetText<Result>(r);
				if (_dbgEnabled && _dbgFinal && !string.IsNullOrWhiteSpace(text))
				{
					_log.LogInfo((object)("[Final:reset] " + text));
				}
				((MonoBehaviour)instance).StartCoroutine(FinalRace(instance, text));
			}
		}

		[CompilerGenerated]
		private sealed class <FinalRace>d__32 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public VoiceControlListener instance;

			public string text;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <FinalRace>d__32(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				//IL_003d: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					instance.tryresult(text);
					<>2__current = (object)new WaitForEndOfFrame();
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if (Time.realtimeSinceStartup - _lastWinnerTs <= 0.05f)
					{
						if (_dbgEnabled && _dbgDecision)
						{
							_log.LogDebug((object)"[Race] Native likely won; skip module.");
						}
						return false;
					}
					TryMatchAndCast(instance, text, "final-race");
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <ModifiedResetMicLong>d__36 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public VoiceControlListener instance;

			private <>c__DisplayClass36_0 <>8__1;

			private SpeechRecognizer <recognizer>5__2;

			private SpeechRecognizer <recognizerNew>5__3;

			private MonoBehaviour[] <>s__4;

			private int <>s__5;

			private MonoBehaviour <mb>5__6;

			private ISpellCommand <sc>5__7;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ModifiedResetMicLong>d__36(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<recognizer>5__2 = null;
				<recognizerNew>5__3 = null;
				<>s__4 = null;
				<mb>5__6 = null;
				<sc>5__7 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_007d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0087: Expected O, but got Unknown
				//IL_0268: Unknown result type (might be due to invalid IL or missing references)
				//IL_0272: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>8__1 = new <>c__DisplayClass36_0();
					<>8__1.instance = instance;
					<recognizer>5__2 = srRef.Invoke(<>8__1.instance);
					((SpeechProcessor)<recognizer>5__2).StopProcessing();
					<>2__current = (object)new WaitForSeconds(VoiceCommandConfig.ResetStopWaitSec.Value);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					Object.Destroy((Object)(object)<recognizer>5__2);
					srRef.Invoke(<>8__1.instance) = ((Component)<>8__1.instance).gameObject.AddComponent<SpeechRecognizer>();
					<recognizerNew>5__3 = srRef.Invoke(<>8__1.instance);
					((SpeechProcessor)<recognizerNew>5__3).LanguageModelProvider = (LanguageModelProvider)(object)((Component)<>8__1.instance).GetComponent<StreamingAssetsLanguageModelProvider>();
					((SpeechProcessor)<recognizerNew>5__3).SpeechSource = (SpeechSource)(object)((Component)<>8__1.instance).GetComponent<DissonanceSpeechSource>();
					if (<recognizerNew>5__3.Vocabulary == null)
					{
						<recognizerNew>5__3.Vocabulary = new List<string>();
					}
					<>8__1.instance.SpellPages = new List<ISpellCommand>();
					<>s__4 = ((Component)<>8__1.instance).gameObject.GetComponents<MonoBehaviour>();
					for (<>s__5 = 0; <>s__5 < <>s__4.Length; <>s__5++)
					{
						<mb>5__6 = <>s__4[<>s__5];
						ref ISpellCommand reference = ref <sc>5__7;
						MonoBehaviour obj = <mb>5__6;
						reference = (ISpellCommand)(object)((obj is ISpellCommand) ? obj : null);
						if (<sc>5__7 != null && <sc>5__7 != null)
						{
							<>8__1.instance.SpellPages.Add(<sc>5__7);
						}
						<sc>5__7 = null;
						<mb>5__6 = null;
					}
					<>s__4 = null;
					AddSpellsToVocabulary(<recognizerNew>5__3);
					((UnityEvent<PartialResult>)(object)<recognizerNew>5__3.PartialResultReady).AddListener((UnityAction<PartialResult>)delegate(PartialResult p)
					{
						//IL_0001: Unknown result type (might be due to invalid IL or missing references)
						string text2 = SafeGetText<PartialResult>(p);
						if (_dbgEnabled && _dbgPartial && !string.IsNullOrWhiteSpace(text2))
						{
							_log.LogInfo((object)("[Partial:reset] " + text2));
						}
						if (!string.IsNullOrWhiteSpace(text2))
						{
							TryMatchAndCast(<>8__1.instance, text2, "partial-reset");
						}
					});
					((UnityEvent<Result>)(object)<recognizerNew>5__3.ResultReady).AddListener((UnityAction<Result>)delegate(Result r)
					{
						//IL_0001: Unknown result type (might be due to invalid IL or missing references)
						string text = SafeGetText<Result>(r);
						if (_dbgEnabled && _dbgFinal && !string.IsNullOrWhiteSpace(text))
						{
							_log.LogInfo((object)("[Final:reset] " + text));
						}
						((MonoBehaviour)<>8__1.instance).StartCoroutine(FinalRace(<>8__1.instance, text));
					});
					<>2__current = (object)new WaitForSeconds(VoiceCommandConfig.RestartWaitSec.Value);
					<>1__state = 2;
					return true;
				case 2:
					<>1__state = -1;
					((SpeechProcessor)<recognizerNew>5__3).StartProcessing();
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <ModifiedWaitGetPlayer>d__31 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public VoiceControlListener instance;

			private <>c__DisplayClass31_0 <>8__1;

			private SpeechRecognizer <recognizer>5__2;

			private PlayerInventory <playerInventory>5__3;

			private MonoBehaviour[] <>s__4;

			private int <>s__5;

			private MonoBehaviour <mb>5__6;

			private ISpellCommand <sc>5__7;

			private StringBuilder <sb>5__8;

			private IEnumerator<ISpellCommand> <>s__9;

			private ISpellCommand <s>5__10;

			private string <dump>5__11;

			private SpeechRecognizer <sr>5__12;

			private VoiceBroadcastTrigger <vbt>5__13;

			private bool <srBusy>5__14;

			private float <idleFor>5__15;

			private bool <debounceOk>5__16;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ModifiedWaitGetPlayer>d__31(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<recognizer>5__2 = null;
				<playerInventory>5__3 = null;
				<>s__4 = null;
				<mb>5__6 = null;
				<sc>5__7 = null;
				<sb>5__8 = null;
				<>s__9 = null;
				<s>5__10 = null;
				<dump>5__11 = null;
				<sr>5__12 = null;
				<vbt>5__13 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_043f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0449: Expected O, but got Unknown
				//IL_0471: Unknown result type (might be due to invalid IL or missing references)
				//IL_047b: Expected O, but got Unknown
				//IL_0504: Unknown result type (might be due to invalid IL or missing references)
				//IL_050a: Invalid comparison between Unknown and I4
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>8__1 = new <>c__DisplayClass31_0();
					<>8__1.instance = instance;
					goto IL_00db;
				case 1:
					<>1__state = -1;
					<playerInventory>5__3 = null;
					goto IL_00db;
				case 2:
					<>1__state = -1;
					srRef.Invoke(<>8__1.instance) = ((Component)<>8__1.instance).GetComponent<SpeechRecognizer>();
					<>8__1.instance.SpellPages = new List<ISpellCommand>();
					<>s__4 = ((Component)<>8__1.instance).gameObject.GetComponents<MonoBehaviour>();
					for (<>s__5 = 0; <>s__5 < <>s__4.Length; <>s__5++)
					{
						<mb>5__6 = <>s__4[<>s__5];
						ref ISpellCommand reference = ref <sc>5__7;
						MonoBehaviour obj = <mb>5__6;
						reference = (ISpellCommand)(object)((obj is ISpellCommand) ? obj : null);
						if (<sc>5__7 != null && <sc>5__7 != null)
						{
							<>8__1.instance.SpellPages.Add(<sc>5__7);
						}
						<sc>5__7 = null;
						<mb>5__6 = null;
					}
					<>s__4 = null;
					try
					{
						<sb>5__8 = new StringBuilder();
						<sb>5__8.AppendLine("[MageArenaChineseVoice] SpellPages dump:");
						<>s__9 = <>8__1.instance.SpellPages.Where((ISpellCommand x) => x != null).GetEnumerator();
						try
						{
							while (<>s__9.MoveNext())
							{
								<s>5__10 = <>s__9.Current;
								<sb>5__8.AppendLine(" • " + <s>5__10.GetSpellName() + "  (type: " + ((object)<s>5__10).GetType().FullName + ")");
								<s>5__10 = null;
							}
						}
						finally
						{
							if (<>s__9 != null)
							{
								<>s__9.Dispose();
							}
						}
						<>s__9 = null;
						if (_dbgEnabled)
						{
							ManualLogSource log = _log;
							if (log != null)
							{
								log.LogInfo((object)<sb>5__8.ToString());
							}
						}
						<sb>5__8 = null;
					}
					catch
					{
					}
					<recognizer>5__2 = srRef.Invoke(<>8__1.instance);
					AddSpellsToVocabulary(<recognizer>5__2);
					if (_dbgEnabled && _dbgDumpVocab)
					{
						<dump>5__11 = string.Join(" | ", from x in <recognizer>5__2.Vocabulary.Distinct()
							orderby x
							select x);
						_log.LogInfo((object)$"[Vocab Dump] Count={<recognizer>5__2.Vocabulary.Count} :: {<dump>5__11}");
						<dump>5__11 = null;
					}
					((UnityEvent<PartialResult>)(object)<recognizer>5__2.PartialResultReady).AddListener((UnityAction<PartialResult>)delegate(PartialResult p)
					{
						//IL_0001: Unknown result type (might be due to invalid IL or missing references)
						string text2 = SafeGetText<PartialResult>(p);
						if (_dbgEnabled && _dbgPartial && !string.IsNullOrWhiteSpace(text2))
						{
							_log.LogInfo((object)("[Partial] " + text2));
						}
						if (!string.IsNullOrWhiteSpace(text2))
						{
							_lastMicActivityTs = Time.realtimeSinceStartup;
							TryMatchAndCast(<>8__1.instance, text2, "partial");
						}
					});
					((UnityEvent<Result>)(object)<recognizer>5__2.ResultReady).AddListener((UnityAction<Result>)delegate(Result res)
					{
						//IL_0001: Unknown result type (might be due to invalid IL or missing references)
						string text = SafeGetText<Result>(res);
						if (_dbgEnabled && _dbgFinal && !string.IsNullOrWhiteSpace(text))
						{
							_log.LogInfo((object)("[Final] " + text));
						}
						if (!string.IsNullOrWhiteSpace(text))
						{
							_lastMicActivityTs = Time.realtimeSinceStartup;
						}
						TryMatchAndCast(<>8__1.instance, text, "final");
					});
					<>2__current = (object)new WaitForSeconds(_startupWaitSec);
					<>1__state = 3;
					return true;
				case 3:
					<>1__state = -1;
					((SpeechProcessor)<recognizer>5__2).StartProcessing();
					break;
				case 4:
					{
						<>1__state = -1;
						<sr>5__12 = srRef.Invoke(<>8__1.instance);
						<vbt>5__13 = vbtRef.Invoke(<>8__1.instance);
						if ((Object)(object)<vbt>5__13 != (Object)null && <vbt>5__13.IsTransmitting)
						{
							_lastMicActivityTs = Time.realtimeSinceStartup;
						}
						<srBusy>5__14 = (Object)(object)<sr>5__12 != (Object)null && (int)((SpeechProcessor)<sr>5__12).State == 2;
						<idleFor>5__15 = Time.realtimeSinceStartup - _lastMicActivityTs;
						<debounceOk>5__16 = Time.realtimeSinceStartup - _lastRestartTs >= 2f;
						if ((<srBusy>5__14 && <idleFor>5__15 >= 5f) & <debounceOk>5__16)
						{
							if (_dbgEnabled && _dbgDecision)
							{
								_log.LogWarning((object)$"[Monitor] IdleFor={<idleFor>5__15:0.00}s (no partial/final). Restart recognizer.");
							}
							_lastRestartTs = Time.realtimeSinceStartup;
							((SpeechProcessor)<sr>5__12).StopProcessing();
							((MonoBehaviour)<>8__1.instance).StartCoroutine((IEnumerator)restartsrMethod.Invoke(<>8__1.instance, null));
						}
						<sr>5__12 = null;
						<vbt>5__13 = null;
						break;
					}
					IL_00db:
					if ((Object)(object)<>8__1.instance.pi == (Object)null)
					{
						if (Object.op_Implicit((Object)(object)Camera.main) && (Object)(object)((Component)Camera.main).transform.parent != (Object)null && ((Component)((Component)Camera.main).transform.parent).TryGetComponent<PlayerInventory>(ref <playerInventory>5__3))
						{
							<>8__1.instance.pi = <playerInventory>5__3;
						}
						<>2__current = null;
						<>1__state = 1;
						return true;
					}
					((Component)<>8__1.instance).GetComponent<SetUpModelProvider>().Setup();
					<>2__current = null;
					<>1__state = 2;
					return true;
				}
				if (Object.op_Implicit((Object)(object)<>8__1.instance) && ((Behaviour)<>8__1.instance).isActiveAndEnabled)
				{
					<>2__current = (object)new WaitForSeconds(_monitorIntervalSec);
					<>1__state = 4;
					return true;
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <SafeRestartSr>d__38 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public VoiceControlListener instance;

			private SpeechRecognizer <recognizer>5__1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <SafeRestartSr>d__38(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<recognizer>5__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0068: Unknown result type (might be due to invalid IL or missing references)
				//IL_006e: Invalid comparison between Unknown and I4
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<recognizer>5__1 = srRef.Invoke(instance);
					if ((Object)(object)<recognizer>5__1 == (Object)null)
					{
						return false;
					}
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				if ((int)((SpeechProcessor)<recognizer>5__1).State > 0)
				{
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				((SpeechProcessor)<recognizer>5__1).StartProcessing();
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static Dictionary<string[], Action<VoiceControlListener>> commandMap;

		private static Dictionary<string, string[]> additionalCommandMap;

		private static List<(string[] keys, Action<VoiceControlListener> action)> commandListNormalized;

		private static Dictionary<string, string[]> additionalMapNormalized;

		private static FastKeywordMatcher _mainMatcher;

		private static FastKeywordMatcher _extraMatcher;

		private static Dictionary<string, Action<VoiceControlListener>> _mainKw2Action;

		private static Dictionary<string, string> _extraKw2SpellId;

		private static readonly Dictionary<string, int> _spellPriority = new Dictionary<string, int>
		{
			["thunderbolt"] = 0,
			["blink"] = 1,
			["divine"] = 2,
			["blast"] = 3,
			["rock"] = 4,
			["wisp"] = 5
		};

		private static string _lastHitKw = null;

		private static float _lastHitTs = 0f;

		private const float PARTIAL_REPEAT_WINDOW = 0.3f;

		private static float _lastWinnerTs = -999f;

		private const float WINNER_LATCH_WINDOW = 0.05f;

		private static float _castCooldownSec;

		private static float _startupWaitSec;

		private static float _resetStopWaitSec;

		private static float _restartWaitSec;

		private static float _monitorIntervalSec;

		private static float _lastCastTs;

		private static ManualLogSource _log;

		private static bool _dbgEnabled;

		private static bool _dbgPartial;

		private static bool _dbgFinal;

		private static bool _dbgDecision;

		private static bool _dbgDumpVocab;

		private static readonly FieldRef<VoiceControlListener, SpeechRecognizer> srRef = AccessTools.FieldRefAccess<VoiceControlListener, SpeechRecognizer>("sr");

		private static readonly FieldRef<VoiceControlListener, VoiceBroadcastTrigger> vbtRef = AccessTools.FieldRefAccess<VoiceControlListener, VoiceBroadcastTrigger>("vbt");

		private static readonly MethodInfo restartsrMethod = AccessTools.Method(typeof(VoiceControlListener), "restartsr", (Type[])null, (Type[])null);

		private static float _lastMicActivityTs;

		private static float _lastRestartTs;

		private const float MIC_IDLE_TIMEOUT_SEC = 5f;

		private const float RESTART_DEBOUNCE_SEC = 2f;

		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void AwakePostfix(VoiceControlListener __instance)
		{
			PluginInfo val = ((IEnumerable<PluginInfo>)Chainloader.PluginInfos.Values).FirstOrDefault((Func<PluginInfo, bool>)((PluginInfo p) => p.Metadata.GUID == "com.xofelttil.MageArenaChineseVoice"));
			if (val == null)
			{
				return;
			}
			VoiceCommandConfig.Init(val.Instance.Config);
			_castCooldownSec = Mathf.Max(0f, VoiceCommandConfig.CastCooldownSec.Value);
			_startupWaitSec = Mathf.Max(0f, VoiceCommandConfig.StartupWaitSec.Value);
			_resetStopWaitSec = Mathf.Max(0f, VoiceCommandConfig.ResetStopWaitSec.Value);
			_restartWaitSec = Mathf.Max(0f, VoiceCommandConfig.RestartWaitSec.Value);
			_monitorIntervalSec = Mathf.Max(0.25f, VoiceCommandConfig.MonitorIntervalSec.Value);
			_dbgEnabled = VoiceCommandConfig.DebugEnabled.Value;
			_dbgPartial = VoiceCommandConfig.DebugLogPartial.Value;
			_dbgFinal = VoiceCommandConfig.DebugLogFinal.Value;
			_dbgDecision = VoiceCommandConfig.DebugLogDecision.Value;
			_dbgDumpVocab = VoiceCommandConfig.DebugDumpVocabulary.Value;
			if (_dbgEnabled && _log == null)
			{
				_log = Logger.CreateLogSource("MageArenaChineseVoice");
			}
			commandMap = new Dictionary<string[], Action<VoiceControlListener>>
			{
				{
					VoiceCommandConfig.GetTokens(VoiceCommandConfig.FireballExpanded),
					delegate(VoiceControlListener v)
					{
						v.CastFireball();
					}
				},
				{
					VoiceCommandConfig.GetTokens(VoiceCommandConfig.FrostBoltExpanded),
					delegate(VoiceControlListener v)
					{
						v.CastFrostBolt();
					}
				},
				{
					VoiceCommandConfig.GetTokens(VoiceCommandConfig.WormExpanded),
					delegate(VoiceControlListener v)
					{
						v.CastWorm();
					}
				},
				{
					VoiceCommandConfig.GetTokens(VoiceCommandConfig.HoleExpanded),
					delegate(VoiceControlListener v)
					{
						v.CastHole();
					}
				},
				{
					VoiceCommandConfig.GetTokens(VoiceCommandConfig.MagicMissileExpanded),
					delegate(VoiceControlListener v)
					{
						v.CastMagicMissle();
					}
				},
				{
					VoiceCommandConfig.GetTokens(VoiceCommandConfig.MirrorExpanded),
					delegate(VoiceControlListener v)
					{
						v.ActivateMirror();
					}
				}
			};
			additionalCommandMap = new Dictionary<string, string[]>
			{
				{
					"rock",
					VoiceCommandConfig.GetTokens(VoiceCommandConfig.RockExpanded)
				},
				{
					"wisp",
					VoiceCommandConfig.GetTokens(VoiceCommandConfig.WispExpanded)
				},
				{
					"blast",
					VoiceCommandConfig.GetTokens(VoiceCommandConfig.BlastExpanded)
				},
				{
					"divine",
					VoiceCommandConfig.GetTokens(VoiceCommandConfig.DivineExpanded)
				},
				{
					"blink",
					VoiceCommandConfig.GetTokens(VoiceCommandConfig.BlinkExpanded)
				},
				{
					"thunderbolt",
					VoiceCommandConfig.GetTokens(VoiceCommandConfig.ThunderboltExpanded)
				}
			};
			foreach (KeyValuePair<string, string[]> item3 in ParseExtraModuleBindings(VoiceCommandConfig.ModuleSpellBindings.Value))
			{
				if (additionalCommandMap.TryGetValue(item3.Key, out var value))
				{
					additionalCommandMap[item3.Key] = value.Concat(item3.Value).Distinct().ToArray();
				}
				else
				{
					additionalCommandMap[item3.Key] = item3.Value;
				}
			}
			commandListNormalized = commandMap.Select((KeyValuePair<string[], Action<VoiceControlListener>> kv) => (kv.Key.Select(NormalizeForMatch).ToArray(), kv.Value)).ToList();
			additionalMapNormalized = additionalCommandMap.ToDictionary((KeyValuePair<string, string[]> kv) => kv.Key, (KeyValuePair<string, string[]> kv) => kv.Value.Select(NormalizeForMatch).ToArray());
			_mainKw2Action = new Dictionary<string, Action<VoiceControlListener>>(StringComparer.Ordinal);
			foreach (var item4 in commandListNormalized)
			{
				string[] item = item4.keys;
				Action<VoiceControlListener> item2 = item4.action;
				string[] array = item;
				foreach (string text in array)
				{
					if (!string.IsNullOrEmpty(text))
					{
						_mainKw2Action[text] = item2;
					}
				}
			}
			_extraKw2SpellId = new Dictionary<string, string>(StringComparer.Ordinal);
			foreach (KeyValuePair<string, string[]> item5 in additionalMapNormalized)
			{
				string[] value2 = item5.Value;
				foreach (string text2 in value2)
				{
					if (!string.IsNullOrEmpty(text2))
					{
						_extraKw2SpellId[text2] = item5.Key;
					}
				}
			}
			_mainMatcher = new FastKeywordMatcher(_mainKw2Action.Keys);
			_extraMatcher = new FastKeywordMatcher(_extraKw2SpellId.Keys);
		}

		[HarmonyPatch("waitgetplayer")]
		[HarmonyPrefix]
		private static bool WaitGetPlayerPrefix(VoiceControlListener __instance, ref IEnumerator __result)
		{
			__result = ModifiedWaitGetPlayer(__instance);
			return false;
		}

		[IteratorStateMachine(typeof(<ModifiedWaitGetPlayer>d__31))]
		private static IEnumerator ModifiedWaitGetPlayer(VoiceControlListener instance)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ModifiedWaitGetPlayer>d__31(0)
			{
				instance = instance
			};
		}

		[IteratorStateMachine(typeof(<FinalRace>d__32))]
		private static IEnumerator FinalRace(VoiceControlListener instance, string text)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <FinalRace>d__32(0)
			{
				instance = instance,
				text = text
			};
		}

		private static void AddSpellsToVocabulary(SpeechRecognizer recognizer)
		{
			if (recognizer.Vocabulary == null)
			{
				recognizer.Vocabulary = new List<string>();
			}
			HashSet<string> seen = new HashSet<string>(recognizer.Vocabulary, StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<string[], Action<VoiceControlListener>> item2 in commandMap)
			{
				string[] key = item2.Key;
				foreach (string term2 in key)
				{
					add(term2);
				}
			}
			foreach (KeyValuePair<string, string[]> item3 in additionalCommandMap)
			{
				string[] value = item3.Value;
				foreach (string term3 in value)
				{
					add(term3);
				}
			}
			void add(string term)
			{
				if (!string.IsNullOrWhiteSpace(term))
				{
					string item = term.Trim();
					if (seen.Add(item))
					{
						recognizer.Vocabulary.Add(item);
					}
				}
			}
		}

		private static bool TryMatchAndCast(VoiceControlListener instance, string raw, string source = "")
		{
			if (Time.realtimeSinceStartup - _lastWinnerTs <= 0.05f)
			{
				return false;
			}
			if (Time.realtimeSinceStartup - _lastCastTs < _castCooldownSec)
			{
				return false;
			}
			string text = NormalizeForMatch(raw);
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			List<(string, int, int)> list = _mainMatcher?.MatchAll(text);
			List<(string, int, int)> list2 = _extraMatcher?.MatchAll(text);
			if ((list == null || list.Count == 0) && (list2 == null || list2.Count == 0))
			{
				if (_dbgEnabled && _dbgDecision)
				{
					_log.LogDebug((object)("[Miss:" + source + "] \"" + raw + "\" => \"" + text + "\""));
				}
				return false;
			}
			string chosenType = null;
			string chosenKw = null;
			int chosenStart = -1;
			int chosenEnd = -1;
			if (list != null)
			{
				foreach (var (kw2, s2, e2) in list)
				{
					Consider("main", kw2, s2, e2);
				}
			}
			if (list2 != null)
			{
				foreach (var item4 in list2)
				{
					string item = item4.Item1;
					int item2 = item4.Item2;
					int item3 = item4.Item3;
					string key = _extraKw2SpellId[item];
					int value;
					int priority2 = (_spellPriority.TryGetValue(key, out value) ? value : int.MaxValue);
					Consider("extra", item, item2, item3, priority2);
				}
			}
			if (source.StartsWith("partial", StringComparison.OrdinalIgnoreCase))
			{
				bool flag = chosenEnd == text.Length - 1;
				bool flag2 = chosenKw == _lastHitKw && Time.realtimeSinceStartup - _lastHitTs <= 0.3f;
				if (!flag && !flag2)
				{
					if (_dbgEnabled && _dbgDecision)
					{
						_log.LogDebug((object)$"[Partial-skip] \"{raw}\" kw={chosenKw} end={chosenEnd} last={_lastHitKw}");
					}
					_lastHitKw = chosenKw;
					_lastHitTs = Time.realtimeSinceStartup;
					return false;
				}
			}
			if (Time.realtimeSinceStartup - _lastWinnerTs <= 0.05f)
			{
				return false;
			}
			_lastWinnerTs = Time.realtimeSinceStartup;
			if (chosenType == "main")
			{
				if (_dbgEnabled && _dbgDecision)
				{
					_log.LogInfo((object)("[Hit:" + source + "] main -> " + chosenKw + " :: \"" + raw + "\""));
				}
				if (!_mainKw2Action.TryGetValue(chosenKw, out var value2))
				{
					return false;
				}
				value2(instance);
			}
			else
			{
				string text2 = _extraKw2SpellId[chosenKw];
				string keyNorm = NormalizeId(text2);
				ISpellCommand val = ((IEnumerable<ISpellCommand>)instance.SpellPages).FirstOrDefault((Func<ISpellCommand, bool>)delegate(ISpellCommand s)
				{
					if (s == null)
					{
						return false;
					}
					string text3 = NormalizeId(s.GetSpellName());
					return text3 == keyNorm;
				});
				if (val == null)
				{
					if (_dbgEnabled && _dbgDecision)
					{
						_log.LogWarning((object)("[Extra] Spell page not found for key='" + text2 + "'. Check dump."));
					}
					return false;
				}
				if (_dbgEnabled && _dbgDecision)
				{
					_log.LogInfo((object)("[Hit:" + source + "] extra:" + text2 + " -> " + chosenKw + " :: \"" + raw + "\""));
				}
				val.TryCastSpell();
			}
			_lastCastTs = Time.realtimeSinceStartup;
			_lastHitKw = chosenKw;
			_lastHitTs = _lastCastTs;
			return true;
			void Consider(string type, string kw, int s, int e, int priority = int.MaxValue)
			{
				if (chosenKw == null)
				{
					chosenType = type;
					chosenKw = kw;
					chosenStart = s;
					chosenEnd = e;
				}
				else
				{
					int num = chosenEnd - chosenStart + 1;
					int num2 = e - s + 1;
					if (num2 > num)
					{
						chosenType = type;
						chosenKw = kw;
						chosenStart = s;
						chosenEnd = e;
					}
					else
					{
						if (type == "extra" && chosenType == "extra")
						{
							int value3;
							int num3 = (_spellPriority.TryGetValue(_extraKw2SpellId[chosenKw], out value3) ? value3 : int.MaxValue);
							if (priority < num3)
							{
								chosenType = type;
								chosenKw = kw;
								chosenStart = s;
								chosenEnd = e;
								return;
							}
							if (priority > num3)
							{
								return;
							}
						}
						if (e > chosenEnd)
						{
							chosenType = type;
							chosenKw = kw;
							chosenStart = s;
							chosenEnd = e;
						}
					}
				}
			}
		}

		[HarmonyPatch("resetmiclong")]
		[HarmonyPrefix]
		private static bool ResetMicLongPrefix(VoiceControlListener __instance, ref IEnumerator __result)
		{
			__result = ModifiedResetMicLong(__instance);
			return false;
		}

		[IteratorStateMachine(typeof(<ModifiedResetMicLong>d__36))]
		private static IEnumerator ModifiedResetMicLong(VoiceControlListener instance)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ModifiedResetMicLong>d__36(0)
			{
				instance = instance
			};
		}

		[HarmonyPatch("restartsr")]
		[HarmonyPrefix]
		private static bool RestartSrPrefix(VoiceControlListener __instance, ref IEnumerator __result)
		{
			__result = SafeRestartSr(__instance);
			return false;
		}

		[IteratorStateMachine(typeof(<SafeRestartSr>d__38))]
		private static IEnumerator SafeRestartSr(VoiceControlListener instance)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SafeRestartSr>d__38(0)
			{
				instance = instance
			};
		}

		private static string SafeGetText<T>(T obj)
		{
			if (obj == null)
			{
				return string.Empty;
			}
			Type type = obj.GetType();
			PropertyInfo propertyInfo = type.GetProperty("Text", BindingFlags.Instance | BindingFlags.Public) ?? type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public);
			if (propertyInfo != null)
			{
				return propertyInfo.GetValue(obj)?.ToString() ?? string.Empty;
			}
			FieldInfo fieldInfo = type.GetField("Text", BindingFlags.Instance | BindingFlags.Public) ?? type.GetField("text", BindingFlags.Instance | BindingFlags.Public);
			if (fieldInfo != null)
			{
				return fieldInfo.GetValue(obj)?.ToString() ?? string.Empty;
			}
			return obj.ToString();
		}

		private static string NormalizeForMatch(string s)
		{
			if (string.IsNullOrWhiteSpace(s))
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length);
			foreach (char c in s)
			{
				if (!char.IsWhiteSpace(c) && ((c >= '一' && c <= '\u9fff') || char.IsLetterOrDigit(c)))
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString().ToLowerInvariant();
		}

		private static string NormalizeId(string s)
		{
			if (string.IsNullOrWhiteSpace(s))
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(s.Length);
			string text = s.Trim().ToLowerInvariant();
			foreach (char c in text)
			{
				if (!char.IsWhiteSpace(c) && c != '_' && c != '-')
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}

		private static Dictionary<string, string[]> ParseExtraModuleBindings(string raw)
		{
			Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrWhiteSpace(raw))
			{
				return dictionary;
			}
			string[] array = raw.Split(new char[3] { '|', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
			string[] array2 = array;
			foreach (string text in array2)
			{
				string[] array3 = text.Split(new char[2] { '=', ':' }, 2, StringSplitOptions.RemoveEmptyEntries);
				if (array3.Length == 2)
				{
					string text2 = array3[0].Trim();
					string[] array4 = (from s in array3[1].Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
						select s.Trim()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
					if (text2.Length != 0 && array4.Length != 0)
					{
						dictionary[text2] = array4;
					}
				}
			}
			return dictionary;
		}
	}
}
namespace MageArenaChineseVoice.Config
{
	public static class VoiceCommandConfig
	{
		public enum DetectionMode
		{
			Any,
			All
		}

		public struct VocabPreview
		{
			public int TotalCount;

			public int UniqueCount;

			public int ModuleEntryCount;

			public List<string> AllTokens;
		}

		public static ConfigEntry<SystemLanguage> ModelLanguage;

		public static ConfigEntry<string> ModelRelativePath;

		public static ConfigEntry<bool> ConvertTraditionalToSimplified;

		public static ConfigEntry<bool> EnableSingleCharExpansion;

		public static ConfigEntry<DetectionMode> DetectMode;

		public static ConfigEntry<string> FireballCommand;

		public static ConfigEntry<string> FrostBoltCommand;

		public static ConfigEntry<string> WormCommand;

		public static ConfigEntry<string> HoleCommand;

		public static ConfigEntry<string> MagicMissileCommand;

		public static ConfigEntry<string> MirrorCommand;

		public static ConfigEntry<string> RockCommand;

		public static ConfigEntry<string> WispCommand;

		public static ConfigEntry<string> BlastCommand;

		public static ConfigEntry<string> DivineCommand;

		public static ConfigEntry<string> BlinkCommand;

		public static ConfigEntry<string> ThunderboltCommand;

		public static ConfigEntry<string> ModuleSpellBindings;

		public static ConfigEntry<float> CastCooldownSec;

		public static ConfigEntry<float> StartupWaitSec;

		public static ConfigEntry<float> ResetStopWaitSec;

		public static ConfigEntry<float> RestartWaitSec;

		public static ConfigEntry<float> MonitorIntervalSec;

		public static ConfigEntry<bool> DebugEnabled;

		public static ConfigEntry<bool> DebugLogPartial;

		public static ConfigEntry<bool> DebugLogFinal;

		public static ConfigEntry<bool> DebugLogDecision;

		public static ConfigEntry<bool> DebugDumpVocabulary;

		public static ConfigEntry<bool> DebugDumpVocabularyToFile;

		private static Dictionary<string, string[]> _moduleSpellTokenMap = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);

		private static Dictionary<char, char> _t2s;

		private static bool _mapBuilt;

		private static string _pluginDir;

		private static readonly char[] _punct = new char[62]
		{
			'.', ',', ';', ':', '!', '?', '"', '\'', '(', ')',
			'[', ']', '{', '}', '<', '>', '/', '\\', '|', '-',
			'_', '+', '=', '*', '&', '^', '%', '$', '#', '@',
			'~', '\t', '\r', '\n', '。', ',', '、', ';', ':', '?',
			'!', '「', '」', '『', '』', '(', ')', '《', '》', '〈',
			'〉', '—', '-', '~', '…', '【', '】', '.', '‧', '|',
			'\', '/'
		};

		private static readonly List<ConfigEntry<string>> _commandEntries = new List<ConfigEntry<string>>();

		public static string FireballExpanded => ExpandForUse(FireballCommand?.Value);

		public static string FrostBoltExpanded => ExpandForUse(FrostBoltCommand?.Value);

		public static string WormExpanded => ExpandForUse(WormCommand?.Value);

		public static string HoleExpanded => ExpandForUse(HoleCommand?.Value);

		public static string MagicMissileExpanded => ExpandForUse(MagicMissileCommand?.Value);

		public static string MirrorExpanded => ExpandForUse(MirrorCommand?.Value);

		public static string RockExpanded => ExpandForUse(RockCommand?.Value);

		public static string WispExpanded => ExpandForUse(WispCommand?.Value);

		public static string BlastExpanded => ExpandForUse(BlastCommand?.Value);

		public static string DivineExpanded => ExpandForUse(DivineCommand?.Value);

		public static string BlinkExpanded => ExpandForUse(BlinkCommand?.Value);

		public static string ThunderboltExpanded => ExpandForUse(ThunderboltCommand?.Value);

		public static string[] FireballTokens => GetTokens(FireballExpanded);

		public static string[] FrostBoltTokens => GetTokens(FrostBoltExpanded);

		public static string[] WormTokens => GetTokens(WormExpanded);

		public static string[] HoleTokens => GetTokens(HoleExpanded);

		public static string[] MagicMissileTokens => GetTokens(MagicMissileExpanded);

		public static string[] MirrorTokens => GetTokens(MirrorExpanded);

		public static string[] RockTokens => GetTokens(RockExpanded);

		public static string[] WispTokens => GetTokens(WispExpanded);

		public static string[] BlastTokens => GetTokens(BlastExpanded);

		public static string[] DivineTokens => GetTokens(DivineExpanded);

		public static string[] BlinkTokens => GetTokens(BlinkExpanded);

		public static string[] ThunderboltTokens => GetTokens(ThunderboltExpanded);

		public static void Init(ConfigFile config)
		{
			//IL_03e2: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Assembly executingAssembly = Assembly.GetExecutingAssembly();
				_pluginDir = Path.GetDirectoryName(executingAssembly.Location)?.Replace('\\', '/');
			}
			catch
			{
				_pluginDir = Directory.GetCurrentDirectory().Replace('\\', '/');
			}
			ModelLanguage = config.Bind<SystemLanguage>("Model", "Language", (SystemLanguage)6, "辨識語言(會傳給 Recognissimo/Vosk)。例如 Chinese、Russian、English。");
			ModelRelativePath = config.Bind<string>("Model", "RelativePath", "LanguageModels/vosk-model-small-cn-0.22", "模型資料夾相對於插件 DLL 的路徑(或絕對路徑)。建議使用輕量版本以降低記憶體佔用。");
			ConvertTraditionalToSimplified = config.Bind<bool>("Behavior", "ConvertTraditionalToSimplified", true, "是否將 Config 的繁體詞在讀取時自動轉為簡體(僅程式內生效,不改檔案)。");
			EnableSingleCharExpansion = config.Bind<bool>("Behavior", "EnableSingleCharExpansion", false, "是否將純中文詞額外拆成單字加入(可提升命中;若誤觸多,建議關閉)。");
			DetectMode = config.Bind<DetectionMode>("Behavior", "DetectionMode", DetectionMode.Any, "命中策略:Any=原生或模組任一命中即觸發;All=兩者皆命中才觸發。");
			FireballCommand = BindCommand(config, "Commands", "Fireball", "火球 爆裂 大爆炸", "中文口令:火球術(空格分隔多個同義詞)");
			FrostBoltCommand = BindCommand(config, "Commands", "FrostBolt", "冰凍 冰槍 凍住 ", "中文口令:冰凍術(空格分隔多個同義詞)");
			WormCommand = BindCommand(config, "Commands", "Worm", "入口 芝麻", "中文口令:入口(空格分隔多個同義詞)");
			HoleCommand = BindCommand(config, "Commands", "Hole", "出口 開門", "中文口令:出口(空格分隔多個同義詞)");
			MagicMissileCommand = BindCommand(config, "Commands", "MagicMissile", "魔法飛彈 魔彈 飛彈 魔法彈 魔法", "中文口令:魔法飛彈(空格分隔多個同義詞)");
			MirrorCommand = BindCommand(config, "Commands", "Mirror", "魔鏡", "中文口令:魔鏡(空格分隔多個同義詞)");
			RockCommand = BindCommand(config, "AdditionalCommands", "Rock", "巨石 岩石 大石", "中文口令:巨石(空格分隔多個同義詞)");
			WispCommand = BindCommand(config, "AdditionalCommands", "Wisp", "鬼火 精靈 光靈 靈火", "中文口令:光靈(空格分隔多個同義詞)");
			BlastCommand = BindCommand(config, "AdditionalCommands", "Blast", "爆破 黑暗 衝擊 衝擊 暗影 波動", "中文口令:暗影衝擊(空格分隔多個同義詞)");
			DivineCommand = BindCommand(config, "AdditionalCommands", "Divine", "聖光 光明 奇蹟 治療 治癒", "中文口令:聖光(空格分隔多個同義詞)");
			BlinkCommand = BindCommand(config, "AdditionalCommands", "Blink", "閃現 瞬移 傳送", "中文口令:閃現(空格分隔多個同義詞)");
			ThunderboltCommand = BindCommand(config, "AdditionalCommands", "Thunderbolt", "雷霆一擊 閃電 雷擊 霹靂 雷電", "中文口令:雷霆一擊(空格分隔多個同義詞)");
			ModuleSpellBindings = config.Bind<string>("Modules", "SpellBindings", "", "為外部模組新增法術口令綁定。格式:spellId=關鍵詞1 關鍵詞2|spellId2=關鍵詞...\n左邊為 ISpellCommand.GetSpellName() 的返回值(建議小寫),右邊為空格分隔的觸發詞。\n例:blackrain=黑雨 黑色風暴|summonimp=小惡魔 召喚小鬼");
			TryHookSettingChanged<string>(ModuleSpellBindings);
			CastCooldownSec = config.Bind<float>("Timing", "CastCooldownSec", 0.15f, "命中後的冷卻時間(秒),避免 partial/final 重複觸發。");
			StartupWaitSec = config.Bind<float>("Timing", "StartupWaitSec", 0.05f, "初次啟動識別前的等待(秒)。");
			ResetStopWaitSec = config.Bind<float>("Timing", "ResetStopWaitSec", 0.01f, "Reset 時 StopProcessing 後等待(秒)。");
			RestartWaitSec = config.Bind<float>("Timing", "RestartWaitSec", 0f, "Reset 結束後 StartProcessing 前等待(秒)。");
			MonitorIntervalSec = config.Bind<float>("Timing", "MonitorIntervalSec", 120f, "麥克風狀態檢查間隔(秒)。");
			DebugEnabled = config.Bind<bool>("Debug", "Enabled", false, "是否啟用 Debug 記錄。");
			DebugLogPartial = config.Bind<bool>("Debug", "LogPartial", false, "是否記錄 Partial 結果。");
			DebugLogFinal = config.Bind<bool>("Debug", "LogFinal", true, "是否記錄 Final 結果。");
			DebugLogDecision = config.Bind<bool>("Debug", "LogDecision", true, "是否記錄匹配/施法決策過程。");
			DebugDumpVocabulary = config.Bind<bool>("Debug", "DumpVocabulary", true, "啟動時是否輸出 Vocabulary 清單(到 Console)。");
			DebugDumpVocabularyToFile = config.Bind<bool>("Debug", "DumpVocabularyToFile", false, "啟動時是否將 Vocabulary 另存檔到 BepInEx/Config/。");
			BuildT2SMap();
			RebuildModuleSpellMap();
			RefreshVocabularyCache();
			if (!DebugEnabled.Value)
			{
				return;
			}
			Debug.Log((object)"[VoiceCommandConfig] DebugEnabled = true");
			Debug.Log((object)$"[VoiceCommandConfig] Language = {ModelLanguage.Value}");
			Debug.Log((object)("[VoiceCommandConfig] ModelPath = " + GetResolvedModelPath()));
			Debug.Log((object)$"[VoiceCommandConfig] DetectionMode = {DetectMode.Value}");
			VocabPreview vocabPreview = BuildVocabularyPreview();
			Debug.Log((object)$"[VoiceCommandConfig] Vocabulary total tokens = {vocabPreview.TotalCount}, unique = {vocabPreview.UniqueCount}, modules = {vocabPreview.ModuleEntryCount}");
			if (DebugDumpVocabulary.Value)
			{
				Debug.Log((object)("[VoiceCommandConfig] === Vocabulary (preview) ===\n" + string.Join(", ", vocabPreview.AllTokens.Take(120)) + ((vocabPreview.UniqueCount > 120) ? " ..." : "")));
			}
			if (!DebugDumpVocabularyToFile.Value)
			{
				return;
			}
			try
			{
				string text = Path.Combine(Paths.ConfigPath, "MageArena.voice.vocab.txt").Replace('\\', '/');
				File.WriteAllLines(text, vocabPreview.AllTokens.OrderBy<string, string>((string s) => s, StringComparer.OrdinalIgnoreCase));
				Debug.Log((object)("[VoiceCommandConfig] Vocabulary written: " + text));
			}
			catch (Exception arg)
			{
				Debug.LogWarning((object)$"[VoiceCommandConfig] Write vocab failed: {arg}");
			}
		}

		public static string GetResolvedModelPath()
		{
			string text = ModelRelativePath?.Value ?? string.Empty;
			if (string.IsNullOrWhiteSpace(text))
			{
				return text;
			}
			if (Path.IsPathRooted(text))
			{
				return NormalizePath(text);
			}
			if (!string.IsNullOrEmpty(_pluginDir))
			{
				return NormalizePath(Path.Combine(_pluginDir, text));
			}
			return NormalizePath(Path.GetFullPath(text));
		}

		private static string NormalizePath(string p)
		{
			return p.Replace('\\', '/');
		}

		public static string[] GetTokens(string raw)
		{
			return Tokenize(ExpandForUse(raw));
		}

		public static IReadOnlyDictionary<string, string[]> GetModuleSpellTokenMap()
		{
			return _moduleSpellTokenMap;
		}

		public static void RebuildModuleSpellMap()
		{
			_moduleSpellTokenMap.Clear();
			string text = ModuleSpellBindings?.Value ?? string.Empty;
			if (string.IsNullOrWhiteSpace(text))
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			string[] array = text.Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text2 in array)
			{
				string[] array2 = text2.Split(new char[1] { '=' }, 2);
				if (array2.Length != 2)
				{
					num2++;
					continue;
				}
				string text3 = array2[0].Trim();
				string raw = array2[1];
				if (string.IsNullOrWhiteSpace(text3))
				{
					num2++;
					continue;
				}
				string expanded = ExpandForUse(raw);
				string[] array3 = Tokenize(expanded);
				if (array3.Length == 0)
				{
					num2++;
				}
				else if (!_moduleSpellTokenMap.ContainsKey(text3))
				{
					_moduleSpellTokenMap[text3] = array3;
					num++;
				}
			}
			if (DebugEnabled.Value)
			{
				Debug.Log((object)$"[VoiceCommandConfig] ModuleSpellBindings parsed: ok={num}, bad={num2}, totalIds={_moduleSpellTokenMap.Count}");
			}
		}

		public static VocabPreview BuildVocabularyPreview()
		{
			List<string> all = new List<string>(128);
			addRange(FireballTokens);
			addRange(FrostBoltTokens);
			addRange(WormTokens);
			addRange(HoleTokens);
			addRange(MagicMissileTokens);
			addRange(MirrorTokens);
			addRange(RockTokens);
			addRange(WispTokens);
			addRange(BlastTokens);
			addRange(DivineTokens);
			addRange(BlinkTokens);
			addRange(ThunderboltTokens);
			int count = all.Count;
			HashSet<string> hashSet = new HashSet<string>(all, StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<string, string[]> item2 in _moduleSpellTokenMap)
			{
				string[] value = item2.Value;
				foreach (string item in value)
				{
					hashSet.Add(item);
				}
			}
			VocabPreview result = default(VocabPreview);
			result.TotalCount = count;
			result.UniqueCount = hashSet.Count;
			result.ModuleEntryCount = _moduleSpellTokenMap.Count;
			result.AllTokens = hashSet.OrderBy<string, string>((string s) => s, StringComparer.OrdinalIgnoreCase).ToList();
			return result;
			void addRange(IEnumerable<string> xs)
			{
				if (xs != null)
				{
					all.AddRange(xs);
				}
			}
		}

		public static Dictionary<string, string[]> GetAllSynonymSets()
		{
			Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase)
			{
				["Fireball"] = FireballTokens,
				["FrostBolt"] = FrostBoltTokens,
				["Worm"] = WormTokens,
				["Hole"] = HoleTokens,
				["MagicMissile"] = MagicMissileTokens,
				["Mirror"] = MirrorTokens,
				["Rock"] = RockTokens,
				["Wisp"] = WispTokens,
				["Blast"] = BlastTokens,
				["Divine"] = DivineTokens,
				["Blink"] = BlinkTokens,
				["Thunderbolt"] = ThunderboltTokens
			};
			foreach (KeyValuePair<string, string[]> item in _moduleSpellTokenMap)
			{
				dictionary[item.Key] = item.Value;
			}
			return dictionary;
		}

		private static void BuildT2SMap()
		{
			if (_mapBuilt && _t2s != null)
			{
				return;
			}
			_mapBuilt = true;
			_t2s = new Dictionary<char, char>(1024);
			string[] array = "術术 彈弹 鏡镜 聖圣 電电 擊击 閃闪 門门 槍枪 凍冻 靈灵 飛飞 間间 衝冲 開开 治治 癒愈 雷雷 魔魔 火火 冰冰 光光 爆爆 岩岩 石石 大大 黑黑 暗暗 影影 波波 動动 奇奇 蹟迹 芝芝 麻麻 出出 口口 進进 入入 出出 口口 巨巨 石石 精精 靈灵 靈灵 暗暗 影影 聖圣 光光 明明 奇奇 蹟迹 治治 癒愈 閃闪 現现 瞬瞬 移移 傳传 送送 雷雷 霆霆 閃闪 電电 擊击 霹霹 靂雳".Split(new char[4] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				if (text.Length == 2)
				{
					char key = text[0];
					char value = text[1];
					if (!_t2s.ContainsKey(key))
					{
						_t2s.Add(key, value);
					}
				}
			}
		}

		private static string ConvertT2S(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return s;
			}
			if (!_mapBuilt || _t2s == null)
			{
				BuildT2SMap();
			}
			char[] array = s.ToCharArray();
			for (int i = 0; i < array.Length; i++)
			{
				if (_t2s.TryGetValue(array[i], out var value))
				{
					array[i] = value;
				}
			}
			return new string(array);
		}

		private static string ExpandForUse(string raw)
		{
			if (string.IsNullOrWhiteSpace(raw))
			{
				return string.Empty;
			}
			List<string> list = (from t in TokenizeRaw(raw).Select(NormalizeForMatch)
				where !string.IsNullOrWhiteSpace(t)
				select t).ToList();
			ConfigEntry<bool> convertTraditionalToSimplified = ConvertTraditionalToSimplified;
			if (convertTraditionalToSimplified != null && convertTraditionalToSimplified.Value)
			{
				for (int i = 0; i < list.Count; i++)
				{
					list[i] = ConvertT2S(list[i]);
				}
			}
			HashSet<string> hashSet = new HashSet<string>(list, StringComparer.OrdinalIgnoreCase);
			ConfigEntry<bool> enableSingleCharExpansion = EnableSingleCharExpansion;
			if (enableSingleCharExpansion != null && enableSingleCharExpansion.Value)
			{
				foreach (string item in list)
				{
					if (IsAllCjk(item))
					{
						string text = item;
						for (int j = 0; j < text.Length; j++)
						{
							hashSet.Add(text[j].ToString());
						}
					}
				}
			}
			return string.Join(" ", hashSet.Where((string s2) => !string.IsNullOrWhiteSpace(s2)));
		}

		private static string[] TokenizeRaw(string raw)
		{
			return (from t in raw.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
				select t.Trim() into t
				where t.Length > 0
				select t).ToArray();
		}

		public static string[] Tokenize(string expanded)
		{
			if (string.IsNullOrWhiteSpace(expanded))
			{
				return Array.Empty<string>();
			}
			return (from t in expanded.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
				select t.Trim() into t
				where t.Length > 0
				select t).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
		}

		public static string NormalizeForMatch(string s)
		{
			if (string.IsNullOrWhiteSpace(s))
			{
				return string.Empty;
			}
			s = ToHalfWidth(s);
			s = RemovePunctuation(s);
			return s.Trim();
		}

		private static string ToHalfWidth(string input)
		{
			StringBuilder stringBuilder = new StringBuilder(input.Length);
			foreach (char c in input)
			{
				if (c == '\u3000')
				{
					stringBuilder.Append(' ');
				}
				else if (c >= '!' && c <= '~')
				{
					stringBuilder.Append((char)(c - 65248));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}

		private static string RemovePunctuation(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return s;
			}
			char[] array = s.ToCharArray();
			for (int i = 0; i < array.Length; i++)
			{
				if (_punct.Contains(array[i]))
				{
					array[i] = ' ';
				}
			}
			return new string(array);
		}

		private static bool IsAllCjk(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return false;
			}
			foreach (char c in s)
			{
				if (c < '一' || c > '\u9fff')
				{
					return false;
				}
			}
			return true;
		}

		private static ConfigEntry<string> BindCommand(ConfigFile config, string section, string key, string defaultValue, string description)
		{
			ConfigEntry<string> val = config.Bind<string>(section, key, defaultValue, description);
			_commandEntries.Add(val);
			TryHookSettingChanged<string>(val);
			return val;
		}

		private static void RefreshVocabularyCache()
		{
			VocabPreview vocabPreview = BuildVocabularyPreview();
			if (DebugEnabled.Value)
			{
				Debug.Log((object)$"[VoiceCommandConfig] Vocabulary refreshed: total={vocabPreview.TotalCount}, unique={vocabPreview.UniqueCount}");
			}
		}

		private static void TryHookSettingChanged<T>(ConfigEntry<T> entry)
		{
			try
			{
				entry.SettingChanged += delegate
				{
					if (entry is ConfigEntry<string> item)
					{
						if (_commandEntries.Contains(item))
						{
							RefreshVocabularyCache();
						}
					}
					else if (entry == ModelRelativePath)
					{
						if (DebugEnabled.Value)
						{
							Debug.Log((object)("[VoiceCommandConfig] Model path changed -> " + GetResolvedModelPath()));
						}
					}
					else if (entry == ModuleSpellBindings)
					{
						RebuildModuleSpellMap();
						RefreshVocabularyCache();
					}
				};
			}
			catch
			{
			}
		}
	}
}