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+5dcfcb8c5c220f408d4dfd47b6cc84089b3fc21a")]
[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 <Name>k__BackingField;
public string Name => <Name>k__BackingField;
public ExtensionMarkerAttribute(string name)
{
<Name>k__BackingField = name;
}
}
}
namespace ue.Peak.TcnPatch
{
public class FileIO
{
private readonly string _path;
public bool Exists => File.Exists(_path);
public FileIO(string path)
{
_path = path;
}
public FileStream Open(FileMode mode, FileAccess access)
{
return new FileStream(_path, mode, access);
}
public FileStream Open(FileMode mode, FileAccess access, FileShare share)
{
return new FileStream(_path, mode, access, share);
}
}
[BepInPlugin("ue.Peak.TcnPatch", "ue.Peak.TcnPatch", "2.2.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.2.0";
private static FileSystemWatcher _watcher;
public const string TcnTranslationFileName = "TcnTranslations.json";
private static Mutex<FileIO> _fileIO;
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; }
public static TranslationStorage TranslationStorage { get; } = new TranslationStorage();
private void Awake()
{
Instance = this;
ModConfig = new PluginConfig(((BaseUnityPlugin)this).Config);
ModConfig.IgnoreAllTranslations.Value = false;
ModConfig.IgnoreAllTranslations.SettingChanged += delegate
{
LocalizedText.RefreshAllText();
};
ModConfig.ExportIgnoreList.SettingChanged += delegate
{
TranslationFile file = CurrentTranslationFile.CreateCopy();
foreach (string item in LocalizedTextPatch.VanillaLocalizationKeys.Where((string x) => !Enumerable.Contains(file.Translations.Keys, x)))
{
file.IgnoredTranslations.Add(item);
}
string text = Path.Combine(Paths.ConfigPath, "ue.Peak.TcnPatch");
if (!Directory.Exists(text))
{
Directory.CreateDirectory(text);
}
string path = Path.Combine(text, "_IgnoredTcnTranslations.json");
string contents = JsonConvert.SerializeObject((object)file, (Formatting)1);
File.WriteAllText(path, contents);
};
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 static FileSystemWatcher InitializeFileWatcher(string dir)
{
FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(dir, "*.json");
fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
fileSystemWatcher.Changed += delegate(object _, FileSystemEventArgs args)
{
if (args.Name == "TcnTranslations.json")
{
Logger.LogInfo((object)"正在更新遊戲內繁體中文翻譯資料...");
UpdateMainTable();
}
};
fileSystemWatcher.EnableRaisingEvents = true;
return fileSystemWatcher;
}
private void Start()
{
string text = Path.Combine(Paths.ConfigPath, "ue.Peak.TcnPatch");
Directory.CreateDirectory(text);
_fileIO = new Mutex<FileIO>(new FileIO(Path.Combine(text, "TcnTranslations.json")));
if (!ModConfig.DownloadFromRemote.Value)
{
UpdateMainTable();
}
else
{
Task.Run(async delegate
{
if (!(await DownloadTranslationsAsync()).IsSuccess)
{
UpdateMainTable();
}
});
}
_watcher = InitializeFileWatcher(text);
}
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()
{
_watcher?.Dispose();
Harmony harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
_fileIO.Dispose();
}
private static async Task SaveTranslationsAsync(string content)
{
using IMutexGuard<FileIO> guard = _fileIO.AcquireExclusive();
await using FileStream targetStream = guard.Value.Open(FileMode.Create, FileAccess.Write);
await using StreamWriter writer = new StreamWriter(targetStream);
await writer.WriteAsync(content);
}
private async Task<Result<Unit, Exception>> 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"));
}
private static void UpdateMainTable()
{
ControlFlow<Unit, Unit> controlFlow = TryReadFromJson("TcnTranslations.json", (Func<JObject>)(() => new JObject())).SelectMany(TranslationFile.TryDeserialize).Select((Func<TranslationFile, ControlFlow<Unit, Unit>>)delegate(TranslationFile f)
{
CurrentTranslationFile = f;
return ControlFlow.Continue();
}).SelectError((Func<Exception, ControlFlow<Unit, Unit>>)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)
{
if (CurrentTranslationFile.Authors.Count > 0)
{
Logger.LogInfo((object)("翻譯資料作者:" + string.Join("、", CurrentTranslationFile.Authors)));
}
else
{
Logger.LogInfo((object)"翻譯資料作者:未知");
}
TranslationStorage.ImportFrom(CurrentTranslationFile);
}
}
private static Result<T, Exception> TryReadFromJson<T>(string fileName, Func<T> defaultContent) where T : class
{
Result<Unit, Exception> result = Result.Catch(delegate
{
using IMutexGuard<FileIO> mutexGuard = _fileIO.AcquireExclusive();
if (!mutexGuard.Value.Exists)
{
using FileStream stream = mutexGuard.Value.Open(FileMode.CreateNew, FileAccess.Write);
using StreamWriter streamWriter = new StreamWriter(stream);
string value = JsonConvert.SerializeObject((object)defaultContent());
streamWriter.Write(value);
}
return Unit.Instance;
});
return result.IfError(delegate(Exception e)
{
Logger.LogError((object)("無法初始化 JSON 設定:" + fileName));
Logger.LogError((object)e);
}).SelectMany((Unit _) => DeserializeFromPath());
Result<T, Exception> DeserializeFromPath()
{
return Result.Catch(delegate
{
using IMutexGuard<FileIO> mutexGuard = _fileIO.AcquireExclusive();
using FileStream stream = mutexGuard.Value.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader streamReader = new StreamReader(stream);
return JsonConvert.DeserializeObject<T>(streamReader.ReadToEnd());
}).IfError(delegate(Exception e)
{
Logger.LogError((object)("無法讀取 JSON 設定:" + fileName));
Logger.LogError((object)e);
});
}
}
}
public class PluginConfig
{
public ConfigEntry<bool> DownloadFromRemote { get; } = config.Bind<bool>("Update", "DownloadFromRemote", true, "是否每次啟動都要從遠端下載最新的翻譯資料? (true: 是, false: 否)");
public ConfigEntry<string> DownloadUrl { get; } = config.Bind<string>("Update", "DownloadUrl", "https://raw.githubusercontent.com/Yuieii/ue.Peak.TcnPatch/refs/heads/master/TcnTranslations.json", "翻譯資料的 URL");
public ConfigEntry<DownloadFailureHandling> DownloadFailureHandling { get; } = config.Bind<DownloadFailureHandling>("Update", "DownloadFailureHandling", ue.Peak.TcnPatch.DownloadFailureHandling.UseDefaultRemote, "當設定的 URL 下載失敗時如何處理");
public ConfigEntry<Language> AutoDumpLanguage { get; } = config.Bind<Language>("Debug", "AutoDumpLanguage", (Language)9, "自動輸出官方參考翻譯時選擇的原始語言");
public ConfigEntry<bool> EnableAutoDumpLanguage { get; } = config.Bind<bool>("Debug", "EnableAutoDumpLanguage", true, string.Join('\n', "是否自動輸出遊戲內的翻譯表以供參考", " (有 ModConfig 的話即時更改可以重新輸出)"));
public ConfigEntry<bool> WarnUnknownTranslationKeys { get; } = config.Bind<bool>("Debug", "WarnUnknownTranslationKeys", false, "有未知的非附加翻譯key時是否觸發警告");
public ConfigEntry<bool> WarnMissingAdditionalKeys { get; } = config.Bind<bool>("Debug", "WarnMissingAdditionalKeys", false, "有缺失的附加翻譯key時是否觸發警告");
public ConfigEntry<bool> IgnoreAllTranslations { get; } = config.Bind<bool>("Debug", "IgnoreAllTranslations", false, "是否忽略翻譯資料? (true: 是, false: 否)");
public ConfigEntry<bool> ExportIgnoreList { get; } = config.Bind<bool>("Debug", "ExportIgnoreList", false, "本設定被更改的當下輸出一份忽略所有未翻譯文本的翻譯資料");
public ConfigEntry<bool> ShowPatchCredit { get; } = config.Bind<bool>("Patch", "ShowPatchCredit", true, "在主畫面版本文字下方顯示本模組作者?");
public ConfigEntry<bool> ShowTranslatorCredit { get; } = config.Bind<bool>("Patch", "ShowTranslatorCredit", true, "在主畫面版本文字下方顯示翻譯資料的作者?");
public ConfigEntry<bool> ShowModVersionInPatchCredit { get; } = config.Bind<bool>("Patch", "ShowModVersionInPatchCredit", true, "在主畫面版本文字後面顯示這個模組的版本? (v2.2.0)");
public PluginConfig(ConfigFile config)
{
}
}
public enum DownloadFailureHandling
{
UseLocal,
UseDefaultRemote
}
internal static class ReflectionExtensions
{
[SpecialName]
public sealed class <G>$8048A6C8BE30A622530249B904B537EB<$T0>
{
[SpecialName]
public static class <M>$B99A874B2F9892B4D9EADD5CC9D40768
{
}
[ExtensionMarker("<M>$B99A874B2F9892B4D9EADD5CC9D40768")]
public TValue GetReflectionFieldValue<TValue>(TypedFieldInfo<$T0, TValue> fieldInfo)
{
throw new NotSupportedException();
}
[ExtensionMarker("<M>$B99A874B2F9892B4D9EADD5CC9D40768")]
public void SetReflectionFieldValue<TValue>(TypedFieldInfo<$T0, TValue> fieldInfo, TValue value)
{
throw new NotSupportedException();
}
}
[SpecialName]
public sealed class <G>$C0F9324A7FB6D595E082877B54F807CC
{
[SpecialName]
public static class <M>$4776623D3010553C7D2AB93E76A250FB
{
}
[ExtensionMarker("<M>$4776623D3010553C7D2AB93E76A250FB")]
[CanBeNull]
public TypedFieldInfo<TOwner, TValue> AsTyped<TOwner, TValue>()
{
throw new NotSupportedException();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TValue GetReflectionFieldValue<TOwner, TValue>(this TOwner owner, TypedFieldInfo<TOwner, TValue> fieldInfo)
{
return (TValue)fieldInfo.FieldInfo.GetValue(owner);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetReflectionFieldValue<TOwner, TValue>(this TOwner owner, TypedFieldInfo<TOwner, TValue> fieldInfo, TValue value)
{
fieldInfo.FieldInfo.SetValue(owner, value);
}
[CanBeNull]
public static TypedFieldInfo<TOwner, TValue> AsTyped<TOwner, TValue>([CanBeNull] this FieldInfo field)
{
return field;
}
}
public static class Refl
{
public static class LoadingScreenAnimation
{
public static TypedFieldInfo<LoadingScreenAnimation, string> LoadingString { get; } = AccessTools.Field(typeof(LoadingScreenAnimation), "loadingString");
public static TypedFieldInfo<LoadingScreenAnimation, float> DefaultLoadingStringLength { get; } = AccessTools.Field(typeof(LoadingScreenAnimation), "defaultLoadingStringLength");
}
public static class LocalizedText
{
public static TypedFieldInfo<LocalizedText, Language> CurrentLanguage { get; } = AccessTools.Field(typeof(LocalizedText), "CURRENT_LANGUAGE");
}
public static class VersionString
{
public static TypedFieldInfo<VersionString, TextMeshProUGUI> Text { get; } = AccessTools.Field(typeof(VersionString), "m_text");
}
}
public class TypedFieldInfo<TOwner, TValue>(FieldInfo fieldInfo)
{
public FieldInfo FieldInfo { get; } = fieldInfo;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator TypedFieldInfo<TOwner, TValue>(FieldInfo fieldInfo)
{
return (fieldInfo == null) ? null : new TypedFieldInfo<TOwner, TValue>(fieldInfo);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator FieldInfo(TypedFieldInfo<TOwner, TValue> 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 const string IgnoredTranslationEntriesKey = "IgnoredTranslations";
[UsedImplicitly]
private int FormatVersion { get; } = 0;
public List<string> Authors { get; } = new List<string>();
public Dictionary<string, string> Translations { get; } = new Dictionary<string, string>();
public Dictionary<string, string> AdditionalTranslations { get; } = new Dictionary<string, string>();
public HashSet<string> IgnoredTranslations { get; } = new HashSet<string>();
public TranslationFile CreateCopy()
{
TranslationFile translationFile = new TranslationFile();
foreach (string author in Authors)
{
translationFile.Authors.Add(author);
}
string value;
string key;
foreach (KeyValuePair<string, string> translation in Translations)
{
translation.Deconstruct(out value, out key);
string key2 = value;
string value2 = key;
translationFile.Translations[key2] = value2;
}
foreach (KeyValuePair<string, string> additionalTranslation in AdditionalTranslations)
{
additionalTranslation.Deconstruct(out key, out value);
string key3 = key;
string value3 = value;
translationFile.AdditionalTranslations[key3] = value3;
}
foreach (string ignoredTranslation in IgnoredTranslations)
{
translationFile.IgnoredTranslations.Add(ignoredTranslation);
}
return translationFile;
}
public static Result<TranslationFile, Exception> 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)
//IL_0150: Unknown result type (might be due to invalid IL or missing references)
//IL_0156: Invalid comparison between Unknown and I4
string[] source = new string[2] { "FormatVersion", "Translations" };
if (!((IEnumerable<string>)source).All((Func<string, bool>)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<int>((IEnumerable<JToken>)val);
if (num > 0)
{
Plugin.Logger.LogWarning((object)"正在讀取過新版本的翻譯資料!可能會無法正確讀取。");
}
((IDictionary<string, JToken>)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<string>((IEnumerable<JToken>)item));
}
return;
}
JValue val7 = (JValue)(object)((authorsToken is JValue) ? authorsToken : null);
if (val7 != null)
{
result.Authors.Add(Extensions.Value<string>((IEnumerable<JToken>)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 (text2, val5) in val3)
{
if (val5 == null || (int)val5.Type != 8)
{
Plugin.Logger.LogWarning((object)("無效的單一翻譯資料:\"" + text2 + "\" (非字串)"));
}
else
{
result.Translations[text2] = Extensions.Value<string>((IEnumerable<JToken>)val5);
}
}
((IDictionary<string, JToken>)obj).GetOptional("AdditionalTranslations").IfSome(delegate(JToken entries)
{
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Invalid comparison between Unknown and I4
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<string> list = new List<string>();
foreach (var (text4, val8) in val6)
{
if (val8 == null || (int)val8.Type != 8)
{
Plugin.Logger.LogWarning((object)("無效的單一附加翻譯資料:\"" + text4 + "\" (非字串)"));
}
else if (list.Contains<string>(text4, StringComparer.InvariantCultureIgnoreCase))
{
Plugin.Logger.LogWarning((object)("翻譯資料出現已註冊過的附加翻譯key「" + text4 + "」!新的同名翻譯將會被忽略。"));
}
else
{
list.Add(text4);
result.AdditionalTranslations[text4] = Extensions.Value<string>((IEnumerable<JToken>)val8);
}
}
});
((IDictionary<string, JToken>)obj).GetOptional("IgnoredTranslations").IfSome(delegate(JToken entries)
{
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Invalid comparison between Unknown and I4
JArray val6 = (JArray)(object)((entries is JArray) ? entries : null);
if (val6 == null)
{
throw new TranslationParseException($"Ignored translation entries must be an array, found {entries.Type}", "無效的忽略翻譯資料! (IgnoredTranslations)");
}
foreach (JToken item2 in val6)
{
if ((int)item2.Type == 8)
{
result.IgnoredTranslations.Add(Extensions.Value<string>((IEnumerable<JToken>)item2));
}
}
});
return result;
}
private static TranslationFile InternalDeserializeFromLegacy(JObject obj)
{
TranslationFile translationFile = new TranslationFile();
foreach (var (key, val2) in obj)
{
translationFile.Translations[key] = Extensions.Value<string>((IEnumerable<JToken>)val2);
}
return translationFile;
}
}
public class TranslationStorage
{
private Dictionary<string, string> TranslationsLookup { get; } = new Dictionary<string, string>();
private Dictionary<string, string> AdditionalTranslationsLookup { get; } = new Dictionary<string, string>();
private Dictionary<string, string> KeyToUnlocalizedLookup { get; } = new Dictionary<string, string>();
public void InvalidateTranslations()
{
TranslationsLookup.Clear();
AdditionalTranslationsLookup.Clear();
}
public void RegisterVanillaLocalizationKey(string key, string translation)
{
string text = key.ToUpperInvariant();
if (TranslationsLookup.ContainsKey(text))
{
Plugin.Logger.LogInfo((object)("發現重複的翻譯key:「" + key + "」!已存在大寫的同名key!"));
return;
}
Dictionary<string, List<string>> mainTable = LocalizedText.mainTable;
if (!mainTable.ContainsKey(text) && Plugin.ModConfig.WarnUnknownTranslationKeys.Value)
{
Plugin.Logger.LogWarning((object)("正在使用未知的翻譯key:「" + text + "」!"));
}
TranslationsLookup[text] = translation;
}
public void RegisterAdditionalLocalizationKey(string key, string translation)
{
AdditionalTranslationsLookup[key] = translation;
}
internal void RegisterExternalLocalizationKey(string key, string unlocalized)
{
KeyToUnlocalizedLookup[key] = unlocalized;
}
public void VisitExternalLocalizationKeys(Action<string, string> handler)
{
foreach (var (arg, arg2) in KeyToUnlocalizedLookup)
{
handler(arg, arg2);
}
}
public void ImportFrom(TranslationFile file)
{
Dictionary<string, List<string>> mainTable = LocalizedText.mainTable;
HashSet<string> hashSet = mainTable.Keys.ToHashSet();
InvalidateTranslations();
string value;
string key;
foreach (KeyValuePair<string, string> translation3 in file.Translations)
{
translation3.Deconstruct(out value, out key);
string text = value;
string translation = key;
RegisterVanillaLocalizationKey(text, translation);
hashSet.Remove(text.ToUpperInvariant());
}
foreach (KeyValuePair<string, string> additionalTranslation in file.AdditionalTranslations)
{
additionalTranslation.Deconstruct(out key, out value);
string text2 = key;
string translation2 = value;
RegisterAdditionalLocalizationKey(text2, translation2);
hashSet.Remove(text2);
}
foreach (string ignoredTranslation in file.IgnoredTranslations)
{
hashSet.Remove(ignoredTranslation.ToUpperInvariant());
hashSet.Remove(ignoredTranslation);
}
HashSet<string> vanillaLocalizationKeys = LocalizedTextPatch.VanillaLocalizationKeys;
foreach (string item in hashSet)
{
if (vanillaLocalizationKeys.Contains(item))
{
Plugin.Logger.LogWarning((object)("缺少「" + item + "」翻譯key,請更新翻譯資料!"));
}
else if (Plugin.ModConfig.WarnMissingAdditionalKeys.Value)
{
Plugin.Logger.LogWarning((object)("*附加翻譯* 缺少「" + item + "」翻譯key!"));
}
}
LocalizedText.RefreshAllText();
}
public Option<string> GetVanilla(string id)
{
if (Plugin.ModConfig.IgnoreAllTranslations.Value)
{
return Option<string>.None;
}
return TranslationsLookup.GetOptional(id.ToUpperInvariant());
}
public Option<string> GetRegistered(string id, Language? language)
{
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Invalid comparison between Unknown and I4
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
if (Plugin.ModConfig.IgnoreAllTranslations.Value)
{
return Option<string>.None;
}
Language valueOrDefault = language.GetValueOrDefault();
if (!language.HasValue)
{
valueOrDefault = LocalizedText.CURRENT_LANGUAGE;
language = valueOrDefault;
}
return (((int)language.GetValueOrDefault() == 10) ? AdditionalTranslationsLookup.GetOptional(id) : Option<string>.None).OrGet(() => KeyToUnlocalizedLookup.GetOptional(id));
}
}
public static class Utils
{
public static Option<T> ToOptionUnity<T>(this T obj) where T : Object
{
return ((Object)(object)obj == (Object)null) ? ((Option<T>)Option.None) : Option.Some(obj);
}
public static Task WaitForFramesAsync(int frames)
{
TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
((MonoBehaviour)Plugin.Instance).StartCoroutine(DelayFramesCoroutine(taskCompletionSource, frames, () => true));
return taskCompletionSource.Task;
}
public static async Task WaitForNextFrameAsync()
{
await WaitForFramesAsync(1);
}
private static IEnumerator DelayFramesCoroutine<T>(TaskCompletionSource<T> source, int frames, Func<T> resultGetter)
{
for (int i = 0; i < frames; i++)
{
yield return (object)new WaitForEndOfFrame();
}
source.SetResult(resultGetter());
}
}
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)
{
return;
}
TypedFieldInfo<VersionString, TextMeshProUGUI> text = Refl.VersionString.Text;
if (text == null)
{
return;
}
TextMeshProUGUI reflectionFieldValue = instance.GetReflectionFieldValue(text);
if (_originalText == null)
{
_originalText = ((TMP_Text)reflectionFieldValue).text;
}
((TMP_Text)reflectionFieldValue).text = _originalText;
if (!Plugin.ModConfig.ShowPatchCredit.Value)
{
return;
}
string text2 = "繁中翻譯by: " + string.Join("、", Plugin.CurrentTranslationFile.Authors);
string text3 = (Plugin.ModConfig.ShowModVersionInPatchCredit.Value ? "繁中支援v2.2.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;
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 + "<br><size=70%><alpha=#88>ue.Peak.TcnPatch v2.2.0<alpha=#FF></size>";
return;
}
((TMP_Text)reflectionFieldValue).text = ((TMP_Text)reflectionFieldValue).text + "<br><size=70%>" + text3 + "</size>";
if (flag)
{
((TMP_Text)reflectionFieldValue).text = ((TMP_Text)reflectionFieldValue).text + "<br><size=70%>" + text2 + "</size>";
}
}
}
}
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<Transform>().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<LocalizedText>();
if ((Object)(object)val == (Object)null)
{
val = ((Component)transform).gameObject.AddComponent<LocalizedText>();
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<CanvasGroup>().SelectMany((CanvasGroup g) => ((Component)g).transform.Find("PromptText").ToOptionUnity<Transform>()).SelectMany((Transform c) => ((Component)c).GetComponent<LocalizedText>().ToOptionUnity<LocalizedText>())
.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<CodeInstruction> PatchLoadingAnimationSwitch(IEnumerable<CodeInstruction> 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<CodeInstruction> 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<CodeInstruction> list2 = new List<CodeInstruction>();
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<CodeInstruction> 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<string> Authors { get; } = new List<string>();
public Dictionary<string, string> Translations { get; }
public Dictionary<string, string> AdditionalTranslations { get; }
public AutoDumpRecord(Dictionary<string, string> translations, Dictionary<string, string> additionalTranslations)
{
Translations = translations;
AdditionalTranslations = additionalTranslations;
base..ctor();
}
}
private static bool _autoDumpedMainTable;
private static readonly Lazy<Unit> _readIfTooLate = new Lazy<Unit>(delegate
{
if (_earlyEnough)
{
return Unit.Instance;
}
Plugin.Logger.LogWarning((object)"正在重新讀取 PEAK 官方翻譯表...");
Dictionary<string, List<string>>.KeyCollection keyCollection = Resources.Load<TextAsset>("Localization/SerializedTermsData").ToOptionUnity<TextAsset>().Select(delegate(TextAsset t)
{
string text = t.text;
return JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(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<int> _languageCountLazy = new Lazy<int>(() => Enum.GetNames(typeof(Language)).Length);
private static bool _earlyEnough;
public static HashSet<string> VanillaLocalizationKeys { get; } = new HashSet<string>();
[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_002c: 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.TranslationStorage.GetRegistered(id, language)
where !string.IsNullOrEmpty(result.Trim())
select result).OrGet(() => Plugin.TranslationStorage.GetRegistered(id.ToUpperInvariant(), language))
where !string.IsNullOrEmpty(result.Trim())
select result).OrGet(() => ((int)language != 10) ? ((Option<string>)Option.None) : Plugin.TranslationStorage.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<string> FixLanguageList(List<string> 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<string, string> translations = LocalizedText.mainTable.Where((KeyValuePair<string, List<string>> p) => VanillaLocalizationKeys.Contains(p.Key)).ToDictionary((KeyValuePair<string, List<string>> p) => p.Key, (KeyValuePair<string, List<string>> p) => FixLanguageList(p.Value)[(int)language]);
Dictionary<string, string> additionalTable = LocalizedText.mainTable.Where((KeyValuePair<string, List<string>> p) => !VanillaLocalizationKeys.Contains(p.Key)).ToDictionary((KeyValuePair<string, List<string>> p) => p.Key, (KeyValuePair<string, List<string>> p) => FixLanguageList(p.Value)[(int)language]);
Plugin.TranslationStorage.VisitExternalLocalizationKeys(delegate(string key, string value)
{
additionalTable[key] = value;
});
string contents = JsonConvert.SerializeObject((object)new AutoDumpRecord(translations, additionalTable), (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<Transform>().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<LocalizedText>();
if ((Object)(object)val == (Object)null)
{
val = ((Component)transform).gameObject.AddComponent<LocalizedText>();
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<RectTransform>();
Vector2 anchoredPosition = component.anchoredPosition;
anchoredPosition.y -= 10f;
component.anchoredPosition = anchoredPosition;
TypedFieldInfo<VersionString, TextMeshProUGUI> 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>(T value)
{
internal T Value { get; } = value;
public ControlFlow<T, TContinue> FulfillContinueType<TContinue>()
{
return ControlFlow<T, TContinue>.Break(Value);
}
}
public class PartialWithContinue<T>(T value)
{
internal T Value { get; } = value;
public ControlFlow<TBreak, T> FulfillBreakType<TBreak>()
{
return ControlFlow<TBreak, T>.Continue(Value);
}
}
public static PartialWithBreak<T> Break<T>(T value)
{
return new PartialWithBreak<T>(value);
}
public static PartialWithBreak<Unit> Break()
{
return new PartialWithBreak<Unit>(Unit.Instance);
}
public static PartialWithContinue<T> Continue<T>(T value)
{
return new PartialWithContinue<T>(value);
}
public static PartialWithContinue<Unit> Continue()
{
return new PartialWithContinue<Unit>(Unit.Instance);
}
}
public abstract class ControlFlow<TBreak, TContinue>
{
private class BreakBranch : ControlFlow<TBreak, TContinue>
{
private readonly TBreak _value;
public override bool IsBreak => true;
public override bool IsContinue => false;
public BreakBranch(TBreak value)
{
_value = value;
}
public override Option<TContinue> GetContinueValue()
{
return Option.None;
}
public override Result<TContinue, TBreak> ToContinueSuccess()
{
return Result.Error(_value);
}
public override ControlFlow<TBreak, TResult> SelectContinue<TResult>(Func<TContinue, TResult> transform)
{
return ControlFlow.Break(_value);
}
public override Option<TBreak> GetBreakValue()
{
return Option.Some(_value);
}
public override Result<TBreak, TContinue> ToBreakSuccess()
{
return Result.Success(_value);
}
public override ControlFlow<TResult, TContinue> SelectBreak<TResult>(Func<TBreak, TResult> transform)
{
return ControlFlow.Break(transform(_value));
}
}
private class ContinueBranch : ControlFlow<TBreak, TContinue>
{
private readonly TContinue _value;
public override bool IsBreak => false;
public override bool IsContinue => true;
public ContinueBranch(TContinue value)
{
_value = value;
}
public override Option<TContinue> GetContinueValue()
{
return Option.Some(_value);
}
public override Result<TContinue, TBreak> ToContinueSuccess()
{
return Result.Success(_value);
}
public override ControlFlow<TBreak, TResult> SelectContinue<TResult>(Func<TContinue, TResult> transform)
{
return ControlFlow.Continue(transform(_value));
}
public override Option<TBreak> GetBreakValue()
{
return Option.None;
}
public override Result<TBreak, TContinue> ToBreakSuccess()
{
return Result.Error(_value);
}
public override ControlFlow<TResult, TContinue> SelectBreak<TResult>(Func<TBreak, TResult> transform)
{
return ControlFlow.Continue(_value);
}
}
public abstract bool IsBreak { get; }
public abstract bool IsContinue { get; }
public static ControlFlow<TBreak, TContinue> Continue(TContinue value)
{
return new ContinueBranch(value);
}
public static ControlFlow<TBreak, TContinue> Break(TBreak value)
{
return new BreakBranch(value);
}
public static implicit operator ControlFlow<TBreak, TContinue>(ControlFlow.PartialWithContinue<TContinue> part)
{
return new ContinueBranch(part.Value);
}
public static implicit operator ControlFlow<TBreak, TContinue>(ControlFlow.PartialWithBreak<TBreak> part)
{
return new BreakBranch(part.Value);
}
public abstract Option<TContinue> GetContinueValue();
public abstract Result<TContinue, TBreak> ToContinueSuccess();
public abstract ControlFlow<TBreak, TResult> SelectContinue<TResult>(Func<TContinue, TResult> transform);
public abstract Option<TBreak> GetBreakValue();
public abstract Result<TBreak, TContinue> ToBreakSuccess();
public abstract ControlFlow<TResult, TContinue> SelectBreak<TResult>(Func<TBreak, TResult> transform);
}
public interface IMutex : IDisposable
{
}
public interface IImmutableMutex<out T> : IMutex, IDisposable
{
IMutexGuard<T> AcquireShared();
}
public interface IMutableMutex<out T> : IMutex, IDisposable
{
IMutexGuard<T> AcquireExclusive();
}
public interface IMutex<out TImmutable, out TMutable> : IImmutableMutex<TImmutable>, IMutex, IDisposable, IMutableMutex<TMutable>
{
IUpgradableMutexGuard<TImmutable, TMutable> AcquireUpgradable();
}
public class Mutex<T> : Mutex<T, T, T>
{
public Mutex(T value)
: base(value)
{
}
public Mutex(Func<T> factory)
: base(factory)
{
}
}
public class Mutex<TValue, TImmutable, TMutable> : AccessViewMutex<OpaqueView<TValue, TImmutable, TMutable>, TImmutable, TMutable> where TValue : TImmutable, TMutable
{
private class ContainerMutexGuard : ScopeGuard, IContainerMutexGuard<TValue>, IMutexGuard<TValue>, IMutexGuard, IDisposable
{
private readonly ReaderWriterLockSlim _lock;
private readonly OpaqueView<TValue, TImmutable, TMutable> _view;
public TValue Value
{
get
{
return _view.Value;
}
set
{
_view.Value = value;
}
}
public ContainerMutexGuard(ReaderWriterLockSlim @lock, OpaqueView<TValue, TImmutable, TMutable> view)
{
_lock = @lock;
_view = view;
}
protected override void EndScope()
{
_lock.ExitWriteLock();
}
}
public Mutex(TValue value)
: base(new OpaqueView<TValue, TImmutable, TMutable>(value))
{
}
public Mutex(Func<TValue> factory)
: base((Func<OpaqueView<TValue, TImmutable, TMutable>>)(() => new OpaqueView<TValue, TImmutable, TMutable>(factory())))
{
}
public IContainerMutexGuard<TValue> AcquireContainer()
{
_lock.EnterWriteLock();
return new ContainerMutexGuard(_lock, _value);
}
}
public interface IAccessView<out TImmutable, out TMutable>
{
TImmutable AsImmutableView();
TMutable AsMutableView();
}
public class OpaqueView<T> : OpaqueView<T, T, T>
{
public OpaqueView(T value)
: base(value)
{
}
}
public class OpaqueView<TValue, TImmutable, TMutable>(TValue value) : IAccessView<TImmutable, TMutable> where TValue : TImmutable, TMutable
{
internal TValue Value { get; set; } = value;
public TImmutable AsImmutableView()
{
return (TImmutable)(object)Value;
}
public TMutable AsMutableView()
{
return (TMutable)(object)Value;
}
}
public class AccessViewMutex<TView, TImmutable, TMutable> : IMutex<TImmutable, TMutable>, IImmutableMutex<TImmutable>, IMutex, IDisposable, IMutableMutex<TMutable> where TView : IAccessView<TImmutable, TMutable>
{
protected readonly TView _value;
protected readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
public AccessViewMutex(TView value)
{
_value = value;
}
public AccessViewMutex(Func<TView> factory)
{
_value = factory();
}
public void Dispose()
{
_lock.Dispose();
}
public IMutexGuard<TImmutable> AcquireShared()
{
_lock.EnterReadLock();
return new ReadOnlyMutexGuard<TImmutable>(_lock, _value.AsImmutableView());
}
public Result<IMutexGuard<TImmutable>, TryLockError> TryAcquireShared()
{
return TryAcquireShared(TimeSpan.Zero);
}
public Result<IMutexGuard<TImmutable>, TryLockError> TryAcquireShared(TimeSpan timeout)
{
try
{
return _lock.TryEnterReadLock(timeout) ? ((Result<IMutexGuard<TImmutable>, TryLockError>)Result.Success((IMutexGuard<TImmutable>)new ReadOnlyMutexGuard<TImmutable>(_lock, _value.AsImmutableView()))) : ((Result<IMutexGuard<TImmutable>, TryLockError>)Result.Error(TryLockError.WouldBlock));
}
catch (LockRecursionException)
{
return Result.Error(TryLockError.WouldBlock);
}
}
public IMutexGuard<TMutable> AcquireExclusive()
{
_lock.EnterWriteLock();
return new ReadWriteMutexGuard<TMutable>(_lock, _value.AsMutableView());
}
public Result<IMutexGuard<TMutable>, TryLockError> TryAcquireExclusive()
{
return TryAcquireExclusive(TimeSpan.Zero);
}
public Result<IMutexGuard<TMutable>, TryLockError> TryAcquireExclusive(TimeSpan timeout)
{
try
{
return _lock.TryEnterWriteLock(timeout) ? ((Result<IMutexGuard<TMutable>, TryLockError>)Result.Success((IMutexGuard<TMutable>)new ReadWriteMutexGuard<TMutable>(_lock, _value.AsMutableView()))) : ((Result<IMutexGuard<TMutable>, TryLockError>)Result.Error(TryLockError.WouldBlock));
}
catch (LockRecursionException)
{
return Result.Error(TryLockError.WouldBlock);
}
}
public IUpgradableMutexGuard<TImmutable, TMutable> AcquireUpgradable()
{
_lock.EnterUpgradeableReadLock();
return new UpgradableMutexGuard<TView, TImmutable, TMutable>(_lock, _value);
}
public TView GetValueUnsafe()
{
return _value;
}
}
public enum TryLockError
{
WouldBlock
}
public static class MutexGuardExtensions
{
[SpecialName]
public sealed class <G>$D2A340436F3968DB55A79EC364A42EF3<$T0>
{
[SpecialName]
public static class <M>$96645F6D8769A494464AFE2592AFDC15
{
}
[ExtensionMarker("<M>$96645F6D8769A494464AFE2592AFDC15")]
public IMutexGuard<TOut> Select<TOut>(Func<$T0, TOut> transform)
{
throw new NotSupportedException();
}
}
public static IMutexGuard<TOut> Select<T, TOut>(this IMutexGuard<T> self, Func<T, TOut> transform)
{
return new MappedMutexGuard<T, TOut>(self, transform);
}
}
public interface IMutexGuard : IDisposable
{
}
public interface IMutexGuard<out T> : IMutexGuard, IDisposable
{
T Value { get; }
}
public interface IContainerMutexGuard<T> : IMutexGuard<T>, IMutexGuard, IDisposable
{
new T Value { get; set; }
T IMutexGuard<T>.Value => Value;
}
public interface IUpgradableMutexGuard<out TImmutable, out TMutable> : IMutexGuard<TImmutable>, IMutexGuard, IDisposable
{
IMutexGuard<TMutable> Upgrade();
}
public sealed class MappedMutexGuard<T, TOut> : ScopeGuard, IMutexGuard<TOut>, IMutexGuard, IDisposable
{
private readonly IMutexGuard<T> _inner;
private readonly Func<T, TOut> _transform;
public TOut Value => _transform(_inner.Value);
public MappedMutexGuard(IMutexGuard<T> inner, Func<T, TOut> transform)
{
_inner = inner;
_transform = transform;
}
protected override void EndScope()
{
_inner.Dispose();
}
}
public sealed class ReadWriteMutexGuard<TMutable> : ScopeGuard, IMutexGuard<TMutable>, IMutexGuard, IDisposable
{
private readonly TMutable _value;
private readonly ReaderWriterLockSlim _lock;
public TMutable Value => _value;
public ReadWriteMutexGuard(ReaderWriterLockSlim @lock, TMutable value)
{
_lock = @lock;
_value = value;
}
protected override void EndScope()
{
_lock.ExitWriteLock();
}
}
public sealed class ReadOnlyMutexGuard<TImmutable> : ScopeGuard, IMutexGuard<TImmutable>, IMutexGuard, IDisposable
{
private readonly TImmutable _value;
private readonly ReaderWriterLockSlim _lock;
public TImmutable Value => _value;
public ReadOnlyMutexGuard(ReaderWriterLockSlim @lock, TImmutable value)
{
_lock = @lock;
_value = value;
}
protected override void EndScope()
{
_lock.ExitReadLock();
}
}
public sealed class UpgradableMutexGuard<TView, TImmutable, TMutable> : ScopeGuard, IUpgradableMutexGuard<TImmutable, TMutable>, IMutexGuard<TImmutable>, IMutexGuard, IDisposable where TView : IAccessView<TImmutable, TMutable>
{
private readonly TView _value;
private readonly ReaderWriterLockSlim _lock;
public TImmutable Value => _value.AsImmutableView();
public UpgradableMutexGuard(ReaderWriterLockSlim @lock, TView value)
{
_lock = @lock;
_value = value;
}
public IMutexGuard<TMutable> Upgrade()
{
return new ReadWriteMutexGuard<TMutable>(_lock, _value.AsMutableView());
}
protected override void EndScope()
{
_lock.ExitUpgradeableReadLock();
}
}
public static class NumberExtensions
{
[SpecialName]
public sealed class <G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69
{
[SpecialName]
public static class <M>$5DACCD576B731A5595F442DFA5E8AE24
{
}
[ExtensionMarker("<M>$5DACCD576B731A5595F442DFA5E8AE24")]
public int BitCount
{
[ExtensionMarker("<M>$5DACCD576B731A5595F442DFA5E8AE24")]
get
{
throw new NotSupportedException();
}
}
[ExtensionMarker("<M>$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<T> Cast<T>();
}
public interface IOption<out T> : IOption
{
T Unwrap();
T Expect(string message);
T Expect(Exception ex);
T Expect(Func<Exception> ex);
IOption<TResult> Select<TResult>(Func<T, TResult> selector);
IOption<TResult> SelectMany<TResult>(Func<T, IOption<TResult>> selector);
IOption<T> Where(Func<T, bool> predicate);
}
public static class Option
{
[StructLayout(LayoutKind.Sequential, Size = 1)]
public struct NoneValuePlaceholder
{
public Option<T> FulfillType<T>()
{
return Option<T>.None;
}
public IOption FulfillType(Type type)
{
Type type2 = typeof(Option<>).MakeGenericType(type);
return (IOption)Activator.CreateInstance(type2);
}
}
[SpecialName]
public sealed class <G>$74CAB2AE3CF21190BB3801A1AE8B218F<$T0>
{
[SpecialName]
public static class <M>$BD89945CA7648600A6623ED9B0D34B66
{
}
[ExtensionMarker("<M>$BD89945CA7648600A6623ED9B0D34B66")]
public $T0 OrElse($T0 other)
{
throw new NotSupportedException();
}
[ExtensionMarker("<M>$BD89945CA7648600A6623ED9B0D34B66")]
public $T0 OrElseGet(Func<$T0> other)
{
throw new NotSupportedException();
}
[ExtensionMarker("<M>$BD89945CA7648600A6623ED9B0D34B66")]
public Option<$T0> SafeUnbox()
{
throw new NotSupportedException();
}
}
[SpecialName]
public sealed class <G>$89F335C99AD8D0EADD6CEDDA8A8F68D2<$T0, $T1>
{
[SpecialName]
public static class <M>$FB1CE76455089A4956A6AB3011B003C7
{
}
[ExtensionMarker("<M>$FB1CE76455089A4956A6AB3011B003C7")]
public (Option<$T0> First, Option<$T1> Second) Unzip()
{
throw new NotSupportedException();
}
}
public static NoneValuePlaceholder None => default(NoneValuePlaceholder);
public static Option<T> Some<T>(T value)
{
return Option<T>.Some(value);
}
public static Option<T> ToOption<T>(this T? value) where T : class
{
return (value == null) ? Option<T>.None : Option<T>.Some(value);
}
public static Option<T> ToOption<T>(this T? value) where T : struct
{
return (!value.HasValue) ? Option<T>.None : Option<T>.Some(value.Value);
}
public static IOption<T> Cast<T>(this IOption self)
{
return (IOption<T>)self;
}
public static TOption Or<TOption, TValue>(this TOption self, TOption other) where TOption : IOption<TValue>
{
return self.IsSome ? self : other;
}
public static Option<T> GetOptional<T>(this IReadOnlyList<T> self, int index)
{
if (index < 0 || index >= self.Count)
{
return None;
}
return Some(self[index]);
}
public static T OrElse<T>(this IOption<T> self, T other)
{
return self.IsSome ? self.Unwrap() : other;
}
public static T OrElseGet<T>(this IOption<T> self, Func<T> other)
{
return self.IsSome ? self.Unwrap() : other();
}
public static Option<T> SafeUnbox<T>(this IOption<T> self)
{
if (self is Option<T> result)
{
return result;
}
return self.Select<Option<T>>(Option<T>.Some).OrElse(Option<T>.None);
}
public static (Option<TFirst> First, Option<TSecond> Second) Unzip<TFirst, TSecond>(this IOption<(TFirst, TSecond)> self)
{
return self.Select(((TFirst, TSecond) t) => (Some(t.Item1), Some(t.Item2))).OrElse((None, None));
}
}
public readonly struct Option<T> : IOption<T>, IOption
{
public bool IsSome { get; }
private T ValueUnsafe { get; }
public static Option<T> None => default(Option<T>);
public bool IsNone => !IsSome;
private Option(T value)
{
IsSome = true;
ValueUnsafe = value;
}
public static implicit operator Option<T>(Option.NoneValuePlaceholder _)
{
return None;
}
public static Option<T> Some(T value)
{
return new Option<T>(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<T>();
}
public T Expect(Func<Exception> ex)
{
return IsSome ? ValueUnsafe : ex().Rethrow<T>();
}
public Option<TResult> Select<TResult>(Func<T, TResult> selector)
{
return IsSome ? Option<TResult>.Some(selector(ValueUnsafe)) : Option<TResult>.None;
}
IOption<TResult> IOption<T>.Select<TResult>(Func<T, TResult> selector)
{
return Select(selector);
}
public Option<TResult> SelectMany<TResult>(Func<T, Option<TResult>> selector)
{
return IsSome ? selector(ValueUnsafe) : Option<TResult>.None;
}
IOption<TResult> IOption<T>.SelectMany<TResult>(Func<T, IOption<TResult>> selector)
{
return SelectMany((T t) => selector(t).Select(Option.Some).OrElse(Option.None));
}
public Option<TResult> SelectMany<TSelectMany, TResult>(Func<T, Option<TSelectMany>> selector, Func<Option<T>, TSelectMany, TResult> resultSelector)
{
T valueUnsafe = ValueUnsafe;
Option<T> self = this;
return IsSome ? (from v in selector(valueUnsafe)
select resultSelector(self, v)) : Option<TResult>.None;
}
public Option<T> IfSome(Action<T> action)
{
if (IsSome)
{
action(ValueUnsafe);
}
return this;
}
public Option<T> IfNone(Action action)
{
if (!IsSome)
{
action();
}
return this;
}
public Option<T> Match(Action? None = null, Action<T>? Some = null)
{
if (IsSome)
{
Some?.Invoke(ValueUnsafe);
}
else
{
None?.Invoke();
}
return this;
}
public IEnumerable<T> AsEnumerable()
{
if (IsSome)
{
yield return ValueUnsafe;
}
}
public Result<T, TError> ToSuccessOr<TError>(TError error)
{
return Select((Func<T, Result<T, TError>>)((T x) => Result.Success(x))).OrElseGet(() => Result.Error(error));
}
public Option<T> Or(Option<T> other)
{
return IsSome ? this : other;
}
public Option<T> Where(Func<T, bool> predicate)
{
return (!IsSome) ? this : (predicate(ValueUnsafe) ? this : None);
}
IOption<T> IOption<T>.Where(Func<T, bool> predicate)
{
return Where(predicate);
}
public T OrElse(T other)
{
return (T)(IsSome ? ((object)ValueUnsafe) : ((object)other));
}
public Option<T> OrGet(Func<Option<T>> other)
{
return IsSome ? this : other();
}
public T OrElseGet(Func<T> other)
{
return IsSome ? ValueUnsafe : other();
}
public Option<TOut> CastOrThrow<TOut>()
{
return IsSome ? Option.Some((TOut)(object)ValueUnsafe) : Option<TOut>.None;
}
public Option<TOut> Cast<TOut>()
{
if (!IsSome)
{
return Option<TOut>.None;
}
T valueUnsafe = ValueUnsafe;
return (valueUnsafe is TOut value) ? Option.Some(value) : ((Option<TOut>)Option.None);
}
IOption<TOut> IOption.Cast<TOut>()
{
return Cast<TOut>();
}
public Option<T> Xor(Option<T> other)
{
if (IsSome == other.IsSome)
{
return Option.None;
}
return IsSome ? this : other;
}
public Option<(T, TOther)> Zip<TOther>(Option<TOther> other)
{
if (!IsSome || !other.IsSome)
{
return Option.None;
}
return Option.Some((ValueUnsafe, other.ValueUnsafe));
}
public Option<TZipped> Zip<TOther, TZipped>(Option<TOther> other, Func<T, TOther, TZipped> 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<T> : IResult
{
Option<T> Value { get; }
T Unwrap();
IResultSuccess<TResult> Select<TResult>(Func<T, TResult> transform);
}
public interface IResultError<T> : IResult
{
Option<T> Error { get; }
T UnwrapError();
IResultError<TResult> SelectError<TResult>(Func<T, TResult> transform);
}
public interface IResult<T, TError> : IResultSuccess<T>, IResult, IResultError<TError>
{
new IResult<TResult, TError> Select<TResult>(Func<T, TResult> transform);
new IResult<T, TResult> SelectError<TResult>(Func<TError, TResult> transform);
IResultSuccess<TResult> IResultSuccess<T>.Select<TResult>(Func<T, TResult> transform)
{
return Select(transform);
}
IResultError<TResult> IResultError<TError>.SelectError<TResult>(Func<TError, TResult> transform)
{
return SelectError(transform);
}
}
public static class Result
{
public class PartialWithSuccess<T>
{
private readonly T _value;
internal PartialWithSuccess(T value)
{
_value = value;
}
public Result<T, TError> FulfillErrorType<TError>()
{
return Result<T, TError>.Success(_value);
}
}
public class PartialWithError<T>
{
private readonly T _error;
internal PartialWithError(T error)
{
_error = error;
}
public Result<TSuccess, T> FulfillSuccessType<TSuccess>()
{
return Result<TSuccess, T>.Error(_error);
}
}
public static PartialWithSuccess<T> Success<T>(T value)
{
return new PartialWithSuccess<T>(value);
}
public static PartialWithError<T> Error<T>(T error)
{
return new PartialWithError<T>(error);
}
public static Result<T, Exception> Catch<T>(Func<T> func)
{
try
{
return Success(func());
}
catch (Exception error)
{
return Error(error);
}
}
public static async Task<Result<T, Exception>> CatchAsync<T>(Func<Task<T>> func)
{
try
{
return Success(await func());
}
catch (Exception ex)
{
Exception ex2 = ex;
return Error(ex2);
}
}
public static T Branch<T>(this IResult<T, T> self)
{
return self.IsSuccess ? self.Unwrap() : self.UnwrapError();
}
public static Result<T, TError> SafeUnbox<T, TError, TResult>(this TResult self) where TResult : IResult<T, TError>
{
if (self is Result<T, TError> result)
{
return result;
}
return self.Select((T val) => Success(val).FulfillErrorType<TError>()).SelectError((TError val) => Error(val).FulfillSuccessType<T>()).Branch();
}
public static T UnwrapOrThrow<T, TError>(this Result<T, TError> result) where TError : Exception
{
Option<T> value = result.Value;
return value.IsSome ? value.Unwrap() : result.UnwrapError().Rethrow<T>();
}
public static T Branch<T>(this Result<T, T> result)
{
return result.IsSuccess ? result.Unwrap() : result.UnwrapError();
}
public static T FastUnwrap<T>(this Result<T, Never> self)
{
return self.ValueUnsafe;
}
public static T FastUnwrapError<T>(this Result<Never, T> self)
{
return self.ErrUnsafe;
}
public static Result<T, TError> Flatten<T, TError>(this Result<Result<T, TError>, TError> self)
{
return self.SelectMany((Result<T, TError> s) => s);
}
public static Result<IEnumerable<TItem>, TError> Process<TItem, TError>(this IEnumerable<Result<TItem, TError>> enumerable)
{
IEnumerable<TItem> enumerable2 = Enumerable.Empty<TItem>();
foreach (Result<TItem, TError> item in enumerable)
{
if (item.IsError)
{
return Error(item.UnwrapError());
}
enumerable2 = enumerable2.Append(item.Unwrap());
}
return Success(enumerable2);
}
}
public abstract class Result<T, TError> : IResult<T, TError>, IResultSuccess<T>, IResult, IResultError<TError>, IEquatable<Result<T, TError>>
{
private class SuccessBranch : Result<T, TError>
{
public new T Value { get; }
internal SuccessBranch(T value)
{
Value = value;
}
public override Result<TResult, TError> Select<TResult>(Func<T, TResult> func)
{
return Result.Success(func(Value));
}
public override Result<TResult, TError> SelectMany<TResult>(Func<T, Result<TResult, TError>> func)
{
return func(Value);
}
public override Result<T, TErrorResult> SelectError<TErrorResult>(Func<TError, TErrorResult> func)
{
return Result.Success(Value);
}
public override Result<T, TError> IfSuccess(Action<T> action)
{
action(Value);
return this;
}
public override Result<T, TError> IfError(Action<TError> action)
{
return this;
}
}
private class ErrorBranch : Result<T, TError>
{
public new TError Error { get; }
internal ErrorBranch(TError error)
{
Error = error;
}
public override Result<TResult, TError> Select<TResult>(Func<T, TResult> func)
{
return Result.Error(Error);
}
public override Result<TResult, TError> SelectMany<TResult>(Func<T, Result<TResult, TError>> func)
{
return Result.Error(Error);
}
public override Result<T, TErrorResult> SelectError<TErrorResult>(Func<TError, TErrorResult> func)
{
return Result.Error(func(Error));
}
public override Result<T, TError> IfSuccess(Action<T> action)
{
return this;
}
public override Result<T, TError> IfError(Action<TError> 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<T> Value => (this is SuccessBranch successBranch) ? Option<T>.Some(successBranch.Value) : Option<T>.None;
public Option<TError> Err => (this is ErrorBranch errorBranch) ? Option<TError>.Some(errorBranch.Error) : Option<TError>.None;
Option<TError> IResultError<TError>.Error => Err;
internal Result()
{
}
public static implicit operator Result<T, TError>(Result.PartialWithSuccess<T> partial)
{
return partial.FulfillErrorType<TError>();
}
public static implicit operator Result<T, TError>(Result.PartialWithError<TError> partial)
{
return partial.FulfillSuccessType<T>();
}
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<TResult, TError> Select<TResult>(Func<T, TResult> func);
IResult<TResult, TError> IResult<T, TError>.Select<TResult>(Func<T, TResult> transform)
{
return Select(transform);
}
public abstract Result<TResult, TError> SelectMany<TResult>(Func<T, Result<TResult, TError>> func);
public Result<TResult, TError> SelectMany<TMiddle, TResult>(Func<T, Result<TMiddle, TError>> func, Func<Result<T, TError>, TMiddle, TResult> resultSelector)
{
return IsError ? ((Result<TResult, TError>)Result.Error(ErrUnsafe)) : (from m in func(ValueUnsafe)
select resultSelector(this, m));
}
public abstract Result<T, TErrorResult> SelectError<TErrorResult>(Func<TError, TErrorResult> func);
IResult<T, TResult> IResult<T, TError>.SelectError<TResult>(Func<TError, TResult> transform)
{
return SelectError(transform);
}
public abstract Result<T, TError> IfSuccess(Action<T> action);
public abstract Result<T, TError> IfError(Action<TError> action);
public IEnumerable<T> 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<T, TError> Success(T value)
{
return new SuccessBranch(value);
}
public static Result<T, TError> Error(TError error)
{
return new ErrorBranch(error);
}
public bool Equals(Result<T, TError>? 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<T, TError> other && Equals(other));
}
public override int GetHashCode()
{
return IsSuccess ? HashCode.Combine(value1: true, ValueUnsafe) : HashCode.Combine(value1: false, ErrUnsafe);
}
public static bool operator ==(Result<T, TError>? left, Result<T, TError>? right)
{
return object.Equals(left, right);
}
public static bool operator !=(Result<T, TError>? left, Result<T, TError>? 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<SemaphoreSlimGuard> 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 <G>$B121A579F9C62187B81C2CC9A1D05898
{
[SpecialName]
public static class <M>$9A581DD593152A881C02B7BBD540726C
{
}
[ExtensionMarker("<M>$9A581DD593152A881C02B7BBD540726C")]
public Task<IDisposable> CreateScopeAsync()
{
throw new NotSupportedException();
}
[ExtensionMarker("<M>$9A581DD593152A881C02B7BBD540726C")]
public IDisposable CreateScope()
{
throw new NotSupportedException();
}
[ExtensionMarker("<M>$9A581DD593152A881C02B7BBD540726C")]
public void EnterScope(Action action)
{
throw new NotSupportedException();
}
[ExtensionMarker("<M>$9A581DD593152A881C02B7BBD540726C")]
public T EnterScope<T>(Func<T> func)
{
throw new NotSupportedException();
}
[ExtensionMarker("<M>$9A581DD593152A881C02B7BBD540726C")]
public Task EnterScopeAsync(Action action)
{
throw new NotSupportedException();
}
[ExtensionMarker("<M>$9A581DD593152A881C02B7BBD540726C")]
public Task<T> EnterScopeAsync<T>(Func<T> func)
{
throw new NotSupportedException();
}
[ExtensionMarker("<M>$9A581DD593152A881C02B7BBD540726C")]
public Task EnterScopeAsync(Func<Task> action)
{
throw new NotSupportedException();
}
[ExtensionMarker("<M>$9A581DD593152A881C02B7BBD540726C")]
public Task<T> EnterScopeAsync<T>(Func<Task<T>> action)
{
throw new NotSupportedException();
}
}
[SpecialName]
public sealed class <G>$6A188C7A03CB85FC44DF3FD030EA0664
{
[SpecialName]
public static class <M>$438D5453E0DFEE874C985587A0074FB9
{
}
[ExtensionMarker("<M>$438D5453E0DFEE874C985587A0074FB9")]
[DoesNotReturn]
public Never Rethrow()
{
throw new NotSupportedException();
}
[ExtensionMarker("<M>$438D5453E0DFEE874C985587A0074FB9")]
[DoesNotReturn]
public T Rethrow<T>()
{
throw new NotSupportedException();
}
}
public static Option<TValue> GetOptional<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
{
TValue value;
return dict.TryGetValue(key, out value) ? Option.Some(value) : Option<TValue>.None;
}
private static Result<Unit, AggregateException> SafeInvokeInternal<T>(T? self, Action<T> invoke) where T : Delegate
{
if (self == null)
{
return Result.Success(Unit.Instance);
}
Lazy<List<Exception>> lazy = new Lazy<List<Exception>>(() => new List<Exception>());
foreach (T item2 in self.GetInvocationList().Cast<T>())
{
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<Unit, AggregateException> SafeInvoke(this Action? self)
{
return SafeInvokeInternal(self, delegate(Action a)
{
a();
});
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result<Unit, AggregateException> SafeInvoke<T>(this Action<T>? self, T arg)
{
return SafeInvokeInternal(self, delegate(Action<T> a)
{
a(arg);
});
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result<Unit, AggregateException> SafeInvoke<T1, T2>(this Action<T1, T2>? self, T1 arg1, T2 arg2)
{
return SafeInvokeInternal(self, delegate(Action<T1, T2> a)
{
a(arg1, arg2);
});
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static async Task<IDisposable> 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<T>(this SemaphoreSlim semaphore, Func<T> 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<T> EnterScopeAsync<T>(this SemaphoreSlim semaphore, Func<T> func) where T : notnull
{
using (await semaphore.CreateScopeAsync())
{
return func();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static async Task EnterScopeAsync(this SemaphoreSlim semaphore, Func<Task> action)
{
using (await semaphore.CreateScopeAsync())
{
await action();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static async Task<T> EnterScopeAsync<T>(this SemaphoreSlim semaphore, Func<Task<T>> 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<Never>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[DoesNotReturn]
public static T Rethrow<T>(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.TranslationStorage.RegisterExternalLocalizationKey(key, unlocalized);
}
}
}
namespace ue.Peak.TcnPatch.Adapters
{
public static class MoreAscentsSupport
{
private class ReflectionFailedError(Action showWarning)
{
public void ShowWarning()
{
showWarning();
}
}
private static readonly Lazy<Type> _gimmickHandlerType = new Lazy<Type>(() => 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<object, bool> typedFieldInfo = AccessTools.Field(GimmickHandlerType, "HasInitialized").AsTyped<object, bool>();
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<object> readOnlyList = (IReadOnlyList<object>)value3.GetValue(null);
MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(AscentData), "Instance");
AscentData val = (AscentData)methodInfo.Invoke(null, Array.Empty<object>());
List<AscentInstanceData> ascents = val.ascents;
foreach (object gimmick in readOnlyList)
{
AscentInstanceData val2 = ((IEnumerable<AscentInstanceData>)ascents).FirstOrDefault((Func<AscentInstanceData, bool>)((AscentInstanceData d) => d.title == (string)gimmickGetTitleMethod.Invoke(gimmick, Array.Empty<object>()) && d.description == (string)gimmickGetDescriptionMethod.Invoke(gimmick, Array.Empty<object>())));
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<object>()));
ue.Peak.TcnPatch.API.TcnPatch.Instance.RegisterLocalizationKey(LocalizedText.GetDescriptionIndex(text), (string)gimmickGetDescriptionMethod.Invoke(gimmick, Array.Empty<object>()));
ue.Peak.TcnPatch.API.TcnPatch.Instance.RegisterLocalizationKey(text + ".Reward", (string)value2.Invoke(gimmick, Array.Empty<object>()));
}
}
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)"無法為 MoreAscents 提供繁中支援... x_x");
Plugin.Logger.LogError((object)ex);
}
}
static Result<FieldInfo, ReflectionFailedError> 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<MethodInfo, ReflectionFailedError> 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<Type, ReflectionFailedError> 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"));
}));
}
}
}
}