Decompiled source of ValheimTW v1.2.9

plugins/ValheimTW.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimTW
{
	internal static class ConfigLocalizer
	{
		internal static readonly Regex Cjk = new Regex("[一-鿿]", RegexOptions.Compiled);

		private static readonly Dictionary<ModDictionary, int> Pending = new Dictionary<ModDictionary, int>();

		private static readonly List<ModDictionary> Buffer = new List<ModDictionary>(8);

		private const int PendingTimeoutMs = 60000;

		private static readonly Dictionary<string, Dictionary<string, string>> PristineDescByPath = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);

		internal static bool Enabled => LocEngine.ConfigUiEnabled;

		internal static string PristineDescriptionFor(string guid, string section, string key)
		{
			if (string.IsNullOrEmpty(guid) || string.IsNullOrEmpty(section) || string.IsNullOrEmpty(key))
			{
				return null;
			}
			try
			{
				if (!PristineDescByPath.TryGetValue(guid, out var value) || value == null)
				{
					return null;
				}
				string value2;
				return value.TryGetValue(section + "::" + key, out value2) ? value2 : null;
			}
			catch (Exception)
			{
				return null;
			}
		}

		internal static void RequestAll()
		{
			IList<ModDictionary> dictionaries = LocEngine.Dictionaries;
			for (int i = 0; i < dictionaries.Count; i++)
			{
				RequestFor(dictionaries[i]);
			}
		}

		internal static void RequestFor(ModDictionary d)
		{
			if (d != null && Enabled)
			{
				if (Pending.Count > 128)
				{
					Pending.Clear();
				}
				Pending[d] = Environment.TickCount;
			}
		}

		internal static void Drain()
		{
			if (Pending.Count == 0)
			{
				return;
			}
			if (!Enabled)
			{
				Pending.Clear();
				return;
			}
			Buffer.Clear();
			foreach (KeyValuePair<ModDictionary, int> item in Pending)
			{
				Buffer.Add(item.Key);
			}
			for (int i = 0; i < Buffer.Count; i++)
			{
				ModDictionary modDictionary = Buffer[i];
				int value;
				if (LocalizeOne(modDictionary))
				{
					Pending.Remove(modDictionary);
				}
				else if (Pending.TryGetValue(modDictionary, out value) && Environment.TickCount - value > 60000)
				{
					Pending.Remove(modDictionary);
				}
			}
		}

		private static bool LocalizeOne(ModDictionary d)
		{
			if (d == null || d.TargetGuids.Length == 0)
			{
				return true;
			}
			if (!d.HasConfigText)
			{
				return true;
			}
			ConfigFile val = null;
			bool flag = false;
			for (int i = 0; i < d.TargetGuids.Length; i++)
			{
				if (LocEngine.IsPluginLoaded(d.TargetGuids[i]))
				{
					if (Chainloader.PluginInfos.TryGetValue(d.TargetGuids[i], out var value) && value != null && (Object)(object)value.Instance != (Object)null && value.Instance.Config != null)
					{
						val = value.Instance.Config;
						break;
					}
					flag = true;
				}
			}
			if (val == null)
			{
				if (flag)
				{
					return !d.AnyPluginLoaded();
				}
				return true;
			}
			string activeGuid = d.FirstLoadedGuid() ?? d.TargetGuids[0];
			int num = RestoreDescriptions(val, d, activeGuid);
			if (num > 0)
			{
				val.Save();
				ValheimTWPlugin.Log($"已把 {d.TargetName} 的 {num} 条被我方污染的配置说明恢复为英文原文。");
			}
			return true;
		}

		private static int RestoreDescriptions(ConfigFile cfg, ModDictionary d, string activeGuid)
		{
			int num = 0;
			Dictionary<string, string> dictionary = null;
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in cfg)
			{
				ConfigEntryBase value = item.Value;
				if (value == null)
				{
					continue;
				}
				ConfigDescription description = value.Description;
				if (description == null || string.IsNullOrEmpty(description.Description))
				{
					continue;
				}
				if (!Cjk.IsMatch(description.Description))
				{
					Snapshot(activeGuid, item.Key.Section, item.Key.Key, description.Description);
					continue;
				}
				string text = ((PristineDescByPath.TryGetValue(activeGuid, out var value2) && value2 != null && value2.TryGetValue(item.Key.Section + "::" + item.Key.Key, out var value3)) ? value3 : null);
				if (string.IsNullOrEmpty(text) || Cjk.IsMatch(text))
				{
					if (dictionary == null)
					{
						dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
						foreach (KeyValuePair<string, string> configDescription in d.ConfigDescriptions)
						{
							string text2 = ModDictionary.StripSyncTail(configDescription.Value);
							if (!string.IsNullOrEmpty(text2) && Cjk.IsMatch(text2))
							{
								if (dictionary.ContainsKey(text2))
								{
									dictionary[text2] = null;
								}
								else
								{
									dictionary[text2] = configDescription.Key;
								}
							}
						}
					}
					if (!dictionary.TryGetValue(description.Description, out var value4) || string.IsNullOrEmpty(value4))
					{
						continue;
					}
					text = value4;
				}
				if (string.IsNullOrEmpty(text) || Cjk.IsMatch(text))
				{
					continue;
				}
				text = text.Replace("\r\n", " ").Replace('\r', ' ').Replace('\n', ' ');
				bool flag = false;
				string text3 = text;
				for (int i = 0; i < text3.Length; i++)
				{
					if (text3[i] < ' ')
					{
						flag = true;
						break;
					}
				}
				if (!flag && TrySetDescription(value, text))
				{
					num++;
				}
			}
			return num;
		}

		private static void Snapshot(string guid, string section, string key, string english)
		{
			if (string.IsNullOrEmpty(guid) || string.IsNullOrEmpty(english) || string.IsNullOrEmpty(section) || string.IsNullOrEmpty(key))
			{
				return;
			}
			try
			{
				if (!PristineDescByPath.TryGetValue(guid, out var value) || value == null)
				{
					value = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
					PristineDescByPath[guid] = value;
				}
				string key2 = section + "::" + key;
				if (!value.ContainsKey(key2) && value.Count < 8000)
				{
					value[key2] = english;
				}
			}
			catch (Exception)
			{
			}
		}

		private static bool TrySetDescription(ConfigEntryBase entry, string text)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			try
			{
				PropertyInfo property = ((object)entry).GetType().GetProperty("Description", BindingFlags.Instance | BindingFlags.Public);
				if (property != null && property.PropertyType == typeof(string))
				{
					MethodInfo setMethod = property.GetSetMethod(nonPublic: false);
					if (setMethod != null)
					{
						setMethod.Invoke(entry, new object[1] { text });
						return true;
					}
				}
				ConfigDescription description = entry.Description;
				ConfigDescription val = new ConfigDescription(text, description.AcceptableValues, description.Tags);
				PropertyInfo property2 = typeof(ConfigEntryBase).GetProperty("Description", BindingFlags.Instance | BindingFlags.Public);
				if (property2 != null)
				{
					MethodInfo setMethod2 = property2.GetSetMethod(nonPublic: true);
					if (setMethod2 != null)
					{
						setMethod2.Invoke(entry, new object[1] { val });
						return true;
					}
				}
				FieldInfo field = typeof(ConfigEntryBase).GetField("<Description>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field != null)
				{
					field.SetValue(entry, val);
					return true;
				}
			}
			catch (Exception)
			{
			}
			return false;
		}

		internal static void OnConfigSaved(ConfigFile file)
		{
			if (file == null || !Enabled)
			{
				return;
			}
			string configFilePath = file.ConfigFilePath;
			if (string.IsNullOrEmpty(configFilePath))
			{
				return;
			}
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(configFilePath);
			ModDictionary modDictionary = LocEngine.Get(fileNameWithoutExtension);
			if (modDictionary == null || !modDictionary.Active || !modDictionary.HasConfigText)
			{
				return;
			}
			try
			{
				RestoreDescriptions(file, modDictionary, fileNameWithoutExtension);
			}
			catch (Exception)
			{
			}
		}
	}
	internal static class DictFormat
	{
		internal sealed class SrcLookup
		{
			private readonly Dictionary<string, string> _exact = new Dictionary<string, string>(StringComparer.Ordinal);

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

			internal void Add(string src, string zh)
			{
				if (!string.IsNullOrEmpty(src) && !string.IsNullOrEmpty(zh) && !string.Equals(src, zh, StringComparison.Ordinal))
				{
					if (!_exact.ContainsKey(src))
					{
						_exact[src] = zh;
					}
					string text = NormalizeWhitespace(src);
					if (!string.IsNullOrEmpty(text) && !_norm.ContainsKey(text))
					{
						_norm[text] = zh;
					}
				}
			}

			internal bool Has(string src)
			{
				if (string.IsNullOrEmpty(src))
				{
					return false;
				}
				if (_exact.ContainsKey(src))
				{
					return true;
				}
				string text = NormalizeWhitespace(src);
				if (!string.IsNullOrEmpty(text))
				{
					return _norm.ContainsKey(text);
				}
				return false;
			}

			internal bool TryGet(string src, out string zh)
			{
				zh = null;
				if (string.IsNullOrEmpty(src))
				{
					return false;
				}
				if (_exact.TryGetValue(src, out zh))
				{
					return true;
				}
				string text = NormalizeWhitespace(src);
				if (!string.IsNullOrEmpty(text) && _norm.TryGetValue(text, out zh))
				{
					return true;
				}
				zh = null;
				return false;
			}
		}

		private static readonly Dictionary<string, string> CfgMarkers = new Dictionary<string, string>(StringComparer.Ordinal)
		{
			["Server"] = "服务器",
			["Yours"] = "你的",
			["Key, yours"] = "按键,你的",
			["Yours, follows the server while connected"] = "你的,联机时跟随服务器",
			["Synced with Server"] = "已与服务器同步",
			["Not Synced with Server"] = "未与服务器同步",
			["Client side Config"] = "客户端配置",
			["Local setting, not synced"] = "本地设置,不同步"
		};

		private static readonly Regex CfgMarkRx = new Regex("^\\[([^\\[\\]]{1,80})\\]\\s");

		public static JObject Transform(JObject doc)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			JToken obj = doc["meta"];
			JObject val = (JObject)(object)((obj is JObject) ? obj : null);
			if (val != null)
			{
				val["schema"] = JToken.op_Implicit(4);
			}
			JToken obj2 = doc["objects"];
			JObject val2 = (JObject)(object)((obj2 is JObject) ? obj2 : null);
			if (val2 != null)
			{
				doc["objects"] = (JToken)(object)TransformObjects(val2);
			}
			JToken obj3 = doc["strings"];
			JObject val3 = (JObject)(object)((obj3 is JObject) ? obj3 : null);
			if (val3 != null)
			{
				JObject val4 = new JObject();
				foreach (KeyValuePair<string, JToken> item in val3)
				{
					JToken value = item.Value;
					JObject val5 = (JObject)(object)((value is JObject) ? value : null);
					if (val5 != null && val5["from"] != null)
					{
						JObject val6 = new JObject();
						if (val5["src"] != null)
						{
							val6["src"] = val5["src"];
						}
						if (val5["zh"] != null)
						{
							val6["zh"] = val5["zh"];
						}
						foreach (KeyValuePair<string, JToken> item2 in val5)
						{
							if (item2.Key != "src" && item2.Key != "zh" && item2.Key != "from")
							{
								val6[item2.Key] = item2.Value;
							}
						}
						val4[item.Key] = (JToken)(object)val6;
					}
					else
					{
						val4[item.Key] = item.Value;
					}
				}
				doc["strings"] = (JToken)(object)val4;
			}
			return doc;
		}

		private static JObject TransformObjects(JObject old)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f6: Expected O, but got Unknown
			//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c2: Expected O, but got Unknown
			//IL_03c7: Expected O, but got Unknown
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Expected O, but got Unknown
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Expected O, but got Unknown
			JObject val = new JObject();
			JObject val2 = new JObject();
			Dictionary<string, JObject> dictionary = new Dictionary<string, JObject>();
			foreach (KeyValuePair<string, JToken> item in old)
			{
				JToken value = item.Value;
				JObject val3 = (JObject)(object)((value is JObject) ? value : null);
				if (val3 == null)
				{
					continue;
				}
				string text = ((string)val3["type"]) ?? "";
				string key = item.Key;
				if (text == "config_section")
				{
					string text2 = (key.StartsWith("cfgsec:", StringComparison.Ordinal) ? key.Substring(7) : key);
					JToken obj = val3["fields"];
					string text3 = ZhOf((obj != null) ? obj[(object)"name"] : null);
					val[text2] = JToken.op_Implicit(text3 ?? "");
				}
				else if (text == "config" && key.StartsWith("cfg:", StringComparison.Ordinal))
				{
					string text4 = key.Substring(4);
					JToken obj2 = val3["fields"];
					object obj3 = ((object)((obj2 is JObject) ? obj2 : null)) ?? ((object)new JObject());
					JObject val4 = new JObject();
					foreach (KeyValuePair<string, JToken> item2 in (JObject)obj3)
					{
						JToken value2 = item2.Value;
						JObject val5 = (JObject)(object)((value2 is JObject) ? value2 : null);
						if (val5 != null)
						{
							string text5 = (string)val5["ty"];
							if (text5 == "binding" || text5 == "storage")
							{
								continue;
							}
							if (item2.Key == "name")
							{
								val4["name"] = JToken.op_Implicit(ZhOf((JToken)(object)val5) ?? "");
								continue;
							}
							if (item2.Key == "desc")
							{
								JObject val6 = new JObject();
								if (val5["src"] != null)
								{
									val6["src"] = val5["src"];
								}
								val6["zh"] = val5["zh"] ?? JToken.op_Implicit("");
								val4["desc"] = (JToken)(object)val6;
								continue;
							}
							if (item2.Key.StartsWith("value:", StringComparison.Ordinal))
							{
								string text6 = ((string)val5["src"]) ?? item2.Key.Substring(6);
								val4["value:" + text6] = JToken.op_Implicit(ZhOf((JToken)(object)val5) ?? "");
								continue;
							}
							JObject val7 = new JObject();
							if (val5["src"] != null)
							{
								val7["src"] = val5["src"];
							}
							val7["zh"] = val5["zh"] ?? JToken.op_Implicit("");
							foreach (KeyValuePair<string, JToken> item3 in val5)
							{
								if (item3.Key != "src" && item3.Key != "zh" && item3.Key != "from")
								{
									val7[item3.Key] = item3.Value;
								}
							}
							val4[item2.Key] = (JToken)(object)val7;
						}
						else
						{
							val4[item2.Key] = item2.Value;
						}
					}
					val2[text4] = (JToken)(object)val4;
				}
				else
				{
					if (!dictionary.TryGetValue(text, out var value3))
					{
						JObject val8 = new JObject();
						JObject val9 = val8;
						dictionary[text] = val8;
						value3 = val9;
					}
					value3[key] = (JToken)(object)val3;
				}
			}
			JObject val10 = new JObject();
			if (((JContainer)val).Count > 0)
			{
				val10["config_section"] = (JToken)(object)val;
			}
			if (((JContainer)val2).Count > 0)
			{
				val10["config"] = (JToken)(object)val2;
			}
			foreach (KeyValuePair<string, JObject> item4 in dictionary)
			{
				if (((JContainer)item4.Value).Count > 0)
				{
					val10[item4.Key] = (JToken)(object)item4.Value;
				}
			}
			return val10;
		}

		private static string ZhOf(JToken e)
		{
			JValue val = (JValue)(object)((e is JValue) ? e : null);
			if (val != null)
			{
				return ((object)val).ToString();
			}
			JToken obj = ((e is JObject) ? e : null);
			if (obj == null)
			{
				return null;
			}
			return ((object)((JObject)obj)["zh"])?.ToString();
		}

		public static int CarryOver(JObject doc, JObject official)
		{
			if (doc == null || official == null)
			{
				return 0;
			}
			SrcLookup srcLookup = BuildSrcLookup(official, withStrings: true, withObjects: false);
			SrcLookup srcLookup2 = BuildSrcLookup(official, withStrings: false);
			if (srcLookup == null && srcLookup2 == null)
			{
				return 0;
			}
			Dictionary<string, KeyValuePair<string, string>> dictionary = new Dictionary<string, KeyValuePair<string, string>>(StringComparer.Ordinal);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			JToken obj = official["strings"];
			JObject val = (JObject)(object)((obj is JObject) ? obj : null);
			if (val != null)
			{
				foreach (KeyValuePair<string, JToken> item in val)
				{
					JToken value = item.Value;
					JObject val2 = (JObject)(object)((value is JObject) ? value : null);
					string text;
					string value2;
					if (val2 != null)
					{
						text = ((string)val2["src"]) ?? item.Key;
						value2 = (string)val2["zh"];
					}
					else
					{
						text = item.Key;
						value2 = ((object)item.Value)?.ToString();
						hashSet.Add(item.Key);
					}
					if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(value2))
					{
						dictionary[item.Key] = new KeyValuePair<string, string>(text, value2);
					}
				}
			}
			int num = 0;
			JToken obj2 = doc["strings"];
			JObject val3 = (JObject)(object)((obj2 is JObject) ? obj2 : null);
			if (val3 != null)
			{
				foreach (KeyValuePair<string, JToken> item2 in val3)
				{
					JToken value3 = item2.Value;
					JObject val4 = (JObject)(object)((value3 is JObject) ? value3 : null);
					if (val4 != null && string.IsNullOrEmpty((string)val4["zh"]) && !IsHexColorValue(((string)val4["src"]) ?? item2.Key))
					{
						string a = ((string)val4["src"]) ?? item2.Key;
						string zh;
						if (dictionary.TryGetValue(item2.Key, out var value4) && (hashSet.Contains(item2.Key) || SrcEquivalent(a, value4.Key)))
						{
							val4["zh"] = JToken.op_Implicit(value4.Value);
							num++;
						}
						else if (srcLookup != null && srcLookup.TryGet(((string)val4["src"]) ?? item2.Key, out zh))
						{
							val4["zh"] = JToken.op_Implicit(zh);
							num++;
						}
					}
				}
			}
			if (srcLookup2 != null)
			{
				JToken obj3 = doc["objects"];
				JObject val5 = (JObject)(object)((obj3 is JObject) ? obj3 : null);
				if (val5 != null)
				{
					JToken obj4 = official["objects"];
					JObject val6 = (JObject)(object)((obj4 is JObject) ? obj4 : null);
					if (val6 != null)
					{
						foreach (KeyValuePair<string, JToken> item3 in val5)
						{
							JToken value5 = item3.Value;
							JObject val7 = (JObject)(object)((value5 is JObject) ? value5 : null);
							if (val7 == null)
							{
								continue;
							}
							JToken obj5 = val6[item3.Key];
							JObject val8 = (JObject)(object)((obj5 is JObject) ? obj5 : null);
							if (val8 == null)
							{
								continue;
							}
							if (item3.Key == "config_section")
							{
								foreach (KeyValuePair<string, JToken> item4 in val7)
								{
									JToken value6 = item4.Value;
									JValue val9 = (JValue)(object)((value6 is JValue) ? value6 : null);
									if (val9 != null && string.IsNullOrEmpty(((object)val9).ToString()))
									{
										JToken obj6 = val8[item4.Key];
										JValue val10 = (JValue)(object)((obj6 is JValue) ? obj6 : null);
										if (val10 != null && !string.IsNullOrEmpty(((object)val10).ToString()))
										{
											val7[item4.Key] = JToken.op_Implicit(((object)val10).ToString());
											num++;
										}
									}
								}
								continue;
							}
							if (item3.Key == "config")
							{
								foreach (KeyValuePair<string, JToken> item5 in val7)
								{
									JToken value7 = item5.Value;
									JObject val11 = (JObject)(object)((value7 is JObject) ? value7 : null);
									JToken obj7 = val8[item5.Key];
									JObject val12 = (JObject)(object)((obj7 is JObject) ? obj7 : null);
									if (val11 == null || val12 == null)
									{
										continue;
									}
									JToken obj8 = val11["name"];
									JValue val13 = (JValue)(object)((obj8 is JValue) ? obj8 : null);
									if (val13 != null && string.IsNullOrEmpty(((object)val13).ToString()))
									{
										string text2 = (string)val12["name"];
										if (!string.IsNullOrEmpty(text2))
										{
											val11["name"] = JToken.op_Implicit(text2);
											num++;
										}
									}
									JToken obj9 = val11["desc"];
									JObject val14 = (JObject)(object)((obj9 is JObject) ? obj9 : null);
									JToken obj10 = val12["desc"];
									JObject val15 = (JObject)(object)((obj10 is JObject) ? obj10 : null);
									string text3 = ((val15 != null) ? ((string)val15["zh"]) : null);
									if (val14 != null)
									{
										if (string.IsNullOrEmpty((string)val14["zh"]) && !string.IsNullOrEmpty(text3) && DescSame((string)val14["src"], (val15 != null) ? ((string)val15["src"]) : null, out var addMark))
										{
											if (!string.IsNullOrEmpty(addMark) && !text3.StartsWith("[", StringComparison.Ordinal))
											{
												string text4 = MarkerZh(addMark);
												if (!string.IsNullOrEmpty(text4))
												{
													text3 = text4 + " " + text3;
												}
											}
											val14["zh"] = JToken.op_Implicit(text3);
											num++;
										}
									}
									else if (!string.IsNullOrEmpty(text3))
									{
										val11["desc"] = ((JToken)val15).DeepClone();
										num++;
									}
									foreach (KeyValuePair<string, JToken> item6 in val11)
									{
										if (!item6.Key.StartsWith("value:", StringComparison.Ordinal))
										{
											continue;
										}
										JToken value8 = item6.Value;
										JValue val16 = (JValue)(object)((value8 is JValue) ? value8 : null);
										if (val16 != null && string.IsNullOrEmpty(((object)val16).ToString()) && !IsHexColorValue(((object)val16).ToString()))
										{
											string text5 = (string)val12[item6.Key];
											if (!string.IsNullOrEmpty(text5))
											{
												val11[item6.Key] = JToken.op_Implicit(text5);
												num++;
											}
										}
									}
								}
								continue;
							}
							foreach (KeyValuePair<string, JToken> item7 in val7)
							{
								JToken value9 = item7.Value;
								JObject val17 = (JObject)(object)((value9 is JObject) ? value9 : null);
								if (val17 == null)
								{
									continue;
								}
								foreach (KeyValuePair<string, JToken> item8 in val17)
								{
									JToken value10 = item8.Value;
									JObject val18 = (JObject)(object)((value10 is JObject) ? value10 : null);
									if (val18 != null && string.IsNullOrEmpty((string)val18["zh"]) && srcLookup2.TryGet((string)val18["src"], out var zh2))
									{
										val18["zh"] = JToken.op_Implicit(zh2);
										num++;
									}
								}
							}
						}
					}
				}
			}
			return num;
		}

		internal static SrcLookup BuildSrcLookup(JObject official, bool withStrings = true, bool withObjects = true)
		{
			if (official == null)
			{
				return null;
			}
			JToken obj = official["meta"];
			JToken obj2 = ((obj is JObject) ? obj : null);
			if ((int?)((obj2 != null) ? ((JObject)obj2)["schema"] : null) != 4)
			{
				return null;
			}
			SrcLookup srcLookup = new SrcLookup();
			if (withStrings)
			{
				JToken obj3 = official["strings"];
				JObject val = (JObject)(object)((obj3 is JObject) ? obj3 : null);
				if (val != null)
				{
					foreach (KeyValuePair<string, JToken> item in val)
					{
						JToken value = item.Value;
						JValue val2 = (JValue)(object)((value is JValue) ? value : null);
						if (val2 != null)
						{
							srcLookup.Add(item.Key, ((object)val2).ToString());
							continue;
						}
						JToken value2 = item.Value;
						JObject val3 = (JObject)(object)((value2 is JObject) ? value2 : null);
						if (val3 != null)
						{
							srcLookup.Add(((string)val3["src"]) ?? item.Key, (string)val3["zh"]);
						}
					}
				}
			}
			if (withObjects)
			{
				JToken obj4 = official["objects"];
				JObject val4 = (JObject)(object)((obj4 is JObject) ? obj4 : null);
				if (val4 != null)
				{
					foreach (KeyValuePair<string, JToken> item2 in val4)
					{
						JToken value3 = item2.Value;
						JObject val5 = (JObject)(object)((value3 is JObject) ? value3 : null);
						if (val5 == null)
						{
							continue;
						}
						if (item2.Key == "config_section")
						{
							foreach (KeyValuePair<string, JToken> item3 in val5)
							{
								string key = item3.Key;
								JToken value4 = item3.Value;
								JValue val6 = (JValue)(object)((value4 is JValue) ? value4 : null);
								string? zh;
								if (val6 == null)
								{
									JToken value5 = item3.Value;
									JToken obj5 = ((value5 is JObject) ? value5 : null);
									object obj6;
									if (obj5 == null)
									{
										obj6 = null;
									}
									else
									{
										JToken obj7 = ((JObject)obj5)["name"];
										obj6 = ((obj7 != null) ? obj7[(object)"zh"] : null);
									}
									zh = (string)(JToken)obj6;
								}
								else
								{
									zh = ((object)val6).ToString();
								}
								srcLookup.Add(key, zh);
							}
							continue;
						}
						foreach (KeyValuePair<string, JToken> item4 in val5)
						{
							JToken value6 = item4.Value;
							JObject val7 = (JObject)(object)((value6 is JObject) ? value6 : null);
							if (val7 == null)
							{
								continue;
							}
							if (item2.Key == "config")
							{
								JToken obj8 = val7["name"];
								JValue val8 = (JValue)(object)((obj8 is JValue) ? obj8 : null);
								if (val8 != null)
								{
									srcLookup.Add(KeyTail(item4.Key), ((object)val8).ToString());
								}
								else
								{
									JToken obj9 = val7["name"];
									JObject val9 = (JObject)(object)((obj9 is JObject) ? obj9 : null);
									if (val9 != null)
									{
										srcLookup.Add(((string)val9["src"]) ?? KeyTail(item4.Key), (string)val9["zh"]);
									}
								}
								JToken obj10 = val7["desc"];
								JObject val10 = (JObject)(object)((obj10 is JObject) ? obj10 : null);
								if (val10 != null)
								{
									srcLookup.Add((string)val10["src"], (string)val10["zh"]);
								}
								foreach (KeyValuePair<string, JToken> item5 in val7)
								{
									if (item5.Key.StartsWith("value:", StringComparison.Ordinal))
									{
										string src = item5.Key.Substring(6);
										JToken value7 = item5.Value;
										JValue val11 = (JValue)(object)((value7 is JValue) ? value7 : null);
										string? zh2;
										if (val11 == null)
										{
											JToken value8 = item5.Value;
											JToken obj11 = ((value8 is JObject) ? value8 : null);
											zh2 = (string)((obj11 != null) ? ((JObject)obj11)["zh"] : null);
										}
										else
										{
											zh2 = ((object)val11).ToString();
										}
										srcLookup.Add(src, zh2);
									}
								}
								continue;
							}
							JToken obj12 = val7["fields"];
							JObject val12 = (JObject)(object)((obj12 is JObject) ? obj12 : null);
							if (val12 == null)
							{
								continue;
							}
							foreach (KeyValuePair<string, JToken> item6 in val12)
							{
								JToken value9 = item6.Value;
								JObject val13 = (JObject)(object)((value9 is JObject) ? value9 : null);
								if (val13 != null)
								{
									srcLookup.Add((string)val13["src"], (string)val13["zh"]);
									continue;
								}
								JToken obj13 = val7["zh"];
								JValue val14 = (JValue)(object)((obj13 is JValue) ? obj13 : null);
								if (val14 != null)
								{
									srcLookup.Add((string)val7["src"], ((object)val14).ToString());
								}
							}
						}
					}
				}
			}
			return srcLookup;
		}

		private static string KeyTail(string key)
		{
			int num = key.LastIndexOf("::", StringComparison.Ordinal);
			if (num < 0)
			{
				return key;
			}
			return key.Substring(num + 2);
		}

		public static int CarryOverPatterns(JObject doc, JObject official, out int collisions)
		{
			collisions = 0;
			if (doc == null || official == null)
			{
				return 0;
			}
			JToken obj = official["meta"];
			JToken obj2 = ((obj is JObject) ? obj : null);
			if ((int?)((obj2 != null) ? ((JObject)obj2)["schema"] : null) != 4)
			{
				return 0;
			}
			JToken obj3 = doc["patterns"];
			JArray val = (JArray)(object)((obj3 is JArray) ? obj3 : null);
			if (val != null)
			{
				JToken obj4 = official["patterns"];
				JArray val2 = (JArray)(object)((obj4 is JArray) ? obj4 : null);
				if (val2 != null)
				{
					Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
					Dictionary<string, string> dictionary2 = new Dictionary<string, string>(StringComparer.Ordinal);
					Dictionary<string, string> dictionary3 = new Dictionary<string, string>(StringComparer.Ordinal);
					Dictionary<string, string> dictionary4 = new Dictionary<string, string>(StringComparer.Ordinal);
					foreach (JToken item in val2)
					{
						JObject val3 = (JObject)(object)((item is JObject) ? item : null);
						if (val3 == null)
						{
							continue;
						}
						string text = (string)val3["template"];
						string text2 = (string)val3["replacement"];
						if (string.IsNullOrEmpty(text))
						{
							string text3 = (string)val3["pattern"];
							if (!string.IsNullOrEmpty(text3) && !string.IsNullOrEmpty(text2) && !dictionary4.ContainsKey(text3))
							{
								dictionary4[text3] = text2;
							}
						}
						else
						{
							if (string.IsNullOrEmpty(text2) || !SlotSetOk(text, text2))
							{
								continue;
							}
							if (!dictionary.ContainsKey(text))
							{
								dictionary[text] = text2;
							}
							string text4 = ExportFilters.NormalizeTemplateKey(text);
							if (string.IsNullOrEmpty(text4))
							{
								continue;
							}
							if (dictionary2.TryGetValue(text4, out var value))
							{
								if (!string.Equals(value, text2, StringComparison.Ordinal))
								{
									collisions++;
								}
							}
							else
							{
								dictionary2[text4] = text2;
								dictionary3[text4] = SlotSignature(text);
							}
						}
					}
					if (dictionary.Count == 0 && dictionary4.Count == 0)
					{
						return 0;
					}
					int num = 0;
					{
						foreach (JToken item2 in val)
						{
							JObject val4 = (JObject)(object)((item2 is JObject) ? item2 : null);
							if (val4 == null || !string.IsNullOrEmpty((string)val4["replacement"]))
							{
								continue;
							}
							string text5 = (string)val4["template"];
							if (string.IsNullOrEmpty(text5))
							{
								string text6 = (string)val4["pattern"];
								if (!string.IsNullOrEmpty(text6) && dictionary4.TryGetValue(text6, out var value2))
								{
									val4["replacement"] = JToken.op_Implicit(value2);
									num++;
								}
								continue;
							}
							if (!dictionary.TryGetValue(text5, out var value3))
							{
								string text7 = ExportFilters.NormalizeTemplateKey(text5);
								if (string.IsNullOrEmpty(text7) || !dictionary2.TryGetValue(text7, out value3) || dictionary3[text7] != SlotSignature(text5))
								{
									continue;
								}
							}
							if (SlotSetOk(text5, value3))
							{
								val4["replacement"] = JToken.op_Implicit(value3);
								num++;
							}
						}
						return num;
					}
				}
			}
			return 0;
		}

		private static HashSet<int> SlotNumbers(string s)
		{
			HashSet<int> hashSet = new HashSet<int>();
			if (string.IsNullOrEmpty(s))
			{
				return hashSet;
			}
			foreach (Match item in Regex.Matches(s, "\\{(\\d+)[^}]*\\}"))
			{
				if (int.TryParse(item.Groups[1].Value, out var result))
				{
					hashSet.Add(result);
				}
			}
			return hashSet;
		}

		private static string SlotSignature(string tpl)
		{
			List<int> list = new List<int>(SlotNumbers(tpl));
			list.Sort();
			return string.Join(",", list);
		}

		private static bool SlotSetOk(string tpl, string rep)
		{
			HashSet<int> hashSet = SlotNumbers(tpl);
			if (hashSet.Count == 0)
			{
				return true;
			}
			foreach (int item in SlotNumbers(rep))
			{
				if (!hashSet.Contains(item))
				{
					return false;
				}
			}
			return true;
		}

		private static string NormalizeWhitespace(string s)
		{
			if (!string.IsNullOrEmpty(s))
			{
				return Regex.Replace(s, "\\s+", " ").Trim();
			}
			return s;
		}

		private static void SplitCfgMarker(string s, out string mark, out string body)
		{
			mark = null;
			body = s ?? "";
			if (!string.IsNullOrEmpty(s))
			{
				Match match = CfgMarkRx.Match(s);
				if (match.Success && CfgMarkers.ContainsKey(match.Groups[1].Value))
				{
					mark = s.Substring(0, match.Length).Trim();
					body = s.Substring(match.Length);
				}
			}
		}

		private static bool DescSame(string a, string b, out string addMark)
		{
			addMark = null;
			if (SrcEquivalent(a, b))
			{
				return true;
			}
			SplitCfgMarker(a, out var mark, out var body);
			SplitCfgMarker(b, out var mark2, out var body2);
			if (!SrcEquivalent(body, body2))
			{
				return false;
			}
			if (string.Equals(mark, mark2, StringComparison.Ordinal))
			{
				return true;
			}
			if (!string.IsNullOrEmpty(mark) && string.IsNullOrEmpty(mark2))
			{
				addMark = mark;
				return true;
			}
			if (string.IsNullOrEmpty(mark) && !string.IsNullOrEmpty(mark2))
			{
				addMark = null;
				return true;
			}
			return false;
		}

		private static string MarkerZh(string mark)
		{
			if (string.IsNullOrEmpty(mark))
			{
				return null;
			}
			string text = ((mark.Length >= 2 && mark[0] == '[' && mark[mark.Length - 1] == ']') ? mark.Substring(1, mark.Length - 2) : mark);
			string value;
			return "[" + (CfgMarkers.TryGetValue(text, out value) ? value : text) + "]";
		}

		private static bool SrcEquivalent(string a, string b)
		{
			if (string.Equals(a, b, StringComparison.Ordinal))
			{
				return true;
			}
			return string.Equals(NormalizeWhitespace(a), NormalizeWhitespace(b), StringComparison.Ordinal);
		}

		internal static bool SrcEquivalentShared(string a, string b)
		{
			return SrcEquivalent(a, b);
		}

		internal static bool IsHexColorValue(string s)
		{
			if (string.IsNullOrEmpty(s) || s[0] != '#')
			{
				return false;
			}
			if (s.Length < 4 || s.Length > 9)
			{
				return false;
			}
			for (int i = 1; i < s.Length; i++)
			{
				char c = s[i];
				if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F'))
				{
					return false;
				}
			}
			return true;
		}

		private static bool TokenField(JToken t, string key, out string src, out string zh)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Invalid comparison between Unknown and I4
			src = key;
			zh = null;
			JObject val = (JObject)(object)((t is JObject) ? t : null);
			if (val != null)
			{
				src = ((string)val["src"]) ?? key;
				zh = (string)val["zh"];
				return true;
			}
			JValue val2 = (JValue)(object)((t is JValue) ? t : null);
			if (val2 != null && (int)((JToken)val2).Type == 8)
			{
				zh = ((object)val2).ToString();
				return true;
			}
			return false;
		}

		public static int CarryOverTokens(JObject doc, JObject official)
		{
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Expected O, but got Unknown
			if (doc == null || official == null)
			{
				return 0;
			}
			JToken obj = official["meta"];
			JToken obj2 = ((obj is JObject) ? obj : null);
			if ((int?)((obj2 != null) ? ((JObject)obj2)["schema"] : null) != 4)
			{
				return 0;
			}
			JToken obj3 = doc["tokens"];
			JObject val = (JObject)(object)((obj3 is JObject) ? obj3 : null);
			JToken obj4 = official["tokens"];
			JObject val2 = (JObject)(object)((obj4 is JObject) ? obj4 : null);
			if (val == null || val2 == null)
			{
				return 0;
			}
			int num = 0;
			foreach (KeyValuePair<string, JToken> item in val)
			{
				if (TokenField(item.Value, item.Key, out var src, out var zh) && string.IsNullOrEmpty(zh) && TokenField(val2[item.Key], item.Key, out var src2, out var zh2) && !string.IsNullOrEmpty(zh2) && (!(val2[item.Key] is JObject) || SrcEquivalent(src2, src)))
				{
					JToken value = item.Value;
					JObject val3 = (JObject)(object)((value is JObject) ? value : null);
					if (val3 != null)
					{
						val3["zh"] = JToken.op_Implicit(zh2);
					}
					else
					{
						val[item.Key] = (JToken)new JObject
						{
							["src"] = JToken.op_Implicit(src ?? src2),
							["zh"] = JToken.op_Implicit(zh2)
						};
					}
					num++;
				}
			}
			return num;
		}

		public static int UnionOfficial(JObject doc, JObject official, out string note)
		{
			//IL_051d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0524: Expected O, but got Unknown
			//IL_067a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0681: Expected O, but got Unknown
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Expected O, but got Unknown
			//IL_0761: Unknown result type (might be due to invalid IL or missing references)
			//IL_0768: Expected O, but got Unknown
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_037a: Expected O, but got Unknown
			//IL_058d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0592: Unknown result type (might be due to invalid IL or missing references)
			//IL_05af: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c6: Expected O, but got Unknown
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Expected O, but got Unknown
			//IL_0459: Unknown result type (might be due to invalid IL or missing references)
			//IL_045e: Unknown result type (might be due to invalid IL or missing references)
			//IL_048a: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a4: Expected O, but got Unknown
			note = "";
			if (doc == null || official == null)
			{
				return 0;
			}
			JToken obj = official["meta"];
			JToken obj2 = ((obj is JObject) ? obj : null);
			if ((int?)((obj2 != null) ? ((JObject)obj2)["schema"] : null) != 4)
			{
				return 0;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			int num6 = 0;
			int num7 = 0;
			int num8 = 0;
			JToken obj3 = doc["strings"];
			JObject val = (JObject)(object)((obj3 is JObject) ? obj3 : null);
			if (val != null)
			{
				JToken obj4 = official["strings"];
				JObject val2 = (JObject)(object)((obj4 is JObject) ? obj4 : null);
				if (val2 != null)
				{
					foreach (KeyValuePair<string, JToken> item in val2)
					{
						JToken value = item.Value;
						JObject val3 = (JObject)(object)((value is JObject) ? value : null);
						string text;
						string text2;
						if (val3 != null)
						{
							text = ((string)val3["src"]) ?? item.Key;
							text2 = (string)val3["zh"];
						}
						else
						{
							text = item.Key;
							JToken value2 = item.Value;
							text2 = ((object)((value2 is JValue) ? value2 : null))?.ToString();
						}
						if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2) || string.Equals(text, text2, StringComparison.Ordinal) || IsHexColorValue(text))
						{
							continue;
						}
						if (val[text] != null)
						{
							num4++;
							continue;
						}
						JToken val4 = val2[text];
						if (val4 != null)
						{
							JObject val5 = (JObject)(object)((val4 is JObject) ? val4 : null);
							string text3 = ((val5 != null) ? ((string)val5["zh"]) : ((object)((val4 is JValue) ? val4 : null))?.ToString());
							if (!string.IsNullOrEmpty(text3))
							{
								text2 = text3;
							}
						}
						string text4 = (HasSlot(text) ? "legacy-slot" : (FragmentLike(text) ? "legacy-frag" : "legacy"));
						val[text] = (JToken)new JObject
						{
							["src"] = JToken.op_Implicit(text),
							["zh"] = JToken.op_Implicit(text2),
							["from"] = JToken.op_Implicit(text4)
						};
						if (text4 == "legacy-slot")
						{
							num2++;
						}
						else if (text4 == "legacy-frag")
						{
							num3++;
						}
						num++;
					}
				}
			}
			JToken obj5 = doc["objects"];
			JObject val6 = (JObject)(object)((obj5 is JObject) ? obj5 : null);
			JToken obj6 = official["objects"];
			JObject val7 = (JObject)(object)((obj6 is JObject) ? obj6 : null);
			if (val6 != null && val7 != null)
			{
				JToken obj7 = val7["config_section"];
				JObject val8 = (JObject)(object)((obj7 is JObject) ? obj7 : null);
				if (val8 != null)
				{
					JToken obj8 = val6["config_section"];
					JObject val9 = (JObject)(object)((obj8 is JObject) ? obj8 : null);
					if (val9 == null)
					{
						val9 = (JObject)(object)(val6["config_section"] = (JToken)new JObject());
					}
					foreach (KeyValuePair<string, JToken> item2 in val8)
					{
						if (val9[item2.Key] == null)
						{
							JToken value3 = item2.Value;
							JValue val11 = (JValue)(object)((value3 is JValue) ? value3 : null);
							if (val11 != null && !string.IsNullOrEmpty(((object)val11).ToString()))
							{
								val9[item2.Key] = JToken.op_Implicit(((object)val11).ToString());
								num6++;
							}
						}
					}
				}
				JToken obj9 = val7["config"];
				JObject val12 = (JObject)(object)((obj9 is JObject) ? obj9 : null);
				if (val12 != null)
				{
					JToken obj10 = val6["config"];
					JObject val13 = (JObject)(object)((obj10 is JObject) ? obj10 : null);
					if (val13 == null)
					{
						val13 = (JObject)(object)(val6["config"] = (JToken)new JObject());
					}
					foreach (KeyValuePair<string, JToken> item3 in val12)
					{
						if (val13[item3.Key] != null || !(item3.Value is JObject))
						{
							continue;
						}
						JToken value4 = item3.Value;
						JToken obj11 = ((value4 is JObject) ? value4 : null).DeepClone();
						JObject val15 = (JObject)(object)((obj11 is JObject) ? obj11 : null);
						foreach (KeyValuePair<string, JToken> item4 in val15)
						{
							if (item4.Key.StartsWith("value:", StringComparison.Ordinal))
							{
								JToken value5 = item4.Value;
								JToken obj12 = ((value5 is JObject) ? value5 : null);
								string text5 = (string)((obj12 != null) ? ((JObject)obj12)["src"] : null);
								if (IsHexColorValue(text5 ?? item4.Key.Substring("value:".Length)))
								{
									val15[item4.Key] = (JToken)new JObject
									{
										["src"] = JToken.op_Implicit(text5 ?? item4.Key.Substring("value:".Length)),
										["zh"] = JToken.op_Implicit("")
									};
								}
							}
						}
						val13[item3.Key] = (JToken)(object)val15;
						num5++;
					}
				}
			}
			JToken obj13 = official["tokens"];
			JObject val16 = (JObject)(object)((obj13 is JObject) ? obj13 : null);
			if (val16 != null)
			{
				JToken obj14 = doc["tokens"];
				JObject val17 = (JObject)(object)((obj14 is JObject) ? obj14 : null);
				if (val17 == null)
				{
					val17 = (JObject)(object)(doc["tokens"] = (JToken)new JObject());
				}
				foreach (KeyValuePair<string, JToken> item5 in val16)
				{
					if (!TokenField(item5.Value, item5.Key, out var src, out var zh) || string.IsNullOrEmpty(zh))
					{
						continue;
					}
					JToken val19 = val17[item5.Key];
					if (val19 == null)
					{
						val17[item5.Key] = (JToken)new JObject
						{
							["src"] = JToken.op_Implicit(src ?? item5.Key),
							["zh"] = JToken.op_Implicit(zh)
						};
						num7++;
						continue;
					}
					JObject val20 = (JObject)(object)((val19 is JObject) ? val19 : null);
					if (val20 != null && string.IsNullOrEmpty((string)val20["zh"]) && SrcEquivalent(((string)val20["src"]) ?? item5.Key, src))
					{
						val20["zh"] = JToken.op_Implicit(zh);
						num8++;
					}
				}
			}
			JToken obj15 = official["officialKeys"];
			JArray val21 = (JArray)(object)((obj15 is JArray) ? obj15 : null);
			if (val21 != null)
			{
				JToken obj16 = doc["officialKeys"];
				JArray val22 = (JArray)(object)((obj16 is JArray) ? obj16 : null);
				if (val22 == null)
				{
					val22 = (JArray)(object)(doc["officialKeys"] = (JToken)new JArray());
				}
				HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
				foreach (JToken item6 in val22)
				{
					hashSet.Add((string)item6);
				}
				foreach (JToken item7 in val21)
				{
					string text6 = (string)item7;
					if (!string.IsNullOrEmpty(text6) && hashSet.Add(text6))
					{
						val22.Add(JToken.op_Implicit(text6));
					}
				}
			}
			int num9 = 0;
			int num10 = 0;
			int num11 = 0;
			int num12 = 0;
			JToken obj17 = official["patterns"];
			JArray val24 = (JArray)(object)((obj17 is JArray) ? obj17 : null);
			if (val24 != null)
			{
				JToken obj18 = doc["patterns"];
				JArray val25 = (JArray)(object)((obj18 is JArray) ? obj18 : null);
				if (val25 == null)
				{
					val25 = (JArray)(object)(doc["patterns"] = (JToken)new JArray());
				}
				HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal);
				for (int i = 0; i < ((JContainer)val25).Count; i++)
				{
					JToken obj19 = val25[i];
					string text7 = PatternIdentityKey((JObject)(object)((obj19 is JObject) ? obj19 : null));
					if (text7 != null)
					{
						hashSet2.Add(text7);
					}
				}
				for (int j = 0; j < ((JContainer)val24).Count; j++)
				{
					JToken obj20 = val24[j];
					JObject val27 = (JObject)(object)((obj20 is JObject) ? obj20 : null);
					if (val27 == null || string.IsNullOrEmpty((string)val27["replacement"]))
					{
						continue;
					}
					string text8 = PatternIdentityKey(val27);
					if (text8 != null && hashSet2.Add(text8))
					{
						val25.Add(((JToken)val27).DeepClone());
						num9++;
						JToken obj21 = val27["homes"];
						JArray val28 = (JArray)(object)((obj21 is JArray) ? obj21 : null);
						if (val28 != null && ((JContainer)val28).Count > 0)
						{
							num10++;
						}
						else if (!string.IsNullOrEmpty((string)val27["pattern"]))
						{
							num11++;
						}
						else
						{
							num12++;
						}
					}
				}
			}
			JToken obj22 = official["patterns"];
			JToken obj23 = ((obj22 is JArray) ? obj22 : null);
			int num13 = ((obj23 != null) ? ((JContainer)obj23).Count : 0);
			JToken obj24 = doc["patterns"];
			JToken obj25 = ((obj24 is JArray) ? obj24 : null);
			int num14 = ((obj25 != null) ? ((JContainer)obj25).Count : 0);
			note = "并集(旧→新): strings +" + num + "(普通 " + (num - num2 - num3) + " / 含槽 " + num2 + " / 碎片 " + num3 + " / 已存在 " + num4 + ") | cfg +" + num5 + " | 节 +" + num6 + " | token +" + num7 + "(键在值空补值 " + num8 + ") | patterns +" + num9 + "(旧 " + num13 + " → 新 " + num14 + ";有家 " + num10 + " / 纯正则 " + num11 + " / 无家模板 " + num12 + " —— 无家条目并回=保持现状,补 homes 是后续清理方向)";
			return num + num5 + num6 + num7 + num9;
		}

		private static string PatternIdentityKey(JObject o)
		{
			if (o == null)
			{
				return null;
			}
			string text = (string)o["template"];
			if (!string.IsNullOrEmpty(text))
			{
				string text2 = ExportFilters.NormalizeTemplateKey(text);
				if (!string.IsNullOrEmpty(text2))
				{
					return "t|" + text2 + "|" + SlotSignature(text);
				}
				return "t|" + text + "|" + SlotSignature(text);
			}
			string text3 = (string)o["pattern"];
			if (!string.IsNullOrEmpty(text3))
			{
				return "p|" + text3;
			}
			return null;
		}

		private static bool HasSlot(string s)
		{
			if (!string.IsNullOrEmpty(s))
			{
				return Regex.IsMatch(s, "\\{[0-9][^}]*\\}");
			}
			return false;
		}

		private static bool FragmentLike(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return true;
			}
			string text = s.Trim();
			if (text.Length == 0)
			{
				return true;
			}
			if (!string.Equals(s, text, StringComparison.Ordinal) && text.Length <= 30)
			{
				return true;
			}
			return Regex.IsMatch(text, "^(?:</?[A-Za-z][^>]*>|\\[[^\\]]*\\]|[-–—·•|:;,.…\\s])+$");
		}

		public static string Serialize(JObject root)
		{
			StringBuilder stringBuilder = new StringBuilder(1048576);
			WriteNode((JToken)(object)root, 0, stringBuilder);
			stringBuilder.Append('\n');
			return stringBuilder.ToString();
		}

		private static void WriteNode(JToken v, int pad, StringBuilder sb)
		{
			JObject val = (JObject)(object)((v is JObject) ? v : null);
			if (val == null)
			{
				JArray val2 = (JArray)(object)((v is JArray) ? v : null);
				if (val2 != null)
				{
					if (((JContainer)val2).Count == 0)
					{
						sb.Append("[]");
						return;
					}
					bool flag = true;
					foreach (JToken item in val2)
					{
						if (item is JObject || item is JArray)
						{
							flag = false;
							break;
						}
					}
					if (((JContainer)val2).Count <= 8 && flag)
					{
						WriteCompact(val2, sb);
					}
					else
					{
						WriteBlock(ItemLines(val2, pad), pad, '[', ']', sb);
					}
				}
				else
				{
					sb.Append(Scalar(v));
				}
			}
			else if (((JContainer)val).Count == 0)
			{
				sb.Append("{}");
			}
			else if (((JContainer)val).Count <= 8 && IsFlat(val))
			{
				WriteCompact(val, sb);
			}
			else
			{
				WriteBlock(PropLines(val, pad), pad, '{', '}', sb);
			}
		}

		private static void WriteBlock(IEnumerable<string> lines, int pad, char open, char close, StringBuilder sb)
		{
			sb.Append(open);
			string value = new string(' ', pad + 2);
			bool flag = true;
			foreach (string line in lines)
			{
				if (!flag)
				{
					sb.Append(',');
				}
				flag = false;
				sb.Append('\n').Append(value).Append(line);
			}
			sb.Append('\n').Append(new string(' ', pad)).Append(close);
		}

		private static IEnumerable<string> PropLines(JObject o, int pad)
		{
			foreach (KeyValuePair<string, JToken> item in o)
			{
				yield return JTokenToKey(item.Key) + ": " + Child(item.Value, pad);
			}
		}

		private static IEnumerable<string> ItemLines(JArray a, int pad)
		{
			foreach (JToken item in a)
			{
				yield return Child(item, pad);
			}
		}

		private static string Child(JToken v, int pad)
		{
			StringBuilder stringBuilder = new StringBuilder();
			WriteNode(v, pad + 2, stringBuilder);
			return stringBuilder.ToString();
		}

		private static void WriteCompact(JObject o, StringBuilder sb)
		{
			sb.Append('{');
			bool flag = true;
			foreach (KeyValuePair<string, JToken> item in o)
			{
				if (!flag)
				{
					sb.Append(',');
				}
				flag = false;
				sb.Append(JTokenToKey(item.Key)).Append(": ").Append(Scalar(item.Value));
			}
			sb.Append('}');
		}

		private static void WriteCompact(JArray a, StringBuilder sb)
		{
			sb.Append('[');
			bool flag = true;
			foreach (JToken item in a)
			{
				if (!flag)
				{
					sb.Append(',');
				}
				flag = false;
				sb.Append(Scalar(item));
			}
			sb.Append(']');
		}

		private static bool IsFlat(JObject o)
		{
			foreach (KeyValuePair<string, JToken> item in o)
			{
				if (item.Value is JObject || item.Value is JArray)
				{
					return false;
				}
			}
			return true;
		}

		private static string JTokenToKey(string key)
		{
			return JToken.FromObject((object)key).ToString((Formatting)0, Array.Empty<JsonConverter>());
		}

		private static string Scalar(JToken v)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			JValue val = (JValue)(object)((v is JValue) ? v : null);
			if (val != null && (int)((JToken)val).Type == 8)
			{
				return EscapeString((string)(JToken)(object)val);
			}
			return v.ToString((Formatting)0, Array.Empty<JsonConverter>());
		}

		private static string EscapeString(string s)
		{
			StringBuilder stringBuilder = new StringBuilder(s.Length + 8);
			stringBuilder.Append('"');
			foreach (char c in s)
			{
				switch (c)
				{
				case '"':
					stringBuilder.Append("\\\"");
					continue;
				case '\\':
					stringBuilder.Append("\\\\");
					continue;
				case '\n':
					stringBuilder.Append("\\n");
					continue;
				case '\r':
					stringBuilder.Append("\\r");
					continue;
				case '\t':
					stringBuilder.Append("\\t");
					continue;
				case '\b':
					stringBuilder.Append("\\b");
					continue;
				case '\f':
					stringBuilder.Append("\\f");
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4"));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			stringBuilder.Append('"');
			return stringBuilder.ToString();
		}
	}
	internal static class FaceIndex
	{
		private static readonly Dictionary<string, Dictionary<string, string>> _byFace = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);

		private static readonly HashSet<string> _faces = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static readonly object _lock = new object();

		private static volatile int _builtForCount = -1;

		public static long Hits;

		public static long Looks;

		internal const string ApiPrefix = "api:";

		internal static void EnsureBuilt(IList<ModDictionary> dicts)
		{
			if (dicts != null && dicts.Count != _builtForCount)
			{
				Rebuild(dicts);
			}
		}

		internal static void Rebuild(IList<ModDictionary> dicts)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			Dictionary<string, Dictionary<string, string>> dictionary = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
			if (dicts != null)
			{
				for (int i = 0; i < dicts.Count; i++)
				{
					ModDictionary modDictionary = dicts[i];
					if (modDictionary == null || !modDictionary.Active)
					{
						continue;
					}
					string text = FirstGuid(modDictionary);
					if (text == null)
					{
						continue;
					}
					Dictionary<string, string[]> facesByText = modDictionary.FacesByText;
					if (facesByText != null && facesByText.Count > 0)
					{
						foreach (KeyValuePair<string, string[]> item in facesByText)
						{
							string[] value = item.Value;
							if (value != null && value.Length != 0)
							{
								for (int j = 0; j < value.Length; j++)
								{
									Add(dictionary, hashSet, value[j], item.Key, text);
								}
							}
						}
					}
					List<RegexRule> globalRegexes = modDictionary.GlobalRegexes;
					if (globalRegexes != null)
					{
						for (int k = 0; k < globalRegexes.Count; k++)
						{
							string[] array = ((globalRegexes[k] != null) ? globalRegexes[k].Faces : null);
							if (array == null)
							{
								continue;
							}
							for (int l = 0; l < array.Length; l++)
							{
								if (!string.IsNullOrEmpty(array[l]))
								{
									hashSet.Add(array[l]);
								}
							}
						}
					}
					globalRegexes = modDictionary.LocalRegexes;
					if (globalRegexes == null)
					{
						continue;
					}
					for (int m = 0; m < globalRegexes.Count; m++)
					{
						string[] array2 = ((globalRegexes[m] != null) ? globalRegexes[m].Faces : null);
						if (array2 == null)
						{
							continue;
						}
						for (int n = 0; n < array2.Length; n++)
						{
							if (!string.IsNullOrEmpty(array2[n]))
							{
								hashSet.Add(array2[n]);
							}
						}
					}
				}
			}
			lock (_lock)
			{
				_byFace.Clear();
				foreach (KeyValuePair<string, Dictionary<string, string>> item2 in dictionary)
				{
					_byFace[item2.Key] = item2.Value;
				}
				_faces.Clear();
				foreach (string item3 in hashSet)
				{
					_faces.Add(item3);
				}
				_builtForCount = dicts?.Count ?? 0;
			}
		}

		private static void Add(Dictionary<string, Dictionary<string, string>> build, HashSet<string> faces, string face, string text, string guid)
		{
			if (!string.IsNullOrEmpty(face) && !string.IsNullOrEmpty(text))
			{
				faces.Add(face);
				if (!build.TryGetValue(face, out var value))
				{
					value = (build[face] = new Dictionary<string, string>(StringComparer.Ordinal));
				}
				if (!value.ContainsKey(text))
				{
					value[text] = guid;
				}
			}
		}

		internal static string Lookup(string face, string text)
		{
			if (string.IsNullOrEmpty(face) || string.IsNullOrEmpty(text))
			{
				return null;
			}
			Looks++;
			Dictionary<string, string> value;
			lock (_lock)
			{
				if (!_byFace.TryGetValue(face, out value))
				{
					return null;
				}
			}
			if (value.TryGetValue(text, out var value2))
			{
				Hits++;
				return value2;
			}
			return null;
		}

		internal static List<string> AllFaces()
		{
			lock (_lock)
			{
				return new List<string>(_faces);
			}
		}

		internal static string Summary()
		{
			int count;
			int num;
			lock (_lock)
			{
				count = _faces.Count;
				num = 0;
				foreach (KeyValuePair<string, Dictionary<string, string>> item in _byFace)
				{
					num += item.Value.Count;
				}
			}
			return "面 " + count + " 个 / 面内可查串 " + num + " 条(查 " + Looks + " / 命中 " + Hits + ")";
		}

		private static string FirstGuid(ModDictionary d)
		{
			if (!string.IsNullOrEmpty(d.TargetGuid))
			{
				return d.TargetGuid;
			}
			if (d.TargetGuids != null && d.TargetGuids.Length != 0)
			{
				return d.TargetGuids[0];
			}
			return null;
		}

		internal static void Clear()
		{
			lock (_lock)
			{
				_byFace.Clear();
				_faces.Clear();
				_builtForCount = -1;
			}
		}
	}
	internal static class GameResourceBridge
	{
		private static readonly Dictionary<string, string> _map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private static bool _built;

		private static string _langAtBuild;

		private static readonly Dictionary<string, string> _biomeToken = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "Meadows", "$biome_meadows" },
			{ "Black Forest", "$biome_blackforest" },
			{ "Swamp", "$biome_swamp" },
			{ "Mountain", "$biome_mountain" },
			{ "Plains", "$biome_plains" },
			{ "Mistlands", "$biome_mistlands" },
			{ "Ashlands", "$biome_ashlands" },
			{ "Deep North", "$biome_deepnorth" },
			{ "Ocean", "$biome_ocean" }
		};

		internal static bool Built
		{
			get
			{
				if (_built)
				{
					return _map.Count > 0;
				}
				return false;
			}
		}

		internal static bool BridgeActive
		{
			get
			{
				if (!Enabled)
				{
					return false;
				}
				EnsureBuilt();
				if (_built)
				{
					return _map.Count > 0;
				}
				return false;
			}
		}

		public static bool Enabled
		{
			get
			{
				if (ValheimTWPlugin.CfgBridgeGameResources != null)
				{
					return ValheimTWPlugin.CfgBridgeGameResources.Value;
				}
				return false;
			}
		}

		public static void EnsureBuilt()
		{
			string text = SafeLang();
			if (!_built || !(_langAtBuild == text) || (_map.Count <= 0 && HasDataSource()))
			{
				Build();
				_built = true;
				_langAtBuild = text;
			}
		}

		private static bool HasDataSource()
		{
			try
			{
				if ((Object)(object)ObjectDB.instance != (Object)null && GetField(ObjectDB.instance, "m_items") is IEnumerable enumerable)
				{
					{
						IEnumerator enumerator = enumerable.GetEnumerator();
						try
						{
							if (enumerator.MoveNext())
							{
								_ = enumerator.Current;
								return true;
							}
						}
						finally
						{
							IDisposable disposable = enumerator as IDisposable;
							if (disposable != null)
							{
								disposable.Dispose();
							}
						}
					}
				}
			}
			catch
			{
			}
			try
			{
				if ((Object)(object)ZoneSystem.instance != (Object)null && GetField(ZoneSystem.instance, "m_locations") is IEnumerable enumerable2)
				{
					{
						IEnumerator enumerator = enumerable2.GetEnumerator();
						try
						{
							if (enumerator.MoveNext())
							{
								_ = enumerator.Current;
								return true;
							}
						}
						finally
						{
							IDisposable disposable2 = enumerator as IDisposable;
							if (disposable2 != null)
							{
								disposable2.Dispose();
							}
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static string SafeLang()
		{
			try
			{
				return (Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : "?";
			}
			catch
			{
				return "?";
			}
		}

		private static void Build()
		{
			_map.Clear();
			try
			{
				ScanPrefabs();
			}
			catch (Exception ex)
			{
				ValheimTWPlugin.Warn("[GameResourceBridge] prefabs scan failed: " + ex.Message);
			}
			try
			{
				ScanLocations();
			}
			catch (Exception ex2)
			{
				ValheimTWPlugin.Warn("[GameResourceBridge] locations scan failed: " + ex2.Message);
			}
			try
			{
				ScanBiomes();
			}
			catch (Exception ex3)
			{
				ValheimTWPlugin.Warn("[GameResourceBridge] biomes scan failed: " + ex3.Message);
			}
		}

		private static void ScanBiomes()
		{
			foreach (KeyValuePair<string, string> item in _biomeToken)
			{
				string text = LocalizeSafe(item.Value);
				if (!string.IsNullOrEmpty(text))
				{
					Add(Norm(item.Key), text);
				}
			}
		}

		private static void ScanPrefabs()
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || !(GetField(instance, "m_items") is IEnumerable enumerable))
			{
				return;
			}
			foreach (object item in enumerable)
			{
				GameObject val = (GameObject)((item is GameObject) ? item : null);
				if (!((Object)(object)val == (Object)null))
				{
					string text = PrefabChinese(val);
					if (!string.IsNullOrEmpty(text))
					{
						Add(Norm(((Object)val).name), text);
					}
				}
			}
		}

		private static string PrefabChinese(GameObject go)
		{
			ItemDrop component = go.GetComponent<ItemDrop>();
			if ((Object)(object)component == (Object)null)
			{
				return null;
			}
			object field = GetField(component, "m_itemData");
			if (field == null)
			{
				return null;
			}
			object field2 = GetField(field, "m_shared");
			if (field2 == null)
			{
				return null;
			}
			string text = GetField(field2, "m_name") as string;
			if (string.IsNullOrEmpty(text) || !text.StartsWith("$"))
			{
				return null;
			}
			return LocalizeSafe(text);
		}

		private static void ScanLocations()
		{
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null || !(GetField(instance, "m_locations") is IEnumerable enumerable))
			{
				return;
			}
			foreach (object item in enumerable)
			{
				if (item == null)
				{
					continue;
				}
				string text = GetField(item, "m_name") as string;
				string text2 = null;
				string text3 = GetField(item, "m_prefabName") as string;
				if (!string.IsNullOrEmpty(text3))
				{
					text2 = Lookup(Norm(text3));
				}
				if (text2 == null)
				{
					object field = GetField(item, "m_prefab");
					string text4 = ((field != null) ? (GetProp(field, "Name") as string) : null);
					if (!string.IsNullOrEmpty(text4))
					{
						text2 = Lookup(Norm(text4));
					}
				}
				if (!string.IsNullOrEmpty(text2) && !string.IsNullOrEmpty(text))
				{
					Add(Norm(text), text2);
				}
			}
		}

		private static string Lookup(string k)
		{
			if (!_map.TryGetValue(k, out var value))
			{
				return null;
			}
			return value;
		}

		private static void Add(string key, string cn)
		{
			if (!string.IsNullOrEmpty(key) && !_map.ContainsKey(key))
			{
				_map[key] = cn;
			}
		}

		public static bool TryBridge(string text, out string cn)
		{
			cn = null;
			if (!Enabled)
			{
				return false;
			}
			EnsureBuilt();
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			return _map.TryGetValue(Norm(text), out cn);
		}

		private static string Norm(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			return Regex.Replace(s, "[\\s_\\-]", "").ToLowerInvariant();
		}

		private static string LocalizeSafe(string tok)
		{
			try
			{
				if (Localization.instance == null)
				{
					return null;
				}
				string text = Localization.instance.Localize(tok);
				return (text == tok) ? null : text;
			}
			catch
			{
				return null;
			}
		}

		private static object GetField(object obj, string name)
		{
			if (obj == null)
			{
				return null;
			}
			FieldInfo field = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null)
			{
				return null;
			}
			try
			{
				return field.GetValue(obj);
			}
			catch
			{
				return null;
			}
		}

		private static object GetProp(object obj, string name)
		{
			if (obj == null)
			{
				return null;
			}
			PropertyInfo property = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property == null)
			{
				return null;
			}
			try
			{
				return property.GetValue(obj);
			}
			catch
			{
				return null;
			}
		}
	}
	internal static class GameTypes
	{
		private static readonly Dictionary<string, Type> _cache = new Dictionary<string, Type>(StringComparer.Ordinal);

		internal static Type Find(string shortName)
		{
			if (string.IsNullOrEmpty(shortName))
			{
				return null;
			}
			if (_cache.TryGetValue(shortName, out var value))
			{
				return value;
			}
			Type type = FindDirect(shortName);
			if (type == null && shortName.IndexOf('+') < 0)
			{
				type = FindNestedByShortName(shortName);
			}
			_cache[shortName] = type;
			return type;
		}

		private static Type FindDirect(string shortName)
		{
			Type type = null;
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					string text = null;
					try
					{
						text = assembly.GetName().Name;
					}
					catch (Exception)
					{
						continue;
					}
					if (!IsGameAssembly(text))
					{
						continue;
					}
					Type type2 = null;
					try
					{
						type2 = assembly.GetType(shortName, throwOnError: false);
					}
					catch (Exception)
					{
					}
					if (type2 == null)
					{
						try
						{
							type2 = assembly.GetType("Valheim." + shortName, throwOnError: false);
						}
						catch (Exception)
						{
						}
					}
					if (!(type2 == null))
					{
						if (type != null && type != type2)
						{
							return null;
						}
						type = type2;
					}
				}
			}
			catch (Exception)
			{
			}
			return type;
		}

		private static Type FindNestedByShortName(string shortName)
		{
			Type type = null;
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					string text = null;
					try
					{
						text = assembly.GetName().Name;
					}
					catch (Exception)
					{
						continue;
					}
					if (!IsGameAssembly(text))
					{
						continue;
					}
					Type[] types;
					try
					{
						types = assembly.GetTypes();
					}
					catch (Exception)
					{
						continue;
					}
					foreach (Type type2 in types)
					{
						if (!(type2 == null) && !(type2.Name != shortName) && type2.IsNested)
						{
							if (type != null && type != type2)
							{
								return null;
							}
							type = type2;
						}
					}
				}
			}
			catch (Exception)
			{
			}
			return type;
		}

		internal static Type FindQualified(string fullName)
		{
			if (string.IsNullOrEmpty(fullName))
			{
				return null;
			}
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					Type type = null;
					try
					{
						type = assemblies[i].GetType(fullName, throwOnError: false);
					}
					catch (Exception)
					{
						continue;
					}
					if (type != null)
					{
						return type;
					}
				}
			}
			catch (Exception)
			{
			}
			return null;
		}

		private static bool IsGameAssembly(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			if (name.StartsWith("assembly_", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			return name.IndexOf("valheim", StringComparison.OrdinalIgnoreCase) >= 0;
		}
	}
	internal static class HostScope
	{
		public static long Hits;

		public static long Blocks;

		public static long ObservedBlocks;

		private static readonly object Lk = new object();

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

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

		private const int MaxSamples = 12;

		public static string Report()
		{
			if (Hits == 0L && Blocks == 0L && ObservedBlocks == 0L)
			{
				return "点级约束(homes): 本会话无样本";
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("点级约束(homes): 命中 ").Append(Hits).Append(" / 拦截 ")
				.Append(Blocks);
			if (ObservedBlocks > 0)
			{
				stringBuilder.Append(" / ①层观测不符 ").Append(ObservedBlocks).Append("(不否决:字面量宿主≠显示点的固有偏差,2026-10-07 起仅记账)");
			}
			lock (Lk)
			{
				if (Samples.Count > 0)
				{
					stringBuilder.Append("  ── 被拦样本(②层无归属,上限 ").Append(12).Append(")──");
					for (int i = 0; i < Samples.Count; i++)
					{
						stringBuilder.AppendLine().Append("  ").Append(Samples[i]);
					}
				}
				if (Observed.Count > 0)
				{
					stringBuilder.Append("  ── ①层观测样本(上限 ").Append(12).Append(")──");
					for (int j = 0; j < Observed.Count; j++)
					{
						stringBuilder.AppendLine().Append("  ").Append(Observed[j]);
					}
				}
			}
			return stringBuilder.ToString();
		}

		public static bool Matches(string[] homes, string sample = null, bool observeOnly = false)
		{
			if (homes == null || homes.Length == 0)
			{
				return true;
			}
			StackTrace stackTrace;
			try
			{
				stackTrace = new StackTrace(fNeedFileInfo: false);
			}
			catch
			{
				return true;
			}
			for (int i = 0; i < stackTrace.FrameCount; i++)
			{
				MethodBase methodBase = stackTrace.GetFrame(i)?.GetMethod();
				if (methodBase == null)
				{
					continue;
				}
				string text = ((methodBase.DeclaringType != null) ? methodBase.DeclaringType.FullName : "") + "." + methodBase.Name;
				text = text.Replace('/', '+');
				foreach (string text2 in homes)
				{
					if (!string.IsNullOrEmpty(text2))
					{
						int num = text2.IndexOf(':');
						string text3 = ((num >= 0) ? text2.Substring(num + 1) : text2);
						text3 = text3.Replace('/', '+');
						if (text3.Length >= 4 && FrameMatches(text, text3))
						{
							Hits++;
							return true;
						}
					}
				}
			}
			if (observeOnly)
			{
				ObservedBlocks++;
				if (!string.IsNullOrEmpty(sample))
				{
					lock (Lk)
					{
						if (Observed.Count < 12)
						{
							Observed.Add((sample.Length > 90) ? sample.Substring(0, 90) : sample);
						}
					}
					string text4 = FirstHome(homes);
					string text5 = TopFrames(stackTrace);
					if (text4 != null)
					{
						ObservationLog.Append("homesObserved", (text5 == null) ? text4 : (text4 + " ⊕ " + text5), sample);
					}
				}
				return true;
			}
			Blocks++;
			if (!string.IsNullOrEmpty(sample))
			{
				lock (Lk)
				{
					if (Samples.Count < 12)
					{
						Samples.Add((sample.Length > 90) ? sample.Substring(0, 90) : sample);
					}
				}
				string text6 = FirstHome(homes);
				if (text6 != null)
				{
					ObservationLog.Append("homesBlocked", text6, sample);
				}
			}
			return false;
		}

		private static bool FrameMatches(string frame, string home)
		{
			if (string.IsNullOrEmpty(frame) || string.IsNullOrEmpty(home))
			{
				return false;
			}
			if (string.Equals(frame, home, StringComparison.Ordinal))
			{
				return true;
			}
			for (int num = frame.IndexOf(home, StringComparison.Ordinal); num >= 0; num = frame.IndexOf(home, num + 1, StringComparison.Ordinal))
			{
				bool num2 = num == 0 || IsNameBoundary(frame[num - 1]);
				int num3 = num + home.Length;
				if (num2)
				{
					if (num3 >= frame.Length)
					{
						return true;
					}
					if (IsNameBoundary(frame[num3]))
					{
						return true;
					}
					if (IsSynthetic(frame) || IsSynthetic(home))
					{
						if (frame[num3] == '<')
						{
							return true;
						}
						if (frame[num3] == 'b' && frame.Length - num3 >= 3 && frame[num3 + 1] == '_' && frame[num3 + 2] == '_')
						{
							return true;
						}
					}
				}
			}
			return false;
		}

		private static bool IsNameBoundary(char c)
		{
			if (c != '.' && c != '+' && c != '/' && c != '<' && c != '>' && c != '`' && c != ',' && c != '(')
			{
				return c == ')';
			}
			return true;
		}

		private static bool IsSynthetic(string s)
		{
			if (s.IndexOf("b__", StringComparison.Ordinal) < 0)
			{
				return s.IndexOf("<>", StringComparison.Ordinal) >= 0;
			}
			return true;
		}

		private static string TopFrames(StackTrace st)
		{
			if (st == null)
			{
				return null;
			}
			StringBuilder stringBuilder = null;
			int num = 0;
			for (int i = 0; i < st.FrameCount; i++)
			{
				if (num >= 3)
				{
					break;
				}
				MethodBase methodBase = st.GetFrame(i)?.GetMethod();
				if (methodBase == null)
				{
					continue;
				}
				string text = ((methodBase.DeclaringType != null) ? methodBase.DeclaringType.FullName : "") + "." + methodBase.Name;
				if (!text.StartsWith("ValheimTW.", StringComparison.Ordinal) && !text.StartsWith("System.", StringComparison.Ordinal))
				{
					if (stringBuilder == null)
					{
						stringBuilder = new StringBuilder(160);
					}
					else
					{
						stringBuilder.Append(" < ");
					}
					stringBuilder.Append(text.Replace('/', '+'));
					num++;
				}
			}
			return stringBuilder?.ToString();
		}

		private static string FirstHome(string[] homes)
		{
			for (int i = 0; i < homes.Length; i++)
			{
				if (!string.IsNullOrEmpty(homes[i]))
				{
					int num = homes[i].IndexOf(':');
					if (num < 0)
					{
						return homes[i];
					}
					return homes[i].Substring(num + 1);
				}
			}
			return null;
		}
	}
	internal static class JotunnLocalizationTakeover
	{
		private static Type _tCustomLoc;

		private static PropertyInfo _pSourceMod;

		private static PropertyInfo _pMap;

		private static MethodInfo _mGetTranslations;

		private static MethodInfo _mGetLanguages;

		private static MethodInfo _mAddJsonFile;

		private static bool _probed;

		private static bool _installed;

		private static bool _hookedAddJsonFile;

		private static bool _hookedLmPrefix;

		private static bool _hookedTryTranslate;

		private static Type _tLM;

		private static FieldInfo _fLocalizations;

		private static FieldInfo _fTranslations;

		private static readonly List<object> Registered = new List<object>();

		private static readonly HashSet<string> OfficialTokens = new HashSet<string>(StringComparer.Ordinal);

		private static bool _officialSnapshotted;

		private static bool _snapshotClean;

		private static bool _dirty;

		private static int _retries;

		private static int _probeTries;

		private static int _installTries;

		private static readonly HashSet<string> Done = new HashSet<string>(StringComparer.Ordinal);

		private const int MaxRetries = 3600;

		private static readonly Dictionary<string, string> ChainZhCache = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly Dictionary<string, string> _tokenOwner = new Dictionary<string, string>(StringComparer.Ordinal);

		private static int _tokenOwnerStamp = -1;

		private static readonly Dictionary<string, string> _tokenEn = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly HashSet<string> _tokenAmbiguous = new HashSet<string>(StringComparer.Ordinal);

		private static readonly Regex SlotRe = new Regex("\\{(\\d+)\\}", RegexOptions.Compiled);

		internal static bool HasCleanOfficialSnapshot => _snapshotClean;

		internal static string StatusLine
		{
			get
			{
				if (_tCustomLoc == null)
				{
					if (_installTries <= 0)
					{
						return "未探测(插件未走到 Install)";
					}
					return "未探测到 Jötunn(已重试 " + _installTries + " 次;该整合包未装 Jötunn 属正常)";
				}
				if (!_installed)
				{
					return "已探测到 Jötunn 但挂载未完成(见启动日志的告警)";
				}
				return "AddJsonFile=" + (_hookedAddJsonFile ? "已挂" : "未找到") + " | LoadAndSetupModLanguages=" + (_hookedLmPrefix ? "已挂" : "未找到") + " | TryTranslate链修复=" + (_hookedTryTranslate ? "已挂" : "未找到") + " | 干净官方快照=" + (_snapshotClean ? "有" : (_officialSnapshotted ? "有但非干净(兜底取的)" : "从未取到")) + " | 已登记语言表实例=" + Registered.Count;
			}
		}

		internal static int TokenOwnerCount
		{
			get
			{
				try
				{
					RebuildTokenOwners();
				}
				catch (Exception)
				{
				}
				return _tokenOwner.Count;
			}
		}

		internal static void Install(Harmony harmony, bool quiet)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Expected O, but got Unknown
			if (_installed)
			{
				return;
			}
			if (!Probe())
			{
				_probeTries++;
				if (!quiet && _probeTries == 1)
				{
					ValheimTWPlugin.Log("尚未发现 Jotunn,先跳过「Jotunn 语言表接管」并继续重试(Jotunn 可能尚未加载完;确实未装 Jotunn 的整合包属正常现象)。");
				}
				return;
			}
			try
			{
				_mAddJsonFile = AccessTools.Method(_tCustomLoc, "AddJsonFile", new Type[2]
				{
					typeof(string),
					typeof(string)
				}, (Type[])null);
				if (_mAddJsonFile != null)
				{
					harmony.Patch((MethodBase)_mAddJsonFile, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(JotunnLocalizationTakeover), "AddJsonFilePostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					_hookedAddJsonFile = true;
				}
				Type type = (_tLM = AccessTools.TypeByName("Jotunn.Managers.LocalizationManager"));
				MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "LoadAndSetupModLanguages", (Type[])null, (Type[])null) : null);
				if (methodInfo != null)
				{
					harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(JotunnLocalizationTakeover), "LoadAndSetupModLanguagesPrefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					_hookedLmPrefix = true;
				}
				MethodInfo methodInfo2 = AccessTools.Method(_tCustomLoc, "TryTranslate", new Type[1] { typeof(string) }, (Type[])null);
				if (methodInfo2 != null)
				{
					harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(JotunnLocalizationTakeover), "TryTranslatePostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					_hookedTryTranslate = true;
				}
				_installed = true;
				ValheimTWPlugin.Log("Jotunn 语言表接管已挂载" + ((_mAddJsonFile != null) ? "" : "(⚠ AddJsonFile 未找到,接管不会生效)") + ((methodInfo != null) ? "" : "(⚠ 未找到 LoadAndSetupModLanguages,官方 token 快照将退化)") + (_hookedTryTranslate ? "" : "(⚠ 未找到 CustomLocalization.TryTranslate,查找链修复不生效)") + "。");
			}
			catch (Exception ex)
			{
				ValheimTWPlugin.Warn("Jotunn 语言表接管挂载失败:" + ex.Message);
			}
		}

		private static bool Probe()
		{
			if (_tCustomLoc != null)
			{
				return true;
			}
			try
			{
				_tCustomLoc = AccessTools.TypeByName("Jotunn.Entities.CustomLocalization");
				if (_tCustomLoc == null)
				{
					return false;
				}
				_pSourceMod = AccessTools.Property(_tCustomLoc, "SourceMod");
				_pMap = AccessTools.Property(_tCustomLoc, "Map");
				_mGetLanguages = AccessTools.Method(_tCustomLoc, "GetLanguages", (Type[])null, (Type[])null);
				_mGetTranslations = AccessTools.Method(_tCustomLoc, "GetTranslations", new Type[1] { typeof(string).MakeByRefType() }, (Type[])null) ?? AccessTools.Method(_tCustomLoc, "GetTranslations", (Type[])null, (Type[])null);
			}
			catch (Exception ex)
			{
				ValheimTWPlugin.Warn("探测 Jotunn CustomLocalization 失败:" + ex.Message);
				_tCustomLoc = null;
			}
			_probed = _tCustomLoc != null;
			return _tCustomLoc != null;
		}

		private static void AddJsonFilePostfix(object __instance)
		{
			try
			{
				if (__instance == null)
				{
					return;
				}
				for (int i = 0; i < Registered.Count; i++)
				{
					if (Registered[i] == __instance)
					{
						_dirty = true;
						return;
					}
				}
				Registered.Add(__instance);
				_dirty = true;
			}
			catch
			{
			}
		}

		private static void LoadAndSetupModLanguagesPrefix(object __0)
		{
			try
			{
				SnapshotOfficial((Localization)((__0 is Localization) ? __0 : null), clean: true);
			}
			catch
			{
			}
		}

		internal static HashSet<string> AllModTokenKeys()
		{
			if (_tCustomLoc == null)
			{
				return null;
			}
			try
			{
				Type type = _tLM ?? AccessTools.TypeByName("Jotunn.Managers.LocalizationManager");
				if (type == null)
				{
					return null;
				}
				object obj = null;
				try
				{
					PropertyInfo propertyInfo = AccessTools.Property(type, "Instance");
					if (propertyInfo != null)
					{
						obj = propertyInfo.GetValue(null, null);
					}
				}
				catch
				{
				}
				if (obj == null)
				{
					return null;
				}
				if (_fLocalizations == null)
				{
					_fLocalizations = AccessTools.Field(type, "Localizations");
				}
				if (_fLocalizations == null)
				{
					return null;
				}
				if (!(_fLocalizations.GetValue(obj) is IEnumerable enumerable))
				{
					return null;
				}
				HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
				foreach (object item in enumerable)
				{
					if (item == null)
					{
						continue;
					}
					object obj3 = item;
					Type type2 = item.GetType();
					PropertyInfo propertyInfo2 = (type2.IsGenericType ? type2.GetProperty("Value") : null);
					if (propertyInfo2 != null)
					{
						obj3 = propertyInfo2.GetValue(item, null);
					}
					if (obj3 == null)
					{
						continue;
					}
					Dictionary<string, string> dictionary = Table(obj3, "English");
					if (dictionary == null)
					{
						foreach (string item2 in LanguagesOf(obj3))
						{
							dictionary = Table(obj3, item2);
							if (dictionary != null)
							{
								break;
							}
						}
					}
					if (dictionary == null)
					{
						continue;
					}
					foreach (KeyValuePair<string, string> item3 in dictionary)
					{
						if (!string.IsNullOrEmpty(item3.Key))
						{
							hashSet.Add(item3.Key);
						}
					}
				}
				return hashSet;
			}
			catch
			{
				return null;
			}
		}

		internal static bool IsOfficialToken(string key)
		{
			if (_snapshotClean && !string.IsNullOrEmpty(key))
			{
				return OfficialTokens.Contains(key);
			}
			return false;
		}

		internal static void Tick()
		{
			if (!_installed)
			{
				if (_installTries < 3600)
				{
					_installTries++;
					Install(ValheimTWPlugin.HarmonyInstance, quiet: true);
				}
				else if (_installTries == 3600)
				{
					_installTries++;
					ValheimTWPlugin.Log("Jotunn 等待超过 " + 60 + " 秒仍未出现,放弃「Jotunn 语言表接管」(本次会话 adapter.tokens 将为空)");
				}
				if (!_installed)
				{
					return;
				}
			}
			SweepLocalizations();
			if (!_dirty)
			{
				return;
			}
			if (ValheimTWPlugin.CfgJotunnTakeover == null || !ValheimTWPlugin.CfgJotunnTakeover.Value)
			{
				_dirty = false;
				return;
			}
			try
			{
				Flush();
			}
			catch (Exception ex)
			{
				_dirty = false;
				ValheimTWPlugin.Warn("Jotunn 语言表接管执行失败:" + ex.Message);
			}
		}

		private static void Flush()
		{
			_dirty = false;
			SweepLocalizations();
			if (Registered.Count == 0)
			{
				return;
			}
			Localization instance = Localization.instance;
			if (instance == null)
			{
				_dirty = true;
				return;
			}
			if (_fTranslations == null)
			{
				_fTranslations = AccessTools.Field(typeof(Localization), "m_translations");
			}
			if (_fTranslations == null)
			{
				ValheimTWPlugin.Warn("Jotunn 语言表接管:未找到 Localization.m_translations,接管停用。");
				return;
			}
			if (!(_fTranslations.GetValue(instance) is Dictionary<string, string> dictionary))
			{
				_dirty = true;
				return;
			}
			if (!_officialSnapshotted)
			{
				SnapshotOfficial(instance, clean: false);
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			bool flag = false;
			for (int i = 0; i < Registered.Count; i++)
			{
				object obj = Registered[i];
				if (obj == null)
				{
					continue;
				}
				string text = GuidOf(obj);
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				ModDictionary modDictionary = LocEngine.Get(text);
				if (modDictionary == null)
				{
					continue;
				}
				if (!modDictionary.Active)
				{
					flag = true;
					continue;
				}
				Dictionary<string, string> dictionary2 = Table(obj, "English");
				Dictionary<string, string> dictionary3 = ChineseTable(obj);
				if (dictionary2 != null)
				{
					foreach (KeyValuePair<string, string> item in dictionary2)
					{
						OfficialTokens.Remove(item.Key);
					}
				}
				if (dictionary3 != null)
				{
					foreach (KeyValuePair<string, string> item2 in dictionary3)
					{
						OfficialTokens.Remove(item2.Key);
					}
				}
				num++;
				if (dictionary2 != null)
				{
					foreach (KeyValuePair<string, string> item3 in dictionary2)
					{
						string key = item3.Key;
						string value = item3.Value;
						if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value) || IsMetaToken(key) || OfficialTokens.Contains(key) || Done.Contains(text + "\0" + key))
						{
							continue;
						}
						string text2 = OurTranslate(modDictionary, value);
						if (text2 != null && !string.Equals(text2, value, StringComparison.Ordinal))
						{
							if (!SlotsSafe(value, text2))
							{
								Done.Add(text + "\0" + key);
								num4++;
							}
							else
							{
								dictionary[key] = text2;
								Done.Add(text + "\0" + key);
								num2++;
							}
						}
					}
				}
				if ((ValheimTWPlugin.CfgJotunnOfficialFill != null && !ValheimTWPlugin.CfgJotunnOfficialFill.Value) || dictionary3 == null)
				{
					continue;
				}
				foreach (KeyValuePair<string, string> item4 in dictionary3)
				{
					string key2 = item4.Key;
					string value2 = item4.Value;
					if (!string.IsNullOrEmpty(key2) && !string.IsNullOrEmpty(value2) && !IsMetaToken(key2) && !OfficialTokens.Contains(key2) && !Done.Contains(text + "\0" + key2))
					{
						string value3 = null;
						dictionary2?.TryGetValue(key2, out value3);
						if (!string.IsNullOrEmpty(value3) && !SlotsSafe(value3, value2))
						{
							Done.Add(text + "\0" + key2);
							num4++;
						}
						else
						{
							dictionary[key2] = value2;
							Done.Add(text + "\0" + key2);
							num3++;
						}
					}
				}
			}
			if (flag)
			{
				if (++_retries < 3600)
				{
					_dirty = true;
				}
				else
				{
					ValheimTWPlugin.Warn("Jotunn 语言表接管:仍有词典未激活,已停止重试。");
				}
			}
			else
			{
				_retries = 0;
			}
			if (num2 + num3 > 0 || num4 > 0)
			{
				ValheimTWPlugin.Log("Jotunn 语言表接管:" + num + " 个 MOD,写入 " + (num2 + num3) + " 条(本插件译文 " + num2 + " / MOD 自带中文 " + num3 + ")" + ((num4 > 0) ? (",跳过占位符不安全 " + num4 + " 条") : "") + "。");
			}
		}

		private static void SweepLocalizations()
		{
			try
			{
				Type type = _tLM ?? AccessTools.TypeByName("Jotunn.Managers.LocalizationManager");
				if (type == null)
				{
					return;
				}
				if (_fLocalizations == null)
				{
					_fLocalizations = AccessTools.Field(type, "Localizations");
				}
				if (_fLocalizations == null)
				{
					return;
				}
				object obj = null;
				try
				{
					PropertyInfo propertyInfo = AccessTools.Property(type, "Instance");
					if (propertyInfo != null)
					{
						obj = propertyInfo.GetValue(null, null);
					}
				}
				catch
				{
				}
				if (obj == null || !(_fLocalizations.GetValue(obj) is IEnumerable enumerable))
				{
					return;
				}
				foreach (object item in enumerable)
				{
					if (item == null)
					{
						continue;
					}
					object obj3 = item;
					Type type2 = item.GetType();
					PropertyInfo propertyInfo2 = (type2.IsGenericType ? type2.GetProperty("Value") : null);
					if (propertyInfo2 != null)
					{
						obj3 = propertyInfo2.GetValue(item, null);
					}
					if (obj3 == null)
					{
						continue;
					}
					bool flag = false;
					for (int i = 0; i < Registered.Count; i++)
					{
						if (Registered[i] == obj3)
						{
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						Registered.Add(obj3);
						_dirty = true;
					}
				}
			}
			catch
			{
			}
		}

		private static void TryTranslatePostfix(object __instance, string __0, ref string __result)
		{
			try
			{
				if (string.IsNullOrEmpty(__result) || HasCjk(__result) || (ValheimTWPlugin.CfgJotunnTakeover != null && !ValheimTWPlugin.CfgJotunnTakeover.Value))
				{
					return;
				}
				string text = ((__0 != null && __0.Length > 0 && __0[0] == '$') ? __0.Substring(1) : __0);
				if (string.IsNullOrEmpty(text))
				{
					return;
				}
				if (ChainZhCache.TryGetValue(text, out var value))
				{
					if (!string.IsNullOrEmpty(value))
					{
						__result = value;
					}
					return;
				}
				value = ResolveChainZh(__instance, text, __result, out var dictReady);
				if (dictReady)
				{
					ChainZhCache[text] = value;
				}
				if (!string.IsNullOrEmpty(value))
				{
					__result = value;
				}
			}
			catch
			{
			}
		}

		private static string ResolveChainZh(object inst, string token, string english, out bool dictReady)
		{
			dictReady = false;
			try
			{
				if (IsMetaToken(token))
				{
					dictReady = true;
					return null;
				}
				string text = GuidOf(inst);
				ModDictionary modDictionary = ((text != null) ? LocEngine.Get(text) : null);
				if (modDictionary == null)
				{
					return null;
				}
				if (!modDictionary.Active)
				{
					return null;
				}
				dictReady = true;
				if (modDictionary.DeclaredTokens.TryGetValue(token, out var value) && !string.IsNullOrEmpty(value) && !string.Equals(value, token, StringComparison.Ordinal))
				{
					return value;
				}
				if (string.IsNullOrEmpty(english))
				{
					return null;
				}
				value = OurTranslate(modDictionary, english);
				if (string.IsNullOrEmpty(value) || string.Equals(value, english, StringComparison.Ordinal))
				{
					return null;
				}
				if (!SlotsSafe(english, value))
				{
					return null;
				}
				return value;
			}
			catch
			{
				return null;
			}
		}

		private static bool HasCjk(string s)
		{
			foreach (char c in s)
			{
				if (c >= '一' && c <= '鿿')
				{
					return true;
				}
			}
			return false;
		}

		internal static JObject CaptureTokensFor(string guid)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			try
			{
				if (_tCustomLoc == null || string.IsNullOrEmpty(guid))
				{
					return null;
				}
				JObject val = new JObject();
				SweepLocalizations();
				for (int i = 0; i < Registered.Count; i++)
				{
					object obj = Registered[i];
					if (obj == null || !string.Equals(GuidOf(obj), guid, StringComparison.Ordinal))
					{
						continue;
					}
					Dictionary<string, string> dictionary = Table(obj, "English");
					if (dictionary == null)
					{
						continue;
					}
					foreach (KeyValuePair<string, string> item in dictionary)
					{
						string key = item.Key;
						string value = item.Value;
						if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value) && !IsMetaToken(key) && val[key] == null)
						{
							val[key] = (JToken)new JObject
							{
								["src"] = JToken.op_Implicit(value),
								["zh"] = JToken.op_Implicit("")
							};
						}
					}
				}
				return val;
			}
			catch
			{
				return null;
			}
		}

		internal static string OwnerOfToken(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return null;
			}
			try
			{
				RebuildTokenOwners();
				string value;
				return _tokenOwner.TryGetValue(token, out value) ? value : null;
			}
			catch (Exception)
			{
				return null;
			}
		}

		private static void RebuildTokenOwners()
		{
			if (_tokenOwnerStamp == Registered.Count)
			{
				return;
			}
			_tokenOwnerStamp = Registered.Count;
			_tokenOwner.Clear();
			_tokenEn.Clear();
			_tokenAmbiguous.Clear();
			for (int i = 0; i < Registered.Count; i++)
			{
				object obj = Registered[i];
				if (obj == null)
				{
					continue;
				}
				string value = GuidOf(obj);
				if (string.IsNullOrEmpty(value))
				{
					continue;
				}
				Dictionary<string, string> dictionary = Table(obj, "English");
				if (dictionary == null)
				{
					continue;
				}
				foreach (KeyValuePair<string, string> item in dictionary)
				{
					if (!string.IsNullOrEmpty(item.Key))
					{
						if (!_tokenOwner.ContainsKey(item.Key))
						{
							_tokenOwner[item.Key] = value;
							_tokenEn[item.Key] = item.Value ?? "";
						}
						else
						{
							_tokenAmbiguous.Add(item.Key);
						}
					}
				}
			}
		}

		internal static bool TryBridgeModToken(string token, out string guid, out string english)
		{
			guid = null;
			english = null;
			if (string.IsNullOrEmpty(token))
			{
				return false;
			}
			try
			{
				RebuildTokenOwners();
				if (_tokenAmbiguous.Contains(token))
				{
					return false;
				}
				if (!_tokenOwner.TryGetValue(token, out guid))
				{
					return false;
				}
				if (!_tokenEn.TryGetValue(token, out english) || string.IsNullOrEmpty(english))
				{
					return false;
				}
				return true;
			}
			catch
			{
				guid = null;
				english = null;
				return false;
			}
		}

		private static string GuidOf(object inst)
		{
			try
			{
				if (_pSourceMod == null)
				{
					return null;
				}
				object? value = _pSourceMod.GetValue(inst, null);
				object? obj = ((value is BepInPlugin) ? value : null);
				return (obj != null) ? ((BepInPlugin)obj).GUID : null;
			}
			catch
			{
				return null;
			}
		}

		private static Dictionary<string, Dictionary<string, string>> MapOf(object inst)
		{
			if (_pMap == null)
			{
				return null;
			}
			try
			{
				return _pMap.GetValue(inst, null) as Dictionary<string, Dictionary<string, string>>;
			}
			catch
			{
				return null;
			}
		}

		private static List<string> LanguagesOf(object inst)
		{
			Dictionary<string, Dictionary<string, string>> dictionary = MapOf(inst);
			if (dictionary != null && dictionary.Count > 0)
			{
				return new List<string>(dictionary.Keys);
			}
			List<string> list = new List<string>();
			if (_mGetLanguages == null)
			{
				return list;
			}
			try
			{
				if (_mGetLanguages.Invoke(inst, null) is IEnumerable<string> enumerable)
				{
					foreach (string item in enumerable)
					{
						list.Add(item);
					}
				}
			}
			catch
			{
			}
			return list;
		}

		private static Dictionary<string, string> Table(object inst, string language)
		{
			if (string.IsNullOrEmpty(language))
			{
				return null;
			}
			Dictionary<string, Dictionary<string, string>> dictionary = MapOf(inst);
			if (dictionary != null && dictionary.TryGetValue(language, out var value) && value != null && value.Count > 0)
			{
				return value;
			}
			if (_mGetTranslations == null)
			{
				return null;
			}
			try
			{
				if (!(_mGetTranslations.Invoke(inst, new object[1] { language }) is IReadOnlyDictionary<string, string> { Count: not 0 } readOnlyDictionary))
				{
					return null;
				}
				if (readOnlyDictionary is Dictionary<string, string> result)
				{
					return result;
				}
				Dictionary<string, string> dictionary2 = new Dictionary<string, string>(readOnlyDictionary.Count, StringComparer.Ordinal);
				foreach (KeyValuePair<string, string> item in readOnlyDictionary)
				{
					dictionary2[item.Key] = item.Value;
				}
				return dictionary2;
			}
			catch
			{
				return null;
			}
		}

		private static Dictionary<string, string> ChineseTable(object inst)
		{
			foreach (string item in LanguagesOf(inst))
			{
				if (string.IsNullOrEmpty(item))
				{
					continue;
				}
				string text = item.ToLowerInvariant();
				if (text.IndexOf("chin", StringComparison.Ordinal) >= 0 || text.IndexOf("zh", StringComparison.Ordinal) == 0)
				{
					Dictionary<string, string> dictionary = Table(inst, item);
					if (dictionary != null && dictionary.Count > 0)
					{
						return dictionary;
					}
				}
			}
			return null;
		}

		private static string OurTranslate(ModDictionary d, string src)
		{
			try
			{
				string text = d.TranslateGlobal(src);
				if (!string.IsNullOrEmpty(text))
				{
					return text;
				}
			}
			catch
			{
			}
			try
			{
				string text2 = d.TranslateWholeUnattributed(src);
				if (!string.IsNullOrEmpty(text2))
				{
					return text2;
				}
			}
			catch
			{
			}
			return null;
		}

		private static bool IsMetaToken(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return true;
			}
			string text = token.ToLowerInvariant();
			switch (text)
			{
			case "localization_version":
			case "version":
			case "language":
				return true;
			default:
				if (text.EndsWith("_version", StringComparison.Ordinal))
				{
					return true;
				}
				if (text.EndsWith("_language", StringComparison.Ordinal))
				{
					return true;
				}
				return false;
			}
		}

		private static bool SlotsSafe(string src, string dst)
		{
			try
			{
				if (dst.IndexOf('{') < 0)
				{
					return true;
				}
				if (src.IndexOf('{') < 0)
				{
					return false;
				}
				foreach (Match item in SlotRe.Matches(dst))
				{
					if (src.IndexOf("{" + item.Groups[1].Value + "}", StringComparison.Ordinal) < 0)
					{
						return false;
					}
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static void SnapshotOfficial(Localization loc, bool clean)
		{
			if (loc == null || _officialSnapshotted)
			{
				return;
			}
			_officialSnapshotted = true;
			_snapshotClean = clean;
			try
			{
				if (_fTranslations == null)
				{
					_fTranslations = AccessTools.Field(typeof(Localization), "m_translations");
				}
				if (!(_fTranslations?.GetValue(loc) is Dictionary<string, string> dictionary))
				{
					return;
				}
				OfficialTokens.Clear();
				foreach (string key in dictionary.Keys)
				{
					OfficialTokens.Add(key);
				}
			}
			catch
			{
			}
		}
	}
	internal static class LocEngine
	{
		private static class Diagnostics
		{
			private static readonly Dictionary<string, int> Counts = new Dictionary<string, int>(StringComparer.Ordinal);

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

			private const int MaxDetailPerShape = 200;

			internal const string ShapeLiteralShort = "字面量不足";

			internal const string ShapeNarrowPass = "窄字面量放行";

			internal const string ShapeBadTemplate = "模板无效";

			internal const string ShapeBadRegex = "正则无效";

			internal const string ShapeEmptyReplacement = "待翻模板未填译";

			internal static void No