Decompiled source of LC Chinese Project v3.2.3

BepInEx/plugins/V81TestChn/V81TestChn.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.UI;
using UnityEngine.Video;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("UnityEngine.UI")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("V81TestChn")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+87e1576962d3ca34e223691ed04620a46ed34371")]
[assembly: AssemblyProduct("V81TestChn")]
[assembly: AssemblyTitle("V81TestChn")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[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 V81TestChn
{
	internal static class AlertTextureReplacementService
	{
		private enum NativeTextRole
		{
			None,
			SystemOnline,
			EnteringAtmosphere
		}

		private readonly struct CachedNativeTextRole
		{
			public int ParentId { get; }

			public NativeTextRole Role { get; }

			public CachedNativeTextRole(int parentId, NativeTextRole role)
			{
				ParentId = parentId;
				Role = role;
			}
		}

		private const int NativeTextRoleCacheLimit = 16384;

		private const string SystemOnlineTitleObjectName = "TipLeft1";

		private const string SystemOnlineTitleFullPath = "Systems/UI/Canvas/IngamePlayerHUD/BottomMiddle/SystemsOnline/TipLeft1";

		private const string SystemOnlineTitlePathSuffix = "IngamePlayerHUD/BottomMiddle/SystemsOnline/TipLeft1";

		private const string SystemOnlineRelativePath = "BottomMiddle/SystemsOnline/TipLeft1";

		private const string EnteringAtmosphereTitleObjectName = "LoadText";

		private const string RelaySceneName = "SampleSceneRelay";

		private const string SystemOnlineLocalizedText = "系统在线";

		private const string EnteringAtmosphereLocalizedText = "正在进入大气层...";

		private const string HazardLevelLocalizedText = "风险级别:";

		private const string LifeSupportOfflineLocalizedText = "[生命维持:离线]";

		private static Coroutine? _systemOnlineWatcher;

		private static Coroutine? _fixedSceneLabelWatcher;

		private static HUDManager? _systemOnlineWatcherOwner;

		private static HUDManager? _fixedSceneLabelWatcherOwner;

		private static bool _fixedSceneLabelSceneLoadedSubscribed;

		private static string _fixedSceneLabelSceneLoadedStage = "SceneLoaded";

		private static readonly BoundedCache<int, CachedNativeTextRole> NativeTextRoleCache = new BoundedCache<int, CachedNativeTextRole>(16384);

		private static readonly Dictionary<string, string> FixedSceneLabels = new Dictionary<string, string>(StringComparer.Ordinal)
		{
			["TO MEET PROFIT QUOTA"] = "以达到利润配额",
			["PERFORMANCE REPORT"] = "绩效报告",
			["EMPLOYEE RANK"] = "员工等级",
			["Fines"] = "罚款",
			["(Dead)"] = "(死亡)",
			["Deceased"] = "死亡",
			["[LIFE SUPPORT: OFFLINE]"] = "[生命维持:离线]",
			["You will keep your employee rank. Your ship and credits will be reset."] = "你的员工等级将被保留,但飞船和点数将被重置。"
		};

		public static void Initialize(string pluginDir)
		{
			if (Plugin.RuntimeLocalizationLogsEnabled)
			{
				Plugin.Log.LogInfo((object)"Native relay title translation enabled; SYSTEMS ONLINE uses original TMP object only.");
			}
		}

		public static void ForceApplySystemOnlineOverlay(HUDManager? hudManager, string stage)
		{
			TMP_Text val = FindSystemOnlineTitle(hudManager, allowGlobalFallback: false);
			if ((Object)(object)val != (Object)null)
			{
				ApplySystemOnlineNativeTranslation(val, stage);
			}
			else if (Plugin.RuntimeLocalizationLogsEnabled)
			{
				Plugin.Log.LogWarning((object)("NativeRelay[" + stage + "] target=SystemOnline action=not-found"));
				AuditSystemOnlineBranch(stage, hudManager);
			}
		}

		public static void BeginSystemOnlineExactPathWatcher(HUDManager? hudManager, string stage)
		{
			if (!((Object)(object)hudManager == (Object)null))
			{
				ResetSystemOnlineWatcherIfStale(hudManager);
				if (_systemOnlineWatcher == null)
				{
					_systemOnlineWatcherOwner = hudManager;
					_systemOnlineWatcher = ((MonoBehaviour)hudManager).StartCoroutine(WaitForSystemOnlineTitle(hudManager, stage));
				}
			}
		}

		public static void TryApplySystemOnlineDirect(HUDManager? hudManager, string stage)
		{
			TMP_Text val = FindSystemOnlineTitle(hudManager, allowGlobalFallback: true);
			if ((Object)(object)val == (Object)null)
			{
				if (Plugin.RuntimeLocalizationLogsEnabled)
				{
					Plugin.Log.LogWarning((object)("NativeRelay[" + stage + "] target=SystemOnline action=not-found"));
				}
			}
			else
			{
				ApplySystemOnlineNativeTranslation(val, stage);
			}
		}

		public static void QueueSystemOnlineSync(HUDManager? hudManager, string stage)
		{
		}

		public static void TryApplyEnteringAtmosphereOverlayFromLoadingScreen(HUDManager? hudManager, string stage)
		{
			TMP_Text val = FindEnteringAtmosphereTitle(hudManager);
			if ((Object)(object)val == (Object)null)
			{
				if (Plugin.RuntimeLocalizationLogsEnabled)
				{
					Plugin.Log.LogWarning((object)("NativeRelay[" + stage + "] target=EnteringAtmosphere action=not-found"));
				}
			}
			else
			{
				ApplyEnteringAtmosphereNativeTranslation(val, stage);
			}
		}

		public static void HideEnteringAtmosphereOverlayForHud(HUDManager? hudManager, string stage)
		{
			TMP_Text val = FindEnteringAtmosphereTitle(hudManager);
			if ((Object)(object)val != (Object)null)
			{
				ApplyEnteringAtmosphereNativeTranslation(val, stage);
			}
		}

		public static void SyncEnteringAtmosphereOverlayState(TMP_Text? text, string stage)
		{
			if (!((Object)(object)text == (Object)null) && GetNativeTextRole(text) == NativeTextRole.EnteringAtmosphere)
			{
				ApplyEnteringAtmosphereNativeTranslation(text, stage);
			}
		}

		public static void SyncHazardLevelRelay(HUDManager? hudManager, string stage)
		{
			TMP_Text val = FindHazardLevelTitle(hudManager);
			if ((Object)(object)val != (Object)null)
			{
				ApplyHazardLevelNativeTranslation(val, stage);
			}
		}

		public static void SyncLandingInfo(HUDManager? hudManager, string stage)
		{
			if (!((Object)(object)hudManager == (Object)null))
			{
				ApplyDynamicFieldTranslation((TMP_Text?)(object)hudManager.planetInfoHeaderText, stage, "PlanetInfoHeader");
				ApplyDynamicFieldTranslation((TMP_Text?)(object)hudManager.planetInfoSummaryText, stage, "PlanetInfoSummary");
				ApplyDynamicFieldTranslation((TMP_Text?)(object)hudManager.planetRiskLevelText, stage, "PlanetRiskLevel");
				SyncHazardLevelRelay(hudManager, stage);
			}
		}

		public static void SyncFixedSceneLabels(HUDManager? hudManager, string stage)
		{
			if (!((Object)(object)hudManager == (Object)null))
			{
				TMP_Text[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<TMP_Text>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					TryReplaceFixedSceneLabel(componentsInChildren[i], stage);
				}
			}
		}

		public static void BeginFixedSceneLabelWatcher(HUDManager? hudManager, string stage)
		{
			if (!((Object)(object)hudManager == (Object)null) && SyncFixedSceneLabelsInRelayScene(stage + ".initial") <= 0)
			{
				_fixedSceneLabelWatcherOwner = hudManager;
				_fixedSceneLabelSceneLoadedStage = stage;
				if (!_fixedSceneLabelSceneLoadedSubscribed)
				{
					SceneManager.sceneLoaded += OnRelaySceneLoadedForFixedLabels;
					_fixedSceneLabelSceneLoadedSubscribed = true;
				}
			}
		}

		public static void Shutdown()
		{
			try
			{
				if (_systemOnlineWatcher != null && (Object)(object)_systemOnlineWatcherOwner != (Object)null)
				{
					((MonoBehaviour)_systemOnlineWatcherOwner).StopCoroutine(_systemOnlineWatcher);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("NativeRelay shutdown failed stopping SystemOnline watcher: " + ex.GetType().Name + ": " + ex.Message));
			}
			try
			{
				if (_fixedSceneLabelWatcher != null && (Object)(object)_fixedSceneLabelWatcherOwner != (Object)null)
				{
					((MonoBehaviour)_fixedSceneLabelWatcherOwner).StopCoroutine(_fixedSceneLabelWatcher);
				}
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogWarning((object)("NativeRelay shutdown failed stopping fixed scene label watcher: " + ex2.GetType().Name + ": " + ex2.Message));
			}
			_systemOnlineWatcher = null;
			_fixedSceneLabelWatcher = null;
			_systemOnlineWatcherOwner = null;
			_fixedSceneLabelWatcherOwner = null;
			if (_fixedSceneLabelSceneLoadedSubscribed)
			{
				SceneManager.sceneLoaded -= OnRelaySceneLoadedForFixedLabels;
				_fixedSceneLabelSceneLoadedSubscribed = false;
			}
			_fixedSceneLabelSceneLoadedStage = "SceneLoaded";
			NativeTextRoleCache.Clear();
		}

		private static void OnRelaySceneLoadedForFixedLabels(Scene scene, LoadSceneMode mode)
		{
			if (string.Equals(((Scene)(ref scene)).name, "SampleSceneRelay", StringComparison.Ordinal))
			{
				SyncFixedSceneLabelsInRelayScene(_fixedSceneLabelSceneLoadedStage + ".scene-loaded");
				SceneManager.sceneLoaded -= OnRelaySceneLoadedForFixedLabels;
				_fixedSceneLabelSceneLoadedSubscribed = false;
				_fixedSceneLabelWatcherOwner = null;
				_fixedSceneLabelSceneLoadedStage = "SceneLoaded";
			}
		}

		private static void ResetSystemOnlineWatcherIfStale(HUDManager currentHud)
		{
			if (_systemOnlineWatcher == null)
			{
				_systemOnlineWatcherOwner = null;
			}
			else if (!((Object)(object)_systemOnlineWatcherOwner == (Object)(object)currentHud))
			{
				StopStaleWatcher(_systemOnlineWatcherOwner, _systemOnlineWatcher, "SystemOnline");
				_systemOnlineWatcher = null;
				_systemOnlineWatcherOwner = null;
			}
		}

		private static void ResetFixedSceneLabelWatcherIfStale(HUDManager currentHud)
		{
			if (_fixedSceneLabelWatcher == null)
			{
				_fixedSceneLabelWatcherOwner = null;
			}
			else if (!((Object)(object)_fixedSceneLabelWatcherOwner == (Object)(object)currentHud))
			{
				StopStaleWatcher(_fixedSceneLabelWatcherOwner, _fixedSceneLabelWatcher, "FixedSceneLabel");
				_fixedSceneLabelWatcher = null;
				_fixedSceneLabelWatcherOwner = null;
			}
		}

		private static void StopStaleWatcher(HUDManager? owner, Coroutine watcher, string label)
		{
			if ((Object)(object)owner == (Object)null)
			{
				return;
			}
			try
			{
				((MonoBehaviour)owner).StopCoroutine(watcher);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("NativeRelay stale " + label + " watcher stop failed: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		public static void TryReplaceSystemOnlineText(TMP_Text? text, string stage)
		{
			if ((Object)(object)text == (Object)null || !MightBeNativeRelayCandidate(text))
			{
				return;
			}
			switch (GetNativeTextRole(text))
			{
			case NativeTextRole.SystemOnline:
				ApplySystemOnlineNativeTranslation(text, stage);
				return;
			case NativeTextRole.EnteringAtmosphere:
				ApplyEnteringAtmosphereNativeTranslation(text, stage);
				return;
			}
			if (IsHazardLevelTitle(text.text))
			{
				ApplyHazardLevelNativeTranslation(text, stage);
			}
			else
			{
				TryReplaceFixedSceneLabel(text, stage);
			}
		}

		public static bool TryReplaceFixedSceneLabel(TMP_Text? text, string stage)
		{
			if ((Object)(object)text == (Object)null || string.IsNullOrWhiteSpace(text.text))
			{
				return false;
			}
			if (!TryResolveFixedSceneLabel(text.text, out string localized))
			{
				return false;
			}
			ApplyLocalizedText(text, localized, stage, "FixedSceneLabel");
			if (Plugin.RuntimeLocalizationLogsEnabled)
			{
				Plugin.Log.LogInfo((object)("NativeRelay[" + stage + "] target=FixedSceneLabel action=applied name=" + ((Object)text).name + " path=" + BuildPath(text.transform) + " text=" + text.text));
			}
			return true;
		}

		public static void TryReplaceSystemOnlineText(Text? text, string stage)
		{
		}

		private static bool MightBeNativeRelayCandidate(TMP_Text text)
		{
			if (string.Equals(((Object)text).name, "TipLeft1", StringComparison.OrdinalIgnoreCase) || string.Equals(((Object)text).name, "LoadText", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			string text2 = text.text;
			if (string.IsNullOrWhiteSpace(text2) || text2.Length > 160)
			{
				return false;
			}
			int i;
			for (i = 0; i < text2.Length && char.IsWhiteSpace(text2[i]); i++)
			{
			}
			if (i >= text2.Length)
			{
				return false;
			}
			switch (char.ToUpperInvariant(text2[i]))
			{
			case '(':
			case 'D':
			case 'E':
			case 'F':
			case 'H':
			case 'P':
			case 'T':
			case 'Y':
			case '[':
				return true;
			default:
				return false;
			}
		}

		public static void TryReplaceSystemOnlineText(TextMesh? text, string stage)
		{
		}

		public static void TryReplace(Image? image, string stage)
		{
		}

		public static void TryReplace(RawImage? image, string stage)
		{
		}

		public static void TryReplace(SpriteRenderer? renderer, string stage)
		{
		}

		public static void TryReplace(Material? material, string stage)
		{
		}

		private static void ApplySystemOnlineNativeTranslation(TMP_Text text, string stage)
		{
			if (!((Behaviour)text).enabled)
			{
				((Behaviour)text).enabled = true;
			}
			ApplyLocalizedText(text, ResolveLocalizedText(text.text, "系统在线"), stage, "SystemOnline");
			if (Plugin.RuntimeLocalizationLogsEnabled)
			{
				Plugin.Log.LogInfo((object)("NativeRelay[" + stage + "] target=SystemOnline action=applied name=" + ((Object)text).name + " path=" + BuildPath(text.transform) + " text=" + text.text));
			}
		}

		private static IEnumerator WaitForSystemOnlineTitle(HUDManager hudManager, string stage)
		{
			float elapsed = 0f;
			while ((Object)(object)hudManager != (Object)null && elapsed < 12f)
			{
				TMP_Text text = FindSystemOnlineTitleByHudPath(hudManager);
				if (IsExactSystemOnlineTitle(text))
				{
					ApplySystemOnlineNativeTranslation(text, stage + ".watcher");
					_systemOnlineWatcher = null;
					_systemOnlineWatcherOwner = null;
					yield break;
				}
				yield return (object)new WaitForSeconds(0.25f);
				elapsed += 0.25f;
			}
			if (Plugin.RuntimeLocalizationLogsEnabled)
			{
				Plugin.Log.LogWarning((object)("NativeRelay[" + stage + ".watcher] target=SystemOnline action=timeout"));
			}
			_systemOnlineWatcher = null;
			_systemOnlineWatcherOwner = null;
		}

		private static IEnumerator WaitForFixedSceneLabels(string stage)
		{
			for (float elapsed = 0f; elapsed < 8f; elapsed += 0.5f)
			{
				if (SyncFixedSceneLabelsInRelayScene(stage + ".relay-scene") > 0)
				{
					_fixedSceneLabelWatcher = null;
					_fixedSceneLabelWatcherOwner = null;
					yield break;
				}
				yield return (object)new WaitForSeconds(0.5f);
			}
			if (Plugin.RuntimeLocalizationLogsEnabled)
			{
				Plugin.Log.LogWarning((object)("NativeRelay[" + stage + ".relay-scene] target=FixedSceneLabel action=timeout"));
			}
			_fixedSceneLabelWatcher = null;
			_fixedSceneLabelWatcherOwner = null;
		}

		private static void ApplyEnteringAtmosphereNativeTranslation(TMP_Text text, string stage)
		{
			ApplyLocalizedText(text, ResolveLocalizedText(text.text, "正在进入大气层..."), stage, "EnteringAtmosphere");
		}

		private static void ApplyHazardLevelNativeTranslation(TMP_Text text, string stage)
		{
			ApplyLocalizedText(text, "风险级别:", stage, "HazardLevel");
		}

		private static void ApplyLocalizedText(TMP_Text text, string localized, string stage, string target)
		{
			if (!string.IsNullOrWhiteSpace(localized))
			{
				if (!string.Equals(text.text, localized, StringComparison.Ordinal))
				{
					text.text = localized;
				}
				FontFallbackService.ApplyFallback(text, localized);
				ApplyLifeSupportOfflineTypography(text, localized);
			}
		}

		private static void ApplyLifeSupportOfflineTypography(TMP_Text text, string localized)
		{
			if (string.Equals(localized, "[生命维持:离线]", StringComparison.Ordinal))
			{
				text.richText = true;
				text.enableWordWrapping = false;
				text.overflowMode = (TextOverflowModes)0;
				text.fontStyle = (FontStyles)0;
				text.characterSpacing = Math.Max(text.characterSpacing, 6f);
				text.wordSpacing = Math.Max(text.wordSpacing, 8f);
			}
		}

		private static TMP_Text? FindSystemOnlineTitle(HUDManager? hudManager, bool allowGlobalFallback)
		{
			if ((Object)(object)hudManager != (Object)null)
			{
				if ((Object)(object)((Component)hudManager).transform != (Object)null)
				{
					Transform obj = ((Component)hudManager).transform.Find("BottomMiddle/SystemsOnline/TipLeft1");
					TMP_Text val = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null);
					if (IsExactSystemOnlineTitle(val))
					{
						return val;
					}
				}
				TMP_Text[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<TMP_Text>(true);
				foreach (TMP_Text val2 in componentsInChildren)
				{
					if (IsExactSystemOnlineTitle(val2))
					{
						return val2;
					}
				}
			}
			if (allowGlobalFallback)
			{
				TMP_Text val3 = FindSystemOnlineTitleByFullPath();
				if (IsExactSystemOnlineTitle(val3))
				{
					return val3;
				}
				TMP_Text[] componentsInChildren = Resources.FindObjectsOfTypeAll<TMP_Text>();
				foreach (TMP_Text val4 in componentsInChildren)
				{
					if (IsExactSystemOnlineTitle(val4))
					{
						return val4;
					}
				}
			}
			return null;
		}

		private static TMP_Text? FindSystemOnlineTitleByFullPath()
		{
			GameObject val = GameObject.Find("Systems/UI/Canvas/IngamePlayerHUD/BottomMiddle/SystemsOnline/TipLeft1");
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return val.GetComponent<TMP_Text>();
		}

		private static TMP_Text? FindSystemOnlineTitleByHudPath(HUDManager? hudManager)
		{
			if ((Object)(object)((hudManager != null) ? ((Component)hudManager).transform : null) == (Object)null)
			{
				return null;
			}
			Transform val = ((Component)hudManager).transform.Find("BottomMiddle/SystemsOnline/TipLeft1");
			if (!((Object)(object)val == (Object)null))
			{
				return ((Component)val).GetComponent<TMP_Text>();
			}
			return null;
		}

		private static TMP_Text? FindEnteringAtmosphereTitle(HUDManager? hudManager)
		{
			if ((Object)(object)hudManager?.LoadingScreen == (Object)null)
			{
				return null;
			}
			TMP_Text[] componentsInChildren = ((Component)hudManager.LoadingScreen).GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val in componentsInChildren)
			{
				if (IsEnteringAtmosphereTitleObject(val))
				{
					return val;
				}
			}
			return null;
		}

		private static TMP_Text? FindHazardLevelTitle(HUDManager? hudManager)
		{
			if ((Object)(object)hudManager == (Object)null)
			{
				return null;
			}
			TMP_Text[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val in componentsInChildren)
			{
				if (IsHazardLevelTitle(val.text))
				{
					return val;
				}
			}
			return null;
		}

		private static bool IsSystemOnlineObject(TMP_Text? text)
		{
			return GetNativeTextRole(text) == NativeTextRole.SystemOnline;
		}

		private static bool IsSystemOnlinePath(Transform? transform)
		{
			if ((Object)(object)transform == (Object)null)
			{
				return false;
			}
			return BuildPath(transform).EndsWith("IngamePlayerHUD/BottomMiddle/SystemsOnline/TipLeft1", StringComparison.OrdinalIgnoreCase);
		}

		private static bool IsEnteringAtmosphereTitleObject(TMP_Text? text)
		{
			return GetNativeTextRole(text) == NativeTextRole.EnteringAtmosphere;
		}

		private static bool IsHazardLevelTitle(string? text)
		{
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string a = text.Trim();
			if (!string.Equals(a, "HAZARD LEVEL:", StringComparison.OrdinalIgnoreCase) && !string.Equals(a, "风险级别:", StringComparison.Ordinal) && !string.Equals(a, "风险级别:", StringComparison.Ordinal) && !string.Equals(a, "危险等级:", StringComparison.Ordinal))
			{
				return string.Equals(a, "危险等级:", StringComparison.Ordinal);
			}
			return true;
		}

		private static string ResolveLocalizedText(string? current, string fallback)
		{
			if (!string.IsNullOrWhiteSpace(current) && TranslationService.TryTranslate(current, out string translated))
			{
				return translated;
			}
			return fallback;
		}

		private static bool TryResolveFixedSceneLabel(string? current, out string localized)
		{
			localized = string.Empty;
			if (string.IsNullOrWhiteSpace(current))
			{
				return false;
			}
			if (FixedSceneLabels.TryGetValue(current.Trim(), out localized) && !string.IsNullOrWhiteSpace(localized))
			{
				return true;
			}
			return false;
		}

		private static int SyncFixedSceneLabelsInRelayScene(string stage)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			Scene sceneByName = SceneManager.GetSceneByName("SampleSceneRelay");
			if (!((Scene)(ref sceneByName)).IsValid() || !((Scene)(ref sceneByName)).isLoaded)
			{
				return 0;
			}
			int num = 0;
			GameObject[] rootGameObjects = ((Scene)(ref sceneByName)).GetRootGameObjects();
			for (int i = 0; i < rootGameObjects.Length; i++)
			{
				TMP_Text[] componentsInChildren = rootGameObjects[i].GetComponentsInChildren<TMP_Text>(true);
				for (int j = 0; j < componentsInChildren.Length; j++)
				{
					if (TryReplaceFixedSceneLabel(componentsInChildren[j], stage))
					{
						num++;
					}
				}
			}
			return num;
		}

		private static bool HasNamedAncestor(Transform? transform, string expectedName)
		{
			Transform val = transform;
			while ((Object)(object)val != (Object)null)
			{
				if (string.Equals(((Object)val).name, expectedName, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		private static void AuditSystemOnlineBranch(string stage, HUDManager? hudManager)
		{
			int num = 0;
			if ((Object)(object)hudManager != (Object)null)
			{
				TMP_Text[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<TMP_Text>(true);
				foreach (TMP_Text val in componentsInChildren)
				{
					if (HasNamedAncestor(val.transform, "SystemsOnline"))
					{
						num++;
						if (Plugin.RuntimeLocalizationLogsEnabled)
						{
							Plugin.Log.LogInfo((object)$"NativeRelay[{stage}] target=SystemOnline action=audit exact={IsExactSystemOnlineTitle(val)} name={((Object)val).name} path={BuildPath(val.transform)} text={val.text}");
						}
					}
				}
			}
			_ = 0;
		}

		private static void ApplyDynamicFieldTranslation(TMP_Text? text, string stage, string target)
		{
			if (!((Object)(object)text == (Object)null) && !string.IsNullOrWhiteSpace(text.text) && (TranslationService.TryTranslateKnownDynamicTextTargeted(DynamicTextDomain.PlanetInfo, text.text, out string translated) || TranslationService.TryTranslate(text.text, out translated)) && !string.IsNullOrWhiteSpace(translated))
			{
				if (!string.Equals(text.text, translated, StringComparison.Ordinal))
				{
					text.text = translated;
				}
				FontFallbackService.ApplyFallback(text, translated);
				if (Plugin.RuntimeLocalizationLogsEnabled)
				{
					Plugin.Log.LogInfo((object)("NativeRelay[" + stage + "] target=" + target + " action=applied name=" + ((Object)text).name + " path=" + BuildPath(text.transform) + " text=" + text.text));
				}
			}
		}

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

		private static bool IsExactSystemOnlineTitle(TMP_Text? text)
		{
			return GetNativeTextRole(text) == NativeTextRole.SystemOnline;
		}

		private static NativeTextRole GetNativeTextRole(TMP_Text? text)
		{
			if ((Object)(object)text == (Object)null)
			{
				return NativeTextRole.None;
			}
			if (TryGetCachedNativeTextRole(text, out var role))
			{
				return role;
			}
			role = ResolveNativeTextRole(text);
			CacheNativeTextRole(text, role);
			return role;
		}

		private static bool TryGetCachedNativeTextRole(TMP_Text text, out NativeTextRole role)
		{
			int parentInstanceId = GetParentInstanceId(text.transform);
			if (NativeTextRoleCache.TryGetValue(((Object)text).GetInstanceID(), out var value) && value.ParentId == parentInstanceId)
			{
				role = value.Role;
				return true;
			}
			role = NativeTextRole.None;
			return false;
		}

		private static NativeTextRole ResolveNativeTextRole(TMP_Text text)
		{
			if (string.Equals(((Object)text).name, "LoadText", StringComparison.OrdinalIgnoreCase) && HasNamedAncestor(text.transform, "LoadingText"))
			{
				return NativeTextRole.EnteringAtmosphere;
			}
			if (string.Equals(((Object)text).name, "TipLeft1", StringComparison.OrdinalIgnoreCase) && IsSystemOnlinePath(text.transform))
			{
				return NativeTextRole.SystemOnline;
			}
			return NativeTextRole.None;
		}

		private static void CacheNativeTextRole(TMP_Text text, NativeTextRole role)
		{
			int instanceID = ((Object)text).GetInstanceID();
			NativeTextRoleCache.Set(instanceID, new CachedNativeTextRole(GetParentInstanceId(text.transform), role), RuntimePerformanceSettings.ComponentTextCacheLimit);
		}

		private static int GetParentInstanceId(Transform? transform)
		{
			if (!((Object)(object)((transform != null) ? transform.parent : null) == (Object)null))
			{
				return ((Object)transform.parent).GetInstanceID();
			}
			return 0;
		}
	}
	internal static class ClipboardManualLocalizationService
	{
		private sealed class TextureSpec
		{
			public string FileName { get; }

			public int Width { get; }

			public int Height { get; }

			public bool PrecompressedBc3 { get; }

			public TextureSpec(string fileName, int width = 0, int height = 0, bool precompressedBc3 = false)
			{
				FileName = fileName;
				Width = width;
				Height = height;
				PrecompressedBc3 = precompressedBc3;
			}
		}

		private sealed class RendererState
		{
			public Renderer Renderer { get; }

			public Material[] OriginalMaterials { get; }

			public List<Material> LocalizedMaterials { get; }

			public RendererState(Renderer renderer, Material[] originalMaterials, List<Material> localizedMaterials)
			{
				Renderer = renderer;
				OriginalMaterials = originalMaterials;
				LocalizedMaterials = localizedMaterials;
			}
		}

		private const int MaxTextureFileBytes = 8388608;

		private const int MaxTextureDimension = 4096;

		private static readonly Dictionary<string, TextureSpec> TextureFiles = new Dictionary<string, TextureSpec>(StringComparer.OrdinalIgnoreCase)
		{
			["Manual1"] = new TextureSpec("ClipboardManualPage1.png"),
			["Manual2"] = new TextureSpec("ClipboardManualPage2.png"),
			["Manual3"] = new TextureSpec("ClipboardManualPage3.png"),
			["Manual4"] = new TextureSpec("ClipboardManualPage4.png"),
			["CruiserManual1"] = new TextureSpec("CruiserManualPage1.bc3", 900, 1260, precompressedBc3: true),
			["CruiserManual2"] = new TextureSpec("CruiserManualPage2.bc3", 900, 1260, precompressedBc3: true),
			["CruiserManual3"] = new TextureSpec("CruiserManualPage3.bc3", 900, 1260, precompressedBc3: true),
			["CruiserManual4"] = new TextureSpec("CruiserManualPage4.bc3", 900, 1260, precompressedBc3: true)
		};

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

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

		private static readonly Dictionary<int, RendererState> RendererStates = new Dictionary<int, RendererState>();

		private static string _textureDirectory = string.Empty;

		private static bool _initialized;

		public static void Initialize(string pluginDir)
		{
			_textureDirectory = Path.Combine(pluginDir, "textures");
			_initialized = true;
			FailedTextures.Clear();
		}

		public static void Apply(GrabbableObject? grabbableObject)
		{
			if (!_initialized || Plugin.IsRuntimeShuttingDown)
			{
				return;
			}
			ClipboardItem val = (ClipboardItem)(object)((grabbableObject is ClipboardItem) ? grabbableObject : null);
			if (val == null)
			{
				return;
			}
			Animator clipboardAnimator = val.clipboardAnimator;
			if (!((Object)(object)clipboardAnimator == (Object)null))
			{
				PruneDestroyedRendererStates();
				SkinnedMeshRenderer[] componentsInChildren = ((Component)clipboardAnimator).GetComponentsInChildren<SkinnedMeshRenderer>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					ApplyToRenderer((Renderer?)(object)componentsInChildren[i]);
				}
			}
		}

		public static void ApplyVehicle(VehicleController? vehicle)
		{
			if (_initialized && !Plugin.IsRuntimeShuttingDown && !((Object)(object)vehicle == (Object)null))
			{
				PruneDestroyedRendererStates();
				Renderer[] componentsInChildren = ((Component)vehicle).GetComponentsInChildren<Renderer>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					ApplyToRenderer(componentsInChildren[i]);
				}
			}
		}

		public static void Shutdown()
		{
			foreach (RendererState value in RendererStates.Values)
			{
				try
				{
					if ((Object)(object)value.Renderer != (Object)null)
					{
						value.Renderer.sharedMaterials = value.OriginalMaterials;
					}
				}
				catch
				{
				}
				foreach (Material localizedMaterial in value.LocalizedMaterials)
				{
					DestroyUnityObject((Object?)(object)localizedMaterial);
				}
			}
			RendererStates.Clear();
			foreach (Texture2D value2 in Textures.Values)
			{
				DestroyUnityObject((Object?)(object)value2);
			}
			Textures.Clear();
			FailedTextures.Clear();
			_textureDirectory = string.Empty;
			_initialized = false;
		}

		public static void OnSceneUnloaded()
		{
			PruneDestroyedRendererStates();
		}

		private static void ApplyToRenderer(Renderer? renderer)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			if ((Object)(object)renderer == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)renderer).GetInstanceID();
			if (RendererStates.ContainsKey(instanceID))
			{
				return;
			}
			Material[] sharedMaterials = renderer.sharedMaterials;
			if (sharedMaterials == null || sharedMaterials.Length == 0)
			{
				return;
			}
			Material[] array = (Material[])sharedMaterials.Clone();
			List<Material> list = new List<Material>();
			for (int i = 0; i < sharedMaterials.Length; i++)
			{
				Material val = sharedMaterials[i];
				if (!((Object)(object)val == (Object)null) && TryResolveTexture(((Object)val).name, out Texture2D texture))
				{
					Material val2 = new Material(val)
					{
						name = NormalizeMaterialName(((Object)val).name) + " (zh-CN)"
					};
					SetTexture(val2, texture);
					array[i] = val2;
					list.Add(val2);
				}
			}
			if (list.Count != 0)
			{
				renderer.sharedMaterials = array;
				RendererStates[instanceID] = new RendererState(renderer, sharedMaterials, list);
			}
		}

		private static void PruneDestroyedRendererStates()
		{
			List<int> list = null;
			foreach (KeyValuePair<int, RendererState> rendererState in RendererStates)
			{
				if ((Object)(object)rendererState.Value.Renderer != (Object)null)
				{
					continue;
				}
				if (list == null)
				{
					list = new List<int>();
				}
				list.Add(rendererState.Key);
				foreach (Material localizedMaterial in rendererState.Value.LocalizedMaterials)
				{
					DestroyUnityObject((Object?)(object)localizedMaterial);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (int item in list)
			{
				RendererStates.Remove(item);
			}
		}

		private static bool TryResolveTexture(string? materialName, out Texture2D texture)
		{
			string text = NormalizeMaterialName(materialName);
			if (Textures.TryGetValue(text, out texture))
			{
				return true;
			}
			if (FailedTextures.Contains(text) || !TextureFiles.TryGetValue(text, out TextureSpec value))
			{
				texture = null;
				return false;
			}
			Texture2D val = LoadTexture(text, value);
			if ((Object)(object)val == (Object)null)
			{
				FailedTextures.Add(text);
				texture = null;
				return false;
			}
			Textures[text] = val;
			texture = val;
			return true;
		}

		private static string NormalizeMaterialName(string? materialName)
		{
			if (string.IsNullOrWhiteSpace(materialName))
			{
				return string.Empty;
			}
			string text = materialName.Trim();
			if (text.EndsWith(" (Instance)", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - " (Instance)".Length);
			}
			if (text.EndsWith(" (zh-CN)", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - " (zh-CN)".Length);
			}
			return text;
		}

		private static void SetTexture(Material material, Texture2D texture)
		{
			if (material.HasProperty("_BaseColorMap"))
			{
				material.SetTexture("_BaseColorMap", (Texture)(object)texture);
			}
			if (material.HasProperty("_MainTex"))
			{
				material.SetTexture("_MainTex", (Texture)(object)texture);
			}
			material.mainTexture = (Texture)(object)texture;
		}

		private static Texture2D? LoadTexture(string materialName, TextureSpec spec)
		{
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Expected O, but got Unknown
			string fileName = spec.FileName;
			string text = Path.Combine(_textureDirectory, fileName);
			Texture2D val = null;
			try
			{
				FileInfo fileInfo = new FileInfo(text);
				if (!fileInfo.Exists)
				{
					Plugin.Log.LogWarning((object)("Clipboard manual texture is missing: " + fileName));
					return null;
				}
				if (fileInfo.Length <= 0 || (!spec.PrecompressedBc3 && fileInfo.Length > 8388608))
				{
					Plugin.Log.LogWarning((object)$"Clipboard manual texture has an invalid file size: {fileName} ({fileInfo.Length} bytes)");
					return null;
				}
				if (spec.PrecompressedBc3)
				{
					val = PrecompressedTextureLoader.LoadBc3(text, spec.Width, spec.Height, mipChain: false, out string error);
					if ((Object)(object)val == (Object)null)
					{
						Plugin.Log.LogWarning((object)("Clipboard manual texture could not be loaded: " + fileName + " (" + error + ")"));
						return null;
					}
				}
				else
				{
					byte[] array = File.ReadAllBytes(text);
					val = new Texture2D(2, 2, (TextureFormat)4, false);
					if (!ImageConversion.LoadImage(val, array, true))
					{
						Plugin.Log.LogWarning((object)("Clipboard manual texture could not be decoded: " + fileName));
						DestroyUnityObject((Object?)(object)val);
						return null;
					}
					if (((Texture)val).width <= 0 || ((Texture)val).height <= 0 || ((Texture)val).width > 4096 || ((Texture)val).height > 4096)
					{
						Plugin.Log.LogWarning((object)$"Clipboard manual texture dimensions are invalid: {fileName} ({((Texture)val).width}x{((Texture)val).height})");
						DestroyUnityObject((Object?)(object)val);
						return null;
					}
				}
				((Object)val).name = materialName + " (zh-CN)";
				((Texture)val).wrapMode = (TextureWrapMode)1;
				((Texture)val).filterMode = (FilterMode)1;
				return val;
			}
			catch (Exception ex)
			{
				DestroyUnityObject((Object?)(object)val);
				Plugin.Log.LogWarning((object)("Clipboard manual texture load failed for " + fileName + ": " + ex.GetType().Name + ": " + ex.Message));
				return null;
			}
		}

		private static void DestroyUnityObject(Object? value)
		{
			if (!(value == (Object)null))
			{
				if (Application.isPlaying)
				{
					Object.Destroy(value);
				}
				else
				{
					Object.DestroyImmediate(value);
				}
			}
		}
	}
	internal static class EnvironmentTextureLocalizationService
	{
		private sealed class TextureSpec
		{
			public string OriginalName { get; }

			public string FileName { get; }

			public int Width { get; }

			public int Height { get; }

			public bool PrecompressedBc3 { get; }

			public bool MipChain { get; }

			public TextureSpec(string originalName, string fileName, int width, int height, bool precompressedBc3 = false, bool mipChain = true)
			{
				OriginalName = originalName;
				FileName = fileName;
				Width = width;
				Height = height;
				PrecompressedBc3 = precompressedBc3;
				MipChain = mipChain;
			}
		}

		private sealed class RendererState
		{
			public Renderer Renderer { get; }

			public Material[] OriginalMaterials { get; }

			public List<int> LocalizedMaterialIds { get; }

			public RendererState(Renderer renderer, Material[] originalMaterials, List<int> localizedMaterialIds)
			{
				Renderer = renderer;
				OriginalMaterials = originalMaterials;
				LocalizedMaterialIds = localizedMaterialIds;
			}
		}

		private sealed class MaterialState
		{
			public WeakReference<Material> Original { get; }

			public Material Localized { get; }

			public MaterialState(Material original, Material localized)
			{
				Original = new WeakReference<Material>(original);
				Localized = localized;
			}

			public bool HasOriginal(Material material)
			{
				if (Original.TryGetTarget(out Material target) && (Object)(object)target != (Object)null)
				{
					return (Object)(object)target == (Object)(object)material;
				}
				return false;
			}

			public bool HasLiveOriginal()
			{
				if (Original.TryGetTarget(out Material target))
				{
					return (Object)(object)target != (Object)null;
				}
				return false;
			}
		}

		private const int MaxTextureFileBytes = 16777216;

		private static readonly string[] TexturePropertyNames = new string[2] { "_BaseColorMap", "_MainTex" };

		private static readonly List<Renderer> RendererScanBuffer = new List<Renderer>();

		private static readonly HashSet<int> ActiveLocalizedMaterialIds = new HashSet<int>();

		private static readonly Dictionary<string, TextureSpec> TextureSpecs = new Dictionary<string, TextureSpec>(StringComparer.OrdinalIgnoreCase)
		{
			["posters"] = new TextureSpec("posters", "ShipPostersLocalized.png", 1024, 1024),
			["TipsPoster2"] = new TextureSpec("TipsPoster2", "ShipTipsPosterLocalized.png", 796, 1024),
			["CompanyCruiserCombined4Diffuse 1"] = new TextureSpec("CompanyCruiserCombined4Diffuse 1", "CompanyCruiserDiffuseLocalized.bc3", 4096, 4096, precompressedBc3: true),
			["CompanyCruiserCombined4DiffuseDirtyV2"] = new TextureSpec("CompanyCruiserCombined4DiffuseDirtyV2", "CompanyCruiserDiffuseDirtyLocalized.bc3", 4096, 4096, precompressedBc3: true)
		};

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

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

		private static readonly Dictionary<int, RendererState> RendererStates = new Dictionary<int, RendererState>();

		private static readonly Dictionary<int, MaterialState> MaterialStates = new Dictionary<int, MaterialState>();

		private static string _textureDirectory = string.Empty;

		private static bool _initialized;

		public static void Initialize(string pluginDir)
		{
			_textureDirectory = Path.Combine(pluginDir, "textures");
			_initialized = true;
		}

		public static void ApplyShipEnvironment(StartOfRound? startOfRound)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (!CanApply() || (Object)(object)startOfRound == (Object)null)
			{
				return;
			}
			PruneDestroyedRendererStates();
			PruneUnusedMaterialStates();
			Scene scene = ((Component)startOfRound).gameObject.scene;
			if (!((Scene)(ref scene)).IsValid() || !((Scene)(ref scene)).isLoaded)
			{
				return;
			}
			GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
			foreach (GameObject val in rootGameObjects)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				RendererScanBuffer.Clear();
				val.GetComponentsInChildren<Renderer>(true, RendererScanBuffer);
				foreach (Renderer item in RendererScanBuffer)
				{
					ApplyToRenderer(item);
				}
			}
			RendererScanBuffer.Clear();
		}

		public static void ApplyVehicle(VehicleController? vehicle)
		{
			if (!CanApply() || (Object)(object)vehicle == (Object)null)
			{
				return;
			}
			PruneDestroyedRendererStates();
			PruneUnusedMaterialStates();
			RendererScanBuffer.Clear();
			((Component)vehicle).GetComponentsInChildren<Renderer>(false, RendererScanBuffer);
			foreach (Renderer item in RendererScanBuffer)
			{
				ApplyToRenderer(item);
			}
			RendererScanBuffer.Clear();
		}

		public static void Shutdown()
		{
			foreach (RendererState value in RendererStates.Values)
			{
				try
				{
					if ((Object)(object)value.Renderer != (Object)null)
					{
						value.Renderer.sharedMaterials = value.OriginalMaterials;
					}
				}
				catch
				{
				}
			}
			RendererStates.Clear();
			foreach (MaterialState value2 in MaterialStates.Values)
			{
				DestroyUnityObject((Object?)(object)value2.Localized);
			}
			MaterialStates.Clear();
			RendererScanBuffer.Clear();
			ActiveLocalizedMaterialIds.Clear();
			foreach (Texture2D value3 in LocalizedTextures.Values)
			{
				DestroyUnityObject((Object?)(object)value3);
			}
			LocalizedTextures.Clear();
			FailedTextures.Clear();
			_textureDirectory = string.Empty;
			_initialized = false;
		}

		public static void OnSceneUnloaded()
		{
			PruneDestroyedRendererStates();
			PruneUnusedMaterialStates();
		}

		private static bool CanApply()
		{
			if (_initialized)
			{
				return !Plugin.IsRuntimeShuttingDown;
			}
			return false;
		}

		private static void ApplyToRenderer(Renderer? renderer)
		{
			if ((Object)(object)renderer == (Object)null || RendererStates.ContainsKey(((Object)renderer).GetInstanceID()))
			{
				return;
			}
			Material[] sharedMaterials = renderer.sharedMaterials;
			if (sharedMaterials == null || sharedMaterials.Length == 0)
			{
				return;
			}
			Material[] array = (Material[])sharedMaterials.Clone();
			List<int> list = null;
			bool flag = false;
			for (int i = 0; i < sharedMaterials.Length; i++)
			{
				Material val = sharedMaterials[i];
				Material orCreateLocalizedMaterial = GetOrCreateLocalizedMaterial(val);
				if (!((Object)(object)orCreateLocalizedMaterial == (Object)null) && orCreateLocalizedMaterial != val)
				{
					array[i] = orCreateLocalizedMaterial;
					if (list == null)
					{
						list = new List<int>();
					}
					int instanceID = ((Object)val).GetInstanceID();
					if (!list.Contains(instanceID))
					{
						list.Add(instanceID);
					}
					flag = true;
				}
			}
			if (flag)
			{
				renderer.sharedMaterials = array;
				RendererStates[((Object)renderer).GetInstanceID()] = new RendererState(renderer, sharedMaterials, list ?? new List<int>());
			}
		}

		private static Material? GetOrCreateLocalizedMaterial(Material? original)
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Expected O, but got Unknown
			if ((Object)(object)original == (Object)null)
			{
				return null;
			}
			int instanceID = ((Object)original).GetInstanceID();
			if (MaterialStates.TryGetValue(instanceID, out MaterialState value))
			{
				if (value.HasOriginal(original))
				{
					return value.Localized;
				}
				DestroyUnityObject((Object?)(object)value.Localized);
				MaterialStates.Remove(instanceID);
			}
			Material val = null;
			bool flag = false;
			string[] texturePropertyNames = TexturePropertyNames;
			foreach (string text in texturePropertyNames)
			{
				if (!original.HasProperty(text) || !TryResolveSpec(original.GetTexture(text), out TextureSpec spec))
				{
					continue;
				}
				Texture2D val2 = EnsureTextureLoaded(spec, original.GetTexture(text));
				if (!((Object)(object)val2 == (Object)null))
				{
					if (val == null)
					{
						val = new Material(original);
					}
					val.SetTexture(text, (Texture)(object)val2);
					flag = true;
				}
			}
			if (!flag && original.HasProperty("_MainTex") && TryResolveSpec(original.mainTexture, out TextureSpec spec2))
			{
				Texture2D val3 = EnsureTextureLoaded(spec2, original.mainTexture);
				if ((Object)(object)val3 != (Object)null)
				{
					val = new Material(original)
					{
						mainTexture = (Texture)(object)val3
					};
					flag = true;
				}
			}
			if (!flag || (Object)(object)val == (Object)null)
			{
				DestroyUnityObject((Object?)(object)val);
				return original;
			}
			((Object)val).name = NormalizeName(((Object)original).name) + " (zh-CN)";
			if (MaterialStates.TryGetValue(instanceID, out MaterialState value2))
			{
				DestroyUnityObject((Object?)(object)value2.Localized);
			}
			MaterialStates[instanceID] = new MaterialState(original, val);
			return val;
		}

		private static Texture2D? EnsureTextureLoaded(TextureSpec spec, Texture? originalTexture)
		{
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			if (LocalizedTextures.TryGetValue(spec.OriginalName, out Texture2D value))
			{
				return value;
			}
			if (FailedTextures.Contains(spec.OriginalName))
			{
				return null;
			}
			string text = Path.Combine(_textureDirectory, spec.FileName);
			Texture2D val = null;
			try
			{
				FileInfo fileInfo = new FileInfo(text);
				if (!fileInfo.Exists)
				{
					throw new FileNotFoundException("localized texture file is missing", text);
				}
				if (fileInfo.Length <= 0 || (!spec.PrecompressedBc3 && fileInfo.Length > 16777216))
				{
					throw new InvalidDataException($"invalid file size ({fileInfo.Length} bytes)");
				}
				if (spec.PrecompressedBc3)
				{
					val = PrecompressedTextureLoader.LoadBc3(text, spec.Width, spec.Height, spec.MipChain, out string error);
					if ((Object)(object)val == (Object)null)
					{
						throw new InvalidDataException(error ?? "precompressed BC3 load failed");
					}
				}
				else
				{
					byte[] data = File.ReadAllBytes(text);
					val = LoadTextureData(data, useRuntimeCompression: true) ?? LoadTextureData(data, useRuntimeCompression: false);
				}
				if ((Object)(object)val == (Object)null || ((Texture)val).width != spec.Width || ((Texture)val).height != spec.Height)
				{
					string arg = (((Object)(object)val == (Object)null) ? "decode failed" : $"{((Texture)val).width}x{((Texture)val).height}");
					throw new InvalidDataException($"expected {spec.Width}x{spec.Height}, got {arg}");
				}
				((Object)val).name = spec.OriginalName + " (zh-CN)";
				if ((Object)(object)originalTexture != (Object)null)
				{
					((Texture)val).wrapMode = originalTexture.wrapMode;
					((Texture)val).filterMode = originalTexture.filterMode;
					((Texture)val).anisoLevel = originalTexture.anisoLevel;
					((Texture)val).mipMapBias = originalTexture.mipMapBias;
				}
				LocalizedTextures[spec.OriginalName] = val;
				return val;
			}
			catch (Exception ex)
			{
				DestroyUnityObject((Object?)(object)val);
				FailedTextures.Add(spec.OriginalName);
				Plugin.Log.LogWarning((object)("Environment texture localization failed for " + spec.FileName + ": " + ex.GetType().Name + ": " + ex.Message));
				return null;
			}
		}

		private static Texture2D? LoadTextureData(byte[] data, bool useRuntimeCompression)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_001e: 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_0026: Expected O, but got Unknown
			Texture2D val = null;
			try
			{
				TextureFormat val2 = (TextureFormat)((useRuntimeCompression && SystemInfo.SupportsTextureFormat((TextureFormat)12)) ? 12 : 4);
				int num = (((int)val2 == 12) ? 4 : 2);
				val = new Texture2D(num, num, val2, true);
				if (ImageConversion.LoadImage(val, data, true))
				{
					return val;
				}
				DestroyUnityObject((Object?)(object)val);
				return null;
			}
			catch
			{
				DestroyUnityObject((Object?)(object)val);
				return null;
			}
		}

		private static bool TryResolveSpec(Texture? texture, out TextureSpec spec)
		{
			return TextureSpecs.TryGetValue(NormalizeName((texture != null) ? ((Object)texture).name : null), out spec);
		}

		private static string NormalizeName(string? name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return string.Empty;
			}
			string text = name.Trim();
			if (text.EndsWith(" (Instance)", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - " (Instance)".Length);
			}
			if (text.EndsWith(" (zh-CN)", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - " (zh-CN)".Length);
			}
			return text;
		}

		private static void PruneDestroyedRendererStates()
		{
			List<int> list = null;
			foreach (KeyValuePair<int, RendererState> rendererState in RendererStates)
			{
				if (!((Object)(object)rendererState.Value.Renderer != (Object)null))
				{
					if (list == null)
					{
						list = new List<int>();
					}
					list.Add(rendererState.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (int item in list)
			{
				RendererStates.Remove(item);
			}
		}

		private static void PruneUnusedMaterialStates()
		{
			ActiveLocalizedMaterialIds.Clear();
			foreach (RendererState value in RendererStates.Values)
			{
				if ((Object)(object)value.Renderer == (Object)null)
				{
					continue;
				}
				foreach (int localizedMaterialId in value.LocalizedMaterialIds)
				{
					ActiveLocalizedMaterialIds.Add(localizedMaterialId);
				}
			}
			List<int> list = null;
			foreach (KeyValuePair<int, MaterialState> materialState in MaterialStates)
			{
				if (!((Object)(object)materialState.Value.Localized != (Object)null) || !materialState.Value.HasLiveOriginal() || !ActiveLocalizedMaterialIds.Contains(materialState.Key))
				{
					if (list == null)
					{
						list = new List<int>();
					}
					list.Add(materialState.Key);
					DestroyUnityObject((Object?)(object)materialState.Value.Localized);
				}
			}
			if (list != null)
			{
				foreach (int item in list)
				{
					MaterialStates.Remove(item);
				}
			}
			ActiveLocalizedMaterialIds.Clear();
		}

		private static void DestroyUnityObject(Object? value)
		{
			if (!(value == (Object)null))
			{
				if (Application.isPlaying)
				{
					Object.Destroy(value);
				}
				else
				{
					Object.DestroyImmediate(value);
				}
			}
		}
	}
	internal static class PrecompressedTextureLoader
	{
		public static Texture2D? LoadBc3(string path, int width, int height, bool mipChain, out string? error)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			error = null;
			Texture2D val = null;
			try
			{
				if (!SystemInfo.SupportsTextureFormat((TextureFormat)12))
				{
					error = "DXT5/BC3 is not supported by this graphics device";
					return null;
				}
				FileInfo fileInfo = new FileInfo(path);
				if (!fileInfo.Exists)
				{
					error = "precompressed texture file is missing";
					return null;
				}
				long num = CalculateBc3ByteCount(width, height, mipChain);
				if (fileInfo.Length != num)
				{
					error = $"expected {num} bytes, got {fileInfo.Length}";
					return null;
				}
				byte[] array = File.ReadAllBytes(path);
				val = new Texture2D(width, height, (TextureFormat)12, mipChain);
				val.LoadRawTextureData(array);
				val.Apply(false, true);
				return val;
			}
			catch (Exception ex)
			{
				DestroyUnityObject((Object?)(object)val);
				error = ex.GetType().Name + ": " + ex.Message;
				return null;
			}
		}

		public static long CalculateBc3ByteCount(int width, int height, bool mipChain)
		{
			long num = 0L;
			while (true)
			{
				num += (long)Math.Max(1, (width + 3) / 4) * (long)Math.Max(1, (height + 3) / 4) * 16;
				if (!mipChain || (width == 1 && height == 1))
				{
					break;
				}
				width = Math.Max(1, width / 2);
				height = Math.Max(1, height / 2);
			}
			return num;
		}

		private static void DestroyUnityObject(Object? value)
		{
			if (!(value == (Object)null))
			{
				if (Application.isPlaying)
				{
					Object.Destroy(value);
				}
				else
				{
					Object.DestroyImmediate(value);
				}
			}
		}
	}
	internal static class RadiationWarningAuditService
	{
		internal const string WarningRootPathSuffix = "IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease";

		private static ConfigEntry<bool>? _enabled;

		private static ConfigEntry<int>? _sampleCount;

		private static ConfigEntry<float>? _sampleIntervalSeconds;

		private static HUDManager? _activeHudManager;

		private static Coroutine? _activeAuditCoroutine;

		public static void Initialize(ConfigFile config)
		{
			_enabled = config.Bind<bool>("90 诊断 - 辐射警告审计", "Enabled", false, "启用辐射警告原始 UI 子树的限量采样诊断。默认关闭,避免运行时枚举 UI 子树。");
			_sampleCount = config.Bind<int>("90 诊断 - 辐射警告审计", "SampleCount", 5, "辐射警告触发后采样的次数上限。");
			_sampleIntervalSeconds = config.Bind<float>("90 诊断 - 辐射警告审计", "SampleIntervalSeconds", 0.2f, "两次辐射警告诊断采样之间的间隔,单位为秒。");
		}

		public static void Shutdown()
		{
			try
			{
				if (_activeAuditCoroutine != null && (Object)(object)_activeHudManager != (Object)null)
				{
					((MonoBehaviour)_activeHudManager).StopCoroutine(_activeAuditCoroutine);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("RadiationWarningAuditService shutdown failed while stopping coroutine: " + ex.GetType().Name + ": " + ex.Message));
			}
			finally
			{
				_activeAuditCoroutine = null;
				_activeHudManager = null;
				_enabled = null;
				_sampleCount = null;
				_sampleIntervalSeconds = null;
			}
		}

		public static void OnRadiationWarningTriggered(HUDManager hudManager, string stage)
		{
			if ((Object)(object)hudManager == (Object)null)
			{
				return;
			}
			ConfigEntry<bool>? enabled = _enabled;
			if (enabled == null || !enabled.Value)
			{
				return;
			}
			if ((Object)(object)FindWarningRoot(hudManager) == (Object)null)
			{
				Plugin.Log.LogWarning((object)("RadiationAudit[" + stage + "] action=root-not-found suffix=IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease"));
				return;
			}
			if (_activeAuditCoroutine != null && (Object)(object)_activeHudManager != (Object)null)
			{
				((MonoBehaviour)_activeHudManager).StopCoroutine(_activeAuditCoroutine);
				_activeAuditCoroutine = null;
			}
			_activeHudManager = hudManager;
			_activeAuditCoroutine = ((MonoBehaviour)hudManager).StartCoroutine(SampleWarningSubtree(hudManager, stage, _sampleCount?.Value ?? 0, _sampleIntervalSeconds?.Value ?? 0f));
		}

		private static IEnumerator SampleWarningSubtree(HUDManager hudManager, string stage, int sampleCount, float sampleIntervalSeconds)
		{
			int boundedSampleCount = Mathf.Max(1, sampleCount);
			float boundedIntervalSeconds = Mathf.Max(0f, sampleIntervalSeconds);
			for (int sampleIndex = 0; sampleIndex < boundedSampleCount; sampleIndex++)
			{
				if ((Object)(object)hudManager == (Object)null)
				{
					break;
				}
				Transform val = FindWarningRoot(hudManager);
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log.LogInfo((object)string.Format("RadiationAudit[{0}] sample={1} action=sampling-stopped-root-lost suffix={2}", stage, sampleIndex, "IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease"));
					break;
				}
				AuditImages(val, stage, sampleIndex);
				AuditRawImages(val, stage, sampleIndex);
				AuditSpriteRenderers(val, stage, sampleIndex);
				AuditGraphicMaterials(val, stage, sampleIndex);
				AuditRendererMaterials(val, stage, sampleIndex);
				if (sampleIndex + 1 < boundedSampleCount)
				{
					yield return (object)((!(boundedIntervalSeconds > 0f)) ? ((WaitForSeconds)null) : new WaitForSeconds(boundedIntervalSeconds));
				}
			}
			_activeAuditCoroutine = null;
			_activeHudManager = null;
		}

		private static void AuditGraphicMaterials(Transform root, string stage, int sampleIndex)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			HashSet<int> hashSet = new HashSet<int>();
			Graphic[] componentsInChildren = ((Component)root).GetComponentsInChildren<Graphic>(true);
			foreach (Graphic val in componentsInChildren)
			{
				Material material;
				try
				{
					material = val.material;
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)$"RadiationAudit[{stage}] sample={sampleIndex} component=GraphicMaterial path={BuildPath(((Component)val).transform)} active={((Component)val).gameObject.activeInHierarchy} enabled={((Behaviour)val).enabled} alpha={val.color.a:0.###} material=<error> texture=<error> error={ex.GetType().Name}:{ex.Message}");
					continue;
				}
				if ((Object)(object)material != (Object)null)
				{
					hashSet.Add(((Object)material).GetInstanceID());
				}
			}
		}

		private static void AuditRendererMaterials(Transform root, string stage, int sampleIndex)
		{
			Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Material[] sharedMaterials = componentsInChildren[i].sharedMaterials;
				if (sharedMaterials != null && sharedMaterials.Length != 0)
				{
					for (int j = 0; j < sharedMaterials.Length; j++)
					{
						_ = sharedMaterials[j];
					}
				}
			}
		}

		private static void AuditImages(Transform root, string stage, int sampleIndex)
		{
			Image[] componentsInChildren = ((Component)root).GetComponentsInChildren<Image>(true);
			foreach (Image val in componentsInChildren)
			{
				Sprite obj = val.overrideSprite ?? val.sprite;
				if (obj != null)
				{
					_ = obj.texture;
				}
			}
		}

		private static void AuditRawImages(Transform root, string stage, int sampleIndex)
		{
			RawImage[] componentsInChildren = ((Component)root).GetComponentsInChildren<RawImage>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				_ = componentsInChildren[i];
			}
		}

		private static void AuditSpriteRenderers(Transform root, string stage, int sampleIndex)
		{
			SpriteRenderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<SpriteRenderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				_ = componentsInChildren[i].sprite;
			}
		}

		private static Transform? FindWarningRoot(HUDManager hudManager)
		{
			Animator radiationGraphicAnimator = hudManager.radiationGraphicAnimator;
			Transform val = ((radiationGraphicAnimator != null) ? ((Component)radiationGraphicAnimator).transform : null);
			if (HasExpectedRootPath(val))
			{
				return val;
			}
			Transform[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<Transform>(true);
			foreach (Transform val2 in componentsInChildren)
			{
				if (HasExpectedRootPath(val2))
				{
					return val2;
				}
			}
			return null;
		}

		private static bool HasExpectedRootPath(Transform? transform)
		{
			if ((Object)(object)transform == (Object)null)
			{
				return false;
			}
			return BuildPath(transform).EndsWith("IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease", StringComparison.OrdinalIgnoreCase);
		}

		private static string DescribeName(Object? obj)
		{
			if (!string.IsNullOrWhiteSpace((obj != null) ? obj.name : null))
			{
				return obj.name;
			}
			return "<null>";
		}

		private static string BuildPath(Transform? transform)
		{
			if ((Object)(object)transform == (Object)null)
			{
				return "<null>";
			}
			string text = ((Object)transform).name;
			Transform parent = transform.parent;
			while ((Object)(object)parent != (Object)null)
			{
				text = ((Object)parent).name + "/" + text;
				parent = parent.parent;
			}
			return text;
		}
	}
	internal static class RadiationWarningPlaybackService
	{
		private sealed class PlaybackState
		{
			public Transform Root { get; }

			public Image PanelImage { get; }

			public Sprite? OriginalRawOverrideSprite { get; }

			public Sprite? LastAppliedLocalizedSprite { get; set; }

			public string? LastAppliedOriginalSpriteName { get; set; }

			public bool CleanupCompleted { get; set; }

			public PlaybackState(Transform root, Image panelImage, Sprite? originalRawOverrideSprite)
			{
				Root = root;
				PanelImage = panelImage;
				OriginalRawOverrideSprite = originalRawOverrideSprite;
			}
		}

		private const string TextureSubfolder = "textures";

		private const string WarningRootPathSuffix = "IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease";

		private const string PanelObjectName = "Panel";

		private const float OriginalClipDurationSeconds = 1.7166667f;

		private const float DefaultFollowDurationSeconds = 1.85f;

		private const float MaxFollowDurationSeconds = 5f;

		private const long MaxTextureBytes = 8388608L;

		private const long MaxTexturePixels = 16777216L;

		private static readonly Dictionary<string, string[]> OriginalSpriteFrameFiles = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase)
		{
			["RedUIPanelGlitchB"] = FrameFileNameCandidates("RedUIPanelGlitchB", "C864CEDFD2-470F6C25C6"),
			["RedUIPanelGlitchBWarning"] = FrameFileNameCandidates("RedUIPanelGlitchBWarning", "E5C5C6E52B-F9F9EA9261"),
			["RedUIPanelGlitchBWarningRadiation"] = FrameFileNameCandidates("RedUIPanelGlitchBWarningRadiation", "FFCC97E055-2C688E6DC8"),
			["RedUIPanelGlitchBWarningRadiationB"] = FrameFileNameCandidates("RedUIPanelGlitchBWarningRadiationB", "7D4ED37952-95F686DE01"),
			["RedUIPanelGlitchBWarningRadiationC"] = FrameFileNameCandidates("RedUIPanelGlitchBWarningRadiationC", "7FBAA1AA30-9E2C036778"),
			["RedUIPanelGlitchBWarningRadiationD"] = FrameFileNameCandidates("RedUIPanelGlitchBWarningRadiationD", "4877939F7B-47E20481B0")
		};

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

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

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

		private static string? _textureDirectory;

		private static ConfigEntry<bool>? _enabled;

		private static ConfigEntry<float>? _followDurationSeconds;

		private static HUDManager? _activeHudManager;

		private static Coroutine? _activePlaybackCoroutine;

		private static PlaybackState? _activePlaybackState;

		public static void Initialize(string pluginDir, ConfigFile config)
		{
			_textureDirectory = Path.Combine(pluginDir, "textures");
			_enabled = config.Bind<bool>("04 资源 - 辐射警告", "Enabled", true, "启用跟随原版动画的辐射警告贴图替换。");
			_followDurationSeconds = config.Bind<float>("04 资源 - 辐射警告", "FollowDurationSeconds", 1.85f, "跟随原版 RadiationIncreaseWarning 动画并替换本地化贴图的持续时间,单位为秒。");
			foreach (string key in OriginalSpriteFrameFiles.Keys)
			{
				ResolveFrameTexture(key, "Initialize");
			}
			Plugin.Log.LogInfo((object)$"RadiationPlayback initialized mode=animator-follow clipSeconds={1.7166667f:0.###}");
		}

		public static void OnRadiationWarningTriggered(HUDManager hudManager, string stage)
		{
			if ((Object)(object)hudManager == (Object)null)
			{
				return;
			}
			ConfigEntry<bool>? enabled = _enabled;
			if (enabled == null || !enabled.Value)
			{
				return;
			}
			if (_activePlaybackCoroutine != null)
			{
				Plugin.Log.LogInfo((object)("RadiationPlayback[" + stage + "] action=duplicate-trigger-restart mode=animator-follow"));
				CleanupActivePlayback(stage, "duplicate-restart");
			}
			Transform val = FindWarningRoot(hudManager);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)("RadiationPlayback[" + stage + "] action=root-not-found suffix=IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease"));
				return;
			}
			Image val2 = FindWarningPanel(val);
			if ((Object)(object)val2 == (Object)null)
			{
				Plugin.Log.LogWarning((object)("RadiationPlayback[" + stage + "] action=panel-not-found path=" + BuildPath(val) + " child=Panel"));
			}
			else if (HasAllLocalizedTextures(stage))
			{
				PlaybackState playbackState = new PlaybackState(val, val2, ReadRawOverrideSprite(val2));
				_activeHudManager = hudManager;
				_activePlaybackState = playbackState;
				_activePlaybackCoroutine = ((MonoBehaviour)hudManager).StartCoroutine(FollowAnimatorSpriteCurve(playbackState, stage));
			}
		}

		public static void Shutdown()
		{
			CleanupActivePlayback("Shutdown", "shutdown");
			ClearCachedAssets();
		}

		public static void ResetForHudLifecycle(HUDManager hudManager, string stage)
		{
			if (!((Object)(object)hudManager == (Object)null) && (_activePlaybackCoroutine != null || _activePlaybackState != null))
			{
				CleanupActivePlayback(stage, "hud-reset");
			}
		}

		private static IEnumerator FollowAnimatorSpriteCurve(PlaybackState playbackState, string stage)
		{
			float followDurationSeconds = GetFollowDurationSeconds();
			float endTime = Time.realtimeSinceStartup + followDurationSeconds;
			Plugin.Log.LogInfo((object)$"RadiationPlayback[{stage}] FollowAnimatorSpriteCurve started path={BuildPath(playbackState.Root)} panel={BuildPath(((Component)playbackState.PanelImage).transform)} duration={followDurationSeconds:0.###}");
			try
			{
				while ((Object)(object)playbackState.PanelImage != (Object)null && Time.realtimeSinceStartup <= endTime)
				{
					ApplyCurrentAnimatorSpriteSubstitution(playbackState, stage);
					yield return null;
				}
				if ((Object)(object)playbackState.PanelImage != (Object)null)
				{
					ApplyCurrentAnimatorSpriteSubstitution(playbackState, stage);
				}
				Plugin.Log.LogInfo((object)("RadiationPlayback[" + stage + "] FollowAnimatorSpriteCurve finished path=" + BuildPath(playbackState.Root)));
			}
			finally
			{
				CleanupPlaybackState(playbackState, stage, "finally");
			}
		}

		private static float GetFollowDurationSeconds()
		{
			float num = _followDurationSeconds?.Value ?? 1.85f;
			if (float.IsNaN(num) || float.IsInfinity(num))
			{
				num = 1.85f;
			}
			return Mathf.Clamp(num, 1.7166667f, 5f);
		}

		private static void ApplyCurrentAnimatorSpriteSubstitution(PlaybackState playbackState, string stage)
		{
			Image panelImage = playbackState.PanelImage;
			Sprite sprite = panelImage.sprite;
			string text = NormalizeSpriteName((sprite != null) ? ((Object)sprite).name : null);
			if (string.IsNullOrWhiteSpace(text))
			{
				return;
			}
			if (!OriginalSpriteFrameFiles.ContainsKey(text))
			{
				ClearLocalizedOverrideIfOwned(playbackState, "unmapped-original");
				return;
			}
			Texture2D val = ResolveFrameTexture(text, stage);
			if (!((Object)(object)val == (Object)null))
			{
				Sprite val2 = ResolveLocalizedSprite(text, sprite, val);
				if (!((Object)(object)val2 == (Object)null) && ReadRawOverrideSprite(panelImage) != val2)
				{
					panelImage.overrideSprite = val2;
					((Graphic)panelImage).SetVerticesDirty();
					((Graphic)panelImage).SetMaterialDirty();
					string.Equals(playbackState.LastAppliedOriginalSpriteName, text, StringComparison.OrdinalIgnoreCase);
					playbackState.LastAppliedOriginalSpriteName = text;
					playbackState.LastAppliedLocalizedSprite = val2;
				}
			}
		}

		private static void CleanupActivePlayback(string stage, string reason)
		{
			Coroutine activePlaybackCoroutine = _activePlaybackCoroutine;
			HUDManager activeHudManager = _activeHudManager;
			PlaybackState activePlaybackState = _activePlaybackState;
			_activePlaybackCoroutine = null;
			_activeHudManager = null;
			_activePlaybackState = null;
			if (activePlaybackCoroutine != null && (Object)(object)activeHudManager != (Object)null)
			{
				((MonoBehaviour)activeHudManager).StopCoroutine(activePlaybackCoroutine);
			}
			if (activePlaybackState != null)
			{
				CleanupPlaybackState(activePlaybackState, stage, reason);
			}
		}

		private static void CleanupPlaybackState(PlaybackState playbackState, string stage, string reason)
		{
			if (!playbackState.CleanupCompleted)
			{
				playbackState.CleanupCompleted = true;
				ClearLocalizedOverrideIfOwned(playbackState, reason);
				if (_activePlaybackState == playbackState)
				{
					_activePlaybackState = null;
					_activePlaybackCoroutine = null;
					_activeHudManager = null;
				}
				string text = (((Object)(object)playbackState.PanelImage != (Object)null) ? BuildPath(((Component)playbackState.PanelImage).transform) : "<null>");
				Plugin.Log.LogInfo((object)("RadiationPlayback[" + stage + "] action=restore reason=" + reason + " path=" + BuildPath(playbackState.Root) + " panel=" + text));
			}
		}

		private static void ClearLocalizedOverrideIfOwned(PlaybackState playbackState, string reason)
		{
			Image panelImage = playbackState.PanelImage;
			if (!((Object)(object)panelImage == (Object)null))
			{
				Sprite val = ReadRawOverrideSprite(panelImage);
				if ((Object)(object)val != (Object)null && val != playbackState.LastAppliedLocalizedSprite && !IsCachedLocalizedSprite(val))
				{
					Plugin.Log.LogInfo((object)("RadiationPlayback action=skip-restore reason=" + reason + " currentOverride=" + ((Object)val).name));
					return;
				}
				panelImage.overrideSprite = playbackState.OriginalRawOverrideSprite;
				((Graphic)panelImage).SetVerticesDirty();
				((Graphic)panelImage).SetMaterialDirty();
			}
		}

		private static bool HasAllLocalizedTextures(string stage)
		{
			foreach (string key in OriginalSpriteFrameFiles.Keys)
			{
				if ((Object)(object)ResolveFrameTexture(key, stage) == (Object)null)
				{
					return false;
				}
			}
			return true;
		}

		private static string[] FrameFileNameCandidates(string baseName, string contentHash)
		{
			return new string[2]
			{
				baseName + " [" + contentHash + "].png",
				baseName + "[" + contentHash + "].png"
			};
		}

		private static string? ResolveFrameFileName(string originalSpriteName, string stage)
		{
			if (!OriginalSpriteFrameFiles.TryGetValue(originalSpriteName, out string[] value))
			{
				return null;
			}
			string[] array = value;
			foreach (string text in array)
			{
				if ((Object)(object)LoadTexture(text) != (Object)null)
				{
					return text;
				}
			}
			Plugin.Log.LogWarning((object)("RadiationPlayback[" + stage + "] action=incomplete-frame-set original=" + originalSpriteName + " candidates=" + string.Join("|", value) + " nativeFallback=true"));
			return null;
		}

		private static Texture2D? ResolveFrameTexture(string originalSpriteName, string stage)
		{
			if (ResolvedFrameTextureCache.TryGetValue(originalSpriteName, out Texture2D value))
			{
				return value;
			}
			string text = ResolveFrameFileName(originalSpriteName, stage);
			if (text == null)
			{
				ResolvedFrameTextureCache[originalSpriteName] = null;
				return null;
			}
			Texture2D val = LoadTexture(text);
			ResolvedFrameTextureCache[originalSpriteName] = val;
			return val;
		}

		private static Image? FindWarningPanel(Transform root)
		{
			Transform obj = root.Find("Panel");
			Image val = ((obj != null) ? ((Component)obj).GetComponent<Image>() : null);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			Image[] componentsInChildren = ((Component)root).GetComponentsInChildren<Image>(true);
			foreach (Image val2 in componentsInChildren)
			{
				if (string.Equals(((Object)val2).name, "Panel", StringComparison.OrdinalIgnoreCase))
				{
					return val2;
				}
			}
			return null;
		}

		private static Sprite? ResolveLocalizedSprite(string originalSpriteName, Sprite? originalSprite, Texture2D texture)
		{
			string key = BuildSpriteCacheKey(originalSpriteName, originalSprite, texture);
			if (SpriteCache.TryGetValue(key, out Sprite value))
			{
				return value;
			}
			Sprite val = CreateLocalizedSprite(texture, originalSprite);
			SpriteCache[key] = val;
			return val;
		}

		private static string BuildSpriteCacheKey(string originalSpriteName, Sprite? originalSprite, Texture2D texture)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_0092: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)originalSprite == (Object)null)
			{
				return originalSpriteName + ":" + ((Object)texture).name + ":fallback";
			}
			Rect rect = originalSprite.rect;
			return $"{originalSpriteName}:{((Object)texture).name}:{((Rect)(ref rect)).x}:{((Rect)(ref rect)).y}:{((Rect)(ref rect)).width}:{((Rect)(ref rect)).height}:{originalSprite.pivot.x}:{originalSprite.pivot.y}:{originalSprite.pixelsPerUnit}";
		}

		private static Sprite? CreateLocalizedSprite(Texture2D texture, Sprite? template)
		{
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)template != (Object)null)
				{
					Rect rect = template.rect;
					if (((Rect)(ref rect)).width > 0f && ((Rect)(ref rect)).height > 0f && ((Rect)(ref rect)).xMax <= (float)((Texture)texture).width && ((Rect)(ref rect)).yMax <= (float)((Texture)texture).height)
					{
						Vector2 val = default(Vector2);
						((Vector2)(ref val))..ctor(template.pivot.x / ((Rect)(ref rect)).width, template.pivot.y / ((Rect)(ref rect)).height);
						Sprite obj = Sprite.Create(texture, rect, val, template.pixelsPerUnit, 0u, (SpriteMeshType)0, template.border);
						((Object)obj).name = ((Object)texture).name;
						return obj;
					}
				}
				Sprite obj2 = Sprite.Create(texture, new Rect(0f, 0f, (float)((Texture)texture).width, (float)((Texture)texture).height), new Vector2(0.5f, 0.5f), 100f);
				((Object)obj2).name = ((Object)texture).name;
				return obj2;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("RadiationPlayback[Sprite] action=create-failed texture=" + ((Object)texture).name + " error=" + ex.GetType().Name + ": " + ex.Message));
				return null;
			}
		}

		private static bool IsCachedLocalizedSprite(Sprite sprite)
		{
			foreach (Sprite value in SpriteCache.Values)
			{
				if (value == sprite)
				{
					return true;
				}
			}
			return false;
		}

		private static Sprite? ReadRawOverrideSprite(Image image)
		{
			try
			{
				return image.m_OverrideSprite;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("RadiationPlayback[Image] action=read-raw-override-failed error=" + ex.GetType().Name + ": " + ex.Message));
				return null;
			}
		}

		private static Transform? FindWarningRoot(HUDManager hudManager)
		{
			Animator radiationGraphicAnimator = hudManager.radiationGraphicAnimator;
			Transform val = ((radiationGraphicAnimator != null) ? ((Component)radiationGraphicAnimator).transform : null);
			if (HasExpectedRootPath(val))
			{
				return val;
			}
			Transform[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<Transform>(true);
			foreach (Transform val2 in componentsInChildren)
			{
				if (HasExpectedRootPath(val2))
				{
					return val2;
				}
			}
			return null;
		}

		private static bool HasExpectedRootPath(Transform? transform)
		{
			if ((Object)(object)transform == (Object)null)
			{
				return false;
			}
			return BuildPath(transform).EndsWith("IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease", StringComparison.OrdinalIgnoreCase);
		}

		private static Texture2D? LoadTexture(string fileName)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			if (TextureCache.TryGetValue(fileName, out Texture2D value))
			{
				return value;
			}
			if (string.IsNullOrWhiteSpace(_textureDirectory))
			{
				return null;
			}
			string text = Path.Combine(_textureDirectory, fileName);
			if (!File.Exists(text))
			{
				TextureCache[fileName] = null;
				return null;
			}
			Texture2D val = null;
			try
			{
				FileInfo fileInfo = new FileInfo(text);
				if (fileInfo.Length > 8388608)
				{
					Plugin.Log.LogWarning((object)$"RadiationPlayback[Texture] action=file-too-large file={fileName} bytes={fileInfo.Length} max={8388608L}");
					TextureCache[fileName] = null;
					return null;
				}
				byte[] array = File.ReadAllBytes(text);
				val = new Texture2D(2, 2, (TextureFormat)4, false);
				if (!ImageConversion.LoadImage(val, array, false))
				{
					Object.Destroy((Object)(object)val);
					val = null;
					TextureCache[fileName] = null;
					return null;
				}
				if (IsDecodedTextureTooLarge(val))
				{
					Plugin.Log.LogWarning((object)("RadiationPlayback[Texture] action=decoded-texture-too-large file=" + fileName + " " + $"width={((Texture)val).width} height={((Texture)val).height} pixels={(long)((Texture)val).width * (long)((Texture)val).height} maxPixels={16777216L}"));
					Object.Destroy((Object)(object)val);
					val = null;
					TextureCache[fileName] = null;
					return null;
				}
				((Object)val).name = Path.GetFileNameWithoutExtension(fileName);
				((Texture)val).wrapMode = (TextureWrapMode)1;
				TextureCache[fileName] = val;
				Texture2D result = val;
				val = null;
				return result;
			}
			catch (Exception ex)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
				Plugin.Log.LogWarning((object)("RadiationPlayback[Texture] action=load-failed file=" + fileName + " error=" + ex.GetType().Name + ": " + ex.Message));
				TextureCache[fileName] = null;
				return null;
			}
		}

		private static void ClearCachedAssets()
		{
			HashSet<int> hashSet = new HashSet<int>();
			foreach (Sprite value in SpriteCache.Values)
			{
				if (!((Object)(object)value == (Object)null) && hashSet.Add(((Object)value).GetInstanceID()))
				{
					Object.Destroy((Object)(object)value);
				}
			}
			HashSet<int> destroyedTextureIds = new HashSet<int>();
			foreach (Texture2D value2 in TextureCache.Values)
			{
				DestroyTextureOnce(value2, destroyedTextureIds);
			}
			foreach (Texture2D value3 in ResolvedFrameTextureCache.Values)
			{
				DestroyTextureOnce(value3, destroyedTextureIds);
			}
			SpriteCache.Clear();
			TextureCache.Clear();
			ResolvedFrameTextureCache.Clear();
		}

		private static bool IsDecodedTextureTooLarge(Texture2D texture)
		{
			if (((Texture)texture).width <= 0 || ((Texture)texture).height <= 0)
			{
				return true;
			}
			return (long)((Texture)texture).width * (long)((Texture)texture).height > 16777216;
		}

		private static void DestroyTextureOnce(Texture2D? texture, HashSet<int> destroyedTextureIds)
		{
			if (!((Object)(object)texture == (Object)null) && destroyedTextureIds.Add(((Object)texture).GetInstanceID()))
			{
				Object.Destroy((Object)(object)texture);
			}
		}

		private static string NormalizeSpriteName(string? spriteName)
		{
			if (string.IsNullOrWhiteSpace(spriteName))
			{
				return string.Empty;
			}
			string text = spriteName.Trim();
			if (text.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				text = text.Substring(0, text.Length - "(Clone)".Length).TrimEnd();
			}
			return text;
		}

		private static string BuildPath(Transform? transform)
		{
			if ((Object)(object)transform == (Object)null)
			{
				return "<null>";
			}
			string text = ((Object)transform).name;
			Transform parent = transform.parent;
			while ((Object)(object)parent != (Object)null)
			{
				text = ((Object)parent).name + "/" + text;
				parent = parent.parent;
			}
			return text;
		}
	}
	internal static class StickyNoteLocalizationService
	{
		private sealed class RendererState
		{
			public MeshRenderer Renderer { get; }

			public Material[] OriginalMaterials { get; }

			public List<Material> LocalizedMaterials { get; }

			public RendererState(MeshRenderer renderer, Material[] originalMaterials, List<Material> localizedMaterials)
			{
				Renderer = renderer;
				OriginalMaterials = originalMaterials;
				LocalizedMaterials = localizedMaterials;
			}
		}

		private const string ItemName = "StickyNoteItem";

		private const string MaterialName = "StickyNoteMaterial";

		private const string TextureFileName = "StickyNoteLocalized.png";

		private const int MaxTextureFileBytes = 8388608;

		private const int ExpectedTextureWidth = 1024;

		private const int ExpectedTextureHeight = 512;

		private static readonly Dictionary<int, RendererState> RendererStates = new Dictionary<int, RendererState>();

		private static string _texturePath = string.Empty;

		private static Texture2D? _localizedTexture;

		private static bool _initialized;

		private static bool _loadAttempted;

		public static void Initialize(string pluginDir)
		{
			_texturePath = Path.Combine(pluginDir, "textures", "StickyNoteLocalized.png");
			_initialized = true;
			_loadAttempted = false;
		}

		public static void Apply(GrabbableObject? grabbableObject)
		{
			if (_initialized && !Plugin.IsRuntimeShuttingDown && !((Object)(object)grabbableObject == (Object)null) && IsStickyNote(((Object)((Component)grabbableObject).gameObject).name) && EnsureTextureLoaded())
			{
				PruneDestroyedRendererStates();
				MeshRenderer[] componentsInChildren = ((Component)grabbableObject).GetComponentsInChildren<MeshRenderer>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					ApplyToRenderer(componentsInChildren[i]);
				}
			}
		}

		public static void Shutdown()
		{
			foreach (RendererState value in RendererStates.Values)
			{
				try
				{
					if ((Object)(object)value.Renderer != (Object)null)
					{
						((Renderer)value.Renderer).sharedMaterials = value.OriginalMaterials;
					}
				}
				catch
				{
				}
				foreach (Material localizedMaterial in value.LocalizedMaterials)
				{
					DestroyUnityObject((Object?)(object)localizedMaterial);
				}
			}
			RendererStates.Clear();
			DestroyUnityObject((Object?)(object)_localizedTexture);
			_localizedTexture = null;
			_texturePath = string.Empty;
			_initialized = false;
			_loadAttempted = false;
		}

		private static bool IsStickyNote(string? objectName)
		{
			if (!string.IsNullOrEmpty(objectName))
			{
				return objectName.StartsWith("StickyNoteItem", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static bool EnsureTextureLoaded()
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			if (_loadAttempted)
			{
				return (Object)(object)_localizedTexture != (Object)null;
			}
			_loadAttempted = true;
			Texture2D val = null;
			try
			{
				FileInfo fileInfo = new FileInfo(_texturePath);
				if (!fileInfo.Exists || fileInfo.Length <= 0 || fileInfo.Length > 8388608)
				{
					Plugin.Log.LogWarning((object)"Sticky-note localization texture is missing or invalid: StickyNoteLocalized.png");
					return false;
				}
				val = new Texture2D(2, 2, (TextureFormat)4, false);
				if (!ImageConversion.LoadImage(val, File.ReadAllBytes(_texturePath), true) || ((Texture)val).width != 1024 || ((Texture)val).height != 512)
				{
					Plugin.Log.LogWarning((object)($"Sticky-note localization texture must be {1024}x{512}: " + $"{((Texture)val).width}x{((Texture)val).height}"));
					DestroyUnityObject((Object?)(object)val);
					return false;
				}
				((Object)val).name = "StickyNoteTex (zh-CN)";
				((Texture)val).wrapMode = (TextureWrapMode)1;
				((Texture)val).filterMode = (FilterMode)1;
				_localizedTexture = val;
				return true;
			}
			catch (Exception ex)
			{
				DestroyUnityObject((Object?)(object)val);
				Plugin.Log.LogWarning((object)("Sticky-note localization texture load failed: " + ex.GetType().Name + ": " + ex.Message));
				return false;
			}
		}

		private static void ApplyToRenderer(MeshRenderer? renderer)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			if ((Object)(object)renderer == (Object)null || RendererStates.ContainsKey(((Object)renderer).GetInstanceID()) || (Object)(object)_localizedTexture == (Object)null)
			{
				return;
			}
			Material[] sharedMaterials = ((Renderer)renderer).sharedMaterials;
			if (sharedMaterials == null || sharedMaterials.Length == 0)
			{
				return;
			}
			Material[] array = (Material[])sharedMaterials.Clone();
			List<Material> list = new List<Material>();
			for (int i = 0; i < sharedMaterials.Length; i++)
			{
				Material val = sharedMaterials[i];
				if (!((Object)(object)val == (Object)null) && string.Equals(NormalizeMaterialName(((Object)val).name), "StickyNoteMaterial", StringComparison.OrdinalIgnoreCase))
				{
					Material val2 = new Material(val)
					{
						name = "StickyNoteMaterial (zh-CN)"
					};
					SetTexture(val2, _localizedTexture);
					array[i] = val2;
					list.Add(val2);
				}
			}
			if (list.Count != 0)
			{
				((Renderer)renderer).sharedMaterials = array;
				RendererStates[((Object)renderer).GetInstanceID()] = new RendererState(renderer, sharedMaterials, list);
			}
		}

		private static void PruneDestroyedRendererStates()
		{
			List<int> list = null;
			foreach (KeyValuePair<int, RendererState> rendererState in RendererStates)
			{
				if ((Object)(object)rendererState.Value.Renderer != (Object)null)
				{
					continue;
				}
				if (list == null)
				{
					list = new List<int>();
				}
				list.Add(rendererState.Key);
				foreach (Material localizedMaterial in rendererState.Value.LocalizedMaterials)
				{
					DestroyUnityObject((Object?)(object)localizedMaterial);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (int item in list)
			{
				RendererStates.Remove(item);
			}
		}

		private static string NormalizeMaterialName(string? materialName)
		{
			if (string.IsNullOrWhiteSpace(materialName))
			{
				return string.Empty;
			}
			string text = materialName.Trim();
			string[] array = new string[2] { " (Instance)", " (zh-CN)" };
			foreach (string text2 in array)
			{
				if (text.EndsWith(text2, StringComparison.OrdinalIgnoreCase))
				{
					text = text.Substring(0, text.Length - text2.Length);
				}
			}
			return text;
		}

		private static void SetTexture(Material material, Texture2D texture)
		{
			if (material.HasProperty("_BaseColorMap"))
			{
				material.SetTexture("_BaseColorMap", (Texture)(object)texture);
			}
			if (material.HasProperty("_MainTex"))
			{
				material.SetTexture("_MainTex", (Texture)(object)texture);
			}
			material.mainTexture = (Texture)(object)texture;
		}

		private static void DestroyUnityObject(Object? value)
		{
			if (!(value == (Object)null))
			{
				if (Application.isPlaying)
				{
					Object.Destroy(value);
				}
				else
				{
					Object.DestroyImmediate(value);
				}
			}
		}
	}
	internal static class ExternalEnglishCompatibilityService
	{
		private const int MaxSourceLength = 512;

		private const int RuntimeCacheLimit = 4096;

		private const string DiscountAlertNoDiscountLocalizedText = "暂无折扣\n明天再来查看";

		private static readonly Dictionary<string, string> ExactEntries = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			["Emote Menu"] = "动作菜单",
			["Random Emote"] = "随机动作",
			["Perform Random Emote"] = "执行随机动作",
			["Zoom"] = "缩放",
			["Freeze"] = "冻结",
			["Swap Page"] = "切换页面",
			["Favorite Emote"] = "收藏动作",
			["Set Quick Emote"] = "设置快捷动作",
			["Mute emote audio"] = "静音动作音频",
			["Only perform emote"] = "仅播放动作",
			["no audio"] = "无音频",
			["(no audio)"] = "(无音频)",
			["First person emotes"] = "第一人称动作",
			["Move while emoting"] = "做动作时可移动",
			["experimental"] = "实验性",
			["(experimental)"] = "(实验性)",
			["DMCA-free mode"] = "DMCA 安全模式",
			["Emote volume"] = "动作音量",
			["Tell autopilot ship to leave early"] = "命令自动驾驶飞船提前离开",
			["Spectate Previous Player"] = "切换上一名玩家",
			["Open Admin UI"] = "打开管理界面",
			["Copy Lobby ID"] = "复制房间 ID",
			["Enter tag or id..."] = "输入标签或 ID...",
			["Enter tag or ip..."] = "输入标签或 IP...",
			["Search Mods"] = "搜索模组",
			["Search Configs"] = "搜索配置",
			["Close"] = "关闭",
			["Apply"] = "应用",
			["Delete"] = "删除",
			["Go back"] = "返回",
			["OK"] = "确认",
			["Server Password"] = "房间密码",
			["Validate Steam Sessions"] = "验证 Steam 会话",
			["Max Players"] = "最大人数",
			["Server Access"] = "房间权限",
			["Invite-only"] = "仅限邀请",
			["Friends-only"] = "仅限好友",
			["Server Tag"] = "房间标签",
			["New File"] = "新存档",
			["Players"] = "玩家",
			["Today's discounts"] = "今日折扣",
			["AVAILABLE NOW!"] = "现已发售!",
			["CURES CANCER!"] = "治愈癌症!",
			["NO WAY!"] = "不会吧!",
			["LIMITED TIME ONLY!"] = "限时供应!",
			["GET YOURS TODAY!"] = "今天就来领取!",
			["Cannot return from storage while the ship is landing or leaving."] = "飞船正在降落或离开时,无法从仓储取回物品。",
			["Infection"] = "感染",
			["HUDScale"] = "HUD 缩放",
			["HideHealthbarAutomatically"] = "自动隐藏生命条",
			["HealthbarHideDelay"] = "生命条隐藏延迟",
			["FlashlightBattery"] = "手电筒电量显示",
			["DetailedStamina"] = "详细耐力",
			["DisplayTimeLeft"] = "显示剩余时间",
			["HidePlanetInfo"] = "隐藏星球信息",
			["PercentageOnly"] = "仅百分比",
			["The size of the HUD."] = "HUD 界面的缩放大小。",
			["Should the healthbar be hidden after not taking damage for a while."] = "一段时间未受伤后是否自动隐藏生命条。",
			["The amount of time before the healthbar starts fading away."] = "生命条开始淡出前的等待时间。",
			["How the flashlight battery is displayed whilst unequipped."] = "未装备手电筒时如何显示其电量。",
			["Disabled - Flashlight battery will not be displayed."] = "禁用 - 不显示手电筒电量。",
			["Vanilla - Flashlight battery will be displayed when you don't have a battery-using item equipped."] = "原版 - 未装备其他耗电物品时显示手电筒电量。",
			["Separate - Flashlight battery will be displayed using a dedicated panel. (recommended)"] = "独立 - 使用独立面板显示手电筒电量。(推荐)",
			["What the stamina text should display."] = "耐力文字要显示的内容。",
			["Disabled - The stamina text will be hidden."] = "禁用 - 隐藏耐力文字。",
			["PercentageOnly - Only the percentage will be displayed. (recommended)"] = "仅百分比 - 只显示百分比。(推荐)",
			["Full - Both percentage and rate of gain/loss will be displayed."] = "完整 - 同时显示百分比与增减速率。",
			["Should the uses/time left for a battery-using item be displayed."] = "是否显示耗电物品的剩余使用次数或时间。",
			["Should planet info be hidden. If modifying from an in-game menu, this requires you to rejoin the game."] = "是否隐藏星球信息。若在游戏内菜单修改,需要重新加入游戏。",
			["Target on ship"] = "飞船上的目标",
			["Signal lost"] = "信号丢失",
			["Antenna stored"] = "天线已存放",
			["PERFORMANCE REPORT"] = "绩效报告",
			["NO SURVIVORS"] = "无人生还",
			["NOTES"] = "备注",
			["DECEASED"] = "死亡",
			["MISSING"] = "失踪",
			["Collected"] = "已收集",
			["Grade"] = "评级",
			["Rating"] = "评级",
			["Lost 100% scrap"] = "损失 100% 废料",
			["* Unknown"] = "* 未知原因",
			["* Bludgeoning"] = "* 钝击",
			["* Gravity"] = "* 坠落",
			["* Blast"] = "* 爆炸",
			["* Strangulation"] = "* 勒杀",
			["* Suffocation"] = "* 窒息",
			["* Mauling"] = "* 撕咬",
			["* Gunshots"] = "* 枪击",
			["* Crushing"] = "* 碾压",
			["* Drowning"] = "* 溺水",
			["* Abandoned"] = "* 遭遗弃",
			["* Electrocution"] = "* 触电",
			["* Kicking"] = "* 踢击",
			["* Burning"] = "* 燃烧",
			["* Stabbing"] = "* 刺伤",
			["* Fan"] = "* 风扇",
			["* Inertia"] = "* 惯性撞击",
			["* Snipping"] = "* 剪伤",
			["* Scratching"] = "* 抓伤",
			["* The laziest employee."] = "* 最懒惰的员工",
			["The laziest employee."] = "最懒惰的员工",
			["* Most profitable"] = "* 最会赚钱的员工",
			["Most profitable"] = "最会赚钱的员工",
			["No items were found"] = "未找到可售物品",
			["Error selling items"] = "售卖物品时出错",
			["You can't afford to sell that amount"] = "可售物品不足,无法卖出该金额",
			["Successfully emptied temporary blacklist"] = "已清空临时黑名单",
			["Successfully emptied temporary priority set"] = "已清空临时优先列表",
			["Item Blacklist"] = "物品黑名单",
			["Priority Items"] = "优先售卖物品",
			["Flag Prefix"] = "标志前缀",
			["Items to never sell by internal name (comma-separated)"] = "永不出售的物品内部名称(逗号分隔)",
			["Items which are prioritized when selling"] = "出售时优先选择的物品",
			["QUICKSELL"] = "快速出售",
			["SELL RESULTS"] = "出售结果",
			["Command"] = "命令",
			["Invalid command"] = "命令无效",
			["HELP PAGE"] = "帮助页面",
			["ALL HELP PAGE"] = "全部出售帮助页面",
			["AMOUNT HELP PAGE"] = "金额帮助页面",
			["BLACKLIST HELP PAGE"] = "黑名单帮助页面",
			["ITEM HELP PAGE"] = "物品帮助页面",
			["PRIORITY HELP PAGE"] = "优先列表帮助页面",
			["QUOTA HELP PAGE"] = "配额帮助页面",
			["FLAG HELP PAGE"] = "标志帮助页面",
			["OVERTIME HELP PAGE"] = "加班奖励帮助页面",
			["PAGES HELP PAGE"] = "页面帮助",
			["-A HELP PAGE"] = "-A 标志帮助",
			["-E HELP PAGE"] = "-E 标志帮助",
			["-N HELP PAGE"] = "-N 标志帮助",
			["-O HELP PAGE"] = "-O 标志帮助",
			["-P HELP PAGE"] = "-P 标志帮助",
			["Combining flags"] = "组合标志",
			["\"help\" to open this page or a specific help page"] = "\"help\":打开此页或指定帮助页",
			["\"quota\" to sell exactly for quota"] = "\"quota\":按配额精确出售",
			["\"all\" to sell all unfiltered scrap available"] = "\"all\":出售所有未过滤的可用废料",
			["\"item\" to sell all items like the one you are holding or the one you specified"] = "\"item\":出售所持有或指定的同类物品",
			["\"-a\" to ignore blacklist (used with quota, all, <amount>)"] = "\"-a\":忽略黑名单(用于 quota、all、<amount>)",
			["\"-o\" to sell accounting for overtime (used with <amount>)"] = "\"-o\":计入加班奖励后出售(用于 <amount>)",
			["The symbol which is used as prefix in flags (aka \"-\" in \"-e\")"] = "命令标志使用的前缀符号(例如 -e 中的 -)",
			["Command variations"] = "命令用法",
			["The sell command was initiated"] = "售卖命令已开始",
			["The sell command completed it's job, terminating"] = "售卖命令已完成,正在结束",
			["The overtime command was initiated"] = "加班奖励命令已开始",
			["Use \"/sell help flags\" to see info on important flags"] = "输入 \"/sell help flags\" 查看重要标志说明",
			["Use \"/sell help <flag>\" to see info on specific flag"] = "输入 \"/sell help <flag>\" 查看指定标志说明",
			["Use \"/sell help <variation>\" to see info on specific command"] = "输入 \"/sell help <variation>\" 查看指定命令说明",
			["Use \"/sell help pages\" to see info on all pages"] = "输入 \"/sell help pages\" 查看所有帮助页面",
			["Use \"/sell help overtime\" to see info on the \"/ot\" (overtime command)"] = "输入 \"/sell help overtime\" 查看 \"/ot\"(加班奖励命令)说明",
			["The value must be positive"] = "数值必须为正",
			["Cannot find terminal!"] = "未找到终端!",
			["Cannot find terminal?!"] = "未找到终端!",
			["Quota is already fulfilled"] = "利润指标已完成",
			["No item is held and no item was specified"] = "未持有物品,也未指定物品",
			["No items on the desk"] = "柜台上没有物品",
			["Door already open"] = "门已经打开",
			["Wrong item name"] = "物品名称错误",
			["No page with this name exists"] = "不存在这个帮助页面",
			["Failed to evalute expression"] = "表达式计算失败",
			["No items to sort"] = "没有可整理的物品",
			["No items on the ship"] = "飞船上没有物品",
			["Sorting all items..."] = "正在整理所有物品...",
			["Finished sorting items"] = "物品整理完成",
			["Invalid arguments"] = "参数无效",
			["Invalid item name"] = "物品名称无效",
			["Error running command"] = "运行命令时出错",
			["Error getting position"] = "获取位置时出错",
			["Automatic sorting failed"] = "自动整理失败",
			["Error while autosorting items"] = "自动整理物品时出错",
			["Automatic sorting failed due to an internal error, check the log for details"] = "自动整理因内部错误失败,请查看日志",
			["Couldn't find ship"] = "未找到飞船",
			["Raycast unsuccessful"] = "射线检测未成功",
			["The ship must be in orbit"] = "飞船必须处于轨道中",
			["You can't pick anything up while sorting items."] = "整理物品时无法拾取任何物品。",
			["Toggles automatic item sorting when leaving a planet"] = "切换离开星球时是否自动整理物品",
			["* The most paranoid employee."] = "* 最多疑的员工",
			["The most paranoid employee."] = "最多疑的员工",
			["* Sustained the most injuries."] = "* 受伤最多",
			["Sustained the most injuries."] = "受伤最多",
			["* Dislikes smoke."] = "* 讨厌烟雾",
			["Dislikes smoke."] = "讨厌烟雾",
			["* The least likely to die next time."] = "* 下次最不可能死亡",
			["The least likely to die next time."] = "下次最不可能死亡",
			["* I think this one's a serial killer."] = "* 我觉得这人像个连环杀手",
			["I think this one's a serial killer."] = "我觉得这人像个连环杀手",
			["* Go! Freaky on a Friday night."] = "* 周五夜晚尽情疯狂",
			["Go! Freaky on a Friday night."] = "周五夜晚尽情疯狂"
		};

		private static readonly Dictionary<string, string> KeyTokenEntries = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			["Scroll Mouse"] = "滚动鼠标",
			["k