Decompiled source of PEAK Breton Translation v1.0.0

BepInEx_Fix_Version/BepInEx_Fix_Version/BepInEx/plugins/com.voc.PeakTxtUpdater.dll

Decompiled 12 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.voc.PeakTxtUpdater")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+c9a16d2e7649da07991b4d6b81adac55a9956bf8")]
[assembly: AssemblyProduct("com.voc.PeakTxtUpdater")]
[assembly: AssemblyTitle("com.voc.PeakTxtUpdater")]
[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.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]
	[Microsoft.CodeAnalysis.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]
	[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 BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace PeakTxtUpdater
{
	[BepInPlugin("com.voc.PeakTxtUpdater", "com.voc.PeakTxtUpdater", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public class Manifest
		{
			public string generated_at { get; set; }

			public string @ref { get; set; }

			public string commit { get; set; }

			public string base_raw_url { get; set; }

			public List<ManifestFile> files { get; set; }
		}

		public class ManifestFile
		{
			public string name { get; set; }

			public string path { get; set; }

			public string sha256 { get; set; }
		}

		private const string DefaultManifestUrl = "https://raw.githubusercontent.com/Vocaloid2048/PEAK-zh-tw-Translation/main/BepInEx/config/zh-tw-voc/Text/manifest.json";

		private const string TestMainifestUrl = "https://raw.githubusercontent.com/Vocaloid2048/PEAK-zh-tw-Translation/test-text-updater/BepInEx/config/zh-tw-voc/Text/manifest.json";

		private static ConfigEntry<bool> configIsEnablePlugin;

		private static string[] updateFilesNameList = new string[12]
		{
			"Achievement.txt", "Book.txt", "Challenge.txt", "Item.txt", "LoadingAndRepeat.txt", "NeedTPMText.txt", "Settings.txt", "UI.txt", "Mod_Glizzy.txt", "Mod_MoreAscents.txt",
			"Mod_PEAK_Invitation_Mod.txt", "Mod_PEAK_Save_Manager.txt"
		};

		internal static ManualLogSource Log;

		public const string Id = "com.voc.PeakTxtUpdater";

		public static string Name => "com.voc.PeakTxtUpdater";

		public static string Version => "1.0.0";

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"PeakTxtUpdater init - reading config.");
			configIsEnablePlugin = ((BaseUnityPlugin)this).Config.Bind<bool>("PeakTxtUpdater", "Is Enable PeakTxtUpdater", true, "若啟用,則允許插件在啟動遊戲時自動更新繁體中文翻譯。");
			if (!configIsEnablePlugin.Value)
			{
				Log.LogInfo((object)"PeakTxtUpdater is DISABLED in config.");
				return;
			}
			string text = Path.Combine(Paths.ConfigPath, "zh-tw-voc", "Text");
			Directory.CreateDirectory(text);
			string path = Path.Combine(text, "Version.txt");
			if (File.Exists(path))
			{
				File.WriteAllText(path, "sr:\"^v.(\\d+).(\\d+).(.+)$\"=v.$1.$2.$3 (夜芷冰繁中翻譯)\nsr:\"^beta.(\\d+).(\\d+).(.+)$\"=beta.$1.$2.$3 (夜芷冰繁中翻譯)\nsr:\"^v(\\d+).(\\d+).(.+)$\"=v$1.$2.$3 (夜芷冰繁中翻譯)\nsr:\"^beta(\\d+).(\\d+).(.+)$\"=beta$1.$2.$3 (夜芷冰繁中翻譯)\n", Encoding.UTF8);
			}
			Task task = RunUpdaterAsync("https://raw.githubusercontent.com/Vocaloid2048/PEAK-zh-tw-Translation/main/BepInEx/config/zh-tw-voc/Text/manifest.json");
			task.ContinueWith(delegate(Task t)
			{
				if (t.IsFaulted)
				{
					Log.LogError((object)$"RunUpdaterAsync failed: {t.Exception}");
				}
			}, TaskScheduler.Default);
		}

		private async Task RunUpdaterAsync(string manifestUrl)
		{
			Log.LogInfo((object)"RunUpdaterAsync Task Started");
			if (Log == null)
			{
				Log = ((BaseUnityPlugin)this).Logger;
			}
			try
			{
				Log.LogInfo((object)("Fetching manifest from " + manifestUrl));
				using HttpClient http = new HttpClient();
				string text = await http.GetStringAsync(manifestUrl);
				if (string.IsNullOrEmpty(text))
				{
					Log.LogWarning((object)"Manifest JSON is empty.");
					return;
				}
				Manifest manifest;
				try
				{
					JsonSerializerSettings val = new JsonSerializerSettings
					{
						MissingMemberHandling = (MissingMemberHandling)0,
						NullValueHandling = (NullValueHandling)1,
						DateParseHandling = (DateParseHandling)0
					};
					manifest = JsonConvert.DeserializeObject<Manifest>(text, val);
					Log.LogInfo((object)"Manifest deserialized");
				}
				catch (JsonException val2)
				{
					JsonException arg = val2;
					Log.LogError((object)$"Failed to deserialize manifest: {arg}");
					if (!string.IsNullOrEmpty(text))
					{
						string text2 = ((text.Length > 1000) ? (text.Substring(0, 1000) + "...") : text);
						Log.LogError((object)("Manifest snippet: " + text2));
					}
					return;
				}
				if (manifest == null)
				{
					Log.LogWarning((object)"Manifest was empty or could not be deserialized.");
					return;
				}
				string localTextDir = Path.Combine(Paths.ConfigPath, "zh-tw-voc", "Text");
				Directory.CreateDirectory(localTextDir);
				foreach (ManifestFile file in manifest.files ?? new List<ManifestFile>())
				{
					if (Array.IndexOf(updateFilesNameList, file.name) == -1)
					{
						continue;
					}
					try
					{
						string localPath = Path.Combine(localTextDir, file.name);
						bool flag = true;
						if (!File.Exists(localPath))
						{
							goto IL_0301;
						}
						if (string.Equals(file.name, "Version.txt", StringComparison.OrdinalIgnoreCase))
						{
							continue;
						}
						string text3 = ComputeFileSha256(localPath);
						if (string.Equals(text3, file.sha256, StringComparison.OrdinalIgnoreCase))
						{
							flag = false;
						}
						else
						{
							Log.LogInfo((object)("Outdated: " + file.name + " (local " + text3 + " vs remote " + file.sha256 + ")"));
						}
						goto IL_0301;
						IL_0301:
						if (flag)
						{
							string text4 = manifest.base_raw_url?.TrimEnd('/') + "/" + file.path;
							Log.LogInfo((object)("Downloading " + file.name + " from " + text4));
							byte[] array = await http.GetByteArrayAsync(text4);
							string text5 = ComputeSha256(array);
							if (!string.Equals(text5, file.sha256, StringComparison.OrdinalIgnoreCase))
							{
								Log.LogWarning((object)("SHA mismatch for " + file.name + ": expected " + file.sha256 + " got " + text5 + ". Skipping write."));
							}
							else
							{
								string text6 = localPath + ".tmp";
								File.WriteAllBytes(text6, array);
								File.Copy(text6, localPath, overwrite: true);
								File.Delete(text6);
								Log.LogInfo((object)("Wrote " + localPath));
							}
						}
					}
					catch (Exception arg2)
					{
						Log.LogError((object)$"Failed to update {file.name}: {arg2}");
					}
				}
				try
				{
					string text7 = Path.Combine(localTextDir, "Version.txt");
					if (File.Exists(text7))
					{
						File.Delete(text7);
					}
					string contents = "##Ref: " + manifest.@ref + "##\n##Commit: " + manifest.commit + "##\n##Generated at: " + manifest.generated_at + "##\n\nsr:\"^v.(\\d+).(\\d+).(.+)$\"=v.$1.$2.$3 (夜芷冰繁中翻譯 - #" + manifest.commit.Substring(0, 6) + ")\nsr:\"^beta.(\\d+).(\\d+).(.+)$\"=beta.$1.$2.$3 (夜芷冰繁中翻譯 - #" + manifest.commit.Substring(0, 6) + ")\nsr:\"^v(\\d+).(\\d+).(.+)$\"=v$1.$2.$3 (夜芷冰繁中翻譯 - #" + manifest.commit.Substring(0, 6) + ")\nsr:\"^beta(\\d+).(\\d+).(.+)$\"=beta$1.$2.$3 (夜芷冰繁中翻譯 - #" + manifest.commit.Substring(0, 6) + ")\n";
					File.WriteAllText(text7, contents, Encoding.UTF8);
					Log.LogInfo((object)("Wrote version info to " + text7));
				}
				catch (Exception arg3)
				{
					Log.LogError((object)$"Failed to write Version.txt: {arg3}");
				}
			}
			catch (Exception arg4)
			{
				Log.LogError((object)$"TxtUpdater failed: {arg4}");
			}
		}

		private static string ComputeFileSha256(string path)
		{
			using FileStream inputStream = File.OpenRead(path);
			using SHA256 sHA = SHA256.Create();
			byte[] array = sHA.ComputeHash(inputStream);
			return BitConverter.ToString(array).Replace("-", "").ToLowerInvariant();
		}

		private static string ComputeSha256(byte[] data)
		{
			using SHA256 sHA = SHA256.Create();
			byte[] array = sHA.ComputeHash(data);
			return BitConverter.ToString(array).Replace("-", "").ToLowerInvariant();
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}

BepInEx_Fix_Version/BepInEx_Fix_Version/BepInEx/plugins/UniversalFontPatcher.dll

Decompiled 12 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("XoFKon")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Replace in-game font with other things")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+d5fa67705b60c049a3e31d66d30cc939edaa91fb")]
[assembly: AssemblyProduct("UniversalFontPatcher")]
[assembly: AssemblyTitle("UniversalFontPatcher")]
[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 UniversalFontPatcher
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "UniversalFontPatcher";

		public const string PLUGIN_NAME = "UniversalFontPatcher";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace FontPatcher
{
	[HarmonyPatch]
	public static class FontLoader
	{
		internal class FontBundle
		{
			public string BundleName;

			public TMP_FontAsset Normal;

			public TMP_FontAsset Transmit;
		}

		internal static List<FontBundle> fontBundles = new List<FontBundle>();

		internal static Regex normalRegex;

		internal static Regex transmitRegex;

		public static void RepatchAllFontsInScene()
		{
			TMP_Text[] array = Object.FindObjectsOfType<TMP_Text>(true);
			foreach (TMP_Text val in array)
			{
				if ((Object)(object)val != (Object)null && (Object)(object)val.font != (Object)null)
				{
					Plugin.LogInfo("[Repatch] " + ((Object)val).name + " using font " + ((Object)val.font).name);
					PatchFontAwake(val.font);
				}
			}
		}

		public static void Load()
		{
			try
			{
				string path = Path.Combine(Paths.ConfigPath);
				string text = Path.Combine(path, Plugin.configFontAssetPath.Value);
				Plugin.LogInfo("Font path: " + text);
				if (!Directory.Exists(text))
				{
					Plugin.LogError("Font assets directory does not exist: " + text);
					return;
				}
				int num = 0;
				int num2 = 0;
				FileInfo[] files = new DirectoryInfo(text).GetFiles("*");
				foreach (FileInfo fileInfo in files)
				{
					try
					{
						AssetBundle val = AssetBundle.LoadFromFile(fileInfo.FullName);
						Plugin.LogInfo("[" + fileInfo.Name + "] loaded");
						FontBundle fontBundle = new FontBundle
						{
							Normal = val.LoadAsset<TMP_FontAsset>("Normal"),
							Transmit = val.LoadAsset<TMP_FontAsset>("Transmit")
						};
						if ((Object)(object)fontBundle.Normal != (Object)null || (Object)(object)fontBundle.Transmit != (Object)null)
						{
							fontBundle.BundleName = fileInfo.Name;
							if ((Object)(object)fontBundle.Normal != (Object)null)
							{
								((Object)fontBundle.Normal).name = fileInfo.Name + "(Normal)";
								Plugin.LogInfo("Normal font found (" + ((Object)fontBundle.Normal).name + ")");
							}
							if ((Object)(object)fontBundle.Transmit != (Object)null)
							{
								((Object)fontBundle.Transmit).name = fileInfo.Name + "(Transmit)";
								Plugin.LogInfo("Transmit font found (" + ((Object)fontBundle.Transmit).name + ")");
							}
							fontBundles.Add(fontBundle);
							num++;
							continue;
						}
						throw new Exception("No recognizable TMP_FontAsset found in bundle");
					}
					catch (Exception ex)
					{
						Plugin.LogError("[" + fileInfo.Name + "] load failed: " + ex.Message);
						num2++;
					}
				}
				normalRegex = new Regex(Plugin.configNormalRegexPattern.Value);
				transmitRegex = new Regex(Plugin.configTransmitRegexPattern.Value);
				Plugin.LogInfo($"{num} fonts loaded, {num2} failed");
				RepatchAllFontsInScene();
			}
			catch (Exception ex2)
			{
				Plugin.LogError(ex2.ToString());
			}
		}

		public static void PatchFontAwake(TMP_FontAsset fontAsset)
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			if ((Object)(object)fontAsset == (Object)null)
			{
				Plugin.LogError("[PatchFontAwake] fontAsset is NULL — skipping.");
				return;
			}
			if ((Object)(object)fontAsset.atlasTexture == (Object)null)
			{
				Plugin.LogError("[Fatal] Font '" + ((Object)fontAsset).name + "' has null atlasTexture — aborting patch to prevent crash.");
				return;
			}
			Plugin.LogInfo($"[FontCheck] {((Object)fontAsset).name}: material={(Object)(object)((TMP_Asset)fontAsset).material != (Object)null}, atlas={(Object)(object)fontAsset.atlasTexture != (Object)null}");
			if ((Object)(object)((TMP_Asset)fontAsset).material == (Object)null)
			{
				Shader val = Shader.Find("TextMeshPro/Distance Field");
				if (!((Object)(object)val != (Object)null))
				{
					Plugin.LogError("[MaterialFix] TMP Shader not found — cannot assign material");
					return;
				}
				((TMP_Asset)fontAsset).material = new Material(val)
				{
					name = ((Object)fontAsset).name + "_Material"
				};
				((TMP_Asset)fontAsset).material.SetTexture("_MainTex", (Texture)(object)fontAsset.atlasTexture);
				((TMP_Asset)fontAsset).material.mainTexture = (Texture)(object)fontAsset.atlasTexture;
				Plugin.LogInfo("[MaterialFix] Created material for " + ((Object)fontAsset).name);
			}
			Material material = ((TMP_Asset)fontAsset).material;
			if ((Object)(object)material != (Object)null)
			{
				if (!material.HasProperty("_MainTex"))
				{
					Plugin.LogWarning("[MaterialCheck] " + ((Object)fontAsset).name + " 材質無 _MainTex");
				}
				if ((Object)(object)material.mainTexture == (Object)null && (Object)(object)fontAsset.atlasTexture != (Object)null)
				{
					material.SetTexture("_MainTex", (Texture)(object)fontAsset.atlasTexture);
					material.mainTexture = (Texture)(object)fontAsset.atlasTexture;
					Plugin.LogInfo("[MainTexFix] Assigned _MainTex from atlas for " + ((Object)fontAsset).name);
				}
				if (material.shaderKeywords == null)
				{
					material.shaderKeywords = Array.Empty<string>();
				}
				if (material.renderQueue < 1000)
				{
					material.renderQueue = 3000;
					Plugin.LogInfo("[RenderQueueFix] Set to 3000 for " + ((Object)fontAsset).name);
				}
				material.SetFloat("_UnderlayDilate", 1f);
				material.SetFloat("_UnderlayOffsetX", 0.1f);
			}
			string name = ((Object)fontAsset).name;
			if (normalRegex.IsMatch(name))
			{
				if (!Plugin.configNormalIngameFont.Value)
				{
					DisableFont(fontAsset);
				}
				AddFallbackFonts(fontAsset, isNormal: true);
			}
			else if (transmitRegex.IsMatch(name))
			{
				if (!Plugin.configTransmitIngameFont.Value)
				{
					DisableFont(fontAsset);
				}
				AddFallbackFonts(fontAsset, isNormal: false);
			}
			else if (fontAsset.characterLookupTable == null || fontAsset.characterLookupTable.Count == 0)
			{
				Plugin.LogWarning("[Disable] Font " + ((Object)fontAsset).name + " has empty lookup table — disabling.");
				DisableFont(fontAsset);
			}
			else
			{
				Plugin.LogWarning("[" + name + "] not matched any regex pattern");
			}
		}

		private static void FixFontAssetMaterial(TMP_FontAsset fontAsset)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			if ((Object)(object)fontAsset == (Object)null)
			{
				return;
			}
			Shader val = Shader.Find("TextMeshPro/Distance Field");
			if ((Object)(object)val == (Object)null)
			{
				Plugin.LogError("[FixMaterial] TMP shader not found!");
				return;
			}
			if ((Object)(object)((TMP_Asset)fontAsset).material == (Object)null)
			{
				((TMP_Asset)fontAsset).material = new Material(val)
				{
					name = ((Object)fontAsset).name + "_Material"
				};
				Plugin.LogInfo("[FixMaterial] Created new material for " + ((Object)fontAsset).name);
			}
			else
			{
				((TMP_Asset)fontAsset).material.shader = val;
				Plugin.LogInfo("[FixMaterial] Reassigned shader for " + ((Object)fontAsset).name);
			}
			if ((Object)(object)fontAsset.atlasTexture != (Object)null)
			{
				((TMP_Asset)fontAsset).material.SetTexture("_MainTex", (Texture)(object)fontAsset.atlasTexture);
				((TMP_Asset)fontAsset).material.mainTexture = (Texture)(object)fontAsset.atlasTexture;
			}
		}

		private static void AddFallbackFonts(TMP_FontAsset target, bool isNormal)
		{
			int num = 0;
			foreach (FontBundle fontBundle in fontBundles)
			{
				TMP_FontAsset val = (isNormal ? fontBundle.Normal : fontBundle.Transmit);
				if (!((Object)(object)val == (Object)null))
				{
					FixFontAssetMaterial(val);
					if ((Object)(object)((TMP_Asset)val).material == (Object)null || (Object)(object)val.atlasTexture == (Object)null || (Object)(object)((TMP_Asset)val).material.mainTexture == (Object)null || !((TMP_Asset)val).material.HasProperty("_MainTex"))
					{
						Plugin.LogWarning("[SkipFallback] " + ((Object)val).name + " is missing required material/texture/_MainTex");
					}
					else if (!target.fallbackFontAssetTable.Contains(val))
					{
						target.fallbackFontAssetTable.Add(val);
						num++;
					}
				}
			}
			if (num > 0)
			{
				Plugin.LogInfo(string.Format("[{0}] font patched ({1}) +{2}", ((Object)target).name, isNormal ? "Normal" : "Transmit", num));
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(TextMeshPro), "Awake")]
		public static void PatchTMPAwake(TextMeshPro __instance)
		{
			if ((Object)(object)((TMP_Text)__instance).font != (Object)null)
			{
				PatchFontAwake(((TMP_Text)__instance).font);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(TextMeshProUGUI), "Awake")]
		public static void PatchTMPUGUIAwake(TextMeshProUGUI __instance)
		{
			if ((Object)(object)((TMP_Text)__instance).font != (Object)null)
			{
				TMP_FontAsset font = ((TMP_Text)__instance).font;
				object[] obj = new object[4]
				{
					((Object)font).name,
					(Object)(object)((TMP_Asset)font).material != (Object)null,
					(Object)(object)font.atlasTexture != (Object)null,
					null
				};
				Material material = ((TMP_Asset)font).material;
				obj[3] = (Object)(object)((material != null) ? material.mainTexture : null) != (Object)null;
				Plugin.LogInfo(string.Format("[TMP Awake] {0} / material={1} / atlas={2} / mainTex={3}", obj));
				PatchFontAwake(font);
			}
			else
			{
				Plugin.LogError("[TMP Awake] font is NULL!");
			}
		}

		private static void DisableFont(TMP_FontAsset font)
		{
			font.characterLookupTable?.Clear();
			font.atlasPopulationMode = (AtlasPopulationMode)0;
		}
	}
	internal static class PluginInfo
	{
		public const string GUID = "universal.fontpatcher";

		public const string Name = "Universal TMP Replacer";

		public const string Version = "1.0.0";
	}
	[BepInPlugin("universal.fontpatcher", "Universal TMP Replacer", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public static ConfigEntry<bool> configNormalIngameFont;

		public static ConfigEntry<bool> configTransmitIngameFont;

		public static ConfigEntry<string> configNormalRegexPattern;

		public static ConfigEntry<string> configTransmitRegexPattern;

		public static ConfigEntry<string> configFontAssetPath;

		public static ConfigEntry<bool> configDebugLog;

		public static Plugin Instance;

		private void Awake()
		{
			if ((Object)(object)Instance == (Object)null)
			{
				Instance = this;
			}
			configNormalIngameFont = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UsingNormalIngameFont", true, "是否啟用遊戲內建的 Normal 字體");
			configTransmitIngameFont = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UsingTransmitIngameFont", true, "是否啟用遊戲內建的 Transmit 字體");
			configNormalRegexPattern = ((BaseUnityPlugin)this).Config.Bind<string>("Regex", "NormalFontNameRegex", ".*", "匹配 Normal 字體名稱的正則表達式");
			configTransmitRegexPattern = ((BaseUnityPlugin)this).Config.Bind<string>("Regex", "TransmitFontNameRegex", ".*", "匹配 Transmit 字體名稱的正則表達式");
			configFontAssetPath = ((BaseUnityPlugin)this).Config.Bind<string>("Path", "FontAssetsPath", "FontPatcher", "字體資源所在的資料夾,位於 <遊戲資料夾>/FontPatcher/<此設定>");
			configDebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Log", false, "是否啟用除錯日誌");
			FontLoader.Load();
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null);
		}

		public static void LogInfo(string msg)
		{
			if (configDebugLog.Value)
			{
				((BaseUnityPlugin)Instance).Logger.LogInfo((object)msg);
			}
		}

		public static void LogWarning(string msg)
		{
			if (configDebugLog.Value)
			{
				((BaseUnityPlugin)Instance).Logger.LogWarning((object)msg);
			}
		}

		public static void LogError(string msg)
		{
			((BaseUnityPlugin)Instance).Logger.LogError((object)msg);
		}
	}
	internal static class ResourcePath
	{
		public const string NormalFont = "Normal";

		public const string TransmitFont = "Transmit";
	}
}