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.3.2
BepInEx/plugins/V81TestChn/V81TestChn.dll
Decompiled a month 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.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Dissonance; using Dissonance.Audio.Capture; using Dissonance.Networking; using Dissonance.VAD; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using NAudio.Wave; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Steamworks; using Steamworks.Data; using TMPro; using Unity.Netcode; using Unity.Profiling; using Unity.Profiling.LowLevel.Unsafe; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.LowLevel; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.TextCore; using UnityEngine.TextCore.LowLevel; using UnityEngine.UI; using UnityEngine.Video; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("V81TestChn")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("3.3.2.0")] [assembly: AssemblyInformationalVersion("3.3.2+fa69832bb5c280f46ca9ea20e4b92c3f12f7c3be")] [assembly: AssemblyProduct("V81TestChn")] [assembly: AssemblyTitle("V81TestChn")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("3.3.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace V81TestChn { internal static class AlertTextureReplacementService { private enum NativeTextRole { None, SystemOnline, EnteringAtmosphere } private readonly struct CachedNativeTextRole { public int ParentId { get; } public NativeTextRole Role { get; } public CachedNativeTextRole(int parentId, NativeTextRole role) { ParentId = parentId; Role = role; } } private const int NativeTextRoleCacheLimit = 16384; private const string SystemOnlineTitleObjectName = "TipLeft1"; private const string SystemOnlineTitleFullPath = "Systems/UI/Canvas/IngamePlayerHUD/BottomMiddle/SystemsOnline/TipLeft1"; private const string SystemOnlineTitlePathSuffix = "IngamePlayerHUD/BottomMiddle/SystemsOnline/TipLeft1"; private const string SystemOnlineRelativePath = "BottomMiddle/SystemsOnline/TipLeft1"; private const string EnteringAtmosphereTitleObjectName = "LoadText"; private const string RelaySceneName = "SampleSceneRelay"; private const string SystemOnlineLocalizedText = "系统在线"; private const string EnteringAtmosphereLocalizedText = "正在进入大气层..."; private const string HazardLevelLocalizedText = "风险级别:"; private const string LifeSupportOfflineLocalizedText = "[生命维持:离线]"; private static Coroutine? _systemOnlineWatcher; private static Coroutine? _fixedSceneLabelWatcher; private static HUDManager? _systemOnlineWatcherOwner; private static HUDManager? _fixedSceneLabelWatcherOwner; private static bool _fixedSceneLabelSceneLoadedSubscribed; private static string _fixedSceneLabelSceneLoadedStage = "SceneLoaded"; private static readonly BoundedCache<int, CachedNativeTextRole> NativeTextRoleCache = new BoundedCache<int, CachedNativeTextRole>(16384); private static readonly Dictionary<string, string> FixedSceneLabels = new Dictionary<string, string>(StringComparer.Ordinal) { ["TO MEET PROFIT QUOTA"] = "以达到利润配额", ["PERFORMANCE REPORT"] = "绩效报告", ["EMPLOYEE RANK"] = "员工等级", ["Fines"] = "罚款", ["(Dead)"] = "(死亡)", ["Deceased"] = "死亡", ["[LIFE SUPPORT: OFFLINE]"] = "[生命维持:离线]", ["You will keep your employee rank. Your ship and credits will be reset."] = "你的员工等级将被保留,但飞船和点数将被重置。" }; public static void Initialize(string pluginDir) { if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.LogRuntimeLocalizationEvent("Native relay title translation enabled; SYSTEMS ONLINE uses original TMP object only."); } } public static void ForceApplySystemOnlineOverlay(HUDManager? hudManager, string stage) { TMP_Text val = FindSystemOnlineTitle(hudManager, allowGlobalFallback: false); if ((Object)(object)val != (Object)null) { ApplySystemOnlineNativeTranslation(val, stage); } else if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogWarning((object)("NativeRelay[" + stage + "] target=SystemOnline action=not-found")); AuditSystemOnlineBranch(stage, hudManager); } } public static void BeginSystemOnlineExactPathWatcher(HUDManager? hudManager, string stage) { if (!((Object)(object)hudManager == (Object)null)) { ResetSystemOnlineWatcherIfStale(hudManager); if (_systemOnlineWatcher == null) { _systemOnlineWatcherOwner = hudManager; _systemOnlineWatcher = ((MonoBehaviour)hudManager).StartCoroutine(WaitForSystemOnlineTitle(hudManager, stage)); } } } public static void TryApplySystemOnlineDirect(HUDManager? hudManager, string stage) { TMP_Text val = FindSystemOnlineTitle(hudManager, allowGlobalFallback: true); if ((Object)(object)val == (Object)null) { if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogWarning((object)("NativeRelay[" + stage + "] target=SystemOnline action=not-found")); } } else { ApplySystemOnlineNativeTranslation(val, stage); } } public static void QueueSystemOnlineSync(HUDManager? hudManager, string stage) { } public static void TryApplyEnteringAtmosphereOverlayFromLoadingScreen(HUDManager? hudManager, string stage) { TMP_Text val = FindEnteringAtmosphereTitle(hudManager); if ((Object)(object)val == (Object)null) { if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogWarning((object)("NativeRelay[" + stage + "] target=EnteringAtmosphere action=not-found")); } } else { ApplyEnteringAtmosphereNativeTranslation(val, stage); } } public static void HideEnteringAtmosphereOverlayForHud(HUDManager? hudManager, string stage) { TMP_Text val = FindEnteringAtmosphereTitle(hudManager); if ((Object)(object)val != (Object)null) { ApplyEnteringAtmosphereNativeTranslation(val, stage); } } public static void SyncEnteringAtmosphereOverlayState(TMP_Text? text, string stage) { if (!((Object)(object)text == (Object)null) && GetNativeTextRole(text) == NativeTextRole.EnteringAtmosphere) { ApplyEnteringAtmosphereNativeTranslation(text, stage); } } public static void SyncHazardLevelRelay(HUDManager? hudManager, string stage) { TMP_Text val = FindHazardLevelTitle(hudManager); if ((Object)(object)val != (Object)null) { ApplyHazardLevelNativeTranslation(val, stage); } } public static void SyncLandingInfo(HUDManager? hudManager, string stage) { if (!((Object)(object)hudManager == (Object)null)) { ApplyDynamicFieldTranslation((TMP_Text?)(object)hudManager.planetInfoHeaderText, stage, "PlanetInfoHeader"); ApplyDynamicFieldTranslation((TMP_Text?)(object)hudManager.planetInfoSummaryText, stage, "PlanetInfoSummary"); ApplyDynamicFieldTranslation((TMP_Text?)(object)hudManager.planetRiskLevelText, stage, "PlanetRiskLevel"); SyncHazardLevelRelay(hudManager, stage); } } public static void SyncFixedSceneLabels(HUDManager? hudManager, string stage) { if (!((Object)(object)hudManager == (Object)null)) { TMP_Text[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<TMP_Text>(true); for (int i = 0; i < componentsInChildren.Length; i++) { TryReplaceFixedSceneLabel(componentsInChildren[i], stage); } } } public static void BeginFixedSceneLabelWatcher(HUDManager? hudManager, string stage) { if (!((Object)(object)hudManager == (Object)null) && SyncFixedSceneLabelsInRelayScene(stage + ".initial") <= 0) { _fixedSceneLabelWatcherOwner = hudManager; _fixedSceneLabelSceneLoadedStage = stage; if (!_fixedSceneLabelSceneLoadedSubscribed) { SceneManager.sceneLoaded += OnRelaySceneLoadedForFixedLabels; _fixedSceneLabelSceneLoadedSubscribed = true; } } } public static void Shutdown() { try { if (_systemOnlineWatcher != null && (Object)(object)_systemOnlineWatcherOwner != (Object)null) { ((MonoBehaviour)_systemOnlineWatcherOwner).StopCoroutine(_systemOnlineWatcher); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("NativeRelay shutdown failed stopping SystemOnline watcher: " + ex.GetType().Name + ": " + ex.Message)); } try { if (_fixedSceneLabelWatcher != null && (Object)(object)_fixedSceneLabelWatcherOwner != (Object)null) { ((MonoBehaviour)_fixedSceneLabelWatcherOwner).StopCoroutine(_fixedSceneLabelWatcher); } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("NativeRelay shutdown failed stopping fixed scene label watcher: " + ex2.GetType().Name + ": " + ex2.Message)); } _systemOnlineWatcher = null; _fixedSceneLabelWatcher = null; _systemOnlineWatcherOwner = null; _fixedSceneLabelWatcherOwner = null; if (_fixedSceneLabelSceneLoadedSubscribed) { SceneManager.sceneLoaded -= OnRelaySceneLoadedForFixedLabels; _fixedSceneLabelSceneLoadedSubscribed = false; } _fixedSceneLabelSceneLoadedStage = "SceneLoaded"; NativeTextRoleCache.Clear(); } private static void OnRelaySceneLoadedForFixedLabels(Scene scene, LoadSceneMode mode) { if (string.Equals(((Scene)(ref scene)).name, "SampleSceneRelay", StringComparison.Ordinal)) { SyncFixedSceneLabelsInRelayScene(_fixedSceneLabelSceneLoadedStage + ".scene-loaded"); SceneManager.sceneLoaded -= OnRelaySceneLoadedForFixedLabels; _fixedSceneLabelSceneLoadedSubscribed = false; _fixedSceneLabelWatcherOwner = null; _fixedSceneLabelSceneLoadedStage = "SceneLoaded"; } } private static void ResetSystemOnlineWatcherIfStale(HUDManager currentHud) { if (_systemOnlineWatcher == null) { _systemOnlineWatcherOwner = null; } else if (!((Object)(object)_systemOnlineWatcherOwner == (Object)(object)currentHud)) { StopStaleWatcher(_systemOnlineWatcherOwner, _systemOnlineWatcher, "SystemOnline"); _systemOnlineWatcher = null; _systemOnlineWatcherOwner = null; } } private static void ResetFixedSceneLabelWatcherIfStale(HUDManager currentHud) { if (_fixedSceneLabelWatcher == null) { _fixedSceneLabelWatcherOwner = null; } else if (!((Object)(object)_fixedSceneLabelWatcherOwner == (Object)(object)currentHud)) { StopStaleWatcher(_fixedSceneLabelWatcherOwner, _fixedSceneLabelWatcher, "FixedSceneLabel"); _fixedSceneLabelWatcher = null; _fixedSceneLabelWatcherOwner = null; } } private static void StopStaleWatcher(HUDManager? owner, Coroutine watcher, string label) { if ((Object)(object)owner == (Object)null) { return; } try { ((MonoBehaviour)owner).StopCoroutine(watcher); } catch (Exception ex) { Plugin.Log.LogWarning((object)("NativeRelay stale " + label + " watcher stop failed: " + ex.GetType().Name + ": " + ex.Message)); } } public static void TryReplaceSystemOnlineText(TMP_Text? text, string stage) { if ((Object)(object)text == (Object)null || !MightBeNativeRelayCandidate(text)) { return; } switch (GetNativeTextRole(text)) { case NativeTextRole.SystemOnline: ApplySystemOnlineNativeTranslation(text, stage); return; case NativeTextRole.EnteringAtmosphere: ApplyEnteringAtmosphereNativeTranslation(text, stage); return; } if (IsHazardLevelTitle(text.text)) { ApplyHazardLevelNativeTranslation(text, stage); } else { TryReplaceFixedSceneLabel(text, stage); } } public static bool TryReplaceFixedSceneLabel(TMP_Text? text, string stage) { if ((Object)(object)text == (Object)null || string.IsNullOrWhiteSpace(text.text)) { return false; } if (!TryResolveFixedSceneLabel(text.text, out string localized)) { return false; } ApplyLocalizedText(text, localized, stage, "FixedSceneLabel"); if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.LogRuntimeLocalizationEvent("NativeRelay[" + stage + "] target=FixedSceneLabel action=applied name=" + ((Object)text).name + " path=" + BuildPath(text.transform) + " text=" + text.text); } return true; } public static void TryReplaceSystemOnlineText(Text? text, string stage) { } private static bool MightBeNativeRelayCandidate(TMP_Text text) { if (string.Equals(((Object)text).name, "TipLeft1", StringComparison.OrdinalIgnoreCase) || string.Equals(((Object)text).name, "LoadText", StringComparison.OrdinalIgnoreCase)) { return true; } string text2 = text.text; if (string.IsNullOrWhiteSpace(text2) || text2.Length > 160) { return false; } int i; for (i = 0; i < text2.Length && char.IsWhiteSpace(text2[i]); i++) { } if (i >= text2.Length) { return false; } switch (char.ToUpperInvariant(text2[i])) { case '(': case 'D': case 'E': case 'F': case 'H': case 'P': case 'T': case 'Y': case '[': return true; default: return false; } } public static void TryReplaceSystemOnlineText(TextMesh? text, string stage) { } public static void TryReplace(Image? image, string stage) { } public static void TryReplace(RawImage? image, string stage) { } public static void TryReplace(SpriteRenderer? renderer, string stage) { } public static void TryReplace(Material? material, string stage) { } private static void ApplySystemOnlineNativeTranslation(TMP_Text text, string stage) { if (!((Behaviour)text).enabled) { ((Behaviour)text).enabled = true; } ApplyLocalizedText(text, ResolveLocalizedText(text.text, "系统在线"), stage, "SystemOnline"); if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.LogRuntimeLocalizationEvent("NativeRelay[" + stage + "] target=SystemOnline action=applied name=" + ((Object)text).name + " path=" + BuildPath(text.transform) + " text=" + text.text); } } private static IEnumerator WaitForSystemOnlineTitle(HUDManager hudManager, string stage) { float elapsed = 0f; while ((Object)(object)hudManager != (Object)null && elapsed < 12f) { TMP_Text text = FindSystemOnlineTitleByHudPath(hudManager); if (IsExactSystemOnlineTitle(text)) { ApplySystemOnlineNativeTranslation(text, stage + ".watcher"); _systemOnlineWatcher = null; _systemOnlineWatcherOwner = null; yield break; } yield return (object)new WaitForSeconds(0.25f); elapsed += 0.25f; } if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogWarning((object)("NativeRelay[" + stage + ".watcher] target=SystemOnline action=timeout")); } _systemOnlineWatcher = null; _systemOnlineWatcherOwner = null; } private static IEnumerator WaitForFixedSceneLabels(string stage) { for (float elapsed = 0f; elapsed < 8f; elapsed += 0.5f) { if (SyncFixedSceneLabelsInRelayScene(stage + ".relay-scene") > 0) { _fixedSceneLabelWatcher = null; _fixedSceneLabelWatcherOwner = null; yield break; } yield return (object)new WaitForSeconds(0.5f); } if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.Log.LogWarning((object)("NativeRelay[" + stage + ".relay-scene] target=FixedSceneLabel action=timeout")); } _fixedSceneLabelWatcher = null; _fixedSceneLabelWatcherOwner = null; } private static void ApplyEnteringAtmosphereNativeTranslation(TMP_Text text, string stage) { ApplyLocalizedText(text, ResolveLocalizedText(text.text, "正在进入大气层..."), stage, "EnteringAtmosphere"); } private static void ApplyHazardLevelNativeTranslation(TMP_Text text, string stage) { ApplyLocalizedText(text, "风险级别:", stage, "HazardLevel"); } private static void ApplyLocalizedText(TMP_Text text, string localized, string stage, string target) { if (!string.IsNullOrWhiteSpace(localized)) { if (!string.Equals(text.text, localized, StringComparison.Ordinal)) { text.text = localized; } FontFallbackService.ApplyFallback(text, localized); ApplyLifeSupportOfflineTypography(text, localized); } } private static void ApplyLifeSupportOfflineTypography(TMP_Text text, string localized) { if (string.Equals(localized, "[生命维持:离线]", StringComparison.Ordinal)) { text.richText = true; text.enableWordWrapping = false; text.overflowMode = (TextOverflowModes)0; text.fontStyle = (FontStyles)0; text.characterSpacing = Math.Max(text.characterSpacing, 6f); text.wordSpacing = Math.Max(text.wordSpacing, 8f); } } private static TMP_Text? FindSystemOnlineTitle(HUDManager? hudManager, bool allowGlobalFallback) { if ((Object)(object)hudManager != (Object)null) { if ((Object)(object)((Component)hudManager).transform != (Object)null) { Transform obj = ((Component)hudManager).transform.Find("BottomMiddle/SystemsOnline/TipLeft1"); TMP_Text val = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null); if (IsExactSystemOnlineTitle(val)) { return val; } } TMP_Text[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val2 in componentsInChildren) { if (IsExactSystemOnlineTitle(val2)) { return val2; } } } if (allowGlobalFallback) { TMP_Text val3 = FindSystemOnlineTitleByFullPath(); if (IsExactSystemOnlineTitle(val3)) { return val3; } TMP_Text[] componentsInChildren = Resources.FindObjectsOfTypeAll<TMP_Text>(); foreach (TMP_Text val4 in componentsInChildren) { if (IsExactSystemOnlineTitle(val4)) { return val4; } } } return null; } private static TMP_Text? FindSystemOnlineTitleByFullPath() { GameObject val = GameObject.Find("Systems/UI/Canvas/IngamePlayerHUD/BottomMiddle/SystemsOnline/TipLeft1"); if (!((Object)(object)val != (Object)null)) { return null; } return val.GetComponent<TMP_Text>(); } private static TMP_Text? FindSystemOnlineTitleByHudPath(HUDManager? hudManager) { if ((Object)(object)((hudManager != null) ? ((Component)hudManager).transform : null) == (Object)null) { return null; } Transform val = ((Component)hudManager).transform.Find("BottomMiddle/SystemsOnline/TipLeft1"); if (!((Object)(object)val == (Object)null)) { return ((Component)val).GetComponent<TMP_Text>(); } return null; } private static TMP_Text? FindEnteringAtmosphereTitle(HUDManager? hudManager) { if ((Object)(object)hudManager?.LoadingScreen == (Object)null) { return null; } TMP_Text[] componentsInChildren = ((Component)hudManager.LoadingScreen).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val in componentsInChildren) { if (IsEnteringAtmosphereTitleObject(val)) { return val; } } return null; } private static TMP_Text? FindHazardLevelTitle(HUDManager? hudManager) { if ((Object)(object)hudManager == (Object)null) { return null; } TMP_Text[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val in componentsInChildren) { if (IsHazardLevelTitle(val.text)) { return val; } } return null; } private static bool IsSystemOnlineObject(TMP_Text? text) { return GetNativeTextRole(text) == NativeTextRole.SystemOnline; } private static bool IsSystemOnlinePath(Transform? transform) { if ((Object)(object)transform == (Object)null) { return false; } return BuildPath(transform).EndsWith("IngamePlayerHUD/BottomMiddle/SystemsOnline/TipLeft1", StringComparison.OrdinalIgnoreCase); } private static bool IsEnteringAtmosphereTitleObject(TMP_Text? text) { return GetNativeTextRole(text) == NativeTextRole.EnteringAtmosphere; } private static bool IsHazardLevelTitle(string? text) { if (string.IsNullOrWhiteSpace(text)) { return false; } string a = text.Trim(); if (!string.Equals(a, "HAZARD LEVEL:", StringComparison.OrdinalIgnoreCase) && !string.Equals(a, "风险级别:", StringComparison.Ordinal) && !string.Equals(a, "风险级别:", StringComparison.Ordinal) && !string.Equals(a, "危险等级:", StringComparison.Ordinal)) { return string.Equals(a, "危险等级:", StringComparison.Ordinal); } return true; } private static string ResolveLocalizedText(string? current, string fallback) { if (!string.IsNullOrWhiteSpace(current) && TranslationService.TryTranslate(current, out string translated)) { return translated; } return fallback; } private static bool TryResolveFixedSceneLabel(string? current, out string localized) { localized = string.Empty; if (string.IsNullOrWhiteSpace(current)) { return false; } if (FixedSceneLabels.TryGetValue(current.Trim(), out localized) && !string.IsNullOrWhiteSpace(localized)) { return true; } return false; } private static int SyncFixedSceneLabelsInRelayScene(string stage) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) Scene sceneByName = SceneManager.GetSceneByName("SampleSceneRelay"); if (!((Scene)(ref sceneByName)).IsValid() || !((Scene)(ref sceneByName)).isLoaded) { return 0; } int num = 0; GameObject[] rootGameObjects = ((Scene)(ref sceneByName)).GetRootGameObjects(); for (int i = 0; i < rootGameObjects.Length; i++) { TMP_Text[] componentsInChildren = rootGameObjects[i].GetComponentsInChildren<TMP_Text>(true); for (int j = 0; j < componentsInChildren.Length; j++) { if (TryReplaceFixedSceneLabel(componentsInChildren[j], stage)) { num++; } } } return num; } private static bool HasNamedAncestor(Transform? transform, string expectedName) { Transform val = transform; while ((Object)(object)val != (Object)null) { if (string.Equals(((Object)val).name, expectedName, StringComparison.OrdinalIgnoreCase)) { return true; } val = val.parent; } return false; } private static void AuditSystemOnlineBranch(string stage, HUDManager? hudManager) { int num = 0; if ((Object)(object)hudManager != (Object)null) { TMP_Text[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val in componentsInChildren) { if (HasNamedAncestor(val.transform, "SystemsOnline")) { num++; if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.LogRuntimeLocalizationEvent($"NativeRelay[{stage}] target=SystemOnline action=audit exact={IsExactSystemOnlineTitle(val)} name={((Object)val).name} path={BuildPath(val.transform)} text={val.text}"); } } } } _ = 0; } private static void ApplyDynamicFieldTranslation(TMP_Text? text, string stage, string target) { if (!((Object)(object)text == (Object)null) && !string.IsNullOrWhiteSpace(text.text) && (TranslationService.TryTranslateKnownDynamicTextTargeted(DynamicTextDomain.PlanetInfo, text.text, out string translated) || TranslationService.TryTranslate(text.text, out translated)) && !string.IsNullOrWhiteSpace(translated)) { if (!string.Equals(text.text, translated, StringComparison.Ordinal)) { text.text = translated; } FontFallbackService.ApplyFallback(text, translated); if (Plugin.RuntimeLocalizationLogsEnabled) { Plugin.LogRuntimeLocalizationEvent("NativeRelay[" + stage + "] target=" + target + " action=applied name=" + ((Object)text).name + " path=" + BuildPath(text.transform) + " text=" + text.text); } } } private static string BuildPath(Transform? transform) { if ((Object)(object)transform == (Object)null) { return "<null>"; } string text = ((Object)transform).name; Transform parent = transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } private static bool IsExactSystemOnlineTitle(TMP_Text? text) { return GetNativeTextRole(text) == NativeTextRole.SystemOnline; } private static NativeTextRole GetNativeTextRole(TMP_Text? text) { if ((Object)(object)text == (Object)null) { return NativeTextRole.None; } if (TryGetCachedNativeTextRole(text, out var role)) { return role; } role = ResolveNativeTextRole(text); CacheNativeTextRole(text, role); return role; } private static bool TryGetCachedNativeTextRole(TMP_Text text, out NativeTextRole role) { int parentInstanceId = GetParentInstanceId(text.transform); if (NativeTextRoleCache.TryGetValue(((Object)text).GetInstanceID(), out var value) && value.ParentId == parentInstanceId) { role = value.Role; return true; } role = NativeTextRole.None; return false; } private static NativeTextRole ResolveNativeTextRole(TMP_Text text) { if (string.Equals(((Object)text).name, "LoadText", StringComparison.OrdinalIgnoreCase) && HasNamedAncestor(text.transform, "LoadingText")) { return NativeTextRole.EnteringAtmosphere; } if (string.Equals(((Object)text).name, "TipLeft1", StringComparison.OrdinalIgnoreCase) && IsSystemOnlinePath(text.transform)) { return NativeTextRole.SystemOnline; } return NativeTextRole.None; } private static void CacheNativeTextRole(TMP_Text text, NativeTextRole role) { int instanceID = ((Object)text).GetInstanceID(); NativeTextRoleCache.Set(instanceID, new CachedNativeTextRole(GetParentInstanceId(text.transform), role), RuntimePerformanceSettings.ComponentTextCacheLimit); } private static int GetParentInstanceId(Transform? transform) { if (!((Object)(object)((transform != null) ? transform.parent : null) == (Object)null)) { return ((Object)transform.parent).GetInstanceID(); } return 0; } } internal static class ClipboardManualLocalizationService { private sealed class TextureSpec { public string FileName { get; } public int Width { get; } public int Height { get; } public bool PrecompressedBc3 { get; } public TextureSpec(string fileName, int width = 0, int height = 0, bool precompressedBc3 = false) { FileName = fileName; Width = width; Height = height; PrecompressedBc3 = precompressedBc3; } } private sealed class RendererState { public Renderer Renderer { get; } public Material[] OriginalMaterials { get; } public List<Material> LocalizedMaterials { get; } public RendererState(Renderer renderer, Material[] originalMaterials, List<Material> localizedMaterials) { Renderer = renderer; OriginalMaterials = originalMaterials; LocalizedMaterials = localizedMaterials; } } private const int MaxTextureFileBytes = 8388608; private const int MaxTextureDimension = 4096; private static readonly Dictionary<string, TextureSpec> TextureFiles = new Dictionary<string, TextureSpec>(StringComparer.OrdinalIgnoreCase) { ["Manual1"] = new TextureSpec("ClipboardManualPage1.png"), ["Manual2"] = new TextureSpec("ClipboardManualPage2.png"), ["Manual3"] = new TextureSpec("ClipboardManualPage3.png"), ["Manual4"] = new TextureSpec("ClipboardManualPage4.png"), ["CruiserManual1"] = new TextureSpec("CruiserManualPage1.bc3", 900, 1260, precompressedBc3: true), ["CruiserManual2"] = new TextureSpec("CruiserManualPage2.bc3", 900, 1260, precompressedBc3: true), ["CruiserManual3"] = new TextureSpec("CruiserManualPage3.bc3", 900, 1260, precompressedBc3: true), ["CruiserManual4"] = new TextureSpec("CruiserManualPage4.bc3", 900, 1260, precompressedBc3: true) }; private static readonly Dictionary<string, Texture2D> Textures = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> FailedTextures = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<int, RendererState> RendererStates = new Dictionary<int, RendererState>(); private static string _textureDirectory = string.Empty; private static bool _initialized; public static void Initialize(string pluginDir) { _textureDirectory = Path.Combine(pluginDir, "textures"); _initialized = true; FailedTextures.Clear(); } public static void Apply(GrabbableObject? grabbableObject) { if (!_initialized || Plugin.IsRuntimeShuttingDown) { return; } ClipboardItem val = (ClipboardItem)(object)((grabbableObject is ClipboardItem) ? grabbableObject : null); if (val == null) { return; } Animator clipboardAnimator = val.clipboardAnimator; if (!((Object)(object)clipboardAnimator == (Object)null)) { PruneDestroyedRendererStates(); SkinnedMeshRenderer[] componentsInChildren = ((Component)clipboardAnimator).GetComponentsInChildren<SkinnedMeshRenderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { ApplyToRenderer((Renderer?)(object)componentsInChildren[i]); } } } public static void ApplyVehicle(VehicleController? vehicle) { if (_initialized && !Plugin.IsRuntimeShuttingDown && !((Object)(object)vehicle == (Object)null)) { PruneDestroyedRendererStates(); Renderer[] componentsInChildren = ((Component)vehicle).GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { ApplyToRenderer(componentsInChildren[i]); } } } public static void Shutdown() { foreach (RendererState value in RendererStates.Values) { try { if ((Object)(object)value.Renderer != (Object)null) { value.Renderer.sharedMaterials = value.OriginalMaterials; } } catch { } foreach (Material localizedMaterial in value.LocalizedMaterials) { DestroyUnityObject((Object?)(object)localizedMaterial); } } RendererStates.Clear(); foreach (Texture2D value2 in Textures.Values) { DestroyUnityObject((Object?)(object)value2); } Textures.Clear(); FailedTextures.Clear(); _textureDirectory = string.Empty; _initialized = false; } public static void OnSceneUnloaded() { PruneDestroyedRendererStates(); } private static void ApplyToRenderer(Renderer? renderer) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown if ((Object)(object)renderer == (Object)null) { return; } int instanceID = ((Object)renderer).GetInstanceID(); if (RendererStates.ContainsKey(instanceID)) { return; } Material[] sharedMaterials = renderer.sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0) { return; } Material[] array = (Material[])sharedMaterials.Clone(); List<Material> list = new List<Material>(); for (int i = 0; i < sharedMaterials.Length; i++) { Material val = sharedMaterials[i]; if (!((Object)(object)val == (Object)null) && TryResolveTexture(((Object)val).name, out Texture2D texture)) { Material val2 = new Material(val) { name = NormalizeMaterialName(((Object)val).name) + " (zh-CN)" }; SetTexture(val2, texture); array[i] = val2; list.Add(val2); } } if (list.Count != 0) { renderer.sharedMaterials = array; RendererStates[instanceID] = new RendererState(renderer, sharedMaterials, list); } } private static void PruneDestroyedRendererStates() { List<int> list = null; foreach (KeyValuePair<int, RendererState> rendererState in RendererStates) { if ((Object)(object)rendererState.Value.Renderer != (Object)null) { continue; } if (list == null) { list = new List<int>(); } list.Add(rendererState.Key); foreach (Material localizedMaterial in rendererState.Value.LocalizedMaterials) { DestroyUnityObject((Object?)(object)localizedMaterial); } } if (list == null) { return; } foreach (int item in list) { RendererStates.Remove(item); } } private static bool TryResolveTexture(string? materialName, out Texture2D texture) { string text = NormalizeMaterialName(materialName); if (Textures.TryGetValue(text, out texture)) { return true; } if (FailedTextures.Contains(text) || !TextureFiles.TryGetValue(text, out TextureSpec value)) { texture = null; return false; } Texture2D val = LoadTexture(text, value); if ((Object)(object)val == (Object)null) { FailedTextures.Add(text); texture = null; return false; } Textures[text] = val; texture = val; return true; } private static string NormalizeMaterialName(string? materialName) { if (string.IsNullOrWhiteSpace(materialName)) { return string.Empty; } string text = materialName.Trim(); if (text.EndsWith(" (Instance)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - " (Instance)".Length); } if (text.EndsWith(" (zh-CN)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - " (zh-CN)".Length); } return text; } private static void SetTexture(Material material, Texture2D texture) { if (material.HasProperty("_BaseColorMap")) { material.SetTexture("_BaseColorMap", (Texture)(object)texture); } if (material.HasProperty("_MainTex")) { material.SetTexture("_MainTex", (Texture)(object)texture); } material.mainTexture = (Texture)(object)texture; } private static Texture2D? LoadTexture(string materialName, TextureSpec spec) { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown string fileName = spec.FileName; string text = Path.Combine(_textureDirectory, fileName); Texture2D val = null; try { FileInfo fileInfo = new FileInfo(text); if (!fileInfo.Exists) { Plugin.Log.LogWarning((object)("Clipboard manual texture is missing: " + fileName)); return null; } if (fileInfo.Length <= 0 || (!spec.PrecompressedBc3 && fileInfo.Length > 8388608)) { Plugin.Log.LogWarning((object)$"Clipboard manual texture has an invalid file size: {fileName} ({fileInfo.Length} bytes)"); return null; } if (spec.PrecompressedBc3) { val = PrecompressedTextureLoader.LoadBc3(text, spec.Width, spec.Height, mipChain: false, out string error); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)("Clipboard manual texture could not be loaded: " + fileName + " (" + error + ")")); return null; } } else { byte[] array = File.ReadAllBytes(text); val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, array, true)) { Plugin.Log.LogWarning((object)("Clipboard manual texture could not be decoded: " + fileName)); DestroyUnityObject((Object?)(object)val); return null; } if (((Texture)val).width <= 0 || ((Texture)val).height <= 0 || ((Texture)val).width > 4096 || ((Texture)val).height > 4096) { Plugin.Log.LogWarning((object)$"Clipboard manual texture dimensions are invalid: {fileName} ({((Texture)val).width}x{((Texture)val).height})"); DestroyUnityObject((Object?)(object)val); return null; } } ((Object)val).name = materialName + " (zh-CN)"; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; return val; } catch (Exception ex) { DestroyUnityObject((Object?)(object)val); Plugin.Log.LogWarning((object)("Clipboard manual texture load failed for " + fileName + ": " + ex.GetType().Name + ": " + ex.Message)); return null; } } private static void DestroyUnityObject(Object? value) { if (!(value == (Object)null)) { if (Application.isPlaying) { Object.Destroy(value); } else { Object.DestroyImmediate(value); } } } } internal struct EnvironmentCleanupGate { private bool _hasCompletedFrame; private int _completedFrame; internal bool NeedsCleanup(int frame) { if (_hasCompletedFrame) { return _completedFrame != frame; } return true; } internal void Complete(int frame) { _completedFrame = frame; _hasCompletedFrame = true; } internal void Reset() { _hasCompletedFrame = false; _completedFrame = 0; } } internal sealed class EnvironmentLevelScan : IDisposable { private struct NodeCursor { internal Transform Node; internal int NextChild; internal int ObservedChildCount; internal Transform? PreviousChild; internal NodeCursor(Transform node) { Node = node; NextChild = 0; ObservedChildCount = 0; PreviousChild = null; } } private sealed class TransformIdentityComparer : IEqualityComparer<Transform> { internal static readonly TransformIdentityComparer Instance = new TransformIdentityComparer(); public bool Equals(Transform? left, Transform? right) { return left == right; } public int GetHashCode(Transform value) { return RuntimeHelpers.GetHashCode(value); } } private static readonly Func<long> Timestamp = Stopwatch.GetTimestamp; private readonly Action<Renderer> _apply; private IEnumerator<Renderer?>? _work; internal EnvironmentLevelScan(Action<Renderer> apply) { _apply = apply; _work = Enumerate().GetEnumerator(); } internal bool Pump() { return Pump(256, Math.Max(1L, Stopwatch.Frequency / 2000), Timestamp); } internal bool Pump(int maximumWorkItems, long maximumTicks, Func<long> timestamp) { if (_work == null) { return false; } long num = timestamp(); for (int i = 0; i < maximumWorkItems; i++) { if (!_work.MoveNext()) { Dispose(); return false; } Renderer current = _work.Current; if ((Object)(object)current != (Object)null) { _apply(current); } if (_work == null) { return false; } if (timestamp() - num >= maximumTicks) { return true; } } return true; } public void Dispose() { IEnumerator<Renderer?>? work = _work; _work = null; work?.Dispose(); } private static IEnumerable<Renderer?> Enumerate() { List<GameObject> roots = new List<GameObject>(); List<Renderer> renderers = new List<Renderer>(); Stack<NodeCursor> nodes = new Stack<NodeCursor>(); HashSet<Transform> visited = new HashSet<Transform>(TransformIdentityComparer.Instance); try { for (int sceneIndex = 0; sceneIndex < SceneManager.sceneCount; sceneIndex++) { Scene scene = SceneManager.GetSceneAt(sceneIndex); roots.Clear(); if (((Scene)(ref scene)).IsValid() && ((Scene)(ref scene)).isLoaded) { if (roots.Capacity < ((Scene)(ref scene)).rootCount) { roots.Capacity = ((Scene)(ref scene)).rootCount; } ((Scene)(ref scene)).GetRootGameObjects(roots); } yield return null; for (int rootIndex = 0; rootIndex < roots.Count; rootIndex++) { if (!((Scene)(ref scene)).IsValid()) { break; } if (!((Scene)(ref scene)).isLoaded) { break; } GameObject val = roots[rootIndex]; if ((Object)(object)val == (Object)null) { yield return null; continue; } Transform next = val.transform; while (((Object)(object)next != (Object)null || nodes.Count > 0) && ((Scene)(ref scene)).IsValid() && ((Scene)(ref scene)).isLoaded) { if ((Object)(object)next != (Object)null) { Transform current = next; next = null; if (!visited.Add(current)) { yield return null; continue; } nodes.Push(new NodeCursor(current)); renderers.Clear(); ((Component)current).GetComponents<Renderer>(renderers); yield return null; for (int rendererIndex = 0; rendererIndex < renderers.Count; rendererIndex++) { if (!((Scene)(ref scene)).IsValid()) { break; } if (!((Scene)(ref scene)).isLoaded) { break; } if ((Object)(object)current == (Object)null) { break; } yield return renderers[rendererIndex]; } renderers.Clear(); continue; } NodeCursor item = nodes.Pop(); if ((Object)(object)item.Node != (Object)null) { int childCount = item.Node.childCount; if (item.NextChild > 0 && (childCount != item.ObservedChildCount || item.NextChild > childCount || item.Node.GetChild(item.NextChild - 1) != item.PreviousChild)) { item.NextChild = 0; } if (item.NextChild < childCount) { next = (item.PreviousChild = item.Node.GetChild(item.NextChild++)); item.ObservedChildCount = childCount; nodes.Push(item); } } yield return null; } nodes.Clear(); yield return null; } } } finally { roots.Clear(); renderers.Clear(); nodes.Clear(); visited.Clear(); } } } internal static class EnvironmentTextureLocalizationService { private sealed class TextureSpec { public string OriginalName { get; } public string FileName { get; } public int Width { get; } public int Height { get; } public bool PrecompressedBc1 { get; } public bool PrecompressedBc3 { get; } public bool PrecompressedBc7 { get; } public bool MipChain { get; } public TextureSpec(string originalName, string fileName, int width, int height, bool precompressedBc1 = false, bool precompressedBc3 = false, bool precompressedBc7 = false, bool mipChain = true) { OriginalName = originalName; FileName = fileName; Width = width; Height = height; PrecompressedBc1 = precompressedBc1; PrecompressedBc3 = precompressedBc3; PrecompressedBc7 = precompressedBc7; MipChain = mipChain; } } private sealed class RendererState { public Renderer Renderer { get; } public Material[] OriginalMaterials { get; } public List<int> LocalizedMaterialIds { get; } public RendererState(Renderer renderer, Material[] originalMaterials, List<int> localizedMaterialIds) { Renderer = renderer; OriginalMaterials = originalMaterials; LocalizedMaterialIds = localizedMaterialIds; } } private sealed class MaterialState { public WeakReference<Material> Original { get; } public Material Localized { get; } public MaterialState(Material original, Material localized) { Original = new WeakReference<Material>(original); Localized = localized; } public bool HasOriginal(Material material) { if (Original.TryGetTarget(out Material target) && (Object)(object)target != (Object)null) { return (Object)(object)target == (Object)(object)material; } return false; } public bool HasLiveOriginal() { if (Original.TryGetTarget(out Material target)) { return (Object)(object)target != (Object)null; } return false; } } private sealed class FireExitState { public Renderer Renderer { get; } public Material[] OriginalMaterials { get; } public GameObject Overlay { get; } public FireExitState(Renderer renderer, Material[] originalMaterials, GameObject overlay) { Renderer = renderer; OriginalMaterials = originalMaterials; Overlay = overlay; } } private const int MaxTextureFileBytes = 16777216; private const string FireExitLabelFileName = "FireExitDoorLabelLocalized.png"; private static readonly string[] TexturePropertyNames = new string[2] { "_BaseColorMap", "_MainTex" }; private static readonly List<Renderer> RendererScanBuffer = new List<Renderer>(); private static readonly HashSet<int> ActiveLocalizedMaterialIds = new HashSet<int>(); private static readonly HashSet<int> UnchangedMaterialIdsThisPass = new HashSet<int>(); private static readonly Dictionary<string, TextureSpec> TextureSpecs = new Dictionary<string, TextureSpec>(StringComparer.OrdinalIgnoreCase) { ["posters"] = new TextureSpec("posters", "ShipPostersLocalized.png", 1024, 1024), ["TipsPoster2"] = new TextureSpec("TipsPoster2", "ShipTipsPosterLocalized.png", 796, 1024), ["ToiletPaperTex"] = new TextureSpec("ToiletPaperTex", "ToiletPaperTexLocalized.png", 1024, 1024), ["CashRegisterTex"] = new TextureSpec("CashRegisterTex", "CashRegisterTexLocalized.png", 1024, 1024), ["FlashbangBottleTexture"] = new TextureSpec("FlashbangBottleTexture", "FlashbangBottleTextureLocalized.png", 1024, 1024), ["WelcomeMatTex"] = new TextureSpec("WelcomeMatTex", "WelcomeMatTexLocalized.png", 1024, 1024), ["AirhornTex"] = new TextureSpec("AirhornTex", "AirhornTexLocalized.png", 512, 512), ["TetraChemicalTex"] = new TextureSpec("TetraChemicalTex", "TetraChemicalTexLocalized.png", 2048, 2048), ["SprayCanTex"] = new TextureSpec("SprayCanTex", "SprayCanTexLocalized.png", 1024, 1024), ["StopSignTex"] = new TextureSpec("StopSignTex", "StopSignTexLocalized.png", 1024, 1024), ["YieldSignTex"] = new TextureSpec("YieldSignTex", "YieldSignTexLocalized.png", 512, 512), ["SodaCanTex1"] = new TextureSpec("SodaCanTex1", "SodaCanTex1Localized.png", 1024, 1024), ["ToothpasteTex"] = new TextureSpec("ToothpasteTex", "ToothpasteTexLocalized.png", 1024, 1024), ["WeedKillerBottleTex"] = new TextureSpec("WeedKillerBottleTex", "WeedKillerBottleTexLocalized.png", 1024, 1024), ["WhoopieCushionTex"] = new TextureSpec("WhoopieCushionTex", "WhoopieCushionTexLocalized.png", 1024, 1024), ["YellowMineDoorTex"] = new TextureSpec("YellowMineDoorTex", "YellowMineDoorTexLocalized.png", 1024, 1024), ["ChemicalBottle1"] = new TextureSpec("ChemicalBottle1", "ChemicalBottle1Localized.png", 1024, 1024), ["powerBoxTextures"] = new TextureSpec("powerBoxTextures", "powerBoxTexturesLocalized.png", 1024, 820), ["PlayerLevelStickers"] = new TextureSpec("PlayerLevelStickers", "PlayerLevelStickersLocalized.png", 256, 512), ["PlayerLevelStickers 1"] = new TextureSpec("PlayerLevelStickers 1", "PlayerVipEmployeeStickerLocalized.png", 512, 128), ["CompanyCruiserCombined4Diffuse 1"] = new TextureSpec("CompanyCruiserCombined4Diffuse 1", "CompanyCruiserDestroyedDiffuseLocalized.dxt1", 4096, 4096, precompressedBc1: true), ["CompanyCruiserCombined4DiffuseDirtyV2"] = new TextureSpec("CompanyCruiserCombined4DiffuseDirtyV2", "CompanyCruiserIntactDiffuseLocalized.bc7", 4096, 4096, precompressedBc1: false, precompressedBc3: false, precompressedBc7: true) }; private static readonly Dictionary<string, Texture2D> LocalizedTextures = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> FailedTextures = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<int, RendererState> RendererStates = new Dictionary<int, RendererState>(); private static readonly Dictionary<int, MaterialState> MaterialStates = new Dictionary<int, MaterialState>(); private static readonly Dictionary<int, FireExitState> FireExitStates = new Dictionary<int, FireExitState>(); private static EnvironmentCleanupGate ObjectCleanupGate; private static Texture2D? _fireExitLabelTexture; private static Material? _fireExitLabelMaterial; private static Material? _fireExitHiddenMaterial; private static Mesh? _fireExitOverlayMesh; private static string _textureDirectory = string.Empty; private static bool _initialized; private static EnvironmentLevelScan? _levelScan; private static RoundManager? _levelScanOwner; private static int _levelScanFrame; private static bool _levelCleanupPending; public static void Initialize(string pluginDir) { CancelLevelScan(); _textureDirectory = Path.Combine(pluginDir, "textures"); ObjectCleanupGate.Reset(); _initialized = true; } public static void ApplyShipEnvironment(StartOfRound? startOfRound) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!CanApply() || (Object)(object)startOfRound == (Object)null) { return; } PruneDestroyedRendererStates(); PruneUnusedMaterialStates(); Scene scene = ((Component)startOfRound).gameObject.scene; if (!((Scene)(ref scene)).IsValid() || !((Scene)(ref scene)).isLoaded) { return; } GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects(); foreach (GameObject val in rootGameObjects) { if ((Object)(object)val == (Object)null) { continue; } RendererScanBuffer.Clear(); val.GetComponentsInChildren<Renderer>(true, RendererScanBuffer); foreach (Renderer item in RendererScanBuffer) { ApplyToRenderer(item); } } RendererScanBuffer.Clear(); } public static void ApplyVehicle(VehicleController? vehicle) { if (!CanApply() || (Object)(object)vehicle == (Object)null) { return; } PruneDestroyedRendererStates(); PruneUnusedMaterialStates(); RendererScanBuffer.Clear(); ((Component)vehicle).GetComponentsInChildren<Renderer>(false, RendererScanBuffer); foreach (Renderer item in RendererScanBuffer) { ApplyToRenderer(item); } RendererScanBuffer.Clear(); } public static void ApplyLevelEnvironment(RoundManager? roundManager) { if (CanApply() && !((Object)(object)roundManager == (Object)null)) { CancelLevelScan(); _levelScanOwner = roundManager; _levelScan = new EnvironmentLevelScan(ApplyLevelRenderer); _levelCleanupPending = true; _levelScanFrame = Time.frameCount; MenuRuntimeHost.Ensure(); } } internal static void PumpPendingLevelScan() { if (_levelScan == null) { return; } if (!CanApply() || (Object)(object)_levelScanOwner == (Object)null) { CancelLevelScan(); } else { if (_levelScanFrame == Time.frameCount) { return; } _levelScanFrame = Time.frameCount; EnvironmentLevelScan levelScan = _levelScan; try { if (_levelCleanupPending) { PruneDestroyedRendererStates(); PruneDestroyedFireExitStates(); PruneUnusedMaterialStates(); _levelCleanupPending = false; } else if (!levelScan.Pump() && levelScan == _levelScan) { CancelLevelScan(); } } catch (Exception ex) { if (levelScan == _levelScan) { CancelLevelScan(); } Plugin.Log.LogWarning((object)("Environment scan failed: " + ex.GetType().Name + ": " + ex.Message)); } finally { UnchangedMaterialIdsThisPass.Clear(); } } } private static void ApplyLevelRenderer(Renderer renderer) { try { ApplyToRenderer(renderer, cacheUnchangedMaterials: true); TryApplyFireExitDoor(renderer); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Environment renderer localization failed: " + ex.GetType().Name + ": " + ex.Message)); } } private static void CancelLevelScan() { EnvironmentLevelScan? levelScan = _levelScan; _levelScan = null; _levelScanOwner = null; _levelCleanupPending = false; levelScan?.Dispose(); UnchangedMaterialIdsThisPass.Clear(); } public static void ApplyGrabbableObject(GrabbableObject? grabbableObject) { if (!CanApply() || (Object)(object)grabbableObject == (Object)null || !IsLocalizedScrap(((Object)((Component)grabbableObject).gameObject).name)) { return; } PruneObjectStatesOncePerFrame(); RendererScanBuffer.Clear(); ((Component)grabbableObject).GetComponentsInChildren<Renderer>(true, RendererScanBuffer); foreach (Renderer item in RendererScanBuffer) { ApplyToRenderer(item); } RendererScanBuffer.Clear(); } public static void ApplyShipDecoration(AutoParentToShip? shipDecoration) { if (!CanApply() || (Object)(object)shipDecoration == (Object)null || !IsLocalizedShipDecoration(((Object)((Component)shipDecoration).gameObject).name)) { return; } PruneObjectStatesOncePerFrame(); RendererScanBuffer.Clear(); ((Component)shipDecoration).GetComponentsInChildren<Renderer>(true, RendererScanBuffer); foreach (Renderer item in RendererScanBuffer) { ApplyToRenderer(item); } RendererScanBuffer.Clear(); } public static void ApplyPlayerBadges(PlayerControllerB? playerController) { if (CanApply() && !((Object)(object)playerController == (Object)null)) { PruneObjectStatesOncePerFrame(); ApplyToRenderer(((Object)(object)playerController.playerBadgeMesh != (Object)null) ? ((Component)playerController.playerBadgeMesh).GetComponent<Renderer>() : null); ApplyToRenderer((Renderer?)(object)playerController.playerBetaBadgeMesh); } } public static void Shutdown() { CancelLevelScan(); foreach (RendererState value in RendererStates.Values) { try { if ((Object)(object)value.Renderer != (Object)null) { value.Renderer.sharedMaterials = value.OriginalMaterials; } } catch { } } RendererStates.Clear(); foreach (FireExitState value2 in FireExitStates.Values) { try { if ((Object)(object)value2.Renderer != (Object)null) { value2.Renderer.sharedMaterials = value2.OriginalMaterials; } } catch { } DestroyUnityObject((Object?)(object)value2.Overlay); } FireExitStates.Clear(); foreach (MaterialState value3 in MaterialStates.Values) { DestroyUnityObject((Object?)(object)value3.Localized); } MaterialStates.Clear(); RendererScanBuffer.Clear(); ActiveLocalizedMaterialIds.Clear(); UnchangedMaterialIdsThisPass.Clear(); foreach (Texture2D value4 in LocalizedTextures.Values) { DestroyUnityObject((Object?)(object)value4); } LocalizedTextures.Clear(); FailedTextures.Clear(); DestroyUnityObject((Object?)(object)_fireExitLabelMaterial); DestroyUnityObject((Object?)(object)_fireExitHiddenMaterial); DestroyUnityObject((Object?)(object)_fireExitOverlayMesh); DestroyUnityObject((Object?)(object)_fireExitLabelTexture); _fireExitLabelMaterial = null; _fireExitHiddenMaterial = null; _fireExitOverlayMesh = null; _fireExitLabelTexture = null; _textureDirectory = string.Empty; ObjectCleanupGate.Reset(); _initialized = false; } public static void OnSceneUnloaded() { RoundManager levelScanOwner = _levelScanOwner; CancelLevelScan(); PruneDestroyedRendererStates(); PruneDestroyedFireExitStates(); PruneUnusedMaterialStates(); ObjectCleanupGate.Reset(); if ((Object)(object)levelScanOwner != (Object)null) { ApplyLevelEnvironment(levelScanOwner); } } private static bool CanApply() { if (_initialized) { return !Plugin.IsRuntimeShuttingDown; } return false; } private static bool IsLocalizedScrap(string? objectName) { if (string.IsNullOrWhiteSpace(objectName)) { return false; } if (!objectName.StartsWith("ToiletPaperRolls", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("CashRegisterItem", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("DiyFlashbang", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("Airhorn", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("TZPChemical", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("SprayPaint", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("StopSign", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("YieldSign", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("RedSodaCan", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("Toothpaste", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("WeedKillerItem", StringComparison.OrdinalIgnoreCase) && !objectName.StartsWith("WhoopieCushion", StringComparison.OrdinalIgnoreCase)) { return objectName.StartsWith("ChemicalJug", StringComparison.OrdinalIgnoreCase); } return true; } private static bool IsLocalizedShipDecoration(string? objectName) { if (!string.IsNullOrWhiteSpace(objectName)) { return objectName.StartsWith("WelcomeMatContainer", StringComparison.OrdinalIgnoreCase); } return false; } private static void TryApplyFireExitDoor(Renderer? renderer) { //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Expected O, but got Unknown if ((Object)(object)renderer == (Object)null || FireExitStates.ContainsKey(((Object)renderer).GetInstanceID()) || !string.Equals(((Object)((Component)renderer).gameObject).name, "Cube.001", StringComparison.Ordinal) || (Object)(object)((Component)renderer).transform.parent == (Object)null || !((Object)((Component)((Component)renderer).transform.parent).gameObject).name.StartsWith("FireExitDoor", StringComparison.OrdinalIgnoreCase)) { return; } MeshFilter component = ((Component)renderer).GetComponent<MeshFilter>(); Mesh val = ((component != null) ? component.sharedMesh : null); Material[] sharedMaterials = renderer.sharedMaterials; if ((Object)(object)val == (Object)null || !string.Equals(((Object)val).name, "Cube.001", StringComparison.Ordinal) || val.subMeshCount != 3 || sharedMaterials == null || sharedMaterials.Length != 3) { return; } Material val2 = EnsureFireExitHiddenMaterial(); Material val3 = EnsureFireExitLabelMaterial(); Mesh val4 = EnsureFireExitOverlayMesh(); if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null) { return; } GameObject val5 = null; try { Material[] array = (Material[])sharedMaterials.Clone(); array[2] = val2; renderer.sharedMaterials = array; val5 = new GameObject("FireExitDoorLabel (zh-CN)") { layer = ((Component)renderer).gameObject.layer }; val5.transform.SetParent(((Component)renderer).transform, false); val5.AddComponent<MeshFilter>().sharedMesh = val4; MeshRenderer obj = val5.AddComponent<MeshRenderer>(); ((Renderer)obj).sharedMaterial = val3; ((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)obj).receiveShadows = false; ((Renderer)obj).lightProbeUsage = (LightProbeUsage)0; ((Renderer)obj).reflectionProbeUsage = (ReflectionProbeUsage)0; FireExitStates[((Object)renderer).GetInstanceID()] = new FireExitState(renderer, sharedMaterials, val5); } catch (Exception ex) { renderer.sharedMaterials = sharedMaterials; DestroyUnityObject((Object?)(object)val5); Plugin.Log.LogWarning((object)("Fire-exit localization failed: " + ex.GetType().Name + ": " + ex.Message)); } } private static Texture2D? EnsureFireExitLabelTexture() { if ((Object)(object)_fireExitLabelTexture != (Object)null) { return _fireExitLabelTexture; } string text = Path.Combine(_textureDirectory, "FireExitDoorLabelLocalized.png"); try { FileInfo fileInfo = new FileInfo(text); if (!fileInfo.Exists || fileInfo.Length <= 0 || fileInfo.Length > 1048576) { throw new InvalidDataException("missing or invalid fire-exit label texture"); } byte[] data = File.ReadAllBytes(text); Texture2D val = LoadTextureData(data, useRuntimeCompression: true) ?? LoadTextureData(data, useRuntimeCompression: false); if ((Object)(object)val == (Object)null || ((Texture)val).width != 512 || ((Texture)val).height != 128) { DestroyUnityObject((Object?)(object)val); throw new InvalidDataException("fire-exit label texture must be 512x128"); } ((Object)val).name = "FireExitDoorLabel (zh-CN)"; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; _fireExitLabelTexture = val; return val; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Fire-exit label load failed: " + ex.GetType().Name + ": " + ex.Message)); return null; } } private static Shader? FindTransparentShader() { return Shader.Find("Unlit/Transparent") ?? Shader.Find("Sprites/Default"); } private static Material? EnsureFireExitLabelMaterial() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown if ((Object)(object)_fireExitLabelMaterial != (Object)null) { return _fireExitLabelMaterial; } Shader val = FindTransparentShader(); Texture2D val2 = EnsureFireExitLabelTexture(); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return null; } _fireExitLabelMaterial = new Material(val) { name = "FireExitDoorLabelMaterial (zh-CN)", mainTexture = (Texture)(object)val2, color = Color.white, renderQueue = 3000 }; return _fireExitLabelMaterial; } private static Material? EnsureFireExitHiddenMaterial() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown if ((Object)(object)_fireExitHiddenMaterial != (Object)null) { return _fireExitHiddenMaterial; } Shader val = FindTransparentShader(); if ((Object)(object)val == (Object)null) { return null; } _fireExitHiddenMaterial = new Material(val) { name = "FireExitDoorOriginalTextHidden (zh-CN)", color = Color.clear, renderQueue = 3000 }; return _fireExitHiddenMaterial; } private static Mesh EnsureFireExitOverlayMesh() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_fireExitOverlayMesh != (Object)null) { return _fireExitOverlayMesh; } Mesh val = new Mesh { name = "FireExitDoorLabelQuad (zh-CN)" }; val.vertices = (Vector3[])(object)new Vector3[4] { new Vector3(1.08f, -34f, -43.6f), new Vector3(1.08f, 34f, -43.6f), new Vector3(1.08f, 34f, -27.8f), new Vector3(1.08f, -34f, -27.8f) }; val.uv = (Vector2[])(object)new Vector2[4] { new Vector2(1f, 1f), new Vector2(0f, 1f), new Vector2(0f, 0f), new Vector2(1f, 0f) }; val.triangles = new int[6] { 0, 1, 2, 0, 2, 3 }; val.RecalculateBounds(); _fireExitOverlayMesh = val; return val; } private static void ApplyToRenderer(Renderer? renderer, bool cacheUnchangedMaterials = false) { if ((Object)(object)renderer == (Object)null) { return; } int instanceID = ((Object)renderer).GetInstanceID(); if (RendererStates.TryGetValue(instanceID, out RendererState value)) { if ((Object)(object)value.Renderer != (Object)null && (Object)(object)value.Renderer == (Object)(object)renderer) { return; } RendererStates.Remove(instanceID); } Material[] sharedMaterials = renderer.sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0) { return; } Material[] array = null; List<int> list = null; bool flag = false; for (int i = 0; i < sharedMaterials.Length; i++) { Material val = sharedMaterials[i]; Material orCreateLocalizedMaterial = GetOrCreateLocalizedMaterial(val, cacheUnchangedMaterials); if (!((Object)(object)orCreateLocalizedMaterial == (Object)null) && orCreateLocalizedMaterial != val) { if (array == null) { array = (Material[])sharedMaterials.Clone(); } array[i] = orCreateLocalizedMaterial; if (list == null) { list = new List<int>(); } int instanceID2 = ((Object)val).GetInstanceID(); if (!list.Contains(instanceID2)) { list.Add(instanceID2); } flag = true; } } if (flag && array != null) { renderer.sharedMaterials = array; RendererStates[instanceID] = new RendererState(renderer, sharedMaterials, list ?? new List<int>()); } } private static Material? GetOrCreateLocalizedMaterial(Material? original, bool cacheUnchangedMaterials) { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Expected O, but got Unknown if ((Object)(object)original == (Object)null) { return null; } int instanceID = ((Object)original).GetInstanceID(); if (cacheUnchangedMaterials && UnchangedMaterialIdsThisPass.Contains(instanceID)) { return original; } if (MaterialStates.TryGetValue(instanceID, out MaterialState value)) { if (value.HasOriginal(original) && (Object)(object)value.Localized != (Object)null) { return value.Localized; } DestroyUnityObject((Object?)(object)value.Localized); MaterialStates.Remove(instanceID); } Material val = null; bool flag = false; string[] texturePropertyNames = TexturePropertyNames; foreach (string text in texturePropertyNames) { if (!original.HasProperty(text) || !TryResolveSpec(original.GetTexture(text), out TextureSpec spec)) { continue; } Texture2D val2 = EnsureTextureLoaded(spec, original.GetTexture(text)); if (!((Object)(object)val2 == (Object)null)) { if (val == null) { val = new Material(original); } val.SetTexture(text, (Texture)(object)val2); flag = true; } } if (!flag && original.HasProperty("_MainTex") && TryResolveSpec(original.mainTexture, out TextureSpec spec2)) { Texture2D val3 = EnsureTextureLoaded(spec2, original.mainTexture); if ((Object)(object)val3 != (Object)null) { val = new Material(original) { mainTexture = (Texture)(object)val3 }; flag = true; } } if (!flag || (Object)(object)val == (Object)null) { DestroyUnityObject((Object?)(object)val); if (cacheUnchangedMaterials) { UnchangedMaterialIdsThisPass.Add(instanceID); } return original; } UnchangedMaterialIdsThisPass.Remove(instanceID); ((Object)val).name = NormalizeName(((Object)original).name) + " (zh-CN)"; if (MaterialStates.TryGetValue(instanceID, out MaterialState value2)) { DestroyUnityObject((Object?)(object)value2.Localized); } MaterialStates[instanceID] = new MaterialState(original, val); return val; } private static Texture2D? EnsureTextureLoaded(TextureSpec spec, Texture? originalTexture) { //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) if (LocalizedTextures.TryGetValue(spec.OriginalName, out Texture2D value)) { return value; } if (FailedTextures.Contains(spec.OriginalName)) { return null; } string text = Path.Combine(_textureDirectory, spec.FileName); Texture2D val = null; try { FileInfo fileInfo = new FileInfo(text); if (!fileInfo.Exists) { throw new FileNotFoundException("localized texture file is missing", text); } if (fileInfo.Length <= 0 || (!spec.PrecompressedBc1 && !spec.PrecompressedBc3 && !spec.PrecompressedBc7 && fileInfo.Length > 16777216)) { throw new InvalidDataException($"invalid file size ({fileInfo.Length} bytes)"); } if (spec.PrecompressedBc1) { val = PrecompressedTextureLoader.LoadBc1(text, spec.Width, spec.Height, spec.MipChain, out string error); if ((Object)(object)val == (Object)null) { throw new InvalidDataException(error ?? "precompressed BC1 load failed"); } } else if (spec.PrecompressedBc3) { val = PrecompressedTextureLoader.LoadBc3(text, spec.Width, spec.Height, spec.MipChain, out string error2); if ((Object)(object)val == (Object)null) { throw new InvalidDataException(error2 ?? "precompressed BC3 load failed"); } } else if (spec.PrecompressedBc7) { val = PrecompressedTextureLoader.LoadBc7(text, spec.Width, spec.Height, spec.MipChain, out string error3); if ((Object)(object)val == (Object)null) { throw new InvalidDataException(error3 ?? "precompressed BC7 load failed"); } } else { byte[] data = File.ReadAllBytes(text); val = LoadTextureData(data, useRuntimeCompression: true) ?? LoadTextureData(data, useRuntimeCompression: false); } if ((Object)(object)val == (Object)null || ((Texture)val).width != spec.Width || ((Texture)val).height != spec.Height) { string arg = (((Object)(object)val == (Object)null) ? "decode failed" : $"{((Texture)val).width}x{((Texture)val).height}"); throw new InvalidDataException($"expected {spec.Width}x{spec.Height}, got {arg}"); } ((Object)val).name = spec.OriginalName + " (zh-CN)"; if ((Object)(object)originalTexture != (Object)null) { ((Texture)val).wrapMode = originalTexture.wrapMode; ((Texture)val).filterMode = originalTexture.filterMode; ((Texture)val).anisoLevel = originalTexture.anisoLevel; ((Texture)val).mipMapBias = originalTexture.mipMapBias; } LocalizedTextures[spec.OriginalName] = val; return val; } catch (Exception ex) { DestroyUnityObject((Object?)(object)val); FailedTextures.Add(spec.OriginalName); Plugin.Log.LogWarning((object)("Environment texture localization failed for " + spec.FileName + ": " + ex.GetType().Name + ": " + ex.Message)); return null; } } private static Texture2D? LoadTextureData(byte[] data, bool useRuntimeCompression) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown Texture2D val = null; try { TextureFormat val2 = (TextureFormat)((useRuntimeCompression && SystemInfo.SupportsTextureFormat((TextureFormat)12)) ? 12 : 4); int num = (((int)val2 == 12) ? 4 : 2); val = new Texture2D(num, num, val2, true); if (ImageConversion.LoadImage(val, data, true)) { return val; } DestroyUnityObject((Object?)(object)val); return null; } catch { DestroyUnityObject((Object?)(object)val); return null; } } private static bool TryResolveSpec(Texture? texture, out TextureSpec spec) { return TextureSpecs.TryGetValue(NormalizeName((texture != null) ? ((Object)texture).name : null), out spec); } private static string NormalizeName(string? name) { if (string.IsNullOrWhiteSpace(name)) { return string.Empty; } string text = name.Trim(); if (text.EndsWith(" (Instance)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - " (Instance)".Length); } if (text.EndsWith(" (zh-CN)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - " (zh-CN)".Length); } return text; } private static void PruneObjectStatesOncePerFrame() { int frameCount = Time.frameCount; if (ObjectCleanupGate.NeedsCleanup(frameCount)) { PruneDestroyedRendererStates(); PruneUnusedMaterialStates(); ObjectCleanupGate.Complete(frameCount); } } private static void PruneDestroyedRendererStates() { List<int> list = null; foreach (KeyValuePair<int, RendererState> rendererState in RendererStates) { if (!((Object)(object)rendererState.Value.Renderer != (Object)null)) { if (list == null) { list = new List<int>(); } list.Add(rendererState.Key); } } if (list == null) { return; } foreach (int item in list) { RendererStates.Remove(item); } } private static void PruneDestroyedFireExitStates() { List<int> list = null; foreach (KeyValuePair<int, FireExitState> fireExitState in FireExitStates) { if (!((Object)(object)fireExitState.Value.Renderer != (Object)null) || !((Object)(object)fireExitState.Value.Overlay != (Object)null)) { if (list == null) { list = new List<int>(); } list.Add(fireExitState.Key); } } if (list == null) { return; } foreach (int item in list) { FireExitStates.Remove(item); } } private static void PruneUnusedMaterialStates() { ActiveLocalizedMaterialIds.Clear(); foreach (RendererState value in RendererStates.Values) { if ((Object)(object)value.Renderer == (Object)null) { continue; } foreach (int localizedMaterialId in value.LocalizedMaterialIds) { ActiveLocalizedMaterialIds.Add(localizedMaterialId); } } List<int> list = null; foreach (KeyValuePair<int, MaterialState> materialState in MaterialStates) { if (!((Object)(object)materialState.Value.Localized != (Object)null) || !materialState.Value.HasLiveOriginal() || !ActiveLocalizedMaterialIds.Contains(materialState.Key)) { if (list == null) { list = new List<int>(); } list.Add(materialState.Key); DestroyUnityObject((Object?)(object)materialState.Value.Localized); } } if (list != null) { foreach (int item in list) { MaterialStates.Remove(item); } } ActiveLocalizedMaterialIds.Clear(); } private static void DestroyUnityObject(Object? value) { if (!(value == (Object)null)) { if (Application.isPlaying) { Object.Destroy(value); } else { Object.DestroyImmediate(value); } } } } internal static class PrecompressedTextureLoader { public static Texture2D? LoadBc1(string path, int width, int height, bool mipChain, out string? error) { return LoadCompressed(path, width, height, mipChain, (TextureFormat)10, 8, "DXT1/BC1", out error); } public static Texture2D? LoadBc3(string path, int width, int height, bool mipChain, out string? error) { return LoadCompressed(path, width, height, mipChain, (TextureFormat)12, 16, "DXT5/BC3", out error); } public static Texture2D? LoadBc7(string path, int width, int height, bool mipChain, out string? error) { return LoadCompressed(path, width, height, mipChain, (TextureFormat)25, 16, "BC7", out error); } private static Texture2D? LoadCompressed(string path, int width, int height, bool mipChain, TextureFormat format, int bytesPerBlock, string formatName, out string? error) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown error = null; Texture2D val = null; try { if (!SystemInfo.SupportsTextureFormat(format)) { error = formatName + " is not supported by this graphics device"; return null; } FileInfo fileInfo = new FileInfo(path); if (!fileInfo.Exists) { error = "precompressed texture file is missing"; return null; } long num = CalculateCompressedByteCount(width, height, mipChain, bytesPerBlock); if (fileInfo.Length != num) { error = $"expected {num} bytes, got {fileInfo.Length}"; return null; } byte[] array = File.ReadAllBytes(path); val = new Texture2D(width, height, format, mipChain); val.LoadRawTextureData(array); val.Apply(false, true); return val; } catch (Exception ex) { DestroyUnityObject((Object?)(object)val); error = ex.GetType().Name + ": " + ex.Message; return null; } } public static long CalculateBc3ByteCount(int width, int height, bool mipChain) { return CalculateCompressedByteCount(width, height, mipChain, 16); } public static long CalculateBc7ByteCount(int width, int height, bool mipChain) { return CalculateCompressedByteCount(width, height, mipChain, 16); } public static long CalculateBc1ByteCount(int width, int height, bool mipChain) { return CalculateCompressedByteCount(width, height, mipChain, 8); } private static long CalculateCompressedByteCount(int width, int height, bool mipChain, int bytesPerBlock) { long num = 0L; while (true) { num += (long)Math.Max(1, (width + 3) / 4) * (long)Math.Max(1, (height + 3) / 4) * bytesPerBlock; if (!mipChain || (width == 1 && height == 1)) { break; } width = Math.Max(1, width / 2); height = Math.Max(1, height / 2); } return num; } private static void DestroyUnityObject(Object? value) { if (!(value == (Object)null)) { if (Application.isPlaying) { Object.Destroy(value); } else { Object.DestroyImmediate(value); } } } } internal static class RadiationWarningAuditService { internal const string WarningRootPathSuffix = "IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease"; private static ConfigEntry<bool>? _enabled; private static ConfigEntry<int>? _sampleCount; private static ConfigEntry<float>? _sampleIntervalSeconds; private static HUDManager? _activeHudManager; private static Coroutine? _activeAuditCoroutine; public static void Initialize(ConfigFile config) { _enabled = config.Bind<bool>("90 诊断 - 辐射警告审计", "Enabled", false, "启用辐射警告原始 UI 子树的限量采样诊断。默认关闭,避免运行时枚举 UI 子树。"); _sampleCount = config.Bind<int>("90 诊断 - 辐射警告审计", "SampleCount", 5, "辐射警告触发后采样的次数上限。"); _sampleIntervalSeconds = config.Bind<float>("90 诊断 - 辐射警告审计", "SampleIntervalSeconds", 0.2f, "两次辐射警告诊断采样之间的间隔,单位为秒。"); } public static void Shutdown() { try { if (_activeAuditCoroutine != null && (Object)(object)_activeHudManager != (Object)null) { ((MonoBehaviour)_activeHudManager).StopCoroutine(_activeAuditCoroutine); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("RadiationWarningAuditService shutdown failed while stopping coroutine: " + ex.GetType().Name + ": " + ex.Message)); } finally { _activeAuditCoroutine = null; _activeHudManager = null; _enabled = null; _sampleCount = null; _sampleIntervalSeconds = null; } } public static void OnRadiationWarningTriggered(HUDManager hudManager, string stage) { if ((Object)(object)hudManager == (Object)null) { return; } ConfigEntry<bool>? enabled = _enabled; if (enabled == null || !enabled.Value) { return; } if ((Object)(object)FindWarningRoot(hudManager) == (Object)null) { Plugin.Log.LogWarning((object)("RadiationAudit[" + stage + "] action=root-not-found suffix=IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease")); return; } if (_activeAuditCoroutine != null && (Object)(object)_activeHudManager != (Object)null) { ((MonoBehaviour)_activeHudManager).StopCoroutine(_activeAuditCoroutine); _activeAuditCoroutine = null; } _activeHudManager = hudManager; _activeAuditCoroutine = ((MonoBehaviour)hudManager).StartCoroutine(SampleWarningSubtree(hudManager, stage, _sampleCount?.Value ?? 0, _sampleIntervalSeconds?.Value ?? 0f)); } private static IEnumerator SampleWarningSubtree(HUDManager hudManager, string stage, int sampleCount, float sampleIntervalSeconds) { int boundedSampleCount = Mathf.Max(1, sampleCount); float boundedIntervalSeconds = Mathf.Max(0f, sampleIntervalSeconds); for (int sampleIndex = 0; sampleIndex < boundedSampleCount; sampleIndex++) { if ((Object)(object)hudManager == (Object)null) { break; } Transform val = FindWarningRoot(hudManager); if ((Object)(object)val == (Object)null) { Plugin.Log.LogInfo((object)string.Format("RadiationAudit[{0}] sample={1} action=sampling-stopped-root-lost suffix={2}", stage, sampleIndex, "IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease")); break; } AuditImages(val, stage, sampleIndex); AuditRawImages(val, stage, sampleIndex); AuditSpriteRenderers(val, stage, sampleIndex); AuditGraphicMaterials(val, stage, sampleIndex); AuditRendererMaterials(val, stage, sampleIndex); if (sampleIndex + 1 < boundedSampleCount) { yield return (object)((!(boundedIntervalSeconds > 0f)) ? ((WaitForSeconds)null) : new WaitForSeconds(boundedIntervalSeconds)); } } _activeAuditCoroutine = null; _activeHudManager = null; } private static void AuditGraphicMaterials(Transform root, string stage, int sampleIndex) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) HashSet<int> hashSet = new HashSet<int>(); Graphic[] componentsInChildren = ((Component)root).GetComponentsInChildren<Graphic>(true); foreach (Graphic val in componentsInChildren) { Material material; try { material = val.material; } catch (Exception ex) { Plugin.Log.LogWarning((object)$"RadiationAudit[{stage}] sample={sampleIndex} component=GraphicMaterial path={BuildPath(((Component)val).transform)} active={((Component)val).gameObject.activeInHierarchy} enabled={((Behaviour)val).enabled} alpha={val.color.a:0.###} material=<error> texture=<error> error={ex.GetType().Name}:{ex.Message}"); continue; } if ((Object)(object)material != (Object)null) { hashSet.Add(((Object)material).GetInstanceID()); } } } private static void AuditRendererMaterials(Transform root, string stage, int sampleIndex) { Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Material[] sharedMaterials = componentsInChildren[i].sharedMaterials; if (sharedMaterials != null && sharedMaterials.Length != 0) { for (int j = 0; j < sharedMaterials.Length; j++) { _ = sharedMaterials[j]; } } } } private static void AuditImages(Transform root, string stage, int sampleIndex) { Image[] componentsInChildren = ((Component)root).GetComponentsInChildren<Image>(true); foreach (Image val in componentsInChildren) { Sprite obj = val.overrideSprite ?? val.sprite; if (obj != null) { _ = obj.texture; } } } private static void AuditRawImages(Transform root, string stage, int sampleIndex) { RawImage[] componentsInChildren = ((Component)root).GetComponentsInChildren<RawImage>(true); for (int i = 0; i < componentsInChildren.Length; i++) { _ = componentsInChildren[i]; } } private static void AuditSpriteRenderers(Transform root, string stage, int sampleIndex) { SpriteRenderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<SpriteRenderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { _ = componentsInChildren[i].sprite; } } private static Transform? FindWarningRoot(HUDManager hudManager) { Animator radiationGraphicAnimator = hudManager.radiationGraphicAnimator; Transform val = ((radiationGraphicAnimator != null) ? ((Component)radiationGraphicAnimator).transform : null); if (HasExpectedRootPath(val)) { return val; } Transform[] componentsInChildren = ((Component)hudManager).GetComponentsInChildren<Transform>(true); foreach (Transform val2 in componentsInChildren) { if (HasExpectedRootPath(val2)) { return val2; } } return null; } private static bool HasExpectedRootPath(Transform? transform) { if ((Object)(object)transform == (Object)null) { return false; } return BuildPath(transform).EndsWith("IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease", StringComparison.OrdinalIgnoreCase); } private static string DescribeName(Object? obj) { if (!string.IsNullOrWhiteSpace((obj != null) ? obj.name : null)) { return obj.name; } return "<null>"; } private static string BuildPath(Transform? transform) { if ((Object)(object)transform == (Object)null) { return "<null>"; } string text = ((Object)transform).name; Transform parent = transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } } internal static class RadiationWarningPlaybackService { private const string WarningRootPathSuffix = "IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease"; private const int NativeWidth = 808; private const int NativeHeight = 336; private const int NativeTextureCacheLimit = 8; private const long MaxTextureBytes = 8388608L; private static readonly Dictionary<string, string> FrameFiles = new Dictionary<string, string>(StringComparer.Ordinal) { ["RedUIPanelGlitchBWarningRadiation"] = "RedUIPanelGlitchBWarningRadiation [FFCC97E055-2C688E6DC8].png", ["RedUIPanelGlitchBWarningRadiationB"] = "RedUIPanelGlitchBWarningRadiationB [7D4ED37952-95F686DE01].png", ["RedUIPanelGlitchBWarningRadiationC"] = "RedUIPanelGlitchBWarningRadiationC [7FBAA1AA30-9E2C036778].png", ["RedUIPanelGlitchBWarningRadiationD"] = "RedUIPanelGlitchBWarningRadiationD [4877939F7B-47E20481B0].png" }; private static readonly Dictionary<string, Texture2D> LocalizedTextures = new Dictionary<string, Texture2D>(StringComparer.Ordinal); private static readonly Dictionary<Texture, Texture2D?> NativeTextureCache = new Dictionary<Texture, Texture2D>(); private static ConfigEntry<bool>? _enabled; private static Image? _panelImage; private static bool _ready; public static void Initialize(string pluginDir, ConfigFile config) { Shutdown(); _enabled = config.Bind<bool>("04 资源 - 辐射警告", "Enabled", true, "汉化核心装置拔出后的辐射警告贴图,沿用原版动画、闪烁、透明度和位置。"); foreach (KeyValuePair<string, string> frameFile in FrameFiles) { Texture2D val = LoadTexture(Path.Combine(pluginDir, "textures", frameFile.Value)); if ((Object)(object)val != (Object)null) { LocalizedTextures.Add(frameFile.Key, val); } } _ready = LocalizedTextures.Count == FrameFiles.Count; if (!_ready) { Plugin.Log.LogWarning((object)"Radiation warning textures are incomplete; preserving native rendering."); } } public static void ResetForHudLifecycle(HUDManager hudManager, string stage) { Image panelImage = _panelImage; _panelImage = null; NativeTextureCache.Clear(); if ((Object)(object)panelImage != (Object)null) { ((Graphic)panelImage).SetMaterialDirty(); } AttachPanel(hudManager); } public static void OnRadiationWarningTriggered(HUDManager hudManager, string stage) { AttachPanel(hudManager); } internal static void LocalizeMainTexture(Image image, ref Texture texture) { if (image != _panelImage || !_ready) { return; } ConfigEntry<bool>? enabled = _enabled; if (enabled == null || !enabled.Value || (Object)(object)image == (Object)null || (Object)(object)texture == (Object)null || (Object)(object)image.m_OverrideSprite != (Object)null || (Object)(object)image.sprite == (Object)null || (object)image.sprite.texture != texture) { return; } if (!NativeTextureCache.TryGetValue(texture, out Texture2D value)) { if (NativeTextureCache.Count >= 8) { NativeTextureCache.Clear(); } if (texture.width == 808 && texture.height == 336) { LocalizedTextures.TryGetValue(((Object)texture).name, out value); } NativeTextureCache.Add(texture, value); } if ((Object)(object)value != (Object)null) { texture = (Texture)(object)value; } } public static void Shutdown() { Image panelImage = _panelImage; _panelImage = null; _ready = false; _enabled = null; NativeTextureCache.Clear(); if ((Object)(object)panelImage != (Object)null) { ((Graphic)panelImage).SetMaterialDirty(); } foreach (Texture2D value in LocalizedTextures.Values) { if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } } LocalizedTextures.Clear(); } private static void AttachPanel(HUDManager hudManager) { if ((Object)(object)hudManager == (Object)null) { return; } Animator radiationGraphicAnimator = hudManager.radiationGraphicAnimator; Transform val = ((radiationGraphicAnimator != null) ? ((Component)radiationGraphicAnimator).transform : null); if (!HasExpectedRootPath(val)) { return; } Transform obj = val.Find("Panel"); Image val2 = ((obj != null) ? ((Component)obj).GetComponent<Image>() : null); if (val2 != _panelImage) { Image panelImage = _panelImage; _panelImage = val2; NativeTextureCache.Clear(); if ((Object)(object)panelImage != (Object)null) { ((Graphic)panelImage).SetMaterialDirty(); } if ((Object)(object)val2 != (Object)null) { ((Graphic)val2).SetMaterialDirty(); } } } private static bool HasExpectedRootPath(Transform? root) { if ((Object)(object)root == (Object)null) { return false; } string text = ((Object)root).name; Transform parent = root.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text.EndsWith("IngamePlayerHUD/SpecialHUDGraphics/RadiationIncrease", StringComparison.Ordinal); } private static Texture2D? LoadTexture(string path) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown Texture2D val = null; try { if (!File.Exists(path) || new FileInfo(path).Length > 8388608) { return null; } byte[] array = File.ReadAllBytes(path); if (!HasExpectedPngDimensions(array)) { return null; } val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, array, true) || ((Texture)val).width != 808 || ((Texture)val).height != 336) { Object.Destroy((Object)(object)val); return null; } ((Object)val).name = Path.GetFileNameWithoutExtension(path); ((Texture)val).filterMode = (FilterMode)0; ((Texture)val).wrapMode = (TextureWrapMode)1; return val; } catch (Exception ex) { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } Plugin.Log.LogWarning((object)("Radiation warning texture load failed: " + Path.GetFileName(path) + "; " + ex.GetType().Name + ": " + ex.Message)); return null; } } private static bool HasExpectedPngDimensions(byte[] data) { if (data.Length >= 24 && data[0] == 137 && data[1] == 80 && data[2] == 78 && data[3] == 71 && data[4] == 13 && data[5] == 10 && data[6] == 26 && data[7] == 10 && data[12] == 73 && data[13] == 72 && data[14] == 68 && data[15] == 82 && data[16] == 0 && data[17] == 0 && data[18] == 3 && data[19] == 40 && data[20] == 0 && data[21] == 0 && data[22] == 1) { return data[23] == 80; } return false; } } internal static class StartupSplashLocalizationService { private const string TargetTextureName = "flashingLightsWarning"; private const string LocalizedFileName = "StartupFlashingLightsWarningLocalized.png"; private const int ExpectedWidth = 800; private const int ExpectedHeight = 600; private const int MaxStartupAttempts = 24; private const int MaxLoggedSprites = 8; private static readonly WaitForSecondsRealtime RetryDelay = new WaitForSecondsRealtime(0.05f); private static readonly ushort[] FullRectTriangleIndices = new ushort[6] { 0, 1, 2, 2, 3, 0 }; private static MonoBehaviour? _host; private static Coroutine? _retryCoroutine; private static ConfigEntry<bool>? _diagnosticsEnabled; private static byte[]? _localizedPng; private static string _localizedPngSha256 = string.Empty; private static bool _diagnosticsEnabledFast; private static bool _applied; private static int _applyAttempt; public static void Initialize(MonoBehaviour host, string pluginDir, ConfigFile config) { Shutdown(); _diagnosticsEnabled = config.Bind<bool>("90 诊断 - 通用", "EnableStartupSplashDiagnostics", false, "启用一次性启动警告纹理与 Sprite 几何诊断。仅在启动替换时记录,不增加常驻轮询。"); _diagnosticsEnabledFast = _diagnosticsEnabled.Value; string text = Path.Combine(pluginDir, "textures", "StartupFlashingLightsWarningLocalized.png"); FileInfo fileInfo = new FileInfo(text); if (!fileInfo.Exists || fileInfo.Length <= 0 || fileInfo.Length > 2097152) { throw new InvalidDataException("Invalid startup splash texture: " + text); } _host = host; _localizedPng = File.ReadAllBytes(text); _localizedPngSha256 = ComputeSha256(_localizedPng); LogDiagnostic($"stage=initialize pngBytes={_localizedPng.Length} pngSha256={_localizedPngSha256} " + $"splashFinished={SplashScreen.isFinished}"); if (!TryApply()) { _retryCoroutine = host.StartCoroutine(RetryDuringStartup()); } } public static void Shutdown() { if (_retryCoroutine != null && (Object)(object)_host != (Object)null) { try { _host.StopCoroutine(_retryCoroutine); } catch { } } _retryCoroutine = null; _host = null; _diagnosticsEnabled = null; _localizedPng = null; _localizedPngSha256 = string.Empty; _diagnosticsEnabledFast = false; _applied = false; _applyAttempt = 0; } private static IEnumerator RetryDuringStartup() { for (int attempt = 0; attempt < 24; attempt++) { yield return RetryDelay; if (TryApply()) { _retryCoroutine = null; yield break; } if (attempt >= 2 && SplashScreen.isFinished) { break; } } _retryCoroutine = null; Plugin.Log.LogWarning((object)"Startup splash localization could not find flashingLightsWarning before the native splash finished."); } private static bool TryApply() { if (_applied || _localizedPng == null) { return _applied; } _applyAttempt++; Texture2D[] array = Resources.FindObjectsOfTypeAll<Texture2D>(); LogDiagnostic($"stage=search attempt={_applyAttempt} loadedTextures={array.Length} " + $"splashFinished={SplashScreen.isFinished}"); Texture2D[] array2 = array; foreach (Texture2D val in array2) { if ((Object)(object)val == (Object)null || !string.Equals(NormalizeName(((Object)val).name), "flashingLightsWarning", StringComparison.Ordinal)) { continue; } try { LogTextureAndReferencingSprites(val, "before-load"); bool flag = ImageConversion.LoadImage(val, _localizedPng, true); LogDiagnostic($"stage=after-load loadSucceeded={flag} pngSha256={_localizedPngSha256} " + DescribeTexture(val)); if (!flag || ((Texture)val).width != 800 || ((Texture)val).height != 600) { Plugin.Log.LogWarning((object)$"Startup splash localization produced an invalid texture size: {((Texture)val).width}x{((Texture)val).height}."); return false; } LogTextureAndReferencingSprites(val, "after-load"); if (!TryOverrideReferencingSpriteGeometry(val)) { Plugin.Log.LogWarning((object)"Startup splash localization could not expand the original Tight Sprite mesh to a full rectangle."); return false; } LogTextureAndReferencingSprites(val, "after-geometry"); ((Object)val).name = "flashingLightsWarning"; _localizedPng = null; _applied = true; Plugin.Log.LogInfo((object)"Localized Unity startup flashing-lights warning texture in memory with full-rectangle Sprite geometry."); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Startup splash localization failed: " + ex.GetType().Name + ": " + ex.Message)); return false; } } LogDiagnostic($"stage=search-result attempt={_applyAttempt} matchingTextures=0"); return false; } private static bool TryOverrideReferencingSpriteGeometry(Texture2D texture) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown res