Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LC Chinese Project v3.2.3
BepInEx/plugins/V81TestChn/V81TestChn.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.TextCore; using UnityEngine.TextCore.LowLevel; using UnityEngine.UI; using UnityEngine.Video; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("V81TestChn")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+87e1576962d3ca34e223691ed04620a46ed34371")] [assembly: AssemblyProduct("V81TestChn")] [assembly: AssemblyTitle("V81TestChn")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace V81TestChn { internal static class AlertTextureReplacementService { private enum NativeTextRole { None, SystemOnline, EnteringAtmosphere } private readonly struct CachedNativeTextRole { public int ParentId { get; } public NativeTextRole Role { get; } public CachedNativeTextRole(int parentId, NativeTextRole role) { ParentId = parentId; Role = role; } } private const int NativeTextRoleCacheLimit = 16384; private const string SystemOnlineTitleObjectName = "TipLeft1"; private const string SystemOnlineTitleFullPath = "Systems/UI/Canvas/IngamePlayerHUD/BottomMiddle/SystemsOnline/TipLeft1"; private const string SystemOnlineTitlePathSuffix = "IngamePlayerHUD/BottomMiddle/SystemsOnline/TipLeft1"; private const string SystemOnlineRelativePath = "BottomMiddle/SystemsOnline/TipLeft1"; private const string EnteringAtmosphereTitleObjectName = "LoadText"; private const string RelaySceneName = "SampleSceneRelay"; private const string SystemOnlineLocalizedText = "系统在线"; private const string EnteringAtmosphereLocalizedText = "正在进入大气层..."; private const string HazardLevelLocalizedText = "风险级别:"; private const string LifeSupportOfflineLocalizedText = "[生命维持:离线]"; private static Coroutine? _systemOnlineWatcher; private static Coroutine? _fixedSceneLabelWatcher; private static HUDManager? _systemOnlineWatcherOwner; private static HUDManager? _fixedSceneLabelWatcherOwner; private static bool _fixedSceneLabelSceneLoadedSubscribed; private static string _fixedSceneLabelSceneLoadedStage = "SceneLoaded"; private static readonly BoundedCache<int, CachedNativeTextRole> NativeTextRoleCache = new BoundedCache<int, CachedNativeTextRole>(16384); private static readonly Dictionary<string, string> FixedSceneLabels = new Dictionary<string, string>(StringComparer.Ordinal) { ["TO MEET PROFIT QUOTA"] = "以达到利润配额", ["PERFORMANCE REPORT"] = "绩效报告", ["EMPLOYEE RANK"] = "员工等级", ["Fines"] = "罚款", ["(Dead)"] = "(死亡)", ["Deceased"] = "死亡", ["[LIFE SUPPORT: OFFLINE]"] = "[生命维持:离线]", ["You will keep your employee rank. Your ship and credits will be reset."] = "你的员工等级将被保留,但飞船和点数将被重置。" }; public static void Initialize(string pluginDir) { if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogInfo((object)"Native relay title translation enabled; SYSTEMS ONLINE uses original TMP object only."); } } public static void ForceApplySystemOnlineOverlay(HUDManager? hudManager, string stage) { TMP_Text val = FindSystemOnlineTitle(hudManager, allowGlobalFallback: false); if ((Object)(object)val != (Object)null) { ApplySystemOnlineNativeTranslation(val, stage); } else if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogWarning((object)("NativeRelay[" + stage + "] target=SystemOnline action=not-found")); AuditSystemOnlineBranch(stage, hudManager); } } public static void BeginSystemOnlineExactPathWatcher(HUDManager? hudManager, string stage) { if (!((Object)(object)hudManager == (Object)null)) { ResetSystemOnlineWatcherIfStale(hudManager); if (_systemOnlineWatcher == null) { _systemOnlineWatcherOwner = hudManager; _systemOnlineWatcher = ((MonoBehaviour)hudManager).StartCoroutine(WaitForSystemOnlineTitle(hudManager, stage)); } } } public static void TryApplySystemOnlineDirect(HUDManager? hudManager, string stage) { TMP_Text val = FindSystemOnlineTitle(hudManager, allowGlobalFallback: true); if ((Object)(object)val == (Object)null) { if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogWarning((object)("NativeRelay[" + stage + "] target=SystemOnline action=not-found")); } } else { ApplySystemOnlineNativeTranslation(val, stage); } } public static void QueueSystemOnlineSync(HUDManager? hudManager, string stage) { } public static void TryApplyEnteringAtmosphereOverlayFromLoadingScreen(HUDManager? hudManager, string stage) { TMP_Text val = FindEnteringAtmosphereTitle(hudManager); if ((Object)(object)val == (Object)null) { if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogWarning((object)("NativeRelay[" + stage + "] target=EnteringAtmosphere action=not-found")); } } else { ApplyEnteringAtmosphereNativeTranslation(val, stage); } } public static void HideEnteringAtmosphereOverlayForHud(HUDManager? hudManager, string stage) { TMP_Text val = FindEnteringAtmosphereTitle(hudManager); if ((Object)(object)val != (Object)null) { ApplyEnteringAtmosphereNativeTranslation(val, stage); } } public static void SyncEnteringAtmosphereOverlayState(TMP_Text? text, string stage) { if (!((Object)(object)text == (Object)null) && GetNativeTextRole(text) == NativeTextRole.EnteringAtmosphere) { ApplyEnteringAtmosphereNativeTranslation(text, stage); } } public static void SyncHazardLevelRelay(HUDManager? hudManager, string stage) { TMP_Text val = FindHazardLevelTitle(hudManager); if ((Object)(object)val != (Object)null) { ApplyHazardLevelNativeTranslation(val, stage); } } public static void SyncLandingInfo(HUDManager? hudManager, string stage) { if (!((Object)(object)hudManager == (Object)null)) { ApplyDynamicFieldTranslation((TMP_Text?)(object)hudManager.planetInfoHeaderText, stage, "PlanetInfoHeader"); ApplyDynamicFieldTranslation((TMP_Text?)(object)hudManager.planetInfoSummaryText, stage, "PlanetInfoSummary"); ApplyDynamicFieldTranslation((TMP_Text?)(object)hudManager.planetRiskLevelText, stage, "PlanetRiskLevel"); SyncHazardLevelRelay(hudManager, stage); } } public static void SyncFixedSceneLabels(HUDManager? hudManager, string stage) { if (!((Object)(object)hudManager == (Object)null)) { TMP_Text[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<TMP_Text>(true); for (int i = 0; i < componentsInChildren.Length; i++) { TryReplaceFixedSceneLabel(componentsInChildren[i], stage); } } } public static void BeginFixedSceneLabelWatcher(HUDManager? hudManager, string stage) { if (!((Object)(object)hudManager == (Object)null) && SyncFixedSceneLabelsInRelayScene(stage + ".initial") <= 0) { _fixedSceneLabelWatcherOwner = hudManager; _fixedSceneLabelSceneLoadedStage = stage; if (!_fixedSceneLabelSceneLoadedSubscribed) { SceneManager.sceneLoaded += OnRelaySceneLoadedForFixedLabels; _fixedSceneLabelSceneLoadedSubscribed = true; } } } public static void Shutdown() { try { if (_systemOnlineWatcher != null && (Object)(object)_systemOnlineWatcherOwner != (Object)null) { ((MonoBehaviour)_systemOnlineWatcherOwner).StopCoroutine(_systemOnlineWatcher); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("NativeRelay shutdown failed stopping SystemOnline watcher: " + ex.GetType().Name + ": " + ex.Message)); } try { if (_fixedSceneLabelWatcher != null && (Object)(object)_fixedSceneLabelWatcherOwner != (Object)null) { ((MonoBehaviour)_fixedSceneLabelWatcherOwner).StopCoroutine(_fixedSceneLabelWatcher); } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("NativeRelay shutdown failed stopping fixed scene label watcher: " + ex2.GetType().Name + ": " + ex2.Message)); } _systemOnlineWatcher = null; _fixedSceneLabelWatcher = null; _systemOnlineWatcherOwner = null; _fixedSceneLabelWatcherOwner = null; if (_fixedSceneLabelSceneLoadedSubscribed) { SceneManager.sceneLoaded -= OnRelaySceneLoadedForFixedLabels; _fixedSceneLabelSceneLoadedSubscribed = false; } _fixedSceneLabelSceneLoadedStage = "SceneLoaded"; NativeTextRoleCache.Clear(); } private static void OnRelaySceneLoadedForFixedLabels(Scene scene, LoadSceneMode mode) { if (string.Equals(((Scene)(ref scene)).name, "SampleSceneRelay", StringComparison.Ordinal)) { SyncFixedSceneLabelsInRelayScene(_fixedSceneLabelSceneLoadedStage + ".scene-loaded"); SceneManager.sceneLoaded -= OnRelaySceneLoadedForFixedLabels; _fixedSceneLabelSceneLoadedSubscribed = false; _fixedSceneLabelWatcherOwner = null; _fixedSceneLabelSceneLoadedStage = "SceneLoaded"; } } private static void ResetSystemOnlineWatcherIfStale(HUDManager currentHud) { if (_systemOnlineWatcher == null) { _systemOnlineWatcherOwner = null; } else if (!((Object)(object)_systemOnlineWatcherOwner == (Object)(object)currentHud)) { StopStaleWatcher(_systemOnlineWatcherOwner, _systemOnlineWatcher, "SystemOnline"); _systemOnlineWatcher = null; _systemOnlineWatcherOwner = null; } } private static void ResetFixedSceneLabelWatcherIfStale(HUDManager currentHud) { if (_fixedSceneLabelWatcher == null) { _fixedSceneLabelWatcherOwner = null; } else if (!((Object)(object)_fixedSceneLabelWatcherOwner == (Object)(object)currentHud)) { StopStaleWatcher(_fixedSceneLabelWatcherOwner, _fixedSceneLabelWatcher, "FixedSceneLabel"); _fixedSceneLabelWatcher = null; _fixedSceneLabelWatcherOwner = null; } } private static void StopStaleWatcher(HUDManager? owner, Coroutine watcher, string label) { if ((Object)(object)owner == (Object)null) { return; } try { ((MonoBehaviour)owner).StopCoroutine(watcher); } catch (Exception ex) { Plugin.Log.LogWarning((object)("NativeRelay stale " + label + " watcher stop failed: " + ex.GetType().Name + ": " + ex.Message)); } } public static void TryReplaceSystemOnlineText(TMP_Text? text, string stage) { if ((Object)(object)text == (Object)null || !MightBeNativeRelayCandidate(text)) { return; } switch (GetNativeTextRole(text)) { case NativeTextRole.SystemOnline: ApplySystemOnlineNativeTranslation(text, stage); return; case NativeTextRole.EnteringAtmosphere: ApplyEnteringAtmosphereNativeTranslation(text, stage); return; } if (IsHazardLevelTitle(text.text)) { ApplyHazardLevelNativeTranslation(text, stage); } else { TryReplaceFixedSceneLabel(text, stage); } } public static bool TryReplaceFixedSceneLabel(TMP_Text? text, string stage) { if ((Object)(object)text == (Object)null || string.IsNullOrWhiteSpace(text.text)) { return false; } if (!TryResolveFixedSceneLabel(text.text, out string localized)) { return false; } ApplyLocalizedText(text, localized, stage, "FixedSceneLabel"); if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogInfo((object)("NativeRelay[" + stage + "] target=FixedSceneLabel action=applied name=" + ((Object)text).name + " path=" + BuildPath(text.transform) + " text=" + text.text)); } return true; } public static void TryReplaceSystemOnlineText(Text? text, string stage) { } private static bool MightBeNativeRelayCandidate(TMP_Text text) { if (string.Equals(((Object)text).name, "TipLeft1", StringComparison.OrdinalIgnoreCase) || string.Equals(((Object)text).name, "LoadText", StringComparison.OrdinalIgnoreCase)) { return true; } string text2 = text.text; if (string.IsNullOrWhiteSpace(text2) || text2.Length > 160) { return false; } int i; for (i = 0; i < text2.Length && char.IsWhiteSpace(text2[i]); i++) { } if (i >= text2.Length) { return false; } switch (char.ToUpperInvariant(text2[i])) { case '(': case 'D': case 'E': case 'F': case 'H': case 'P': case 'T': case 'Y': case '[': return true; default: return false; } } public static void TryReplaceSystemOnlineText(TextMesh? text, string stage) { } public static void TryReplace(Image? image, string stage) { } public static void TryReplace(RawImage? image, string stage) { } public static void TryReplace(SpriteRenderer? renderer, string stage) { } public static void TryReplace(Material? material, string stage) { } private static void ApplySystemOnlineNativeTranslation(TMP_Text text, string stage) { if (!((Behaviour)text).enabled) { ((Behaviour)text).enabled = true; } ApplyLocalizedText(text, ResolveLocalizedText(text.text, "系统在线"), stage, "SystemOnline"); if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogInfo((object)("NativeRelay[" + stage + "] target=SystemOnline action=applied name=" + ((Object)text).name + " path=" + BuildPath(text.transform) + " text=" + text.text)); } } private static IEnumerator WaitForSystemOnlineTitle(HUDManager hudManager, string stage) { float elapsed = 0f; while ((Object)(object)hudManager != (Object)null && elapsed < 12f) { TMP_Text text = FindSystemOnlineTitleByHudPath(hudManager); if (IsExactSystemOnlineTitle(text)) { ApplySystemOnlineNativeTranslation(text, stage + ".watcher"); _systemOnlineWatcher = null; _systemOnlineWatcherOwner = null; yield break; } yield return (object)new WaitForSeconds(0.25f); elapsed += 0.25f; } if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogWarning((object)("NativeRelay[" + stage + ".watcher] target=SystemOnline action=timeout")); } _systemOnlineWatcher = null; _systemOnlineWatcherOwner = null; } private static IEnumerator WaitForFixedSceneLabels(string stage) { for (float elapsed = 0f; elapsed < 8f; elapsed += 0.5f) { if (SyncFixedSceneLabelsInRelayScene(stage + ".relay-scene") > 0) { _fixedSceneLabelWatcher = null; _fixedSceneLabelWatcherOwner = null; yield break; } yield return (object)new WaitForSeconds(0.5f); } if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogWarning((object)("NativeRelay[" + stage + ".relay-scene] target=FixedSceneLabel action=timeout")); } _fixedSceneLabelWatcher = null; _fixedSceneLabelWatcherOwner = null; } private static void ApplyEnteringAtmosphereNativeTranslation(TMP_Text text, string stage) { ApplyLocalizedText(text, ResolveLocalizedText(text.text, "正在进入大气层..."), stage, "EnteringAtmosphere"); } private static void ApplyHazardLevelNativeTranslation(TMP_Text text, string stage) { ApplyLocalizedText(text, "风险级别:", stage, "HazardLevel"); } private static void ApplyLocalizedText(TMP_Text text, string localized, string stage, string target) { if (!string.IsNullOrWhiteSpace(localized)) { if (!string.Equals(text.text, localized, StringComparison.Ordinal)) { text.text = localized; } FontFallbackService.ApplyFallback(text, localized); ApplyLifeSupportOfflineTypography(text, localized); } } private static void ApplyLifeSupportOfflineTypography(TMP_Text text, string localized) { if (string.Equals(localized, "[生命维持:离线]", StringComparison.Ordinal)) { text.richText = true; text.enableWordWrapping = false; text.overflowMode = (TextOverflowModes)0; text.fontStyle = (FontStyles)0; text.characterSpacing = Math.Max(text.characterSpacing, 6f); text.wordSpacing = Math.Max(text.wordSpacing, 8f); } } private static TMP_Text? FindSystemOnlineTitle(HUDManager? hudManager, bool allowGlobalFallback) { if ((Object)(object)hudManager != (Object)null) { if ((Object)(object)((Component)hudManager).transform != (Object)null) { Transform obj = ((Component)hudManager).transform.Find("BottomMiddle/SystemsOnline/TipLeft1"); TMP_Text val = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null); if (IsExactSystemOnlineTitle(val)) { return val; } } TMP_Text[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val2 in componentsInChildren) { if (IsExactSystemOnlineTitle(val2)) { return val2; } } } if (allowGlobalFallback) { TMP_Text val3 = FindSystemOnlineTitleByFullPath(); if (IsExactSystemOnlineTitle(val3)) { return val3; } TMP_Text[] componentsInChildren = Resources.FindObjectsOfTypeAll<TMP_Text>(); foreach (TMP_Text val4 in componentsInChildren) { if (IsExactSystemOnlineTitle(val4)) { return val4; } } } return null; } private static TMP_Text? FindSystemOnlineTitleByFullPath() { GameObject val = GameObject.Find("Systems/UI/Canvas/IngamePlayerHUD/BottomMiddle/SystemsOnline/TipLeft1"); if (!((Object)(object)val != (Object)null)) { return null; } return val.GetComponent<TMP_Text>(); } private static TMP_Text? FindSystemOnlineTitleByHudPath(HUDManager? hudManager) { if ((Object)(object)((hudManager != null) ? ((Component)hudManager).transform : null) == (Object)null) { return null; } Transform val = ((Component)hudManager).transform.Find("BottomMiddle/SystemsOnline/TipLeft1"); if (!((Object)(object)val == (Object)null)) { return ((Component)val).GetComponent<TMP_Text>(); } return null; } private static TMP_Text? FindEnteringAtmosphereTitle(HUDManager? hudManager) { if ((Object)(object)hudManager?.LoadingScreen == (Object)null) { return null; } TMP_Text[] componentsInChildren = ((Component)hudManager.LoadingScreen).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val in componentsInChildren) { if (IsEnteringAtmosphereTitleObject(val)) { return val; } } return null; } private static TMP_Text? FindHazardLevelTitle(HUDManager? hudManager) { if ((Object)(object)hudManager == (Object)null) { return null; } TMP_Text[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val in componentsInChildren) { if (IsHazardLevelTitle(val.text)) { return val; } } return null; } private static bool IsSystemOnlineObject(TMP_Text? text) { return GetNativeTextRole(text) == NativeTextRole.SystemOnline; } private static bool IsSystemOnlinePath(Transform? transform) { if ((Object)(object)transform == (Object)null) { return false; } return BuildPath(transform).EndsWith("IngamePlayerHUD/BottomMiddle/SystemsOnline/TipLeft1", StringComparison.OrdinalIgnoreCase); } private static bool IsEnteringAtmosphereTitleObject(TMP_Text? text) { return GetNativeTextRole(text) == NativeTextRole.EnteringAtmosphere; } private static bool IsHazardLevelTitle(string? text) { if (string.IsNullOrWhiteSpace(text)) { return false; } string a = text.Trim(); if (!string.Equals(a, "HAZARD LEVEL:", StringComparison.OrdinalIgnoreCase) && !string.Equals(a, "风险级别:", StringComparison.Ordinal) && !string.Equals(a, "风险级别:", StringComparison.Ordinal) && !string.Equals(a, "危险等级:", StringComparison.Ordinal)) { return string.Equals(a, "危险等级:", StringComparison.Ordinal); } return true; } private static string ResolveLocalizedText(string? current, string fallback) { if (!string.IsNullOrWhiteSpace(current) && TranslationService.TryTranslate(current, out string translated)) { return translated; } return fallback; } private static bool TryResolveFixedSceneLabel(string? current, out string localized) { localized = string.Empty; if (string.IsNullOrWhiteSpace(current)) { return false; } if (FixedSceneLabels.TryGetValue(current.Trim(), out localized) && !string.IsNullOrWhiteSpace(localized)) { return true; } return false; } private static int SyncFixedSceneLabelsInRelayScene(string stage) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) Scene sceneByName = SceneManager.GetSceneByName("SampleSceneRelay"); if (!((Scene)(ref sceneByName)).IsValid() || !((Scene)(ref sceneByName)).isLoaded) { return 0; } int num = 0; GameObject[] rootGameObjects = ((Scene)(ref sceneByName)).GetRootGameObjects(); for (int i = 0; i < rootGameObjects.Length; i++) { TMP_Text[] componentsInChildren = rootGameObjects[i].GetComponentsInChildren<TMP_Text>(true); for (int j = 0; j < componentsInChildren.Length; j++) { if (TryReplaceFixedSceneLabel(componentsInChildren[j], stage)) { num++; } } } return num; } private static bool HasNamedAncestor(Transform? transform, string expectedName) { Transform val = transform; while ((Object)(object)val != (Object)null) { if (string.Equals(((Object)val).name, expectedName, StringComparison.OrdinalIgnoreCase)) { return true; } val = val.parent; } return false; } private static void AuditSystemOnlineBranch(string stage, HUDManager? hudManager) { int num = 0; if ((Object)(object)hudManager != (Object)null) { TMP_Text[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val in componentsInChildren) { if (HasNamedAncestor(val.transform, "SystemsOnline")) { num++; if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogInfo((object)$"NativeRelay[{stage}] target=SystemOnline action=audit exact={IsExactSystemOnlineTitle(val)} name={((Object)val).name} path={BuildPath(val.transform)} text={val.text}"); } } } } _ = 0; } private static void ApplyDynamicFieldTranslation(TMP_Text? text, string stage, string target) { if (!((Object)(object)text == (Object)null) && !string.IsNullOrWhiteSpace(text.text) && (TranslationService.TryTranslateKnownDynamicTextTargeted(DynamicTextDomain.PlanetInfo, text.text, out string translated) || TranslationService.TryTranslate(text.text, out translated)) && !string.IsNullOrWhiteSpace(translated)) { if (!string.Equals(text.text, translated, StringComparison.Ordinal)) { text.text = translated; } FontFallbackService.ApplyFallback(text, translated); if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogInfo((object)("NativeRelay[" + stage + "] target=" + target + " action=applied name=" + ((Object)text).name + " path=" + BuildPath(text.transform) + " text=" + text.text)); } } } private static string BuildPath(Transform? transform) { if ((Object)(object)transform == (Object)null) { return "<null>"; } string text = ((Object)transform).name; Transform parent = transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } private static bool IsExactSystemOnlineTitle(TMP_Text? text) { return GetNativeTextRole(text) == NativeTextRole.SystemOnline; } private static NativeTextRole GetNativeTextRole(TMP_Text? text) { if ((Object)(object)text == (Object)null) { return NativeTextRole.None; } if (TryGetCachedNativeTextRole(text, out var role)) { return role; } role = ResolveNativeTextRole(text); CacheNativeTextRole(text, role); return role; } private static bool TryGetCachedNativeTextRole(TMP_Text text, out NativeTextRole role) { int parentInstanceId = GetParentInstanceId(text.transform); if (NativeTextRoleCache.TryGetValue(((Object)text).GetInstanceID(), out var value) && value.ParentId == parentInstanceId) { role = value.Role; return true; } role = NativeTextRole.None; return false; } private static NativeTextRole ResolveNativeTextRole(TMP_Text text) { if (string.Equals(((Object)text).name, "LoadText", StringComparison.OrdinalIgnoreCase) && HasNamedAncestor(text.transform, "LoadingText")) { return NativeTextRole.EnteringAtmosphere; } if (string.Equals(((Object)text).name, "TipLeft1", StringComparison.OrdinalIgnoreCase) && IsSystemOnlinePath(text.transform)) { return NativeTextRole.SystemOnline; } return NativeTextRole.None; } private static void CacheNativeTextRole(TMP_Text text, NativeTextRole role) { int instanceID = ((Object)text).GetInstanceID(); NativeTextRoleCache.Set(instanceID, new CachedNativeTextRole(GetParentInstanceId(text.transform), role), RuntimePerformanceSettings.ComponentTextCacheLimit); } private static int GetParentInstanceId(Transform? transform) { if (!((Object)(object)((transform != null) ? transform.parent : null) == (Object)null)) { return ((Object)transform.parent).GetInstanceID(); } return 0; } } internal static class ClipboardManualLocalizationService { private sealed class TextureSpec { public string FileName { get; } public int Width { get; } public int Height { get; } public bool PrecompressedBc3 { get; } public TextureSpec(string fileName, int width = 0, int height = 0, bool precompressedBc3 = false) { FileName = fileName; Width = width; Height = height; PrecompressedBc3 = precompressedBc3; } } private sealed class RendererState { public Renderer Renderer { get; } public Material[] OriginalMaterials { get; } public List<Material> LocalizedMaterials { get; } public RendererState(Renderer renderer, Material[] originalMaterials, List<Material> localizedMaterials) { Renderer = renderer; OriginalMaterials = originalMaterials; LocalizedMaterials = localizedMaterials; } } private const int MaxTextureFileBytes = 8388608; private const int MaxTextureDimension = 4096; private static readonly Dictionary<string, TextureSpec> TextureFiles = new Dictionary<string, TextureSpec>(StringComparer.OrdinalIgnoreCase) { ["Manual1"] = new TextureSpec("ClipboardManualPage1.png"), ["Manual2"] = new TextureSpec("ClipboardManualPage2.png"), ["Manual3"] = new TextureSpec("ClipboardManualPage3.png"), ["Manual4"] = new TextureSpec("ClipboardManualPage4.png"), ["CruiserManual1"] = new TextureSpec("CruiserManualPage1.bc3", 900, 1260, precompressedBc3: true), ["CruiserManual2"] = new TextureSpec("CruiserManualPage2.bc3", 900, 1260, precompressedBc3: true), ["CruiserManual3"] = new TextureSpec("CruiserManualPage3.bc3", 900, 1260, precompressedBc3: true), ["CruiserManual4"] = new TextureSpec("CruiserManualPage4.bc3", 900, 1260, precompressedBc3: true) }; private static readonly Dictionary<string, Texture2D> Textures = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> FailedTextures = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<int, RendererState> RendererStates = new Dictionary<int, RendererState>(); private static string _textureDirectory = string.Empty; private static bool _initialized; public static void Initialize(string pluginDir) { _textureDirectory = Path.Combine(pluginDir, "textures"); _initialized = true; FailedTextures.Clear(); } public static void Apply(GrabbableObject? grabbableObject) { if (!_initialized || Plugin.IsRuntimeShuttingDown) { return; } ClipboardItem val = (ClipboardItem)(object)((grabbableObject is ClipboardItem) ? grabbableObject : null); if (val == null) { return; } Animator clipboardAnimator = val.clipboardAnimator; if (!((Object)(object)clipboardAnimator == (Object)null)) { PruneDestroyedRendererStates(); SkinnedMeshRenderer[] componentsInChildren = ((Component)clipboardAnimator).GetComponentsInChildren<SkinnedMeshRenderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { ApplyToRenderer((Renderer?)(object)componentsInChildren[i]); } } } public static void ApplyVehicle(VehicleController? vehicle) { if (_initialized && !Plugin.IsRuntimeShuttingDown && !((Object)(object)vehicle == (Object)null)) { PruneDestroyedRendererStates(); Renderer[] componentsInChildren = ((Component)vehicle).GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { ApplyToRenderer(componentsInChildren[i]); } } } public static void Shutdown() { foreach (RendererState value in RendererStates.Values) { try { if ((Object)(object)value.Renderer != (Object)null) { value.Renderer.sharedMaterials = value.OriginalMaterials; } } catch { } foreach (Material localizedMaterial in value.LocalizedMaterials) { DestroyUnityObject((Object?)(object)localizedMaterial); } } RendererStates.Clear(); foreach (Texture2D value2 in Textures.Values) { DestroyUnityObject((Object?)(object)value2); } Textures.Clear(); FailedTextures.Clear(); _textureDirectory = string.Empty; _initialized = false; } public static void OnSceneUnloaded() { PruneDestroyedRendererStates(); } private static void ApplyToRenderer(Renderer? renderer) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown if ((Object)(object)renderer == (Object)null) { return; } int instanceID = ((Object)renderer).GetInstanceID(); if (RendererStates.ContainsKey(instanceID)) { return; } Material[] sharedMaterials = renderer.sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0) { return; } Material[] array = (Material[])sharedMaterials.Clone(); List<Material> list = new List<Material>(); for (int i = 0; i < sharedMaterials.Length; i++) { Material val = sharedMaterials[i]; if (!((Object)(object)val == (Object)null) && TryResolveTexture(((Object)val).name, out Texture2D texture)) { Material val2 = new Material(val) { name = NormalizeMaterialName(((Object)val).name) + " (zh-CN)" }; SetTexture(val2, texture); array[i] = val2; list.Add(val2); } } if (list.Count != 0) { renderer.sharedMaterials = array; RendererStates[instanceID] = new RendererState(renderer, sharedMaterials, list); } } private static void PruneDestroyedRendererStates() { List<int> list = null; foreach (KeyValuePair<int, RendererState> rendererState in RendererStates) { if ((Object)(object)rendererState.Value.Renderer != (Object)null) { continue; } if (list == null) { list = new List<int>(); } list.Add(rendererState.Key); foreach (Material localizedMaterial in rendererState.Value.LocalizedMaterials) { DestroyUnityObject((Object?)(object)localizedMaterial); } } if (list == null) { return; } foreach (int item in list) { RendererStates.Remove(item); } } private static bool TryResolveTexture(string? materialName, out Texture2D texture) { string text = NormalizeMaterialName(materialName); if (Textures.TryGetValue(text, out texture)) { return true; } if (FailedTextures.Contains(text) || !TextureFiles.TryGetValue(text, out TextureSpec value)) { texture = null; return false; } Texture2D val = LoadTexture(text, value); if ((Object)(object)val == (Object)null) { FailedTextures.Add(text); texture = null; return false; } Textures[text] = val; texture = val; return true; } private static string NormalizeMaterialName(string? materialName) { if (string.IsNullOrWhiteSpace(materialName)) { return string.Empty; } string text = materialName.Trim(); if (text.EndsWith(" (Instance)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - " (Instance)".Length); } if (text.EndsWith(" (zh-CN)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - " (zh-CN)".Length); } return text; } private static void SetTexture(Material material, Texture2D texture) { if (material.HasProperty("_BaseColorMap")) { material.SetTexture("_BaseColorMap", (Texture)(object)texture); } if (material.HasProperty("_MainTex")) { material.SetTexture("_MainTex", (Texture)(object)texture); } material.mainTexture = (Texture)(object)texture; } private static Texture2D? LoadTexture(string materialName, TextureSpec spec) { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown string fileName = spec.FileName; string text = Path.Combine(_textureDirectory, fileName); Texture2D val = null; try { FileInfo fileInfo = new FileInfo(text); if (!fileInfo.Exists) { Plugin.Log.LogWarning((object)("Clipboard manual texture is missing: " + fileName)); return null; } if (fileInfo.Length <= 0 || (!spec.PrecompressedBc3 && fileInfo.Length > 8388608)) { Plugin.Log.LogWarning((object)$"Clipboard manual texture has an invalid file size: {fileName} ({fileInfo.Length} bytes)"); return null; } if (spec.PrecompressedBc3) { val = PrecompressedTextureLoader.LoadBc3(text, spec.Width, spec.Height, mipChain: false, out string error); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)("Clipboard manual texture could not be loaded: " + fileName + " (" + error + ")")); return null; } } else { byte[] array = File.ReadAllBytes(text); val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, array, true)) { Plugin.Log.LogWarning((object)("Clipboard manual texture could not be decoded: " + fileName)); DestroyUnityObject((Object?)(object)val); return null; } if (((Texture)val).width <= 0 || ((Texture)val).height <= 0 || ((Texture)val).width > 4096 || ((Texture)val).height > 4096) { Plugin.Log.LogWarning((object)$"Clipboard manual texture dimensions are invalid: {fileName} ({((Texture)val).width}x{((Texture)val).height})"); DestroyUnityObject((Object?)(object)val); return null; } } ((Object)val).name = materialName + " (zh-CN)"; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; return val; } catch (Exception ex) { DestroyUnityObject((Object?)(object)val); Plugin.Log.LogWarning((object)("Clipboard manual texture load failed for " + fileName + ": " + ex.GetType().Name + ": " + ex.Message)); return null; } } private static void DestroyUnityObject(Object? value) { if (!(value == (Object)null)) { if (Application.isPlaying) { Object.Destroy(value); } else { Object.DestroyImmediate(value); } } } } internal static class EnvironmentTextureLocalizationService { private sealed class TextureSpec { public string OriginalName { get; } public string FileName { get; } public int Width { get; } public int Height { get; } public bool PrecompressedBc3 { get; } public bool MipChain { get; } public TextureSpec(string originalName, string fileName, int width, int height, bool precompressedBc3 = false, bool mipChain = true) { OriginalName = originalName; FileName = fileName; Width = width; Height = height; PrecompressedBc3 = precompressedBc3; MipChain = mipChain; } } private sealed class RendererState { public Renderer Renderer { get; } public Material[] OriginalMaterials { get; } public List<int> LocalizedMaterialIds { get; } public RendererState(Renderer renderer, Material[] originalMaterials, List<int> localizedMaterialIds) { Renderer = renderer; OriginalMaterials = originalMaterials; LocalizedMaterialIds = localizedMaterialIds; } } private sealed class MaterialState { public WeakReference<Material> Original { get; } public Material Localized { get; } public MaterialState(Material original, Material localized) { Original = new WeakReference<Material>(original); Localized = localized; } public bool HasOriginal(Material material) { if (Original.TryGetTarget(out Material target) && (Object)(object)target != (Object)null) { return (Object)(object)target == (Object)(object)material; } return false; } public bool HasLiveOriginal() { if (Original.TryGetTarget(out Material target)) { return (Object)(object)target != (Object)null; } return false; } } private const int MaxTextureFileBytes = 16777216; private static readonly string[] TexturePropertyNames = new string[2] { "_BaseColorMap", "_MainTex" }; private static readonly List<Renderer> RendererScanBuffer = new List<Renderer>(); private static readonly HashSet<int> ActiveLocalizedMaterialIds = new HashSet<int>(); private static readonly Dictionary<string, TextureSpec> TextureSpecs = new Dictionary<string, TextureSpec>(StringComparer.OrdinalIgnoreCase) { ["posters"] = new TextureSpec("posters", "ShipPostersLocalized.png", 1024, 1024), ["TipsPoster2"] = new TextureSpec("TipsPoster2", "ShipTipsPosterLocalized.png", 796, 1024), ["CompanyCruiserCombined4Diffuse 1"] = new TextureSpec("CompanyCruiserCombined4Diffuse 1", "CompanyCruiserDiffuseLocalized.bc3", 4096, 4096, precompressedBc3: true), ["CompanyCruiserCombined4DiffuseDirtyV2"] = new TextureSpec("CompanyCruiserCombined4DiffuseDirtyV2", "CompanyCruiserDiffuseDirtyLocalized.bc3", 4096, 4096, precompressedBc3: true) }; private static readonly Dictionary<string, Texture2D> LocalizedTextures = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> FailedTextures = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<int, RendererState> RendererStates = new Dictionary<int, RendererState>(); private static readonly Dictionary<int, MaterialState> MaterialStates = new Dictionary<int, MaterialState>(); private static string _textureDirectory = string.Empty; private static bool _initialized; public static void Initialize(string pluginDir) { _textureDirectory = Path.Combine(pluginDir, "textures"); _initialized = true; } public static void ApplyShipEnvironment(StartOfRound? startOfRound) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!CanApply() || (Object)(object)startOfRound == (Object)null) { return; } PruneDestroyedRendererStates(); PruneUnusedMaterialStates(); Scene scene = ((Component)startOfRound).gameObject.scene; if (!((Scene)(ref scene)).IsValid() || !((Scene)(ref scene)).isLoaded) { return; } GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects(); foreach (GameObject val in rootGameObjects) { if ((Object)(object)val == (Object)null) { continue; } RendererScanBuffer.Clear(); val.GetComponentsInChildren<Renderer>(true, RendererScanBuffer); foreach (Renderer item in RendererScanBuffer) { ApplyToRenderer(item); } } RendererScanBuffer.Clear(); } public static void ApplyVehicle(VehicleController? vehicle) { if (!CanApply() || (Object)(object)vehicle == (Object)null) { return; } PruneDestroyedRendererStates(); PruneUnusedMaterialStates(); RendererScanBuffer.Clear(); ((Component)vehicle).GetComponentsInChildren<Renderer>(false, RendererScanBuffer); foreach (Renderer item in RendererScanBuffer) { ApplyToRenderer(item); } RendererScanBuffer.Clear(); } public static void Shutdown() { foreach (RendererState value in RendererStates.Values) { try { if ((Object)(object)value.Renderer != (Object)null) { value.Renderer.sharedMaterials = value.OriginalMaterials; } } catch { } } RendererStates.Clear(); foreach (MaterialState value2 in MaterialStates.Values) { DestroyUnityObject((Object?)(object)value2.Localized); } MaterialStates.Clear(); RendererScanBuffer.Clear(); ActiveLocalizedMaterialIds.Clear(); foreach (Texture2D value3 in LocalizedTextures.Values) { DestroyUnityObject((Object?)(object)value3); } LocalizedTextures.Clear(); FailedTextures.Clear(); _textureDirectory = string.Empty; _initialized = false; } public static void OnSceneUnloaded() { PruneDestroyedRendererStates(); PruneUnusedMaterialStates(); } private static bool CanApply() { if (_initialized) { return !Plugin.IsRuntimeShuttingDown; } return false; } private static void ApplyToRenderer(Renderer? renderer) { if ((Object)(object)renderer == (Object)null || RendererStates.ContainsKey(((Object)renderer).GetInstanceID())) { return; } Material[] sharedMaterials = renderer.sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0) { return; } Material[] array = (Material[])sharedMaterials.Clone(); List<int> list = null; bool flag = false; for (int i = 0; i < sharedMaterials.Length; i++) { Material val = sharedMaterials[i]; Material orCreateLocalizedMaterial = GetOrCreateLocalizedMaterial(val); if (!((Object)(object)orCreateLocalizedMaterial == (Object)null) && orCreateLocalizedMaterial != val) { array[i] = orCreateLocalizedMaterial; if (list == null) { list = new List<int>(); } int instanceID = ((Object)val).GetInstanceID(); if (!list.Contains(instanceID)) { list.Add(instanceID); } flag = true; } } if (flag) { renderer.sharedMaterials = array; RendererStates[((Object)renderer).GetInstanceID()] = new RendererState(renderer, sharedMaterials, list ?? new List<int>()); } } private static Material? GetOrCreateLocalizedMaterial(Material? original) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Expected O, but got Unknown if ((Object)(object)original == (Object)null) { return null; } int instanceID = ((Object)original).GetInstanceID(); if (MaterialStates.TryGetValue(instanceID, out MaterialState value)) { if (value.HasOriginal(original)) { return value.Localized; } DestroyUnityObject((Object?)(object)value.Localized); MaterialStates.Remove(instanceID); } Material val = null; bool flag = false; string[] texturePropertyNames = TexturePropertyNames; foreach (string text in texturePropertyNames) { if (!original.HasProperty(text) || !TryResolveSpec(original.GetTexture(text), out TextureSpec spec)) { continue; } Texture2D val2 = EnsureTextureLoaded(spec, original.GetTexture(text)); if (!((Object)(object)val2 == (Object)null)) { if (val == null) { val = new Material(original); } val.SetTexture(text, (Texture)(object)val2); flag = true; } } if (!flag && original.HasProperty("_MainTex") && TryResolveSpec(original.mainTexture, out TextureSpec spec2)) { Texture2D val3 = EnsureTextureLoaded(spec2, original.mainTexture); if ((Object)(object)val3 != (Object)null) { val = new Material(original) { mainTexture = (Texture)(object)val3 }; flag = true; } } if (!flag || (Object)(object)val == (Object)null) { DestroyUnityObject((Object?)(object)val); return original; } ((Object)val).name = NormalizeName(((Object)original).name) + " (zh-CN)"; if (MaterialStates.TryGetValue(instanceID, out MaterialState value2)) { DestroyUnityObject((Object?)(object)value2.Localized); } MaterialStates[instanceID] = new MaterialState(original, val); return val; } private static Texture2D? EnsureTextureLoaded(TextureSpec spec, Texture? originalTexture) { //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) if (LocalizedTextures.TryGetValue(spec.OriginalName, out Texture2D value)) { return value; } if (FailedTextures.Contains(spec.OriginalName)) { return null; } string text = Path.Combine(_textureDirectory, spec.FileName); Texture2D val = null; try { FileInfo fileInfo = new FileInfo(text); if (!fileInfo.Exists) { throw new FileNotFoundException("localized texture file is missing", text); } if (fileInfo.Length <= 0 || (!spec.PrecompressedBc3 && fileInfo.Length > 16777216)) { throw new InvalidDataException($"invalid file size ({fileInfo.Length} bytes)"); } if (spec.PrecompressedBc3) { val = PrecompressedTextureLoader.LoadBc3(text, spec.Width, spec.Height, spec.MipChain, out string error); if ((Object)(object)val == (Object)null) { throw new InvalidDataException(error ?? "precompressed BC3 load failed"); } } else { byte[] data = File.ReadAllBytes(text); val = LoadTextureData(data, useRuntimeCompression: true) ?? LoadTextureData(data, useRuntimeCompression: false); } if ((Object)(object)val == (Object)null || ((Texture)val).width != spec.Width || ((Texture)val).height != spec.Height) { string arg = (((Object)(object)val == (Object)null) ? "decode failed" : $"{((Texture)val).width}x{((Texture)val).height}"); throw new InvalidDataException($"expected {spec.Width}x{spec.Height}, got {arg}"); } ((Object)val).name = spec.OriginalName + " (zh-CN)"; if ((Object)(object)originalTexture != (Object)null) { ((Texture)val).wrapMode = originalTexture.wrapMode; ((Texture)val).filterMode = originalTexture.filterMode; ((Texture)val).anisoLevel = originalTexture.anisoLevel; ((Texture)val).mipMapBias = originalTexture.mipMapBias; } LocalizedTextures[spec.OriginalName] = val; return val; } catch (Exception ex) { DestroyUnityObject((Object?)(object)val); FailedTextures.Add(spec.OriginalName); Plugin.Log.LogWarning((object)("Environment texture localization failed for " + spec.FileName + ": " + ex.GetType().Name + ": " + ex.Message)); return null; } } private static Texture2D? LoadTextureData(byte[] data, bool useRuntimeCompression) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown Texture2D val = null; try { TextureFormat val2 = (TextureFormat)((useRuntimeCompression && SystemInfo.SupportsTextureFormat((TextureFormat)12)) ? 12 : 4); int num = (((int)val2 == 12) ? 4 : 2); val = new Texture2D(num, num, val2, true); if (ImageConversion.LoadImage(val, data, true)) { return val; } DestroyUnityObject((Object?)(object)val); return null; } catch { DestroyUnityObject((Object?)(object)val); return null; } } private static bool TryResolveSpec(Texture? texture, out TextureSpec spec) { return TextureSpecs.TryGetValue(NormalizeName((texture != null) ? ((Object)texture).name : null), out spec); } private static string NormalizeName(string? name) { if (string.IsNullOrWhiteSpace(name)) { return string.Empty; } string text = name.Trim(); if (text.EndsWith(" (Instance)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - " (Instance)".Length); } if (text.EndsWith(" (zh-CN)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - " (zh-CN)".Length); } return text; } private static void PruneDestroyedRendererStates() { List<int> list = null; foreach (KeyValuePair<int, RendererState> rendererState in RendererStates) { if (!((Object)(object)rendererState.Value.Renderer != (Object)null)) { if (list == null) { list = new List<int>(); } list.Add(rendererState.Key); } } if (list == null) { return; } foreach (int item in list) { RendererStates.Remove(item); } } private static void PruneUnusedMaterialStates() { ActiveLocalizedMaterialIds.Clear(); foreach (RendererState value in RendererStates.Values) { if ((Object)(object)value.Renderer == (Object)null) { continue; } foreach (int localizedMaterialId in value.LocalizedMaterialIds) { ActiveLocalizedMaterialIds.Add(localizedMaterialId); } } List<int> list = null; foreach (KeyValuePair<int, MaterialState> materialState in MaterialStates) { if (!((Object)(object)materialState.Value.Localized != (Object)null) || !materialState.Value.HasLiveOriginal() || !ActiveLocalizedMaterialIds.Contains(materialState.Key)) { if (list == null) { list = new List<int>(); } list.Add(materialState.Key); DestroyUnityObject((Object?)(object)materialState.Value.Localized); } } if (list != null) { foreach (int item in list) { MaterialStates.Remove(item); } } ActiveLocalizedMaterialIds.Clear(); } private static void DestroyUnityObject(Object? value) { if (!(value == (Object)null)) { if (Application.isPlaying) { Object.Destroy(value); } else { Object.DestroyImmediate(value); } } } } internal static class PrecompressedTextureLoader { public static Texture2D? LoadBc3(string path, int width, int height, bool mipChain, out string? error) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown error = null; Texture2D val = null; try { if (!SystemInfo.SupportsTextureFormat((TextureFormat)12)) { error = "DXT5/BC3 is not supported by this graphics device"; return null; } FileInfo fileInfo = new FileInfo(path); if (!fileInfo.Exists) { error = "precompressed texture file is missing"; return null; } long num = CalculateBc3ByteCount(width, height, mipChain); if (fileInfo.Length != num) { error = $"expected {num} bytes, got {fileInfo.Length}"; return null; } byte[] array = File.ReadAllBytes(path); val = new Texture2D(width, height, (TextureFormat)12, mipChain); val.LoadRawTextureData(array); val.Apply(false, true); return val; } catch (Exception ex) { DestroyUnityObject((Object?)(object)val); error = ex.GetType().Name + ": " + ex.Message; return null; } } public static long CalculateBc3ByteCount(int width, int height, bool mipChain) { long num = 0L; while (true) { num += (long)Math.Max(1, (width + 3) / 4) * (long)Math.Max(1, (height + 3) / 4) * 16; if (!mipChain || (width == 1 && height == 1)) { break; } width = Math.Max(1, width / 2); height = Math.Max(1, height / 2); } return num; } private static void DestroyUnityObject(Object? value) { if (!(value == (Object)null)) { if (Application.isPlaying) { Object.Destroy(value); } else { Object.DestroyImmediate(value); } } } } internal static class RadiationWarningAuditService { internal const string WarningRootPathSuffix = "IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease"; private static ConfigEntry<bool>? _enabled; private static ConfigEntry<int>? _sampleCount; private static ConfigEntry<float>? _sampleIntervalSeconds; private static HUDManager? _activeHudManager; private static Coroutine? _activeAuditCoroutine; public static void Initialize(ConfigFile config) { _enabled = config.Bind<bool>("90 诊断 - 辐射警告审计", "Enabled", false, "启用辐射警告原始 UI 子树的限量采样诊断。默认关闭,避免运行时枚举 UI 子树。"); _sampleCount = config.Bind<int>("90 诊断 - 辐射警告审计", "SampleCount", 5, "辐射警告触发后采样的次数上限。"); _sampleIntervalSeconds = config.Bind<float>("90 诊断 - 辐射警告审计", "SampleIntervalSeconds", 0.2f, "两次辐射警告诊断采样之间的间隔,单位为秒。"); } public static void Shutdown() { try { if (_activeAuditCoroutine != null && (Object)(object)_activeHudManager != (Object)null) { ((MonoBehaviour)_activeHudManager).StopCoroutine(_activeAuditCoroutine); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("RadiationWarningAuditService shutdown failed while stopping coroutine: " + ex.GetType().Name + ": " + ex.Message)); } finally { _activeAuditCoroutine = null; _activeHudManager = null; _enabled = null; _sampleCount = null; _sampleIntervalSeconds = null; } } public static void OnRadiationWarningTriggered(HUDManager hudManager, string stage) { if ((Object)(object)hudManager == (Object)null) { return; } ConfigEntry<bool>? enabled = _enabled; if (enabled == null || !enabled.Value) { return; } if ((Object)(object)FindWarningRoot(hudManager) == (Object)null) { Plugin.Log.LogWarning((object)("RadiationAudit[" + stage + "] action=root-not-found suffix=IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease")); return; } if (_activeAuditCoroutine != null && (Object)(object)_activeHudManager != (Object)null) { ((MonoBehaviour)_activeHudManager).StopCoroutine(_activeAuditCoroutine); _activeAuditCoroutine = null; } _activeHudManager = hudManager; _activeAuditCoroutine = ((MonoBehaviour)hudManager).StartCoroutine(SampleWarningSubtree(hudManager, stage, _sampleCount?.Value ?? 0, _sampleIntervalSeconds?.Value ?? 0f)); } private static IEnumerator SampleWarningSubtree(HUDManager hudManager, string stage, int sampleCount, float sampleIntervalSeconds) { int boundedSampleCount = Mathf.Max(1, sampleCount); float boundedIntervalSeconds = Mathf.Max(0f, sampleIntervalSeconds); for (int sampleIndex = 0; sampleIndex < boundedSampleCount; sampleIndex++) { if ((Object)(object)hudManager == (Object)null) { break; } Transform val = FindWarningRoot(hudManager); if ((Object)(object)val == (Object)null) { Plugin.Log.LogInfo((object)string.Format("RadiationAudit[{0}] sample={1} action=sampling-stopped-root-lost suffix={2}", stage, sampleIndex, "IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease")); break; } AuditImages(val, stage, sampleIndex); AuditRawImages(val, stage, sampleIndex); AuditSpriteRenderers(val, stage, sampleIndex); AuditGraphicMaterials(val, stage, sampleIndex); AuditRendererMaterials(val, stage, sampleIndex); if (sampleIndex + 1 < boundedSampleCount) { yield return (object)((!(boundedIntervalSeconds > 0f)) ? ((WaitForSeconds)null) : new WaitForSeconds(boundedIntervalSeconds)); } } _activeAuditCoroutine = null; _activeHudManager = null; } private static void AuditGraphicMaterials(Transform root, string stage, int sampleIndex) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) HashSet<int> hashSet = new HashSet<int>(); Graphic[] componentsInChildren = ((Component)root).GetComponentsInChildren<Graphic>(true); foreach (Graphic val in componentsInChildren) { Material material; try { material = val.material; } catch (Exception ex) { Plugin.Log.LogWarning((object)$"RadiationAudit[{stage}] sample={sampleIndex} component=GraphicMaterial path={BuildPath(((Component)val).transform)} active={((Component)val).gameObject.activeInHierarchy} enabled={((Behaviour)val).enabled} alpha={val.color.a:0.###} material=<error> texture=<error> error={ex.GetType().Name}:{ex.Message}"); continue; } if ((Object)(object)material != (Object)null) { hashSet.Add(((Object)material).GetInstanceID()); } } } private static void AuditRendererMaterials(Transform root, string stage, int sampleIndex) { Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Material[] sharedMaterials = componentsInChildren[i].sharedMaterials; if (sharedMaterials != null && sharedMaterials.Length != 0) { for (int j = 0; j < sharedMaterials.Length; j++) { _ = sharedMaterials[j]; } } } } private static void AuditImages(Transform root, string stage, int sampleIndex) { Image[] componentsInChildren = ((Component)root).GetComponentsInChildren<Image>(true); foreach (Image val in componentsInChildren) { Sprite obj = val.overrideSprite ?? val.sprite; if (obj != null) { _ = obj.texture; } } } private static void AuditRawImages(Transform root, string stage, int sampleIndex) { RawImage[] componentsInChildren = ((Component)root).GetComponentsInChildren<RawImage>(true); for (int i = 0; i < componentsInChildren.Length; i++) { _ = componentsInChildren[i]; } } private static void AuditSpriteRenderers(Transform root, string stage, int sampleIndex) { SpriteRenderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<SpriteRenderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { _ = componentsInChildren[i].sprite; } } private static Transform? FindWarningRoot(HUDManager hudManager) { Animator radiationGraphicAnimator = hudManager.radiationGraphicAnimator; Transform val = ((radiationGraphicAnimator != null) ? ((Component)radiationGraphicAnimator).transform : null); if (HasExpectedRootPath(val)) { return val; } Transform[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<Transform>(true); foreach (Transform val2 in componentsInChildren) { if (HasExpectedRootPath(val2)) { return val2; } } return null; } private static bool HasExpectedRootPath(Transform? transform) { if ((Object)(object)transform == (Object)null) { return false; } return BuildPath(transform).EndsWith("IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease", StringComparison.OrdinalIgnoreCase); } private static string DescribeName(Object? obj) { if (!string.IsNullOrWhiteSpace((obj != null) ? obj.name : null)) { return obj.name; } return "<null>"; } private static string BuildPath(Transform? transform) { if ((Object)(object)transform == (Object)null) { return "<null>"; } string text = ((Object)transform).name; Transform parent = transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } } internal static class RadiationWarningPlaybackService { private sealed class PlaybackState { public Transform Root { get; } public Image PanelImage { get; } public Sprite? OriginalRawOverrideSprite { get; } public Sprite? LastAppliedLocalizedSprite { get; set; } public string? LastAppliedOriginalSpriteName { get; set; } public bool CleanupCompleted { get; set; } public PlaybackState(Transform root, Image panelImage, Sprite? originalRawOverrideSprite) { Root = root; PanelImage = panelImage; OriginalRawOverrideSprite = originalRawOverrideSprite; } } private const string TextureSubfolder = "textures"; private const string WarningRootPathSuffix = "IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease"; private const string PanelObjectName = "Panel"; private const float OriginalClipDurationSeconds = 1.7166667f; private const float DefaultFollowDurationSeconds = 1.85f; private const float MaxFollowDurationSeconds = 5f; private const long MaxTextureBytes = 8388608L; private const long MaxTexturePixels = 16777216L; private static readonly Dictionary<string, string[]> OriginalSpriteFrameFiles = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase) { ["RedUIPanelGlitchB"] = FrameFileNameCandidates("RedUIPanelGlitchB", "C864CEDFD2-470F6C25C6"), ["RedUIPanelGlitchBWarning"] = FrameFileNameCandidates("RedUIPanelGlitchBWarning", "E5C5C6E52B-F9F9EA9261"), ["RedUIPanelGlitchBWarningRadiation"] = FrameFileNameCandidates("RedUIPanelGlitchBWarningRadiation", "FFCC97E055-2C688E6DC8"), ["RedUIPanelGlitchBWarningRadiationB"] = FrameFileNameCandidates("RedUIPanelGlitchBWarningRadiationB", "7D4ED37952-95F686DE01"), ["RedUIPanelGlitchBWarningRadiationC"] = FrameFileNameCandidates("RedUIPanelGlitchBWarningRadiationC", "7FBAA1AA30-9E2C036778"), ["RedUIPanelGlitchBWarningRadiationD"] = FrameFileNameCandidates("RedUIPanelGlitchBWarningRadiationD", "4877939F7B-47E20481B0") }; private static readonly Dictionary<string, Texture2D?> TextureCache = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, Texture2D?> ResolvedFrameTextureCache = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, Sprite?> SpriteCache = new Dictionary<string, Sprite>(StringComparer.OrdinalIgnoreCase); private static string? _textureDirectory; private static ConfigEntry<bool>? _enabled; private static ConfigEntry<float>? _followDurationSeconds; private static HUDManager? _activeHudManager; private static Coroutine? _activePlaybackCoroutine; private static PlaybackState? _activePlaybackState; public static void Initialize(string pluginDir, ConfigFile config) { _textureDirectory = Path.Combine(pluginDir, "textures"); _enabled = config.Bind<bool>("04 资源 - 辐射警告", "Enabled", true, "启用跟随原版动画的辐射警告贴图替换。"); _followDurationSeconds = config.Bind<float>("04 资源 - 辐射警告", "FollowDurationSeconds", 1.85f, "跟随原版 RadiationIncreaseWarning 动画并替换本地化贴图的持续时间,单位为秒。"); foreach (string key in OriginalSpriteFrameFiles.Keys) { ResolveFrameTexture(key, "Initialize"); } Plugin.Log.LogInfo((object)$"RadiationPlayback initialized mode=animator-follow clipSeconds={1.7166667f:0.###}"); } public static void OnRadiationWarningTriggered(HUDManager hudManager, string stage) { if ((Object)(object)hudManager == (Object)null) { return; } ConfigEntry<bool>? enabled = _enabled; if (enabled == null || !enabled.Value) { return; } if (_activePlaybackCoroutine != null) { Plugin.Log.LogInfo((object)("RadiationPlayback[" + stage + "] action=duplicate-trigger-restart mode=animator-follow")); CleanupActivePlayback(stage, "duplicate-restart"); } Transform val = FindWarningRoot(hudManager); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)("RadiationPlayback[" + stage + "] action=root-not-found suffix=IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease")); return; } Image val2 = FindWarningPanel(val); if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogWarning((object)("RadiationPlayback[" + stage + "] action=panel-not-found path=" + BuildPath(val) + " child=Panel")); } else if (HasAllLocalizedTextures(stage)) { PlaybackState playbackState = new PlaybackState(val, val2, ReadRawOverrideSprite(val2)); _activeHudManager = hudManager; _activePlaybackState = playbackState; _activePlaybackCoroutine = ((MonoBehaviour)hudManager).StartCoroutine(FollowAnimatorSpriteCurve(playbackState, stage)); } } public static void Shutdown() { CleanupActivePlayback("Shutdown", "shutdown"); ClearCachedAssets(); } public static void ResetForHudLifecycle(HUDManager hudManager, string stage) { if (!((Object)(object)hudManager == (Object)null) && (_activePlaybackCoroutine != null || _activePlaybackState != null)) { CleanupActivePlayback(stage, "hud-reset"); } } private static IEnumerator FollowAnimatorSpriteCurve(PlaybackState playbackState, string stage) { float followDurationSeconds = GetFollowDurationSeconds(); float endTime = Time.realtimeSinceStartup + followDurationSeconds; Plugin.Log.LogInfo((object)$"RadiationPlayback[{stage}] FollowAnimatorSpriteCurve started path={BuildPath(playbackState.Root)} panel={BuildPath(((Component)playbackState.PanelImage).transform)} duration={followDurationSeconds:0.###}"); try { while ((Object)(object)playbackState.PanelImage != (Object)null && Time.realtimeSinceStartup <= endTime) { ApplyCurrentAnimatorSpriteSubstitution(playbackState, stage); yield return null; } if ((Object)(object)playbackState.PanelImage != (Object)null) { ApplyCurrentAnimatorSpriteSubstitution(playbackState, stage); } Plugin.Log.LogInfo((object)("RadiationPlayback[" + stage + "] FollowAnimatorSpriteCurve finished path=" + BuildPath(playbackState.Root))); } finally { CleanupPlaybackState(playbackState, stage, "finally"); } } private static float GetFollowDurationSeconds() { float num = _followDurationSeconds?.Value ?? 1.85f; if (float.IsNaN(num) || float.IsInfinity(num)) { num = 1.85f; } return Mathf.Clamp(num, 1.7166667f, 5f); } private static void ApplyCurrentAnimatorSpriteSubstitution(PlaybackState playbackState, string stage) { Image panelImage = playbackState.PanelImage; Sprite sprite = panelImage.sprite; string text = NormalizeSpriteName((sprite != null) ? ((Object)sprite).name : null); if (string.IsNullOrWhiteSpace(text)) { return; } if (!OriginalSpriteFrameFiles.ContainsKey(text)) { ClearLocalizedOverrideIfOwned(playbackState, "unmapped-original"); return; } Texture2D val = ResolveFrameTexture(text, stage); if (!((Object)(object)val == (Object)null)) { Sprite val2 = ResolveLocalizedSprite(text, sprite, val); if (!((Object)(object)val2 == (Object)null) && ReadRawOverrideSprite(panelImage) != val2) { panelImage.overrideSprite = val2; ((Graphic)panelImage).SetVerticesDirty(); ((Graphic)panelImage).SetMaterialDirty(); string.Equals(playbackState.LastAppliedOriginalSpriteName, text, StringComparison.OrdinalIgnoreCase); playbackState.LastAppliedOriginalSpriteName = text; playbackState.LastAppliedLocalizedSprite = val2; } } } private static void CleanupActivePlayback(string stage, string reason) { Coroutine activePlaybackCoroutine = _activePlaybackCoroutine; HUDManager activeHudManager = _activeHudManager; PlaybackState activePlaybackState = _activePlaybackState; _activePlaybackCoroutine = null; _activeHudManager = null; _activePlaybackState = null; if (activePlaybackCoroutine != null && (Object)(object)activeHudManager != (Object)null) { ((MonoBehaviour)activeHudManager).StopCoroutine(activePlaybackCoroutine); } if (activePlaybackState != null) { CleanupPlaybackState(activePlaybackState, stage, reason); } } private static void CleanupPlaybackState(PlaybackState playbackState, string stage, string reason) { if (!playbackState.CleanupCompleted) { playbackState.CleanupCompleted = true; ClearLocalizedOverrideIfOwned(playbackState, reason); if (_activePlaybackState == playbackState) { _activePlaybackState = null; _activePlaybackCoroutine = null; _activeHudManager = null; } string text = (((Object)(object)playbackState.PanelImage != (Object)null) ? BuildPath(((Component)playbackState.PanelImage).transform) : "<null>"); Plugin.Log.LogInfo((object)("RadiationPlayback[" + stage + "] action=restore reason=" + reason + " path=" + BuildPath(playbackState.Root) + " panel=" + text)); } } private static void ClearLocalizedOverrideIfOwned(PlaybackState playbackState, string reason) { Image panelImage = playbackState.PanelImage; if (!((Object)(object)panelImage == (Object)null)) { Sprite val = ReadRawOverrideSprite(panelImage); if ((Object)(object)val != (Object)null && val != playbackState.LastAppliedLocalizedSprite && !IsCachedLocalizedSprite(val)) { Plugin.Log.LogInfo((object)("RadiationPlayback action=skip-restore reason=" + reason + " currentOverride=" + ((Object)val).name)); return; } panelImage.overrideSprite = playbackState.OriginalRawOverrideSprite; ((Graphic)panelImage).SetVerticesDirty(); ((Graphic)panelImage).SetMaterialDirty(); } } private static bool HasAllLocalizedTextures(string stage) { foreach (string key in OriginalSpriteFrameFiles.Keys) { if ((Object)(object)ResolveFrameTexture(key, stage) == (Object)null) { return false; } } return true; } private static string[] FrameFileNameCandidates(string baseName, string contentHash) { return new string[2] { baseName + " [" + contentHash + "].png", baseName + "[" + contentHash + "].png" }; } private static string? ResolveFrameFileName(string originalSpriteName, string stage) { if (!OriginalSpriteFrameFiles.TryGetValue(originalSpriteName, out string[] value)) { return null; } string[] array = value; foreach (string text in array) { if ((Object)(object)LoadTexture(text) != (Object)null) { return text; } } Plugin.Log.LogWarning((object)("RadiationPlayback[" + stage + "] action=incomplete-frame-set original=" + originalSpriteName + " candidates=" + string.Join("|", value) + " nativeFallback=true")); return null; } private static Texture2D? ResolveFrameTexture(string originalSpriteName, string stage) { if (ResolvedFrameTextureCache.TryGetValue(originalSpriteName, out Texture2D value)) { return value; } string text = ResolveFrameFileName(originalSpriteName, stage); if (text == null) { ResolvedFrameTextureCache[originalSpriteName] = null; return null; } Texture2D val = LoadTexture(text); ResolvedFrameTextureCache[originalSpriteName] = val; return val; } private static Image? FindWarningPanel(Transform root) { Transform obj = root.Find("Panel"); Image val = ((obj != null) ? ((Component)obj).GetComponent<Image>() : null); if ((Object)(object)val != (Object)null) { return val; } Image[] componentsInChildren = ((Component)root).GetComponentsInChildren<Image>(true); foreach (Image val2 in componentsInChildren) { if (string.Equals(((Object)val2).name, "Panel", StringComparison.OrdinalIgnoreCase)) { return val2; } } return null; } private static Sprite? ResolveLocalizedSprite(string originalSpriteName, Sprite? originalSprite, Texture2D texture) { string key = BuildSpriteCacheKey(originalSpriteName, originalSprite, texture); if (SpriteCache.TryGetValue(key, out Sprite value)) { return value; } Sprite val = CreateLocalizedSprite(texture, originalSprite); SpriteCache[key] = val; return val; } private static string BuildSpriteCacheKey(string originalSpriteName, Sprite? originalSprite, Texture2D texture) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)originalSprite == (Object)null) { return originalSpriteName + ":" + ((Object)texture).name + ":fallback"; } Rect rect = originalSprite.rect; return $"{originalSpriteName}:{((Object)texture).name}:{((Rect)(ref rect)).x}:{((Rect)(ref rect)).y}:{((Rect)(ref rect)).width}:{((Rect)(ref rect)).height}:{originalSprite.pivot.x}:{originalSprite.pivot.y}:{originalSprite.pixelsPerUnit}"; } private static Sprite? CreateLocalizedSprite(Texture2D texture, Sprite? template) { //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)template != (Object)null) { Rect rect = template.rect; if (((Rect)(ref rect)).width > 0f && ((Rect)(ref rect)).height > 0f && ((Rect)(ref rect)).xMax <= (float)((Texture)texture).width && ((Rect)(ref rect)).yMax <= (float)((Texture)texture).height) { Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(template.pivot.x / ((Rect)(ref rect)).width, template.pivot.y / ((Rect)(ref rect)).height); Sprite obj = Sprite.Create(texture, rect, val, template.pixelsPerUnit, 0u, (SpriteMeshType)0, template.border); ((Object)obj).name = ((Object)texture).name; return obj; } } Sprite obj2 = Sprite.Create(texture, new Rect(0f, 0f, (float)((Texture)texture).width, (float)((Texture)texture).height), new Vector2(0.5f, 0.5f), 100f); ((Object)obj2).name = ((Object)texture).name; return obj2; } catch (Exception ex) { Plugin.Log.LogWarning((object)("RadiationPlayback[Sprite] action=create-failed texture=" + ((Object)texture).name + " error=" + ex.GetType().Name + ": " + ex.Message)); return null; } } private static bool IsCachedLocalizedSprite(Sprite sprite) { foreach (Sprite value in SpriteCache.Values) { if (value == sprite) { return true; } } return false; } private static Sprite? ReadRawOverrideSprite(Image image) { try { return image.m_OverrideSprite; } catch (Exception ex) { Plugin.Log.LogWarning((object)("RadiationPlayback[Image] action=read-raw-override-failed error=" + ex.GetType().Name + ": " + ex.Message)); return null; } } private static Transform? FindWarningRoot(HUDManager hudManager) { Animator radiationGraphicAnimator = hudManager.radiationGraphicAnimator; Transform val = ((radiationGraphicAnimator != null) ? ((Component)radiationGraphicAnimator).transform : null); if (HasExpectedRootPath(val)) { return val; } Transform[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<Transform>(true); foreach (Transform val2 in componentsInChildren) { if (HasExpectedRootPath(val2)) { return val2; } } return null; } private static bool HasExpectedRootPath(Transform? transform) { if ((Object)(object)transform == (Object)null) { return false; } return BuildPath(transform).EndsWith("IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease", StringComparison.OrdinalIgnoreCase); } private static Texture2D? LoadTexture(string fileName) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown if (TextureCache.TryGetValue(fileName, out Texture2D value)) { return value; } if (string.IsNullOrWhiteSpace(_textureDirectory)) { return null; } string text = Path.Combine(_textureDirectory, fileName); if (!File.Exists(text)) { TextureCache[fileName] = null; return null; } Texture2D val = null; try { FileInfo fileInfo = new FileInfo(text); if (fileInfo.Length > 8388608) { Plugin.Log.LogWarning((object)$"RadiationPlayback[Texture] action=file-too-large file={fileName} bytes={fileInfo.Length} max={8388608L}"); TextureCache[fileName] = null; return null; } byte[] array = File.ReadAllBytes(text); val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, array, false)) { Object.Destroy((Object)(object)val); val = null; TextureCache[fileName] = null; return null; } if (IsDecodedTextureTooLarge(val)) { Plugin.Log.LogWarning((object)("RadiationPlayback[Texture] action=decoded-texture-too-large file=" + fileName + " " + $"width={((Texture)val).width} height={((Texture)val).height} pixels={(long)((Texture)val).width * (long)((Texture)val).height} maxPixels={16777216L}")); Object.Destroy((Object)(object)val); val = null; TextureCache[fileName] = null; return null; } ((Object)val).name = Path.GetFileNameWithoutExtension(fileName); ((Texture)val).wrapMode = (TextureWrapMode)1; TextureCache[fileName] = val; Texture2D result = val; val = null; return result; } catch (Exception ex) { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } Plugin.Log.LogWarning((object)("RadiationPlayback[Texture] action=load-failed file=" + fileName + " error=" + ex.GetType().Name + ": " + ex.Message)); TextureCache[fileName] = null; return null; } } private static void ClearCachedAssets() { HashSet<int> hashSet = new HashSet<int>(); foreach (Sprite value in SpriteCache.Values) { if (!((Object)(object)value == (Object)null) && hashSet.Add(((Object)value).GetInstanceID())) { Object.Destroy((Object)(object)value); } } HashSet<int> destroyedTextureIds = new HashSet<int>(); foreach (Texture2D value2 in TextureCache.Values) { DestroyTextureOnce(value2, destroyedTextureIds); } foreach (Texture2D value3 in ResolvedFrameTextureCache.Values) { DestroyTextureOnce(value3, destroyedTextureIds); } SpriteCache.Clear(); TextureCache.Clear(); ResolvedFrameTextureCache.Clear(); } private static bool IsDecodedTextureTooLarge(Texture2D texture) { if (((Texture)texture).width <= 0 || ((Texture)texture).height <= 0) { return true; } return (long)((Texture)texture).width * (long)((Texture)texture).height > 16777216; } private static void DestroyTextureOnce(Texture2D? texture, HashSet<int> destroyedTextureIds) { if (!((Object)(object)texture == (Object)null) && destroyedTextureIds.Add(((Object)texture).GetInstanceID())) { Object.Destroy((Object)(object)texture); } } private static string NormalizeSpriteName(string? spriteName) { if (string.IsNullOrWhiteSpace(spriteName)) { return string.Empty; } string text = spriteName.Trim(); if (text.EndsWith("(Clone)", StringComparison.Ordinal)) { text = text.Substring(0, text.Length - "(Clone)".Length).TrimEnd(); } return text; } private static string BuildPath(Transform? transform) { if ((Object)(object)transform == (Object)null) { return "<null>"; } string text = ((Object)transform).name; Transform parent = transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } } internal static class StickyNoteLocalizationService { private sealed class RendererState { public MeshRenderer Renderer { get; } public Material[] OriginalMaterials { get; } public List<Material> LocalizedMaterials { get; } public RendererState(MeshRenderer renderer, Material[] originalMaterials, List<Material> localizedMaterials) { Renderer = renderer; OriginalMaterials = originalMaterials; LocalizedMaterials = localizedMaterials; } } private const string ItemName = "StickyNoteItem"; private const string MaterialName = "StickyNoteMaterial"; private const string TextureFileName = "StickyNoteLocalized.png"; private const int MaxTextureFileBytes = 8388608; private const int ExpectedTextureWidth = 1024; private const int ExpectedTextureHeight = 512; private static readonly Dictionary<int, RendererState> RendererStates = new Dictionary<int, RendererState>(); private static string _texturePath = string.Empty; private static Texture2D? _localizedTexture; private static bool _initialized; private static bool _loadAttempted; public static void Initialize(string pluginDir) { _texturePath = Path.Combine(pluginDir, "textures", "StickyNoteLocalized.png"); _initialized = true; _loadAttempted = false; } public static void Apply(GrabbableObject? grabbableObject) { if (_initialized && !Plugin.IsRuntimeShuttingDown && !((Object)(object)grabbableObject == (Object)null) && IsStickyNote(((Object)((Component)grabbableObject).gameObject).name) && EnsureTextureLoaded()) { PruneDestroyedRendererStates(); MeshRenderer[] componentsInChildren = ((Component)grabbableObject).GetComponentsInChildren<MeshRenderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { ApplyToRenderer(componentsInChildren[i]); } } } public static void Shutdown() { foreach (RendererState value in RendererStates.Values) { try { if ((Object)(object)value.Renderer != (Object)null) { ((Renderer)value.Renderer).sharedMaterials = value.OriginalMaterials; } } catch { } foreach (Material localizedMaterial in value.LocalizedMaterials) { DestroyUnityObject((Object?)(object)localizedMaterial); } } RendererStates.Clear(); DestroyUnityObject((Object?)(object)_localizedTexture); _localizedTexture = null; _texturePath = string.Empty; _initialized = false; _loadAttempted = false; } private static bool IsStickyNote(string? objectName) { if (!string.IsNullOrEmpty(objectName)) { return objectName.StartsWith("StickyNoteItem", StringComparison.OrdinalIgnoreCase); } return false; } private static bool EnsureTextureLoaded() { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown if (_loadAttempted) { return (Object)(object)_localizedTexture != (Object)null; } _loadAttempted = true; Texture2D val = null; try { FileInfo fileInfo = new FileInfo(_texturePath); if (!fileInfo.Exists || fileInfo.Length <= 0 || fileInfo.Length > 8388608) { Plugin.Log.LogWarning((object)"Sticky-note localization texture is missing or invalid: StickyNoteLocalized.png"); return false; } val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, File.ReadAllBytes(_texturePath), true) || ((Texture)val).width != 1024 || ((Texture)val).height != 512) { Plugin.Log.LogWarning((object)($"Sticky-note localization texture must be {1024}x{512}: " + $"{((Texture)val).width}x{((Texture)val).height}")); DestroyUnityObject((Object?)(object)val); return false; } ((Object)val).name = "StickyNoteTex (zh-CN)"; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; _localizedTexture = val; return true; } catch (Exception ex) { DestroyUnityObject((Object?)(object)val); Plugin.Log.LogWarning((object)("Sticky-note localization texture load failed: " + ex.GetType().Name + ": " + ex.Message)); return false; } } private static void ApplyToRenderer(MeshRenderer? renderer) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown if ((Object)(object)renderer == (Object)null || RendererStates.ContainsKey(((Object)renderer).GetInstanceID()) || (Object)(object)_localizedTexture == (Object)null) { return; } Material[] sharedMaterials = ((Renderer)renderer).sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0) { return; } Material[] array = (Material[])sharedMaterials.Clone(); List<Material> list = new List<Material>(); for (int i = 0; i < sharedMaterials.Length; i++) { Material val = sharedMaterials[i]; if (!((Object)(object)val == (Object)null) && string.Equals(NormalizeMaterialName(((Object)val).name), "StickyNoteMaterial", StringComparison.OrdinalIgnoreCase)) { Material val2 = new Material(val) { name = "StickyNoteMaterial (zh-CN)" }; SetTexture(val2, _localizedTexture); array[i] = val2; list.Add(val2); } } if (list.Count != 0) { ((Renderer)renderer).sharedMaterials = array; RendererStates[((Object)renderer).GetInstanceID()] = new RendererState(renderer, sharedMaterials, list); } } private static void PruneDestroyedRendererStates() { List<int> list = null; foreach (KeyValuePair<int, RendererState> rendererState in RendererStates) { if ((Object)(object)rendererState.Value.Renderer != (Object)null) { continue; } if (list == null) { list = new List<int>(); } list.Add(rendererState.Key); foreach (Material localizedMaterial in rendererState.Value.LocalizedMaterials) { DestroyUnityObject((Object?)(object)localizedMaterial); } } if (list == null) { return; } foreach (int item in list) { RendererStates.Remove(item); } } private static string NormalizeMaterialName(string? materialName) { if (string.IsNullOrWhiteSpace(materialName)) { return string.Empty; } string text = materialName.Trim(); string[] array = new string[2] { " (Instance)", " (zh-CN)" }; foreach (string text2 in array) { if (text.EndsWith(text2, StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - text2.Length); } } return text; } private static void SetTexture(Material material, Texture2D texture) { if (material.HasProperty("_BaseColorMap")) { material.SetTexture("_BaseColorMap", (Texture)(object)texture); } if (material.HasProperty("_MainTex")) { material.SetTexture("_MainTex", (Texture)(object)texture); } material.mainTexture = (Texture)(object)texture; } private static void DestroyUnityObject(Object? value) { if (!(value == (Object)null)) { if (Application.isPlaying) { Object.Destroy(value); } else { Object.DestroyImmediate(value); } } } } internal static class ExternalEnglishCompatibilityService { private const int MaxSourceLength = 512; private const int RuntimeCacheLimit = 4096; private const string DiscountAlertNoDiscountLocalizedText = "暂无折扣\n明天再来查看"; private static readonly Dictionary<string, string> ExactEntries = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { ["Emote Menu"] = "动作菜单", ["Random Emote"] = "随机动作", ["Perform Random Emote"] = "执行随机动作", ["Zoom"] = "缩放", ["Freeze"] = "冻结", ["Swap Page"] = "切换页面", ["Favorite Emote"] = "收藏动作", ["Set Quick Emote"] = "设置快捷动作", ["Mute emote audio"] = "静音动作音频", ["Only perform emote"] = "仅播放动作", ["no audio"] = "无音频", ["(no audio)"] = "(无音频)", ["First person emotes"] = "第一人称动作", ["Move while emoting"] = "做动作时可移动", ["experimental"] = "实验性", ["(experimental)"] = "(实验性)", ["DMCA-free mode"] = "DMCA 安全模式", ["Emote volume"] = "动作音量", ["Tell autopilot ship to leave early"] = "命令自动驾驶飞船提前离开", ["Spectate Previous Player"] = "切换上一名玩家", ["Open Admin UI"] = "打开管理界面", ["Copy Lobby ID"] = "复制房间 ID", ["Enter tag or id..."] = "输入标签或 ID...", ["Enter tag or ip..."] = "输入标签或 IP...", ["Search Mods"] = "搜索模组", ["Search Configs"] = "搜索配置", ["Close"] = "关闭", ["Apply"] = "应用", ["Delete"] = "删除", ["Go back"] = "返回", ["OK"] = "确认", ["Server Password"] = "房间密码", ["Validate Steam Sessions"] = "验证 Steam 会话", ["Max Players"] = "最大人数", ["Server Access"] = "房间权限", ["Invite-only"] = "仅限邀请", ["Friends-only"] = "仅限好友", ["Server Tag"] = "房间标签", ["New File"] = "新存档", ["Players"] = "玩家", ["Today's discounts"] = "今日折扣", ["AVAILABLE NOW!"] = "现已发售!", ["CURES CANCER!"] = "治愈癌症!", ["NO WAY!"] = "不会吧!", ["LIMITED TIME ONLY!"] = "限时供应!", ["GET YOURS TODAY!"] = "今天就来领取!", ["Cannot return from storage while the ship is landing or leaving."] = "飞船正在降落或离开时,无法从仓储取回物品。", ["Infection"] = "感染", ["HUDScale"] = "HUD 缩放", ["HideHealthbarAutomatically"] = "自动隐藏生命条", ["HealthbarHideDelay"] = "生命条隐藏延迟", ["FlashlightBattery"] = "手电筒电量显示", ["DetailedStamina"] = "详细耐力", ["DisplayTimeLeft"] = "显示剩余时间", ["HidePlanetInfo"] = "隐藏星球信息", ["PercentageOnly"] = "仅百分比", ["The size of the HUD."] = "HUD 界面的缩放大小。", ["Should the healthbar be hidden after not taking damage for a while."] = "一段时间未受伤后是否自动隐藏生命条。", ["The amount of time before the healthbar starts fading away."] = "生命条开始淡出前的等待时间。", ["How the flashlight battery is displayed whilst unequipped."] = "未装备手电筒时如何显示其电量。", ["Disabled - Flashlight battery will not be displayed."] = "禁用 - 不显示手电筒电量。", ["Vanilla - Flashlight battery will be displayed when you don't have a battery-using item equipped."] = "原版 - 未装备其他耗电物品时显示手电筒电量。", ["Separate - Flashlight battery will be displayed using a dedicated panel. (recommended)"] = "独立 - 使用独立面板显示手电筒电量。(推荐)", ["What the stamina text should display."] = "耐力文字要显示的内容。", ["Disabled - The stamina text will be hidden."] = "禁用 - 隐藏耐力文字。", ["PercentageOnly - Only the percentage will be displayed. (recommended)"] = "仅百分比 - 只显示百分比。(推荐)", ["Full - Both percentage and rate of gain/loss will be displayed."] = "完整 - 同时显示百分比与增减速率。", ["Should the uses/time left for a battery-using item be displayed."] = "是否显示耗电物品的剩余使用次数或时间。", ["Should planet info be hidden. If modifying from an in-game menu, this requires you to rejoin the game."] = "是否隐藏星球信息。若在游戏内菜单修改,需要重新加入游戏。", ["Target on ship"] = "飞船上的目标", ["Signal lost"] = "信号丢失", ["Antenna stored"] = "天线已存放", ["PERFORMANCE REPORT"] = "绩效报告", ["NO SURVIVORS"] = "无人生还", ["NOTES"] = "备注", ["DECEASED"] = "死亡", ["MISSING"] = "失踪", ["Collected"] = "已收集", ["Grade"] = "评级", ["Rating"] = "评级", ["Lost 100% scrap"] = "损失 100% 废料", ["* Unknown"] = "* 未知原因", ["* Bludgeoning"] = "* 钝击", ["* Gravity"] = "* 坠落", ["* Blast"] = "* 爆炸", ["* Strangulation"] = "* 勒杀", ["* Suffocation"] = "* 窒息", ["* Mauling"] = "* 撕咬", ["* Gunshots"] = "* 枪击", ["* Crushing"] = "* 碾压", ["* Drowning"] = "* 溺水", ["* Abandoned"] = "* 遭遗弃", ["* Electrocution"] = "* 触电", ["* Kicking"] = "* 踢击", ["* Burning"] = "* 燃烧", ["* Stabbing"] = "* 刺伤", ["* Fan"] = "* 风扇", ["* Inertia"] = "* 惯性撞击", ["* Snipping"] = "* 剪伤", ["* Scratching"] = "* 抓伤", ["* The laziest employee."] = "* 最懒惰的员工", ["The laziest employee."] = "最懒惰的员工", ["* Most profitable"] = "* 最会赚钱的员工", ["Most profitable"] = "最会赚钱的员工", ["No items were found"] = "未找到可售物品", ["Error selling items"] = "售卖物品时出错", ["You can't afford to sell that amount"] = "可售物品不足,无法卖出该金额", ["Successfully emptied temporary blacklist"] = "已清空临时黑名单", ["Successfully emptied temporary priority set"] = "已清空临时优先列表", ["Item Blacklist"] = "物品黑名单", ["Priority Items"] = "优先售卖物品", ["Flag Prefix"] = "标志前缀", ["Items to never sell by internal name (comma-separated)"] = "永不出售的物品内部名称(逗号分隔)", ["Items which are prioritized when selling"] = "出售时优先选择的物品", ["QUICKSELL"] = "快速出售", ["SELL RESULTS"] = "出售结果", ["Command"] = "命令", ["Invalid command"] = "命令无效", ["HELP PAGE"] = "帮助页面", ["ALL HELP PAGE"] = "全部出售帮助页面", ["AMOUNT HELP PAGE"] = "金额帮助页面", ["BLACKLIST HELP PAGE"] = "黑名单帮助页面", ["ITEM HELP PAGE"] = "物品帮助页面", ["PRIORITY HELP PAGE"] = "优先列表帮助页面", ["QUOTA HELP PAGE"] = "配额帮助页面", ["FLAG HELP PAGE"] = "标志帮助页面", ["OVERTIME HELP PAGE"] = "加班奖励帮助页面", ["PAGES HELP PAGE"] = "页面帮助", ["-A HELP PAGE"] = "-A 标志帮助", ["-E HELP PAGE"] = "-E 标志帮助", ["-N HELP PAGE"] = "-N 标志帮助", ["-O HELP PAGE"] = "-O 标志帮助", ["-P HELP PAGE"] = "-P 标志帮助", ["Combining flags"] = "组合标志", ["\"help\" to open this page or a specific help page"] = "\"help\":打开此页或指定帮助页", ["\"quota\" to sell exactly for quota"] = "\"quota\":按配额精确出售", ["\"all\" to sell all unfiltered scrap available"] = "\"all\":出售所有未过滤的可用废料", ["\"item\" to sell all items like the one you are holding or the one you specified"] = "\"item\":出售所持有或指定的同类物品", ["\"-a\" to ignore blacklist (used with quota, all, <amount>)"] = "\"-a\":忽略黑名单(用于 quota、all、<amount>)", ["\"-o\" to sell accounting for overtime (used with <amount>)"] = "\"-o\":计入加班奖励后出售(用于 <amount>)", ["The symbol which is used as prefix in flags (aka \"-\" in \"-e\")"] = "命令标志使用的前缀符号(例如 -e 中的 -)", ["Command variations"] = "命令用法", ["The sell command was initiated"] = "售卖命令已开始", ["The sell command completed it's job, terminating"] = "售卖命令已完成,正在结束", ["The overtime command was initiated"] = "加班奖励命令已开始", ["Use \"/sell help flags\" to see info on important flags"] = "输入 \"/sell help flags\" 查看重要标志说明", ["Use \"/sell help <flag>\" to see info on specific flag"] = "输入 \"/sell help <flag>\" 查看指定标志说明", ["Use \"/sell help <variation>\" to see info on specific command"] = "输入 \"/sell help <variation>\" 查看指定命令说明", ["Use \"/sell help pages\" to see info on all pages"] = "输入 \"/sell help pages\" 查看所有帮助页面", ["Use \"/sell help overtime\" to see info on the \"/ot\" (overtime command)"] = "输入 \"/sell help overtime\" 查看 \"/ot\"(加班奖励命令)说明", ["The value must be positive"] = "数值必须为正", ["Cannot find terminal!"] = "未找到终端!", ["Cannot find terminal?!"] = "未找到终端!", ["Quota is already fulfilled"] = "利润指标已完成", ["No item is held and no item was specified"] = "未持有物品,也未指定物品", ["No items on the desk"] = "柜台上没有物品", ["Door already open"] = "门已经打开", ["Wrong item name"] = "物品名称错误", ["No page with this name exists"] = "不存在这个帮助页面", ["Failed to evalute expression"] = "表达式计算失败", ["No items to sort"] = "没有可整理的物品", ["No items on the ship"] = "飞船上没有物品", ["Sorting all items..."] = "正在整理所有物品...", ["Finished sorting items"] = "物品整理完成", ["Invalid arguments"] = "参数无效", ["Invalid item name"] = "物品名称无效", ["Error running command"] = "运行命令时出错", ["Error getting position"] = "获取位置时出错", ["Automatic sorting failed"] = "自动整理失败", ["Error while autosorting items"] = "自动整理物品时出错", ["Automatic sorting failed due to an internal error, check the log for details"] = "自动整理因内部错误失败,请查看日志", ["Couldn't find ship"] = "未找到飞船", ["Raycast unsuccessful"] = "射线检测未成功", ["The ship must be in orbit"] = "飞船必须处于轨道中", ["You can't pick anything up while sorting items."] = "整理物品时无法拾取任何物品。", ["Toggles automatic item sorting when leaving a planet"] = "切换离开星球时是否自动整理物品", ["* The most paranoid employee."] = "* 最多疑的员工", ["The most paranoid employee."] = "最多疑的员工", ["* Sustained the most injuries."] = "* 受伤最多", ["Sustained the most injuries."] = "受伤最多", ["* Dislikes smoke."] = "* 讨厌烟雾", ["Dislikes smoke."] = "讨厌烟雾", ["* The least likely to die next time."] = "* 下次最不可能死亡", ["The least likely to die next time."] = "下次最不可能死亡", ["* I think this one's a serial killer."] = "* 我觉得这人像个连环杀手", ["I think this one's a serial killer."] = "我觉得这人像个连环杀手", ["* Go! Freaky on a Friday night."] = "* 周五夜晚尽情疯狂", ["Go! Freaky on a Friday night."] = "周五夜晚尽情疯狂" }; private static readonly Dictionary<string, string> KeyTokenEntries = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { ["Scroll Mouse"] = "滚动鼠标", ["k