Decompiled source of LC Chinese Project v3.2.9

BepInEx/plugins/V81TestChn/V81TestChn.dll

Decompiled a week 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.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
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.Rendering;
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+d32fd5429c8f37de0d7070c097765cf20cabdf1a")]
[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.LogRuntimeLocalizationEvent("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.LogRuntimeLocalizationEvent("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.LogRuntimeLocalizationEvent("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.LogRuntimeLocalizationEvent($"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.LogRuntimeLocalizationEvent("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 PrecompressedBc1 { get; }

			public bool PrecompressedBc3 { get; }

			public bool PrecompressedBc7 { get; }

			public bool MipChain { get; }

			public TextureSpec(string originalName, string fileName, int width, int height, bool precompressedBc1 = false, bool precompressedBc3 = false, bool precompressedBc7 = false, bool mipChain = true)
			{
				OriginalName = originalName;
				FileName = fileName;
				Width = width;
				Height = height;
				PrecompressedBc1 = precompressedBc1;
				PrecompressedBc3 = precompressedBc3;
				PrecompressedBc7 = precompressedBc7;
				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 sealed class FireExitState
		{
			public Renderer Renderer { get; }

			public Material[] OriginalMaterials { get; }

			public GameObject Overlay { get; }

			public FireExitState(Renderer renderer, Material[] originalMaterials, GameObject overlay)
			{
				Renderer = renderer;
				OriginalMaterials = originalMaterials;
				Overlay = overlay;
			}
		}

		private const int MaxTextureFileBytes = 16777216;

		private const string FireExitLabelFileName = "FireExitDoorLabelLocalized.png";

		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 HashSet<int> UnchangedMaterialIdsThisPass = 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),
			["ToiletPaperTex"] = new TextureSpec("ToiletPaperTex", "ToiletPaperTexLocalized.png", 1024, 1024),
			["CashRegisterTex"] = new TextureSpec("CashRegisterTex", "CashRegisterTexLocalized.png", 1024, 1024),
			["FlashbangBottleTexture"] = new TextureSpec("FlashbangBottleTexture", "FlashbangBottleTextureLocalized.png", 1024, 1024),
			["WelcomeMatTex"] = new TextureSpec("WelcomeMatTex", "WelcomeMatTexLocalized.png", 1024, 1024),
			["AirhornTex"] = new TextureSpec("AirhornTex", "AirhornTexLocalized.png", 512, 512),
			["TetraChemicalTex"] = new TextureSpec("TetraChemicalTex", "TetraChemicalTexLocalized.png", 2048, 2048),
			["SprayCanTex"] = new TextureSpec("SprayCanTex", "SprayCanTexLocalized.png", 1024, 1024),
			["StopSignTex"] = new TextureSpec("StopSignTex", "StopSignTexLocalized.png", 1024, 1024),
			["YieldSignTex"] = new TextureSpec("YieldSignTex", "YieldSignTexLocalized.png", 512, 512),
			["SodaCanTex1"] = new TextureSpec("SodaCanTex1", "SodaCanTex1Localized.png", 1024, 1024),
			["ToothpasteTex"] = new TextureSpec("ToothpasteTex", "ToothpasteTexLocalized.png", 1024, 1024),
			["WeedKillerBottleTex"] = new TextureSpec("WeedKillerBottleTex", "WeedKillerBottleTexLocalized.png", 1024, 1024),
			["WhoopieCushionTex"] = new TextureSpec("WhoopieCushionTex", "WhoopieCushionTexLocalized.png", 1024, 1024),
			["YellowMineDoorTex"] = new TextureSpec("YellowMineDoorTex", "YellowMineDoorTexLocalized.png", 1024, 1024),
			["ChemicalBottle1"] = new TextureSpec("ChemicalBottle1", "ChemicalBottle1Localized.png", 1024, 1024),
			["powerBoxTextures"] = new TextureSpec("powerBoxTextures", "powerBoxTexturesLocalized.png", 1024, 820),
			["PlayerLevelStickers"] = new TextureSpec("PlayerLevelStickers", "PlayerLevelStickersLocalized.png", 256, 512),
			["PlayerLevelStickers 1"] = new TextureSpec("PlayerLevelStickers 1", "PlayerVipEmployeeStickerLocalized.png", 512, 128),
			["CompanyCruiserCombined4Diffuse 1"] = new TextureSpec("CompanyCruiserCombined4Diffuse 1", "CompanyCruiserDestroyedDiffuseLocalized.dxt1", 4096, 4096, precompressedBc1: true),
			["CompanyCruiserCombined4DiffuseDirtyV2"] = new TextureSpec("CompanyCruiserCombined4DiffuseDirtyV2", "CompanyCruiserIntactDiffuseLocalized.bc7", 4096, 4096, precompressedBc1: false, precompressedBc3: false, precompressedBc7: 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 readonly Dictionary<int, FireExitState> FireExitStates = new Dictionary<int, FireExitState>();

		private static Texture2D? _fireExitLabelTexture;

		private static Material? _fireExitLabelMaterial;

		private static Material? _fireExitHiddenMaterial;

		private static Mesh? _fireExitOverlayMesh;

		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 ApplyLevelEnvironment(RoundManager? roundManager)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if (!CanApply() || (Object)(object)roundManager == (Object)null)
			{
				return;
			}
			PruneDestroyedRendererStates();
			PruneDestroyedFireExitStates();
			PruneUnusedMaterialStates();
			UnchangedMaterialIdsThisPass.Clear();
			try
			{
				for (int i = 0; i < SceneManager.sceneCount; i++)
				{
					Scene sceneAt = SceneManager.GetSceneAt(i);
					if (!((Scene)(ref sceneAt)).IsValid() || !((Scene)(ref sceneAt)).isLoaded)
					{
						continue;
					}
					GameObject[] rootGameObjects = ((Scene)(ref sceneAt)).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, cacheUnchangedMaterials: true);
							TryApplyFireExitDoor(item);
						}
					}
				}
			}
			finally
			{
				RendererScanBuffer.Clear();
				UnchangedMaterialIdsThisPass.Clear();
			}
		}

		public static void ApplyGrabbableObject(GrabbableObject? grabbableObject)
		{
			if (!CanApply() || (Object)(object)grabbableObject == (Object)null || !IsLocalizedScrap(((Object)((Component)grabbableObject).gameObject).name))
			{
				return;
			}
			PruneDestroyedRendererStates();
			PruneUnusedMaterialStates();
			RendererScanBuffer.Clear();
			((Component)grabbableObject).GetComponentsInChildren<Renderer>(true, RendererScanBuffer);
			foreach (Renderer item in RendererScanBuffer)
			{
				ApplyToRenderer(item);
			}
			RendererScanBuffer.Clear();
		}

		public static void ApplyShipDecoration(AutoParentToShip? shipDecoration)
		{
			if (!CanApply() || (Object)(object)shipDecoration == (Object)null || !IsLocalizedShipDecoration(((Object)((Component)shipDecoration).gameObject).name))
			{
				return;
			}
			PruneDestroyedRendererStates();
			PruneUnusedMaterialStates();
			RendererScanBuffer.Clear();
			((Component)shipDecoration).GetComponentsInChildren<Renderer>(true, RendererScanBuffer);
			foreach (Renderer item in RendererScanBuffer)
			{
				ApplyToRenderer(item);
			}
			RendererScanBuffer.Clear();
		}

		public static void ApplyPlayerBadges(PlayerControllerB? playerController)
		{
			if (CanApply() && !((Object)(object)playerController == (Object)null))
			{
				PruneDestroyedRendererStates();
				PruneUnusedMaterialStates();
				ApplyToRenderer(((Object)(object)playerController.playerBadgeMesh != (Object)null) ? ((Component)playerController.playerBadgeMesh).GetComponent<Renderer>() : null);
				ApplyToRenderer((Renderer?)(object)playerController.playerBetaBadgeMesh);
			}
		}

		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 (FireExitState value2 in FireExitStates.Values)
			{
				try
				{
					if ((Object)(object)value2.Renderer != (Object)null)
					{
						value2.Renderer.sharedMaterials = value2.OriginalMaterials;
					}
				}
				catch
				{
				}
				DestroyUnityObject((Object?)(object)value2.Overlay);
			}
			FireExitStates.Clear();
			foreach (MaterialState value3 in MaterialStates.Values)
			{
				DestroyUnityObject((Object?)(object)value3.Localized);
			}
			MaterialStates.Clear();
			RendererScanBuffer.Clear();
			ActiveLocalizedMaterialIds.Clear();
			UnchangedMaterialIdsThisPass.Clear();
			foreach (Texture2D value4 in LocalizedTextures.Values)
			{
				DestroyUnityObject((Object?)(object)value4);
			}
			LocalizedTextures.Clear();
			FailedTextures.Clear();
			DestroyUnityObject((Object?)(object)_fireExitLabelMaterial);
			DestroyUnityObject((Object?)(object)_fireExitHiddenMaterial);
			DestroyUnityObject((Object?)(object)_fireExitOverlayMesh);
			DestroyUnityObject((Object?)(object)_fireExitLabelTexture);
			_fireExitLabelMaterial = null;
			_fireExitHiddenMaterial = null;
			_fireExitOverlayMesh = null;
			_fireExitLabelTexture = null;
			_textureDirectory = string.Empty;
			_initialized = false;
		}

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

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

		private static bool IsLocalizedScrap(string? objectName)
		{
			if (string.IsNullOrWhiteSpace(objectName))
			{
				return false;
			}
			if (!objectName.StartsWith("ToiletPaperRolls", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("CashRegisterItem", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("DiyFlashbang", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("Airhorn", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("TZPChemical", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("SprayPaint", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("StopSign", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("YieldSign", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("RedSodaCan", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("Toothpaste", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("WeedKillerItem", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("WhoopieCushion", StringComparison.OrdinalIgnoreCase))
			{
				return objectName.StartsWith("ChemicalJug", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static bool IsLocalizedShipDecoration(string? objectName)
		{
			if (!string.IsNullOrWhiteSpace(objectName))
			{
				return objectName.StartsWith("WelcomeMatContainer", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static void TryApplyFireExitDoor(Renderer? renderer)
		{
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			if ((Object)(object)renderer == (Object)null || FireExitStates.ContainsKey(((Object)renderer).GetInstanceID()) || !string.Equals(((Object)((Component)renderer).gameObject).name, "Cube.001", StringComparison.Ordinal) || (Object)(object)((Component)renderer).transform.parent == (Object)null || !((Object)((Component)((Component)renderer).transform.parent).gameObject).name.StartsWith("FireExitDoor", StringComparison.OrdinalIgnoreCase))
			{
				return;
			}
			MeshFilter component = ((Component)renderer).GetComponent<MeshFilter>();
			Mesh val = ((component != null) ? component.sharedMesh : null);
			Material[] sharedMaterials = renderer.sharedMaterials;
			if ((Object)(object)val == (Object)null || !string.Equals(((Object)val).name, "Cube.001", StringComparison.Ordinal) || val.subMeshCount != 3 || sharedMaterials == null || sharedMaterials.Length != 3)
			{
				return;
			}
			Material val2 = EnsureFireExitHiddenMaterial();
			Material val3 = EnsureFireExitLabelMaterial();
			Mesh val4 = EnsureFireExitOverlayMesh();
			if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null)
			{
				return;
			}
			GameObject val5 = null;
			try
			{
				Material[] array = (Material[])sharedMaterials.Clone();
				array[2] = val2;
				renderer.sharedMaterials = array;
				val5 = new GameObject("FireExitDoorLabel (zh-CN)")
				{
					layer = ((Component)renderer).gameObject.layer
				};
				val5.transform.SetParent(((Component)renderer).transform, false);
				val5.AddComponent<MeshFilter>().sharedMesh = val4;
				MeshRenderer obj = val5.AddComponent<MeshRenderer>();
				((Renderer)obj).sharedMaterial = val3;
				((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0;
				((Renderer)obj).receiveShadows = false;
				((Renderer)obj).lightProbeUsage = (LightProbeUsage)0;
				((Renderer)obj).reflectionProbeUsage = (ReflectionProbeUsage)0;
				FireExitStates[((Object)renderer).GetInstanceID()] = new FireExitState(renderer, sharedMaterials, val5);
			}
			catch (Exception ex)
			{
				renderer.sharedMaterials = sharedMaterials;
				DestroyUnityObject((Object?)(object)val5);
				Plugin.Log.LogWarning((object)("Fire-exit localization failed: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static Texture2D? EnsureFireExitLabelTexture()
		{
			if ((Object)(object)_fireExitLabelTexture != (Object)null)
			{
				return _fireExitLabelTexture;
			}
			string text = Path.Combine(_textureDirectory, "FireExitDoorLabelLocalized.png");
			try
			{
				FileInfo fileInfo = new FileInfo(text);
				if (!fileInfo.Exists || fileInfo.Length <= 0 || fileInfo.Length > 1048576)
				{
					throw new InvalidDataException("missing or invalid fire-exit label texture");
				}
				Texture2D val = LoadTextureData(File.ReadAllBytes(text), useRuntimeCompression: true) ?? LoadTextureData(File.ReadAllBytes(text), useRuntimeCompression: false);
				if ((Object)(object)val == (Object)null || ((Texture)val).width != 512 || ((Texture)val).height != 128)
				{
					DestroyUnityObject((Object?)(object)val);
					throw new InvalidDataException("fire-exit label texture must be 512x128");
				}
				((Object)val).name = "FireExitDoorLabel (zh-CN)";
				((Texture)val).wrapMode = (TextureWrapMode)1;
				((Texture)val).filterMode = (FilterMode)1;
				_fireExitLabelTexture = val;
				return val;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Fire-exit label load failed: " + ex.GetType().Name + ": " + ex.Message));
				return null;
			}
		}

		private static Shader? FindTransparentShader()
		{
			return Shader.Find("Unlit/Transparent") ?? Shader.Find("Sprites/Default");
		}

		private static Material? EnsureFireExitLabelMaterial()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			if ((Object)(object)_fireExitLabelMaterial != (Object)null)
			{
				return _fireExitLabelMaterial;
			}
			Shader val = FindTransparentShader();
			Texture2D val2 = EnsureFireExitLabelTexture();
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				return null;
			}
			_fireExitLabelMaterial = new Material(val)
			{
				name = "FireExitDoorLabelMaterial (zh-CN)",
				mainTexture = (Texture)(object)val2,
				color = Color.white,
				renderQueue = 3000
			};
			return _fireExitLabelMaterial;
		}

		private static Material? EnsureFireExitHiddenMaterial()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			if ((Object)(object)_fireExitHiddenMaterial != (Object)null)
			{
				return _fireExitHiddenMaterial;
			}
			Shader val = FindTransparentShader();
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			_fireExitHiddenMaterial = new Material(val)
			{
				name = "FireExitDoorOriginalTextHidden (zh-CN)",
				color = Color.clear,
				renderQueue = 3000
			};
			return _fireExitHiddenMaterial;
		}

		private static Mesh EnsureFireExitOverlayMesh()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: 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)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_fireExitOverlayMesh != (Object)null)
			{
				return _fireExitOverlayMesh;
			}
			Mesh val = new Mesh
			{
				name = "FireExitDoorLabelQuad (zh-CN)"
			};
			val.vertices = (Vector3[])(object)new Vector3[4]
			{
				new Vector3(1.08f, -34f, -43.6f),
				new Vector3(1.08f, 34f, -43.6f),
				new Vector3(1.08f, 34f, -27.8f),
				new Vector3(1.08f, -34f, -27.8f)
			};
			val.uv = (Vector2[])(object)new Vector2[4]
			{
				new Vector2(1f, 1f),
				new Vector2(0f, 1f),
				new Vector2(0f, 0f),
				new Vector2(1f, 0f)
			};
			val.triangles = new int[6] { 0, 1, 2, 0, 2, 3 };
			val.RecalculateBounds();
			_fireExitOverlayMesh = val;
			return val;
		}

		private static void ApplyToRenderer(Renderer? renderer, bool cacheUnchangedMaterials = false)
		{
			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, cacheUnchangedMaterials);
				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, bool cacheUnchangedMaterials)
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Expected O, but got Unknown
			if ((Object)(object)original == (Object)null)
			{
				return null;
			}
			int instanceID = ((Object)original).GetInstanceID();
			if (cacheUnchangedMaterials && UnchangedMaterialIdsThisPass.Contains(instanceID))
			{
				return original;
			}
			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);
				if (cacheUnchangedMaterials)
				{
					UnchangedMaterialIdsThisPass.Add(instanceID);
				}
				return original;
			}
			UnchangedMaterialIdsThisPass.Remove(instanceID);
			((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_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: 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.PrecompressedBc1 && !spec.PrecompressedBc3 && !spec.PrecompressedBc7 && fileInfo.Length > 16777216))
				{
					throw new InvalidDataException($"invalid file size ({fileInfo.Length} bytes)");
				}
				if (spec.PrecompressedBc1)
				{
					val = PrecompressedTextureLoader.LoadBc1(text, spec.Width, spec.Height, spec.MipChain, out string error);
					if ((Object)(object)val == (Object)null)
					{
						throw new InvalidDataException(error ?? "precompressed BC1 load failed");
					}
				}
				else if (spec.PrecompressedBc3)
				{
					val = PrecompressedTextureLoader.LoadBc3(text, spec.Width, spec.Height, spec.MipChain, out string error2);
					if ((Object)(object)val == (Object)null)
					{
						throw new InvalidDataException(error2 ?? "precompressed BC3 load failed");
					}
				}
				else if (spec.PrecompressedBc7)
				{
					val = PrecompressedTextureLoader.LoadBc7(text, spec.Width, spec.Height, spec.MipChain, out string error3);
					if ((Object)(object)val == (Object)null)
					{
						throw new InvalidDataException(error3 ?? "precompressed BC7 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 PruneDestroyedFireExitStates()
		{
			List<int> list = null;
			foreach (KeyValuePair<int, FireExitState> fireExitState in FireExitStates)
			{
				if (!((Object)(object)fireExitState.Value.Renderer != (Object)null) || !((Object)(object)fireExitState.Value.Overlay != (Object)null))
				{
					if (list == null)
					{
						list = new List<int>();
					}
					list.Add(fireExitState.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (int item in list)
			{
				FireExitStates.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? LoadBc1(string path, int width, int height, bool mipChain, out string? error)
		{
			return LoadCompressed(path, width, height, mipChain, (TextureFormat)10, 8, "DXT1/BC1", out error);
		}

		public static Texture2D? LoadBc3(string path, int width, int height, bool mipChain, out string? error)
		{
			return LoadCompressed(path, width, height, mipChain, (TextureFormat)12, 16, "DXT5/BC3", out error);
		}

		public static Texture2D? LoadBc7(string path, int width, int height, bool mipChain, out string? error)
		{
			return LoadCompressed(path, width, height, mipChain, (TextureFormat)25, 16, "BC7", out error);
		}

		private static Texture2D? LoadCompressed(string path, int width, int height, bool mipChain, TextureFormat format, int bytesPerBlock, string formatName, out string? error)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			error = null;
			Texture2D val = null;
			try
			{
				if (!SystemInfo.SupportsTextureFormat(format))
				{
					error = formatName + " 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 = CalculateCompressedByteCount(width, height, mipChain, bytesPerBlock);
				if (fileInfo.Length != num)
				{
					error = $"expected {num} bytes, got {fileInfo.Length}";
					return null;
				}
				byte[] array = File.ReadAllBytes(path);
				val = new Texture2D(width, height, format, 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)
		{
			return CalculateCompressedByteCount(width, height, mipChain, 16);
		}

		public static long CalculateBc7ByteCount(int width, int height, bool mipChain)
		{
			return CalculateCompressedByteCount(width, height, mipChain, 16);
		}

		public static long CalculateBc1ByteCount(int width, int height, bool mipChain)
		{
			return CalculateCompressedByteCount(width, height, mipChain, 8);
		}

		private static long CalculateCompressedByteCount(int width, int height, bool mipChain, int bytesPerBlock)
		{
			long num = 0L;
			while (true)
			{
				num += (long)Math.Max(1, (width + 3) / 4) * (long)Math.Max(1, (height + 3) / 4) * bytesPerBlock;
				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);
			}
		}