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.1.3
BepInEx/plugins/V81TestChn/V81TestChn.dll
Decompiled 2 weeks 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.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.LowLevel; using UnityEngine.UI; using UnityEngine.Video; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [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+1facf15d35766b81c56e6948d20291ab6fc9c82c")] [assembly: AssemblyProduct("V81TestChn")] [assembly: AssemblyTitle("V81TestChn")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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; } } [CompilerGenerated] private sealed class <WaitForFixedSceneLabels>d__49 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string stage; private float <elapsed>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForFixedSceneLabels>d__49(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <elapsed>5__2 = 0f; break; case 1: <>1__state = -1; <elapsed>5__2 += 0.5f; break; } if (<elapsed>5__2 < 8f) { if (SyncFixedSceneLabelsInRelayScene(stage + ".relay-scene") > 0) { _fixedSceneLabelWatcher = null; _fixedSceneLabelWatcherOwner = null; return false; } <>2__current = (object)new WaitForSeconds(0.5f); <>1__state = 1; return true; } if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogWarning((object)("NativeRelay[" + stage + ".relay-scene] target=FixedSceneLabel action=timeout")); } _fixedSceneLabelWatcher = null; _fixedSceneLabelWatcherOwner = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <WaitForSystemOnlineTitle>d__48 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public HUDManager hudManager; public string stage; private float <elapsed>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForSystemOnlineTitle>d__48(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <elapsed>5__2 = 0f; break; case 1: <>1__state = -1; <elapsed>5__2 += 0.25f; break; } if ((Object)(object)hudManager != (Object)null && <elapsed>5__2 < 12f) { TMP_Text text = FindSystemOnlineTitleByHudPath(hudManager); if (IsExactSystemOnlineTitle(text)) { ApplySystemOnlineNativeTranslation(text, stage + ".watcher"); _systemOnlineWatcher = null; _systemOnlineWatcherOwner = null; return false; } <>2__current = (object)new WaitForSeconds(0.25f); <>1__state = 1; return true; } if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogWarning((object)("NativeRelay[" + stage + ".watcher] target=SystemOnline action=timeout")); } _systemOnlineWatcher = null; _systemOnlineWatcherOwner = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } 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 Dictionary<int, CachedNativeTextRole> NativeTextRoleCache = new Dictionary<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)); } } [IteratorStateMachine(typeof(<WaitForSystemOnlineTitle>d__48))] private static IEnumerator WaitForSystemOnlineTitle(HUDManager hudManager, string stage) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForSystemOnlineTitle>d__48(0) { hudManager = hudManager, stage = stage }; } [IteratorStateMachine(typeof(<WaitForFixedSceneLabels>d__49))] private static IEnumerator WaitForFixedSceneLabels(string stage) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForFixedSceneLabels>d__49(0) { stage = stage }; } 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(); if (NativeTextRoleCache.Count < RuntimePerformanceSettings.ComponentTextCacheLimit || NativeTextRoleCache.ContainsKey(instanceID)) { NativeTextRoleCache[instanceID] = new CachedNativeTextRole(GetParentInstanceId(text.transform), role); } } 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 RadiationWarningAuditService { [CompilerGenerated] private sealed class <SampleWarningSubtree>d__9 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public int sampleCount; public float sampleIntervalSeconds; public HUDManager hudManager; public string stage; private int <boundedSampleCount>5__2; private float <boundedIntervalSeconds>5__3; private int <sampleIndex>5__4; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <SampleWarningSubtree>d__9(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0124: Unknown result type (might be due to invalid IL or missing references) int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_013e; } <>1__state = -1; <boundedSampleCount>5__2 = Mathf.Max(1, sampleCount); <boundedIntervalSeconds>5__3 = Mathf.Max(0f, sampleIntervalSeconds); <sampleIndex>5__4 = 0; goto IL_014e; IL_013e: <sampleIndex>5__4++; goto IL_014e; IL_014e: if (<sampleIndex>5__4 < <boundedSampleCount>5__2 && !((Object)(object)hudManager == (Object)null)) { Transform val = FindWarningRoot(hudManager); if (!((Object)(object)val == (Object)null)) { AuditImages(val, stage, <sampleIndex>5__4); AuditRawImages(val, stage, <sampleIndex>5__4); AuditSpriteRenderers(val, stage, <sampleIndex>5__4); AuditGraphicMaterials(val, stage, <sampleIndex>5__4); AuditRendererMaterials(val, stage, <sampleIndex>5__4); if (<sampleIndex>5__4 + 1 < <boundedSampleCount>5__2) { object obj = this; obj = (object)((!(<boundedIntervalSeconds>5__3 > 0f)) ? ((WaitForSeconds)null) : new WaitForSeconds(<boundedIntervalSeconds>5__3)); ((<SampleWarningSubtree>d__9)obj).<>2__current = obj; <>1__state = 1; return true; } goto IL_013e; } Plugin.Log.LogInfo((object)string.Format("RadiationAudit[{0}] sample={1} action=sampling-stopped-root-lost suffix={2}", stage, <sampleIndex>5__4, "IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease")); } _activeAuditCoroutine = null; _activeHudManager = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } 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)); } [IteratorStateMachine(typeof(<SampleWarningSubtree>d__9))] private static IEnumerator SampleWarningSubtree(HUDManager hudManager, string stage, int sampleCount, float sampleIntervalSeconds) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <SampleWarningSubtree>d__9(0) { hudManager = hudManager, stage = stage, sampleCount = sampleCount, sampleIntervalSeconds = sampleIntervalSeconds }; } 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; } } [CompilerGenerated] private sealed class <FollowAnimatorSpriteCurve>d__24 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string stage; public PlaybackState playbackState; private float <endTime>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <FollowAnimatorSpriteCurve>d__24(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: { <>1__state = -1; float followDurationSeconds = GetFollowDurationSeconds(); <endTime>5__2 = Time.realtimeSinceStartup + followDurationSeconds; Plugin.Log.LogInfo((object)$"RadiationPlayback[{stage}] FollowAnimatorSpriteCurve started path={BuildPath(playbackState.Root)} panel={BuildPath(((Component)playbackState.PanelImage).transform)} duration={followDurationSeconds:0.###}"); <>1__state = -3; break; } case 1: <>1__state = -3; break; } if ((Object)(object)playbackState.PanelImage != (Object)null && Time.realtimeSinceStartup <= <endTime>5__2) { ApplyCurrentAnimatorSpriteSubstitution(playbackState, stage); <>2__current = null; <>1__state = 1; return true; } if ((Object)(object)playbackState.PanelImage != (Object)null) { ApplyCurrentAnimatorSpriteSubstitution(playbackState, stage); } Plugin.Log.LogInfo((object)("RadiationPlayback[" + stage + "] FollowAnimatorSpriteCurve finished path=" + BuildPath(playbackState.Root))); <>m__Finally1(); return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; CleanupPlaybackState(playbackState, stage, "finally"); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } 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 readonly FieldInfo? RawOverrideSpriteField = typeof(Image).GetField("m_OverrideSprite", BindingFlags.Instance | BindingFlags.NonPublic); 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"); } } [IteratorStateMachine(typeof(<FollowAnimatorSpriteCurve>d__24))] private static IEnumerator FollowAnimatorSpriteCurve(PlaybackState playbackState, string stage) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <FollowAnimatorSpriteCurve>d__24(0) { playbackState = playbackState, stage = stage }; } 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) { if (RawOverrideSpriteField == null) { return null; } try { object? value = RawOverrideSpriteField.GetValue(image); return (Sprite?)((value is Sprite) ? value : null); } 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 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"] = "随机动作", ["Zoom"] = "缩放", ["Freeze"] = "冻结", ["Swap Page"] = "切换页面", ["Favorite Emote"] = "收藏动作", ["Set Quick Emote"] = "设置快捷动作", ["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"] = "今日折扣", ["Target on ship"] = "飞船上的目标", ["Signal lost"] = "信号丢失", ["Antenna stored"] = "天线已存放", ["PERFORMANCE REPORT"] = "绩效报告", ["NO SURVIVORS"] = "无人生还", ["NOTES"] = "备注", ["DECEASED"] = "死亡", ["Collected"] = "已收集", ["Grade"] = "评级", ["Rating"] = "评级", ["Lost 100% scrap"] = "损失 100% 废料", ["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"] = "滚动鼠标", ["keybind #"] = "按键绑定 #" }; private static readonly string[] SellingNamedMarkers = new string[3] { " item(s) named \"", " items named \"", " item named \"" }; private static readonly string[] SellingCountMarkers = new string[3] { " item(s) with a total value of ", " items with a total value of ", " item with a total value of " }; private static readonly Dictionary<string, bool> CanHandleCache = new Dictionary<string, bool>(4096, StringComparer.Ordinal); private static readonly Dictionary<string, string?> TranslationCache = new Dictionary<string, string>(4096, StringComparer.Ordinal); public static void ClearRuntimeCaches() { CanHandleCache.Clear(); TranslationCache.Clear(); } public static bool CanHandleCheap(string? source) { if (string.IsNullOrWhiteSpace(source) || source.Length > 512) { return false; } if (CanHandleCache.TryGetValue(source, out var value)) { return value; } bool result = CanHandleCheapCore(source); CacheCanHandleResult(source, result); return result; } public static bool MightTranslateStatusLikeTextCheap(string? source) { if (string.IsNullOrWhiteSpace(source) || source.Length > 512) { return false; } if (ContainsLineBreak(source)) { int num = 0; while (num <= source.Length) { int num2 = source.IndexOf('\n', num); int num3 = ((num2 < 0) ? source.Length : num2); ReadOnlySpan<char> line = source.AsSpan(num, num3 - num); if (line.Length > 0) { if (line[line.Length - 1] == '\r') { line = line.Slice(0, line.Length - 1); } } if (LineMightNeedExternalCompatibilityCheck(line) && MightTranslateStatusLikeSingleLineCheap(line.ToString())) { return true; } if (num2 < 0) { break; } num = num2 + 1; } return false; } return MightTranslateStatusLikeSingleLineCheap(source); } public static bool MightTranslateStatusLikeLabelCheap(string? label) { if (string.IsNullOrWhiteSpace(label) || label.Length > 64) { return false; } string text = StripMenuSelectionPrefix(StripLeadingSimpleRichTextTags(StripOuterSimpleRichTextEnvelope(label)).Trim()); if (text.EndsWith(":", StringComparison.Ordinal)) { string text2 = text; text = text2.Substring(0, text2.Length - 1).TrimEnd(); } return ExactEntries.ContainsKey(text); } private static bool CanHandleCheapCore(string source) { if (MightContainDeleteFilePrompt(source) && LooksLikeDeleteFilePrompt(source)) { return true; } if (MightContainDiscountAlertNoDiscountText(source) && LooksLikeDiscountAlertNoDiscountText(source)) { return true; } if (ContainsLineBreak(source)) { return CanHandleAnyLineCheap(source); } if (ContainsCjk(source)) { return false; } return CanHandleSingleLineCheap(source); } private static bool CanHandleAnyLineCheap(string source) { int num = 0; while (num <= source.Length) { int num2 = source.IndexOf('\n', num); int num3 = ((num2 < 0) ? source.Length : num2); ReadOnlySpan<char> line = source.AsSpan(num, num3 - num); if (line.Length > 0) { if (line[line.Length - 1] == '\r') { line = line.Slice(0, line.Length - 1); } } if (LineMightNeedExternalCompatibilityCheck(line) && CanHandleSingleLineCheap(line.ToString())) { return true; } if (num2 < 0) { break; } num = num2 + 1; } return false; } private static bool LineMightNeedExternalCompatibilityCheck(ReadOnlySpan<char> line) { line = line.Trim(); if (line.Length == 0 || line.Length > 512) { return false; } bool result = false; ReadOnlySpan<char> readOnlySpan = line; for (int i = 0; i < readOnlySpan.Length; i++) { char ch = readOnlySpan[i]; if (IsCjk(ch)) { return false; } if (IsAsciiLetter(ch)) { result = true; } } return result; } private static bool MightContainDeleteFilePrompt(string source) { if (source.IndexOf("delete", StringComparison.OrdinalIgnoreCase) >= 0) { return source.IndexOf("file", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static bool MightContainDiscountAlertNoDiscountText(string source) { if (source.IndexOf("None", StringComparison.OrdinalIgnoreCase) >= 0) { return source.IndexOf("tomorrow", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static bool MightContainDiscountLine(string source) { if (source.IndexOf('$') >= 0) { if (source.IndexOf(" off!", StringComparison.OrdinalIgnoreCase) < 0) { return source.IndexOf(" up!", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } return false; } private static bool MightContainCompositeExternalUiShape(string text) { foreach (char c in text) { bool flag = char.IsWhiteSpace(c); if (!flag) { bool flag2; switch (c) { case '!': case '#': case '$': case '%': case '(': case ')': case '*': case '-': case ':': case '[': case ']': flag2 = true; break; default: flag2 = false; break; } flag = flag2; } if (flag) { return true; } } return false; } private static bool CanHandleSingleLineCheap(string source) { string text = StripLeadingSimpleRichTextTags(StripOuterSimpleRichTextEnvelope(source)).Trim(); if (text.Length == 0) { return false; } string text2 = StripMenuSelectionPrefix(text); if (ExactEntries.ContainsKey(text2) || LooksLikeBracketedKnownExternalUiToken(text2) || LooksLikeDecoratedKnownExternalUiToken(text2)) { return true; } if (!MightContainCompositeExternalUiShape(text2)) { return false; } if (!LooksLikeKnownExternalUiLabel(text2) && !LooksLikeSaveFileLabel(text2) && !LooksLikeAdvancedFeaturesPlayerLabel(text2) && !LooksLikeDeleteFilePrompt(text2) && text.IndexOf("Emote", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Admin UI", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Lobby ID", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Server Access", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Steam Sessions", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("FRIENDS ONLY", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("INVITE ONLY", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("discount", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf(" off!", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf(" up!", StringComparison.OrdinalIgnoreCase) < 0) { return LooksLikeChatCommandCompatibilityText(text2); } return true; } private static bool MightTranslateStatusLikeSingleLineCheap(string source) { string text = StripLeadingSimpleRichTextTags(StripOuterSimpleRichTextEnvelope(source)).Trim(); if (text.Length == 0 || ContainsCjk(text)) { return false; } string text2 = StripMenuSelectionPrefix(text).Trim(); if (!LooksLikeKnownExternalUiLabel(text2) && !LooksLikeBracketedKnownExternalUiToken(text2) && !LooksLikeDecoratedKnownExternalUiToken(text2)) { return LooksLikeChatCommandCompatibilityText(text2); } return true; } public static bool TryTranslateFast(string? source, out string translated) { translated = source ?? string.Empty; if (string.IsNullOrWhiteSpace(source) || source.Length > 512) { return false; } if (TryGetCachedTranslation(source, out translated, out var hasTranslation)) { return hasTranslation; } if (!CanHandleCheap(source)) { CacheTranslationResult(source, null); return false; } if (TryTranslateDiscountAlertNoDiscountText(source, out translated) || TryTranslateDeleteFilePrompt(source, out translated)) { CacheTranslationResult(source, translated); return true; } if (ContainsLineBreak(source)) { bool flag = TryTranslateLines(source, out translated); CacheTranslationResult(source, flag ? translated : null); return flag; } bool flag2 = TryTranslateSingleLinePreservingWhitespace(source, out translated); CacheTranslationResult(source, flag2 ? translated : null); return flag2; } public static bool TryTranslateDisplayTipText(string? source, out string translated) { translated = source ?? string.Empty; if (string.IsNullOrWhiteSpace(source)) { return false; } if (source.Length <= 512) { return TryTranslateFast(source, out translated); } if (ContainsLineBreak(source)) { return TryTranslateDisplayTipLines(source, out translated); } return false; } public static bool MightContainDisplayTipCompatibilityText(string? source) { if (string.IsNullOrWhiteSpace(source)) { return false; } if (source.Length <= 512) { if (!CanHandleCheap(source)) { return MightContainDiscountLine(source); } return true; } return MightContainDiscountLine(source); } private static void CacheCanHandleResult(string source, bool result) { if ((result || !LooksLikeVolatileNegativeCacheSource(source)) && CanHandleCache.Count < RuntimePerformanceSettings.ExternalCompatibilityCacheLimit) { CanHandleCache[source] = result; } } private static bool TryGetCachedTranslation(string source, out string translated, out bool hasTranslation) { if (!TranslationCache.TryGetValue(source, out string value)) { translated = source; hasTranslation = false; return false; } hasTranslation = value != null; translated = value ?? source; return true; } private static void CacheTranslationResult(string source, string? translated) { if ((translated != null || !LooksLikeVolatileNegativeCacheSource(source)) && TranslationCache.Count < RuntimePerformanceSettings.ExternalCompatibilityCacheLimit) { TranslationCache[source] = translated; } } private static bool LooksLikeVolatileNegativeCacheSource(string source) { if (source.Length > 128) { return true; } foreach (char c in source) { bool flag = char.IsDigit(c); if (!flag) { bool flag2; switch (c) { case '\n': case '\r': case '#': case '$': case '%': case '(': case ')': case '/': case ':': case '<': case '>': case '[': case '\\': case ']': flag2 = true; break; default: flag2 = false; break; } flag = flag2; } if (flag) { return true; } } return false; } private static bool TryTranslateLines(string source, out string translated) { bool flag = false; StringBuilder stringBuilder = new StringBuilder(source.Length + 16); int num = 0; while (num <= source.Length) { int num2 = source.IndexOf('\n', num); int num3 = ((num2 < 0) ? source.Length : num2); string text = source.Substring(num, num3 - num); bool flag2 = text.EndsWith("\r", StringComparison.Ordinal); string source2; if (!flag2) { source2 = text; } else { string text2 = text; source2 = text2.Substring(0, text2.Length - 1); } if (TryTranslateSingleLinePreservingWhitespace(source2, out string translated2)) { stringBuilder.Append(translated2); if (flag2) { stringBuilder.Append('\r'); } flag = true; } else { stringBuilder.Append(text); } if (num2 < 0) { break; } stringBuilder.Append('\n'); num = num2 + 1; } translated = (flag ? stringBuilder.ToString() : source); return flag; } private static bool TryTranslateDisplayTipLines(string source, out string translated) { bool flag = false; StringBuilder stringBuilder = new StringBuilder(source.Length + 16); int num = 0; while (num <= source.Length) { int num2 = source.IndexOf('\n', num); int num3 = ((num2 < 0) ? source.Length : num2); string text = source.Substring(num, num3 - num); bool flag2 = text.EndsWith("\r", StringComparison.Ordinal); string text2; if (!flag2) { text2 = text; } else { string text3 = text; text2 = text3.Substring(0, text3.Length - 1); } string text4 = text2; if (text4.Length <= 512 && LineMightNeedExternalCompatibilityCheck(text4.AsSpan()) && TryTranslateSingleLinePreservingWhitespace(text4, out string translated2)) { stringBuilder.Append(translated2); if (flag2) { stringBuilder.Append('\r'); } flag = true; } else { stringBuilder.Append(text); } if (num2 < 0) { break; } stringBuilder.Append('\n'); num = num2 + 1; } translated = (flag ? stringBuilder.ToString() : source); return flag; } private static bool TryTranslateSingleLinePreservingWhitespace(string source, out string translated) { translated = source; int num = source.Length - source.TrimStart().Length; int num2 = source.Length - source.TrimEnd().Length; int num3 = source.Length - num - num2; if (num3 <= 0) { return false; } string text = ((num > 0) ? source.Substring(0, num) : string.Empty); string text2; if (num2 <= 0) { text2 = string.Empty; } else { int num4 = num2; int length = source.Length; int num5 = length - num4; text2 = source.Substring(num5, length - num5); } string text3 = text2; if (!TryTranslateSingleLineCore(source.Substring(num, num3), out string translated2)) { return false; } translated = text + translated2 + text3; return true; } private static bool TryTranslateSingleLineCore(string source, out string translated) { translated = source; string value = source.Trim(); string text = string.Empty; string text2 = string.Empty; string prefix; string inner; string suffix; while (TryExtractOuterSimpleRichTextEnvelope(value, out prefix, out inner, out suffix)) { text += prefix; text2 = suffix + text2; value = inner.Trim(); } text += ExtractLeadingSimpleRichTextPrefix(ref value); string text3 = string.Empty; if (value.StartsWith(">", StringComparison.Ordinal)) { text3 = "> "; value = StripMenuSelectionPrefix(value); } if (LooksLikeNonUiName(value)) { return false; } if (TryTranslateBracketedCommand(value, out translated) || TryTranslateDecoratedExactUiText(value, out translated) || TryTranslateControlTip(value, out translated) || TryTranslateLabelValue(value, out translated) || TryTranslateDiscountLine(value, out translated) || TryTranslateSaveFileLabel(value, out translated) || TryTranslateAdvancedFeaturesPlayerLabel(value, out translated) || TryTranslateDeleteFilePrompt(value, out translated) || TryTranslateChatCommandCompatibilityText(value, out translated) || TryTranslateExactUiText(value, out translated)) { translated = text3 + text + translated + text2; return true; } return false; } private static bool TryTranslateSaveFileLabel(string text, out string translated) { translated = text; string text2 = StripMenuSelectionPrefix(text).Trim(); if (!LooksLikeSaveFileLabel(text2)) { return false; } string text3 = text2; int length = "File ".Length; translated = text3.Substring(length, text3.Length - length).TrimStart() + " 号存档"; return true; } private static bool TryTranslateDeleteFilePrompt(string text, out string translated) { translated = text; if (TryExtractDeleteFileAlias(text, out string alias)) { translated = "要删除 " + alias + " 吗"; return true; } if (!TryExtractDeleteFileNumber(text, out string fileNumber)) { return false; } translated = "要删除 " + fileNumber + " 号存档吗"; return true; } private static bool TryTranslateChatCommandCompatibilityText(string text, out string translated) { translated = text; string text2 = text.Trim(); if (!TryTranslateCommandItemLabel(text2, "Item to sell:", "要售卖的物品", out translated) && !TryTranslateCommandItemLabel(text2, "Items with priority:", "优先售卖物品", out translated) && !TryTranslateNoItemsCalled(text2, out translated) && !TryTranslateThereIsScrapOnShip(text2, out translated) && !TryTranslateItemsCouldNotBeSorted(text2, out translated) && !TryTranslateItemsOfTypePosition(text2, out translated) && !TryTranslateMovingAllItemsOfType(text2, out translated) && !TryTranslateSellingItemsSummary(text2, out translated) && !TryTranslateSoldRequestedSummary(text2, out translated) && !TryTranslateCommandValueLabel(text2, "Overtime:", "加班奖励", out translated) && !TryTranslateCommandValueLabel(text2, "Money after takeoff:", "起飞后资金", out translated)) { return TryTranslateMoneyInTerminal(text2, out translated); } return true; } private static bool LooksLikeChatCommandCompatibilityText(string text) { string text2 = text.Trim(); if (!text2.StartsWith("Item to sell:", StringComparison.OrdinalIgnoreCase) && !text2.StartsWith("Items with priority:", StringComparison.OrdinalIgnoreCase) && !text2.StartsWith("No items called \"", StringComparison.OrdinalIgnoreCase) && !text2.StartsWith("There is ", StringComparison.OrdinalIgnoreCase) && !text2.StartsWith("Items of type ", StringComparison.OrdinalIgnoreCase) && !text2.StartsWith("Moving all items of type ", StringComparison.OrdinalIgnoreCase) && !text2.StartsWith("Selling ", StringComparison.OrdinalIgnoreCase) && !text2.StartsWith("Overtime:", StringComparison.OrdinalIgnoreCase) && !text2.StartsWith("Money after takeoff:", StringComparison.OrdinalIgnoreCase) && !text2.StartsWith("Money in terminal", StringComparison.OrdinalIgnoreCase) && text2.IndexOf(" sold / ", StringComparison.OrdinalIgnoreCase) <= 0) { return text2.EndsWith(" items couldn't be sorted", StringComparison.OrdinalIgnoreCase); } return true; } private static bool TryTranslateCommandItemLabel(string text, string prefix, string localizedLabel, out string translated) { translated = text; if (!text.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { return false; } int length = prefix.Length; string text2 = text.Substring(length, text.Length - length).Trim(); if (text2.Length == 0) { return false; } translated = localizedLabel + ":" + LocalizeCommandItemName(text2); return true; } private static bool TryTranslateNoItemsCalled(string text, out string translated) { translated = text; if (!text.StartsWith("No items called \"", StringComparison.OrdinalIgnoreCase)) { return false; } int num = text.IndexOf('"', "No items called \"".Length); if (num <= "No items called \"".Length) { return false; } string text2 = text.Substring("No items called \"".Length, num - "No items called \"".Length).Trim(); int num2 = num + 1; string text3 = text.Substring(num2, text.Length - num2).Trim(); if (text2.Length == 0 || (text3.Length > 0 && !string.Equals(text3, "were detected", StringComparison.OrdinalIgnoreCase))) { return false; } translated = "未找到名为 \"" + LocalizeCommandItemName(text2) + "\" 的物品"; return true; } private static bool TryTranslateThereIsScrapOnShip(string text, out string translated) { translated = text; if (!text.StartsWith("There is ", StringComparison.OrdinalIgnoreCase) || !text.EndsWith(" scrap on ship", StringComparison.OrdinalIgnoreCase)) { return false; } string text2 = text.Substring("There is ".Length, text.Length - "There is ".Length - " scrap on ship".Length).Trim(); if (!LooksLikeSimpleNumber(text2)) { return false; } translated = "飞船上有 " + text2 + " 件废料"; return true; } private static bool TryTranslateItemsCouldNotBeSorted(string text, out string translated) { translated = text; if (!text.EndsWith(" items couldn't be sorted", StringComparison.OrdinalIgnoreCase)) { return false; } int length = " items couldn't be sorted".Length; string text2 = text.Substring(0, text.Length - length).Trim(); if (!LooksLikeSimpleNumber(text2)) { return false; } translated = text2 + " 件物品无法整理"; return true; } private static bool TryTranslateItemsOfTypePosition(string text, out string translated) { translated = text; if (!text.StartsWith("Items of type ", StringComparison.OrdinalIgnoreCase)) { return false; } int num = text.IndexOf(" will be put on position ", StringComparison.OrdinalIgnoreCase); if (num <= "Items of type ".Length) { return false; } string text2 = text.Substring("Items of type ".Length, num - "Items of type ".Length).Trim(); string text3 = text; int num2 = num + " will be put on position ".Length; string text4 = text3.Substring(num2, text3.Length - num2).Trim(); bool flag = false; if (text4.EndsWith(" for this game", StringComparison.OrdinalIgnoreCase)) { flag = true; text3 = text4; num2 = " for this game".Length; text4 = text3.Substring(0, text3.Length - num2).TrimEnd(); } if (text2.Length == 0 || text4.Length == 0) { return false; } string text5 = LocalizeCommandItemName(text2); translated = (flag ? ("本局内,类型为 " + text5 + " 的物品将放到 " + text4 + " 位置") : ("类型为 " + text5 + " 的物品将放到 " + text4 + " 位置")); return true; } private static bool TryTranslateMovingAllItemsOfType(string text, out string translated) { translated = text; if (!text.StartsWith("Moving all items of type ", StringComparison.OrdinalIgnoreCase)) { return false; } int num = text.IndexOf(" to position ", StringComparison.OrdinalIgnoreCase); if (num <= "Moving all items of type ".Length) { return false; } string text2 = text.Substring("Moving all items of type ".Length, num - "Moving all items of type ".Length).Trim(); int num2 = num + " to position ".Length; string text3 = text.Substring(num2, text.Length - num2).Trim(); if (text2.Length == 0 || text3.Length == 0) { return false; } translated = "正在将所有 " + LocalizeCommandItemName(text2) + " 类物品移动到 " + text3 + " 位置"; return true; } private static bool TryTranslateSellingItemsSummary(string text, out string translated) { translated = text; if (!text.StartsWith("Selling ", StringComparison.OrdinalIgnoreCase)) { return false; } string markerText; int num = FindFirstMarker(text, "Selling ".Length, SellingNamedMarkers, out markerText); string text4; int num4; if (num > "Selling ".Length) { string text2 = text.Substring("Selling ".Length, num - "Selling ".Length).Trim(); int num2 = num + markerText.Length; int num3 = text.IndexOf("\" with a total value of ", num2, StringComparison.OrdinalIgnoreCase); if (!LooksLikeSimpleNumber(text2) || num3 <= num2) { return false; } string text3 = text.Substring(num2, num3 - num2).Trim(); text4 = text; num4 = num3 + "\" with a total value of ".Length; string text5 = text4.Substring(num4, text4.Length - num4).Trim(); if (text3.Length == 0 || text5.Length == 0) { return false; } translated = "正在出售 " + text2 + " 件名为 \"" + LocalizeCommandItemName(text3) + "\" 的物品,总价值 " + LocalizeCommandResultValueTail(text5); return true; } string markerText2; int num5 = FindFirstMarker(text, "Selling ".Length, SellingCountMarkers, out markerText2); if (num5 <= "Selling ".Length) { return false; } string text6 = text.Substring("Selling ".Length, num5 - "Selling ".Length).Trim(); text4 = text; num4 = num5 + markerText2.Length; string text7 = text4.Substring(num4, text4.Length - num4).Trim(); if (!LooksLikeSimpleNumber(text6) || text7.Length == 0) { return false; } translated = "正在出售 " + text6 + " 件物品,总价值 " + LocalizeCommandResultValueTail(text7); return true; } private static int FindFirstMarker(string text, int startIndex, string[] markers, out string markerText) { int num = -1; markerText = string.Empty; foreach (string text2 in markers) { int num2 = text.IndexOf(text2, startIndex, StringComparison.OrdinalIgnoreCase); if (num2 >= 0 && (num < 0 || num2 < num)) { num = num2; markerText = text2; } } return num; } private static string LocalizeCommandResultValueTail(string value) { string text = value.Replace(" overtime", " 加班奖励", StringComparison.OrdinalIgnoreCase).Replace(", sold every unblacklisted item", ",已出售所有未列入黑名单的物品", StringComparison.OrdinalIgnoreCase); if (!TryTranslateSoldRequestedResultTail(text, out string translated)) { return text; } return translated; } private static bool TryTranslateSoldRequestedSummary(string text, out string translated) { return TryTranslateSoldRequestedSummaryCore(text, out translated); } private static bool TryTranslateSoldRequestedResultTail(string text, out string translated) { translated = text; if (TryTranslateSoldRequestedSummaryCore(text, out translated)) { return true; } int num = text.LastIndexOf(':'); if (num <= 0 || num >= text.Length - 1) { return false; } string text2 = text.Substring(0, num).TrimEnd(); int num2 = num + 1; string text3 = text.Substring(num2, text.Length - num2).TrimStart(); if (text2.Length == 0 || !TryTranslateSoldRequestedSummaryCore(text3, out string translated2)) { return false; } translated = text2 + ":" + translated2; return true; } private static bool TryTranslateSoldRequestedSummaryCore(string text, out string translated) { translated = text; text = text.Trim(); int num = text.IndexOf(" sold / ", StringComparison.OrdinalIgnoreCase); if (num <= 0 || !text.EndsWith(" requested", StringComparison.OrdinalIgnoreCase)) { return false; } string text2 = text.Substring(0, num).Trim(); string text3 = text.Substring(num + " sold / ".Length, text.Length - num - " sold / ".Length - " requested".Length).Trim(); if (!LooksLikeSimpleNumber(text2) || !LooksLikeSimpleNumber(text3)) { return false; } translated = "已出售 " + text2 + " / 目标 " + text3; return true; } private static bool TryTranslateCommandValueLabel(string text, string prefix, string localizedLabel, out string translated) { translated = text; if (!text.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { return false; } int length = prefix.Length; string text2 = text.Substring(length, text.Length - length).Trim(); if (text2.Length == 0) { return false; } translated = localizedLabel + ":" + text2; return true; } private static bool TryTranslateMoneyInTerminal(string text, out string translated) { translated = text; if (!text.StartsWith("Money in terminal", StringComparison.OrdinalIgnoreCase)) { return false; } int num = FindTopLevelColon(text); if (num <= 0 || num >= text.Length - 1) { return false; } string text2 = text.Substring(0, num); int num2 = num + 1; string text3 = text.Substring(num2, text.Length - num2).Trim(); if (text3.Length == 0) { return false; } translated = ((text2.IndexOf("desk", StringComparison.OrdinalIgnoreCase) >= 0) ? ("终端资金(含柜台):" + text3) : ("终端资金:" + text3)); return true; } private static string LocalizeCommandItemName(string item) { string text = item.Trim(); if (text.Length == 0) { return text; } string text2 = TranslationService.BuildTerminalLocalizedItemName(text); if (!string.IsNullOrWhiteSpace(text2)) { return text2; } return text; } private static bool LooksLikeSimpleNumber(string value) { if (value.Length == 0) { return false; } for (int i = 0; i < value.Length; i++) { if (!char.IsDigit(value[i])) { return false; } } return true; } private static bool TryTranslateDiscountAlertNoDiscountText(string text, out string translated) { translated = text; int num = text.Length - text.TrimStart().Length; int num2 = text.Length - text.TrimEnd().Length; int num3 = text.Length - num - num2; if (num3 <= 0) { return false; } string text2 = ((num > 0) ? text.Substring(0, num) : string.Empty); string text3; if (num2 <= 0) { text3 = string.Empty; } else { int num4 = num2; int length = text.Length; int num5 = length - num4; text3 = text.Substring(num5, length - num5); } string text4 = text3; string text5 = text.Substring(num, num3); string text6 = string.Empty; string text7 = string.Empty; string prefix; string inner; string suffix; while (TryExtractOuterSimpleRichTextEnvelope(text5.Trim(), out prefix, out inner, out suffix)) { text6 += prefix; text7 = suffix + text7; text5 = inner; } if (!LooksLikeDiscountAlertNoDiscountCore(text5)) { return false; } translated = text2 + text6 + "暂无折扣\n明天再来查看" + text7 + text4; return true; } private static bool LooksLikeDiscountAlertNoDiscountText(string text) { return LooksLikeDiscountAlertNoDiscountCore(StripLeadingSimpleRichTextTags(StripOuterSimpleRichTextEnvelope(text))); } private static bool LooksLikeDiscountAlertNoDiscountCore(string text) { return NormalizeAsciiWhitespace(text.Replace("\\n", "\n", StringComparison.Ordinal).Replace("\r\n", "\n", StringComparison.Ordinal).Replace('\r', '\n')).Equals("None :( Check back tomorrow!", StringComparison.OrdinalIgnoreCase); } internal static bool TryTranslateBetterSavesDeleteFilePrompt(string? source, int fileToDelete, out string translated) { translated = source ?? string.Empty; if (fileToDelete <= 0 || string.IsNullOrWhiteSpace(source)) { return false; } if (TryExtractDeleteFileAlias(source, out string alias)) { translated = "要删除 " + alias + " 吗"; return true; } if (!TryExtractDeleteFileNumber(source, out string _) && !LooksLikeLocalizedDeleteFileNumberPrompt(source)) { return false; } translated = "要删除 " + fileToDelete.ToString(CultureInfo.InvariantCulture) + " 号存档吗"; return true; } private static bool TryTranslateExactUiText(string text, out string translated) { translated = text; if (ExactEntries.TryGetValue(text, out translated)) { return true; } if (text.EndsWith(":", StringComparison.Ordinal)) { string