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 ValheimTW v1.2.9
plugins/ValheimTW.dll
Decompiled 2 days 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.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [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("ValheimTW")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ValheimTW")] [assembly: AssemblyTitle("ValheimTW")] [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; } } } namespace ValheimTW { internal static class ConfigLocalizer { internal static readonly Regex Cjk = new Regex("[一-鿿]", RegexOptions.Compiled); private static readonly Dictionary<ModDictionary, int> Pending = new Dictionary<ModDictionary, int>(); private static readonly List<ModDictionary> Buffer = new List<ModDictionary>(8); private const int PendingTimeoutMs = 60000; private static readonly Dictionary<string, Dictionary<string, string>> PristineDescByPath = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase); internal static bool Enabled => LocEngine.ConfigUiEnabled; internal static string PristineDescriptionFor(string guid, string section, string key) { if (string.IsNullOrEmpty(guid) || string.IsNullOrEmpty(section) || string.IsNullOrEmpty(key)) { return null; } try { if (!PristineDescByPath.TryGetValue(guid, out var value) || value == null) { return null; } string value2; return value.TryGetValue(section + "::" + key, out value2) ? value2 : null; } catch (Exception) { return null; } } internal static void RequestAll() { IList<ModDictionary> dictionaries = LocEngine.Dictionaries; for (int i = 0; i < dictionaries.Count; i++) { RequestFor(dictionaries[i]); } } internal static void RequestFor(ModDictionary d) { if (d != null && Enabled) { if (Pending.Count > 128) { Pending.Clear(); } Pending[d] = Environment.TickCount; } } internal static void Drain() { if (Pending.Count == 0) { return; } if (!Enabled) { Pending.Clear(); return; } Buffer.Clear(); foreach (KeyValuePair<ModDictionary, int> item in Pending) { Buffer.Add(item.Key); } for (int i = 0; i < Buffer.Count; i++) { ModDictionary modDictionary = Buffer[i]; int value; if (LocalizeOne(modDictionary)) { Pending.Remove(modDictionary); } else if (Pending.TryGetValue(modDictionary, out value) && Environment.TickCount - value > 60000) { Pending.Remove(modDictionary); } } } private static bool LocalizeOne(ModDictionary d) { if (d == null || d.TargetGuids.Length == 0) { return true; } if (!d.HasConfigText) { return true; } ConfigFile val = null; bool flag = false; for (int i = 0; i < d.TargetGuids.Length; i++) { if (LocEngine.IsPluginLoaded(d.TargetGuids[i])) { if (Chainloader.PluginInfos.TryGetValue(d.TargetGuids[i], out var value) && value != null && (Object)(object)value.Instance != (Object)null && value.Instance.Config != null) { val = value.Instance.Config; break; } flag = true; } } if (val == null) { if (flag) { return !d.AnyPluginLoaded(); } return true; } string activeGuid = d.FirstLoadedGuid() ?? d.TargetGuids[0]; int num = RestoreDescriptions(val, d, activeGuid); if (num > 0) { val.Save(); ValheimTWPlugin.Log($"已把 {d.TargetName} 的 {num} 条被我方污染的配置说明恢复为英文原文。"); } return true; } private static int RestoreDescriptions(ConfigFile cfg, ModDictionary d, string activeGuid) { int num = 0; Dictionary<string, string> dictionary = null; foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in cfg) { ConfigEntryBase value = item.Value; if (value == null) { continue; } ConfigDescription description = value.Description; if (description == null || string.IsNullOrEmpty(description.Description)) { continue; } if (!Cjk.IsMatch(description.Description)) { Snapshot(activeGuid, item.Key.Section, item.Key.Key, description.Description); continue; } string text = ((PristineDescByPath.TryGetValue(activeGuid, out var value2) && value2 != null && value2.TryGetValue(item.Key.Section + "::" + item.Key.Key, out var value3)) ? value3 : null); if (string.IsNullOrEmpty(text) || Cjk.IsMatch(text)) { if (dictionary == null) { dictionary = new Dictionary<string, string>(StringComparer.Ordinal); foreach (KeyValuePair<string, string> configDescription in d.ConfigDescriptions) { string text2 = ModDictionary.StripSyncTail(configDescription.Value); if (!string.IsNullOrEmpty(text2) && Cjk.IsMatch(text2)) { if (dictionary.ContainsKey(text2)) { dictionary[text2] = null; } else { dictionary[text2] = configDescription.Key; } } } } if (!dictionary.TryGetValue(description.Description, out var value4) || string.IsNullOrEmpty(value4)) { continue; } text = value4; } if (string.IsNullOrEmpty(text) || Cjk.IsMatch(text)) { continue; } text = text.Replace("\r\n", " ").Replace('\r', ' ').Replace('\n', ' '); bool flag = false; string text3 = text; for (int i = 0; i < text3.Length; i++) { if (text3[i] < ' ') { flag = true; break; } } if (!flag && TrySetDescription(value, text)) { num++; } } return num; } private static void Snapshot(string guid, string section, string key, string english) { if (string.IsNullOrEmpty(guid) || string.IsNullOrEmpty(english) || string.IsNullOrEmpty(section) || string.IsNullOrEmpty(key)) { return; } try { if (!PristineDescByPath.TryGetValue(guid, out var value) || value == null) { value = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); PristineDescByPath[guid] = value; } string key2 = section + "::" + key; if (!value.ContainsKey(key2) && value.Count < 8000) { value[key2] = english; } } catch (Exception) { } } private static bool TrySetDescription(ConfigEntryBase entry, string text) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown try { PropertyInfo property = ((object)entry).GetType().GetProperty("Description", BindingFlags.Instance | BindingFlags.Public); if (property != null && property.PropertyType == typeof(string)) { MethodInfo setMethod = property.GetSetMethod(nonPublic: false); if (setMethod != null) { setMethod.Invoke(entry, new object[1] { text }); return true; } } ConfigDescription description = entry.Description; ConfigDescription val = new ConfigDescription(text, description.AcceptableValues, description.Tags); PropertyInfo property2 = typeof(ConfigEntryBase).GetProperty("Description", BindingFlags.Instance | BindingFlags.Public); if (property2 != null) { MethodInfo setMethod2 = property2.GetSetMethod(nonPublic: true); if (setMethod2 != null) { setMethod2.Invoke(entry, new object[1] { val }); return true; } } FieldInfo field = typeof(ConfigEntryBase).GetField("<Description>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(entry, val); return true; } } catch (Exception) { } return false; } internal static void OnConfigSaved(ConfigFile file) { if (file == null || !Enabled) { return; } string configFilePath = file.ConfigFilePath; if (string.IsNullOrEmpty(configFilePath)) { return; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(configFilePath); ModDictionary modDictionary = LocEngine.Get(fileNameWithoutExtension); if (modDictionary == null || !modDictionary.Active || !modDictionary.HasConfigText) { return; } try { RestoreDescriptions(file, modDictionary, fileNameWithoutExtension); } catch (Exception) { } } } internal static class DictFormat { internal sealed class SrcLookup { private readonly Dictionary<string, string> _exact = new Dictionary<string, string>(StringComparer.Ordinal); private readonly Dictionary<string, string> _norm = new Dictionary<string, string>(StringComparer.Ordinal); internal void Add(string src, string zh) { if (!string.IsNullOrEmpty(src) && !string.IsNullOrEmpty(zh) && !string.Equals(src, zh, StringComparison.Ordinal)) { if (!_exact.ContainsKey(src)) { _exact[src] = zh; } string text = NormalizeWhitespace(src); if (!string.IsNullOrEmpty(text) && !_norm.ContainsKey(text)) { _norm[text] = zh; } } } internal bool Has(string src) { if (string.IsNullOrEmpty(src)) { return false; } if (_exact.ContainsKey(src)) { return true; } string text = NormalizeWhitespace(src); if (!string.IsNullOrEmpty(text)) { return _norm.ContainsKey(text); } return false; } internal bool TryGet(string src, out string zh) { zh = null; if (string.IsNullOrEmpty(src)) { return false; } if (_exact.TryGetValue(src, out zh)) { return true; } string text = NormalizeWhitespace(src); if (!string.IsNullOrEmpty(text) && _norm.TryGetValue(text, out zh)) { return true; } zh = null; return false; } } private static readonly Dictionary<string, string> CfgMarkers = new Dictionary<string, string>(StringComparer.Ordinal) { ["Server"] = "服务器", ["Yours"] = "你的", ["Key, yours"] = "按键,你的", ["Yours, follows the server while connected"] = "你的,联机时跟随服务器", ["Synced with Server"] = "已与服务器同步", ["Not Synced with Server"] = "未与服务器同步", ["Client side Config"] = "客户端配置", ["Local setting, not synced"] = "本地设置,不同步" }; private static readonly Regex CfgMarkRx = new Regex("^\\[([^\\[\\]]{1,80})\\]\\s"); public static JObject Transform(JObject doc) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown JToken obj = doc["meta"]; JObject val = (JObject)(object)((obj is JObject) ? obj : null); if (val != null) { val["schema"] = JToken.op_Implicit(4); } JToken obj2 = doc["objects"]; JObject val2 = (JObject)(object)((obj2 is JObject) ? obj2 : null); if (val2 != null) { doc["objects"] = (JToken)(object)TransformObjects(val2); } JToken obj3 = doc["strings"]; JObject val3 = (JObject)(object)((obj3 is JObject) ? obj3 : null); if (val3 != null) { JObject val4 = new JObject(); foreach (KeyValuePair<string, JToken> item in val3) { JToken value = item.Value; JObject val5 = (JObject)(object)((value is JObject) ? value : null); if (val5 != null && val5["from"] != null) { JObject val6 = new JObject(); if (val5["src"] != null) { val6["src"] = val5["src"]; } if (val5["zh"] != null) { val6["zh"] = val5["zh"]; } foreach (KeyValuePair<string, JToken> item2 in val5) { if (item2.Key != "src" && item2.Key != "zh" && item2.Key != "from") { val6[item2.Key] = item2.Value; } } val4[item.Key] = (JToken)(object)val6; } else { val4[item.Key] = item.Value; } } doc["strings"] = (JToken)(object)val4; } return doc; } private static JObject TransformObjects(JObject old) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_03f0: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Expected O, but got Unknown //IL_03ba: Unknown result type (might be due to invalid IL or missing references) //IL_03bf: Unknown result type (might be due to invalid IL or missing references) //IL_03c2: Expected O, but got Unknown //IL_03c7: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Expected O, but got Unknown //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Expected O, but got Unknown //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Expected O, but got Unknown JObject val = new JObject(); JObject val2 = new JObject(); Dictionary<string, JObject> dictionary = new Dictionary<string, JObject>(); foreach (KeyValuePair<string, JToken> item in old) { JToken value = item.Value; JObject val3 = (JObject)(object)((value is JObject) ? value : null); if (val3 == null) { continue; } string text = ((string)val3["type"]) ?? ""; string key = item.Key; if (text == "config_section") { string text2 = (key.StartsWith("cfgsec:", StringComparison.Ordinal) ? key.Substring(7) : key); JToken obj = val3["fields"]; string text3 = ZhOf((obj != null) ? obj[(object)"name"] : null); val[text2] = JToken.op_Implicit(text3 ?? ""); } else if (text == "config" && key.StartsWith("cfg:", StringComparison.Ordinal)) { string text4 = key.Substring(4); JToken obj2 = val3["fields"]; object obj3 = ((object)((obj2 is JObject) ? obj2 : null)) ?? ((object)new JObject()); JObject val4 = new JObject(); foreach (KeyValuePair<string, JToken> item2 in (JObject)obj3) { JToken value2 = item2.Value; JObject val5 = (JObject)(object)((value2 is JObject) ? value2 : null); if (val5 != null) { string text5 = (string)val5["ty"]; if (text5 == "binding" || text5 == "storage") { continue; } if (item2.Key == "name") { val4["name"] = JToken.op_Implicit(ZhOf((JToken)(object)val5) ?? ""); continue; } if (item2.Key == "desc") { JObject val6 = new JObject(); if (val5["src"] != null) { val6["src"] = val5["src"]; } val6["zh"] = val5["zh"] ?? JToken.op_Implicit(""); val4["desc"] = (JToken)(object)val6; continue; } if (item2.Key.StartsWith("value:", StringComparison.Ordinal)) { string text6 = ((string)val5["src"]) ?? item2.Key.Substring(6); val4["value:" + text6] = JToken.op_Implicit(ZhOf((JToken)(object)val5) ?? ""); continue; } JObject val7 = new JObject(); if (val5["src"] != null) { val7["src"] = val5["src"]; } val7["zh"] = val5["zh"] ?? JToken.op_Implicit(""); foreach (KeyValuePair<string, JToken> item3 in val5) { if (item3.Key != "src" && item3.Key != "zh" && item3.Key != "from") { val7[item3.Key] = item3.Value; } } val4[item2.Key] = (JToken)(object)val7; } else { val4[item2.Key] = item2.Value; } } val2[text4] = (JToken)(object)val4; } else { if (!dictionary.TryGetValue(text, out var value3)) { JObject val8 = new JObject(); JObject val9 = val8; dictionary[text] = val8; value3 = val9; } value3[key] = (JToken)(object)val3; } } JObject val10 = new JObject(); if (((JContainer)val).Count > 0) { val10["config_section"] = (JToken)(object)val; } if (((JContainer)val2).Count > 0) { val10["config"] = (JToken)(object)val2; } foreach (KeyValuePair<string, JObject> item4 in dictionary) { if (((JContainer)item4.Value).Count > 0) { val10[item4.Key] = (JToken)(object)item4.Value; } } return val10; } private static string ZhOf(JToken e) { JValue val = (JValue)(object)((e is JValue) ? e : null); if (val != null) { return ((object)val).ToString(); } JToken obj = ((e is JObject) ? e : null); if (obj == null) { return null; } return ((object)((JObject)obj)["zh"])?.ToString(); } public static int CarryOver(JObject doc, JObject official) { if (doc == null || official == null) { return 0; } SrcLookup srcLookup = BuildSrcLookup(official, withStrings: true, withObjects: false); SrcLookup srcLookup2 = BuildSrcLookup(official, withStrings: false); if (srcLookup == null && srcLookup2 == null) { return 0; } Dictionary<string, KeyValuePair<string, string>> dictionary = new Dictionary<string, KeyValuePair<string, string>>(StringComparer.Ordinal); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); JToken obj = official["strings"]; JObject val = (JObject)(object)((obj is JObject) ? obj : null); if (val != null) { foreach (KeyValuePair<string, JToken> item in val) { JToken value = item.Value; JObject val2 = (JObject)(object)((value is JObject) ? value : null); string text; string value2; if (val2 != null) { text = ((string)val2["src"]) ?? item.Key; value2 = (string)val2["zh"]; } else { text = item.Key; value2 = ((object)item.Value)?.ToString(); hashSet.Add(item.Key); } if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(value2)) { dictionary[item.Key] = new KeyValuePair<string, string>(text, value2); } } } int num = 0; JToken obj2 = doc["strings"]; JObject val3 = (JObject)(object)((obj2 is JObject) ? obj2 : null); if (val3 != null) { foreach (KeyValuePair<string, JToken> item2 in val3) { JToken value3 = item2.Value; JObject val4 = (JObject)(object)((value3 is JObject) ? value3 : null); if (val4 != null && string.IsNullOrEmpty((string)val4["zh"]) && !IsHexColorValue(((string)val4["src"]) ?? item2.Key)) { string a = ((string)val4["src"]) ?? item2.Key; string zh; if (dictionary.TryGetValue(item2.Key, out var value4) && (hashSet.Contains(item2.Key) || SrcEquivalent(a, value4.Key))) { val4["zh"] = JToken.op_Implicit(value4.Value); num++; } else if (srcLookup != null && srcLookup.TryGet(((string)val4["src"]) ?? item2.Key, out zh)) { val4["zh"] = JToken.op_Implicit(zh); num++; } } } } if (srcLookup2 != null) { JToken obj3 = doc["objects"]; JObject val5 = (JObject)(object)((obj3 is JObject) ? obj3 : null); if (val5 != null) { JToken obj4 = official["objects"]; JObject val6 = (JObject)(object)((obj4 is JObject) ? obj4 : null); if (val6 != null) { foreach (KeyValuePair<string, JToken> item3 in val5) { JToken value5 = item3.Value; JObject val7 = (JObject)(object)((value5 is JObject) ? value5 : null); if (val7 == null) { continue; } JToken obj5 = val6[item3.Key]; JObject val8 = (JObject)(object)((obj5 is JObject) ? obj5 : null); if (val8 == null) { continue; } if (item3.Key == "config_section") { foreach (KeyValuePair<string, JToken> item4 in val7) { JToken value6 = item4.Value; JValue val9 = (JValue)(object)((value6 is JValue) ? value6 : null); if (val9 != null && string.IsNullOrEmpty(((object)val9).ToString())) { JToken obj6 = val8[item4.Key]; JValue val10 = (JValue)(object)((obj6 is JValue) ? obj6 : null); if (val10 != null && !string.IsNullOrEmpty(((object)val10).ToString())) { val7[item4.Key] = JToken.op_Implicit(((object)val10).ToString()); num++; } } } continue; } if (item3.Key == "config") { foreach (KeyValuePair<string, JToken> item5 in val7) { JToken value7 = item5.Value; JObject val11 = (JObject)(object)((value7 is JObject) ? value7 : null); JToken obj7 = val8[item5.Key]; JObject val12 = (JObject)(object)((obj7 is JObject) ? obj7 : null); if (val11 == null || val12 == null) { continue; } JToken obj8 = val11["name"]; JValue val13 = (JValue)(object)((obj8 is JValue) ? obj8 : null); if (val13 != null && string.IsNullOrEmpty(((object)val13).ToString())) { string text2 = (string)val12["name"]; if (!string.IsNullOrEmpty(text2)) { val11["name"] = JToken.op_Implicit(text2); num++; } } JToken obj9 = val11["desc"]; JObject val14 = (JObject)(object)((obj9 is JObject) ? obj9 : null); JToken obj10 = val12["desc"]; JObject val15 = (JObject)(object)((obj10 is JObject) ? obj10 : null); string text3 = ((val15 != null) ? ((string)val15["zh"]) : null); if (val14 != null) { if (string.IsNullOrEmpty((string)val14["zh"]) && !string.IsNullOrEmpty(text3) && DescSame((string)val14["src"], (val15 != null) ? ((string)val15["src"]) : null, out var addMark)) { if (!string.IsNullOrEmpty(addMark) && !text3.StartsWith("[", StringComparison.Ordinal)) { string text4 = MarkerZh(addMark); if (!string.IsNullOrEmpty(text4)) { text3 = text4 + " " + text3; } } val14["zh"] = JToken.op_Implicit(text3); num++; } } else if (!string.IsNullOrEmpty(text3)) { val11["desc"] = ((JToken)val15).DeepClone(); num++; } foreach (KeyValuePair<string, JToken> item6 in val11) { if (!item6.Key.StartsWith("value:", StringComparison.Ordinal)) { continue; } JToken value8 = item6.Value; JValue val16 = (JValue)(object)((value8 is JValue) ? value8 : null); if (val16 != null && string.IsNullOrEmpty(((object)val16).ToString()) && !IsHexColorValue(((object)val16).ToString())) { string text5 = (string)val12[item6.Key]; if (!string.IsNullOrEmpty(text5)) { val11[item6.Key] = JToken.op_Implicit(text5); num++; } } } } continue; } foreach (KeyValuePair<string, JToken> item7 in val7) { JToken value9 = item7.Value; JObject val17 = (JObject)(object)((value9 is JObject) ? value9 : null); if (val17 == null) { continue; } foreach (KeyValuePair<string, JToken> item8 in val17) { JToken value10 = item8.Value; JObject val18 = (JObject)(object)((value10 is JObject) ? value10 : null); if (val18 != null && string.IsNullOrEmpty((string)val18["zh"]) && srcLookup2.TryGet((string)val18["src"], out var zh2)) { val18["zh"] = JToken.op_Implicit(zh2); num++; } } } } } } } return num; } internal static SrcLookup BuildSrcLookup(JObject official, bool withStrings = true, bool withObjects = true) { if (official == null) { return null; } JToken obj = official["meta"]; JToken obj2 = ((obj is JObject) ? obj : null); if ((int?)((obj2 != null) ? ((JObject)obj2)["schema"] : null) != 4) { return null; } SrcLookup srcLookup = new SrcLookup(); if (withStrings) { JToken obj3 = official["strings"]; JObject val = (JObject)(object)((obj3 is JObject) ? obj3 : null); if (val != null) { foreach (KeyValuePair<string, JToken> item in val) { JToken value = item.Value; JValue val2 = (JValue)(object)((value is JValue) ? value : null); if (val2 != null) { srcLookup.Add(item.Key, ((object)val2).ToString()); continue; } JToken value2 = item.Value; JObject val3 = (JObject)(object)((value2 is JObject) ? value2 : null); if (val3 != null) { srcLookup.Add(((string)val3["src"]) ?? item.Key, (string)val3["zh"]); } } } } if (withObjects) { JToken obj4 = official["objects"]; JObject val4 = (JObject)(object)((obj4 is JObject) ? obj4 : null); if (val4 != null) { foreach (KeyValuePair<string, JToken> item2 in val4) { JToken value3 = item2.Value; JObject val5 = (JObject)(object)((value3 is JObject) ? value3 : null); if (val5 == null) { continue; } if (item2.Key == "config_section") { foreach (KeyValuePair<string, JToken> item3 in val5) { string key = item3.Key; JToken value4 = item3.Value; JValue val6 = (JValue)(object)((value4 is JValue) ? value4 : null); string? zh; if (val6 == null) { JToken value5 = item3.Value; JToken obj5 = ((value5 is JObject) ? value5 : null); object obj6; if (obj5 == null) { obj6 = null; } else { JToken obj7 = ((JObject)obj5)["name"]; obj6 = ((obj7 != null) ? obj7[(object)"zh"] : null); } zh = (string)(JToken)obj6; } else { zh = ((object)val6).ToString(); } srcLookup.Add(key, zh); } continue; } foreach (KeyValuePair<string, JToken> item4 in val5) { JToken value6 = item4.Value; JObject val7 = (JObject)(object)((value6 is JObject) ? value6 : null); if (val7 == null) { continue; } if (item2.Key == "config") { JToken obj8 = val7["name"]; JValue val8 = (JValue)(object)((obj8 is JValue) ? obj8 : null); if (val8 != null) { srcLookup.Add(KeyTail(item4.Key), ((object)val8).ToString()); } else { JToken obj9 = val7["name"]; JObject val9 = (JObject)(object)((obj9 is JObject) ? obj9 : null); if (val9 != null) { srcLookup.Add(((string)val9["src"]) ?? KeyTail(item4.Key), (string)val9["zh"]); } } JToken obj10 = val7["desc"]; JObject val10 = (JObject)(object)((obj10 is JObject) ? obj10 : null); if (val10 != null) { srcLookup.Add((string)val10["src"], (string)val10["zh"]); } foreach (KeyValuePair<string, JToken> item5 in val7) { if (item5.Key.StartsWith("value:", StringComparison.Ordinal)) { string src = item5.Key.Substring(6); JToken value7 = item5.Value; JValue val11 = (JValue)(object)((value7 is JValue) ? value7 : null); string? zh2; if (val11 == null) { JToken value8 = item5.Value; JToken obj11 = ((value8 is JObject) ? value8 : null); zh2 = (string)((obj11 != null) ? ((JObject)obj11)["zh"] : null); } else { zh2 = ((object)val11).ToString(); } srcLookup.Add(src, zh2); } } continue; } JToken obj12 = val7["fields"]; JObject val12 = (JObject)(object)((obj12 is JObject) ? obj12 : null); if (val12 == null) { continue; } foreach (KeyValuePair<string, JToken> item6 in val12) { JToken value9 = item6.Value; JObject val13 = (JObject)(object)((value9 is JObject) ? value9 : null); if (val13 != null) { srcLookup.Add((string)val13["src"], (string)val13["zh"]); continue; } JToken obj13 = val7["zh"]; JValue val14 = (JValue)(object)((obj13 is JValue) ? obj13 : null); if (val14 != null) { srcLookup.Add((string)val7["src"], ((object)val14).ToString()); } } } } } } return srcLookup; } private static string KeyTail(string key) { int num = key.LastIndexOf("::", StringComparison.Ordinal); if (num < 0) { return key; } return key.Substring(num + 2); } public static int CarryOverPatterns(JObject doc, JObject official, out int collisions) { collisions = 0; if (doc == null || official == null) { return 0; } JToken obj = official["meta"]; JToken obj2 = ((obj is JObject) ? obj : null); if ((int?)((obj2 != null) ? ((JObject)obj2)["schema"] : null) != 4) { return 0; } JToken obj3 = doc["patterns"]; JArray val = (JArray)(object)((obj3 is JArray) ? obj3 : null); if (val != null) { JToken obj4 = official["patterns"]; JArray val2 = (JArray)(object)((obj4 is JArray) ? obj4 : null); if (val2 != null) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal); Dictionary<string, string> dictionary2 = new Dictionary<string, string>(StringComparer.Ordinal); Dictionary<string, string> dictionary3 = new Dictionary<string, string>(StringComparer.Ordinal); Dictionary<string, string> dictionary4 = new Dictionary<string, string>(StringComparer.Ordinal); foreach (JToken item in val2) { JObject val3 = (JObject)(object)((item is JObject) ? item : null); if (val3 == null) { continue; } string text = (string)val3["template"]; string text2 = (string)val3["replacement"]; if (string.IsNullOrEmpty(text)) { string text3 = (string)val3["pattern"]; if (!string.IsNullOrEmpty(text3) && !string.IsNullOrEmpty(text2) && !dictionary4.ContainsKey(text3)) { dictionary4[text3] = text2; } } else { if (string.IsNullOrEmpty(text2) || !SlotSetOk(text, text2)) { continue; } if (!dictionary.ContainsKey(text)) { dictionary[text] = text2; } string text4 = ExportFilters.NormalizeTemplateKey(text); if (string.IsNullOrEmpty(text4)) { continue; } if (dictionary2.TryGetValue(text4, out var value)) { if (!string.Equals(value, text2, StringComparison.Ordinal)) { collisions++; } } else { dictionary2[text4] = text2; dictionary3[text4] = SlotSignature(text); } } } if (dictionary.Count == 0 && dictionary4.Count == 0) { return 0; } int num = 0; { foreach (JToken item2 in val) { JObject val4 = (JObject)(object)((item2 is JObject) ? item2 : null); if (val4 == null || !string.IsNullOrEmpty((string)val4["replacement"])) { continue; } string text5 = (string)val4["template"]; if (string.IsNullOrEmpty(text5)) { string text6 = (string)val4["pattern"]; if (!string.IsNullOrEmpty(text6) && dictionary4.TryGetValue(text6, out var value2)) { val4["replacement"] = JToken.op_Implicit(value2); num++; } continue; } if (!dictionary.TryGetValue(text5, out var value3)) { string text7 = ExportFilters.NormalizeTemplateKey(text5); if (string.IsNullOrEmpty(text7) || !dictionary2.TryGetValue(text7, out value3) || dictionary3[text7] != SlotSignature(text5)) { continue; } } if (SlotSetOk(text5, value3)) { val4["replacement"] = JToken.op_Implicit(value3); num++; } } return num; } } } return 0; } private static HashSet<int> SlotNumbers(string s) { HashSet<int> hashSet = new HashSet<int>(); if (string.IsNullOrEmpty(s)) { return hashSet; } foreach (Match item in Regex.Matches(s, "\\{(\\d+)[^}]*\\}")) { if (int.TryParse(item.Groups[1].Value, out var result)) { hashSet.Add(result); } } return hashSet; } private static string SlotSignature(string tpl) { List<int> list = new List<int>(SlotNumbers(tpl)); list.Sort(); return string.Join(",", list); } private static bool SlotSetOk(string tpl, string rep) { HashSet<int> hashSet = SlotNumbers(tpl); if (hashSet.Count == 0) { return true; } foreach (int item in SlotNumbers(rep)) { if (!hashSet.Contains(item)) { return false; } } return true; } private static string NormalizeWhitespace(string s) { if (!string.IsNullOrEmpty(s)) { return Regex.Replace(s, "\\s+", " ").Trim(); } return s; } private static void SplitCfgMarker(string s, out string mark, out string body) { mark = null; body = s ?? ""; if (!string.IsNullOrEmpty(s)) { Match match = CfgMarkRx.Match(s); if (match.Success && CfgMarkers.ContainsKey(match.Groups[1].Value)) { mark = s.Substring(0, match.Length).Trim(); body = s.Substring(match.Length); } } } private static bool DescSame(string a, string b, out string addMark) { addMark = null; if (SrcEquivalent(a, b)) { return true; } SplitCfgMarker(a, out var mark, out var body); SplitCfgMarker(b, out var mark2, out var body2); if (!SrcEquivalent(body, body2)) { return false; } if (string.Equals(mark, mark2, StringComparison.Ordinal)) { return true; } if (!string.IsNullOrEmpty(mark) && string.IsNullOrEmpty(mark2)) { addMark = mark; return true; } if (string.IsNullOrEmpty(mark) && !string.IsNullOrEmpty(mark2)) { addMark = null; return true; } return false; } private static string MarkerZh(string mark) { if (string.IsNullOrEmpty(mark)) { return null; } string text = ((mark.Length >= 2 && mark[0] == '[' && mark[mark.Length - 1] == ']') ? mark.Substring(1, mark.Length - 2) : mark); string value; return "[" + (CfgMarkers.TryGetValue(text, out value) ? value : text) + "]"; } private static bool SrcEquivalent(string a, string b) { if (string.Equals(a, b, StringComparison.Ordinal)) { return true; } return string.Equals(NormalizeWhitespace(a), NormalizeWhitespace(b), StringComparison.Ordinal); } internal static bool SrcEquivalentShared(string a, string b) { return SrcEquivalent(a, b); } internal static bool IsHexColorValue(string s) { if (string.IsNullOrEmpty(s) || s[0] != '#') { return false; } if (s.Length < 4 || s.Length > 9) { return false; } for (int i = 1; i < s.Length; i++) { char c = s[i]; if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F')) { return false; } } return true; } private static bool TokenField(JToken t, string key, out string src, out string zh) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Invalid comparison between Unknown and I4 src = key; zh = null; JObject val = (JObject)(object)((t is JObject) ? t : null); if (val != null) { src = ((string)val["src"]) ?? key; zh = (string)val["zh"]; return true; } JValue val2 = (JValue)(object)((t is JValue) ? t : null); if (val2 != null && (int)((JToken)val2).Type == 8) { zh = ((object)val2).ToString(); return true; } return false; } public static int CarryOverTokens(JObject doc, JObject official) { //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Expected O, but got Unknown if (doc == null || official == null) { return 0; } JToken obj = official["meta"]; JToken obj2 = ((obj is JObject) ? obj : null); if ((int?)((obj2 != null) ? ((JObject)obj2)["schema"] : null) != 4) { return 0; } JToken obj3 = doc["tokens"]; JObject val = (JObject)(object)((obj3 is JObject) ? obj3 : null); JToken obj4 = official["tokens"]; JObject val2 = (JObject)(object)((obj4 is JObject) ? obj4 : null); if (val == null || val2 == null) { return 0; } int num = 0; foreach (KeyValuePair<string, JToken> item in val) { if (TokenField(item.Value, item.Key, out var src, out var zh) && string.IsNullOrEmpty(zh) && TokenField(val2[item.Key], item.Key, out var src2, out var zh2) && !string.IsNullOrEmpty(zh2) && (!(val2[item.Key] is JObject) || SrcEquivalent(src2, src))) { JToken value = item.Value; JObject val3 = (JObject)(object)((value is JObject) ? value : null); if (val3 != null) { val3["zh"] = JToken.op_Implicit(zh2); } else { val[item.Key] = (JToken)new JObject { ["src"] = JToken.op_Implicit(src ?? src2), ["zh"] = JToken.op_Implicit(zh2) }; } num++; } } return num; } public static int UnionOfficial(JObject doc, JObject official, out string note) { //IL_051d: Unknown result type (might be due to invalid IL or missing references) //IL_0524: Expected O, but got Unknown //IL_067a: Unknown result type (might be due to invalid IL or missing references) //IL_0681: Expected O, but got Unknown //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Expected O, but got Unknown //IL_0761: Unknown result type (might be due to invalid IL or missing references) //IL_0768: Expected O, but got Unknown //IL_0373: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Expected O, but got Unknown //IL_058d: Unknown result type (might be due to invalid IL or missing references) //IL_0592: Unknown result type (might be due to invalid IL or missing references) //IL_05af: Unknown result type (might be due to invalid IL or missing references) //IL_05c6: Expected O, but got Unknown //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Expected O, but got Unknown //IL_0459: Unknown result type (might be due to invalid IL or missing references) //IL_045e: Unknown result type (might be due to invalid IL or missing references) //IL_048a: Unknown result type (might be due to invalid IL or missing references) //IL_04a4: Expected O, but got Unknown note = ""; if (doc == null || official == null) { return 0; } JToken obj = official["meta"]; JToken obj2 = ((obj is JObject) ? obj : null); if ((int?)((obj2 != null) ? ((JObject)obj2)["schema"] : null) != 4) { return 0; } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; int num7 = 0; int num8 = 0; JToken obj3 = doc["strings"]; JObject val = (JObject)(object)((obj3 is JObject) ? obj3 : null); if (val != null) { JToken obj4 = official["strings"]; JObject val2 = (JObject)(object)((obj4 is JObject) ? obj4 : null); if (val2 != null) { foreach (KeyValuePair<string, JToken> item in val2) { JToken value = item.Value; JObject val3 = (JObject)(object)((value is JObject) ? value : null); string text; string text2; if (val3 != null) { text = ((string)val3["src"]) ?? item.Key; text2 = (string)val3["zh"]; } else { text = item.Key; JToken value2 = item.Value; text2 = ((object)((value2 is JValue) ? value2 : null))?.ToString(); } if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2) || string.Equals(text, text2, StringComparison.Ordinal) || IsHexColorValue(text)) { continue; } if (val[text] != null) { num4++; continue; } JToken val4 = val2[text]; if (val4 != null) { JObject val5 = (JObject)(object)((val4 is JObject) ? val4 : null); string text3 = ((val5 != null) ? ((string)val5["zh"]) : ((object)((val4 is JValue) ? val4 : null))?.ToString()); if (!string.IsNullOrEmpty(text3)) { text2 = text3; } } string text4 = (HasSlot(text) ? "legacy-slot" : (FragmentLike(text) ? "legacy-frag" : "legacy")); val[text] = (JToken)new JObject { ["src"] = JToken.op_Implicit(text), ["zh"] = JToken.op_Implicit(text2), ["from"] = JToken.op_Implicit(text4) }; if (text4 == "legacy-slot") { num2++; } else if (text4 == "legacy-frag") { num3++; } num++; } } } JToken obj5 = doc["objects"]; JObject val6 = (JObject)(object)((obj5 is JObject) ? obj5 : null); JToken obj6 = official["objects"]; JObject val7 = (JObject)(object)((obj6 is JObject) ? obj6 : null); if (val6 != null && val7 != null) { JToken obj7 = val7["config_section"]; JObject val8 = (JObject)(object)((obj7 is JObject) ? obj7 : null); if (val8 != null) { JToken obj8 = val6["config_section"]; JObject val9 = (JObject)(object)((obj8 is JObject) ? obj8 : null); if (val9 == null) { val9 = (JObject)(object)(val6["config_section"] = (JToken)new JObject()); } foreach (KeyValuePair<string, JToken> item2 in val8) { if (val9[item2.Key] == null) { JToken value3 = item2.Value; JValue val11 = (JValue)(object)((value3 is JValue) ? value3 : null); if (val11 != null && !string.IsNullOrEmpty(((object)val11).ToString())) { val9[item2.Key] = JToken.op_Implicit(((object)val11).ToString()); num6++; } } } } JToken obj9 = val7["config"]; JObject val12 = (JObject)(object)((obj9 is JObject) ? obj9 : null); if (val12 != null) { JToken obj10 = val6["config"]; JObject val13 = (JObject)(object)((obj10 is JObject) ? obj10 : null); if (val13 == null) { val13 = (JObject)(object)(val6["config"] = (JToken)new JObject()); } foreach (KeyValuePair<string, JToken> item3 in val12) { if (val13[item3.Key] != null || !(item3.Value is JObject)) { continue; } JToken value4 = item3.Value; JToken obj11 = ((value4 is JObject) ? value4 : null).DeepClone(); JObject val15 = (JObject)(object)((obj11 is JObject) ? obj11 : null); foreach (KeyValuePair<string, JToken> item4 in val15) { if (item4.Key.StartsWith("value:", StringComparison.Ordinal)) { JToken value5 = item4.Value; JToken obj12 = ((value5 is JObject) ? value5 : null); string text5 = (string)((obj12 != null) ? ((JObject)obj12)["src"] : null); if (IsHexColorValue(text5 ?? item4.Key.Substring("value:".Length))) { val15[item4.Key] = (JToken)new JObject { ["src"] = JToken.op_Implicit(text5 ?? item4.Key.Substring("value:".Length)), ["zh"] = JToken.op_Implicit("") }; } } } val13[item3.Key] = (JToken)(object)val15; num5++; } } } JToken obj13 = official["tokens"]; JObject val16 = (JObject)(object)((obj13 is JObject) ? obj13 : null); if (val16 != null) { JToken obj14 = doc["tokens"]; JObject val17 = (JObject)(object)((obj14 is JObject) ? obj14 : null); if (val17 == null) { val17 = (JObject)(object)(doc["tokens"] = (JToken)new JObject()); } foreach (KeyValuePair<string, JToken> item5 in val16) { if (!TokenField(item5.Value, item5.Key, out var src, out var zh) || string.IsNullOrEmpty(zh)) { continue; } JToken val19 = val17[item5.Key]; if (val19 == null) { val17[item5.Key] = (JToken)new JObject { ["src"] = JToken.op_Implicit(src ?? item5.Key), ["zh"] = JToken.op_Implicit(zh) }; num7++; continue; } JObject val20 = (JObject)(object)((val19 is JObject) ? val19 : null); if (val20 != null && string.IsNullOrEmpty((string)val20["zh"]) && SrcEquivalent(((string)val20["src"]) ?? item5.Key, src)) { val20["zh"] = JToken.op_Implicit(zh); num8++; } } } JToken obj15 = official["officialKeys"]; JArray val21 = (JArray)(object)((obj15 is JArray) ? obj15 : null); if (val21 != null) { JToken obj16 = doc["officialKeys"]; JArray val22 = (JArray)(object)((obj16 is JArray) ? obj16 : null); if (val22 == null) { val22 = (JArray)(object)(doc["officialKeys"] = (JToken)new JArray()); } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (JToken item6 in val22) { hashSet.Add((string)item6); } foreach (JToken item7 in val21) { string text6 = (string)item7; if (!string.IsNullOrEmpty(text6) && hashSet.Add(text6)) { val22.Add(JToken.op_Implicit(text6)); } } } int num9 = 0; int num10 = 0; int num11 = 0; int num12 = 0; JToken obj17 = official["patterns"]; JArray val24 = (JArray)(object)((obj17 is JArray) ? obj17 : null); if (val24 != null) { JToken obj18 = doc["patterns"]; JArray val25 = (JArray)(object)((obj18 is JArray) ? obj18 : null); if (val25 == null) { val25 = (JArray)(object)(doc["patterns"] = (JToken)new JArray()); } HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal); for (int i = 0; i < ((JContainer)val25).Count; i++) { JToken obj19 = val25[i]; string text7 = PatternIdentityKey((JObject)(object)((obj19 is JObject) ? obj19 : null)); if (text7 != null) { hashSet2.Add(text7); } } for (int j = 0; j < ((JContainer)val24).Count; j++) { JToken obj20 = val24[j]; JObject val27 = (JObject)(object)((obj20 is JObject) ? obj20 : null); if (val27 == null || string.IsNullOrEmpty((string)val27["replacement"])) { continue; } string text8 = PatternIdentityKey(val27); if (text8 != null && hashSet2.Add(text8)) { val25.Add(((JToken)val27).DeepClone()); num9++; JToken obj21 = val27["homes"]; JArray val28 = (JArray)(object)((obj21 is JArray) ? obj21 : null); if (val28 != null && ((JContainer)val28).Count > 0) { num10++; } else if (!string.IsNullOrEmpty((string)val27["pattern"])) { num11++; } else { num12++; } } } } JToken obj22 = official["patterns"]; JToken obj23 = ((obj22 is JArray) ? obj22 : null); int num13 = ((obj23 != null) ? ((JContainer)obj23).Count : 0); JToken obj24 = doc["patterns"]; JToken obj25 = ((obj24 is JArray) ? obj24 : null); int num14 = ((obj25 != null) ? ((JContainer)obj25).Count : 0); note = "并集(旧→新): strings +" + num + "(普通 " + (num - num2 - num3) + " / 含槽 " + num2 + " / 碎片 " + num3 + " / 已存在 " + num4 + ") | cfg +" + num5 + " | 节 +" + num6 + " | token +" + num7 + "(键在值空补值 " + num8 + ") | patterns +" + num9 + "(旧 " + num13 + " → 新 " + num14 + ";有家 " + num10 + " / 纯正则 " + num11 + " / 无家模板 " + num12 + " —— 无家条目并回=保持现状,补 homes 是后续清理方向)"; return num + num5 + num6 + num7 + num9; } private static string PatternIdentityKey(JObject o) { if (o == null) { return null; } string text = (string)o["template"]; if (!string.IsNullOrEmpty(text)) { string text2 = ExportFilters.NormalizeTemplateKey(text); if (!string.IsNullOrEmpty(text2)) { return "t|" + text2 + "|" + SlotSignature(text); } return "t|" + text + "|" + SlotSignature(text); } string text3 = (string)o["pattern"]; if (!string.IsNullOrEmpty(text3)) { return "p|" + text3; } return null; } private static bool HasSlot(string s) { if (!string.IsNullOrEmpty(s)) { return Regex.IsMatch(s, "\\{[0-9][^}]*\\}"); } return false; } private static bool FragmentLike(string s) { if (string.IsNullOrEmpty(s)) { return true; } string text = s.Trim(); if (text.Length == 0) { return true; } if (!string.Equals(s, text, StringComparison.Ordinal) && text.Length <= 30) { return true; } return Regex.IsMatch(text, "^(?:</?[A-Za-z][^>]*>|\\[[^\\]]*\\]|[-–—·•|:;,.…\\s])+$"); } public static string Serialize(JObject root) { StringBuilder stringBuilder = new StringBuilder(1048576); WriteNode((JToken)(object)root, 0, stringBuilder); stringBuilder.Append('\n'); return stringBuilder.ToString(); } private static void WriteNode(JToken v, int pad, StringBuilder sb) { JObject val = (JObject)(object)((v is JObject) ? v : null); if (val == null) { JArray val2 = (JArray)(object)((v is JArray) ? v : null); if (val2 != null) { if (((JContainer)val2).Count == 0) { sb.Append("[]"); return; } bool flag = true; foreach (JToken item in val2) { if (item is JObject || item is JArray) { flag = false; break; } } if (((JContainer)val2).Count <= 8 && flag) { WriteCompact(val2, sb); } else { WriteBlock(ItemLines(val2, pad), pad, '[', ']', sb); } } else { sb.Append(Scalar(v)); } } else if (((JContainer)val).Count == 0) { sb.Append("{}"); } else if (((JContainer)val).Count <= 8 && IsFlat(val)) { WriteCompact(val, sb); } else { WriteBlock(PropLines(val, pad), pad, '{', '}', sb); } } private static void WriteBlock(IEnumerable<string> lines, int pad, char open, char close, StringBuilder sb) { sb.Append(open); string value = new string(' ', pad + 2); bool flag = true; foreach (string line in lines) { if (!flag) { sb.Append(','); } flag = false; sb.Append('\n').Append(value).Append(line); } sb.Append('\n').Append(new string(' ', pad)).Append(close); } private static IEnumerable<string> PropLines(JObject o, int pad) { foreach (KeyValuePair<string, JToken> item in o) { yield return JTokenToKey(item.Key) + ": " + Child(item.Value, pad); } } private static IEnumerable<string> ItemLines(JArray a, int pad) { foreach (JToken item in a) { yield return Child(item, pad); } } private static string Child(JToken v, int pad) { StringBuilder stringBuilder = new StringBuilder(); WriteNode(v, pad + 2, stringBuilder); return stringBuilder.ToString(); } private static void WriteCompact(JObject o, StringBuilder sb) { sb.Append('{'); bool flag = true; foreach (KeyValuePair<string, JToken> item in o) { if (!flag) { sb.Append(','); } flag = false; sb.Append(JTokenToKey(item.Key)).Append(": ").Append(Scalar(item.Value)); } sb.Append('}'); } private static void WriteCompact(JArray a, StringBuilder sb) { sb.Append('['); bool flag = true; foreach (JToken item in a) { if (!flag) { sb.Append(','); } flag = false; sb.Append(Scalar(item)); } sb.Append(']'); } private static bool IsFlat(JObject o) { foreach (KeyValuePair<string, JToken> item in o) { if (item.Value is JObject || item.Value is JArray) { return false; } } return true; } private static string JTokenToKey(string key) { return JToken.FromObject((object)key).ToString((Formatting)0, Array.Empty<JsonConverter>()); } private static string Scalar(JToken v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 JValue val = (JValue)(object)((v is JValue) ? v : null); if (val != null && (int)((JToken)val).Type == 8) { return EscapeString((string)(JToken)(object)val); } return v.ToString((Formatting)0, Array.Empty<JsonConverter>()); } private static string EscapeString(string s) { StringBuilder stringBuilder = new StringBuilder(s.Length + 8); stringBuilder.Append('"'); foreach (char c in s) { switch (c) { case '"': stringBuilder.Append("\\\""); continue; case '\\': stringBuilder.Append("\\\\"); continue; case '\n': stringBuilder.Append("\\n"); continue; case '\r': stringBuilder.Append("\\r"); continue; case '\t': stringBuilder.Append("\\t"); continue; case '\b': stringBuilder.Append("\\b"); continue; case '\f': stringBuilder.Append("\\f"); continue; } if (c < ' ') { StringBuilder stringBuilder2 = stringBuilder.Append("\\u"); int num = c; stringBuilder2.Append(num.ToString("x4")); } else { stringBuilder.Append(c); } } stringBuilder.Append('"'); return stringBuilder.ToString(); } } internal static class FaceIndex { private static readonly Dictionary<string, Dictionary<string, string>> _byFace = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> _faces = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly object _lock = new object(); private static volatile int _builtForCount = -1; public static long Hits; public static long Looks; internal const string ApiPrefix = "api:"; internal static void EnsureBuilt(IList<ModDictionary> dicts) { if (dicts != null && dicts.Count != _builtForCount) { Rebuild(dicts); } } internal static void Rebuild(IList<ModDictionary> dicts) { HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); Dictionary<string, Dictionary<string, string>> dictionary = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase); if (dicts != null) { for (int i = 0; i < dicts.Count; i++) { ModDictionary modDictionary = dicts[i]; if (modDictionary == null || !modDictionary.Active) { continue; } string text = FirstGuid(modDictionary); if (text == null) { continue; } Dictionary<string, string[]> facesByText = modDictionary.FacesByText; if (facesByText != null && facesByText.Count > 0) { foreach (KeyValuePair<string, string[]> item in facesByText) { string[] value = item.Value; if (value != null && value.Length != 0) { for (int j = 0; j < value.Length; j++) { Add(dictionary, hashSet, value[j], item.Key, text); } } } } List<RegexRule> globalRegexes = modDictionary.GlobalRegexes; if (globalRegexes != null) { for (int k = 0; k < globalRegexes.Count; k++) { string[] array = ((globalRegexes[k] != null) ? globalRegexes[k].Faces : null); if (array == null) { continue; } for (int l = 0; l < array.Length; l++) { if (!string.IsNullOrEmpty(array[l])) { hashSet.Add(array[l]); } } } } globalRegexes = modDictionary.LocalRegexes; if (globalRegexes == null) { continue; } for (int m = 0; m < globalRegexes.Count; m++) { string[] array2 = ((globalRegexes[m] != null) ? globalRegexes[m].Faces : null); if (array2 == null) { continue; } for (int n = 0; n < array2.Length; n++) { if (!string.IsNullOrEmpty(array2[n])) { hashSet.Add(array2[n]); } } } } } lock (_lock) { _byFace.Clear(); foreach (KeyValuePair<string, Dictionary<string, string>> item2 in dictionary) { _byFace[item2.Key] = item2.Value; } _faces.Clear(); foreach (string item3 in hashSet) { _faces.Add(item3); } _builtForCount = dicts?.Count ?? 0; } } private static void Add(Dictionary<string, Dictionary<string, string>> build, HashSet<string> faces, string face, string text, string guid) { if (!string.IsNullOrEmpty(face) && !string.IsNullOrEmpty(text)) { faces.Add(face); if (!build.TryGetValue(face, out var value)) { value = (build[face] = new Dictionary<string, string>(StringComparer.Ordinal)); } if (!value.ContainsKey(text)) { value[text] = guid; } } } internal static string Lookup(string face, string text) { if (string.IsNullOrEmpty(face) || string.IsNullOrEmpty(text)) { return null; } Looks++; Dictionary<string, string> value; lock (_lock) { if (!_byFace.TryGetValue(face, out value)) { return null; } } if (value.TryGetValue(text, out var value2)) { Hits++; return value2; } return null; } internal static List<string> AllFaces() { lock (_lock) { return new List<string>(_faces); } } internal static string Summary() { int count; int num; lock (_lock) { count = _faces.Count; num = 0; foreach (KeyValuePair<string, Dictionary<string, string>> item in _byFace) { num += item.Value.Count; } } return "面 " + count + " 个 / 面内可查串 " + num + " 条(查 " + Looks + " / 命中 " + Hits + ")"; } private static string FirstGuid(ModDictionary d) { if (!string.IsNullOrEmpty(d.TargetGuid)) { return d.TargetGuid; } if (d.TargetGuids != null && d.TargetGuids.Length != 0) { return d.TargetGuids[0]; } return null; } internal static void Clear() { lock (_lock) { _byFace.Clear(); _faces.Clear(); _builtForCount = -1; } } } internal static class GameResourceBridge { private static readonly Dictionary<string, string> _map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private static bool _built; private static string _langAtBuild; private static readonly Dictionary<string, string> _biomeToken = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Meadows", "$biome_meadows" }, { "Black Forest", "$biome_blackforest" }, { "Swamp", "$biome_swamp" }, { "Mountain", "$biome_mountain" }, { "Plains", "$biome_plains" }, { "Mistlands", "$biome_mistlands" }, { "Ashlands", "$biome_ashlands" }, { "Deep North", "$biome_deepnorth" }, { "Ocean", "$biome_ocean" } }; internal static bool Built { get { if (_built) { return _map.Count > 0; } return false; } } internal static bool BridgeActive { get { if (!Enabled) { return false; } EnsureBuilt(); if (_built) { return _map.Count > 0; } return false; } } public static bool Enabled { get { if (ValheimTWPlugin.CfgBridgeGameResources != null) { return ValheimTWPlugin.CfgBridgeGameResources.Value; } return false; } } public static void EnsureBuilt() { string text = SafeLang(); if (!_built || !(_langAtBuild == text) || (_map.Count <= 0 && HasDataSource())) { Build(); _built = true; _langAtBuild = text; } } private static bool HasDataSource() { try { if ((Object)(object)ObjectDB.instance != (Object)null && GetField(ObjectDB.instance, "m_items") is IEnumerable enumerable) { { IEnumerator enumerator = enumerable.GetEnumerator(); try { if (enumerator.MoveNext()) { _ = enumerator.Current; return true; } } finally { IDisposable disposable = enumerator as IDisposable; if (disposable != null) { disposable.Dispose(); } } } } } catch { } try { if ((Object)(object)ZoneSystem.instance != (Object)null && GetField(ZoneSystem.instance, "m_locations") is IEnumerable enumerable2) { { IEnumerator enumerator = enumerable2.GetEnumerator(); try { if (enumerator.MoveNext()) { _ = enumerator.Current; return true; } } finally { IDisposable disposable2 = enumerator as IDisposable; if (disposable2 != null) { disposable2.Dispose(); } } } } } catch { } return false; } private static string SafeLang() { try { return (Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : "?"; } catch { return "?"; } } private static void Build() { _map.Clear(); try { ScanPrefabs(); } catch (Exception ex) { ValheimTWPlugin.Warn("[GameResourceBridge] prefabs scan failed: " + ex.Message); } try { ScanLocations(); } catch (Exception ex2) { ValheimTWPlugin.Warn("[GameResourceBridge] locations scan failed: " + ex2.Message); } try { ScanBiomes(); } catch (Exception ex3) { ValheimTWPlugin.Warn("[GameResourceBridge] biomes scan failed: " + ex3.Message); } } private static void ScanBiomes() { foreach (KeyValuePair<string, string> item in _biomeToken) { string text = LocalizeSafe(item.Value); if (!string.IsNullOrEmpty(text)) { Add(Norm(item.Key), text); } } } private static void ScanPrefabs() { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null || !(GetField(instance, "m_items") is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { GameObject val = (GameObject)((item is GameObject) ? item : null); if (!((Object)(object)val == (Object)null)) { string text = PrefabChinese(val); if (!string.IsNullOrEmpty(text)) { Add(Norm(((Object)val).name), text); } } } } private static string PrefabChinese(GameObject go) { ItemDrop component = go.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { return null; } object field = GetField(component, "m_itemData"); if (field == null) { return null; } object field2 = GetField(field, "m_shared"); if (field2 == null) { return null; } string text = GetField(field2, "m_name") as string; if (string.IsNullOrEmpty(text) || !text.StartsWith("$")) { return null; } return LocalizeSafe(text); } private static void ScanLocations() { ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null || !(GetField(instance, "m_locations") is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { if (item == null) { continue; } string text = GetField(item, "m_name") as string; string text2 = null; string text3 = GetField(item, "m_prefabName") as string; if (!string.IsNullOrEmpty(text3)) { text2 = Lookup(Norm(text3)); } if (text2 == null) { object field = GetField(item, "m_prefab"); string text4 = ((field != null) ? (GetProp(field, "Name") as string) : null); if (!string.IsNullOrEmpty(text4)) { text2 = Lookup(Norm(text4)); } } if (!string.IsNullOrEmpty(text2) && !string.IsNullOrEmpty(text)) { Add(Norm(text), text2); } } } private static string Lookup(string k) { if (!_map.TryGetValue(k, out var value)) { return null; } return value; } private static void Add(string key, string cn) { if (!string.IsNullOrEmpty(key) && !_map.ContainsKey(key)) { _map[key] = cn; } } public static bool TryBridge(string text, out string cn) { cn = null; if (!Enabled) { return false; } EnsureBuilt(); if (string.IsNullOrEmpty(text)) { return false; } return _map.TryGetValue(Norm(text), out cn); } private static string Norm(string s) { if (string.IsNullOrEmpty(s)) { return ""; } return Regex.Replace(s, "[\\s_\\-]", "").ToLowerInvariant(); } private static string LocalizeSafe(string tok) { try { if (Localization.instance == null) { return null; } string text = Localization.instance.Localize(tok); return (text == tok) ? null : text; } catch { return null; } } private static object GetField(object obj, string name) { if (obj == null) { return null; } FieldInfo field = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { return null; } try { return field.GetValue(obj); } catch { return null; } } private static object GetProp(object obj, string name) { if (obj == null) { return null; } PropertyInfo property = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property == null) { return null; } try { return property.GetValue(obj); } catch { return null; } } } internal static class GameTypes { private static readonly Dictionary<string, Type> _cache = new Dictionary<string, Type>(StringComparer.Ordinal); internal static Type Find(string shortName) { if (string.IsNullOrEmpty(shortName)) { return null; } if (_cache.TryGetValue(shortName, out var value)) { return value; } Type type = FindDirect(shortName); if (type == null && shortName.IndexOf('+') < 0) { type = FindNestedByShortName(shortName); } _cache[shortName] = type; return type; } private static Type FindDirect(string shortName) { Type type = null; try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { string text = null; try { text = assembly.GetName().Name; } catch (Exception) { continue; } if (!IsGameAssembly(text)) { continue; } Type type2 = null; try { type2 = assembly.GetType(shortName, throwOnError: false); } catch (Exception) { } if (type2 == null) { try { type2 = assembly.GetType("Valheim." + shortName, throwOnError: false); } catch (Exception) { } } if (!(type2 == null)) { if (type != null && type != type2) { return null; } type = type2; } } } catch (Exception) { } return type; } private static Type FindNestedByShortName(string shortName) { Type type = null; try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { string text = null; try { text = assembly.GetName().Name; } catch (Exception) { continue; } if (!IsGameAssembly(text)) { continue; } Type[] types; try { types = assembly.GetTypes(); } catch (Exception) { continue; } foreach (Type type2 in types) { if (!(type2 == null) && !(type2.Name != shortName) && type2.IsNested) { if (type != null && type != type2) { return null; } type = type2; } } } } catch (Exception) { } return type; } internal static Type FindQualified(string fullName) { if (string.IsNullOrEmpty(fullName)) { return null; } try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = null; try { type = assemblies[i].GetType(fullName, throwOnError: false); } catch (Exception) { continue; } if (type != null) { return type; } } } catch (Exception) { } return null; } private static bool IsGameAssembly(string name) { if (string.IsNullOrEmpty(name)) { return false; } if (name.StartsWith("assembly_", StringComparison.OrdinalIgnoreCase)) { return true; } return name.IndexOf("valheim", StringComparison.OrdinalIgnoreCase) >= 0; } } internal static class HostScope { public static long Hits; public static long Blocks; public static long ObservedBlocks; private static readonly object Lk = new object(); private static readonly List<string> Samples = new List<string>(); private static readonly List<string> Observed = new List<string>(); private const int MaxSamples = 12; public static string Report() { if (Hits == 0L && Blocks == 0L && ObservedBlocks == 0L) { return "点级约束(homes): 本会话无样本"; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("点级约束(homes): 命中 ").Append(Hits).Append(" / 拦截 ") .Append(Blocks); if (ObservedBlocks > 0) { stringBuilder.Append(" / ①层观测不符 ").Append(ObservedBlocks).Append("(不否决:字面量宿主≠显示点的固有偏差,2026-10-07 起仅记账)"); } lock (Lk) { if (Samples.Count > 0) { stringBuilder.Append(" ── 被拦样本(②层无归属,上限 ").Append(12).Append(")──"); for (int i = 0; i < Samples.Count; i++) { stringBuilder.AppendLine().Append(" ").Append(Samples[i]); } } if (Observed.Count > 0) { stringBuilder.Append(" ── ①层观测样本(上限 ").Append(12).Append(")──"); for (int j = 0; j < Observed.Count; j++) { stringBuilder.AppendLine().Append(" ").Append(Observed[j]); } } } return stringBuilder.ToString(); } public static bool Matches(string[] homes, string sample = null, bool observeOnly = false) { if (homes == null || homes.Length == 0) { return true; } StackTrace stackTrace; try { stackTrace = new StackTrace(fNeedFileInfo: false); } catch { return true; } for (int i = 0; i < stackTrace.FrameCount; i++) { MethodBase methodBase = stackTrace.GetFrame(i)?.GetMethod(); if (methodBase == null) { continue; } string text = ((methodBase.DeclaringType != null) ? methodBase.DeclaringType.FullName : "") + "." + methodBase.Name; text = text.Replace('/', '+'); foreach (string text2 in homes) { if (!string.IsNullOrEmpty(text2)) { int num = text2.IndexOf(':'); string text3 = ((num >= 0) ? text2.Substring(num + 1) : text2); text3 = text3.Replace('/', '+'); if (text3.Length >= 4 && FrameMatches(text, text3)) { Hits++; return true; } } } } if (observeOnly) { ObservedBlocks++; if (!string.IsNullOrEmpty(sample)) { lock (Lk) { if (Observed.Count < 12) { Observed.Add((sample.Length > 90) ? sample.Substring(0, 90) : sample); } } string text4 = FirstHome(homes); string text5 = TopFrames(stackTrace); if (text4 != null) { ObservationLog.Append("homesObserved", (text5 == null) ? text4 : (text4 + " ⊕ " + text5), sample); } } return true; } Blocks++; if (!string.IsNullOrEmpty(sample)) { lock (Lk) { if (Samples.Count < 12) { Samples.Add((sample.Length > 90) ? sample.Substring(0, 90) : sample); } } string text6 = FirstHome(homes); if (text6 != null) { ObservationLog.Append("homesBlocked", text6, sample); } } return false; } private static bool FrameMatches(string frame, string home) { if (string.IsNullOrEmpty(frame) || string.IsNullOrEmpty(home)) { return false; } if (string.Equals(frame, home, StringComparison.Ordinal)) { return true; } for (int num = frame.IndexOf(home, StringComparison.Ordinal); num >= 0; num = frame.IndexOf(home, num + 1, StringComparison.Ordinal)) { bool num2 = num == 0 || IsNameBoundary(frame[num - 1]); int num3 = num + home.Length; if (num2) { if (num3 >= frame.Length) { return true; } if (IsNameBoundary(frame[num3])) { return true; } if (IsSynthetic(frame) || IsSynthetic(home)) { if (frame[num3] == '<') { return true; } if (frame[num3] == 'b' && frame.Length - num3 >= 3 && frame[num3 + 1] == '_' && frame[num3 + 2] == '_') { return true; } } } } return false; } private static bool IsNameBoundary(char c) { if (c != '.' && c != '+' && c != '/' && c != '<' && c != '>' && c != '`' && c != ',' && c != '(') { return c == ')'; } return true; } private static bool IsSynthetic(string s) { if (s.IndexOf("b__", StringComparison.Ordinal) < 0) { return s.IndexOf("<>", StringComparison.Ordinal) >= 0; } return true; } private static string TopFrames(StackTrace st) { if (st == null) { return null; } StringBuilder stringBuilder = null; int num = 0; for (int i = 0; i < st.FrameCount; i++) { if (num >= 3) { break; } MethodBase methodBase = st.GetFrame(i)?.GetMethod(); if (methodBase == null) { continue; } string text = ((methodBase.DeclaringType != null) ? methodBase.DeclaringType.FullName : "") + "." + methodBase.Name; if (!text.StartsWith("ValheimTW.", StringComparison.Ordinal) && !text.StartsWith("System.", StringComparison.Ordinal)) { if (stringBuilder == null) { stringBuilder = new StringBuilder(160); } else { stringBuilder.Append(" < "); } stringBuilder.Append(text.Replace('/', '+')); num++; } } return stringBuilder?.ToString(); } private static string FirstHome(string[] homes) { for (int i = 0; i < homes.Length; i++) { if (!string.IsNullOrEmpty(homes[i])) { int num = homes[i].IndexOf(':'); if (num < 0) { return homes[i]; } return homes[i].Substring(num + 1); } } return null; } } internal static class JotunnLocalizationTakeover { private static Type _tCustomLoc; private static PropertyInfo _pSourceMod; private static PropertyInfo _pMap; private static MethodInfo _mGetTranslations; private static MethodInfo _mGetLanguages; private static MethodInfo _mAddJsonFile; private static bool _probed; private static bool _installed; private static bool _hookedAddJsonFile; private static bool _hookedLmPrefix; private static bool _hookedTryTranslate; private static Type _tLM; private static FieldInfo _fLocalizations; private static FieldInfo _fTranslations; private static readonly List<object> Registered = new List<object>(); private static readonly HashSet<string> OfficialTokens = new HashSet<string>(StringComparer.Ordinal); private static bool _officialSnapshotted; private static bool _snapshotClean; private static bool _dirty; private static int _retries; private static int _probeTries; private static int _installTries; private static readonly HashSet<string> Done = new HashSet<string>(StringComparer.Ordinal); private const int MaxRetries = 3600; private static readonly Dictionary<string, string> ChainZhCache = new Dictionary<string, string>(StringComparer.Ordinal); private static readonly Dictionary<string, string> _tokenOwner = new Dictionary<string, string>(StringComparer.Ordinal); private static int _tokenOwnerStamp = -1; private static readonly Dictionary<string, string> _tokenEn = new Dictionary<string, string>(StringComparer.Ordinal); private static readonly HashSet<string> _tokenAmbiguous = new HashSet<string>(StringComparer.Ordinal); private static readonly Regex SlotRe = new Regex("\\{(\\d+)\\}", RegexOptions.Compiled); internal static bool HasCleanOfficialSnapshot => _snapshotClean; internal static string StatusLine { get { if (_tCustomLoc == null) { if (_installTries <= 0) { return "未探测(插件未走到 Install)"; } return "未探测到 Jötunn(已重试 " + _installTries + " 次;该整合包未装 Jötunn 属正常)"; } if (!_installed) { return "已探测到 Jötunn 但挂载未完成(见启动日志的告警)"; } return "AddJsonFile=" + (_hookedAddJsonFile ? "已挂" : "未找到") + " | LoadAndSetupModLanguages=" + (_hookedLmPrefix ? "已挂" : "未找到") + " | TryTranslate链修复=" + (_hookedTryTranslate ? "已挂" : "未找到") + " | 干净官方快照=" + (_snapshotClean ? "有" : (_officialSnapshotted ? "有但非干净(兜底取的)" : "从未取到")) + " | 已登记语言表实例=" + Registered.Count; } } internal static int TokenOwnerCount { get { try { RebuildTokenOwners(); } catch (Exception) { } return _tokenOwner.Count; } } internal static void Install(Harmony harmony, bool quiet) { //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Expected O, but got Unknown //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Expected O, but got Unknown if (_installed) { return; } if (!Probe()) { _probeTries++; if (!quiet && _probeTries == 1) { ValheimTWPlugin.Log("尚未发现 Jotunn,先跳过「Jotunn 语言表接管」并继续重试(Jotunn 可能尚未加载完;确实未装 Jotunn 的整合包属正常现象)。"); } return; } try { _mAddJsonFile = AccessTools.Method(_tCustomLoc, "AddJsonFile", new Type[2] { typeof(string), typeof(string) }, (Type[])null); if (_mAddJsonFile != null) { harmony.Patch((MethodBase)_mAddJsonFile, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(JotunnLocalizationTakeover), "AddJsonFilePostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _hookedAddJsonFile = true; } Type type = (_tLM = AccessTools.TypeByName("Jotunn.Managers.LocalizationManager")); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "LoadAndSetupModLanguages", (Type[])null, (Type[])null) : null); if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(JotunnLocalizationTakeover), "LoadAndSetupModLanguagesPrefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _hookedLmPrefix = true; } MethodInfo methodInfo2 = AccessTools.Method(_tCustomLoc, "TryTranslate", new Type[1] { typeof(string) }, (Type[])null); if (methodInfo2 != null) { harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(JotunnLocalizationTakeover), "TryTranslatePostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _hookedTryTranslate = true; } _installed = true; ValheimTWPlugin.Log("Jotunn 语言表接管已挂载" + ((_mAddJsonFile != null) ? "" : "(⚠ AddJsonFile 未找到,接管不会生效)") + ((methodInfo != null) ? "" : "(⚠ 未找到 LoadAndSetupModLanguages,官方 token 快照将退化)") + (_hookedTryTranslate ? "" : "(⚠ 未找到 CustomLocalization.TryTranslate,查找链修复不生效)") + "。"); } catch (Exception ex) { ValheimTWPlugin.Warn("Jotunn 语言表接管挂载失败:" + ex.Message); } } private static bool Probe() { if (_tCustomLoc != null) { return true; } try { _tCustomLoc = AccessTools.TypeByName("Jotunn.Entities.CustomLocalization"); if (_tCustomLoc == null) { return false; } _pSourceMod = AccessTools.Property(_tCustomLoc, "SourceMod"); _pMap = AccessTools.Property(_tCustomLoc, "Map"); _mGetLanguages = AccessTools.Method(_tCustomLoc, "GetLanguages", (Type[])null, (Type[])null); _mGetTranslations = AccessTools.Method(_tCustomLoc, "GetTranslations", new Type[1] { typeof(string).MakeByRefType() }, (Type[])null) ?? AccessTools.Method(_tCustomLoc, "GetTranslations", (Type[])null, (Type[])null); } catch (Exception ex) { ValheimTWPlugin.Warn("探测 Jotunn CustomLocalization 失败:" + ex.Message); _tCustomLoc = null; } _probed = _tCustomLoc != null; return _tCustomLoc != null; } private static void AddJsonFilePostfix(object __instance) { try { if (__instance == null) { return; } for (int i = 0; i < Registered.Count; i++) { if (Registered[i] == __instance) { _dirty = true; return; } } Registered.Add(__instance); _dirty = true; } catch { } } private static void LoadAndSetupModLanguagesPrefix(object __0) { try { SnapshotOfficial((Localization)((__0 is Localization) ? __0 : null), clean: true); } catch { } } internal static HashSet<string> AllModTokenKeys() { if (_tCustomLoc == null) { return null; } try { Type type = _tLM ?? AccessTools.TypeByName("Jotunn.Managers.LocalizationManager"); if (type == null) { return null; } object obj = null; try { PropertyInfo propertyInfo = AccessTools.Property(type, "Instance"); if (propertyInfo != null) { obj = propertyInfo.GetValue(null, null); } } catch { } if (obj == null) { return null; } if (_fLocalizations == null) { _fLocalizations = AccessTools.Field(type, "Localizations"); } if (_fLocalizations == null) { return null; } if (!(_fLocalizations.GetValue(obj) is IEnumerable enumerable)) { return null; } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (object item in enumerable) { if (item == null) { continue; } object obj3 = item; Type type2 = item.GetType(); PropertyInfo propertyInfo2 = (type2.IsGenericType ? type2.GetProperty("Value") : null); if (propertyInfo2 != null) { obj3 = propertyInfo2.GetValue(item, null); } if (obj3 == null) { continue; } Dictionary<string, string> dictionary = Table(obj3, "English"); if (dictionary == null) { foreach (string item2 in LanguagesOf(obj3)) { dictionary = Table(obj3, item2); if (dictionary != null) { break; } } } if (dictionary == null) { continue; } foreach (KeyValuePair<string, string> item3 in dictionary) { if (!string.IsNullOrEmpty(item3.Key)) { hashSet.Add(item3.Key); } } } return hashSet; } catch { return null; } } internal static bool IsOfficialToken(string key) { if (_snapshotClean && !string.IsNullOrEmpty(key)) { return OfficialTokens.Contains(key); } return false; } internal static void Tick() { if (!_installed) { if (_installTries < 3600) { _installTries++; Install(ValheimTWPlugin.HarmonyInstance, quiet: true); } else if (_installTries == 3600) { _installTries++; ValheimTWPlugin.Log("Jotunn 等待超过 " + 60 + " 秒仍未出现,放弃「Jotunn 语言表接管」(本次会话 adapter.tokens 将为空)"); } if (!_installed) { return; } } SweepLocalizations(); if (!_dirty) { return; } if (ValheimTWPlugin.CfgJotunnTakeover == null || !ValheimTWPlugin.CfgJotunnTakeover.Value) { _dirty = false; return; } try { Flush(); } catch (Exception ex) { _dirty = false; ValheimTWPlugin.Warn("Jotunn 语言表接管执行失败:" + ex.Message); } } private static void Flush() { _dirty = false; SweepLocalizations(); if (Registered.Count == 0) { return; } Localization instance = Localization.instance; if (instance == null) { _dirty = true; return; } if (_fTranslations == null) { _fTranslations = AccessTools.Field(typeof(Localization), "m_translations"); } if (_fTranslations == null) { ValheimTWPlugin.Warn("Jotunn 语言表接管:未找到 Localization.m_translations,接管停用。"); return; } if (!(_fTranslations.GetValue(instance) is Dictionary<string, string> dictionary)) { _dirty = true; return; } if (!_officialSnapshotted) { SnapshotOfficial(instance, clean: false); } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; bool flag = false; for (int i = 0; i < Registered.Count; i++) { object obj = Registered[i]; if (obj == null) { continue; } string text = GuidOf(obj); if (string.IsNullOrEmpty(text)) { continue; } ModDictionary modDictionary = LocEngine.Get(text); if (modDictionary == null) { continue; } if (!modDictionary.Active) { flag = true; continue; } Dictionary<string, string> dictionary2 = Table(obj, "English"); Dictionary<string, string> dictionary3 = ChineseTable(obj); if (dictionary2 != null) { foreach (KeyValuePair<string, string> item in dictionary2) { OfficialTokens.Remove(item.Key); } } if (dictionary3 != null) { foreach (KeyValuePair<string, string> item2 in dictionary3) { OfficialTokens.Remove(item2.Key); } } num++; if (dictionary2 != null) { foreach (KeyValuePair<string, string> item3 in dictionary2) { string key = item3.Key; string value = item3.Value; if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value) || IsMetaToken(key) || OfficialTokens.Contains(key) || Done.Contains(text + "\0" + key)) { continue; } string text2 = OurTranslate(modDictionary, value); if (text2 != null && !string.Equals(text2, value, StringComparison.Ordinal)) { if (!SlotsSafe(value, text2)) { Done.Add(text + "\0" + key); num4++; } else { dictionary[key] = text2; Done.Add(text + "\0" + key); num2++; } } } } if ((ValheimTWPlugin.CfgJotunnOfficialFill != null && !ValheimTWPlugin.CfgJotunnOfficialFill.Value) || dictionary3 == null) { continue; } foreach (KeyValuePair<string, string> item4 in dictionary3) { string key2 = item4.Key; string value2 = item4.Value; if (!string.IsNullOrEmpty(key2) && !string.IsNullOrEmpty(value2) && !IsMetaToken(key2) && !OfficialTokens.Contains(key2) && !Done.Contains(text + "\0" + key2)) { string value3 = null; dictionary2?.TryGetValue(key2, out value3); if (!string.IsNullOrEmpty(value3) && !SlotsSafe(value3, value2)) { Done.Add(text + "\0" + key2); num4++; } else { dictionary[key2] = value2; Done.Add(text + "\0" + key2); num3++; } } } } if (flag) { if (++_retries < 3600) { _dirty = true; } else { ValheimTWPlugin.Warn("Jotunn 语言表接管:仍有词典未激活,已停止重试。"); } } else { _retries = 0; } if (num2 + num3 > 0 || num4 > 0) { ValheimTWPlugin.Log("Jotunn 语言表接管:" + num + " 个 MOD,写入 " + (num2 + num3) + " 条(本插件译文 " + num2 + " / MOD 自带中文 " + num3 + ")" + ((num4 > 0) ? (",跳过占位符不安全 " + num4 + " 条") : "") + "。"); } } private static void SweepLocalizations() { try { Type type = _tLM ?? AccessTools.TypeByName("Jotunn.Managers.LocalizationManager"); if (type == null) { return; } if (_fLocalizations == null) { _fLocalizations = AccessTools.Field(type, "Localizations"); } if (_fLocalizations == null) { return; } object obj = null; try { PropertyInfo propertyInfo = AccessTools.Property(type, "Instance"); if (propertyInfo != null) { obj = propertyInfo.GetValue(null, null); } } catch { } if (obj == null || !(_fLocalizations.GetValue(obj) is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { if (item == null) { continue; } object obj3 = item; Type type2 = item.GetType(); PropertyInfo propertyInfo2 = (type2.IsGenericType ? type2.GetProperty("Value") : null); if (propertyInfo2 != null) { obj3 = propertyInfo2.GetValue(item, null); } if (obj3 == null) { continue; } bool flag = false; for (int i = 0; i < Registered.Count; i++) { if (Registered[i] == obj3) { flag = true; break; } } if (!flag) { Registered.Add(obj3); _dirty = true; } } } catch { } } private static void TryTranslatePostfix(object __instance, string __0, ref string __result) { try { if (string.IsNullOrEmpty(__result) || HasCjk(__result) || (ValheimTWPlugin.CfgJotunnTakeover != null && !ValheimTWPlugin.CfgJotunnTakeover.Value)) { return; } string text = ((__0 != null && __0.Length > 0 && __0[0] == '$') ? __0.Substring(1) : __0); if (string.IsNullOrEmpty(text)) { return; } if (ChainZhCache.TryGetValue(text, out var value)) { if (!string.IsNullOrEmpty(value)) { __result = value; } return; } value = ResolveChainZh(__instance, text, __result, out var dictReady); if (dictReady) { ChainZhCache[text] = value; } if (!string.IsNullOrEmpty(value)) { __result = value; } } catch { } } private static string ResolveChainZh(object inst, string token, string english, out bool dictReady) { dictReady = false; try { if (IsMetaToken(token)) { dictReady = true; return null; } string text = GuidOf(inst); ModDictionary modDictionary = ((text != null) ? LocEngine.Get(text) : null); if (modDictionary == null) { return null; } if (!modDictionary.Active) { return null; } dictReady = true; if (modDictionary.DeclaredTokens.TryGetValue(token, out var value) && !string.IsNullOrEmpty(value) && !string.Equals(value, token, StringComparison.Ordinal)) { return value; } if (string.IsNullOrEmpty(english)) { return null; } value = OurTranslate(modDictionary, english); if (string.IsNullOrEmpty(value) || string.Equals(value, english, StringComparison.Ordinal)) { return null; } if (!SlotsSafe(english, value)) { return null; } return value; } catch { return null; } } private static bool HasCjk(string s) { foreach (char c in s) { if (c >= '一' && c <= '鿿') { return true; } } return false; } internal static JObject CaptureTokensFor(string guid) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown try { if (_tCustomLoc == null || string.IsNullOrEmpty(guid)) { return null; } JObject val = new JObject(); SweepLocalizations(); for (int i = 0; i < Registered.Count; i++) { object obj = Registered[i]; if (obj == null || !string.Equals(GuidOf(obj), guid, StringComparison.Ordinal)) { continue; } Dictionary<string, string> dictionary = Table(obj, "English"); if (dictionary == null) { continue; } foreach (KeyValuePair<string, string> item in dictionary) { string key = item.Key; string value = item.Value; if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value) && !IsMetaToken(key) && val[key] == null) { val[key] = (JToken)new JObject { ["src"] = JToken.op_Implicit(value), ["zh"] = JToken.op_Implicit("") }; } } } return val; } catch { return null; } } internal static string OwnerOfToken(string token) { if (string.IsNullOrEmpty(token)) { return null; } try { RebuildTokenOwners(); string value; return _tokenOwner.TryGetValue(token, out value) ? value : null; } catch (Exception) { return null; } } private static void RebuildTokenOwners() { if (_tokenOwnerStamp == Registered.Count) { return; } _tokenOwnerStamp = Registered.Count; _tokenOwner.Clear(); _tokenEn.Clear(); _tokenAmbiguous.Clear(); for (int i = 0; i < Registered.Count; i++) { object obj = Registered[i]; if (obj == null) { continue; } string value = GuidOf(obj); if (string.IsNullOrEmpty(value)) { continue; } Dictionary<string, string> dictionary = Table(obj, "English"); if (dictionary == null) { continue; } foreach (KeyValuePair<string, string> item in dictionary) { if (!string.IsNullOrEmpty(item.Key)) { if (!_tokenOwner.ContainsKey(item.Key)) { _tokenOwner[item.Key] = value; _tokenEn[item.Key] = item.Value ?? ""; } else { _tokenAmbiguous.Add(item.Key); } } } } } internal static bool TryBridgeModToken(string token, out string guid, out string english) { guid = null; english = null; if (string.IsNullOrEmpty(token)) { return false; } try { RebuildTokenOwners(); if (_tokenAmbiguous.Contains(token)) { return false; } if (!_tokenOwner.TryGetValue(token, out guid)) { return false; } if (!_tokenEn.TryGetValue(token, out english) || string.IsNullOrEmpty(english)) { return false; } return true; } catch { guid = null; english = null; return false; } } private static string GuidOf(object inst) { try { if (_pSourceMod == null) { return null; } object? value = _pSourceMod.GetValue(inst, null); object? obj = ((value is BepInPlugin) ? value : null); return (obj != null) ? ((BepInPlugin)obj).GUID : null; } catch { return null; } } private static Dictionary<string, Dictionary<string, string>> MapOf(object inst) { if (_pMap == null) { return null; } try { return _pMap.GetValue(inst, null) as Dictionary<string, Dictionary<string, string>>; } catch { return null; } } private static List<string> LanguagesOf(object inst) { Dictionary<string, Dictionary<string, string>> dictionary = MapOf(inst); if (dictionary != null && dictionary.Count > 0) { return new List<string>(dictionary.Keys); } List<string> list = new List<string>(); if (_mGetLanguages == null) { return list; } try { if (_mGetLanguages.Invoke(inst, null) is IEnumerable<string> enumerable) { foreach (string item in enumerable) { list.Add(item); } } } catch { } return list; } private static Dictionary<string, string> Table(object inst, string language) { if (string.IsNullOrEmpty(language)) { return null; } Dictionary<string, Dictionary<string, string>> dictionary = MapOf(inst); if (dictionary != null && dictionary.TryGetValue(language, out var value) && value != null && value.Count > 0) { return value; } if (_mGetTranslations == null) { return null; } try { if (!(_mGetTranslations.Invoke(inst, new object[1] { language }) is IReadOnlyDictionary<string, string> { Count: not 0 } readOnlyDictionary)) { return null; } if (readOnlyDictionary is Dictionary<string, string> result) { return result; } Dictionary<string, string> dictionary2 = new Dictionary<string, string>(readOnlyDictionary.Count, StringComparer.Ordinal); foreach (KeyValuePair<string, string> item in readOnlyDictionary) { dictionary2[item.Key] = item.Value; } return dictionary2; } catch { return null; } } private static Dictionary<string, string> ChineseTable(object inst) { foreach (string item in LanguagesOf(inst)) { if (string.IsNullOrEmpty(item)) { continue; } string text = item.ToLowerInvariant(); if (text.IndexOf("chin", StringComparison.Ordinal) >= 0 || text.IndexOf("zh", StringComparison.Ordinal) == 0) { Dictionary<string, string> dictionary = Table(inst, item); if (dictionary != null && dictionary.Count > 0) { return dictionary; } } } return null; } private static string OurTranslate(ModDictionary d, string src) { try { string text = d.TranslateGlobal(src); if (!string.IsNullOrEmpty(text)) { return text; } } catch { } try { string text2 = d.TranslateWholeUnattributed(src); if (!string.IsNullOrEmpty(text2)) { return text2; } } catch { } return null; } private static bool IsMetaToken(string token) { if (string.IsNullOrEmpty(token)) { return true; } string text = token.ToLowerInvariant(); switch (text) { case "localization_version": case "version": case "language": return true; default: if (text.EndsWith("_version", StringComparison.Ordinal)) { return true; } if (text.EndsWith("_language", StringComparison.Ordinal)) { return true; } return false; } } private static bool SlotsSafe(string src, string dst) { try { if (dst.IndexOf('{') < 0) { return true; } if (src.IndexOf('{') < 0) { return false; } foreach (Match item in SlotRe.Matches(dst)) { if (src.IndexOf("{" + item.Groups[1].Value + "}", StringComparison.Ordinal) < 0) { return false; } } return true; } catch { return false; } } private static void SnapshotOfficial(Localization loc, bool clean) { if (loc == null || _officialSnapshotted) { return; } _officialSnapshotted = true; _snapshotClean = clean; try { if (_fTranslations == null) { _fTranslations = AccessTools.Field(typeof(Localization), "m_translations"); } if (!(_fTranslations?.GetValue(loc) is Dictionary<string, string> dictionary)) { return; } OfficialTokens.Clear(); foreach (string key in dictionary.Keys) { OfficialTokens.Add(key); } } catch { } } } internal static class LocEngine { private static class Diagnostics { private static readonly Dictionary<string, int> Counts = new Dictionary<string, int>(StringComparer.Ordinal); private static readonly List<string> Details = new List<string>(); private const int MaxDetailPerShape = 200; internal const string ShapeLiteralShort = "字面量不足"; internal const string ShapeNarrowPass = "窄字面量放行"; internal const string ShapeBadTemplate = "模板无效"; internal const string ShapeBadRegex = "正则无效"; internal const string ShapeEmptyReplacement = "待翻模板未填译"; internal static void No