Decompiled source of REPO FontChanger v1.3.0

REPO_FontChanger.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("REPO_FontChanger")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("REPO_FontChanger")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4f1ea24a-de37-4359-ae1c-399da89f0baa")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace REPO_FontChanger;

[BepInPlugin("jp.repofontchanger.plugin", "REPO Font Changer", "1.3.0")]
public class Plugin : BaseUnityPlugin
{
	private class FontFileEntry
	{
		public string FilePath;

		public int FaceIndex;

		public string DisplayName;
	}

	private enum CharKind
	{
		Kanji,
		Kana,
		Alphabet,
		Number,
		Space,
		Other
	}

	private struct ReservedRect
	{
		public int Group;

		public Rect Rect;
	}

	internal static Plugin Instance;

	private Harmony harmony;

	private ConfigEntry<string> selectedFontName;

	private ConfigEntry<float> fontSizeScale;

	private ConfigEntry<bool> includeSystemFonts;

	private ConfigEntry<bool> preventWordBreak;

	private ConfigEntry<bool> fitToBox;

	private ConfigEntry<string> keepSizeObjects;

	private ConfigEntry<bool> debugLogTexts;

	private ConfigEntry<bool> useForKanji;

	private ConfigEntry<bool> useForKana;

	private ConfigEntry<bool> useForAlphabet;

	private ConfigEntry<bool> useForNumbers;

	private ConfigEntry<bool> useForSymbols;

	private readonly Dictionary<int, string> lastLayoutText = new Dictionary<int, string>();

	private readonly Dictionary<int, bool> originalWrap = new Dictionary<int, bool>();

	private float nextWrapCheckTime = 0f;

	private const string NoneFontName = "None";

	private string lastAppliedFontName = null;

	private float lastAppliedFontSize = -1f;

	private string customFontDir;

	private readonly Dictionary<string, FontFileEntry> fontEntries = new Dictionary<string, FontFileEntry>(StringComparer.Ordinal);

	private readonly Dictionary<int, TMP_FontAsset> originalFonts = new Dictionary<int, TMP_FontAsset>();

	private readonly Dictionary<int, Material> originalMaterials = new Dictionary<int, Material>();

	private readonly Dictionary<int, float> originalFontSizes = new Dictionary<int, float>();

	private readonly Dictionary<int, TMP_Text> cachedTexts = new Dictionary<int, TMP_Text>();

	private readonly Dictionary<long, Material> convertedMaterials = new Dictionary<long, Material>();

	private FontFileEntry selectedEntry;

	private readonly Dictionary<int, TMP_FontAsset> fontVariants = new Dictionary<int, TMP_FontAsset>();

	private readonly HashSet<int> ourFontIds = new HashSet<int>();

	private string lastCharFilterKey = "";

	private const int MaxFitsPerPass = 40;

	private readonly Dictionary<int, ReservedRect> originalRects = new Dictionary<int, ReservedRect>();

	private static readonly Regex AbsoluteLayoutTag = new Regex("<\\s*(pos|space|voffset|indent|line-indent|line-height|margin|margin-left|margin-right|mspace|cspace|size)\\s*=\\s*[+-]?\\d+(\\.\\d+)?\\s*(px)?\\s*>", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private readonly HashSet<int> loggedFixedLayout = new HashSet<int>();

	private readonly HashSet<int> loggedDebug = new HashSet<int>();

	private string keepSizeSource = null;

	private string[] keepSizeNames = new string[0];

	private readonly Dictionary<int, float> originalSizeMax = new Dictionary<int, float>();

	private readonly Dictionary<int, float> originalSizeMin = new Dictionary<int, float>();

	private readonly Dictionary<int, bool> compositeCache = new Dictionary<int, bool>();

	private bool FontActive => selectedEntry != null;

	private void Awake()
	{
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Expected O, but got Unknown
		//IL_022e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0238: Expected O, but got Unknown
		//IL_032d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0337: Expected O, but got Unknown
		Instance = this;
		((BaseUnityPlugin)this).Logger.LogInfo((object)"REPO Font Changer v1.3.0 loading...");
		fontSizeScale = ((BaseUnityPlugin)this).Config.Bind<float>("Font Settings", "Font Size", 1f, new ConfigDescription("ゲーム内の文字サイズ倍率", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 1.5f), Array.Empty<object>()));
		includeSystemFonts = ((BaseUnityPlugin)this).Config.Bind<bool>("Font Settings", "Include System Fonts", false, "Windowsの全フォント(C:\\Windows\\Fonts)も一覧に入れます。数が多くなるので通常はオフ推奨です。変更後はゲームの再起動が必要です。");
		preventWordBreak = ((BaseUnityPlugin)this).Config.Bind<bool>("Font Settings", "Prevent Word Break", true, "値段などスペースの無い短い文字が、枠に入りきらず途中で改行されるのを防ぎます。");
		fitToBox = ((BaseUnityPlugin)this).Config.Bind<bool>("Font Settings", "Fit Text To Box", true, "文字を大きくしたとき、元の枠からはみ出す分だけ小さくして、隣の文字と重ならないようにします。");
		useForKanji = ((BaseUnityPlugin)this).Config.Bind<bool>("Font Characters", "Kanji", true, "漢字を選んだフォントで表示します。オフにすると元のゲームフォントのままになります。");
		useForKana = ((BaseUnityPlugin)this).Config.Bind<bool>("Font Characters", "Hiragana Katakana", true, "ひらがな・カタカナ(長音「ー」、半角カナを含む)を選んだフォントで表示します。オフにすると元のゲームフォントのままになります。");
		useForAlphabet = ((BaseUnityPlugin)this).Config.Bind<bool>("Font Characters", "Alphabet", true, "アルファベット(全角を含む)を選んだフォントで表示します。オフにすると元のゲームフォントのままになります。");
		useForNumbers = ((BaseUnityPlugin)this).Config.Bind<bool>("Font Characters", "Numbers", true, "数字(全角を含む)を選んだフォントで表示します。オフにすると元のゲームフォントのままになります。");
		useForSymbols = ((BaseUnityPlugin)this).Config.Bind<bool>("Font Characters", "Symbols", true, "記号($ / ! ? 、など、上のどれにも入らない文字)を選んだフォントで表示します。オフにすると元のゲームフォントのままになります。");
		keepSizeObjects = ((BaseUnityPlugin)this).Config.Bind<string>("Advanced", "Keep Size Objects", "", "文字サイズを変えないオブジェクト名(カンマ区切り、部分一致)。例: Health,Energy");
		debugLogTexts = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced", "Debug Log Texts", false, "オンにすると、文字のオブジェクト名と中身をログ(LogOutput.log)に出します。Keep Size Objectsに書く名前を調べる用です。");
		ScanFonts(includeSystemFonts.Value);
		List<string> list = new List<string> { "None" };
		list.AddRange(fontEntries.Keys.OrderBy<string, string>((string n) => n, StringComparer.OrdinalIgnoreCase));
		selectedFontName = ((BaseUnityPlugin)this).Config.Bind<string>("Font Settings", "Font", "None", new ConfigDescription("使用するフォント。Noneの場合はゲーム標準フォントです。", (AcceptableValueBase)(object)new AcceptableValueList<string>(list.ToArray()), Array.Empty<object>()));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Font folder: " + customFontDir));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Available fonts: " + fontEntries.Count));
		foreach (FontFileEntry item in fontEntries.Values.OrderBy((FontFileEntry v) => v.DisplayName))
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[Font] " + item.DisplayName + " <= " + item.FilePath + " #" + item.FaceIndex));
		}
		harmony = new Harmony("jp.repofontchanger.plugin");
		PatchTMPText();
		SceneManager.sceneLoaded += OnSceneLoaded;
		CreateSelectedFont(selectedFontName.Value);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"REPO Font Changer v1.3.0 loaded!");
	}

	private void ScanFonts(bool includeSystem)
	{
		List<string> list = new List<string>();
		try
		{
			string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			customFontDir = Path.Combine(directoryName, "Fonts");
			Directory.CreateDirectory(customFontDir);
			list.Add(customFontDir);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not create font folder: " + ex.Message));
		}
		try
		{
			string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
			if (!string.IsNullOrEmpty(folderPath))
			{
				list.Add(Path.Combine(folderPath, "Microsoft", "Windows", "Fonts"));
			}
		}
		catch
		{
		}
		if (includeSystem)
		{
			try
			{
				string text = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
				if (string.IsNullOrEmpty(text))
				{
					text = Path.Combine(Environment.GetEnvironmentVariable("WINDIR") ?? "C:\\Windows", "Fonts");
				}
				list.Add(text);
			}
			catch
			{
			}
		}
		foreach (string item in list)
		{
			if (!Directory.Exists(item))
			{
				continue;
			}
			string[] files;
			try
			{
				files = Directory.GetFiles(item, "*.*", SearchOption.AllDirectories);
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not read folder " + item + ": " + ex2.Message));
				continue;
			}
			string[] array = files;
			foreach (string text2 in array)
			{
				string text3 = Path.GetExtension(text2).ToLowerInvariant();
				if (!(text3 != ".ttf") || !(text3 != ".otf") || !(text3 != ".ttc") || !(text3 != ".otc"))
				{
					AddFontFile(text2);
				}
			}
		}
	}

	private void AddFontFile(string file)
	{
		List<KeyValuePair<string, string>> list;
		try
		{
			list = FontNameReader.Read(file);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not read font name: " + file + " (" + ex.Message + ")"));
			list = new List<KeyValuePair<string, string>>
			{
				new KeyValuePair<string, string>(null, null)
			};
		}
		for (int i = 0; i < list.Count; i++)
		{
			string text = list[i].Key;
			string value = list[i].Value;
			if (string.IsNullOrWhiteSpace(text))
			{
				text = Path.GetFileNameWithoutExtension(file);
			}
			string text2 = text.Trim();
			if (!IsRegularStyle(value))
			{
				text2 = text2 + " " + value.Trim();
			}
			text2 = text2.Replace("\"", "").Replace("\n", " ").Replace("\r", "");
			if (!(text2 == "None") && !fontEntries.ContainsKey(text2))
			{
				fontEntries[text2] = new FontFileEntry
				{
					FilePath = file,
					FaceIndex = i,
					DisplayName = text2
				};
			}
		}
	}

	private static bool IsRegularStyle(string style)
	{
		if (string.IsNullOrWhiteSpace(style))
		{
			return true;
		}
		string text = style.Trim().ToLowerInvariant();
		int result;
		switch (text)
		{
		default:
			result = ((text == "r") ? 1 : 0);
			break;
		case "regular":
		case "normal":
		case "book":
		case "roman":
		case "標準":
			result = 1;
			break;
		}
		return (byte)result != 0;
	}

	private void PatchTMPText()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Expected O, but got Unknown
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_018e: Expected O, but got Unknown
		try
		{
			HarmonyMethod val = new HarmonyMethod(typeof(TMPTextHarmonyPatch), "TryAddCharacterPrefix", (Type[])null);
			int num = 0;
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(TMP_FontAsset)))
			{
				if (!(declaredMethod.Name != "TryAddCharacterInternal"))
				{
					ParameterInfo[] parameters = declaredMethod.GetParameters();
					if (parameters.Any((ParameterInfo x) => x.Name == "unicode" && x.ParameterType == typeof(uint)) && parameters.Any((ParameterInfo x) => x.Name == "character" && x.ParameterType == typeof(TMP_Character).MakeByRefType()) && !(declaredMethod.ReturnType != typeof(bool)))
					{
						harmony.Patch((MethodBase)declaredMethod, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						num++;
					}
				}
			}
			if (num > 0)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Harmony patch installed: TMP_FontAsset.TryAddCharacterInternal x" + num));
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"TMP_FontAsset.TryAddCharacterInternal was not found. Character filter will not work.");
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Character filter patch failed: " + ex));
		}
		HarmonyMethod val2 = new HarmonyMethod(typeof(TMPTextHarmonyPatch), "OnEnablePostfix", (Type[])null);
		Type[] array = new Type[2]
		{
			typeof(TextMeshProUGUI),
			typeof(TextMeshPro)
		};
		foreach (Type type in array)
		{
			try
			{
				MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "OnEnable", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)(type.Name + ".OnEnable was not found."));
					continue;
				}
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Harmony patch installed: " + type.Name + ".OnEnable"));
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Harmony patch failed (" + type.Name + "): " + ex2));
			}
		}
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		int num = 0;
		TMP_Text[] array = Resources.FindObjectsOfTypeAll<TMP_Text>();
		foreach (TMP_Text val in array)
		{
			if (!((Object)(object)val == (Object)null))
			{
				Scene scene2 = ((Component)val).gameObject.scene;
				if (((Scene)(ref scene2)).IsValid())
				{
					ApplyFontToText(val);
					num++;
				}
			}
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Scene text initialized: " + num));
	}

	private void CreateSelectedFont(string fontName)
	{
		lastAppliedFontName = fontName;
		lastCharFilterKey = GetCharFilterKey();
		List<TMP_FontAsset> list = fontVariants.Values.ToList();
		List<Material> list2 = convertedMaterials.Values.ToList();
		fontVariants.Clear();
		convertedMaterials.Clear();
		ourFontIds.Clear();
		selectedEntry = null;
		if (fontName != "None")
		{
			if (fontEntries.TryGetValue(fontName, out var value))
			{
				selectedEntry = value;
				if ((Object)(object)GetVariant(null) == (Object)null)
				{
					selectedEntry = null;
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not load font. Using original font: " + fontName));
				}
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Font not found in list: " + fontName));
			}
		}
		ApplyFontToCachedTexts();
		foreach (Material item in list2)
		{
			if ((Object)(object)item != (Object)null)
			{
				Object.Destroy((Object)(object)item);
			}
		}
		foreach (TMP_FontAsset item2 in list)
		{
			if ((Object)(object)item2 != (Object)null)
			{
				Object.Destroy((Object)(object)item2);
			}
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)(FontActive ? ("Font loaded: " + fontName + " (" + DescribeCharFilter() + ")") : "Using original game font."));
	}

	private TMP_FontAsset GetVariant(TMP_FontAsset originalFont)
	{
		if (selectedEntry == null)
		{
			return null;
		}
		int key = (((Object)(object)originalFont != (Object)null) ? ((Object)originalFont).GetInstanceID() : 0);
		if (fontVariants.TryGetValue(key, out var value) && (Object)(object)value != (Object)null)
		{
			return value;
		}
		value = TryCreateFont(selectedEntry);
		if ((Object)(object)value == (Object)null)
		{
			return null;
		}
		if ((Object)(object)originalFont != (Object)null)
		{
			((Object)value).name = "REPOFontChanger_" + selectedEntry.DisplayName + "_" + ((Object)originalFont).name;
			value.fallbackFontAssetTable.Add(originalFont);
		}
		fontVariants[key] = value;
		ourFontIds.Add(((Object)value).GetInstanceID());
		return value;
	}

	private TMP_FontAsset TryCreateFont(FontFileEntry entry)
	{
		try
		{
			TMP_FontAsset val = TMP_FontAsset.CreateFontAsset(entry.FilePath, entry.FaceIndex, 90, 9, (GlyphRenderMode)4165, 2048, 2048);
			if ((Object)(object)val == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("TMP Font creation returned NULL: " + entry.FilePath));
				return null;
			}
			((Object)val).name = "REPOFontChanger_" + entry.DisplayName;
			val.atlasPopulationMode = (AtlasPopulationMode)1;
			val.isMultiAtlasTexturesEnabled = true;
			if (val.fallbackFontAssetTable == null)
			{
				val.fallbackFontAssetTable = new List<TMP_FontAsset>();
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("TMP Font created: " + ((Object)val).name));
			return val;
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Font loading failed: " + ex));
			return null;
		}
	}

	private string GetCharFilterKey()
	{
		return (useForKanji.Value ? "1" : "0") + (useForKana.Value ? "1" : "0") + (useForAlphabet.Value ? "1" : "0") + (useForNumbers.Value ? "1" : "0") + (useForSymbols.Value ? "1" : "0");
	}

	private string DescribeCharFilter()
	{
		return "漢字:" + (useForKanji.Value ? "ON" : "OFF") + " かな:" + (useForKana.Value ? "ON" : "OFF") + " 英字:" + (useForAlphabet.Value ? "ON" : "OFF") + " 数字:" + (useForNumbers.Value ? "ON" : "OFF") + " 記号:" + (useForSymbols.Value ? "ON" : "OFF");
	}

	internal bool IsOurFont(TMP_FontAsset font)
	{
		return (Object)(object)font != (Object)null && ourFontIds.Contains(((Object)font).GetInstanceID());
	}

	internal bool IsCharAllowed(uint c)
	{
		return GetCharKind(c) switch
		{
			CharKind.Kanji => useForKanji.Value, 
			CharKind.Kana => useForKana.Value, 
			CharKind.Alphabet => useForAlphabet.Value, 
			CharKind.Number => useForNumbers.Value, 
			CharKind.Space => true, 
			_ => useForSymbols.Value, 
		};
	}

	private static CharKind GetCharKind(uint c)
	{
		if ((c >= 48 && c <= 57) || (c >= 65296 && c <= 65305))
		{
			return CharKind.Number;
		}
		if ((c >= 65 && c <= 90) || (c >= 97 && c <= 122) || (c >= 65313 && c <= 65338) || (c >= 65345 && c <= 65370) || (c >= 192 && c <= 591 && c != 215 && c != 247))
		{
			return CharKind.Alphabet;
		}
		if ((c >= 12353 && c <= 12447) || (c >= 12448 && c <= 12543) || (c >= 12784 && c <= 12799) || (c >= 65382 && c <= 65439))
		{
			return CharKind.Kana;
		}
		if ((c >= 19968 && c <= 40959) || (c >= 13312 && c <= 19903) || (c >= 63744 && c <= 64255) || (c >= 131072 && c <= 201551) || c == 12293 || c == 12294 || c == 12295)
		{
			return CharKind.Kanji;
		}
		if (c == 32 || c == 12288 || c == 9 || c == 160)
		{
			return CharKind.Space;
		}
		return CharKind.Other;
	}

	private Material GetConvertedMaterial(Material original, TMP_FontAsset font)
	{
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Expected O, but got Unknown
		if ((Object)(object)font == (Object)null)
		{
			return null;
		}
		Material material = ((TMP_Asset)font).material;
		if ((Object)(object)original == (Object)null || (Object)(object)material == (Object)null)
		{
			return material;
		}
		long key = ((long)((Object)font).GetInstanceID() << 32) ^ (uint)((Object)original).GetInstanceID();
		if (convertedMaterials.TryGetValue(key, out var value) && (Object)(object)value != (Object)null)
		{
			return value;
		}
		Material val;
		try
		{
			val = new Material(original);
			((Object)val).name = ((Object)original).name + " (REPOFontChanger)";
			Texture texture = material.GetTexture(ShaderUtilities.ID_MainTex);
			val.SetTexture(ShaderUtilities.ID_MainTex, texture);
			if (val.HasProperty(ShaderUtilities.ID_TextureWidth))
			{
				val.SetFloat(ShaderUtilities.ID_TextureWidth, (float)font.atlasWidth);
			}
			if (val.HasProperty(ShaderUtilities.ID_TextureHeight))
			{
				val.SetFloat(ShaderUtilities.ID_TextureHeight, (float)font.atlasHeight);
			}
			if (val.HasProperty(ShaderUtilities.ID_GradientScale))
			{
				val.SetFloat(ShaderUtilities.ID_GradientScale, (float)(font.atlasPadding + 1));
			}
			if (val.HasProperty(ShaderUtilities.ID_WeightNormal))
			{
				val.SetFloat(ShaderUtilities.ID_WeightNormal, font.normalStyle);
			}
			if (val.HasProperty(ShaderUtilities.ID_WeightBold))
			{
				val.SetFloat(ShaderUtilities.ID_WeightBold, font.boldStyle);
			}
		}
		catch
		{
			val = material;
		}
		convertedMaterials[key] = val;
		return val;
	}

	internal void ApplyFontToText(TMP_Text text)
	{
		if ((Object)(object)text == (Object)null)
		{
			return;
		}
		try
		{
			int instanceID = ((Object)text).GetInstanceID();
			if (!originalFonts.ContainsKey(instanceID))
			{
				originalFonts[instanceID] = text.font;
				originalMaterials[instanceID] = text.fontSharedMaterial;
				originalFontSizes[instanceID] = text.fontSize;
			}
			cachedTexts[instanceID] = text;
			ApplyFontToSingleText(text, instanceID);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Text update failed: " + ex.Message));
		}
	}

	private void ApplyFontToSingleText(TMP_Text text, int id)
	{
		if ((Object)(object)text == (Object)null)
		{
			return;
		}
		originalFonts.TryGetValue(id, out var value);
		originalMaterials.TryGetValue(id, out var value2);
		TMP_FontAsset val = (FontActive ? GetVariant(value) : null);
		if ((Object)(object)val != (Object)null)
		{
			if ((Object)(object)text.font != (Object)(object)val)
			{
				text.font = val;
			}
			Material convertedMaterial = GetConvertedMaterial(value2, val);
			if ((Object)(object)convertedMaterial != (Object)null && (Object)(object)text.fontSharedMaterial != (Object)(object)convertedMaterial)
			{
				text.fontSharedMaterial = convertedMaterial;
			}
		}
		else
		{
			if ((Object)(object)value != (Object)null && (Object)(object)text.font != (Object)(object)value)
			{
				text.font = value;
			}
			if ((Object)(object)value2 != (Object)null && (Object)(object)text.fontSharedMaterial != (Object)(object)value2)
			{
				text.fontSharedMaterial = value2;
			}
		}
		ApplySize(text, id, full: false);
		((Graphic)text).SetAllDirty();
	}

	private void ApplyFontToCachedTexts()
	{
		List<int> list = new List<int>();
		foreach (KeyValuePair<int, TMP_Text> cachedText in cachedTexts)
		{
			if ((Object)(object)cachedText.Value == (Object)null)
			{
				list.Add(cachedText.Key);
				continue;
			}
			try
			{
				ApplyFontToSingleText(cachedText.Value, cachedText.Key);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Cached text update failed: " + ex.Message));
			}
		}
		foreach (int item in list)
		{
			cachedTexts.Remove(item);
			originalFonts.Remove(item);
			originalMaterials.Remove(item);
			originalFontSizes.Remove(item);
			originalWrap.Remove(item);
			lastLayoutText.Remove(item);
			originalRects.Remove(item);
			originalSizeMax.Remove(item);
			originalSizeMin.Remove(item);
			compositeCache.Remove(item);
		}
		lastAppliedFontSize = fontSizeScale.Value;
	}

	private void Update()
	{
		if (selectedFontName.Value != lastAppliedFontName || GetCharFilterKey() != lastCharFilterKey)
		{
			CreateSelectedFont(selectedFontName.Value);
		}
		else if (!Mathf.Approximately(fontSizeScale.Value, lastAppliedFontSize))
		{
			ApplyFontToCachedTexts();
		}
		if (Time.unscaledTime >= nextWrapCheckTime)
		{
			nextWrapCheckTime = Time.unscaledTime + 0.1f;
			UpdateTextLayout();
		}
	}

	private bool IsKeepSizeObject(Transform t)
	{
		if (keepSizeSource != keepSizeObjects.Value)
		{
			keepSizeSource = keepSizeObjects.Value;
			keepSizeNames = (from x in (keepSizeSource ?? "").Split(new char[1] { ',' })
				select x.Trim() into x
				where x.Length > 0
				select x).ToArray();
		}
		if (keepSizeNames.Length == 0)
		{
			return false;
		}
		int num = 0;
		while ((Object)(object)t != (Object)null && num < 4)
		{
			string[] array = keepSizeNames;
			foreach (string value in array)
			{
				if (((Object)t).name.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			num++;
			t = t.parent;
		}
		return false;
	}

	private bool IsCompositeText(TMP_Text text, int id)
	{
		if (compositeCache.TryGetValue(id, out var value))
		{
			return value;
		}
		bool flag = false;
		Transform transform = text.transform;
		if ((Object)(object)transform.parent != (Object)null && (Object)(object)((Component)transform.parent).GetComponent<TMP_Text>() != (Object)null)
		{
			flag = true;
		}
		if (!flag)
		{
			for (int i = 0; i < transform.childCount; i++)
			{
				if ((Object)(object)((Component)transform.GetChild(i)).GetComponent<TMP_Text>() != (Object)null)
				{
					flag = true;
					break;
				}
			}
		}
		compositeCache[id] = flag;
		return flag;
	}

	private static string GetPath(Transform t)
	{
		string text = ((Object)t).name;
		for (int i = 0; i < 3; i++)
		{
			if (!((Object)(object)t.parent != (Object)null))
			{
				break;
			}
			t = t.parent;
			text = ((Object)t).name + "/" + text;
		}
		return text;
	}

	private void ApplySize(TMP_Text text, int id, bool full)
	{
		if (!originalFontSizes.TryGetValue(id, out var value))
		{
			return;
		}
		if (full && debugLogTexts.Value && !string.IsNullOrEmpty(text.text) && loggedDebug.Add(id))
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[Text] " + GetPath(text.transform) + " | size " + value + " -> " + text.fontSize + " | " + text.text.Replace("\n", "\\n")));
		}
		if (text.enableAutoSizing)
		{
			if (!originalSizeMax.ContainsKey(id))
			{
				originalSizeMax[id] = text.fontSizeMax;
				originalSizeMin[id] = text.fontSizeMin;
			}
			float num = fontSizeScale.Value;
			if (num > 1f && fitToBox.Value && IsCompositeText(text, id))
			{
				num = 1f;
			}
			float num2 = originalSizeMax[id] * num;
			float num3 = originalSizeMin[id] * num;
			if (!Mathf.Approximately(text.fontSizeMax, num2))
			{
				text.fontSizeMax = num2;
			}
			if (!Mathf.Approximately(text.fontSizeMin, num3))
			{
				text.fontSizeMin = num3;
			}
			lastLayoutText[id] = text.text ?? "";
			return;
		}
		float num4;
		if (!full && fitToBox.Value && fontSizeScale.Value > 1f)
		{
			num4 = value;
			lastLayoutText.Remove(id);
		}
		else
		{
			num4 = ComputeTargetSize(text, value);
			if (fitToBox.Value)
			{
				num4 = FitAvoidOverlap(text, id, value, num4);
			}
			lastLayoutText[id] = text.text ?? "";
		}
		if (!Mathf.Approximately(text.fontSize, num4))
		{
			text.fontSize = num4;
		}
		if (full)
		{
			((Graphic)text).SetAllDirty();
		}
	}

	private static Transform GetGroupTransform(TMP_Text text)
	{
		TextMeshProUGUI val = (TextMeshProUGUI)(object)((text is TextMeshProUGUI) ? text : null);
		if ((Object)(object)val != (Object)null && (Object)(object)((Graphic)val).canvas != (Object)null)
		{
			return ((Component)((Graphic)val).canvas.rootCanvas).transform;
		}
		return text.transform.parent;
	}

	private static bool TryGetBounds(TMP_Text t, Transform space, out Rect rect)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: 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_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: Unknown result type (might be due to invalid IL or missing references)
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_018a: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: Unknown result type (might be due to invalid IL or missing references)
		rect = default(Rect);
		Bounds textBounds = t.textBounds;
		if (((Bounds)(ref textBounds)).size.x <= 0f || ((Bounds)(ref textBounds)).size.y <= 0f)
		{
			return false;
		}
		Vector3[] array = (Vector3[])(object)new Vector3[4]
		{
			new Vector3(((Bounds)(ref textBounds)).min.x, ((Bounds)(ref textBounds)).min.y, 0f),
			new Vector3(((Bounds)(ref textBounds)).min.x, ((Bounds)(ref textBounds)).max.y, 0f),
			new Vector3(((Bounds)(ref textBounds)).max.x, ((Bounds)(ref textBounds)).min.y, 0f),
			new Vector3(((Bounds)(ref textBounds)).max.x, ((Bounds)(ref textBounds)).max.y, 0f)
		};
		float num = float.MaxValue;
		float num2 = float.MaxValue;
		float num3 = float.MinValue;
		float num4 = float.MinValue;
		Vector3[] array2 = array;
		foreach (Vector3 val in array2)
		{
			Vector3 val2 = space.InverseTransformPoint(t.transform.TransformPoint(val));
			num = Mathf.Min(num, val2.x);
			num3 = Mathf.Max(num3, val2.x);
			num2 = Mathf.Min(num2, val2.y);
			num4 = Mathf.Max(num4, val2.y);
		}
		rect = Rect.MinMaxRect(num, num2, num3, num4);
		return true;
	}

	private static bool OverlapsAny(Rect r, List<Rect> others)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		foreach (Rect other in others)
		{
			if (((Rect)(ref r)).Overlaps(other))
			{
				return true;
			}
		}
		return false;
	}

	private float FitAvoidOverlap(TMP_Text text, int id, float originalSize, float size)
	{
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: 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_0246: Unknown result type (might be due to invalid IL or missing references)
		//IL_0198: Unknown result type (might be due to invalid IL or missing references)
		if (string.IsNullOrEmpty(text.text))
		{
			return size;
		}
		Transform groupTransform = GetGroupTransform(text);
		if ((Object)(object)groupTransform == (Object)null)
		{
			return size;
		}
		int instanceID = ((Object)groupTransform).GetInstanceID();
		float fontSize = text.fontSize;
		try
		{
			text.fontSize = originalSize;
			text.ForceMeshUpdate(false, false);
			if (!TryGetBounds(text, groupTransform, out var rect))
			{
				return size;
			}
			originalRects[id] = new ReservedRect
			{
				Group = instanceID,
				Rect = rect
			};
			if (size <= originalSize + 0.01f)
			{
				return size;
			}
			List<Rect> list = new List<Rect>();
			foreach (KeyValuePair<int, TMP_Text> cachedText in cachedTexts)
			{
				if (cachedText.Key == id)
				{
					continue;
				}
				TMP_Text value = cachedText.Value;
				if (!((Object)(object)value == (Object)null) && ((Behaviour)value).isActiveAndEnabled && !string.IsNullOrEmpty(value.text))
				{
					Rect rect2;
					if (originalRects.TryGetValue(cachedText.Key, out var value2) && value2.Group == instanceID)
					{
						rect2 = value2.Rect;
					}
					else if ((Object)(object)GetGroupTransform(value) != (Object)(object)groupTransform || !TryGetBounds(value, groupTransform, out rect2))
					{
						continue;
					}
					if (!((Rect)(ref rect2)).Overlaps(rect))
					{
						list.Add(rect2);
					}
				}
			}
			if (list.Count == 0)
			{
				return size;
			}
			text.fontSize = size;
			text.ForceMeshUpdate(false, false);
			if (TryGetBounds(text, groupTransform, out var rect3) && !OverlapsAny(rect3, list))
			{
				return size;
			}
			float num = originalSize;
			float num2 = size;
			for (int i = 0; i < 6; i++)
			{
				float num3 = (text.fontSize = (num + num2) * 0.5f);
				text.ForceMeshUpdate(false, false);
				if (TryGetBounds(text, groupTransform, out var rect4) && OverlapsAny(rect4, list))
				{
					num2 = num3;
				}
				else
				{
					num = num3;
				}
			}
			return num;
		}
		finally
		{
			text.fontSize = fontSize;
		}
	}

	private float ComputeTargetSize(TMP_Text text, float originalSize)
	{
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		//IL_019f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
		float num = originalSize * fontSizeScale.Value;
		if (!fitToBox.Value || num <= originalSize || text.enableAutoSizing)
		{
			return num;
		}
		string text2 = text.text;
		if (string.IsNullOrEmpty(text2))
		{
			return num;
		}
		if (IsKeepSizeObject(text.transform))
		{
			return originalSize;
		}
		if (IsCompositeText(text, ((Object)text).GetInstanceID()))
		{
			return originalSize;
		}
		if (AbsoluteLayoutTag.IsMatch(text2))
		{
			int instanceID = ((Object)text).GetInstanceID();
			if (loggedFixedLayout.Add(instanceID))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[Keep size] " + ((Object)text).name + ": " + text2));
			}
			return originalSize;
		}
		Rect rect = text.rectTransform.rect;
		Vector4 margin = text.margin;
		float num2 = ((Rect)(ref rect)).width - margin.x - margin.z;
		float num3 = ((Rect)(ref rect)).height - margin.y - margin.w;
		bool enableWordWrapping = text.enableWordWrapping;
		float fontSize = text.fontSize;
		Vector2 preferredValues;
		try
		{
			text.fontSize = num;
			preferredValues = text.GetPreferredValues(text2, (enableWordWrapping && num2 > 0f) ? num2 : 100000f, 100000f);
		}
		finally
		{
			text.fontSize = fontSize;
		}
		if (preferredValues.x <= 0f || preferredValues.y <= 0f)
		{
			return num;
		}
		float num4 = originalSize / num;
		float num5 = Mathf.Max(num2, preferredValues.x * num4);
		float num6 = Mathf.Max(num3, preferredValues.y * num4);
		float num7 = Mathf.Min(1f, Mathf.Min(num5 / preferredValues.x, num6 / preferredValues.y));
		return Mathf.Max(originalSize, num * num7);
	}

	private void UpdateTextLayout()
	{
		bool flag = preventWordBreak.Value && (FontActive || fontSizeScale.Value > 1f);
		int num = 0;
		foreach (KeyValuePair<int, TMP_Text> cachedText in cachedTexts)
		{
			TMP_Text value = cachedText.Value;
			if ((Object)(object)value == (Object)null || !((Behaviour)value).isActiveAndEnabled)
			{
				continue;
			}
			int key = cachedText.Key;
			bool flag2 = false;
			bool value2;
			bool flag3 = originalWrap.TryGetValue(key, out value2);
			if (flag && ShouldKeepOneLine(value.text))
			{
				if (!flag3 && value.enableWordWrapping)
				{
					originalWrap[key] = true;
					flag3 = true;
				}
				if (flag3 && value.enableWordWrapping)
				{
					value.enableWordWrapping = false;
					flag2 = true;
				}
			}
			else if (flag3)
			{
				value.enableWordWrapping = value2;
				originalWrap.Remove(key);
				flag2 = true;
			}
			lastLayoutText.TryGetValue(key, out var value3);
			if ((flag2 || value3 != (value.text ?? "")) && num < 40)
			{
				num++;
				try
				{
					ApplySize(value, key, full: true);
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Size update failed: " + ex.Message));
				}
			}
		}
	}

	private static bool ShouldKeepOneLine(string s)
	{
		if (string.IsNullOrEmpty(s) || s.Length > 24)
		{
			return false;
		}
		string text = Regex.Replace(s, "<[^>]*>", "");
		string text2 = text;
		foreach (char c in text2)
		{
			if (char.IsWhiteSpace(c))
			{
				return false;
			}
		}
		return text.Length > 0;
	}

	private void OnDestroy()
	{
		SceneManager.sceneLoaded -= OnSceneLoaded;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		foreach (Material value in convertedMaterials.Values)
		{
			if ((Object)(object)value != (Object)null)
			{
				Object.Destroy((Object)(object)value);
			}
		}
		foreach (TMP_FontAsset value2 in fontVariants.Values)
		{
			if ((Object)(object)value2 != (Object)null)
			{
				Object.Destroy((Object)(object)value2);
			}
		}
		Instance = null;
	}
}
public static class TMPTextHarmonyPatch
{
	public static bool TryAddCharacterPrefix(TMP_FontAsset __instance, uint unicode, ref TMP_Character character, ref bool __result)
	{
		Plugin instance = Plugin.Instance;
		if ((Object)(object)instance == (Object)null || !instance.IsOurFont(__instance))
		{
			return true;
		}
		if (instance.IsCharAllowed(unicode))
		{
			return true;
		}
		character = null;
		__result = false;
		return false;
	}

	public static void OnEnablePostfix(TMP_Text __instance)
	{
		if (!((Object)(object)Plugin.Instance == (Object)null) && !((Object)(object)__instance == (Object)null))
		{
			Plugin.Instance.ApplyFontToText(__instance);
		}
	}
}
internal static class FontNameReader
{
	public static List<KeyValuePair<string, string>> Read(string path)
	{
		List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
		using (FileStream input = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
		{
			using BinaryReader br = new BinaryReader(input);
			uint num = ReadU32(br);
			if (num == 1953784678)
			{
				ReadU32(br);
				uint num2 = ReadU32(br);
				if (num2 > 64)
				{
					num2 = 64u;
				}
				uint[] array = new uint[num2];
				for (int i = 0; i < num2; i++)
				{
					array[i] = ReadU32(br);
				}
				uint[] array2 = array;
				foreach (uint num3 in array2)
				{
					list.Add(ReadFace(br, num3));
				}
			}
			else
			{
				list.Add(ReadFace(br, 0L));
			}
		}
		return list;
	}

	private static KeyValuePair<string, string> ReadFace(BinaryReader br, long faceOffset)
	{
		br.BaseStream.Position = faceOffset + 4;
		ushort num = ReadU16(br);
		br.BaseStream.Position = faceOffset + 12;
		long num2 = -1L;
		for (int i = 0; i < num; i++)
		{
			uint num3 = ReadU32(br);
			ReadU32(br);
			uint num4 = ReadU32(br);
			ReadU32(br);
			if (num3 == 1851878757)
			{
				num2 = num4;
			}
		}
		if (num2 < 0)
		{
			return new KeyValuePair<string, string>(null, null);
		}
		br.BaseStream.Position = num2;
		ReadU16(br);
		ushort num5 = ReadU16(br);
		ushort num6 = ReadU16(br);
		List<int[]> list = new List<int[]>();
		for (int j = 0; j < num5; j++)
		{
			list.Add(new int[6]
			{
				ReadU16(br),
				ReadU16(br),
				ReadU16(br),
				ReadU16(br),
				ReadU16(br),
				ReadU16(br)
			});
		}
		long strBase = num2 + num6;
		string key = Pick(br, strBase, list, 16) ?? Pick(br, strBase, list, 1);
		string value = Pick(br, strBase, list, 17) ?? Pick(br, strBase, list, 2);
		return new KeyValuePair<string, string>(key, value);
	}

	private static string Pick(BinaryReader br, long strBase, List<int[]> records, int nameId)
	{
		int[] array = null;
		int num = -1;
		foreach (int[] record in records)
		{
			if (record[3] != nameId)
			{
				continue;
			}
			int num2;
			if (record[0] == 3 || record[0] == 0)
			{
				num2 = ((record[2] == 1041) ? 3 : ((record[2] != 1033) ? 1 : 2));
			}
			else
			{
				if (record[0] != 1 || record[1] != 0)
				{
					continue;
				}
				num2 = 0;
			}
			if (num2 > num)
			{
				num = num2;
				array = record;
			}
		}
		if (array == null)
		{
			return null;
		}
		br.BaseStream.Position = strBase + array[5];
		byte[] bytes = br.ReadBytes(array[4]);
		string text = ((array[0] == 3 || array[0] == 0) ? Encoding.BigEndianUnicode.GetString(bytes) : Encoding.ASCII.GetString(bytes));
		text = text.Trim('\0', ' ');
		return (text.Length == 0) ? null : text;
	}

	private static ushort ReadU16(BinaryReader br)
	{
		byte[] array = br.ReadBytes(2);
		return (ushort)((array[0] << 8) | array[1]);
	}

	private static uint ReadU32(BinaryReader br)
	{
		byte[] array = br.ReadBytes(4);
		return (uint)((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]);
	}
}