using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
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.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.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+1f0177b817be447945970dbebe05adf398213ca0")]
[assembly: AssemblyProduct("ue.Peak.TcnPatch")]
[assembly: AssemblyTitle("ue.Peak.TcnPatch")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace ue.Peak.TcnPatch
{
[BepInPlugin("ue.Peak.TcnPatch", "ue.Peak.TcnPatch", "1.4.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 = "1.4.0";
private static FileSystemWatcher _watcher;
public const string TcnTranslationFileName = "TcnTranslations.json";
private static SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
internal static Plugin Instance { get; private set; }
internal static ManualLogSource Logger { get; private set; }
internal static TranslationFile EmptyTranslationFile { get; set; }
internal static TranslationFile CurrentTranslationFile { get; private set; }
internal static PluginConfig ModConfig { get; private set; }
internal static HashSet<string> EphemeralTranslationKeys { get; } = new HashSet<string>();
internal static bool HasOfficialTcn { get; private set; }
internal static Dictionary<string, string> TcnTable { get; } = new Dictionary<string, string>();
internal static Dictionary<string, string> RegisteredTable { get; } = new Dictionary<string, string>();
internal static Dictionary<string, string> RegisteredOrigTable { get; } = new Dictionary<string, string>();
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");
if (Enum.GetValues(typeof(Language)).Cast<int>().Any((int values) => values == 10))
{
HasOfficialTcn = true;
}
if (ModConfig.DownloadFromRemote.Value)
{
Task.Run(async delegate
{
string url = ModConfig.DownloadUrl.Value;
Logger.LogInfo((object)"正在從遠端下載翻譯資料... (可以在模組設定停用)");
Logger.LogInfo((object)("網址:" + url));
HttpClient client = new HttpClient();
try
{
string content = await client.GetStringAsync(url);
try
{
JObject.Parse(content);
}
catch (Exception ex)
{
Exception e2 = ex;
Logger.LogWarning((object)"無效的遠端翻譯資料!將使用本機資料。");
Logger.LogWarning((object)e2);
return;
}
string dir = Path.Combine(Paths.ConfigPath, "ue.Peak.TcnPatch");
Directory.CreateDirectory(dir);
string path = Path.Combine(dir, "TcnTranslations.json");
await _lock.WaitAsync();
try
{
FileStream targetStream = File.Open(path, FileMode.Create, FileAccess.Write);
try
{
StreamWriter writer = new StreamWriter(targetStream);
try
{
await writer.WriteAsync(content);
}
finally
{
if (writer != null)
{
await writer.DisposeAsync();
}
}
}
finally
{
if (targetStream != null)
{
await targetStream.DisposeAsync();
}
}
}
finally
{
_lock.Release();
}
Logger.LogInfo((object)"翻譯資料下載完成!");
}
catch (Exception ex)
{
Exception e = ex;
Logger.LogError((object)"翻譯資料下載失敗!將使用本機資料。");
Logger.LogError((object)e);
}
client.Dispose();
});
}
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");
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();
}
};
UpdateMainTable();
_watcher.EnableRaisingEvents = true;
}
internal static bool TryGetVanilla(string id, out string result)
{
return TcnTable.TryGetValue(id.ToUpperInvariant(), out result);
}
internal static bool TryGetRegistered(string id, Language? language, out string result)
{
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: 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_002b: Invalid comparison between Unknown and I4
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: 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;
}
if ((int)language.GetValueOrDefault() == 10 && RegisteredTable.TryGetValue(id, out result))
{
return true;
}
return RegisteredOrigTable.TryGetValue(id, out result);
}
private static void UpdateMainTable()
{
_lock.Wait();
try
{
if (!TryReadFromJson<JObject>("TcnTranslations.json", out var result, () => new JObject()))
{
return;
}
CurrentTranslationFile = TranslationFile.Deserialize(result);
}
catch (TranslationParseException ex)
{
Logger.LogError((object)ex.UserMessage);
Logger.LogError((object)"翻譯資料分析失敗!");
return;
}
catch (Exception ex2)
{
Logger.LogError((object)"翻譯資料分析失敗!");
Logger.LogError((object)ex2);
return;
}
finally
{
_lock.Release();
}
Dictionary<string, List<string>> mainTable = LocalizedText.mainTable;
HashSet<string> hashSet = mainTable.Keys.ToHashSet();
if (CurrentTranslationFile.Authors.Count > 0)
{
Logger.LogInfo((object)("翻譯資料作者:" + string.Join("、", CurrentTranslationFile.Authors)));
}
else
{
Logger.LogInfo((object)"翻譯資料作者:未知");
}
TcnTable.Clear();
RegisteredTable.Clear();
string key;
string value;
foreach (KeyValuePair<string, string> translation in CurrentTranslationFile.Translations)
{
translation.Deconstruct(out key, out value);
string text = key;
string value2 = value;
string text2 = text.ToUpperInvariant();
if (TcnTable.ContainsKey(text2))
{
Logger.LogInfo((object)("發現重複的翻譯key:「" + text + "」!已存在大寫的同名key!"));
continue;
}
if (!mainTable.ContainsKey(text2) && ModConfig.WarnUnknownTranslationKeys.Value)
{
Logger.LogWarning((object)("正在使用未知的翻譯key:「" + text2 + "」!"));
}
TcnTable[text2] = value2;
hashSet.Remove(text2);
}
foreach (KeyValuePair<string, string> additionalTranslation in CurrentTranslationFile.AdditionalTranslations)
{
additionalTranslation.Deconstruct(out value, out key);
string text3 = value;
string value3 = key;
RegisteredTable[text3] = value3;
hashSet.Remove(text3);
}
HashSet<string> 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 bool TryReadFromJson<T>(string fileName, out T result, Func<T> defaultContent) where T : class
{
string text = Path.Combine(Paths.ConfigPath, "ue.Peak.TcnPatch");
Directory.CreateDirectory(text);
string path = Path.Combine(text, fileName);
if (!File.Exists(path))
{
string contents = JsonConvert.SerializeObject((object)defaultContent());
File.WriteAllText(path, contents);
}
try
{
using FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader streamReader = new StreamReader(stream);
result = JsonConvert.DeserializeObject<T>(streamReader.ReadToEnd());
return true;
}
catch (Exception ex)
{
Logger.LogError((object)("無法讀取 JSON 設定:" + fileName));
Logger.LogError((object)ex);
result = null;
return false;
}
}
}
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<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<LanguagePatchMode> LanguagePatchMode { get; } = config.Bind<LanguagePatchMode>("Patch", "LanguagePatchMode", ue.Peak.TcnPatch.LanguagePatchMode.InsertAfterSimplifiedChinese, "設定如何修正設定中的語言清單");
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, "在主畫面版本文字後面顯示這個模組的版本? (v1.4.0)");
public PluginConfig(ConfigFile config)
{
}
}
public enum LanguagePatchMode
{
InsertAfterSimplifiedChinese,
ReplaceEnglish,
ReplaceSimplifiedChinese,
Append,
TraditionalChineseOnly
}
internal static class ReflectionExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TValue GetReflectionFieldValue<TOwner, TValue>(this TOwner owner, ReflectionMembers.TypedFieldInfo<TOwner, TValue> fieldInfo)
{
return (TValue)fieldInfo.FieldInfo.GetValue(owner);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetReflectionFieldValue<TOwner, TValue>(this TOwner owner, ReflectionMembers.TypedFieldInfo<TOwner, TValue> fieldInfo, out TValue result)
{
if (fieldInfo == null)
{
result = default(TValue);
return false;
}
result = (TValue)fieldInfo.FieldInfo.GetValue(owner);
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetReflectionFieldValue<TOwner, TValue>(this TOwner owner, ReflectionMembers.TypedFieldInfo<TOwner, TValue> fieldInfo, TValue value)
{
fieldInfo.FieldInfo.SetValue(owner, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TrySetReflectionFieldValue<TOwner, TValue>(this TOwner owner, ReflectionMembers.TypedFieldInfo<TOwner, TValue> fieldInfo, TValue value)
{
if (fieldInfo == null)
{
return false;
}
fieldInfo.FieldInfo.SetValue(owner, value);
return true;
}
}
public static class ReflectionMembers
{
public static class Fields
{
public static TypedFieldInfo<LocalizedText, Language> CurrentLanguage { get; } = AccessTools.Field(typeof(LocalizedText), "CURRENT_LANGUAGE");
public static TypedFieldInfo<VersionString, TextMeshProUGUI> VersionStringText { get; } = AccessTools.Field(typeof(VersionString), "m_text");
public static TypedFieldInfo<LoadingScreenAnimation, string> LoadingScreenString { get; } = AccessTools.Field(typeof(LoadingScreenAnimation), "loadingString");
public static TypedFieldInfo<LoadingScreenAnimation, float> LoadingScreenDefaultStringLength { get; } = AccessTools.Field(typeof(LoadingScreenAnimation), "defaultLoadingStringLength");
}
public class TypedFieldInfo<TOwner, TValue>
{
public FieldInfo FieldInfo { get; }
public TypedFieldInfo(FieldInfo fieldInfo)
{
FieldInfo = fieldInfo;
base..ctor();
}
[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 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<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 static TranslationFile Deserialize(JObject obj)
{
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Invalid comparison between Unknown and I4
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_0186: Unknown result type (might be due to invalid IL or missing references)
//IL_0230: Unknown result type (might be due to invalid IL or missing references)
string[] source = new string[2] { "FormatVersion", "Translations" };
if (!((IEnumerable<string>)source).All((Func<string, bool>)obj.ContainsKey))
{
return InternalDeserializeFromLegacy(obj);
}
TranslationFile translationFile = 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)"正在讀取過新版本的翻譯資料!可能會無法正確讀取。");
}
JToken val2 = default(JToken);
if (obj.TryGetValue("Authors", ref val2))
{
JArray val3 = (JArray)(object)((val2 is JArray) ? val2 : null);
if (val3 != null)
{
foreach (JToken item in val3)
{
translationFile.Authors.Add(Extensions.Value<string>((IEnumerable<JToken>)item));
}
}
else
{
JValue val4 = (JValue)(object)((val2 is JValue) ? val2 : null);
if (val4 != null)
{
translationFile.Authors.Add(Extensions.Value<string>((IEnumerable<JToken>)val4));
}
else
{
Plugin.Logger.LogWarning((object)"無效的翻譯者資料! (Authors)");
}
}
}
JToken val5 = obj["Translations"];
JObject val6 = (JObject)(object)((val5 is JObject) ? val5 : null);
if (val6 == null)
{
throw new TranslationParseException($"Translation entries must be an object, found {val5.Type}", "無效的翻譯資料! (Translations)");
}
string key;
JToken value;
foreach (KeyValuePair<string, JToken> item2 in val6)
{
item2.Deconstruct(out key, out value);
string key2 = key;
JToken val7 = value;
translationFile.Translations[key2] = Extensions.Value<string>((IEnumerable<JToken>)val7);
}
JToken val8 = default(JToken);
if (obj.TryGetValue("AdditionalTranslations", ref val8))
{
JObject val9 = (JObject)(object)((val8 is JObject) ? val8 : null);
if (val9 == null)
{
throw new TranslationParseException($"Additional translation entries must be an object, found {val8.Type}", "無效的附加翻譯資料! (AdditionalTranslations)");
}
List<string> list = new List<string>();
foreach (KeyValuePair<string, JToken> item3 in val9)
{
item3.Deconstruct(out key, out value);
string text = key;
JToken val10 = value;
if (list.Contains<string>(text, StringComparer.InvariantCultureIgnoreCase))
{
Plugin.Logger.LogWarning((object)("翻譯資料出現已註冊過的附加翻譯key「" + text + "」!新的同名翻譯將會被忽略。"));
continue;
}
list.Add(text);
translationFile.AdditionalTranslations[text] = Extensions.Value<string>((IEnumerable<JToken>)val10);
}
}
return translationFile;
}
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 static class Utils
{
[CompilerGenerated]
private sealed class <DelayFramesCoroutine>d__2<T> : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public TaskCompletionSource<T> source;
public int frames;
public Func<T> resultGetter;
private int <i>5__1;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <DelayFramesCoroutine>d__2(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<i>5__1 = 0;
break;
case 1:
<>1__state = -1;
<i>5__1++;
break;
}
if (<i>5__1 < frames)
{
<>2__current = (object)new WaitForEndOfFrame();
<>1__state = 1;
return true;
}
source.SetResult(resultGetter());
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
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);
}
[IteratorStateMachine(typeof(<DelayFramesCoroutine>d__2<>))]
private static IEnumerator DelayFramesCoroutine<T>(TaskCompletionSource<T> source, int frames, Func<T> resultGetter)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <DelayFramesCoroutine>d__2<T>(0)
{
source = source,
frames = frames,
resultGetter = resultGetter
};
}
}
}
namespace ue.Peak.TcnPatch.Patches
{
[HarmonyPatch]
public class LanguageSettingPatch
{
[HarmonyPatch(typeof(LanguageSetting), "GetCustomLocalizedChoices")]
[HarmonyPostfix]
private static void PatchLanguageChoices(LanguageSetting __instance, ref List<string> __result)
{
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
if (!Plugin.HasOfficialTcn)
{
switch (Plugin.ModConfig.LanguagePatchMode.Value)
{
case LanguagePatchMode.ReplaceEnglish:
case LanguagePatchMode.ReplaceSimplifiedChinese:
break;
case LanguagePatchMode.InsertAfterSimplifiedChinese:
case LanguagePatchMode.Append:
{
int num = Enum.GetNames(typeof(Language)).Length;
string text = LocalizedText.GetText("CURRENT_LANGUAGE", __instance.ValueToLanguage(num));
Plugin.Logger.LogInfo((object)"正在修正設定中的語言清單...");
__result.Add(text);
break;
}
case LanguagePatchMode.TraditionalChineseOnly:
__result = __result.Take(1).ToList();
break;
}
}
}
[HarmonyPatch(typeof(LanguageSetting), "ValueToLanguage")]
[HarmonyPostfix]
private static void PatchValueToLanguage(int val, ref Language __result)
{
if (Plugin.HasOfficialTcn)
{
return;
}
switch (Plugin.ModConfig.LanguagePatchMode.Value)
{
case LanguagePatchMode.InsertAfterSimplifiedChinese:
__result = (Language)val;
break;
case LanguagePatchMode.ReplaceEnglish:
if (val == 0)
{
__result = (Language)10;
}
break;
case LanguagePatchMode.ReplaceSimplifiedChinese:
if (val == 9)
{
__result = (Language)10;
}
break;
case LanguagePatchMode.Append:
{
int length = Enum.GetValues(typeof(Language)).Length;
if (val == length)
{
__result = (Language)10;
}
break;
}
case LanguagePatchMode.TraditionalChineseOnly:
__result = (Language)10;
break;
}
}
}
[HarmonyPatch]
public class LoadingScreenAnimationPatch
{
private static bool _loadingScreenAnimationStartTranspiled;
[HarmonyPatch(typeof(LoadingScreenAnimation), "Start")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> PatchLoadingAnimationSwitch(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
//IL_0163: Unknown result type (might be due to invalid IL or missing references)
//IL_016d: Expected O, but got Unknown
//IL_017c: Unknown result type (might be due to invalid IL or missing references)
//IL_0186: Expected O, but got Unknown
if (Plugin.HasOfficialTcn)
{
_loadingScreenAnimationStartTranspiled = true;
return instructions;
}
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)ReflectionMembers.Fields.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 (ReflectionMembers.Fields.LoadingScreenString == null)
{
Plugin.Logger.LogWarning((object)"Patcher 找不到欄位: LoadingScreenAnimation.loadingString");
return;
}
if (ReflectionMembers.Fields.LoadingScreenDefaultStringLength == null)
{
Plugin.Logger.LogWarning((object)"Patcher 找不到欄位: LoadingScreenAnimation.defaultLoadingStringLength");
return;
}
Plugin.Logger.LogInfo((object)"正在修正載入中動畫的文字 @ LoadingScreenAnimation.Start()...");
string text2 = text + "..." + text + "..." + text + "..." + text + "...";
__instance.SetReflectionFieldValue(ReflectionMembers.Fields.LoadingScreenString, text2);
__instance.SetReflectionFieldValue(ReflectionMembers.Fields.LoadingScreenDefaultStringLength, text2.Length);
}
}
}
[HarmonyPatch]
public class LocalizedTextPatch
{
private class AutoDumpRecord
{
public int FormatVersion { get; } = 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 _writtenMainTable;
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_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Invalid comparison between Unknown and I4
if (Plugin.TryGetRegistered(id, language, out var result) && !string.IsNullOrEmpty(result))
{
__runOriginal = false;
__result = result;
}
else if ((int)language == 10 && Plugin.TryGetVanilla(id, out result) && !string.IsNullOrEmpty(result))
{
__runOriginal = false;
__result = result;
}
}
[HarmonyPatch(typeof(LocalizedText), "LoadMainTable")]
[HarmonyPriority(800)]
[HarmonyPostfix]
private static void PatchLoadMainTableFirstChance()
{
if (VanillaLocalizationKeys.Count > 0)
{
foreach (string item in LocalizedText.mainTable.Keys.Where((string key) => !VanillaLocalizationKeys.Contains(key)))
{
LocalizedText.mainTable.Remove(item);
}
return;
}
foreach (string key in LocalizedText.mainTable.Keys)
{
VanillaLocalizationKeys.Add(key);
}
}
[HarmonyPatch(typeof(LocalizedText), "LoadMainTable")]
[HarmonyPriority(0)]
[HarmonyPostfix]
private static void PatchLoadMainTable()
{
if (!_writtenMainTable)
{
_writtenMainTable = 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) => p.Value[(int)language]);
Dictionary<string, string> dictionary = 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) => p.Value[(int)language]);
foreach (KeyValuePair<string, string> item in Plugin.RegisteredOrigTable)
{
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)
{
Transform val = ((Component)__instance).transform.Find("PassportUI/Canvas/Panel/Panel/BG/Text/Nationality/Text");
if ((Object)(object)val == (Object)null)
{
Plugin.Logger.LogDebug((object)"!!: Failed to find the Crabland text in passport UI");
return;
}
LocalizedText val2 = ((Component)val).gameObject.AddComponent<LocalizedText>();
val2.index = "PeakTcnPatch.Passport.Crabland";
val2.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 (ReflectionMembers.Fields.VersionStringText == 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;
ReflectionMembers.TypedFieldInfo<VersionString, TextMeshProUGUI> versionStringText = ReflectionMembers.Fields.VersionStringText;
TextMeshProUGUI reflectionFieldValue = __instance.GetReflectionFieldValue(versionStringText);
((TMP_Text)reflectionFieldValue).verticalAlignment = (VerticalAlignmentOptions)256;
}
}
[HarmonyPatch(typeof(VersionString), "Update")]
[HarmonyPostfix]
private static void PatchVersionString(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;
}
ReflectionMembers.TypedFieldInfo<VersionString, TextMeshProUGUI> versionStringText = ReflectionMembers.Fields.VersionStringText;
if (versionStringText == null)
{
return;
}
TextMeshProUGUI reflectionFieldValue = __instance.GetReflectionFieldValue(versionStringText);
string text = "繁中翻譯by: " + string.Join("、", Plugin.CurrentTranslationFile.Authors);
string text2 = (Plugin.ModConfig.ShowModVersionInPatchCredit.Value ? "繁中支援v1.4.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 text3 = ((flag & (Math.Floor(Time.realtimeSinceStartup / 10f) % 2.0 != 0.0)) ? text : text2);
((TMP_Text)reflectionFieldValue).text = ((TMP_Text)reflectionFieldValue).text + " (" + text3 + ")";
return;
}
if (name != "MainPage")
{
((TMP_Text)reflectionFieldValue).text = ((TMP_Text)reflectionFieldValue).text + "<br><size=70%><alpha=#88>ue.Peak.TcnPatch v1.4.0<alpha=#FF></size>";
return;
}
((TMP_Text)reflectionFieldValue).text = ((TMP_Text)reflectionFieldValue).text + "<br><size=70%>" + text2 + "</size>";
if (flag)
{
((TMP_Text)reflectionFieldValue).text = ((TMP_Text)reflectionFieldValue).text + "<br><size=70%>" + text + "</size>";
}
}
}
}
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.RegisteredOrigTable[key] = unlocalized;
}
}
}
namespace ue.Peak.TcnPatch.Adapters
{
public static class MoreAscentsSupport
{
private static readonly Lazy<Type> _gimmickHandlerType = new Lazy<Type>(() => Type.GetType("MoreAscents.AscentGimmickHandler"));
private static bool _setupSupport;
public static bool IsMoreAscentsInstalled => _gimmickHandlerType.Value != null;
private static Type GimmickHandlerType => _gimmickHandlerType.Value;
public static void RegisterLocalizations()
{
//IL_0152: Unknown result type (might be due to invalid IL or missing references)
//IL_0159: Expected O, but got Unknown
if (!IsMoreAscentsInstalled)
{
return;
}
FieldInfo fieldInfo = AccessTools.Field(GimmickHandlerType, "HasInitialized");
if (fieldInfo == null)
{
return;
}
if (!(bool)fieldInfo.GetValue(null))
{
Plugin.Logger.LogWarning((object)"MoreAscents 還沒初始化!?");
}
else
{
if (_setupSupport)
{
return;
}
_setupSupport = true;
Plugin.Logger.LogInfo((object)"偵測到 MoreAscents!正在為 MoreAscents 提供繁中支援...");
try
{
if (!TryFindTypeOrWarn("MoreAscents.AscentGimmick", out var type2) || !TryFindMethodOrWarn(type2, "GetTitle", out var gimmickGetTitleMethod) || !TryFindMethodOrWarn(type2, "GetDescription", out var gimmickGetDescriptionMethod) || !TryFindMethodOrWarn(type2, "GetTitleReward", out var method2) || !TryFindFieldOrWarn(GimmickHandlerType, "gimmicks", out var field2))
{
return;
}
IReadOnlyList<object> readOnlyList = (IReadOnlyList<object>)field2.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)method2.Invoke(gimmick, Array.Empty<object>()));
}
}
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)"無法為 MoreAscents 提供繁中支援... x_x");
Plugin.Logger.LogError((object)ex);
}
}
static bool TryFindFieldOrWarn(Type type, string fieldName, out FieldInfo field)
{
field = AccessTools.Field(type, fieldName);
if (field != null)
{
return true;
}
Plugin.Logger.LogWarning((object)("無法找到 " + type.Name + "." + fieldName + " 欄位!無法為 MoreAscents 提供繁中支援... x_x"));
return false;
}
static bool TryFindMethodOrWarn(Type type, string methodName, out MethodInfo method)
{
method = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
if (method != null)
{
return true;
}
Plugin.Logger.LogWarning((object)("無法找到 " + type.Name + "." + methodName + "() 方法!無法為 MoreAscents 提供繁中支援... x_x"));
return false;
}
static bool TryFindTypeOrWarn(string typeName, out Type type)
{
type = AccessTools.TypeByName(typeName);
if (type != null)
{
return true;
}
Plugin.Logger.LogWarning((object)("無法找到 " + typeName + " 類型!無法為 MoreAscents 提供繁中支援... x_x"));
return false;
}
}
}
}