Decompiled source of ValheimCN v1.1.8

plugins/ValheimCN.dll

Decompiled 14 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
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 BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
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("ValheimCN")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ValheimCN")]
[assembly: AssemblyTitle("ValheimCN")]
[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 ValheimCN
{
	internal static class ConfigCommentRewriter
	{
		private const string CreatedByPrefix = "Settings file was created by plugin ";

		private const string PluginGuidPrefix = "Plugin GUID: ";

		private const string SettingTypePrefix = "Setting type: ";

		private const string DefaultValuePrefix = "Default value: ";

		private const string AcceptableValuesPrefix = "Acceptable values: ";

		internal static string Rewrite(string text, Func<string, string> lookupDescription)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			string[] array = text.Split(new char[1] { '\n' });
			StringBuilder stringBuilder = new StringBuilder(text.Length + 512);
			bool flag = false;
			for (int num = 0; num < array.Length; num++)
			{
				if (num > 0)
				{
					stringBuilder.Append('\n');
				}
				string text2 = array[num];
				int num2;
				string text3;
				if (text2.Length > 0)
				{
					num2 = ((text2[text2.Length - 1] == '\r') ? 1 : 0);
					if (num2 != 0)
					{
						text3 = text2.Substring(0, text2.Length - 1);
						goto IL_007f;
					}
				}
				else
				{
					num2 = 0;
				}
				text3 = text2;
				goto IL_007f;
				IL_007f:
				string text4 = text3;
				string text5 = RewriteCommentLine(text4, lookupDescription);
				if (!string.Equals(text5, text4, StringComparison.Ordinal))
				{
					flag = true;
				}
				stringBuilder.Append(text5);
				if (num2 != 0)
				{
					stringBuilder.Append('\r');
				}
			}
			if (!flag)
			{
				return text;
			}
			return stringBuilder.ToString();
		}

		internal static string RewriteCommentLine(string line, Func<string, string> lookupDescription)
		{
			if (string.IsNullOrEmpty(line) || line[0] != '#')
			{
				return line;
			}
			if (line.Length > 2 && line[1] == '#' && line[2] == ' ')
			{
				string text = line.Substring(3);
				string text2 = lookupDescription?.Invoke(text);
				if (!string.IsNullOrEmpty(text2))
				{
					return "## " + text2;
				}
				if (text.StartsWith("Settings file was created by plugin ", StringComparison.Ordinal))
				{
					return "## 本配置文件由插件 " + text.Substring("Settings file was created by plugin ".Length) + " 自动生成";
				}
				if (text.StartsWith("Plugin GUID: ", StringComparison.Ordinal))
				{
					return "## 插件 GUID:" + text.Substring("Plugin GUID: ".Length);
				}
				return line;
			}
			if (line.Length > 2 && line[1] == ' ')
			{
				string text3 = line.Substring(2);
				if (text3.StartsWith("Setting type: ", StringComparison.Ordinal))
				{
					return "# 设置类型:" + text3.Substring("Setting type: ".Length);
				}
				if (text3.StartsWith("Default value: ", StringComparison.Ordinal))
				{
					return "# 默认值:" + text3.Substring("Default value: ".Length);
				}
				if (text3.StartsWith("Acceptable values: ", StringComparison.Ordinal))
				{
					return "# 可选值:" + text3.Substring("Acceptable values: ".Length);
				}
			}
			return line;
		}
	}
	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 static readonly HashSet<string> WarnedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private const int PendingTimeoutMs = 60000;

		internal static bool Enabled => LocEngine.ConfigUiEnabled;

		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 || string.IsNullOrEmpty(d.TargetGuid))
			{
				return true;
			}
			if (!d.HasConfigText)
			{
				return true;
			}
			if (!LocEngine.IsPluginLoaded(d.TargetGuid))
			{
				return true;
			}
			if (!Chainloader.PluginInfos.TryGetValue(d.TargetGuid, out var value) || value == null || (Object)(object)value.Instance == (Object)null)
			{
				return false;
			}
			ConfigFile config = value.Instance.Config;
			if (config == null)
			{
				return true;
			}
			int num = ApplyDescriptions(config, d);
			if (num > 0)
			{
				config.Save();
			}
			bool flag = RewriteFile(config.ConfigFilePath, d);
			if (num > 0)
			{
				ValheimCNPlugin.Log($"已把 {d.TargetName} 的 {num} 条配置说明改写为中文。");
			}
			else if (flag)
			{
				ValheimCNPlugin.Log(d.TargetName + " 的 cfg 注释已汉化。");
			}
			return true;
		}

		private static int ApplyDescriptions(ConfigFile cfg, ModDictionary d)
		{
			int num = 0;
			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) && !Cjk.IsMatch(description.Description))
				{
					string text = d.TranslateConfigDescription(description.Description);
					if (!string.IsNullOrEmpty(text) && TrySetDescription(value, text))
					{
						num++;
					}
				}
			}
			return num;
		}

		private static bool TrySetDescription(ConfigEntryBase entry, string cn)
		{
			//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] { cn });
						return true;
					}
				}
				ConfigDescription description = entry.Description;
				ConfigDescription val = new ConfigDescription(cn, 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))
			{
				ModDictionary modDictionary = LocEngine.Get(Path.GetFileNameWithoutExtension(configFilePath));
				if (modDictionary != null && modDictionary.Active && modDictionary.HasConfigText)
				{
					RewriteFile(configFilePath, modDictionary);
				}
			}
		}

		internal static bool RewriteFile(string path, ModDictionary dict)
		{
			if (dict == null || string.IsNullOrEmpty(path) || !File.Exists(path))
			{
				return false;
			}
			try
			{
				byte[] array = File.ReadAllBytes(path);
				bool flag = array.Length >= 3 && array[0] == 239 && array[1] == 187 && array[2] == 191;
				int num = (flag ? 3 : 0);
				string text;
				try
				{
					text = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(array, num, array.Length - num);
				}
				catch (DecoderFallbackException)
				{
					return false;
				}
				string text2 = ConfigCommentRewriter.Rewrite(text, dict.TranslateConfigDescription);
				if (string.Equals(text2, text, StringComparison.Ordinal))
				{
					return false;
				}
				File.WriteAllText(path, text2, new UTF8Encoding(flag));
				return true;
			}
			catch (Exception ex2)
			{
				if (WarnedPaths.Add(path))
				{
					ValheimCNPlugin.Warn("汉化 cfg 注释失败(" + Path.GetFileName(path) + "):" + ex2.Message);
				}
				return false;
			}
		}
	}
	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;

		public static bool Enabled
		{
			get
			{
				if (ValheimCNPlugin.CfgBridgeGameResources != null)
				{
					return ValheimCNPlugin.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)
			{
				ValheimCNPlugin.Warn("[GameResourceBridge] prefabs scan failed: " + ex.Message);
			}
			try
			{
				ScanLocations();
			}
			catch (Exception ex2)
			{
				ValheimCNPlugin.Warn("[GameResourceBridge] locations scan failed: " + ex2.Message);
			}
		}

		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 LocEngine
	{
		internal static string DictDir;

		private static ManualLogSource _logger;

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

		private static string _cachedLang;

		private static float _nextLangCheck = -1f;

		private static bool _hasInactive;

		private static float _nextActivationCheck;

		internal static readonly Dictionary<string, string> PluginNameOverrides = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

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

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

		private const int CacheCap = 4096;

		private static readonly HashSet<Component> Pending = new HashSet<Component>();

		private const int PendingCap = 2048;

		private static Component[] _pendingBuffer;

		internal static int DictCount => Dicts.Count;

		internal static int EntryCount
		{
			get
			{
				int num = 0;
				for (int i = 0; i < Dicts.Count; i++)
				{
					num += Dicts[i].Exact.Count + Dicts[i].Regexes.Count;
				}
				return num;
			}
		}

		internal static IList<ModDictionary> Dictionaries => Dicts;

		internal static bool ConfigUiEnabled
		{
			get
			{
				if (ValheimCNPlugin.CfgConfigDescriptions == null || !ValheimCNPlugin.CfgConfigDescriptions.Value)
				{
					return false;
				}
				if (!ShouldTranslate())
				{
					return false;
				}
				RefreshActiveStateIfStale();
				return true;
			}
		}

		internal static void Initialize(string configPath, ManualLogSource logger)
		{
			_logger = logger;
			DictDir = Path.Combine(configPath, "ValheimCN");
			Directory.CreateDirectory(DictDir);
			SeedBundledDictionaries();
			LoadAll();
			RefreshActiveState();
		}

		private static void SeedBundledDictionaries()
		{
			try
			{
				string path = Path.Combine(Path.GetDirectoryName(typeof(LocEngine).Assembly.Location) ?? "", "Localization");
				if (!Directory.Exists(path))
				{
					return;
				}
				string[] files = Directory.GetFiles(path, "*.json", SearchOption.AllDirectories);
				foreach (string text in files)
				{
					string text2 = Path.Combine(DictDir, Path.GetFileName(text));
					if (!File.Exists(text2))
					{
						File.Copy(text, text2, overwrite: false);
						ManualLogSource logger = _logger;
						if (logger != null)
						{
							logger.LogInfo((object)("已释放词典 " + Path.GetFileName(text) + " 到 " + DictDir));
						}
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = _logger;
				if (logger2 != null)
				{
					logger2.LogWarning((object)("释放内置词典失败:" + ex.Message));
				}
			}
		}

		private static void LoadAll()
		{
			Dicts.Clear();
			string[] files;
			try
			{
				files = Directory.GetFiles(DictDir, "*.json", SearchOption.AllDirectories);
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)("读取词典目录失败:" + ex.Message));
				}
				return;
			}
			string[] array = files;
			foreach (string path in array)
			{
				try
				{
					ModDictionary modDictionary = Parse(File.ReadAllText(path, Encoding.UTF8), path);
					if (modDictionary == null)
					{
						continue;
					}
					Dicts.Add(modDictionary);
					if (!string.IsNullOrEmpty(modDictionary.MismatchNote))
					{
						ManualLogSource logger2 = _logger;
						if (logger2 != null)
						{
							logger2.LogInfo((object)(Path.GetFileName(path) + ":" + modDictionary.MismatchNote));
						}
					}
				}
				catch (Exception ex2)
				{
					ManualLogSource logger3 = _logger;
					if (logger3 != null)
					{
						logger3.LogError((object)("词典 " + Path.GetFileName(path) + " 解析失败:" + ex2.Message));
					}
				}
			}
		}

		private static ModDictionary Parse(string json, string path)
		{
			JObject val = JObject.Parse(json);
			ModDictionary modDictionary = new ModDictionary
			{
				FilePath = path
			};
			JToken obj = val["meta"];
			JObject val2 = (JObject)(object)((obj is JObject) ? obj : null);
			if (val2 != null)
			{
				modDictionary.TargetGuid = ((string)val2["targetGuid"]) ?? "";
				modDictionary.TargetName = ((string)val2["targetName"]) ?? "";
				modDictionary.TargetVersion = ((string)val2["targetVersion"]) ?? "";
				modDictionary.DisplayName = ((string)val2["displayName"]) ?? "";
				modDictionary.ExpectedMd5 = ((string)val2["md5"]) ?? "";
				modDictionary.ExpectedSize = ((long?)val2["size"]).GetValueOrDefault();
			}
			if (string.IsNullOrEmpty(modDictionary.TargetName))
			{
				modDictionary.TargetName = Path.GetFileNameWithoutExtension(path);
			}
			JToken obj2 = val["objects"];
			JArray val3 = (JArray)(object)((obj2 is JArray) ? obj2 : null);
			if (val3 != null)
			{
				foreach (JToken item in val3)
				{
					modDictionary.Objects.Add((string)item);
				}
			}
			JToken obj3 = val["objectPrefixes"];
			JArray val4 = (JArray)(object)((obj3 is JArray) ? obj3 : null);
			if (val4 != null)
			{
				foreach (JToken item2 in val4)
				{
					modDictionary.ObjectPrefixes.Add((string)item2);
				}
			}
			JToken obj4 = val["panelRoots"];
			JArray val5 = (JArray)(object)((obj4 is JArray) ? obj4 : null);
			if (val5 != null)
			{
				foreach (JToken item3 in val5)
				{
					modDictionary.PanelRoots.Add((string)item3);
				}
			}
			JToken obj5 = val["panelRootPrefixes"];
			JArray val6 = (JArray)(object)((obj5 is JArray) ? obj5 : null);
			if (val6 != null)
			{
				foreach (JToken item4 in val6)
				{
					modDictionary.PanelRootPrefixes.Add((string)item4);
				}
			}
			JToken obj6 = val["exact"];
			JObject val7 = (JObject)(object)((obj6 is JObject) ? obj6 : null);
			if (val7 != null)
			{
				foreach (KeyValuePair<string, JToken> item5 in val7)
				{
					modDictionary.Exact[item5.Key] = (string)item5.Value;
				}
			}
			JToken obj7 = val["regex"];
			JArray val8 = (JArray)(object)((obj7 is JArray) ? obj7 : null);
			if (val8 != null)
			{
				ParseRegexArray(val8, modDictionary, modDictionary.Regexes);
			}
			JToken obj8 = val["globalRegex"];
			JArray val9 = (JArray)(object)((obj8 is JArray) ? obj8 : null);
			if (val9 != null)
			{
				ParseRegexArray(val9, modDictionary, modDictionary.GlobalRegexes);
			}
			ReadMap(val["globalExact"], modDictionary.GlobalExact);
			ReadMap(val["configDescriptions"], modDictionary.ConfigDescriptions);
			ReadMap(val["configNames"], modDictionary.ConfigNames);
			ReadMap(val["configSections"], modDictionary.ConfigSections);
			ReadMap(val["configValues"], modDictionary.ConfigValues);
			JToken obj9 = val["loadingTips"];
			JArray val10 = (JArray)(object)((obj9 is JArray) ? obj9 : null);
			if (val10 != null)
			{
				foreach (JToken item6 in val10)
				{
					modDictionary.LoadingTips.Add((string)item6);
				}
			}
			VerifyFingerprint(modDictionary);
			return modDictionary;
		}

		private static void ReadMap(JToken token, Dictionary<string, string> target)
		{
			JObject val = (JObject)(object)((token is JObject) ? token : null);
			if (val == null)
			{
				return;
			}
			foreach (KeyValuePair<string, JToken> item in val)
			{
				target[item.Key] = (string)item.Value;
			}
		}

		private static void ParseRegexArray(JArray arr, ModDictionary d, List<RegexRule> target)
		{
			foreach (JToken item in arr)
			{
				string text = (string)item[(object)"pattern"];
				string text2 = (string)item[(object)"replacement"];
				bool valueOrDefault = (bool?)item[(object)"chain"] == true;
				if (string.IsNullOrEmpty(text))
				{
					string text3 = (string)item[(object)"template"];
					if (string.IsNullOrEmpty(text3))
					{
						continue;
					}
					try
					{
						RegexRule regexRule = new RegexRule
						{
							Template = text3,
							Replacement = (text2 ?? ""),
							Owner = d,
							Chain = valueOrDefault
						};
						regexRule.CompileTemplate();
						target.Add(regexRule);
					}
					catch (Exception ex)
					{
						ManualLogSource logger = _logger;
						if (logger != null)
						{
							logger.LogWarning((object)("插值规则无效,已跳过:" + text3 + "(" + ex.Message + ")"));
						}
					}
					continue;
				}
				try
				{
					target.Add(new RegexRule
					{
						Re = new Regex(text, RegexOptions.Compiled),
						Replacement = (text2 ?? ""),
						Chain = valueOrDefault
					});
				}
				catch (Exception ex2)
				{
					ManualLogSource logger2 = _logger;
					if (logger2 != null)
					{
						logger2.LogWarning((object)("正则无效,已跳过:" + text + "(" + ex2.Message + ")"));
					}
				}
			}
		}

		private static void VerifyFingerprint(ModDictionary d)
		{
			if (string.IsNullOrEmpty(d.ExpectedMd5) || string.IsNullOrEmpty(d.TargetName) || (!string.IsNullOrEmpty(d.TargetGuid) && !IsPluginLoaded(d.TargetGuid)))
			{
				return;
			}
			try
			{
				string text = LocateTargetDll(d);
				if (text != null && !string.Equals(Md5Of(text), d.ExpectedMd5, StringComparison.OrdinalIgnoreCase))
				{
					d.MismatchNote = "原版 MOD 有新版,词典稍旧:已汉化的文字照常生效;如发现未翻译的文案,请到「请求汉化」页提交反馈。";
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogInfo((object)("指纹校验跳过:" + ex.Message));
				}
			}
		}

		private static string LocateTargetDll(ModDictionary d)
		{
			try
			{
				if (!string.IsNullOrEmpty(d.TargetGuid) && Chainloader.PluginInfos.TryGetValue(d.TargetGuid, out var value) && !string.IsNullOrEmpty((value != null) ? value.Location : null) && File.Exists(value.Location))
				{
					return value.Location;
				}
			}
			catch (Exception)
			{
			}
			try
			{
				string[] files = Directory.GetFiles(Path.Combine(Paths.GameRootPath, "BepInEx", "plugins"), d.TargetName + ".dll", SearchOption.AllDirectories);
				return (files.Length != 0) ? files[0] : null;
			}
			catch (Exception)
			{
				return null;
			}
		}

		private static string CurrentFileVersion(string path)
		{
			try
			{
				string text = FileVersionInfo.GetVersionInfo(path)?.FileVersion;
				return string.IsNullOrEmpty(text) ? "未知版本" : text;
			}
			catch (Exception)
			{
				return "未知版本";
			}
		}

		internal static string Md5Of(string path)
		{
			using MD5 mD = MD5.Create();
			using FileStream inputStream = File.OpenRead(path);
			byte[] array = mD.ComputeHash(inputStream);
			StringBuilder stringBuilder = new StringBuilder(32);
			byte[] array2 = array;
			foreach (byte b in array2)
			{
				stringBuilder.Append(b.ToString("x2"));
			}
			return stringBuilder.ToString();
		}

		internal static void RefreshActiveState()
		{
			bool hasInactive = false;
			PluginNameOverrides.Clear();
			foreach (ModDictionary dict in Dicts)
			{
				if (string.IsNullOrEmpty(dict.TargetGuid))
				{
					dict.Active = true;
					continue;
				}
				bool flag = dict.ActiveChecked && dict.Active;
				bool flag2 = (dict.Active = IsPluginLoaded(dict.TargetGuid));
				dict.ActiveChecked = true;
				if (flag2 && !flag)
				{
					ConfigLocalizer.RequestFor(dict);
					VerifyFingerprint(dict);
					ClearCaches();
				}
				if (flag2 && !string.IsNullOrEmpty(dict.DisplayName))
				{
					PluginNameOverrides[dict.TargetGuid] = dict.DisplayName;
				}
				if (!flag2)
				{
					hasInactive = true;
					dict.MismatchNote = null;
					if (!dict.NotifiedInactive)
					{
						dict.NotifiedInactive = true;
						ManualLogSource logger = _logger;
						if (logger != null)
						{
							logger.LogInfo((object)(Path.GetFileName(dict.FilePath) + ":未检测到插件 " + dict.TargetGuid + ",词典停用。"));
						}
						if (flag)
						{
							ClearCaches();
						}
					}
				}
				else if (dict.NotifiedInactive)
				{
					dict.NotifiedInactive = false;
					ManualLogSource logger2 = _logger;
					if (logger2 != null)
					{
						logger2.LogInfo((object)(Path.GetFileName(dict.FilePath) + ":检测到插件 " + dict.TargetGuid + ",词典已启用。"));
					}
				}
			}
			_hasInactive = hasInactive;
			_nextActivationCheck = Time.realtimeSinceStartup + 5f;
		}

		internal static void Tick()
		{
			if (_hasInactive && !(Time.realtimeSinceStartup < _nextActivationCheck))
			{
				RefreshActiveState();
			}
		}

		private static void RefreshActiveStateIfStale()
		{
			if (_hasInactive && !(Time.realtimeSinceStartup < _nextActivationCheck))
			{
				RefreshActiveState();
			}
		}

		internal static bool IsPluginLoaded(string guid)
		{
			try
			{
				return Chainloader.PluginInfos.ContainsKey(guid);
			}
			catch (Exception)
			{
				return false;
			}
		}

		internal static bool IsEnglishGame()
		{
			RefreshLanguageIfNeeded();
			return string.Equals(_cachedLang, "English", StringComparison.OrdinalIgnoreCase);
		}

		internal static string CurrentLanguage()
		{
			RefreshLanguageIfNeeded();
			return _cachedLang;
		}

		private static void RefreshLanguageIfNeeded()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (_cachedLang != null && realtimeSinceStartup < _nextLangCheck)
			{
				return;
			}
			_nextLangCheck = realtimeSinceStartup + 2f;
			try
			{
				Localization instance = Localization.instance;
				if (instance == null)
				{
					_cachedLang = "";
				}
				else
				{
					_cachedLang = instance.GetSelectedLanguage() ?? "";
				}
			}
			catch (Exception)
			{
				_cachedLang = "";
			}
		}

		internal static bool ShouldTranslate()
		{
			if ((Object)(object)ValheimCNPlugin.Instance == (Object)null)
			{
				return false;
			}
			if (ValheimCNPlugin.CfgEnabled == null || !ValheimCNPlugin.CfgEnabled.Value)
			{
				return false;
			}
			return !IsEnglishGame();
		}

		internal static string Translate(string text, Component host)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			if (!ShouldTranslate())
			{
				return text;
			}
			RefreshActiveStateIfStale();
			bool flag = (Object)(object)host != (Object)null && (Object)(object)host.gameObject != (Object)null && host.gameObject.activeInHierarchy;
			for (int i = 0; i < Dicts.Count; i++)
			{
				ModDictionary modDictionary = Dicts[i];
				if (!modDictionary.Active)
				{
					continue;
				}
				string text2 = null;
				if (modDictionary.OwnsObject(host))
				{
					string text3 = modDictionary.Translate(text);
					if (text3 != null)
					{
						if (text3 == text)
						{
							if (!flag)
							{
								QueuePending(host);
							}
						}
						else if (!flag)
						{
							QueuePending(host);
						}
						else
						{
							text2 = text3;
						}
					}
				}
				else if (modDictionary.HasHierarchyRoot && modDictionary.WouldTranslate(text))
				{
					QueuePending(host);
				}
				if (text2 == null && (modDictionary.GlobalRegexes.Count > 0 || modDictionary.GlobalExact.Count > 0))
				{
					text2 = modDictionary.TranslateGlobal(text);
				}
				if (text2 == null)
				{
					continue;
				}
				if (ValheimCNPlugin.CfgVerbose != null && ValheimCNPlugin.CfgVerbose.Value)
				{
					string text4 = (((Object)(object)host != (Object)null && (Object)(object)host.gameObject != (Object)null) ? ((Object)host.gameObject).name : "(global)");
					ManualLogSource logger = _logger;
					if (logger != null)
					{
						logger.LogInfo((object)("[ValheimCN] " + text4 + ": \"" + text + "\" -> \"" + text2 + "\""));
					}
				}
				return text2;
			}
			if (GameResourceBridge.TryBridge(text, out var cn))
			{
				return cn;
			}
			return text;
		}

		internal static string TranslateGlobalOnly(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			if (!ShouldTranslate())
			{
				return text;
			}
			if (GuiCache.TryGetValue(text, out var value))
			{
				return value;
			}
			RefreshActiveStateIfStale();
			string text2 = text;
			for (int i = 0; i < Dicts.Count; i++)
			{
				ModDictionary modDictionary = Dicts[i];
				if (!modDictionary.Active)
				{
					continue;
				}
				string text3 = modDictionary.TranslateGlobal(text);
				if (text3 == null)
				{
					continue;
				}
				if (ValheimCNPlugin.CfgVerbose != null && ValheimCNPlugin.CfgVerbose.Value)
				{
					ManualLogSource logger = _logger;
					if (logger != null)
					{
						logger.LogInfo((object)("[ValheimCN] (imgui): \"" + text + "\" -> \"" + text3 + "\""));
					}
				}
				text2 = text3;
				break;
			}
			CachePut(GuiCache, text, text2);
			return text2;
		}

		private static void CachePut(Dictionary<string, string> cache, string key, string value)
		{
			if (cache.Count >= 4096)
			{
				cache.Clear();
			}
			cache[key] = value;
		}

		private static void ClearCaches()
		{
			GuiCache.Clear();
			CfgCache.Clear();
		}

		private static void QueuePending(Component host)
		{
			if (!((Object)(object)host == (Object)null) && Pending.Count < 2048)
			{
				Pending.Add(host);
			}
		}

		internal static void DrainPending()
		{
			if (Pending.Count == 0 || !ShouldTranslate())
			{
				return;
			}
			int count = Pending.Count;
			if (_pendingBuffer == null || _pendingBuffer.Length < count)
			{
				_pendingBuffer = (Component[])(object)new Component[count];
			}
			Pending.CopyTo(_pendingBuffer);
			for (int i = 0; i < count; i++)
			{
				Component val = _pendingBuffer[i];
				_pendingBuffer[i] = null;
				if ((Object)(object)val == (Object)null)
				{
					Pending.Remove(val);
					continue;
				}
				GameObject gameObject = val.gameObject;
				if ((Object)(object)gameObject == (Object)null)
				{
					Pending.Remove(val);
				}
				else if (gameObject.activeInHierarchy)
				{
					if (!OwnedByActiveDict(val))
					{
						Pending.Remove(val);
						continue;
					}
					ApplyCurrentText(val);
					Pending.Remove(val);
				}
			}
		}

		private static bool OwnedByActiveDict(Component host)
		{
			for (int i = 0; i < Dicts.Count; i++)
			{
				ModDictionary modDictionary = Dicts[i];
				if (modDictionary.Active && modDictionary.OwnsObject(host))
				{
					return true;
				}
			}
			return false;
		}

		private static void ApplyCurrentText(Component host)
		{
			try
			{
				Text val = (Text)(object)((host is Text) ? host : null);
				if ((Object)(object)val != (Object)null)
				{
					string text = val.text;
					val.text = text;
					return;
				}
				TMP_Text val2 = (TMP_Text)(object)((host is TMP_Text) ? host : null);
				if ((Object)(object)val2 != (Object)null)
				{
					string text2 = val2.text;
					val2.text = text2;
				}
			}
			catch (Exception)
			{
			}
		}

		internal static ModDictionary Get(string targetGuid)
		{
			for (int i = 0; i < Dicts.Count; i++)
			{
				if (string.Equals(Dicts[i].TargetGuid, targetGuid, StringComparison.OrdinalIgnoreCase))
				{
					return Dicts[i];
				}
			}
			return null;
		}

		internal static string ConfigName(string guid, string key)
		{
			return CfgCached("N", guid, key, (ModDictionary d) => d.TranslateConfigName(key));
		}

		internal static string ConfigDescription(string guid, string desc)
		{
			return CfgCached("D", guid, desc, (ModDictionary d) => d.TranslateConfigDescription(desc));
		}

		internal static string ConfigSection(string guid, string section)
		{
			return CfgCached("S", guid, section, (ModDictionary d) => d.TranslateConfigSection(section));
		}

		internal static string ConfigValue(string guid, string value)
		{
			return CfgCached("V", guid, value, (ModDictionary d) => d.TranslateConfigValue(value));
		}

		internal static string BilingualizeDescription(string guid, string cnDesc)
		{
			if (string.IsNullOrEmpty(cnDesc))
			{
				return cnDesc;
			}
			if (!ConfigUiEnabled)
			{
				return cnDesc;
			}
			string key = "B\n" + guid + "\n" + cnDesc;
			if (CfgCache.TryGetValue(key, out var value))
			{
				return value;
			}
			ModDictionary modDictionary = ActiveDict(guid);
			string text = ((modDictionary == null) ? cnDesc : modDictionary.BilingualizeValueNames(cnDesc));
			CachePut(CfgCache, key, text);
			return text;
		}

		private static string CfgCached(string channel, string guid, string input, Func<ModDictionary, string> lookup)
		{
			if (string.IsNullOrEmpty(input))
			{
				return null;
			}
			if (!ConfigUiEnabled)
			{
				return null;
			}
			string key = channel + "\n" + guid + "\n" + input;
			if (CfgCache.TryGetValue(key, out var value))
			{
				if (value.Length != 0)
				{
					return value;
				}
				return null;
			}
			ModDictionary modDictionary = ActiveDict(guid);
			string text = ((modDictionary == null) ? null : lookup(modDictionary));
			CachePut(CfgCache, key, text ?? "");
			return text;
		}

		private static ModDictionary ActiveDict(string guid)
		{
			if (string.IsNullOrEmpty(guid))
			{
				return null;
			}
			ModDictionary modDictionary = Get(guid);
			if (modDictionary == null || !modDictionary.Active)
			{
				return null;
			}
			return modDictionary;
		}

		internal static void Reload()
		{
			LoadAll();
			RefreshActiveState();
			ClearCaches();
		}
	}
	internal sealed class RegexRule
	{
		public Regex Re;

		public string Replacement;

		public string Template;

		public bool Chain;

		public ModDictionary Owner;

		private Regex _templateRegex;

		private int[] _slotIndexes;

		private string _literalProbe;

		private static readonly Regex Placeholder = new Regex("{([0-9])}", RegexOptions.Compiled);

		private static readonly Regex NonAscii = new Regex("[^\\x00-\\x7F]", RegexOptions.Compiled);

		private string BuildLiteralProbe()
		{
			string text = (string.IsNullOrEmpty(Replacement) ? Template : Replacement);
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			string text2 = Placeholder.Replace(text, "");
			if (string.IsNullOrEmpty(text2))
			{
				return null;
			}
			Match match = NonAscii.Match(text2);
			if (!match.Success)
			{
				return null;
			}
			int index = match.Index;
			int i;
			for (i = index; i < text2.Length && (text2[i] < '0' || text2[i] > '9'); i++)
			{
			}
			string text3 = text2.Substring(index, i - index).Trim();
			while (text3.Length > 0 && " :,.".IndexOf(text3[0]) >= 0)
			{
				text3 = text3.Substring(1);
			}
			while (text3.Length > 0 && " :,.".IndexOf(text3[text3.Length - 1]) >= 0)
			{
				text3 = text3.Substring(0, text3.Length - 1);
			}
			if (text3.Length < 2)
			{
				return null;
			}
			return text3;
		}

		public bool ApplyTemplate(string text)
		{
			if (!string.IsNullOrEmpty(_literalProbe) && text.IndexOf(_literalProbe, StringComparison.Ordinal) >= 0)
			{
				return false;
			}
			if (_templateRegex == null)
			{
				return false;
			}
			Match match = _templateRegex.Match(text);
			if (!match.Success)
			{
				return false;
			}
			string replacement = Placeholder.Replace(Replacement ?? "", delegate(Match p)
			{
				int num = p.Groups[1].Value[0] - 48;
				int num2 = -1;
				for (int i = 0; i < _slotIndexes.Length; i++)
				{
					if (_slotIndexes[i] == num)
					{
						num2 = i;
						break;
					}
				}
				return (num2 >= 0) ? ("$" + (num2 + 1)) : "";
			});
			string value = match.Result(replacement);
			Owner.TemplateResults[text] = value;
			return true;
		}

		public void CompileTemplate()
		{
			if (string.IsNullOrEmpty(Template))
			{
				return;
			}
			MatchCollection matchCollection = Placeholder.Matches(Template);
			List<int> list = new List<int>();
			foreach (Match item in matchCollection)
			{
				list.Add(item.Groups[1].Value[0] - 48);
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append('^');
			int num = 0;
			int num2 = 0;
			foreach (Match item2 in matchCollection)
			{
				stringBuilder.Append(EscapeLiteral(Template.Substring(num, item2.Index - num)));
				_ = item2.Groups[1].Value[0];
				bool flag = item2.Index + item2.Length < Template.Length;
				if (IsNumericSlot(Template, item2))
				{
					stringBuilder.Append("(?<![\\d.,])([\\d.,]+)");
				}
				else if (flag)
				{
					stringBuilder.Append("([^\\n]*?)");
				}
				else
				{
					stringBuilder.Append("([^\\n]*)");
				}
				num = item2.Index + item2.Length;
				num2++;
			}
			stringBuilder.Append(EscapeLiteral(Template.Substring(num)));
			stringBuilder.Append('$');
			_templateRegex = new Regex(stringBuilder.ToString(), RegexOptions.Singleline);
			_slotIndexes = list.ToArray();
			_literalProbe = BuildLiteralProbe();
		}

		private static bool IsNumericSlot(string template, Match slot)
		{
			int num = slot.Index + slot.Length;
			if (num < template.Length)
			{
				char c = template[num];
				if (c >= '0' && c <= '9')
				{
					return true;
				}
			}
			return false;
		}

		private static string EscapeLiteral(string s)
		{
			if (s.Length != 0)
			{
				return Regex.Escape(s);
			}
			return "";
		}
	}
	internal sealed class ModDictionary
	{
		public string FilePath;

		public string TargetGuid;

		public string TargetName;

		public string TargetVersion;

		public string DisplayName;

		public string ExpectedMd5;

		public long ExpectedSize;

		public readonly HashSet<string> Objects = new HashSet<string>(StringComparer.Ordinal);

		public readonly List<string> ObjectPrefixes = new List<string>();

		public readonly List<string> PanelRoots = new List<string>();

		public readonly List<string> PanelRootPrefixes = new List<string>();

		public readonly List<RegexRule> GlobalRegexes = new List<RegexRule>();

		public readonly Dictionary<string, string> GlobalExact = new Dictionary<string, string>(StringComparer.Ordinal);

		public readonly Dictionary<string, string> Exact = new Dictionary<string, string>(StringComparer.Ordinal);

		public readonly List<RegexRule> Regexes = new List<RegexRule>();

		public readonly Dictionary<string, string> ConfigDescriptions = new Dictionary<string, string>(StringComparer.Ordinal);

		public readonly Dictionary<string, string> ConfigNames = new Dictionary<string, string>(StringComparer.Ordinal);

		public readonly Dictionary<string, string> ConfigSections = new Dictionary<string, string>(StringComparer.Ordinal);

		public readonly Dictionary<string, string> ConfigValues = new Dictionary<string, string>(StringComparer.Ordinal);

		public readonly List<string> LoadingTips = new List<string>();

		public readonly Dictionary<string, string> TemplateResults = new Dictionary<string, string>(StringComparer.Ordinal);

		public bool Active = true;

		public bool ActiveChecked;

		public bool NotifiedInactive;

		public string MismatchNote;

		private static readonly Regex ColorTag = new Regex("<color=(#[0-9A-Fa-f]{6,8})>(.*?)</color>", RegexOptions.Compiled);

		private static readonly Regex LetterProbe = new Regex("[A-Za-z]", RegexOptions.Compiled);

		public bool HasHierarchyRoot
		{
			get
			{
				if (PanelRoots.Count <= 0)
				{
					return PanelRootPrefixes.Count > 0;
				}
				return true;
			}
		}

		public bool HasConfigText
		{
			get
			{
				if (ConfigDescriptions.Count <= 0 && ConfigNames.Count <= 0 && ConfigSections.Count <= 0)
				{
					return ConfigValues.Count > 0;
				}
				return true;
			}
		}

		public bool OwnsObject(Component host)
		{
			if ((Object)(object)host == (Object)null || (Object)(object)host.gameObject == (Object)null)
			{
				return false;
			}
			if (OwnsByName(((Object)host.gameObject).name))
			{
				return true;
			}
			if (PanelRoots.Count == 0 && PanelRootPrefixes.Count == 0)
			{
				return false;
			}
			Transform parent = host.transform.parent;
			while ((Object)(object)parent != (Object)null)
			{
				if (OwnsRoot(((Object)((Component)parent).gameObject).name))
				{
					return true;
				}
				parent = parent.parent;
			}
			return false;
		}

		private bool OwnsByName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			if (Objects.Contains(name))
			{
				return true;
			}
			for (int i = 0; i < ObjectPrefixes.Count; i++)
			{
				if (name.StartsWith(ObjectPrefixes[i], StringComparison.Ordinal))
				{
					return true;
				}
			}
			return false;
		}

		private bool OwnsRoot(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			if (PanelRoots.Contains(name))
			{
				return true;
			}
			for (int i = 0; i < PanelRootPrefixes.Count; i++)
			{
				if (name.StartsWith(PanelRootPrefixes[i], StringComparison.Ordinal))
				{
					return true;
				}
			}
			return false;
		}

		public string Translate(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			if (!LetterProbe.IsMatch(text))
			{
				return null;
			}
			if (text.IndexOf('\n') >= 0)
			{
				if (Exact.TryGetValue(text, out var value))
				{
					return value;
				}
				string[] array = text.Split(new char[1] { '\n' });
				bool flag = false;
				for (int i = 0; i < array.Length; i++)
				{
					string text2 = TranslateLine(array[i]);
					if ((object)text2 != array[i])
					{
						array[i] = text2;
						flag = true;
					}
				}
				if (flag)
				{
					return string.Join("\n", array);
				}
				return TranslateFlat(text);
			}
			return TranslateRichLine(text);
		}

		private string TranslateLine(string line)
		{
			if (string.IsNullOrEmpty(line))
			{
				return line;
			}
			return TranslateRichLine(line) ?? line;
		}

		private string TranslateRichLine(string line)
		{
			Match match = ColorTag.Match(line);
			if (match.Success)
			{
				Group obj = match.Groups[2];
				string text = TranslateFlat(obj.Value);
				if (text != null)
				{
					return line.Substring(0, obj.Index) + text + line.Substring(obj.Index + obj.Length);
				}
			}
			return TranslateFlat(line);
		}

		private string TranslateFlat(string text)
		{
			if (Exact.TryGetValue(text, out var value))
			{
				return value;
			}
			TemplateResults.Clear();
			return RunRegexes(text, Regexes);
		}

		public string TranslateGlobal(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			if (!LetterProbe.IsMatch(text))
			{
				return null;
			}
			if (text.IndexOf('\n') >= 0)
			{
				if (TryGlobalExact(text, out var result))
				{
					return result;
				}
				string[] array = text.Split(new char[1] { '\n' });
				bool flag = false;
				for (int i = 0; i < array.Length; i++)
				{
					string text2 = TranslateGlobalLine(array[i]);
					if (text2 != null)
					{
						array[i] = text2;
						flag = true;
					}
				}
				if (!flag)
				{
					return null;
				}
				return string.Join("\n", array);
			}
			return TranslateGlobalLine(text);
		}

		private string TranslateGlobalLine(string line)
		{
			if (string.IsNullOrEmpty(line))
			{
				return null;
			}
			string text = TranslateGlobalFlat(line);
			if (text != null)
			{
				return text;
			}
			Match match = ColorTag.Match(line);
			if (match.Success)
			{
				Group obj = match.Groups[2];
				string text2 = TranslateGlobalFlat(obj.Value);
				if (text2 != null)
				{
					return line.Substring(0, obj.Index) + text2 + line.Substring(obj.Index + obj.Length);
				}
			}
			return null;
		}

		private string TranslateGlobalFlat(string text)
		{
			if (string.IsNullOrEmpty(text) || !LetterProbe.IsMatch(text))
			{
				return null;
			}
			if (TryGlobalExact(text, out var result))
			{
				return result;
			}
			if (GlobalRegexes.Count == 0)
			{
				return null;
			}
			TemplateResults.Clear();
			string text2 = RunRegexes(text, GlobalRegexes);
			if (text2 != null)
			{
				return text2;
			}
			if (SplitDecoration(text, out var lead, out var tail, out var core))
			{
				TemplateResults.Clear();
				text2 = RunRegexes(core, GlobalRegexes);
				if (text2 != null)
				{
					return lead + text2 + tail;
				}
			}
			return null;
		}

		private bool TryGlobalExact(string text, out string result)
		{
			result = null;
			if (GlobalExact.TryGetValue(text, out result))
			{
				return true;
			}
			if (!SplitDecoration(text, out var lead, out var tail, out var core))
			{
				return false;
			}
			if (!GlobalExact.TryGetValue(core, out result))
			{
				return false;
			}
			result = lead + result + tail;
			return true;
		}

		private static bool SplitDecoration(string text, out string lead, out string tail, out string core)
		{
			lead = null;
			tail = null;
			core = null;
			int i = 0;
			int num;
			for (num = text.Length; i < num && char.IsWhiteSpace(text[i]); i++)
			{
			}
			while (num > i && char.IsWhiteSpace(text[num - 1]))
			{
				num--;
			}
			if (i + 1 < num && !char.IsLetterOrDigit(text[i]) && !char.IsWhiteSpace(text[i]))
			{
				int j;
				for (j = i + 1; j < num && char.IsWhiteSpace(text[j]); j++)
				{
				}
				if (j < num && j > i + 1)
				{
					i = j;
				}
			}
			if (i == 0 && num == text.Length)
			{
				return false;
			}
			core = text.Substring(i, num - i);
			if (core.Length == 0)
			{
				return false;
			}
			lead = text.Substring(0, i);
			tail = text.Substring(num);
			return true;
		}

		public bool WouldTranslate(string text)
		{
			return Translate(text) != null;
		}

		private string RunRegexes(string text, List<RegexRule> rules)
		{
			if (rules == null || rules.Count == 0)
			{
				return null;
			}
			string text2 = text;
			bool flag = false;
			for (int i = 0; i < rules.Count; i++)
			{
				RegexRule regexRule = rules[i];
				string value2;
				if (string.IsNullOrEmpty(regexRule.Template))
				{
					if (regexRule.Re == null)
					{
						continue;
					}
					if (regexRule.Chain)
					{
						if (regexRule.Re.IsMatch(text2))
						{
							text2 = regexRule.Re.Replace(text2, regexRule.Replacement);
							flag = true;
						}
					}
					else if (regexRule.Re.IsMatch(text2))
					{
						return regexRule.Re.Replace(text2, regexRule.Replacement);
					}
				}
				else if (regexRule.Chain)
				{
					if (regexRule.ApplyTemplate(text2) && TemplateResults.TryGetValue(text2, out var value))
					{
						text2 = value;
						flag = true;
					}
				}
				else if (regexRule.ApplyTemplate(text2) && TemplateResults.TryGetValue(text2, out value2))
				{
					return value2;
				}
			}
			if (!flag)
			{
				return null;
			}
			return text2;
		}

		public string TranslateConfigDescription(string desc)
		{
			if (string.IsNullOrEmpty(desc))
			{
				return null;
			}
			if (!ConfigDescriptions.TryGetValue(desc, out var value))
			{
				return null;
			}
			return value;
		}

		public string TranslateConfigName(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return null;
			}
			if (!ConfigNames.TryGetValue(key, out var value))
			{
				return null;
			}
			return value;
		}

		public string TranslateConfigSection(string section)
		{
			if (string.IsNullOrEmpty(section))
			{
				return null;
			}
			if (!ConfigSections.TryGetValue(section, out var value))
			{
				return null;
			}
			return value;
		}

		public string TranslateConfigValue(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return null;
			}
			if (ConfigValues.TryGetValue(value, out var value2))
			{
				return value2;
			}
			string text = value.Replace(" ", "");
			if (!string.Equals(text, value, StringComparison.Ordinal) && ConfigValues.TryGetValue(text, out value2))
			{
				return value2;
			}
			return null;
		}

		public string BilingualizeValueNames(string cnDesc)
		{
			if (string.IsNullOrEmpty(cnDesc) || ConfigValues.Count == 0)
			{
				return cnDesc;
			}
			string[] array = cnDesc.Split(new char[1] { '\n' });
			bool flag = false;
			for (int i = 0; i < array.Length; i++)
			{
				string text = BilingualizeLine(array[i]);
				if (text != array[i])
				{
					array[i] = text;
					flag = true;
				}
			}
			if (!flag)
			{
				return cnDesc;
			}
			return string.Join("\n", array);
		}

		private string BilingualizeLine(string line)
		{
			int num = FirstColon(line);
			int num2 = line.IndexOf('=');
			int num3 = ((num > 0) ? num : ((num2 > 0) ? num2 : (-1)));
			if (num3 > 0)
			{
				string text = line.Substring(0, num3).Trim();
				if (text.Length > 0 && IsAsciiName(text))
				{
					string text2 = TranslateConfigValue(text);
					if (text2 != null)
					{
						return text2 + "(" + text + ")" + line.Substring(num3);
					}
				}
			}
			if (num > 0)
			{
				int i;
				for (i = num + 1; i < line.Length && line[i] == ' '; i++)
				{
				}
				int j;
				for (j = i; j < line.Length && IsNameChar(line[j]); j++)
				{
				}
				string text3 = line.Substring(i, j - i).Trim();
				if (text3.Length > 0 && IsAsciiName(text3))
				{
					string text4 = TranslateConfigValue(text3);
					if (text4 != null)
					{
						return line.Substring(0, i) + text4 + "(" + text3 + ")" + line.Substring(j);
					}
				}
			}
			return line;
		}

		private static bool IsNameChar(char c)
		{
			if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') && c != '_')
			{
				return c == ' ';
			}
			return true;
		}

		private static int FirstColon(string s)
		{
			int num = s.IndexOf(':');
			int num2 = s.IndexOf(':');
			if (num < 0)
			{
				return num2;
			}
			if (num2 < 0)
			{
				return num;
			}
			if (num >= num2)
			{
				return num2;
			}
			return num;
		}

		private static bool IsAsciiName(string s)
		{
			bool result = false;
			foreach (char c in s)
			{
				if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
				{
					result = true;
				}
				else if ((c < '0' || c > '9') && c != '_' && c != ' ')
				{
					return false;
				}
			}
			return result;
		}

		public string GetLoadingTip(Random rng)
		{
			if (LoadingTips.Count == 0)
			{
				return null;
			}
			int index = rng?.Next(LoadingTips.Count) ?? new Random().Next(LoadingTips.Count);
			return LoadingTips[index];
		}
	}
	internal static class ConfigManagerPatches
	{
		private static bool _applied;

		private static PropertyInfo _pluginInfoGetter;

		private static float _nextInstallTry;

		private static int _installTries;

		internal static bool SuppressImgui;

		private static string _currentGuid;

		internal static string CurrentGuid => _currentGuid;

		internal static bool Applied => _applied;

		internal static void TryLateInstall()
		{
			if (!_applied && _installTries < 30)
			{
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if (!(realtimeSinceStartup < _nextInstallTry))
				{
					_nextInstallTry = realtimeSinceStartup + 2f;
					_installTries++;
					Install(ValheimCNPlugin.HarmonyInstance);
				}
			}
		}

		internal static void Install(Harmony harmony)
		{
			if (_applied || harmony == null)
			{
				return;
			}
			try
			{
				Type type = AccessTools.TypeByName("ConfigurationManager.SettingEntryBase");
				if (!(type == null))
				{
					Type type2 = AccessTools.TypeByName("ConfigurationManager.SettingFieldDrawer");
					Type type3 = AccessTools.TypeByName("ConfigurationManager.ConfigurationManager");
					_pluginInfoGetter = type.GetProperty("PluginInfo", BindingFlags.Instance | BindingFlags.Public);
					bool num = Patch(harmony, AccessTools.Method(type, "get_DispName", (Type[])null, (Type[])null), null, "DispNamePostfix");
					Patch(harmony, AccessTools.Method(type, "get_Category", (Type[])null, (Type[])null), null, "CategoryPostfix");
					Patch(harmony, AccessTools.Method(type, "get_Description", (Type[])null, (Type[])null), null, "DescriptionPostfix");
					if (type2 != null)
					{
						Patch(harmony, AccessTools.Method(type2, "DrawComboboxField", (Type[])null, (Type[])null), "ContextPrefix", null);
						Patch(harmony, AccessTools.Method(type2, "DrawComboboxField", (Type[])null, (Type[])null), "ComboboxSuppressPrefix", null, "ComboboxSuppressFinalizer");
						Patch(harmony, AccessTools.Method(type2, "ObjectToGuiContent", (Type[])null, (Type[])null), null, "ValuePostfix");
					}
					if (type3 != null)
					{
						Patch(harmony, AccessTools.Method(type3, "DrawSingleSetting", (Type[])null, (Type[])null), "ContextPrefix", null);
					}
					if (num)
					{
						_applied = true;
						ValheimCNPlugin.Log("已挂载配置界面汉化补丁(条目名 / 分组标题 / 悬停提示;下拉框值不翻)。");
					}
				}
			}
			catch (Exception ex)
			{
				ValheimCNPlugin.Warn("配置界面补丁挂载失败:" + ex.Message);
			}
		}

		private static bool Patch(Harmony harmony, MethodBase target, string prefix, string postfix, string finalizer = null)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if (target == null)
			{
				return false;
			}
			try
			{
				MethodInfo methodInfo = ((prefix == null) ? null : AccessTools.Method(typeof(ConfigManagerPatches), prefix, (Type[])null, (Type[])null));
				MethodInfo methodInfo2 = ((postfix == null) ? null : AccessTools.Method(typeof(ConfigManagerPatches), postfix, (Type[])null, (Type[])null));
				MethodInfo methodInfo3 = ((finalizer == null) ? null : AccessTools.Method(typeof(ConfigManagerPatches), finalizer, (Type[])null, (Type[])null));
				if (methodInfo == null && methodInfo2 == null && methodInfo3 == null)
				{
					return false;
				}
				harmony.Patch(target, (methodInfo == null) ? ((HarmonyMethod)null) : new HarmonyMethod(methodInfo), (methodInfo2 == null) ? ((HarmonyMethod)null) : new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (methodInfo3 == null) ? ((HarmonyMethod)null) : new HarmonyMethod(methodInfo3), (HarmonyMethod)null);
				return true;
			}
			catch (Exception ex)
			{
				ValheimCNPlugin.Warn("配置界面补丁跳过 " + target.Name + ":" + ex.Message);
				return false;
			}
		}

		private static void DispNamePostfix(object __instance, ref string __result)
		{
			if (!string.IsNullOrEmpty(__result))
			{
				bool flag = __result[0] == '!';
				string key = (flag ? __result.Substring(1) : __result);
				string text = LocEngine.ConfigName(GuidOf(__instance), key);
				if (!string.IsNullOrEmpty(text))
				{
					__result = (flag ? ("!" + text) : text);
				}
			}
		}

		private static void CategoryPostfix(object __instance, ref string __result)
		{
			if (!string.IsNullOrEmpty(__result))
			{
				string text = LocEngine.ConfigSection(GuidOf(__instance), __result);
				if (!string.IsNullOrEmpty(text))
				{
					__result = text;
				}
			}
		}

		private static void DescriptionPostfix(object __instance, ref string __result)
		{
			if (!string.IsNullOrEmpty(__result))
			{
				string guid = GuidOf(__instance);
				string text = LocEngine.ConfigDescription(guid, __result);
				if (!string.IsNullOrEmpty(text))
				{
					__result = text;
				}
				__result = LocEngine.BilingualizeDescription(guid, __result);
			}
		}

		private static void ContextPrefix(object __0)
		{
			_currentGuid = ((__0 == null) ? null : GuidOf(__0));
		}

		private static void ComboboxSuppressPrefix()
		{
			SuppressImgui = true;
		}

		private static void ComboboxSuppressFinalizer()
		{
			SuppressImgui = false;
			_currentGuid = null;
		}

		private static void ValuePostfix(object __0, ref GUIContent __result)
		{
			if (__result == null || string.IsNullOrEmpty(_currentGuid))
			{
				return;
			}
			string text = __result.text;
			if (!string.IsNullOrEmpty(text))
			{
				string text2 = LocEngine.ConfigValue(_currentGuid, text);
				if (text2 == null && __0 is Enum)
				{
					text2 = LocEngine.ConfigValue(_currentGuid, __0.ToString());
				}
				if (!string.IsNullOrEmpty(text2))
				{
					__result.text = text2;
				}
			}
		}

		private static string GuidOf(object entry)
		{
			if (entry == null || _pluginInfoGetter == null)
			{
				return null;
			}
			try
			{
				object? value = _pluginInfoGetter.GetValue(entry, null);
				BepInPlugin val = (BepInPlugin)((value is BepInPlugin) ? value : null);
				return (val == null) ? null : val.GUID;
			}
			catch (Exception)
			{
				return null;
			}
		}
	}
	internal static class ConfigSavePatch
	{
		internal static void Apply(Harmony harmony)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			if (harmony == null)
			{
				return;
			}
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(ConfigFile), "Save", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					ValheimCNPlugin.Warn("未找到 ConfigFile.Save,cfg 注释保存后置补丁未挂载。");
					return;
				}
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(ConfigSavePatch), "Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				ValheimCNPlugin.Log("已拦截 ConfigFile.Save(保存后同步汉化 cfg 注释)。");
			}
			catch (Exception ex)
			{
				ValheimCNPlugin.Warn("cfg 补丁挂载失败:" + ex.Message);
			}
		}

		private static void Postfix(ConfigFile __instance)
		{
			try
			{
				ConfigLocalizer.OnConfigSaved(__instance);
			}
			catch (Exception)
			{
			}
		}
	}
	internal static class GuiLabelPatch
	{
		internal static int Apply(Harmony harmony)
		{
			int num = 0;
			num += Patch(harmony, typeof(GUI), "Label", new Type[3]
			{
				typeof(Rect),
				typeof(string),
				typeof(GUIStyle)
			});
			num += Patch(harmony, typeof(GUI), "Label", new Type[2]
			{
				typeof(Rect),
				typeof(string)
			});
			num += Patch(harmony, typeof(GUI), "Button", new Type[3]
			{
				typeof(Rect),
				typeof(string),
				typeof(GUIStyle)
			});
			num += Patch(harmony, typeof(GUI), "Button", new Type[2]
			{
				typeof(Rect),
				typeof(string)
			});
			num += Patch(harmony, typeof(GUI), "Toggle", new Type[4]
			{
				typeof(Rect),
				typeof(bool),
				typeof(string),
				typeof(GUIStyle)
			});
			num += Patch(harmony, typeof(GUI), "Toggle", new Type[3]
			{
				typeof(Rect),
				typeof(bool),
				typeof(string)
			});
			num += Patch(harmony, typeof(GUI), "Box", new Type[3]
			{
				typeof(Rect),
				typeof(string),
				typeof(GUIStyle)
			});
			num += Patch(harmony, typeof(GUI), "Box", new Type[2]
			{
				typeof(Rect),
				typeof(string)
			});
			num += Patch(harmony, typeof(GUI), "Window", new Type[4]
			{
				typeof(int),
				typeof(Rect),
				typeof(WindowFunction),
				typeof(string)
			});
			num += Patch(harmony, typeof(GUI), "Window", new Type[5]
			{
				typeof(int),
				typeof(Rect),
				typeof(WindowFunction),
				typeof(string),
				typeof(GUIStyle)
			});
			num += Patch(harmony, typeof(GUI), "Window", new Type[4]
			{
				typeof(int),
				typeof(Rect),
				typeof(Func<int>),
				typeof(string)
			});
			num += Patch(harmony, typeof(GUI), "Label", new Type[2]
			{
				typeof(Rect),
				typeof(GUIContent)
			});
			num += Patch(harmony, typeof(GUI), "Label", new Type[3]
			{
				typeof(Rect),
				typeof(GUIContent),
				typeof(GUIStyle)
			});
			num += Patch(harmony, typeof(GUI), "Button", new Type[2]
			{
				typeof(Rect),
				typeof(GUIContent)
			});
			num += Patch(harmony, typeof(GUI), "Button", new Type[3]
			{
				typeof(Rect),
				typeof(GUIContent),
				typeof(GUIStyle)
			});
			num += Patch(harmony, typeof(GUI), "Toggle", new Type[3]
			{
				typeof(Rect),
				typeof(bool),
				typeof(GUIContent)
			});
			num += Patch(harmony, typeof(GUI), "Toggle", new Type[4]
			{
				typeof(Rect),
				typeof(bool),
				typeof(GUIContent),
				typeof(GUIStyle)
			});
			num += Patch(harmony, typeof(GUI), "Box", new Type[2]
			{
				typeof(Rect),
				typeof(GUIContent)
			});
			num += Patch(harmony, typeof(GUI), "Box", new Type[3]
			{
				typeof(Rect),
				typeof(GUIContent),
				typeof(GUIStyle)
			});
			num += Patch(harmony, typeof(GUILayout), "Label", new Type[2]
			{
				typeof(string),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Label", new Type[3]
			{
				typeof(string),
				typeof(GUIStyle),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Box", new Type[2]
			{
				typeof(string),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Box", new Type[3]
			{
				typeof(string),
				typeof(GUIStyle),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Button", new Type[2]
			{
				typeof(string),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Button", new Type[3]
			{
				typeof(string),
				typeof(GUIStyle),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Toggle", new Type[3]
			{
				typeof(bool),
				typeof(string),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Toggle", new Type[4]
			{
				typeof(bool),
				typeof(string),
				typeof(GUIStyle),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Window", new Type[5]
			{
				typeof(int),
				typeof(Rect),
				typeof(WindowFunction),
				typeof(string),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Window", new Type[6]
			{
				typeof(int),
				typeof(Rect),
				typeof(WindowFunction),
				typeof(string),
				typeof(GUIStyle),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Label", new Type[2]
			{
				typeof(GUIContent),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Label", new Type[3]
			{
				typeof(GUIContent),
				typeof(GUIStyle),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Button", new Type[2]
			{
				typeof(GUIContent),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Button", new Type[3]
			{
				typeof(GUIContent),
				typeof(GUIStyle),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Box", new Type[2]
			{
				typeof(GUIContent),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Box", new Type[3]
			{
				typeof(GUIContent),
				typeof(GUIStyle),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Toggle", new Type[3]
			{
				typeof(bool),
				typeof(GUIContent),
				typeof(GUILayoutOption[])
			});
			num += Patch(harmony, typeof(GUILayout), "Toggle", new Type[4]
			{
				typeof(bool),
				typeof(GUIContent),
				typeof(GUIStyle),
				typeof(GUILayoutOption[])
			});
			if (num > 0)
			{
				ValheimCNPlugin.Log($"已拦截 {num} 个 IMGUI 文本入口(GUI + GUILayout,string + GUIContent)。");
			}
			return num;
		}

		private static int Patch(Harmony harmony, Type owner, string name, Type[] signature)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = AccessTools.Method(owner, name, signature, (Type[])null);
				if (methodInfo == null)
				{
					return 0;
				}
				bool flag = false;
				ParameterInfo[] parameters = methodInfo.GetParameters();
				for (int i = 0; i < parameters.Length; i++)
				{
					if (parameters[i].ParameterType == typeof(GUIContent))
					{
						flag = true;
						break;
					}
				}
				HarmonyMethod val = (flag ? new HarmonyMethod(typeof(GuiLabelPatch), "PrefixContent", (Type[])null) : new HarmonyMethod(typeof(GuiLabelPatch), "Prefix", (Type[])null));
				harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				return 1;
			}
			catch (Exception ex)
			{
				ValheimCNPlugin.Warn("拦截 " + owner.Name + "." + name + " 失败:" + ex.Message);
				return 0;
			}
		}

		private static void Prefix(ref string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			if (ConfigManagerPatches.SuppressImgui)
			{
				text = LocEngine.ConfigValue(ConfigManagerPatches.CurrentGuid, text) ?? text;
				return;
			}
			try
			{
				text = LocEngine.TranslateGlobalOnly(text);
			}
			catch (Exception)
			{
			}
		}

		private static void PrefixContent(ref GUIContent content)
		{
			if (content == null)
			{
				return;
			}
			if (ConfigManagerPatches.SuppressImgui)
			{
				if (!string.IsNullOrEmpty(content.text))
				{
					content.text = LocEngine.ConfigValue(ConfigManagerPatches.CurrentGuid, content.text) ?? content.text;
				}
				return;
			}
			try
			{
				if (!string.IsNullOrEmpty(content.text))
				{
					content.text = LocEngine.TranslateGlobalOnly(content.text);
				}
				if (!string.IsNullOrEmpty(content.tooltip))
				{
					content.tooltip = LocEngine.TranslateGlobalOnly(content.tooltip);
				}
			}
			catch (Exception)
			{
			}
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "Awake")]
	internal static class MainMenuButtonPatch
	{
		private const string ButtonName = "ValheimCN_MenuButton";

		private const float AboveGap = 34f;

		private static void Postfix(FejdStartup __instance, GameObject ___m_menuList)
		{
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			try
			{
				if (ValheimCNPlugin.CfgShowPanelButton == null || !ValheimCNPlugin.CfgShowPanelButton.Value || (Object)(object)___m_menuList == (Object)null)
				{
					return;
				}
				Button[] componentsInChildren = ___m_menuList.GetComponentsInChildren<Button>();
				if (componentsInChildren.Length == 0)
				{
					return;
				}
				Transform transform = ___m_menuList.transform;
				if ((Object)(object)transform.Find("ValheimCN_MenuButton") != (Object)null)
				{
					return;
				}
				GameObject val = Object.Instantiate<GameObject>(((Component)componentsInChildren[0]).gameObject, transform);
				((Object)val).name = "ValheimCN_MenuButton";
				val.transform.SetAsLastSibling();
				PlaceAboveFirst(val, componentsInChildren);
				SetButtonText(val, "英灵神殿MOD汉化");
				Button btn = val.GetComponent<Button>();
				if (!((Object)(object)btn == (Object)null))
				{
					ClearPersistentCalls(btn);
					((UnityEventBase)btn.onClick).RemoveAllListeners();
					((UnityEvent)btn.onClick).AddListener((UnityAction)delegate
					{
						Canvas componentInParent = ((Component)btn).GetComponentInParent<Canvas>();
						CNPanel.OpenOn(((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).gameObject : ((Component)btn).gameObject);
					});
					ValheimCNPlugin.Log("已在主菜单「开始游戏」上方插入「英灵汉化」按钮。");
				}
			}
			catch (Exception ex)
			{
				ValheimCNPlugin.Warn("插入主菜单按钮失败:" + ex.Message);
			}
		}

		private static void PlaceAboveFirst(GameObject clone, Button[] buttons)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = null;
			for (int i = 0; i < buttons.Length; i++)
			{
				RectTransform val2 = (RectTransform)((Component)buttons[i]).transform;
				if ((Object)(object)val == (Object)null || val2.anchoredPosition.y > val.anchoredPosition.y)
				{
					val = val2;
				}
			}
			if (!((Object)(object)val == (Object)null))
			{
				((RectTransform)clone.transform).anchoredPosition = val.anchoredPosition + new Vector2(0f, val.sizeDelta.y + 34f);
			}
		}

		private static void SetButtonText(GameObject buttonObject, string text)
		{
			Text componentInChildren = buttonObject.GetComponentInChildren<Text>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = text;
				return;
			}
			TMP_Text componentInChildren2 = buttonObject.GetComponentInChildren<TMP_Text>();
			if ((Object)(object)componentInChildren2 != (Object)null)
			{
				componentInChildren2.text = text;
			}
		}

		private static void ClearPersistentCalls(Button button)
		{
			try
			{
				object obj = typeof(UnityEventBase).GetField("m_PersistentCalls", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(button.onClick);
				obj?.GetType().GetMethod("Clear")?.Invoke(obj, null);
			}
			catch (Exception)
			{
			}
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "UpdateKeyboard")]
	internal static class MainMenuInputBlockKeyboard
	{
		private static bool Prefix()
		{
			return !CNPanel.IsOpen;
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "UpdateGamepad")]
	internal static class MainMenuInputBlockGamepad
	{
		private static bool Prefix()
		{
			return !CNPanel.IsOpen;
		}
	}
	internal static class MarsarahUI
	{
		[HarmonyPatch(typeof(Hud), "Awake")]
		internal static class HudAwakePatch
		{
			private static void Postfix()
			{
				_currentTip = null;
			}
		}

		[HarmonyPatch(typeof(Hud), "UpdateBlackScreen")]
		internal static class HudLoadingTipPatch
		{
			private static void Postfix(Hud __instance)
			{
				try
				{
					if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.m_loadingTip == (Object)null || ValheimCNPlugin.CfgLoadingTips == null || !ValheimCNPlugin.CfgLoadingTips.Value || !LocEngine.ShouldTranslate() || !LocEngine.IsPluginLoaded("Marsarah.MarsarahUI"))
					{
						return;
					}
					ModDictionary modDictionary = LocEngine.Get("Marsarah.MarsarahUI");
					if ((modDictionary != null && !modDictionary.Active) || modDictionary == null || modDictionary.LoadingTips.Count == 0)
					{
						return;
					}
					if (string.IsNullOrEmpty(_currentTip))
					{
						lock (Rng)
						{
							_currentTip = modDictionary.GetLoadingTip(Rng);
						}
					}
					if (!string.IsNullOrEmpty(_currentTip))
					{
						__instance.m_loadingTip.text = _currentTip;
					}
				}
				catch (Exception)
				{
				}
			}
		}

		internal const string Guid = "Marsarah.MarsarahUI";

		private static string _currentTip;

		private static readonly Random Rng = new Random();
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class PluginNamePatch
	{
		internal static void Postfix(BepInPlugin __instance, ref string __result)
		{
			if ((ValheimCNPlugin.CfgEnabled == null || ValheimCNPlugin.CfgEnabled.Value) && LocEngine.PluginNameOverrides.TryGetValue(__instance.GUID, out var value) && value != null)
			{
				__result = value;
			}
		}
	}
	internal static class TextSetTextPatch
	{
		internal static int Apply(Harmony harmony)
		{
			return 0 + Patch(harmony, typeof(Text), "UnityEngine.UI.Text") + Patch(harmony, typeof(TMP_Text), "TMPro.TMP_Text");
		}

		private static int Patch(Harmony harmony, Type owner, string display)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = AccessTools.PropertySetter(owner, "text");
				if (methodInfo == null)
				{
					ValheimCNPlugin.Warn("未能定位 " + display + ".text 的赋值方法,该类型文本不会被汉化。");
					return 0;
				}
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(TextSetTextPatch), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				ValheimCNPlugin.Log("已拦截 " + display + ".text 赋值。");
				return 1;
			}
			catch (Exception ex)
			{
				ValheimCNPlugin.Warn("拦截 " + display + ".text 失败:" + ex.Message);
				return 0;
			}
		}

		private static void Prefix(object __instance, ref string value)
		{
			try
			{
				if (__instance != null && !string.IsNullOrEmpty(value))
				{
					Component host = (Component)((__instance is Component) ? __instance : null);
					value = LocEngine.Translate(value, host);
				}
			}
			catch (Exception)
			{
			}
		}
	}
	[BepInPlugin("Catnip.ValheimCN", "ValheimCN - 英灵汉化", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class ValheimCNPlugin : BaseUnityPlugin
	{
		public const string GUID = "Catnip.ValheimCN";

		public const string NAME = "ValheimCN - 英灵汉化";

		public const string VERSION = "1.0.0";

		internal static ValheimCNPlugin Instance;

		internal static Harmony HarmonyInstance;

		internal static ConfigEntry<bool> CfgEnabled;

		internal static ConfigEntry<bool> CfgLoadingTips;

		internal static ConfigEntry<bool> CfgConfigDescriptions;

		internal static ConfigEntry<bool> CfgVerbose;

		internal static ConfigEntry<bool> CfgShowPanelButton;

		internal static ConfigEntry<KeyCode> CfgPanelHotkey;

		internal static ConfigEntry<bool> CfgBridgeGameResources;

		private bool _cfgSwitchWasOn = true;

		private void Awake()
		{
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Expected O, but got Unknown
			Instance = this;
			try
			{
				CfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("00 - 总开关", "启用汉化", true, "关闭后本插件完全不介入,所有 MOD 保持英文原文。");
				CfgLoadingTips = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - 汉化范围", "汉化增强加载提示", true, "MarsarahUI 的增强加载提示原本只在游戏语言为英文时生效。开启后,中文环境下也显示这些提示的中文版。");
				CfgConfigDescriptions = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - 汉化范围", "汉化配置文件说明", true, "把各 MOD 的配置界面汉化:条目名、分组标题、候选项(如 Digital Clock),以及 cfg 文件里的说明文字。只改显示文字,不改键名,你已有的设置值不会丢。");
				CfgBridgeGameResources = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - 汉化范围", "桥接游戏原生资源名", true, "自动把 MOD 直接复用的游戏资源 / 地点(如 AutoPOIMarker 的地图标记)的英文标识符,桥接到游戏官方中文名(prefab / 地点的本地化)。能减少手写词典量且用官方译名。仅整串精确匹配、非 English 才生效;若发现误翻可在 cfg 里关掉。");
				CfgVerbose = ((BaseUnityPlugin)this).Config.Bind<bool>("02 - 调试", "输出详细日志", false, "在 BepInEx 日志里打印每一条被替换的文本,用于排查词典缺失。会让日志文件变大。");
				CfgShowPanelButton = ((BaseUnityPlugin)this).Config.Bind<bool>("03 - 汉化界面", "主菜单显示汉化按钮", true, "在游戏主菜单底部显示「英灵汉化」按钮,可以查看已汉化的 MOD、提交汉化请求、查看打赏方式。");
				CfgPanelHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("03 - 汉化界面", "游戏内打开界面的热键", (KeyCode)289, "在游戏中按下该键随时打开/关闭汉化界面。设为 None 可禁用热键。");
				LocEngine.Initialize(Paths.ConfigPath, ((BaseUnityPlugin)this).Logger);
				HarmonyInstance = new Harmony("Catnip.ValheimCN");
				HarmonyInstance.PatchAll();
				TextSetTextPatch.Apply(HarmonyInstance);
				GuiLabelPatch.Apply(HarmonyInstance);
				ConfigSavePatch.Apply(HarmonyInstance);
				CNHotkey.EnsureCreated();
				ConfigManagerPatches.Install(HarmonyInstance);
				((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} 已加载,词典 {2} 份,条目 {3} 条。", "ValheimCN - 英灵汉化", "1.0.0", LocEngine.DictCount, LocEngine.EntryCount));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("ValheimCN 初始化失败:" + ex));
			}
		}

		private void Update()
		{
			try
			{
				LocEngine.Tick();
				ConfigManagerPatches.TryLateInstall();
				bool flag = CfgConfigDescriptions != null && CfgConfigDescriptions.Value;
				if (flag && !_cfgSwitchWasOn)
				{
					ConfigLocalizer.RequestAll();
				}
				_cfgSwitchWasOn = flag;
				ConfigLocalizer.Drain();
				LocEngine.DrainPending();
			}
			catch (Exception)
			{
			}
		}

		internal static void Log(string msg)
		{
			ValheimCNPlugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogInfo((object)msg);
			}
		}

		internal static void Warn(string msg)
		{
			ValheimCNPlugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogWarning((object)msg);
			}
		}
	}
	internal sealed class CNHotkey : MonoBehaviour
	{
		private static CNHotkey _instance;

		internal static void EnsureCreated()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (!((Object)(object)_instance != (Object)null))
			{
				GameObject val = new GameObject("ValheimCN_Hotkey");
				Object.DontDestroyOnLoad((Object)val);
				_instance = val.AddComponent<CNHotkey>();
			}
		}

		private void Update()
		{
			try
			{
				if (!HotkeyDown())
				{
					return;
				}
				if (CNPanel.IsOpen)
				{
					CNPanel.Close();
					return;
				}
				Transform val = FindCanvas();
				if ((Object)(object)val != (Object)null)
				{
					CNPanel.Open(val);
				}
			}
			catch (Exception)
			{
			}
		}

		private static bool HotkeyDown()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				KeyCode val = (KeyCode)((ValheimCNPlugin.CfgPanelHotkey == null) ? 289 : ((int)ValheimCNPlugin.CfgPanelHotkey.Value));
				return (int)val != 0 && Input.GetKeyDown(val);
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static Transform FindCanvas()
		{
			try
			{
				Hud instance = Hud.instance;
				if ((Object)(object)instance != (Object)null)
				{
					Canvas componentInParent = ((Component)instance).GetComponentInParent<Canvas>();
					if ((Object)(object)componentInParent != (Object)null)
					{
						return ((Component)componentInParent).transform;
					}
					if ((Object)(object)((Component)instance).gameObject != (Object)null)
					{
						return ((Component)instance).gameObject.transform;
					}
				}
			}
			catch (Exception)
			{
			}
			try
			{
				Canvas[] array = Object.FindObjectsOfType<Canvas>();
				for (int i = 0; i < array.Length; i++)
				{
					if ((Object)(object)array[i] != (Object)null && ((Behaviour)array[i]).isActiveAndEnabled)
					{
						return ((Component)array[i]).transform;
					}
				}
			}
			catch (Exception)
			{
			}
			return null;
		}

		private void OnDestroy()
		{
			if (_instance == this)
			{
				_instance = null;
			}
		}
	}
	internal sealed class CNPanel : MonoBehaviour
	{
		internal enum Tab
		{
			Localized,
			Inactive,
			Request,
			Donate
		}

		private const float PanelWFrac = 0.427f;

		private const float PanelHFrac = 0.537f;

		private const float PanelY = 20f;

		private const float TabLeft = -80f;

		private const float TabTop = -23f;

		private const float TabH = 37f;

		private const float TabGap = 7f;

		private const float TabPadX = 36f;

		private const float TabMinW = 104f;

		private const int TabFontSize = 16;

		private const float ContentLeft = 82f;

		private const float ContentRight = 30f;

		private const float ContentTop = 44f;

		private const float BottomMargin = 20f;

		private const float ButtonH = 36f;

		internal static bool Opened;

		private RectTransform _content;

		private Button[] _tabs;

		private Text[] _tabTexts;

		private Tab _current;

		private static CNPanel Instance;

		internal static bool IsOpen => Opened;

		internal static void Toggle(Transform canvas)
		{
			if (Opened)
			{
				Close();
			}
			else
			{
				Open(canvas);
			}
		}

		internal static void Open(Transform parent)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			if (Opened || (Object)(object)parent == (Object)null)
			{
				return;
			}
			GameObject val = new GameObject("ValheimCN_Panel", new Type[1] { typeof(RectTransform) });
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(parent, false);
			((Transform)val2).localScale = Vector3.one;
			((Transform)val2).localRotation = Quaternion.identity;
			((Transform)val2).localPosition = Vector3.zero;
			UICore.Stretch(val2, 0f, 0f, 0f, 0f);
			((Transform)val2).SetAsLastSibling();
			float canvasW = 1920f;
			float canvasH = 1080f;
			RectTransform val3 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if ((Object)(object)val3 != (Object)null)
			{
				Rect rect = val3.rect;
				if (((Rect)(ref rect)).width > 1f)
				{
					rect = val3.rect;
					if (((Rect)(ref rect)).height > 1f)
					{
						rect = val3.rect;
						canvasW = ((Rect)(ref rect)).width;
						rect = val3.rect;
						canvasH = ((Rect)(ref rect)).height;
					}
				}
			}
			val.AddComponent<CNPanel>().Build(canvasW, canvasH);
		}

		internal static void Close()
		{
			if ((Object)(object)Instance != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)Instance).gameObject);
			}
		}

		private void Build(float canvasW, float canvasH)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Expected O, but got Unknown
			Opened = true;
			Instance = this;
			float num = canvasW * 0.427f;
			float num2 = canvasH * 0.537f;
			Image obj = UICore.Box(((Component)this).transform, "Mask", UICore.MaskColor);
			UICore.Stretch(UICore.Rect(((Component)obj).gameObject), 0f, 0f, 0f, 0f);
			((Graphic)obj).raycastTarget = true;
			Image val = UICore.FramedPanel(((Component)this).transform, "Panel", UICore.PanelColor, new Color(0.22f, 0.58f, 0.6f, 1f));
			RectTransform obj2 = UICore.Rect(((Component)val).gameObject);
			obj2.anchorMin = new Vector2(0.5f, 0.5f);
			obj2.anchorMax = new Vector2(0.5f, 0.5f);
			obj2.pivot = new Vector2(0.5f, 0.5f);
			obj2.anchoredPosition = new Vector2(0f, 20f);
			obj2.sizeDelta = new Vector2(num, num2);
			((Transform)obj2).localScale = Vector3.one;
			((Graphic)val).raycastTarget = true;
			UICore.Place(UICore.Rect(((Component)UICore.Label(((Component)val).transform, "Title", "英灵神殿汉化MOD合集", 13, UICore.AccentColor, (TextAnchor)3)).gameObject), 24f, 0f - num2 + 26f, 300f, 20f);
			BuildTabs(((Component)val).transform);
			float h = num2 - 44f - 72f;
			GameObject val2 = new GameObject("Content", new Type[1] { typeof(RectTransform) });
			val2.transform.SetParent(((Component)val).transform, false);
			_content = UICore.Rect(val2);
			UICore.Place(_content, 82f, -44f, num - 82f - 30f, h);
			UICore.MakeButton(((Component)val).transform, "关闭", num - 24f - 90f, 0f - num2 + 20f + 36f, 90f, 36f, Close);
			ShowTab(Tab.Localized);
		}

		private void BuildTabs(Transform parent)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			string[] array = new string[4] { "已汉化 MOD", "未生效", "请求汉化", "支持打赏" };
			_tabs = (Button[])(object)new Button[array.Length];
			_tabTexts = (Text[])(object)new Text[array.Length];
			float num = 104f;
			string[] array2 = array;
			foreach (string text in array2)
			{
				num = Mathf.Max(num, MeasureTextWidth(text, 16) + 36f);
			}
			float num2 = -23f;
			for (int j = 0; j < array.Length; j++)
			{
				int index = j;
				Image val = UICore.Box(parent, "Tab" + j, UICore.TabOffColor);
				UICore.Place(UICore.Rect(((Component)val).gameObject), -80f, num2, num, 37f);
				Button val2 = ((Component)val).gameObject.AddComponent<Button>();
				((Selectable)val2).targetGraphic = (Graphic)(object)val;
				((UnityEvent)val2.onClick).AddListener((UnityAction)delegate
				{
					ShowTab((Tab)index);
				});
				_tabs[j] = val2;
				_tabTexts[j] = UICore.Label(((Component)val).transform, "Text", array[j], 16, UICore.GrayText, (TextAnchor)3);
				UICore.Place(UICore.Rect(((Component)_tabTexts[j]).gameObject), 14f, 1f, num - 18f, 35f);
				num2 -= 44f;
			}
		}

		private static float MeasureTextWidth(string text, int fontSize)
		{
			if (string.IsNullOrEmpty(text))
			{
				return 0f;
			}
			float num = 0f;
			foreach (char c in text)
			{
				num += ((c >= '⺀') ? 1f : 0.55f);
			}
			return num * (float)fontSize;
		}

		private void ShowTab(Tab tab)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			_current = tab;
			for (int i = 0; i < _tabs.Length; i++)
			{
				bool flag = tab == (Tab)i;
				((Graphic)((Component)_tabs[i]).GetComponent<Image>()).color = (flag ? UICore.TabOnColor : UICore.TabOffColor);
				((Graphic)_tabTexts[i]).color = (flag ? UICore.AccentColor : UICore.GrayText);
			}
			UICore.ClearChildren((Transform)(object)_content);
			RectTransform content = UICore.MakeScroll((Transform)(object)_content, "Scroller");
			switch (tab)
			{
			case Tab.Localized:
				PanelTabs.BuildLocalized(content);
				break;
			case Tab.Inactive:
				PanelTabs.BuildInactive(content);
				break;
			case Tab.Request:
				PanelTabs.BuildRequest(content);
				break;
			case Tab.Donate:
				PanelTabs.BuildDonate(content);
				break;
			}
		}

		private void Update()
		{
			if (Input.GetKeyDown((KeyCode)27))
			{
				Close();
			}
		}

		private void OnDestroy()
		{
			Opened = false;
			if (Instance == this)
			{
				Instance = null;
			}
		}

		internal static void OpenOn(GameObject context)
		{
			if (!((Object)(object)context == (Object)null))
			{
				Canvas val = context.GetComponentInParent<Canvas>();
				if ((Object)(object)val != (Object)null && (Object)(object)val.rootCanvas != (Object)null)
				{
					val = val.rootCanvas;
				}
				Open(((Object)(object)val != (Object)null) ? ((Component)val).transform : context.transform);
			}
		}

		internal static void ToggleOn(GameObject context)
		{
			if (Opened)
			{
				Close();
			}
			else
			{
				OpenOn(context);
			}
		}
	}
	internal static class PanelTabs
	{
		private const float RowH = 36f;

		private const float Gap = 3f;

		private const float RowTop = 33f;

		private static readonly HashSet<string> LibraryGuids = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "denikson-BepInExPack_Valheim", "BepInEx.Harmony", "BepInEx.Preloader", "com.jotunn.jotunn", "Jotunn", "YamlDotNet", "Catnip.ValheimCN" };

		private const string RequestFormUrl = "https://docs.qq.com/form/page/DUHhtbW9RdXF3Z0dJ";

		private static HashSet<string> _requested;

		private static readonly string RequestedFile = Path.Combine(LocEngine.DictDir, "requested.txt");

		private const string DonateAlipayFile = "alipay.png";

		private const string DonateWechatFile = "wechat.png";

		private static MethodInfo _unityLoadImage;

		private static bool _unityLoadImageSearched;

		private static float W(RectTransform content)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			Rect rect = content.rect;
			return ((Rect)(ref rect)).width;
		}

		private static void SetContentHeight(RectTransform content, float bottomY, float pad)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			float num = 0f - bottomY + pad;
			if (num < 0f)
			{
				num = 0f;
			}
			content.sizeDelta = new Vector2(content.sizeDelta.x, num);
		}

		internal static void BuildLocalized(RectTransform content)
		{
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
			IList<ModDictionary> dictionaries = LocEngine.Dictionaries;
			if (dictionaries == null || dictionaries.Count == 0)
			{
				EmptyHint(content, "还没有安装任何汉化词典。", "把词典 JSON 放进 BepInEx/config/ValheimCN/ 后重启游戏。");
				SetContentHeight(content, -170f, 8f);
				return;
			}
			float w = W(content);
			List<ModDictionary> list = new List<ModDictionary>();
			int num = 0;
			for (int i = 0; i < dictionaries.Count; i++)
			{
				if (dictionaries[i].Active)
				{
					list.Add(dictionaries[i]);
				}
				else
				{
					num++;
				}
			}
			int num2 = 0;
			int num3 = 0;
			for (int j = 0; j < list.Count; j++)
			{
				num2 += list[j].Exact.Count + list[j].Regexes.Count;
				num3 += list[j].ConfigSections.Count + list[j].ConfigNames.Count + list[j].ConfigDescriptions.Count + list[j].ConfigValues.Count;
			}
			UICore.Place(UICore.Rect(((Component)UICore.Label((Transform)(object)content, "Summary", $"已生效 {list.Count} 个 MOD,共 {num2} 条词条、{num3} 条配置。", 14, UICore.GrayText, (TextAnchor)3)).gameObject), 0f, 0f, w, 22f);
			float num4 = -33f;
			for (int k = 0; k < list.Count; k++)
			{
				BuildDictRow(content, list[k], num4, k % 2 == 1);
				num4 -= 39f;
			}
			num4 += 3f;
			List<string> list2 = new List<string>();
			for (int l = 0; l < list.Count; l++)
			{
				if (!string.IsNullOrEmpty(list[l].MismatchNote))
				{
					list2.Add(string.IsNullOrEmpty(list[l].TargetName) ? Path.GetFileNameWithoutExtension(list[l].FilePath) : list[l].TargetName);
				}
			}
			string text = null;
			if (num > 0)
			{
				text = $"另有 {num} 份词典未生效(未订阅对应的原版 MOD),见「未生效」页。";
			}
			if (list2.Count > 0)
			{
				string text2 = ((list2.Count <= 3) ? string.Join("、", list2) : (list2[0] + $" 等 {list2.Count} 个 MOD")) + " 出了新版本。已汉化的文字照常生效;如发现未翻译的文案,请到「请求汉化」页提交反馈。";
				text = ((text == null) ? "" : (text + "\n")) + "「MOD 有新版」:" + text2;
			}
			float num5 = 0f;
			if (text != null)
			{
				num5 = ((list2.Count > 0) ? 58f : 30f);
				UICore.Place(UICore.Rect(((Component)UICore.Paragraph((Transform)(object)content, "Note", text, 12, (list2.Count > 0) ? UICore.WarnColor : UICore.DimText, (TextAnchor)0)).gameObject), 0f, num4 - 6f, w, num5);
			}
			SetContentHeight(content, num4 - 6f - num5, 8f);
		}

		private static void BuildDictRow(RectTransform content, ModDictionary d, float y, bool alt)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			Image obj = UICore.MakeRowStretch((Transform)(object)content, "Row", y, 36f, alt ? UICore.RowAltColor : UICore.RowColor);
			bool flag = d.Active && !string.IsNullOrEmpty(d.MismatchNote);
			UICore.Place(UICore.Rect(((Component)UICore.Box(((Component)obj).transform, "Bar", (!d.Active) ? UICore.DimText : (flag ? UICore.WarnColor : UICore.OkColor))).gameObject), 0f, 0f, 4f, 36f);
			UICore.PlaceFill(UICore.Rect(((Component)UICore.Label(text: string.IsNullOrEmpty(d.TargetName) ? Path.GetFileNameWithoutExtension(d.FilePath) : d.TargetName, parent: ((Component)obj).transform, name: "Name", size: 15, color: UICore.TextColor, anchor: (TextAnchor)3)).gameObject), 12f, 548f, 0f, 36f);
			UICore.Place(UICore.Rect(((Component)UICore.Label(((Component)obj).transform, "Ver", string.IsNullOrEmpty(d.TargetVersion) ? "" : ("v" + d.TargetVersion), 12, UICore.DimText, (TextAnchor)3)).gameObject), 196f, 0f, 70f, 36f);
			int count = d.Exact.Count;
			int count2 = d.Regexes.Count;
			int count3 = d.ConfigDescriptions.Count;
			int count4 = d.LoadingTips.Count;
			List<string> list = new List<string>();
			if (count > 0)
			{
				list.Add($"词条 {count}");
			}
			if (count2 > 0)
			{
				list.Add($"模板 {count2}");
			}
			if (count3 > 0)
			{
				list.Add($"配置说明 {count3}");
			}
			if (count4 > 0)
			{
				list.Add($"加载提示 {count4}");
			}
			UICore.Place(UICore.Rect(((Component)UICore.Label(((Component)obj).transform, "Count", string.Join(" · ", list), 12, UICore.GrayText, (TextAnchor)3)).gameObject), 272f, 0f, 290f, 36f);
			UICore.PlaceRight(UICore.Rect(((Component)UICore.Label(text: (!d.Active) ? "未生效" : (flag ? "已生效·有新版" : "已生效"), color: (!d.Active) ? UICore.DimText : (flag ? UICore.WarnColor : UICore.OkColor), parent: ((Component)obj).transform, name: "Status", size: 13, anchor: (TextAnchor)5)).gameObject), 12f, 0f, 104f, 36f);
		}

		internal static void BuildInactive(RectTransform content)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			IList<ModDictionary> dictionaries = LocEngine.Dictionaries;
			List<ModDictionary> list = new List<ModDictionary>();
			if (dictionaries != null)
			{
				for (int i = 0; i < dictionaries.Count; i++)
				{
					if (!dictionaries[i].Active)
					{
						list.Add(dictionaries[i]);
					}
				}
			}
			if (list.Count == 0)
			{
				EmptyHint(content, "所有词典均已生效。", "未启用源 MOD(未订阅或未安装)的词典才会显示在这里。");
				SetContentHeight(content, -170f, 8f);
				return;
			}
			float w = W(content);
			UICore.Place(UICore.Rect(((Component)UICore.Label((Transform)(object)content, "Summary", $"未生效 {list.Count} 份词典。", 14, UICore.GrayText, (TextAnchor)3)).gameObject), 0f, 0f, w, 22f);
			UICore.Place(UICore.Rect(((Component)UICore.Paragraph((Transform)(object)content, "Explain", "这些词典对应的原版 MOD 未订阅(或未安装、加载失败),当前不起作用。\n词典会自动跳过、不会出错;订阅/安装原版 MOD 后重启游戏,会自动