using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Net.Http; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using TMPro; using UnityEngine; using ue.Peak.TcnPatch.API; using ue.Peak.TcnPatch.Adapters; using ue.Peak.TcnPatch.Core; using ue.Peak.TcnPatch.Patches; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ue.Peak.TcnPatch")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.5.7.0")] [assembly: AssemblyInformationalVersion("1.5.7+66ae37b0ce94abe7fb3220489ab5e3ad1c3fb0e5")] [assembly: AssemblyProduct("ue.Peak.TcnPatch")] [assembly: AssemblyTitle("ue.Peak.TcnPatch")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.5.7.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class ExtensionMarkerAttribute : Attribute { private readonly string k__BackingField; public string Name => k__BackingField; public ExtensionMarkerAttribute(string name) { k__BackingField = name; } } } namespace ue.Peak.TcnPatch { [BepInPlugin("ue.Peak.TcnPatch", "ue.Peak.TcnPatch", "2.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string ModGuid = "ue.Peak.TcnPatch"; public const string ModName = "ue.Peak.TcnPatch"; public const string ModVersion = "2.1.0"; private static FileSystemWatcher _watcher; public const string TcnTranslationFileName = "TcnTranslations.json"; private static readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); private static readonly VersionTextUpdater _versionStringUpdater = new VersionTextUpdater(); [CanBeNull] private Harmony _harmony; private bool _hasSetTraditionalChineseFont; private static readonly HttpClient _httpClient = new HttpClient(); internal static Plugin Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } internal static TranslationFile CurrentTranslationFile { get; private set; } = new TranslationFile(); internal static PluginConfig ModConfig { get; private set; } [CanBeNull] internal static VersionString VersionStringInstance { get; set; } internal static Dictionary TranslationsLookup { get; } = new Dictionary(); internal static Dictionary AdditionalTranslationsLookup { get; } = new Dictionary(); internal static Dictionary KeyToUnlocalizedLookup { get; } = new Dictionary(); private void Awake() { Instance = this; ModConfig = new PluginConfig(((BaseUnityPlugin)this).Config); ModConfig.EnableAutoDumpLanguage.SettingChanged += delegate { if (ModConfig.EnableAutoDumpLanguage.Value) { LocalizedTextPatch.DumpLanguageEntries(); } }; Logger = ((BaseUnityPlugin)this).Logger; Logger.LogInfo((object)"正在載入模組 - ue.Peak.TcnPatch"); _harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "ue.Peak.TcnPatch"); ue.Peak.TcnPatch.API.TcnPatch internalInstance = ue.Peak.TcnPatch.API.TcnPatch.InternalInstance; internalInstance.RegisterLocalizationKey("PeakTcnPatch.Passport.Crabland", "CRABLAND"); internalInstance.RegisterLocalizationKey("PeakTcnPatch.BoardingPass.CustomExpedition", "CUSTOM EXPEDITION"); MoreAscentsSupport.RegisterLocalizations(); Logger.LogInfo((object)"已載入模組 - ue.Peak.TcnPatch"); Logger.LogInfo((object)" + 非官方繁體中文翻譯支援模組 -- by悠依"); } private void Start() { string path = Path.Combine(Paths.ConfigPath, "ue.Peak.TcnPatch"); Directory.CreateDirectory(path); _watcher = new FileSystemWatcher(path, "*.json"); _watcher.NotifyFilter = NotifyFilters.LastWrite; _watcher.Changed += delegate(object _, FileSystemEventArgs args) { if (args.Name == "TcnTranslations.json") { Logger.LogInfo((object)"正在更新遊戲內繁體中文翻譯資料..."); UpdateMainTable(); } }; if (!ModConfig.DownloadFromRemote.Value) { UpdateMainTable(); } else { Task.Run(async delegate { if (!(await DownloadTranslationsAsync()).IsSuccess) { UpdateMainTable(); } }); } _watcher.EnableRaisingEvents = true; } private void Update() { if (Object.op_Implicit((Object)(object)VersionStringInstance)) { _versionStringUpdater.Update(VersionStringInstance); } SetTraditionalChineseFont(); } private void SetTraditionalChineseFont() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 if (!_hasSetTraditionalChineseFont && Object.op_Implicit((Object)(object)FontFallbackSwapper.instance)) { _hasSetTraditionalChineseFont = true; if ((int)LocalizedText.CURRENT_LANGUAGE == 10) { FontFallbackSwapper.instance.SwitchToTraditional(); } } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _lock.Dispose(); } private static async Task SaveTranslationsAsync(string content) { string dir = Path.Combine(Paths.ConfigPath, "ue.Peak.TcnPatch"); Directory.CreateDirectory(dir); string path = Path.Combine(dir, "TcnTranslations.json"); await _lock.EnterScopeAsync(async delegate { await using FileStream targetStream = File.Open(path, FileMode.Create, FileAccess.Write); await using StreamWriter writer = new StreamWriter(targetStream); await writer.WriteAsync(content); }); } private async Task> DownloadTranslationsAsync() { string url = ModConfig.DownloadUrl.Value; for (int i = 0; i < 2; i++) { Logger.LogInfo((object)"正在從遠端下載翻譯資料... (可以在模組設定停用)"); Logger.LogInfo((object)("網址:" + url)); HttpClient client = _httpClient; try { string content = await client.GetStringAsync(url); try { JObject.Parse(content); } catch (Exception ex) { Exception e = ex; Logger.LogWarning((object)"無效的遠端翻譯資料!將使用本機資料。"); Logger.LogWarning((object)e); return Result.Error(e); } await SaveTranslationsAsync(content); Logger.LogInfo((object)"翻譯資料下載完成!"); return Result.Success(Unit.Instance); } catch (HttpRequestException ex2) { if (i > 0 || ModConfig.DownloadFailureHandling.Value == DownloadFailureHandling.UseLocal) { Logger.LogError((object)"翻譯資料下載失敗!將使用本機資料。"); Logger.LogError((object)ex2); return Result.Error((Exception)ex2); } Logger.LogError((object)"翻譯資料下載失敗!將嘗試使用預設的遠端資料。"); url = (string)((ConfigEntryBase)ModConfig.DownloadUrl).DefaultValue; } catch (Exception ex3) { Logger.LogError((object)"翻譯資料下載失敗!將使用本機資料。"); Logger.LogError((object)ex3); return Result.Error(ex3); } } return Result.Error(new Exception("unreachable")); } internal static Option GetVanilla(string id) { return TranslationsLookup.GetOptional(id.ToUpperInvariant()); } internal static Option GetRegistered(string id, Language? language) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_0038: Invalid comparison between Unknown and I4 //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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) Language valueOrDefault = language.GetValueOrDefault(); if (!language.HasValue) { valueOrDefault = LocalizedText.CURRENT_LANGUAGE; language = valueOrDefault; } return (((int)language.GetValueOrDefault() == 10) ? AdditionalTranslationsLookup.GetOptional(id) : Option.None).OrGet(() => KeyToUnlocalizedLookup.GetOptional(id)); } private static void UpdateMainTable() { ControlFlow controlFlow = _lock.EnterScope(() => TryReadFromJson("TcnTranslations.json", (Func)(() => new JObject())).SelectMany(TranslationFile.TryDeserialize).Select((Func>)delegate(TranslationFile f) { CurrentTranslationFile = f; return ControlFlow.Continue(); }).SelectError((Func>)delegate(Exception ex) { if (ex is TranslationParseException ex2) { Logger.LogError((object)ex2.UserMessage); Logger.LogError((object)"翻譯資料分析失敗!"); } else { Logger.LogError((object)"翻譯資料分析失敗!"); Logger.LogError((object)ex); } return ControlFlow.Break(); }) .Branch()); if (controlFlow.IsBreak) { return; } if (CurrentTranslationFile.Authors.Count > 0) { Logger.LogInfo((object)("翻譯資料作者:" + string.Join("、", CurrentTranslationFile.Authors))); } else { Logger.LogInfo((object)"翻譯資料作者:未知"); } Dictionary> mainTable = LocalizedText.mainTable; HashSet hashSet = mainTable.Keys.ToHashSet(); TranslationsLookup.Clear(); AdditionalTranslationsLookup.Clear(); string value; string key; foreach (KeyValuePair translation in CurrentTranslationFile.Translations) { translation.Deconstruct(out value, out key); string text = value; string value2 = key; string text2 = text.ToUpperInvariant(); if (TranslationsLookup.ContainsKey(text2)) { Logger.LogInfo((object)("發現重複的翻譯key:「" + text + "」!已存在大寫的同名key!")); continue; } if (!mainTable.ContainsKey(text2) && ModConfig.WarnUnknownTranslationKeys.Value) { Logger.LogWarning((object)("正在使用未知的翻譯key:「" + text2 + "」!")); } TranslationsLookup[text2] = value2; hashSet.Remove(text2); } foreach (KeyValuePair additionalTranslation in CurrentTranslationFile.AdditionalTranslations) { additionalTranslation.Deconstruct(out key, out value); string text3 = key; string value3 = value; AdditionalTranslationsLookup[text3] = value3; hashSet.Remove(text3); } HashSet vanillaLocalizationKeys = LocalizedTextPatch.VanillaLocalizationKeys; foreach (string item in hashSet) { if (vanillaLocalizationKeys.Contains(item)) { Logger.LogWarning((object)("缺少「" + item + "」翻譯key,請更新翻譯資料!")); } else if (ModConfig.WarnMissingAdditionalKeys.Value) { Logger.LogWarning((object)("*附加翻譯* 缺少「" + item + "」翻譯key!")); } } LocalizedText.RefreshAllText(); } private static Result TryReadFromJson(string fileName, Func defaultContent) where T : class { Result result = Result.Catch(delegate { string text = Path.Combine(Paths.ConfigPath, "ue.Peak.TcnPatch"); Directory.CreateDirectory(text); string text2 = Path.Combine(text, fileName); if (File.Exists(text2)) { return text2; } string contents = JsonConvert.SerializeObject((object)defaultContent()); File.WriteAllText(text2, contents); return text2; }); return result.IfError(delegate(Exception e) { Logger.LogError((object)("無法初始化 JSON 設定:" + fileName)); Logger.LogError((object)e); }).SelectMany(DeserializeFromPath); Result DeserializeFromPath(string path) { return Result.Catch(delegate { using FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); using StreamReader streamReader = new StreamReader(stream); return JsonConvert.DeserializeObject(streamReader.ReadToEnd()); }).IfError(delegate(Exception e) { Logger.LogError((object)("無法讀取 JSON 設定:" + fileName)); Logger.LogError((object)e); }); } } } public class PluginConfig { public ConfigEntry DownloadFromRemote { get; } = config.Bind("Update", "DownloadFromRemote", true, "是否每次啟動都要從遠端下載最新的翻譯資料? (true: 是, false: 否)"); public ConfigEntry DownloadUrl { get; } = config.Bind("Update", "DownloadUrl", "https://raw.githubusercontent.com/Yuieii/ue.Peak.TcnPatch/refs/heads/master/TcnTranslations.json", "翻譯資料的 URL"); public ConfigEntry DownloadFailureHandling { get; } = config.Bind("Update", "DownloadFailureHandling", ue.Peak.TcnPatch.DownloadFailureHandling.UseDefaultRemote, "當設定的 URL 下載失敗時如何處理"); public ConfigEntry AutoDumpLanguage { get; } = config.Bind("Debug", "AutoDumpLanguage", (Language)9, "自動輸出官方參考翻譯時選擇的原始語言"); public ConfigEntry EnableAutoDumpLanguage { get; } = config.Bind("Debug", "EnableAutoDumpLanguage", true, string.Join('\n', "是否自動輸出遊戲內的翻譯表以供參考", " (有 ModConfig 的話即時更改可以重新輸出)")); public ConfigEntry WarnUnknownTranslationKeys { get; } = config.Bind("Debug", "WarnUnknownTranslationKeys", false, "有未知的非附加翻譯key時是否觸發警告"); public ConfigEntry WarnMissingAdditionalKeys { get; } = config.Bind("Debug", "WarnMissingAdditionalKeys", false, "有缺失的附加翻譯key時是否觸發警告"); public ConfigEntry ShowPatchCredit { get; } = config.Bind("Patch", "ShowPatchCredit", true, "在主畫面版本文字下方顯示本模組作者?"); public ConfigEntry ShowTranslatorCredit { get; } = config.Bind("Patch", "ShowTranslatorCredit", true, "在主畫面版本文字下方顯示翻譯資料的作者?"); public ConfigEntry ShowModVersionInPatchCredit { get; } = config.Bind("Patch", "ShowModVersionInPatchCredit", true, "在主畫面版本文字後面顯示這個模組的版本? (v2.1.0)"); public PluginConfig(ConfigFile config) { } } public enum DownloadFailureHandling { UseLocal, UseDefaultRemote } internal static class ReflectionExtensions { [SpecialName] public sealed class $8048A6C8BE30A622530249B904B537EB<$T0> { [SpecialName] public static class $B99A874B2F9892B4D9EADD5CC9D40768 { } [ExtensionMarker("$B99A874B2F9892B4D9EADD5CC9D40768")] public TValue GetReflectionFieldValue(TypedFieldInfo<$T0, TValue> fieldInfo) { throw new NotSupportedException(); } [ExtensionMarker("$B99A874B2F9892B4D9EADD5CC9D40768")] public void SetReflectionFieldValue(TypedFieldInfo<$T0, TValue> fieldInfo, TValue value) { throw new NotSupportedException(); } } [SpecialName] public sealed class $C0F9324A7FB6D595E082877B54F807CC { [SpecialName] public static class $4776623D3010553C7D2AB93E76A250FB { } [ExtensionMarker("$4776623D3010553C7D2AB93E76A250FB")] [CanBeNull] public TypedFieldInfo AsTyped() { throw new NotSupportedException(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TValue GetReflectionFieldValue(this TOwner owner, TypedFieldInfo fieldInfo) { return (TValue)fieldInfo.FieldInfo.GetValue(owner); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetReflectionFieldValue(this TOwner owner, TypedFieldInfo fieldInfo, TValue value) { fieldInfo.FieldInfo.SetValue(owner, value); } [CanBeNull] public static TypedFieldInfo AsTyped([CanBeNull] this FieldInfo field) { return field; } } public static class Refl { public static class LoadingScreenAnimation { public static TypedFieldInfo LoadingString { get; } = AccessTools.Field(typeof(LoadingScreenAnimation), "loadingString"); public static TypedFieldInfo DefaultLoadingStringLength { get; } = AccessTools.Field(typeof(LoadingScreenAnimation), "defaultLoadingStringLength"); } public static class LocalizedText { public static TypedFieldInfo CurrentLanguage { get; } = AccessTools.Field(typeof(LocalizedText), "CURRENT_LANGUAGE"); } public static class VersionString { public static TypedFieldInfo Text { get; } = AccessTools.Field(typeof(VersionString), "m_text"); } } public class TypedFieldInfo(FieldInfo fieldInfo) { public FieldInfo FieldInfo { get; } = fieldInfo; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator TypedFieldInfo(FieldInfo fieldInfo) { return (fieldInfo == null) ? null : new TypedFieldInfo(fieldInfo); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator FieldInfo(TypedFieldInfo fieldInfo) { return fieldInfo?.FieldInfo; } public TValue GetStaticValue() { return (TValue)FieldInfo.GetValue(null); } } public class TranslationParseException : Exception { public string UserMessage { get; } public TranslationParseException(string message, string userMessage) : base(message) { UserMessage = userMessage; } public TranslationParseException(string message) : base(message) { UserMessage = message; } } public class TranslationFile { public const int CurrentFormatVersion = 0; public const string FormatVersionKey = "FormatVersion"; public const string AuthorKey = "Authors"; public const string TranslationEntriesKey = "Translations"; public const string AdditionalTranslationEntriesKey = "AdditionalTranslations"; public List Authors { get; } = new List(); public Dictionary Translations { get; } = new Dictionary(); public Dictionary AdditionalTranslations { get; } = new Dictionary(); public static Result TryDeserialize(JObject obj) { return Result.Catch(() => Deserialize(obj)); } public static TranslationFile Deserialize(JObject obj) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Invalid comparison between Unknown and I4 //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) string[] source = new string[2] { "FormatVersion", "Translations" }; if (!((IEnumerable)source).All((Func)obj.ContainsKey)) { return InternalDeserializeFromLegacy(obj); } TranslationFile result = new TranslationFile(); JToken val = obj["FormatVersion"]; if ((int)val.Type != 6) { throw new TranslationParseException($"Format version must be an integer value, found {val.Type}", "無效的格式版本!格式版本必須為一個整數!"); } int num = Extensions.Value((IEnumerable)val); if (num > 0) { Plugin.Logger.LogWarning((object)"正在讀取過新版本的翻譯資料!可能會無法正確讀取。"); } ((IDictionary)obj).GetOptional("Authors").IfSome(delegate(JToken authorsToken) { JArray val6 = (JArray)(object)((authorsToken is JArray) ? authorsToken : null); if (val6 != null) { foreach (JToken item in val6) { result.Authors.Add(Extensions.Value((IEnumerable)item)); } return; } JValue val7 = (JValue)(object)((authorsToken is JValue) ? authorsToken : null); if (val7 != null) { result.Authors.Add(Extensions.Value((IEnumerable)val7)); } else { Plugin.Logger.LogWarning((object)"無效的翻譯者資料! (Authors)"); } }); JToken val2 = obj["Translations"]; JObject val3 = (JObject)(object)((val2 is JObject) ? val2 : null); if (val3 == null) { throw new TranslationParseException($"Translation entries must be an object, found {val2.Type}", "無效的翻譯資料! (Translations)"); } foreach (var (key, val5) in val3) { result.Translations[key] = Extensions.Value((IEnumerable)val5); } ((IDictionary)obj).GetOptional("AdditionalTranslations").IfSome(delegate(JToken entries) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) JObject val6 = (JObject)(object)((entries is JObject) ? entries : null); if (val6 == null) { throw new TranslationParseException($"Additional translation entries must be an object, found {entries.Type}", "無效的附加翻譯資料! (AdditionalTranslations)"); } List list = new List(); foreach (var (text3, val8) in val6) { if (list.Contains(text3, StringComparer.InvariantCultureIgnoreCase)) { Plugin.Logger.LogWarning((object)("翻譯資料出現已註冊過的附加翻譯key「" + text3 + "」!新的同名翻譯將會被忽略。")); } else { list.Add(text3); result.AdditionalTranslations[text3] = Extensions.Value((IEnumerable)val8); } } }); return result; } private static TranslationFile InternalDeserializeFromLegacy(JObject obj) { TranslationFile translationFile = new TranslationFile(); foreach (var (key, val2) in obj) { translationFile.Translations[key] = Extensions.Value((IEnumerable)val2); } return translationFile; } } public static class Utils { public static Option ToOptionUnity(this T obj) where T : Object { return ((Object)(object)obj == (Object)null) ? ((Option)Option.None) : Option.Some(obj); } public static Task WaitForFramesAsync(int frames) { TaskCompletionSource taskCompletionSource = new TaskCompletionSource(); ((MonoBehaviour)Plugin.Instance).StartCoroutine(DelayFramesCoroutine(taskCompletionSource, frames, () => true)); return taskCompletionSource.Task; } public static async Task WaitForNextFrameAsync() { await WaitForFramesAsync(1); } private static IEnumerator DelayFramesCoroutine(TaskCompletionSource source, int frames, Func resultGetter) { for (int i = 0; i < frames; i++) { yield return (object)new WaitForEndOfFrame(); } source.SetResult(resultGetter()); } } public enum ReturnFlow { Continue, Break } public class VersionTextUpdater { [CanBeNull] private string _originalText; public void Update(VersionString instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 if ((int)LocalizedText.CURRENT_LANGUAGE != 10 || !Plugin.ModConfig.ShowPatchCredit.Value) { return; } TypedFieldInfo text = Refl.VersionString.Text; if (text == null) { return; } TextMeshProUGUI reflectionFieldValue = instance.GetReflectionFieldValue(text); if (_originalText == null) { _originalText = ((TMP_Text)reflectionFieldValue).text; } string text2 = "繁中翻譯by: " + string.Join("、", Plugin.CurrentTranslationFile.Authors); string text3 = (Plugin.ModConfig.ShowModVersionInPatchCredit.Value ? "繁中支援v2.1.0 by悠依" : "繁中支援by悠依"); bool flag = Plugin.ModConfig.ShowTranslatorCredit.Value && Plugin.CurrentTranslationFile.Authors.Count > 0; string name = ((Object)((Component)((Component)instance).transform.GetParent()).gameObject).name; string name2 = ((Object)((Component)instance).gameObject).name; ((TMP_Text)reflectionFieldValue).text = _originalText; if (name2 == "Version" && name == "Logo") { string text4 = ((flag & (Math.Floor(Time.realtimeSinceStartup / 10f) % 2.0 != 0.0)) ? text2 : text3); ((TMP_Text)reflectionFieldValue).text = ((TMP_Text)reflectionFieldValue).text + " (" + text4 + ")"; return; } if (name != "MainPage") { ((TMP_Text)reflectionFieldValue).text = ((TMP_Text)reflectionFieldValue).text + "
ue.Peak.TcnPatch v2.1.0"; return; } ((TMP_Text)reflectionFieldValue).text = ((TMP_Text)reflectionFieldValue).text + "
" + text3 + ""; if (flag) { ((TMP_Text)reflectionFieldValue).text = ((TMP_Text)reflectionFieldValue).text + "
" + text2 + ""; } } } } namespace ue.Peak.TcnPatch.Patches { [HarmonyPatch] public class CustomOptionsWindowPatch { [HarmonyPatch(typeof(CustomOptionsWindow), "Initialize")] [HarmonyPostfix] private static void PatchTitle(CustomOptionsWindow __instance) { ((Component)__instance).transform.Find("CustomOptions/Panel/Title").ToOptionUnity().IfNone(delegate { Plugin.Logger.LogDebug((object)"!!: Failed to find the title text in custom options UI"); }) .IfSome(delegate(Transform transform) { LocalizedText val = ((Component)transform).gameObject.GetComponent(); if ((Object)(object)val == (Object)null) { val = ((Component)transform).gameObject.AddComponent(); val.autoSet = false; } if (!val.autoSet) { val.index = "PeakTcnPatch.BoardingPass.CustomExpedition"; val.autoSet = true; val.RefreshText(); } }); } } [HarmonyPatch] public class GUIManagerPatch { [HarmonyPatch(typeof(GUIManager), "Awake")] [HarmonyPostfix] private static void PatchPrompt(GUIManager __instance) { __instance.strugglePrompt.ToOptionUnity().SelectMany((CanvasGroup g) => ((Component)g).transform.Find("PromptText").ToOptionUnity()).SelectMany((Transform c) => ((Component)c).GetComponent().ToOptionUnity()) .IfNone(delegate { Plugin.Logger.LogDebug((object)"!!: Failed to find the text in the Struggle prompt UI"); }) .IfSome(delegate(LocalizedText text) { if (!text.autoSet) { text.autoSet = true; text.RefreshText(); } }); } } [HarmonyPatch] public class LoadingScreenAnimationPatch { private static bool _loadingScreenAnimationStartTranspiled; [HarmonyPatch(typeof(LoadingScreenAnimation), "Start")] [HarmonyTranspiler] private static IEnumerable PatchLoadingAnimationSwitch(IEnumerable instructions, ILGenerator generator) { //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Expected O, but got Unknown Plugin.Logger.LogInfo((object)"正在修補 LoadingScreenAnimation.Start() 的 IL code..."); List list = instructions.ToList(); int num = -1; int num2 = -1; for (int i = 0; i < list.Count; i++) { CodeInstruction val = list[i]; if (CodeInstructionExtensions.LoadsField(val, (FieldInfo)Refl.LocalizedText.CurrentLanguage, false)) { CodeInstruction val2 = list[i + 1]; if (!(val2.opcode != OpCodes.Ldc_I4_S) && val2.operand.Equals((sbyte)9)) { num = i; num2 = i + 3; break; } } } if (num == -1 || num2 == -1) { Plugin.Logger.LogWarning((object)"沒有在 LoadingScreenAnimation.Start() 找到合適的對應 branch!"); Plugin.Logger.LogWarning((object)"無法以 IL 方式修補 LoadingScreenAnimation.Start()!將會使用 Postfix 方式修補文字。"); return list; } Plugin.Logger.LogInfo((object)"正在插入新的 IL code..."); CodeInstruction val3 = list[num2]; Label label = generator.DefineLabel(); val3.labels.Add(label); List list2 = new List(); list2.Add(CodeInstruction.LoadField(typeof(LocalizedText), "CURRENT_LANGUAGE", false)); list2.Add(new CodeInstruction(OpCodes.Ldc_I4_S, (object)10)); list2.Add(new CodeInstruction(OpCodes.Beq_S, (object)label)); List collection = list2; list.InsertRange(num, collection); _loadingScreenAnimationStartTranspiled = true; return list; } [HarmonyPatch(typeof(LoadingScreenAnimation), "Start")] [HarmonyPostfix] private static void PatchLoadingAnimation(LoadingScreenAnimation __instance) { if (!_loadingScreenAnimationStartTranspiled) { string text = LocalizedText.GetText("LOADING", true); if (Refl.LoadingScreenAnimation.LoadingString == null) { Plugin.Logger.LogWarning((object)"Patcher 找不到欄位: LoadingScreenAnimation.loadingString"); return; } if (Refl.LoadingScreenAnimation.DefaultLoadingStringLength == null) { Plugin.Logger.LogWarning((object)"Patcher 找不到欄位: LoadingScreenAnimation.defaultLoadingStringLength"); return; } Plugin.Logger.LogInfo((object)"正在修正載入中動畫的文字 @ LoadingScreenAnimation.Start()..."); string text2 = text + "..." + text + "..." + text + "..." + text + "..."; __instance.SetReflectionFieldValue(Refl.LoadingScreenAnimation.LoadingString, text2); __instance.SetReflectionFieldValue(Refl.LoadingScreenAnimation.DefaultLoadingStringLength, text2.Length); } } } [HarmonyPatch] public class LocalizedTextPatch { private class AutoDumpRecord { public int FormatVersion { get; set; } = 0; public List Authors { get; } = new List(); public Dictionary Translations { get; } public Dictionary AdditionalTranslations { get; } public AutoDumpRecord(Dictionary translations, Dictionary additionalTranslations) { Translations = translations; AdditionalTranslations = additionalTranslations; base..ctor(); } } private static bool _autoDumpedMainTable; private static readonly Lazy _readIfTooLate = new Lazy(delegate { if (_earlyEnough) { return Unit.Instance; } Plugin.Logger.LogWarning((object)"正在重新讀取 PEAK 官方翻譯表..."); Dictionary>.KeyCollection keyCollection = Resources.Load("Localization/SerializedTermsData").ToOptionUnity().Select(delegate(TextAsset t) { string text = t.text; return JsonConvert.DeserializeObject>>(text).Keys; }) .OrElseGet(delegate { Plugin.Logger.LogWarning((object)"找不到 PEAK 官方翻譯表: 無法讀取 TextAsset @ Localization/SerializedTermsData"); Plugin.Logger.LogWarning((object)"所有的現有翻譯將會被視為原版翻譯。"); return LocalizedText.mainTable.Keys; }); foreach (string item in keyCollection) { VanillaLocalizationKeys.Add(item); } if (!_autoDumpedMainTable) { PatchLoadMainTable(); } return Unit.Instance; }); private static readonly Lazy _languageCountLazy = new Lazy(() => Enum.GetNames(typeof(Language)).Length); private static bool _earlyEnough; public static HashSet VanillaLocalizationKeys { get; } = new HashSet(); [HarmonyPatch(typeof(LocalizedText), "GetText", new Type[] { typeof(string), typeof(bool) })] [HarmonyPrefix] private static void PatchGetText(string id, ref string __result, ref bool __runOriginal) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) PatchGetText(id, LocalizedText.CURRENT_LANGUAGE, ref __result, ref __runOriginal); } [HarmonyPatch(typeof(LocalizedText), "GetText", new Type[] { typeof(string), typeof(Language) })] [HarmonyPrefix] private static void PatchGetText(string id, Language language, ref string __result, ref bool __runOriginal) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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) _ = _readIfTooLate.Value; (bool, string result) tuple = (from result in (from result in (from result in Plugin.GetRegistered(id, language) where !string.IsNullOrEmpty(result.Trim()) select result).OrGet(() => Plugin.GetRegistered(id.ToUpperInvariant(), language)) where !string.IsNullOrEmpty(result.Trim()) select result).OrGet(() => ((int)language != 10) ? ((Option)Option.None) : Plugin.GetVanilla(id)) where !string.IsNullOrEmpty(result.Trim()) select (false, result: result)).OrElse((true, null)); bool item = tuple.Item1; string item2 = tuple.result; __runOriginal = item; __result = item2; if (__runOriginal && LocalizedText.mainTable.TryGetValue(id.ToUpperInvariant(), out var value)) { FixLanguageList(value); } } private static List FixLanguageList(List list) { while (list.Count < _languageCountLazy.Value) { list.Add(string.Empty); } return list; } [HarmonyPatch(typeof(LocalizedText), "LoadMainTable")] [HarmonyPriority(800)] [HarmonyPostfix] private static void PatchLoadMainTableFirstChance() { _earlyEnough = true; foreach (string key in LocalizedText.mainTable.Keys) { VanillaLocalizationKeys.Add(key); } } [HarmonyPatch(typeof(LocalizedText), "LoadMainTable")] [HarmonyPriority(0)] [HarmonyPostfix] private static void PatchLoadMainTable() { if (!_autoDumpedMainTable) { _autoDumpedMainTable = true; if (Plugin.ModConfig.EnableAutoDumpLanguage.Value) { DumpLanguageEntries(); } } } internal static void DumpLanguageEntries() { //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) Plugin.Logger.LogInfo((object)"正在自動輸出翻譯表至 _AutoTcnTranslations.json..."); string text = Path.Combine(Paths.ConfigPath, "ue.Peak.TcnPatch"); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); } string path = Path.Combine(text, "_AutoTcnTranslations.json"); Language language = Plugin.ModConfig.AutoDumpLanguage.Value; Dictionary translations = LocalizedText.mainTable.Where((KeyValuePair> p) => VanillaLocalizationKeys.Contains(p.Key)).ToDictionary((KeyValuePair> p) => p.Key, (KeyValuePair> p) => FixLanguageList(p.Value)[(int)language]); Dictionary dictionary = LocalizedText.mainTable.Where((KeyValuePair> p) => !VanillaLocalizationKeys.Contains(p.Key)).ToDictionary((KeyValuePair> p) => p.Key, (KeyValuePair> p) => FixLanguageList(p.Value)[(int)language]); foreach (KeyValuePair item in Plugin.KeyToUnlocalizedLookup) { item.Deconstruct(out var key, out var value); string key2 = key; string value2 = value; dictionary[key2] = value2; } string contents = JsonConvert.SerializeObject((object)new AutoDumpRecord(translations, dictionary), (Formatting)1); File.WriteAllText(path, contents); } } [HarmonyPatch] public class PassportManagerPatch { [HarmonyPatch(typeof(PassportManager), "Awake")] [HarmonyPostfix] private static void PatchCrabland(PassportManager __instance) { ((Component)__instance).transform.Find("PassportUI/Canvas/Panel/Panel/BG/Text/Nationality/Text").ToOptionUnity().IfNone(delegate { Plugin.Logger.LogDebug((object)"!!: Failed to find the Crabland text in passport UI"); }) .IfSome(delegate(Transform transform) { LocalizedText val = ((Component)transform).gameObject.GetComponent(); if ((Object)(object)val == (Object)null) { val = ((Component)transform).gameObject.AddComponent(); val.autoSet = false; } if (!val.autoSet) { val.index = "PeakTcnPatch.Passport.Crabland"; val.autoSet = true; val.RefreshText(); } }); } } [HarmonyPatch] public class VersionStringPatch { private static bool _versionTextMissingWarned; [HarmonyPatch(typeof(VersionString), "Start")] [HarmonyPostfix] private static void PatchVersionStringWarnOnStart(VersionString __instance) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) if (Refl.VersionString.Text == null) { if (!_versionTextMissingWarned) { _versionTextMissingWarned = true; Plugin.Logger.LogWarning((object)"VersionString: 找不到版本資訊的 m_text 欄位!"); } return; } string name = ((Object)((Component)((Component)__instance).transform.GetParent()).gameObject).name; string name2 = ((Object)((Component)__instance).gameObject).name; if (name2 == "Version" && name == "MainPage") { RectTransform component = ((Component)__instance).GetComponent(); Vector2 anchoredPosition = component.anchoredPosition; anchoredPosition.y -= 10f; component.anchoredPosition = anchoredPosition; TypedFieldInfo text = Refl.VersionString.Text; TextMeshProUGUI reflectionFieldValue = __instance.GetReflectionFieldValue(text); ((TMP_Text)reflectionFieldValue).verticalAlignment = (VerticalAlignmentOptions)256; } Plugin.VersionStringInstance = __instance; } } } namespace ue.Peak.TcnPatch.Core { public static class ControlFlow { public class PartialWithBreak(T value) { internal T Value { get; } = value; public ControlFlow FulfillContinueType() { return ControlFlow.Break(Value); } } public class PartialWithContinue(T value) { internal T Value { get; } = value; public ControlFlow FulfillBreakType() { return ControlFlow.Continue(Value); } } public static PartialWithBreak Break(T value) { return new PartialWithBreak(value); } public static PartialWithBreak Break() { return new PartialWithBreak(Unit.Instance); } public static PartialWithContinue Continue(T value) { return new PartialWithContinue(value); } public static PartialWithContinue Continue() { return new PartialWithContinue(Unit.Instance); } } public abstract class ControlFlow { private class BreakBranch : ControlFlow { private readonly TBreak _value; public override bool IsBreak => true; public override bool IsContinue => false; public BreakBranch(TBreak value) { _value = value; } public override Option GetContinueValue() { return Option.None; } public override Result ToContinueSuccess() { return Result.Error(_value); } public override ControlFlow SelectContinue(Func transform) { return ControlFlow.Break(_value); } public override Option GetBreakValue() { return Option.Some(_value); } public override Result ToBreakSuccess() { return Result.Success(_value); } public override ControlFlow SelectBreak(Func transform) { return ControlFlow.Break(transform(_value)); } } private class ContinueBranch : ControlFlow { private readonly TContinue _value; public override bool IsBreak => false; public override bool IsContinue => true; public ContinueBranch(TContinue value) { _value = value; } public override Option GetContinueValue() { return Option.Some(_value); } public override Result ToContinueSuccess() { return Result.Success(_value); } public override ControlFlow SelectContinue(Func transform) { return ControlFlow.Continue(transform(_value)); } public override Option GetBreakValue() { return Option.None; } public override Result ToBreakSuccess() { return Result.Error(_value); } public override ControlFlow SelectBreak(Func transform) { return ControlFlow.Continue(_value); } } public abstract bool IsBreak { get; } public abstract bool IsContinue { get; } public static ControlFlow Continue(TContinue value) { return new ContinueBranch(value); } public static ControlFlow Break(TBreak value) { return new BreakBranch(value); } public static implicit operator ControlFlow(ControlFlow.PartialWithContinue part) { return new ContinueBranch(part.Value); } public static implicit operator ControlFlow(ControlFlow.PartialWithBreak part) { return new BreakBranch(part.Value); } public abstract Option GetContinueValue(); public abstract Result ToContinueSuccess(); public abstract ControlFlow SelectContinue(Func transform); public abstract Option GetBreakValue(); public abstract Result ToBreakSuccess(); public abstract ControlFlow SelectBreak(Func transform); } public static class NumberExtensions { [SpecialName] public sealed class $BA41CFE2B5EDAEB8C1B9062F59ED4D69 { [SpecialName] public static class $5DACCD576B731A5595F442DFA5E8AE24 { } [ExtensionMarker("$5DACCD576B731A5595F442DFA5E8AE24")] public int BitCount { [ExtensionMarker("$5DACCD576B731A5595F442DFA5E8AE24")] get { throw new NotSupportedException(); } } [ExtensionMarker("$5DACCD576B731A5595F442DFA5E8AE24")] public int GetBitCount() { throw new NotSupportedException(); } } public static int get_BitCount(int self) { self -= (self >>> 1) & 0x55555555; self = (self & 0x33333333) + ((self >>> 2) & 0x33333333); self = (self + (self >>> 4)) & 0xF0F0F0F; self += self >>> 8; self += self >>> 16; return self & 0x3F; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetBitCount(this int self) { return get_BitCount(self); } } public interface IOption { bool IsSome { get; } bool IsNone { get; } IOption Cast(); } public interface IOption : IOption { T Unwrap(); T Expect(string message); T Expect(Exception ex); T Expect(Func ex); IOption Select(Func selector); IOption SelectMany(Func> selector); IOption Where(Func predicate); } public static class Option { [StructLayout(LayoutKind.Sequential, Size = 1)] public struct NoneValuePlaceholder { public Option FulfillType() { return Option.None; } public IOption FulfillType(Type type) { Type type2 = typeof(Option<>).MakeGenericType(type); return (IOption)Activator.CreateInstance(type2); } } [SpecialName] public sealed class $74CAB2AE3CF21190BB3801A1AE8B218F<$T0> { [SpecialName] public static class $BD89945CA7648600A6623ED9B0D34B66 { } [ExtensionMarker("$BD89945CA7648600A6623ED9B0D34B66")] public $T0 OrElse($T0 other) { throw new NotSupportedException(); } [ExtensionMarker("$BD89945CA7648600A6623ED9B0D34B66")] public $T0 OrElseGet(Func<$T0> other) { throw new NotSupportedException(); } [ExtensionMarker("$BD89945CA7648600A6623ED9B0D34B66")] public Option<$T0> SafeUnbox() { throw new NotSupportedException(); } } [SpecialName] public sealed class $89F335C99AD8D0EADD6CEDDA8A8F68D2<$T0, $T1> { [SpecialName] public static class $FB1CE76455089A4956A6AB3011B003C7 { } [ExtensionMarker("$FB1CE76455089A4956A6AB3011B003C7")] public (Option<$T0> First, Option<$T1> Second) Unzip() { throw new NotSupportedException(); } } public static NoneValuePlaceholder None => default(NoneValuePlaceholder); public static Option Some(T value) { return Option.Some(value); } public static Option ToOption(this T? value) where T : class { return (value == null) ? Option.None : Option.Some(value); } public static Option ToOption(this T? value) where T : struct { return (!value.HasValue) ? Option.None : Option.Some(value.Value); } public static IOption Cast(this IOption self) { return (IOption)self; } public static TOption Or(this TOption self, TOption other) where TOption : IOption { return self.IsSome ? self : other; } public static Option GetOptional(this IReadOnlyList self, int index) { if (index < 0 || index >= self.Count) { return None; } return Some(self[index]); } public static T OrElse(this IOption self, T other) { return self.IsSome ? self.Unwrap() : other; } public static T OrElseGet(this IOption self, Func other) { return self.IsSome ? self.Unwrap() : other(); } public static Option SafeUnbox(this IOption self) { if (self is Option result) { return result; } return self.Select>(Option.Some).OrElse(Option.None); } public static (Option First, Option Second) Unzip(this IOption<(TFirst, TSecond)> self) { return self.Select(((TFirst, TSecond) t) => (Some(t.Item1), Some(t.Item2))).OrElse((None, None)); } } public readonly struct Option : IOption, IOption { public bool IsSome { get; } private T ValueUnsafe { get; } public static Option None => default(Option); public bool IsNone => !IsSome; private Option(T value) { IsSome = true; ValueUnsafe = value; } public static implicit operator Option(Option.NoneValuePlaceholder _) { return None; } public static Option Some(T value) { return new Option(value); } public T Unwrap() { return Expect("Cannot unwrap a None value."); } public T Expect(string message) { if (!IsSome) { throw new InvalidOperationException(message); } return ValueUnsafe; } public T Expect(Exception ex) { return IsSome ? ValueUnsafe : ex.Rethrow(); } public T Expect(Func ex) { return IsSome ? ValueUnsafe : ex().Rethrow(); } public Option Select(Func selector) { return IsSome ? Option.Some(selector(ValueUnsafe)) : Option.None; } IOption IOption.Select(Func selector) { return Select(selector); } public Option SelectMany(Func> selector) { return IsSome ? selector(ValueUnsafe) : Option.None; } IOption IOption.SelectMany(Func> selector) { return SelectMany((T t) => selector(t).Select(Option.Some).OrElse(Option.None)); } public Option SelectMany(Func> selector, Func, TSelectMany, TResult> resultSelector) { T valueUnsafe = ValueUnsafe; Option self = this; return IsSome ? (from v in selector(valueUnsafe) select resultSelector(self, v)) : Option.None; } public Option IfSome(Action action) { if (IsSome) { action(ValueUnsafe); } return this; } public Option IfNone(Action action) { if (!IsSome) { action(); } return this; } public Option Match(Action? None = null, Action? Some = null) { if (IsSome) { Some?.Invoke(ValueUnsafe); } else { None?.Invoke(); } return this; } public IEnumerable AsEnumerable() { if (IsSome) { yield return ValueUnsafe; } } public Result ToSuccessOr(TError error) { return Select((Func>)((T x) => Result.Success(x))).OrElseGet(() => Result.Error(error)); } public Option Or(Option other) { return IsSome ? this : other; } public Option Where(Func predicate) { return (!IsSome) ? this : (predicate(ValueUnsafe) ? this : None); } IOption IOption.Where(Func predicate) { return Where(predicate); } public T OrElse(T other) { return (T)(IsSome ? ((object)ValueUnsafe) : ((object)other)); } public Option OrGet(Func> other) { return IsSome ? this : other(); } public T OrElseGet(Func other) { return IsSome ? ValueUnsafe : other(); } public Option CastOrThrow() { return IsSome ? Option.Some((TOut)(object)ValueUnsafe) : Option.None; } public Option Cast() { if (!IsSome) { return Option.None; } T valueUnsafe = ValueUnsafe; return (valueUnsafe is TOut value) ? Option.Some(value) : ((Option)Option.None); } IOption IOption.Cast() { return Cast(); } public Option Xor(Option other) { if (IsSome == other.IsSome) { return Option.None; } return IsSome ? this : other; } public Option<(T, TOther)> Zip(Option other) { if (!IsSome || !other.IsSome) { return Option.None; } return Option.Some((ValueUnsafe, other.ValueUnsafe)); } public Option Zip(Option other, Func zip) { if (!IsSome || !other.IsSome) { return Option.None; } return Option.Some(zip(ValueUnsafe, other.ValueUnsafe)); } } public interface IResult { bool IsSuccess { get; } bool IsError { get; } } public interface IResultSuccess : IResult { Option Value { get; } T Unwrap(); IResultSuccess Select(Func transform); } public interface IResultError : IResult { Option Error { get; } T UnwrapError(); IResultError SelectError(Func transform); } public interface IResult : IResultSuccess, IResult, IResultError { new IResult Select(Func transform); new IResult SelectError(Func transform); IResultSuccess IResultSuccess.Select(Func transform) { return Select(transform); } IResultError IResultError.SelectError(Func transform) { return SelectError(transform); } } public static class Result { public class PartialWithSuccess { private readonly T _value; internal PartialWithSuccess(T value) { _value = value; } public Result FulfillErrorType() { return Result.Success(_value); } } public class PartialWithError { private readonly T _error; internal PartialWithError(T error) { _error = error; } public Result FulfillSuccessType() { return Result.Error(_error); } } public static PartialWithSuccess Success(T value) { return new PartialWithSuccess(value); } public static PartialWithError Error(T error) { return new PartialWithError(error); } public static Result Catch(Func func) { try { return Success(func()); } catch (Exception error) { return Error(error); } } public static T Branch(this IResult self) { return self.IsSuccess ? self.Unwrap() : self.UnwrapError(); } public static Result SafeUnbox(this TResult self) where TResult : IResult { if (self is Result result) { return result; } return self.Select((T val) => Success(val).FulfillErrorType()).SelectError((TError val) => Error(val).FulfillSuccessType()).Branch(); } public static T UnwrapOrThrow(this Result result) where TError : Exception { Option value = result.Value; return value.IsSome ? value.Unwrap() : result.UnwrapError().Rethrow(); } public static T Branch(this Result result) { return result.IsSuccess ? result.Unwrap() : result.UnwrapError(); } public static T FastUnwrap(this Result self) { return self.ValueUnsafe; } public static T FastUnwrapError(this Result self) { return self.ErrUnsafe; } public static Result Flatten(this Result, TError> self) { return self.SelectMany((Result s) => s); } public static Result, TError> Process(this IEnumerable> enumerable) { IEnumerable enumerable2 = Enumerable.Empty(); foreach (Result item in enumerable) { if (item.IsError) { return Error(item.UnwrapError()); } enumerable2 = enumerable2.Append(item.Unwrap()); } return Success(enumerable2); } } public abstract class Result : IResult, IResultSuccess, IResult, IResultError, IEquatable> { private class SuccessBranch : Result { public new T Value { get; } internal SuccessBranch(T value) { Value = value; } public override Result Select(Func func) { return Result.Success(func(Value)); } public override Result SelectMany(Func> func) { return func(Value); } public override Result SelectError(Func func) { return Result.Success(Value); } public override Result IfSuccess(Action action) { action(Value); return this; } public override Result IfError(Action action) { return this; } } private class ErrorBranch : Result { public new TError Error { get; } internal ErrorBranch(TError error) { Error = error; } public override Result Select(Func func) { return Result.Error(Error); } public override Result SelectMany(Func> func) { return Result.Error(Error); } public override Result SelectError(Func func) { return Result.Error(func(Error)); } public override Result IfSuccess(Action action) { return this; } public override Result IfError(Action action) { action(Error); return this; } } public bool IsSuccess => this is SuccessBranch; public bool IsError => this is ErrorBranch; internal T ValueUnsafe => ((SuccessBranch)this).Value; internal TError ErrUnsafe => ((ErrorBranch)this).Error; public Option Value => (this is SuccessBranch successBranch) ? Option.Some(successBranch.Value) : Option.None; public Option Err => (this is ErrorBranch errorBranch) ? Option.Some(errorBranch.Error) : Option.None; Option IResultError.Error => Err; internal Result() { } public static implicit operator Result(Result.PartialWithSuccess partial) { return partial.FulfillErrorType(); } public static implicit operator Result(Result.PartialWithError partial) { return partial.FulfillSuccessType(); } public T Unwrap() { return Value.Expect("Cannot unwrap a success value from an Error branch."); } public TError UnwrapError() { return Err.Expect("Cannot unwrap an error value from a Success branch."); } public abstract Result Select(Func func); IResult IResult.Select(Func transform) { return Select(transform); } public abstract Result SelectMany(Func> func); public Result SelectMany(Func> func, Func, TMiddle, TResult> resultSelector) { return IsError ? ((Result)Result.Error(ErrUnsafe)) : (from m in func(ValueUnsafe) select resultSelector(this, m)); } public abstract Result SelectError(Func func); IResult IResult.SelectError(Func transform) { return SelectError(transform); } public abstract Result IfSuccess(Action action); public abstract Result IfError(Action action); public IEnumerable AsEnumerable() { return Value.AsEnumerable(); } public bool TryUnwrap([MaybeNullWhen(false)] out T value) { if (IsSuccess) { value = ValueUnsafe; return true; } value = default(T); return false; } public bool TryUnwrapError([MaybeNullWhen(false)] out TError error) { if (IsError) { error = ErrUnsafe; return true; } error = default(TError); return false; } public static Result Success(T value) { return new SuccessBranch(value); } public static Result Error(TError error) { return new ErrorBranch(error); } public bool Equals(Result? other) { if ((object)other == null) { return false; } if (IsSuccess != other.IsSuccess) { return false; } return IsSuccess ? ValueUnsafe.Equals(other.ValueUnsafe) : ErrUnsafe.Equals(other.ErrUnsafe); } public override bool Equals(object? obj) { return this == obj || (obj is Result other && Equals(other)); } public override int GetHashCode() { return IsSuccess ? HashCode.Combine(value1: true, ValueUnsafe) : HashCode.Combine(value1: false, ErrUnsafe); } public static bool operator ==(Result? left, Result? right) { return object.Equals(left, right); } public static bool operator !=(Result? left, Result? right) { return !object.Equals(left, right); } } public abstract class ScopeGuard : IDisposable { protected virtual bool ShouldEndScope => true; protected abstract void EndScope(); public void Dispose() { if (ShouldEndScope) { EndScope(); } } } public class DelegateScopeGuard : ScopeGuard { private readonly Action _endScope; private DelegateScopeGuard(Action endScope) { _endScope = endScope; } protected override void EndScope() { _endScope(); } } public class SemaphoreSlimGuard : ScopeGuard { private readonly SemaphoreSlim _semaphore; private SemaphoreSlimGuard(SemaphoreSlim semaphore) { _semaphore = semaphore; } public static SemaphoreSlimGuard Create(SemaphoreSlim semaphore) { semaphore.Wait(); return new SemaphoreSlimGuard(semaphore); } public static async Task CreateAsync(SemaphoreSlim semaphore) { await semaphore.WaitAsync(); return new SemaphoreSlimGuard(semaphore); } protected override void EndScope() { _semaphore.Release(); } } public enum Never { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct Unit { public static Unit Instance => default(Unit); } public static class Utils { [SpecialName] public sealed class $B121A579F9C62187B81C2CC9A1D05898 { [SpecialName] public static class $9A581DD593152A881C02B7BBD540726C { } [ExtensionMarker("$9A581DD593152A881C02B7BBD540726C")] public Task CreateScopeAsync() { throw new NotSupportedException(); } [ExtensionMarker("$9A581DD593152A881C02B7BBD540726C")] public IDisposable CreateScope() { throw new NotSupportedException(); } [ExtensionMarker("$9A581DD593152A881C02B7BBD540726C")] public void EnterScope(Action action) { throw new NotSupportedException(); } [ExtensionMarker("$9A581DD593152A881C02B7BBD540726C")] public T EnterScope(Func func) { throw new NotSupportedException(); } [ExtensionMarker("$9A581DD593152A881C02B7BBD540726C")] public Task EnterScopeAsync(Action action) { throw new NotSupportedException(); } [ExtensionMarker("$9A581DD593152A881C02B7BBD540726C")] public Task EnterScopeAsync(Func func) { throw new NotSupportedException(); } [ExtensionMarker("$9A581DD593152A881C02B7BBD540726C")] public Task EnterScopeAsync(Func action) { throw new NotSupportedException(); } [ExtensionMarker("$9A581DD593152A881C02B7BBD540726C")] public Task EnterScopeAsync(Func> action) { throw new NotSupportedException(); } } [SpecialName] public sealed class $6A188C7A03CB85FC44DF3FD030EA0664 { [SpecialName] public static class $438D5453E0DFEE874C985587A0074FB9 { } [ExtensionMarker("$438D5453E0DFEE874C985587A0074FB9")] [DoesNotReturn] public Never Rethrow() { throw new NotSupportedException(); } [ExtensionMarker("$438D5453E0DFEE874C985587A0074FB9")] [DoesNotReturn] public T Rethrow() { throw new NotSupportedException(); } } public static Option GetOptional(this IDictionary dict, TKey key) { TValue value; return dict.TryGetValue(key, out value) ? Option.Some(value) : Option.None; } private static Result SafeInvokeInternal(T? self, Action invoke) where T : Delegate { if (self == null) { return Result.Success(Unit.Instance); } Lazy> lazy = new Lazy>(() => new List()); foreach (T item2 in self.GetInvocationList().Cast()) { try { invoke(item2); } catch (Exception item) { lazy.Value.Add(item); } } if (lazy.IsValueCreated) { return Result.Error(new AggregateException("One or more exceptions occured while safe-invoking the delegate.", lazy.Value)); } return Result.Success(Unit.Instance); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Result SafeInvoke(this Action? self) { return SafeInvokeInternal(self, delegate(Action a) { a(); }); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Result SafeInvoke(this Action? self, T arg) { return SafeInvokeInternal(self, delegate(Action a) { a(arg); }); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Result SafeInvoke(this Action? self, T1 arg1, T2 arg2) { return SafeInvokeInternal(self, delegate(Action a) { a(arg1, arg2); }); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static async Task CreateScopeAsync(this SemaphoreSlim semaphore) { return await SemaphoreSlimGuard.CreateAsync(semaphore); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IDisposable CreateScope(this SemaphoreSlim semaphore) { return SemaphoreSlimGuard.Create(semaphore); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void EnterScope(this SemaphoreSlim semaphore, Action action) { using (semaphore.CreateScope()) { action(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T EnterScope(this SemaphoreSlim semaphore, Func func) { using (semaphore.CreateScope()) { return func(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static async Task EnterScopeAsync(this SemaphoreSlim semaphore, Action action) { using (await semaphore.CreateScopeAsync()) { action(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static async Task EnterScopeAsync(this SemaphoreSlim semaphore, Func func) where T : notnull { using (await semaphore.CreateScopeAsync()) { return func(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static async Task EnterScopeAsync(this SemaphoreSlim semaphore, Func action) { using (await semaphore.CreateScopeAsync()) { await action(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static async Task EnterScopeAsync(this SemaphoreSlim semaphore, Func> action) where T : notnull { using (await semaphore.CreateScopeAsync()) { return await action(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] [DoesNotReturn] public static Never Rethrow(this Exception ex) { return ex.Rethrow(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [DoesNotReturn] public static T Rethrow(this Exception ex) { ExceptionDispatchInfo.Capture(ex).Throw(); return default(T); } } } namespace ue.Peak.TcnPatch.API { public interface ITcnPatch { void RegisterLocalizationKey(string key, string unlocalized); } public class TcnPatch : ITcnPatch { public static ITcnPatch Instance => InternalInstance; internal static TcnPatch InternalInstance { get; } = new TcnPatch(); public void RegisterLocalizationKey(string key, string unlocalized) { Plugin.KeyToUnlocalizedLookup[key] = unlocalized; } } } namespace ue.Peak.TcnPatch.Adapters { public static class MoreAscentsSupport { private class ReflectionFailedError(Action showWarning) { public void ShowWarning() { showWarning(); } } private static readonly Lazy _gimmickHandlerType = new Lazy(() => Type.GetType("MoreAscents.AscentGimmickHandler")); private static bool _addedSupport; public static bool IsMoreAscentsInstalled => _gimmickHandlerType.Value != null; private static Type GimmickHandlerType => _gimmickHandlerType.Value; public static void RegisterLocalizations() { //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Expected O, but got Unknown if (!IsMoreAscentsInstalled) { return; } TypedFieldInfo typedFieldInfo = AccessTools.Field(GimmickHandlerType, "HasInitialized").AsTyped(); if (typedFieldInfo == null) { return; } if (!typedFieldInfo.GetStaticValue()) { Plugin.Logger.LogWarning((object)"MoreAscents 還沒初始化!?"); } else { if (_addedSupport) { return; } _addedSupport = true; Plugin.Logger.LogInfo((object)"偵測到 MoreAscents!正在為 MoreAscents 提供繁中支援..."); try { if (!FindType("MoreAscents.AscentGimmick").IfError(delegate(ReflectionFailedError e) { e.ShowWarning(); }).TryUnwrap(out var value) || !FindMethod(value, "GetTitle").IfError(delegate(ReflectionFailedError e) { e.ShowWarning(); }).TryUnwrap(out var gimmickGetTitleMethod) || !FindMethod(value, "GetDescription").IfError(delegate(ReflectionFailedError e) { e.ShowWarning(); }).TryUnwrap(out var gimmickGetDescriptionMethod) || !FindMethod(value, "GetTitleReward").IfError(delegate(ReflectionFailedError e) { e.ShowWarning(); }).TryUnwrap(out var value2) || !FindField(GimmickHandlerType, "gimmicks").IfError(delegate(ReflectionFailedError e) { e.ShowWarning(); }).TryUnwrap(out var value3)) { return; } IReadOnlyList readOnlyList = (IReadOnlyList)value3.GetValue(null); MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(AscentData), "Instance"); AscentData val = (AscentData)methodInfo.Invoke(null, Array.Empty()); List ascents = val.ascents; foreach (object gimmick in readOnlyList) { AscentInstanceData val2 = ((IEnumerable)ascents).FirstOrDefault((Func)((AscentInstanceData d) => d.title == (string)gimmickGetTitleMethod.Invoke(gimmick, Array.Empty()) && d.description == (string)gimmickGetDescriptionMethod.Invoke(gimmick, Array.Empty()))); if (val2 != null) { string text = (val2.title = "MoreAscent." + gimmick.GetType().Name); val2.titleReward = text + ".Reward"; ue.Peak.TcnPatch.API.TcnPatch.Instance.RegisterLocalizationKey(text ?? "", (string)gimmickGetTitleMethod.Invoke(gimmick, Array.Empty())); ue.Peak.TcnPatch.API.TcnPatch.Instance.RegisterLocalizationKey(LocalizedText.GetDescriptionIndex(text), (string)gimmickGetDescriptionMethod.Invoke(gimmick, Array.Empty())); ue.Peak.TcnPatch.API.TcnPatch.Instance.RegisterLocalizationKey(text + ".Reward", (string)value2.Invoke(gimmick, Array.Empty())); } } } catch (Exception ex) { Plugin.Logger.LogError((object)"無法為 MoreAscents 提供繁中支援... x_x"); Plugin.Logger.LogError((object)ex); } } static Result FindField(Type type, string fieldName) { FieldInfo fieldInfo = AccessTools.Field(type, fieldName); if (fieldInfo != null) { return Result.Success(fieldInfo); } return Result.Error(new ReflectionFailedError(delegate { Plugin.Logger.LogWarning((object)("無法找到 " + type.Name + "." + fieldName + " 欄位!無法為 MoreAscents 提供繁中支援... x_x")); })); } static Result FindMethod(Type type, string methodName) { MethodInfo methodInfo2 = AccessTools.Method(type, methodName, (Type[])null, (Type[])null); if (methodInfo2 != null) { return Result.Success(methodInfo2); } return Result.Error(new ReflectionFailedError(delegate { Plugin.Logger.LogWarning((object)("無法找到 " + type.Name + "." + methodName + "() 方法!無法為 MoreAscents 提供繁中支援... x_x")); })); } static Result FindType(string typeName) { Type type = AccessTools.TypeByName(typeName); if (type != null) { return Result.Success(type); } return Result.Error(new ReflectionFailedError(delegate { Plugin.Logger.LogWarning((object)("無法找到 " + typeName + " 類型!無法為 MoreAscents 提供繁中支援... x_x")); })); } } } }