Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of REPO FontChanger v1.3.0
REPO_FontChanger.dll
Decompiled 2 weeks agousing 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]); } }