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 TabMapZoom v1.1.1
Zichen-TabMapZoom-1.1.1.dll
Decompiled 4 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using Zichen_TabMapZoom.I18N; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("Zichen-TabMapZoom-1.1.1")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+39dc04d9da07ed5cb1d34c6e90488b5fe8478f5b")] [assembly: AssemblyProduct("Zichen-TabMapZoom-1.1.1")] [assembly: AssemblyTitle("Zichen-TabMapZoom-1.1.1")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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; } } } [BepInPlugin("zichen.tabmapzoom", "A.Tab Map Zoom", "1.1.1")] public sealed class ZichenTabMapZoomPlugin : BaseUnityPlugin { private static ZichenTabMapZoomPlugin Instance; public const string PluginGuid = "zichen.tabmapzoom"; public const string PluginVersion = "1.1.1"; public const string PluginName = "A.Tab Map Zoom"; private const string ContactConfigSection = "模组信息"; private const string TabMapConfigSection = "A.Tab地图缩放"; private const string MinimapPluginGuid = "Minimap"; private const string MinimapPackageFolder = "dig-Minimap"; private static readonly string[] KnownTabMapConflictPluginIds = new string[2] { "Minimap", "dig-Minimap" }; private static readonly string[] TabMapConflictKeywords = new string[12] { "minimap", "mini map", "mapzoom", "map zoom", "tabmapzoom", "tab map zoom", "mapscale", "map scale", "mapresize", "map resize", "地图缩放", "小地图" }; private ConfigEntry<string> moduleNameInfo; private ConfigEntry<string> moduleVersionInfo; private ConfigEntry<string> contactInfo; private ConfigEntry<bool> tabMapZoomEnabled; private ConfigEntry<bool> tabMapZoomForceEnabled; private ConfigEntry<bool> tabMapRestoreOfficialOnOpen; private ConfigEntry<float> tabMapDefaultZoom; private ConfigEntry<float> tabMapMinZoom; private ConfigEntry<float> tabMapMaxZoom; private ConfigEntry<float> tabMapScrollSpeed; private float tabMapZoomMultiplier = 1f; private bool tabMapWasActive; private bool tabMapZoomBlockedByConflict; private bool tabMapConflictCompatibilityChecked; private Transform tabMapZoomTarget; private Vector3 tabMapOriginalScale = Vector3.one; private Vector3 tabMapOriginalLocalPosition = Vector3.zero; private Vector3 tabMapOriginalCenterLocal = Vector3.zero; private float tabMapOriginalMapScale = 1f; private bool tabMapHasOriginalCenter; private bool tabMapZoomDirty = true; private float tabMapLastAppliedZoom = float.MinValue; private int tabMapKnownLayerCount = -1; private int tabMapKnownModuleCount = -1; private Transform tabMapOverLayerTransform; private Vector3 tabMapOverLayerOffset = Vector3.zero; private readonly List<TabMapTransformSnapshot> tabMapLayerSnapshots = new List<TabMapTransformSnapshot>(); private readonly List<TabMapModuleRuntimeSnapshot> tabMapModuleSnapshots = new List<TabMapModuleRuntimeSnapshot>(); private readonly List<TabMapTransformSnapshot> tabMapModuleChildSnapshots = new List<TabMapTransformSnapshot>(); private Harmony harmony; private I18NRegistry _i18n; private I18NSwitcher _i18nSwitcher; private bool _autoLocaleChecked; public static bool UseChinese() { return Instance?._i18n?.UseChinese == true; } private void Awake() { Instance = this; DetachFromManager(); MakePluginHostPersistent(); ResetConfigIfVersionChanged(); bool defaultChinese = LanguageDetector.DetectDefault(); _i18n = new I18NRegistry(((BaseUnityPlugin)this).Config); _i18n.BindLanguage(defaultChinese); moduleNameInfo = _i18n.BindReadOnly("Mod Info", "模组信息", "Mod Name", "模组名称", "Tab Map Zoom", "地图缩放"); moduleVersionInfo = _i18n.BindReadOnly("Mod Info", "模组信息", "Mod Version", "模组版本号", "1.1.1", "1.1.1"); contactInfo = _i18n.BindReadOnly("Mod Info", "模组信息", "Contact QQ", "REPO交流QQ群", "824639225", "824639225"); tabMapZoomEnabled = _i18n.Bind("A.Tab地图缩放", "A.Tab地图缩放", "Enabled", "启用", "When enabled, you can use the mouse wheel to zoom the map content when opening the vanilla map with Tab.", "开启后,按 Tab 打开原版地图时可以用鼠标滚轮缩放地图内容。", defaultValue: true); tabMapZoomForceEnabled = _i18n.Bind("A.Tab地图缩放", "A.Tab地图缩放", "Force Enable", "强制生效", "Enabled by default. When enabled, ignores Minimap, mini-map, map zoom and other potentially conflicting mods detected at startup, forcing Tab map zoom to work.", "默认开启。开启后会忽略启动时检测到的 Minimap、小地图、地图缩放等可能冲突的模组,强制让 Tab 地图缩放生效。", defaultValue: true); tabMapRestoreOfficialOnOpen = _i18n.Bind("A.Tab地图缩放", "A.Tab地图缩放", "Restore Official State On Open", "每次打开地图强制恢复官方状态", "When enabled, every time the Tab map is closed/reopened it restores the official default map state, then applies the default zoom. When disabled, the map keeps the state from the last wheel zoom.", "开启后,每次关闭/重新打开 Tab 地图都会恢复官方默认地图状态,再应用默认缩放。关闭后,地图会保留上一次滚轮缩放后的状态。", defaultValue: true); tabMapDefaultZoom = _i18n.Bind("A.Tab地图缩放", "A.Tab地图缩放", "Default Zoom", "默认缩放/倍率", "Default zoom multiplier each time the Tab map opens. Below 1 shows a larger area, above 1 zooms into details.", "每次打开 Tab 地图时的默认缩放倍率。小于 1 可以看到更大范围,大于 1 会放大细节。", 0.75f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f)); tabMapMinZoom = _i18n.Bind("A.Tab地图缩放", "A.Tab地图缩放", "Min Zoom", "最小缩放/倍率", "The minimum the map can zoom out to. Smaller values show a larger area at once; lower it further if some rooms are still not visible.", "地图最多缩小到多少。数值越小,一次看到的范围越大;如果还有房间看不到,就继续调低这个值。", 0.3f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f)); tabMapMaxZoom = _i18n.Bind("A.Tab地图缩放", "A.Tab地图缩放", "Max Zoom", "最大缩放/倍率", "The maximum the map can zoom in to. This controls zooming into details, not seeing more rooms; lower the min zoom to see more rooms.", "地图最多放大到多少。这个控制的是放大看细节,不控制看到更多房间;要看到更多房间请调低最小缩放倍率。", 2f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f)); tabMapScrollSpeed = _i18n.Bind("A.Tab地图缩放", "A.Tab地图缩放", "Scroll Speed", "滚轮缩放速度/倍率", "Zoom speed adjustment per mouse wheel notch.", "鼠标滚轮每格调整的缩放速度。", 0.18f, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.02f, 0.5f)); tabMapZoomEnabled.SettingChanged += OnTabMapCompatibilitySettingChanged; tabMapZoomForceEnabled.SettingChanged += OnTabMapCompatibilitySettingChanged; ApplyHarmonyPatches(); _i18nSwitcher = new I18NSwitcher(_i18n, harmony, "A.Tab Map Zoom", ((BaseUnityPlugin)this).Logger); _i18nSwitcher.InstallRepoConfigHook(); TryBootstrapUpdateLoopFromCurrentMap(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"zichen-tab-map-zoom loaded."); if (!_autoLocaleChecked) { ((MonoBehaviour)this).StartCoroutine(AutoFollowGameLocaleOnce()); } } private IEnumerator AutoFollowGameLocaleOnce() { float waited = 0f; while (waited < 20f) { waited += 0.2f; yield return (object)new WaitForSecondsRealtime(0.2f); if (LanguageDetector.TryReadGameLocaleCode(out var code)) { if (LanguageDetector.IsChineseCode(code) && _i18n?.LanguageEntry != null && _i18n.LanguageEntry.Value != I18NRegistry.DisplayLanguage.中文) { _i18n.LanguageEntry.Value = I18NRegistry.DisplayLanguage.中文; ((BaseUnityPlugin)this).Logger.LogInfo((object)"已根据游戏内语言设置自动切换为中文。"); } _autoLocaleChecked = true; yield break; } } _autoLocaleChecked = true; } private void LateUpdate() { UpdateTabMapZoom(); } private void DetachFromManager() { ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } private void OnDestroy() { if (Instance == this) { Instance = null; } Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if ((Object)(object)tabMapZoomTarget != (Object)null) { RestoreTabMapZoomTarget(); } } private void OnTabMapCompatibilitySettingChanged(object sender, EventArgs e) { if (!IsTabMapZoomEnabledEffective()) { ResetTabMapZoom(); SetUpdateLoopActive(active: false); } else { tabMapZoomDirty = true; TryBootstrapUpdateLoopFromCurrentMap(); } } private void ApplyTabMapConflictCompatibility() { if (TryDetectTabMapConflictPlugin(out var detectedPlugin)) { tabMapZoomBlockedByConflict = true; ((BaseUnityPlugin)this).Logger.LogWarning((object)("Detected possible map zoom/minimap conflict plugin '" + detectedPlugin + "'. Tab map zoom will stay inactive unless 强制生效 is enabled.")); } } private bool IsTabMapZoomEnabledEffective() { if (tabMapZoomEnabled == null || !tabMapZoomEnabled.Value) { return false; } EnsureTabMapConflictCompatibilityChecked(); if (!tabMapZoomBlockedByConflict) { return true; } if (tabMapZoomForceEnabled != null) { return tabMapZoomForceEnabled.Value; } return false; } private void EnsureTabMapConflictCompatibilityChecked() { if (!tabMapConflictCompatibilityChecked) { tabMapConflictCompatibilityChecked = true; ApplyTabMapConflictCompatibility(); } } private static bool TryDetectTabMapConflictPlugin(out string detectedPlugin) { detectedPlugin = string.Empty; try { foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos) { PluginInfo value = pluginInfo.Value; object obj; if (value == null) { obj = null; } else { BepInPlugin metadata = value.Metadata; obj = ((metadata != null) ? metadata.GUID : null); } if (obj == null) { obj = pluginInfo.Key; } string text = (string)obj; object obj2; if (value == null) { obj2 = null; } else { BepInPlugin metadata2 = value.Metadata; obj2 = ((metadata2 != null) ? metadata2.Name : null); } if (obj2 == null) { obj2 = string.Empty; } string text2 = (string)obj2; string text3 = ((value != null) ? value.Location : null) ?? string.Empty; if (!IsSelfPlugin(text, text2, text3) && (IsKnownTabMapConflict(text) || IsKnownTabMapConflict(text2) || LooksLikeTabMapConflictPlugin(text) || LooksLikeTabMapConflictPlugin(text2) || LooksLikeTabMapConflictPlugin(text3) || LooksLikeTabMapConflictPlugin(Path.GetFileNameWithoutExtension(text3)) || LooksLikeTabMapConflictPlugin(Path.GetFileName(Path.GetDirectoryName(text3) ?? string.Empty)))) { detectedPlugin = (string.IsNullOrWhiteSpace(text2) ? text : (text2 + " (" + text + ")")); return true; } } } catch { } try { string pluginPath = Paths.PluginPath; if (string.IsNullOrWhiteSpace(pluginPath) || !Directory.Exists(pluginPath)) { return false; } if (Directory.Exists(Path.Combine(pluginPath, "dig-Minimap"))) { detectedPlugin = "dig-Minimap"; return true; } foreach (string item in Directory.EnumerateFiles(pluginPath, "*.dll", SearchOption.AllDirectories)) { if (!IsSelfPlugin(string.Empty, string.Empty, item)) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item); string fileName = Path.GetFileName(Path.GetDirectoryName(item) ?? string.Empty); if (IsKnownTabMapConflict(fileNameWithoutExtension) || IsKnownTabMapConflict(fileName) || LooksLikeTabMapConflictPlugin(fileNameWithoutExtension) || LooksLikeTabMapConflictPlugin(fileName) || LooksLikeTabMapConflictPlugin(item)) { detectedPlugin = (string.IsNullOrWhiteSpace(fileName) ? fileNameWithoutExtension : (fileName + "/" + fileNameWithoutExtension + ".dll")); return true; } } } } catch { } return false; } private static bool IsSelfPlugin(string guid, string name, string location) { if (!string.Equals(guid, "zichen.tabmapzoom", StringComparison.OrdinalIgnoreCase) && !string.Equals(name, "A.Tab Map Zoom", StringComparison.OrdinalIgnoreCase)) { return string.Equals(Path.GetFileNameWithoutExtension(location ?? string.Empty), "Zichen-TabMapZoom", StringComparison.OrdinalIgnoreCase); } return true; } private static bool IsKnownTabMapConflict(string text) { if (string.IsNullOrWhiteSpace(text)) { return false; } string[] knownTabMapConflictPluginIds = KnownTabMapConflictPluginIds; foreach (string text2 in knownTabMapConflictPluginIds) { if (string.Equals(text.Trim(), text2, StringComparison.OrdinalIgnoreCase) || text.IndexOf(text2, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } private static bool LooksLikeTabMapConflictPlugin(string text) { if (string.IsNullOrWhiteSpace(text)) { return false; } string text2 = NormalizePluginSearchText(text); string[] tabMapConflictKeywords = TabMapConflictKeywords; for (int i = 0; i < tabMapConflictKeywords.Length; i++) { string value = NormalizePluginSearchText(tabMapConflictKeywords[i]); if (text2.Contains(value)) { return true; } } return false; } private static string NormalizePluginSearchText(string text) { if (string.IsNullOrWhiteSpace(text)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(text.Length); foreach (char c in text) { if (char.IsLetterOrDigit(c) || c > '\u007f') { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } private void UpdateTabMapZoom() { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) if (!IsTabMapZoomEnabledEffective() || !IsRunLevelActive()) { ResetTabMapZoom(); SetUpdateLoopActive(active: false); return; } Map instance = Map.Instance; if ((Object)(object)instance == (Object)null) { ResetTabMapZoom(); SetUpdateLoopActive(active: false); return; } if (!instance.Active) { if (tabMapRestoreOfficialOnOpen.Value) { ResetTabMapZoom(); } SetUpdateLoopActive(active: false); return; } Transform transform = ((Component)instance).transform; if ((Object)(object)transform == (Object)null) { ResetTabMapZoom(); return; } if ((Object)(object)tabMapZoomTarget != (Object)(object)transform || !tabMapWasActive) { if ((Object)(object)tabMapZoomTarget != (Object)(object)transform) { RestoreTabMapZoomTarget(); } tabMapZoomTarget = transform; tabMapOriginalScale = transform.localScale; tabMapOriginalLocalPosition = transform.localPosition; tabMapOriginalMapScale = instance.Scale; RestoreTabMapModulePositions(instance); tabMapZoomMultiplier = ClampTabMapZoom(tabMapDefaultZoom.Value); RefreshTabMapSnapshots(instance, transform); tabMapWasActive = true; } else if (HasTabMapStructureChanged(instance)) { RestoreTabMapZoomTarget(); RefreshTabMapSnapshots(instance, transform); } float axis = Input.GetAxis("Mouse ScrollWheel"); if (Mathf.Abs(axis) > 0.001f) { float num = ClampTabMapZoom(tabMapZoomMultiplier + axis * tabMapScrollSpeed.Value * 10f); if (!Mathf.Approximately(num, tabMapZoomMultiplier)) { tabMapZoomMultiplier = num; tabMapZoomDirty = true; } } if (tabMapZoomDirty || !Mathf.Approximately(tabMapLastAppliedZoom, tabMapZoomMultiplier)) { ApplyTabMapZoom(instance, transform); tabMapLastAppliedZoom = tabMapZoomMultiplier; tabMapZoomDirty = false; } } private void ResetTabMapZoom() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) RestoreTabMapZoomTarget(); tabMapWasActive = false; tabMapZoomMultiplier = ClampTabMapZoom(tabMapDefaultZoom.Value); tabMapZoomTarget = null; tabMapOriginalScale = Vector3.one; tabMapOriginalLocalPosition = Vector3.zero; tabMapOriginalCenterLocal = Vector3.zero; tabMapOriginalMapScale = 1f; tabMapHasOriginalCenter = false; tabMapZoomDirty = true; tabMapLastAppliedZoom = float.MinValue; tabMapKnownLayerCount = -1; tabMapKnownModuleCount = -1; tabMapOverLayerTransform = null; tabMapOverLayerOffset = Vector3.zero; tabMapLayerSnapshots.Clear(); tabMapModuleSnapshots.Clear(); tabMapModuleChildSnapshots.Clear(); } private void RestoreTabMapZoomTarget() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)tabMapZoomTarget == (Object)null)) { tabMapZoomTarget.localScale = tabMapOriginalScale; tabMapZoomTarget.localPosition = tabMapOriginalLocalPosition; Map instance = Map.Instance; if ((Object)(object)instance != (Object)null) { instance.Scale = tabMapOriginalMapScale; RestoreTabMapLayerSnapshots(); RestoreTabMapModuleChildSnapshots(); RestoreTabMapModulePositions(instance); } } } private void ApplyTabMapZoom(Map map, Transform target) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) target.localPosition = tabMapOriginalLocalPosition; target.localScale = tabMapOriginalScale; map.Scale = tabMapOriginalMapScale * tabMapZoomMultiplier; ApplyTabMapLayerZoom(); ApplyTabMapModuleZoom(map); if (tabMapHasOriginalCenter) { Vector3 val = ScaleTabMapLocalVector(tabMapOriginalCenterLocal, tabMapZoomMultiplier); Vector3 val2 = ToParentSpace(target, target.TransformPoint(val)); Vector3 val3 = GetTabMapAnchorInParentSpace(map, target) - val2; val3.y = 0f; target.localPosition += val3; } } private void CaptureTabMapLayerSnapshots(Map map) { if ((Object)(object)map == (Object)null || map.Layers == null) { return; } List<MapLayer> layers = map.Layers; for (int i = 0; i < layers.Count; i++) { MapLayer val = layers[i]; if (!((Object)(object)val == (Object)null)) { Transform transform = ((Component)val).transform; if (ShouldZoomTabMapLayer(map, transform)) { AddTransformSnapshot(transform, tabMapLayerSnapshots); } } } } private void ApplyTabMapLayerZoom() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_005f: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < tabMapLayerSnapshots.Count; i++) { TabMapTransformSnapshot tabMapTransformSnapshot = tabMapLayerSnapshots[i]; if (!((Object)(object)tabMapTransformSnapshot.Transform == (Object)null)) { tabMapTransformSnapshot.Transform.localPosition = tabMapTransformSnapshot.LocalPosition; Vector3 localScale = tabMapTransformSnapshot.LocalScale; localScale.x *= tabMapZoomMultiplier; localScale.z *= tabMapZoomMultiplier; tabMapTransformSnapshot.Transform.localScale = localScale; } } } private void RestoreTabMapLayerSnapshots() { RestoreTransformSnapshots(tabMapLayerSnapshots); } private void RefreshTabMapSnapshots(Map map, Transform target) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) tabMapLayerSnapshots.Clear(); tabMapModuleSnapshots.Clear(); tabMapModuleChildSnapshots.Clear(); CaptureTabMapLayerSnapshots(map); CaptureTabMapModuleSnapshots(map); UpdateTabMapStructureCounts(map); CacheTabMapOverLayerData(map); tabMapHasOriginalCenter = TryGetMapContentBounds(map, target, out var bounds); tabMapOriginalCenterLocal = (tabMapHasOriginalCenter ? target.InverseTransformPoint(((Bounds)(ref bounds)).center) : Vector3.zero); tabMapZoomDirty = true; tabMapLastAppliedZoom = float.MinValue; } private void CaptureTabMapModuleSnapshots(Map map) { if ((Object)(object)map == (Object)null || map.MapModules == null) { return; } List<MapModule> mapModules = map.MapModules; for (int i = 0; i < mapModules.Count; i++) { MapModule val = mapModules[i]; if ((Object)(object)val == (Object)null || (Object)(object)val.module == (Object)null) { continue; } Transform transform = ((Component)val).transform; Transform transform2 = ((Component)val.module).transform; if (!((Object)(object)transform == (Object)null) && !((Object)(object)transform2 == (Object)null)) { tabMapModuleSnapshots.Add(new TabMapModuleRuntimeSnapshot { ModuleTransform = transform, SourceTransform = transform2 }); int childCount = transform.childCount; for (int j = 0; j < childCount; j++) { AddTransformSnapshot(transform.GetChild(j), tabMapModuleChildSnapshots); } } } } private void ApplyTabMapModuleZoom(Map map) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)map == (Object)null) { return; } RestoreTabMapModulePositions(map); for (int i = 0; i < tabMapModuleChildSnapshots.Count; i++) { TabMapTransformSnapshot tabMapTransformSnapshot = tabMapModuleChildSnapshots[i]; if (!((Object)(object)tabMapTransformSnapshot.Transform == (Object)null)) { Vector3 localPosition = tabMapTransformSnapshot.LocalPosition; localPosition.x *= tabMapZoomMultiplier; localPosition.z *= tabMapZoomMultiplier; tabMapTransformSnapshot.Transform.localPosition = localPosition; Vector3 localScale = tabMapTransformSnapshot.LocalScale; localScale.x *= tabMapZoomMultiplier; localScale.z *= tabMapZoomMultiplier; tabMapTransformSnapshot.Transform.localScale = localScale; } } } private void RestoreTabMapModuleChildSnapshots() { RestoreTransformSnapshots(tabMapModuleChildSnapshots); } private static void RestoreTabMapModulePositions(Map map) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Instance == (Object)null || (Object)(object)map == (Object)null || (Object)(object)Instance.tabMapOverLayerTransform == (Object)null) { return; } Vector3 position = Instance.tabMapOverLayerTransform.position; for (int i = 0; i < Instance.tabMapModuleSnapshots.Count; i++) { TabMapModuleRuntimeSnapshot tabMapModuleRuntimeSnapshot = Instance.tabMapModuleSnapshots[i]; if (!((Object)(object)tabMapModuleRuntimeSnapshot.ModuleTransform == (Object)null) && !((Object)(object)tabMapModuleRuntimeSnapshot.SourceTransform == (Object)null)) { tabMapModuleRuntimeSnapshot.ModuleTransform.position = tabMapModuleRuntimeSnapshot.SourceTransform.position * map.Scale + position + Instance.tabMapOverLayerOffset; } } } private bool HasTabMapStructureChanged(Map map) { if ((Object)(object)map == (Object)null) { return false; } int num = ((map.Layers != null) ? map.Layers.Count : 0); int num2 = ((map.MapModules != null) ? map.MapModules.Count : 0); if (num == tabMapKnownLayerCount && num2 == tabMapKnownModuleCount) { return (Object)(object)map.OverLayerParent != (Object)(object)tabMapOverLayerTransform; } return true; } private void UpdateTabMapStructureCounts(Map map) { tabMapKnownLayerCount = ((map?.Layers != null) ? map.Layers.Count : 0); tabMapKnownModuleCount = ((map?.MapModules != null) ? map.MapModules.Count : 0); } private static bool ShouldZoomTabMapLayer(Map map, Transform transform) { if ((Object)(object)transform == (Object)null) { return false; } if (!((Object)(object)map == (Object)null) && !((Object)(object)map.OverLayerParent == (Object)null)) { return (Object)(object)transform != (Object)(object)map.OverLayerParent; } return true; } private static Vector3 GetTabMapAnchorInParentSpace(Map map, Transform target) { //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_0034: Unknown result type (might be due to invalid IL or missing references) Transform val = (((Object)(object)map != (Object)null && (Object)(object)map.ActiveParent != (Object)null) ? map.ActiveParent.transform : target.parent); if (!((Object)(object)val != (Object)null)) { return Vector3.zero; } return ToParentSpace(target, val.position); } private static bool TryGetMapContentBounds(Map map, Transform target, out Bounds bounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) bounds = default(Bounds); bool hasBounds = false; if ((Object)(object)map != (Object)null && map.Layers != null) { foreach (MapLayer layer in map.Layers) { if (!((Object)(object)layer == (Object)null) && ShouldZoomTabMapLayer(map, ((Component)layer).transform)) { EncapsulateRenderers(((Component)layer).transform, ref bounds, ref hasBounds); } } } if (hasBounds) { return true; } if ((Object)(object)target == (Object)null) { return false; } EncapsulateRenderers(target, ref bounds, ref hasBounds); return hasBounds; } private static void EncapsulateRenderers(Transform root, ref Bounds bounds, ref bool hasBounds) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) if ((Object)(object)root == (Object)null) { return; } Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(false); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && val.enabled) { if (!hasBounds) { bounds = val.bounds; hasBounds = true; } else { ((Bounds)(ref bounds)).Encapsulate(val.bounds); } } } } private static Vector3 ToParentSpace(Transform target, Vector3 worldPoint) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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) if (!((Object)(object)target != (Object)null) || !((Object)(object)target.parent != (Object)null)) { return worldPoint; } return target.parent.InverseTransformPoint(worldPoint); } private static Vector3 ScaleTabMapLocalVector(Vector3 value, float multiplier) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) value.x *= multiplier; value.z *= multiplier; return value; } private static void AddTransformSnapshot(Transform transform, List<TabMapTransformSnapshot> snapshots) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)transform == (Object)null)) { snapshots?.Add(new TabMapTransformSnapshot { Transform = transform, LocalPosition = transform.localPosition, LocalScale = transform.localScale }); } } private static void RestoreTransformSnapshots(List<TabMapTransformSnapshot> snapshots) { //IL_0025: 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) if (snapshots == null) { return; } for (int i = 0; i < snapshots.Count; i++) { TabMapTransformSnapshot tabMapTransformSnapshot = snapshots[i]; if (!((Object)(object)tabMapTransformSnapshot.Transform == (Object)null)) { tabMapTransformSnapshot.Transform.localPosition = tabMapTransformSnapshot.LocalPosition; tabMapTransformSnapshot.Transform.localScale = tabMapTransformSnapshot.LocalScale; } } } private void CacheTabMapOverLayerData(Map map) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) tabMapOverLayerTransform = (((Object)(object)map != (Object)null) ? map.OverLayerParent : null); tabMapOverLayerOffset = Vector3.zero; if (!((Object)(object)map == (Object)null) && !((Object)(object)tabMapOverLayerTransform == (Object)null)) { MapLayer component = ((Component)tabMapOverLayerTransform).GetComponent<MapLayer>(); if ((Object)(object)component != (Object)null) { tabMapOverLayerOffset = map.GetLayerPosition(component.layer); } } } private void ApplyHarmonyPatches() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown harmony = new Harmony("zichen.tabmapzoom.patch"); harmony.Patch((MethodBase)AccessTools.Method(typeof(Map), "ActiveSet", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ZichenTabMapZoomPlugin), "MapActiveSetPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private void TryBootstrapUpdateLoopFromCurrentMap() { Map instance = Map.Instance; bool flag = (Object)(object)instance != (Object)null && instance.Active && IsRunLevelActive() && IsTabMapZoomEnabledEffective(); SetUpdateLoopActive(flag); if (flag) { tabMapZoomDirty = true; } } private void SetUpdateLoopActive(bool active) { ((Behaviour)this).enabled = active; } private void OnMapActiveSet(Map map, bool active) { if (!active) { if (tabMapRestoreOfficialOnOpen != null && tabMapRestoreOfficialOnOpen.Value) { ResetTabMapZoom(); } SetUpdateLoopActive(active: false); } else { tabMapZoomDirty = true; bool updateLoopActive = (Object)(object)map != (Object)null && IsRunLevelActive() && IsTabMapZoomEnabledEffective(); SetUpdateLoopActive(updateLoopActive); } } private static void MapActiveSetPostfix(Map __instance, bool active) { Instance?.OnMapActiveSet(__instance, active); } private float ClampTabMapZoom(float value) { float num = Mathf.Min(tabMapMinZoom.Value, tabMapMaxZoom.Value); float num2 = Mathf.Max(tabMapMinZoom.Value, tabMapMaxZoom.Value); return Mathf.Clamp(value, num, num2); } private static bool IsRunLevelActive() { try { return SemiFunc.RunIsLevel(); } catch { return true; } } private void MakePluginHostPersistent() { try { ((Component)this).transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to mark plugin host persistent: " + ex.GetType().Name + ": " + ex.Message)); } } private void ResetConfigIfVersionChanged() { try { string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; string text = ReadConfigPluginVersion(configFilePath); if (!string.IsNullOrWhiteSpace(text) && !(text == "1.1.1")) { ResetConfigFileToDefaults(configFilePath); ((BaseUnityPlugin)this).Logger.LogWarning((object)"Config version changed. Old config was reset to defaults."); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to reset config by version: " + ex.Message)); } } private static string ReadConfigPluginVersion(string configPath) { if (!File.Exists(configPath)) { return null; } Match match = Regex.Match(File.ReadAllText(configPath), "(?m)^模组版本号\\s*=\\s*(.+?)\\s*$"); if (!match.Success) { return null; } return match.Groups[1].Value.Trim(); } private void ResetConfigFileToDefaults(string configPath) { ((BaseUnityPlugin)this).Config.Clear(); if (File.Exists(configPath)) { File.Delete(configPath); } ((BaseUnityPlugin)this).Config.Reload(); } } internal struct TabMapTransformSnapshot { public Transform Transform; public Vector3 LocalPosition; public Vector3 LocalScale; } internal struct TabMapModuleRuntimeSnapshot { public Transform ModuleTransform; public Transform SourceTransform; } namespace Zichen_TabMapZoom.I18N { internal static class LanguageDetector { public static readonly string[] DefaultChineseKeywords = new string[6] { "chinese", "简体", "繁體", "繁体", "汉化", "中文" }; public static readonly string[] DefaultChineseEnvironmentPluginGuids = new string[1] { "gravydevsupreme.xunity.autotranslator" }; private const string ChineseLocaleCodePrefix = "zh"; private static bool _reflectionResolved; private static Type _localizationSettingsType; private static PropertyInfo _selectedLocaleProp; private static PropertyInfo _localeIdentifierProp; private static PropertyInfo _identifierCodeProp; public static bool DetectDefault() { if (DetectFromGameLocale()) { return true; } return DetectDefault(DefaultChineseKeywords, DefaultChineseEnvironmentPluginGuids); } public static bool IsGameLocaleChinese() { return DetectFromGameLocale(); } public static bool TryReadGameLocaleCode(out string code) { code = null; try { code = TryReadGameActiveLocaleCode(); if (!string.IsNullOrEmpty(code)) { return true; } code = TryReadSelectedLocaleCode(); return !string.IsNullOrEmpty(code); } catch { code = null; return false; } } public static bool IsChineseCode(string code) { if (!string.IsNullOrEmpty(code)) { return code.StartsWith("zh", StringComparison.OrdinalIgnoreCase); } return false; } private static bool DetectFromGameLocale() { if (TryReadGameLocaleCode(out var code)) { return IsChineseCode(code); } return false; } private static string TryReadGameActiveLocaleCode() { try { Type type = Type.GetType("LocalizationManager, Assembly-CSharp"); if (type == null) { return null; } object obj = type.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj == null) { return null; } object obj2 = type.GetField("ActiveLocale")?.GetValue(obj); if (obj2 == null) { return null; } return ReadLocaleCode(obj2.GetType().GetField("LocaleReference")?.GetValue(obj2)); } catch { return null; } } private static string TryReadSelectedLocaleCode() { try { ResolveReflectionCache(); if (_localizationSettingsType == null || _selectedLocaleProp == null) { return null; } return ReadLocaleCode(_selectedLocaleProp.GetValue(null, null)); } catch { return null; } } private static string ReadLocaleCode(object locale) { if (locale == null) { return null; } try { object obj = _localeIdentifierProp?.GetValue(locale, null); if (obj == null) { return null; } return _identifierCodeProp?.GetValue(obj, null) as string; } catch { return null; } } private static void ResolveReflectionCache() { if (_reflectionResolved) { return; } _reflectionResolved = true; try { _localizationSettingsType = Type.GetType("UnityEngine.Localization.Settings.LocalizationSettings, Unity.Localization"); if (_localizationSettingsType != null) { _selectedLocaleProp = _localizationSettingsType.GetProperty("SelectedLocale"); if (_selectedLocaleProp != null) { _localeIdentifierProp = _selectedLocaleProp.PropertyType?.GetProperty("Identifier"); _identifierCodeProp = (_localeIdentifierProp?.PropertyType)?.GetProperty("Code"); } } } catch { _localizationSettingsType = null; _selectedLocaleProp = null; } } public static bool DetectDefault(IEnumerable<string> chineseKeywords, IEnumerable<string> chineseEnvironmentPluginGuids) { string[] array = ToFilteredArray(chineseKeywords) ?? DefaultChineseKeywords; string[] array2 = ToFilteredArray(chineseEnvironmentPluginGuids) ?? DefaultChineseEnvironmentPluginGuids; try { foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos) { string text = pluginInfo.Key ?? string.Empty; PluginInfo value = pluginInfo.Value; object obj; if (value == null) { obj = null; } else { BepInPlugin metadata = value.Metadata; obj = ((metadata != null) ? metadata.Name : null); } if (obj == null) { obj = string.Empty; } string text2 = (string)obj; foreach (string value2 in array) { if (text.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } for (int j = 0; j < array2.Length; j++) { if (string.Equals(text, array2[j], StringComparison.OrdinalIgnoreCase)) { return true; } } } } catch { } return false; } private static string[] ToFilteredArray(IEnumerable<string> source) { if (source == null) { return null; } List<string> list = new List<string>(); foreach (string item in source) { if (!string.IsNullOrEmpty(item)) { list.Add(item); } } if (list.Count != 0) { return list.ToArray(); } return null; } } internal sealed class I18NRegistry { public sealed class CfgI18N { public string SectionEN; public string SectionCN; public string KeyEN; public string KeyCN; public string DescEN; public string DescCN; public ConfigurationManagerAttributes Attrs; public string ValueEN; public string ValueCN; public ConfigEntry<string> ReadOnlyEntry; } public enum DisplayLanguage { 中文, English } private readonly ConfigFile _config; private readonly ManualLogSource _logger; private readonly List<CfgI18N> _all = new List<CfgI18N>(20); private readonly Dictionary<string, CfgI18N> _bySection = new Dictionary<string, CfgI18N>(StringComparer.Ordinal); private readonly Dictionary<string, Dictionary<string, CfgI18N>> _bySectionAndKey = new Dictionary<string, Dictionary<string, CfgI18N>>(StringComparer.Ordinal); public ConfigEntry<DisplayLanguage> LanguageEntry { get; private set; } public bool UseChinese { get { if (LanguageEntry != null) { return LanguageEntry.Value == DisplayLanguage.中文; } return false; } } public int RegisteredCount => _all.Count; public IReadOnlyList<CfgI18N> All => _all; public event Action LanguageChanged; public I18NRegistry(ConfigFile config, ManualLogSource logger = null) { _config = config ?? throw new ArgumentNullException("config"); _logger = logger; } public ConfigEntry<DisplayLanguage> BindLanguage(bool defaultChinese, string sectionEN = "Mod Info", string sectionCN = "模组信息") { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown DisplayLanguage displayLanguage = ((!defaultChinese) ? DisplayLanguage.English : DisplayLanguage.中文); CfgI18N cfgI18N = NewI18N(sectionEN, sectionCN, "Language", "语言", "Switch display language.", "切换显示语言。", readOnly: false); LanguageEntry = _config.Bind<DisplayLanguage>(sectionEN, "Language", displayLanguage, new ConfigDescription(cfgI18N.DescEN, (AcceptableValueBase)null, new object[1] { cfgI18N.Attrs })); LanguageEntry.SettingChanged += delegate { RefreshAttrs(); InvokeLanguageChanged(); }; return LanguageEntry; } public ConfigEntry<T> Bind<T>(string sectEN, string sectCN, string keyEN, string keyCN, string descEN, string descCN, T defaultValue, AcceptableValueBase acceptableValues = null, int order = 0, Action<ConfigEntryBase> customDrawer = null, bool? showRangeAsPercent = null, bool? isAdvanced = null, bool? browsable = null) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown CfgI18N cfgI18N = NewI18N(sectEN, sectCN, keyEN, keyCN, descEN, descCN, readOnly: false); if (order != 0) { cfgI18N.Attrs.Order = order; } if (customDrawer != null) { cfgI18N.Attrs.CustomDrawer = customDrawer; } if (showRangeAsPercent.HasValue) { cfgI18N.Attrs.ShowRangeAsPercent = showRangeAsPercent.Value; } if (isAdvanced.HasValue) { cfgI18N.Attrs.IsAdvanced = isAdvanced.Value; } if (browsable.HasValue) { cfgI18N.Attrs.Browsable = browsable.Value; } return _config.Bind<T>(sectEN, keyEN, defaultValue, new ConfigDescription(descEN, acceptableValues, new object[1] { cfgI18N.Attrs })); } public ConfigEntry<string> BindReadOnly(string sectEN, string sectCN, string keyEN, string keyCN, string valueEN, string valueCN, int order = 0, Action<ConfigEntryBase> customDrawer = null) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown CfgI18N cfgI18N = NewI18N(sectEN, sectCN, keyEN, keyCN, "", "", readOnly: true); cfgI18N.ValueEN = valueEN; cfgI18N.ValueCN = valueCN; if (order != 0) { cfgI18N.Attrs.Order = order; } if (customDrawer != null) { cfgI18N.Attrs.CustomDrawer = customDrawer; } cfgI18N.ReadOnlyEntry = _config.Bind<string>(sectEN, keyEN, valueEN, new ConfigDescription("", (AcceptableValueBase)null, new object[1] { cfgI18N.Attrs })); string text = (UseChinese ? valueCN : valueEN); if (cfgI18N.ReadOnlyEntry.Value != text) { cfgI18N.ReadOnlyEntry.Value = text; } cfgI18N.ReadOnlyEntry.SettingChanged += MakeReadOnlyValueGuard(cfgI18N); return cfgI18N.ReadOnlyEntry; } private EventHandler MakeReadOnlyValueGuard(CfgI18N i18n) { return delegate { if (i18n.ReadOnlyEntry != null) { string text = (UseChinese ? i18n.ValueCN : i18n.ValueEN); if (i18n.ReadOnlyEntry.Value != text) { i18n.ReadOnlyEntry.Value = text; InvokeLanguageChanged(); } } }; } private void InvokeLanguageChanged() { Action action = this.LanguageChanged; if (action == null) { return; } Delegate[] invocationList = action.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action action2 = (Action)invocationList[i]; try { action2(); } catch (Exception ex) { ManualLogSource logger = _logger; if (logger != null) { logger.LogWarning((object)("LanguageChanged 订阅方异常:" + ex.Message)); } } } } private CfgI18N NewI18N(string sectEN, string sectCN, string keyEN, string keyCN, string descEN, string descCN, bool readOnly) { CfgI18N cfgI18N = new CfgI18N { SectionEN = sectEN, SectionCN = sectCN, KeyEN = keyEN, KeyCN = keyCN, DescEN = descEN, DescCN = descCN, Attrs = (readOnly ? new ConfigurationManagerAttributes { ReadOnly = true, HideDefaultButton = true } : new ConfigurationManagerAttributes()) }; ApplyI18N(cfgI18N); _all.Add(cfgI18N); _bySection[sectEN] = cfgI18N; if (!_bySectionAndKey.TryGetValue(sectEN, out var value)) { value = new Dictionary<string, CfgI18N>(StringComparer.Ordinal); _bySectionAndKey[sectEN] = value; } value[keyEN] = cfgI18N; return cfgI18N; } private void ApplyI18N(CfgI18N i18n) { bool useChinese = UseChinese; i18n.Attrs.Category = (useChinese ? i18n.SectionCN : i18n.SectionEN); i18n.Attrs.DispName = (useChinese ? i18n.KeyCN : i18n.KeyEN); i18n.Attrs.Description = (useChinese ? i18n.DescCN : i18n.DescEN); if (i18n.ReadOnlyEntry != null) { string text = (useChinese ? i18n.ValueCN : i18n.ValueEN); if (i18n.ReadOnlyEntry.Value != text) { i18n.ReadOnlyEntry.Value = text; } } } public void RefreshAttrs() { for (int i = 0; i < _all.Count; i++) { ApplyI18N(_all[i]); } } public void RefreshNow() { RefreshAttrs(); InvokeLanguageChanged(); } public bool TryGetBySection(string sectEN, out CfgI18N i18n) { return _bySection.TryGetValue(sectEN, out i18n); } public bool TryGetByKey(string keyEN, out CfgI18N i18n) { i18n = null; foreach (KeyValuePair<string, Dictionary<string, CfgI18N>> item in _bySectionAndKey) { if (item.Value.TryGetValue(keyEN, out i18n)) { return true; } } return false; } } internal sealed class ConfigurationManagerAttributes { public bool? ShowRangeAsPercent; public Action<ConfigEntryBase> CustomDrawer; public bool? Browsable; public string Category; public object DefaultValue; public bool? HideDefaultButton; public bool? HideSettingName; public string Description; public string DispName; public int? Order; public bool? ReadOnly; public bool? IsAdvanced; } internal sealed class I18NSwitcher { private sealed class LiveLabel { public WeakReference<TextMeshProUGUI> Tmp; public I18NRegistry.CfgI18N I18N; public bool IsSection; } private sealed class LiveValue { public WeakReference<Object> InputField; public I18NRegistry.CfgI18N I18N; } private sealed class LiveToggleButtons { public WeakReference<TextMeshProUGUI> LeftTmp; public WeakReference<TextMeshProUGUI> RightTmp; public bool IsLanguage; } private readonly I18NRegistry _registry; private readonly Harmony _harmony; private readonly ManualLogSource _logger; private readonly string _pluginName; private readonly string _pluginNameFixedForm; private readonly List<LiveLabel> _liveLabels = new List<LiveLabel>(64); private readonly List<LiveValue> _liveValues = new List<LiveValue>(8); private readonly List<LiveToggleButtons> _liveToggleButtons = new List<LiveToggleButtons>(16); private static readonly Dictionary<Type, FieldInfo> _labelTmpFieldCache = new Dictionary<Type, FieldInfo>(8); private static readonly Dictionary<Type, FieldInfo> _inputStringSystemFieldCache = new Dictionary<Type, FieldInfo>(2); private static readonly Dictionary<Type, MethodInfo> _inputStringSystemSetValueCache = new Dictionary<Type, MethodInfo>(2); private static readonly Dictionary<Type, FieldInfo> _toggleLeftTmpCache = new Dictionary<Type, FieldInfo>(4); private static readonly Dictionary<Type, FieldInfo> _toggleRightTmpCache = new Dictionary<Type, FieldInfo>(4); private static readonly object _reflectionCacheLock = new object(); private static readonly object _globalInstancesLock = new object(); private static readonly List<WeakReference<I18NSwitcher>> _globalInstances = new List<WeakReference<I18NSwitcher>>(2); public int LiveUiCount => _liveLabels.Count + _liveValues.Count + _liveToggleButtons.Count; public I18NSwitcher(I18NRegistry registry, Harmony harmony, string pluginName, ManualLogSource logger = null) { _registry = registry ?? throw new ArgumentNullException("registry"); _harmony = harmony ?? throw new ArgumentNullException("harmony"); _pluginName = pluginName ?? throw new ArgumentNullException("pluginName"); _pluginNameFixedForm = FixNamingForOwnership(pluginName); _logger = logger; _registry.LanguageChanged += RefreshLiveUi; } private static string FixNamingForOwnership(string input) { if (string.IsNullOrEmpty(input)) { return input; } input = Regex.Replace(input, "([a-z])([A-Z])", "$1 $2"); input = Regex.Replace(input, "([A-Z])([A-Z][a-z])", "$1 $2"); input = Regex.Replace(input, "\\s+", " "); input = Regex.Replace(input, "([A-Z]\\.)\\s([A-Z]\\.)", "$1$2"); return input.Trim(); } public void InstallRepoConfigHook() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("MenuLib.MenuAPI"); if (type == null) { ManualLogSource logger = _logger; if (logger != null) { logger.LogInfo((object)"MenuLib 未安装,跳过 REPOConfig 双语劫持。"); } return; } HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(I18NSwitcher), "PostfixDispatch", (Type[])null, (Type[])null)) { priority = 0 }; HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal) { "CreateREPOLabel", "CreateREPOToggle", "CreateREPOSlider", "CreateREPOInputField", "CreateREPOButton" }; int num = 0; foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type)) { if (!hashSet.Contains(declaredMethod.Name)) { continue; } ParameterInfo[] parameters = declaredMethod.GetParameters(); if (parameters.Length != 0 && !(parameters[0].ParameterType != typeof(string))) { try { _harmony.Patch((MethodBase)declaredMethod, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } catch { } } } if (num > 0) { ManualLogSource logger2 = _logger; if (logger2 != null) { logger2.LogInfo((object)$"REPOConfig 双语显示已挂载({num} 个 UI 创建方法)。"); } } else { ManualLogSource logger3 = _logger; if (logger3 != null) { logger3.LogInfo((object)"REPOConfig 没找到可劫持的 UI 方法。"); } } RegisterGlobalInstance(this); } catch (Exception ex) { ManualLogSource logger4 = _logger; if (logger4 != null) { logger4.LogInfo((object)("REPOConfig 劫持失败:" + ex.Message)); } } } private static void RegisterGlobalInstance(I18NSwitcher inst) { lock (_globalInstancesLock) { _globalInstances.Add(new WeakReference<I18NSwitcher>(inst)); } } public static void PostfixDispatch(object[] __args, object __result) { try { if (__args == null || __args.Length == 0 || __result == null || !(__args[0] is string text) || string.IsNullOrEmpty(text)) { return; } List<I18NSwitcher> list = null; lock (_globalInstancesLock) { for (int num = _globalInstances.Count - 1; num >= 0; num--) { if (!_globalInstances[num].TryGetTarget(out var target)) { _globalInstances.RemoveAt(num); } else { (list ?? (list = new List<I18NSwitcher>(_globalInstances.Count))).Add(target); } } } if (list != null) { for (int i = 0; i < list.Count; i++) { list[i].TryRegisterLiveLabel(text, __result); } } } catch { } } private static string GetOwningPluginName(object uiElement) { try { object obj = ((uiElement is Component) ? uiElement : null); GameObject val = (GameObject)(((obj != null) ? ((Component)obj).gameObject : null) ?? ((uiElement is GameObject) ? uiElement : null)); if ((Object)(object)val == (Object)null) { return null; } Transform val2 = val.transform; while ((Object)(object)val2 != (Object)null) { string name = ((Object)((Component)val2).gameObject).name; if (name != null && name.StartsWith("Menu Page ", StringComparison.Ordinal)) { return name.Substring("Menu Page ".Length); } val2 = val2.parent; } } catch { } return null; } private void TryRegisterLiveLabel(string label, object uiElement) { string owningPluginName = GetOwningPluginName(uiElement); if ((owningPluginName != null && !string.Equals(owningPluginName, _pluginNameFixedForm, StringComparison.Ordinal)) || (!_registry.TryGetBySection(label, out var i18n) && !_registry.TryGetByKey(label, out i18n))) { return; } bool flag = string.Equals(label, i18n.SectionEN, StringComparison.Ordinal); TextMeshProUGUI labelTmp = GetLabelTmp(uiElement); if ((Object)(object)labelTmp == (Object)null) { return; } bool useChinese = _registry.UseChinese; string text = ((!flag) ? (useChinese ? i18n.KeyCN : i18n.KeyEN) : (useChinese ? i18n.SectionCN : i18n.SectionEN)); if (!string.IsNullOrEmpty(text)) { try { ((TMP_Text)labelTmp).text = text; } catch { } } _liveLabels.Add(new LiveLabel { Tmp = new WeakReference<TextMeshProUGUI>(labelTmp), I18N = i18n, IsSection = flag }); if (!flag && i18n.ReadOnlyEntry != null) { Object val = (Object)((uiElement is Object) ? uiElement : null); if (val != null && HasInputStringSystemField(((object)val).GetType())) { _liveValues.Add(new LiveValue { InputField = new WeakReference<Object>(val), I18N = i18n }); string text2 = (useChinese ? i18n.ValueCN : i18n.ValueEN); if (i18n.ReadOnlyEntry.Value != text2) { i18n.ReadOnlyEntry.Value = text2; } TrySetInputFieldValue(val, text2); } } if (flag) { return; } Type type = uiElement.GetType(); FieldInfo value; FieldInfo value2; lock (_reflectionCacheLock) { if (!_toggleLeftTmpCache.TryGetValue(type, out value)) { value = AccessTools.Field(type, "leftButtonTMP"); _toggleLeftTmpCache[type] = value; } if (!_toggleRightTmpCache.TryGetValue(type, out value2)) { value2 = AccessTools.Field(type, "rightButtonTMP"); _toggleRightTmpCache[type] = value2; } } if (value != null && value2 != null) { object? value3 = value.GetValue(uiElement); TextMeshProUGUI val2 = (TextMeshProUGUI)((value3 is TextMeshProUGUI) ? value3 : null); object? value4 = value2.GetValue(uiElement); TextMeshProUGUI val3 = (TextMeshProUGUI)((value4 is TextMeshProUGUI) ? value4 : null); if ((Object)(object)val2 != (Object)null && (Object)(object)val3 != (Object)null) { _liveToggleButtons.Add(new LiveToggleButtons { LeftTmp = new WeakReference<TextMeshProUGUI>(val2), RightTmp = new WeakReference<TextMeshProUGUI>(val3), IsLanguage = false }); ApplyToggleButtonText(val2, val3, useChinese, isLanguage: false); } } } private static TextMeshProUGUI GetLabelTmp(object uiElement) { try { Type type = uiElement.GetType(); FieldInfo value; lock (_reflectionCacheLock) { if (!_labelTmpFieldCache.TryGetValue(type, out value)) { value = AccessTools.Field(type, "labelTMP"); _labelTmpFieldCache[type] = value; } } object? obj = value?.GetValue(uiElement); return (TextMeshProUGUI)((obj is TextMeshProUGUI) ? obj : null); } catch { return null; } } private static bool HasInputStringSystemField(Type t) { lock (_reflectionCacheLock) { if (!_inputStringSystemFieldCache.TryGetValue(t, out var value)) { value = AccessTools.Field(t, "inputStringSystem"); _inputStringSystemFieldCache[t] = value; } return value != null; } } private static bool TrySetInputFieldValue(Object ifObj, string newVal) { try { Type type = ((object)ifObj).GetType(); FieldInfo value; lock (_reflectionCacheLock) { if (!_inputStringSystemFieldCache.TryGetValue(type, out value)) { value = AccessTools.Field(type, "inputStringSystem"); _inputStringSystemFieldCache[type] = value; } } if (value == null) { return false; } object value2 = value.GetValue(ifObj); if (value2 == null) { return false; } MethodInfo value3; lock (_reflectionCacheLock) { Type type2 = value2.GetType(); if (!_inputStringSystemSetValueCache.TryGetValue(type2, out value3)) { value3 = AccessTools.Method(type2, "SetValue", new Type[2] { typeof(string), typeof(bool) }, (Type[])null); _inputStringSystemSetValueCache[type2] = value3; } } if (value3 == null) { return false; } value3.Invoke(value2, new object[2] { newVal ?? string.Empty, false }); return true; } catch { return false; } } private static void ApplyToggleButtonText(TextMeshProUGUI leftTmp, TextMeshProUGUI rightTmp, bool cn, bool isLanguage) { try { if (isLanguage) { ((TMP_Text)leftTmp).text = (cn ? "中文" : "Chinese"); ((TMP_Text)rightTmp).text = (cn ? "英文" : "English"); } else { ((TMP_Text)leftTmp).text = (cn ? "开" : "ON"); ((TMP_Text)rightTmp).text = (cn ? "关" : "OFF"); } } catch { } } private void RefreshLiveUi() { bool useChinese = _registry.UseChinese; for (int num = _liveLabels.Count - 1; num >= 0; num--) { LiveLabel liveLabel = _liveLabels[num]; if (!liveLabel.Tmp.TryGetTarget(out var target) || (Object)(object)target == (Object)null || (Object)(object)((Component)target).gameObject == (Object)null) { _liveLabels.RemoveAt(num); } else { string text = ((!liveLabel.IsSection) ? (useChinese ? liveLabel.I18N.KeyCN : liveLabel.I18N.KeyEN) : (useChinese ? liveLabel.I18N.SectionCN : liveLabel.I18N.SectionEN)); try { ((TMP_Text)target).text = text; } catch { _liveLabels.RemoveAt(num); } } } for (int num2 = _liveValues.Count - 1; num2 >= 0; num2--) { LiveValue liveValue = _liveValues[num2]; if (!liveValue.InputField.TryGetTarget(out var target2) || target2 == (Object)null) { _liveValues.RemoveAt(num2); } else { string newVal = (useChinese ? liveValue.I18N.ValueCN : liveValue.I18N.ValueEN); if (!TrySetInputFieldValue(target2, newVal)) { _liveValues.RemoveAt(num2); } } } for (int num3 = _liveToggleButtons.Count - 1; num3 >= 0; num3--) { LiveToggleButtons liveToggleButtons = _liveToggleButtons[num3]; if (!liveToggleButtons.LeftTmp.TryGetTarget(out var target3) || (Object)(object)target3 == (Object)null || !liveToggleButtons.RightTmp.TryGetTarget(out var target4) || (Object)(object)target4 == (Object)null) { _liveToggleButtons.RemoveAt(num3); } else { ApplyToggleButtonText(target3, target4, useChinese, liveToggleButtons.IsLanguage); } } } } }