Decompiled source of BetterEXP Chinese v0.1.0

BepInEx/plugins/BetterEXPChinese/BetterEXPChinese.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Xml;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;

[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.1.0.0")]
namespace BetterEXPChinese;

[BepInDependency("Swaggies.BetterEXP", "3.0.1")]
[BepInDependency("Aueser.LCChineseProject", "3.3.2")]
[BepInPlugin("hjm.betterexp.zhcn", "BetterEXP 简体中文", "0.1.0")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string Guid = "hjm.betterexp.zhcn";

	private Harmony harmony;

	internal static ManualLogSource Log;

	private void Awake()
	{
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Expected O, but got Unknown
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		PluginInfo val = Chainloader.PluginInfos["Swaggies.BetterEXP"];
		if (val.Metadata.Version != new Version(3, 0, 1))
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"BetterEXP Chinese 0.1.0 targets BetterEXP 3.0.1. Other versions are left unchanged.");
			return;
		}
		try
		{
			string path = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "translations.zh-CN.xml");
			if (File.Exists(path))
			{
				using FileStream stream = File.OpenRead(path);
				Translator.Load(stream);
			}
			else
			{
				Translator.LoadEmbedded();
			}
			List<MethodBase> list = DisplayPatches.FindTargets(((object)val.Instance).GetType().Assembly);
			harmony = new Harmony("hjm.betterexp.zhcn");
			foreach (MethodBase item in list)
			{
				harmony.Patch(item, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(DisplayPatches), "Transpile", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Loaded " + Translator.Count + " Chinese entries; installed " + list.Count + " display patches. EXP calculations and rank/save data are untouched."));
		}
		catch (Exception ex)
		{
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			((BaseUnityPlugin)this).Logger.LogError((object)("Chinese display patches could not be installed; keeping BetterEXP unchanged. " + ex));
		}
	}

	private void OnApplicationQuit()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
public static class DisplayPatches
{
	public static List<MethodBase> FindTargets(Assembly assembly)
	{
		List<MethodBase> list = new List<MethodBase>();
		Add(list, assembly, "LCBetterEXP.NotesList+Note", "Name");
		Add(list, assembly, "LCBetterEXP.NotesList+Note", "Listing");
		Add(list, assembly, "LCBetterEXP.EXPBarManager", "StartEXPBarAnimation");
		Add(list, assembly, "LCBetterEXP.EXPBarManager", "UpdateEXPBar");
		Add(list, assembly, "LCBetterEXP.PauseMenuDisplay", "Update");
		Add(list, assembly, "LCBetterEXP.PerformanceReportManager", "OnPerformanceReport");
		int num = 0;
		Type[] types = assembly.GetTypes();
		foreach (Type type in types)
		{
			if (!(type.DeclaringType == null) && !(type.DeclaringType.FullName != "LCBetterEXP.GlobalManager") && type.Name.IndexOf("ShowExpGains", StringComparison.Ordinal) >= 0)
			{
				MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "MoveNext", (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					list.Add(methodInfo);
					num++;
				}
			}
		}
		if (num != 1)
		{
			throw new MissingMethodException("Expected exactly one BetterEXP summary iterator.");
		}
		return list;
	}

	private static void Add(List<MethodBase> methods, Assembly assembly, string type, string name)
	{
		Type type2 = assembly.GetType(type, throwOnError: true);
		MethodInfo methodInfo = AccessTools.DeclaredMethod(type2, name, (Type[])null, (Type[])null);
		if (methodInfo == null)
		{
			throw new MissingMethodException(type, name);
		}
		methods.Add(methodInfo);
	}

	public static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> source, MethodBase __originalMethod)
	{
		MethodInfo exact = AccessTools.Method(typeof(Translator), "Exact", (Type[])null, (Type[])null);
		MethodInfo label = AccessTools.Method(typeof(Translator), "ExpLabel", (Type[])null, (Type[])null);
		MethodInfo setter = AccessTools.Method(typeof(ChineseText), "SetText", (Type[])null, (Type[])null);
		bool listing = __originalMethod.Name == "Listing";
		foreach (CodeInstruction instruction in source)
		{
			if (instruction.opcode == OpCodes.Ldstr)
			{
				instruction.operand = Translator.Exact((string)instruction.operand);
			}
			MethodInfo called = instruction.operand as MethodInfo;
			if (called != null && called.Name == "set_text" && called.DeclaringType == typeof(TMP_Text))
			{
				instruction.opcode = OpCodes.Call;
				instruction.operand = setter;
			}
			yield return instruction;
			FieldInfo field = instruction.operand as FieldInfo;
			if (instruction.opcode == OpCodes.Ldfld && field != null && field.FieldType == typeof(string))
			{
				string owner = field.DeclaringType.FullName;
				bool name = field.Name == "name" && (owner == "LCBetterEXP.EXPLevels+EXPLevel" || owner == "LCBetterEXP.NotesList+Note");
				bool unit = listing && field.DeclaringType.IsGenericType && field.DeclaringType.GetGenericTypeDefinition() == typeof(ValueTuple<, >) && (field.Name == "Item1" || field.Name == "Item2");
				if (name || unit)
				{
					yield return new CodeInstruction(OpCodes.Call, (object)exact);
				}
			}
			if (called != null && called.Name == "get_Value" && called.ReturnType == typeof(string) && called.DeclaringType.IsGenericType && called.DeclaringType.GetGenericTypeDefinition() == typeof(ConfigEntry<>))
			{
				yield return new CodeInstruction(OpCodes.Call, (object)label);
			}
		}
	}
}
public static class ChineseText
{
	private static bool warned;

	public static void SetText(TMP_Text text, string value)
	{
		if (!((Object)(object)text == (Object)null))
		{
			if (Translator.ContainsChinese(value))
			{
				EnsureFont(text);
			}
			text.text = value;
		}
	}

	private static void EnsureFont(TMP_Text text)
	{
		List<TMP_FontAsset> fallbackFontAssets = TMP_Settings.fallbackFontAssets;
		if ((Object)(object)text.font == (Object)null || fallbackFontAssets == null)
		{
			return;
		}
		foreach (TMP_FontAsset item in fallbackFontAssets)
		{
			if ((Object)(object)item == (Object)null || !item.HasCharacter('中', false, false))
			{
				continue;
			}
			if (!((Object)(object)item == (Object)(object)text.font))
			{
				List<TMP_FontAsset> list = text.font.fallbackFontAssetTable;
				if (list == null)
				{
					list = (text.font.fallbackFontAssetTable = new List<TMP_FontAsset>());
				}
				if (!list.Contains(item))
				{
					list.Add(item);
				}
			}
			return;
		}
		if (!warned)
		{
			warned = true;
			if (Plugin.Log != null)
			{
				Plugin.Log.LogWarning((object)"No Chinese fallback font is available yet. Check LC Chinese Project font loading.");
			}
		}
	}
}
public static class Translator
{
	private static Dictionary<string, string> entries = new Dictionary<string, string>(StringComparer.Ordinal);

	public static int Count => entries.Count;

	public static void Load(Stream stream)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Expected O, but got Unknown
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Expected O, but got Unknown
		XmlReaderSettings val = new XmlReaderSettings();
		val.DtdProcessing = (DtdProcessing)0;
		val.XmlResolver = null;
		XmlReaderSettings val2 = val;
		XmlDocument val3 = new XmlDocument();
		val3.XmlResolver = null;
		XmlDocument val4 = val3;
		XmlReader val5 = XmlReader.Create(stream, val2);
		try
		{
			val4.Load(val5);
		}
		finally
		{
			((IDisposable)val5)?.Dispose();
		}
		Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
		foreach (XmlNode item in ((XmlNode)val4).SelectNodes("/translations/entry"))
		{
			XmlNode val6 = item;
			string text = ((XmlNode)val6["source"]).InnerText.Replace("\r\n", "\n");
			if (dictionary.ContainsKey(text))
			{
				throw new InvalidDataException("Duplicate translation: " + text);
			}
			dictionary.Add(text, ((XmlNode)val6["target"]).InnerText);
		}
		entries = dictionary;
	}

	public static void LoadEmbedded()
	{
		using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("BetterEXPChinese.zhCN.xml");
		if (stream == null)
		{
			throw new FileNotFoundException("Embedded Chinese translation is missing.");
		}
		Load(stream);
	}

	public static string Exact(string source)
	{
		if (source == null)
		{
			return null;
		}
		if (!entries.TryGetValue(source.Replace("\r\n", "\n"), out var value))
		{
			return source;
		}
		return value;
	}

	public static string ExpLabel(string source)
	{
		switch (source)
		{
		default:
			return source;
		case "EXP":
		case "BXP":
		case "XP":
			return "经验";
		}
	}

	public static bool ContainsChinese(string value)
	{
		if (value == null)
		{
			return false;
		}
		foreach (char c in value)
		{
			if (c >= '㐀' && c <= '鿿')
			{
				return true;
			}
		}
		return false;
	}
}