using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.TextCore.LowLevel; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("ValheimVietnameseFont")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+9ace735278ffd89e89db0466074df1844798748e")] [assembly: AssemblyProduct("ValheimVietnameseFont")] [assembly: AssemblyTitle("ValheimVietnameseFont")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 ValheimVietnameseFont { [BepInPlugin("dev.valheim-vn.font-fallback", "Valheim Vietnamese Font Fallback", "0.2.11")] public sealed class VietnameseFontPlugin : BaseUnityPlugin { public const string PluginId = "dev.valheim-vn.font-fallback"; public const string PluginName = "Valheim Vietnamese Font Fallback"; public const string PluginVersion = "0.2.11"; private const string VietnameseCharacters = "ÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴÈÉẸẺẼÊỀẾỆỂỄÌÍỊỈĨÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠÙÚỤỦŨƯỪỨỰỬỮỲÝỴỶỸĐàáạảãâầấậẩẫăằắặẳẵèéẹẻẽêềếệểễìíịỉĩòóọỏõôồốộổỗơờớợởỡùúụủũưừứựửữỳýỵỷỹđ"; private TMP_FontAsset _customRegular; private TMP_FontAsset _customBold; private string _averiaSansPath; private string _averiaSerifPath; private TMP_FontAsset _sansFallback; private TMP_FontAsset _serifFallback; private readonly HashSet _patchedAveriaAssets = new HashSet(); private readonly Dictionary _regularMaterials = new Dictionary(); private readonly Dictionary _boldMaterials = new Dictionary(); private bool _installed; private void Awake() { ((MonoBehaviour)this).StartCoroutine(InstallWhenResourcesAreReady()); } private IEnumerator InstallWhenResourcesAreReady() { for (int attempt = 1; attempt <= 120; attempt++) { if (TryInstall()) { yield break; } yield return null; } ((BaseUnityPlugin)this).Logger.LogError((object)"Could not find Valheim's embedded Noto fonts after 120 frames."); } private bool TryInstall() { if (_installed) { return true; } Font[] source = Resources.FindObjectsOfTypeAll(); Font val = ((IEnumerable)source).FirstOrDefault((Func)((Font font) => ((Object)font).name == "NotoSans-Regular")); Font val2 = ((IEnumerable)source).FirstOrDefault((Func)((Font font) => ((Object)font).name == "NotoSerif-Regular")); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return false; } string directoryName = Path.GetDirectoryName(typeof(VietnameseFontPlugin).Assembly.Location); _averiaSansPath = Path.Combine(directoryName, "ValheimVN-Sans-Regular.ttf"); _averiaSerifPath = Path.Combine(directoryName, "ValheimVN-Serif-Regular.ttf"); if (!File.Exists(_averiaSansPath) || !File.Exists(_averiaSerifPath)) { ((BaseUnityPlugin)this).Logger.LogError((object)"The bundled Vietnamese-complete Averia source files are missing; the original font assets cannot be populated safely."); return true; } _sansFallback = CreateFallback(val, "ValheimVN-NotoSans-Fallback"); _serifFallback = CreateFallback(val2, "ValheimVN-NotoSerif-Fallback"); if ((Object)(object)_sansFallback == (Object)null || (Object)(object)_serifFallback == (Object)null) { ((BaseUnityPlugin)this).Logger.LogError((object)"TextMeshPro could not create the embedded Noto safety fallback assets."); return true; } string text = Path.Combine(directoryName, "SVN-Norse Regular.otf"); string text2 = Path.Combine(directoryName, "SVN-Norse Bold.otf"); if (File.Exists(text) && File.Exists(text2)) { _customRegular = CreateFallback(text, "ValheimVN-SVN-Norse-Regular"); _customBold = CreateFallback(text2, "ValheimVN-SVN-Norse-Bold"); if ((Object)(object)_customRegular == (Object)null || (Object)(object)_customBold == (Object)null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"SVN-Norse was found but TextMeshPro could not load it; using bundled Noto fonts."); _customRegular = null; _customBold = null; } } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Optional SVN-Norse fonts were not found; using Valheim's bundled Noto fonts."); } EnableFallbackMaterialPresetMatching(); AddGlobalFallback(_sansFallback); if ((Object)(object)_customRegular != (Object)null) { AddAssetFallback(_customRegular, _sansFallback); AddAssetFallback(_customBold, _sansFallback); } int num = PatchLoadedFontAssets(); int num2 = ReplaceLoadedFonts(); ((MonoBehaviour)this).StartCoroutine(PatchFontsAsTheyLoad()); string text3 = "Norse=" + (((Object)(object)_customRegular == (Object)null) ? "original" : "SVN-Norse Regular/Bold") + ", AveriaSans=original asset populated from patched bundled source, AveriaSerif=original asset populated from patched bundled source, Noto safety fallback"; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Vietnamese font fallback ready with " + text3 + "; preloaded " + string.Format("{0} characters, patched {1} fallback tables, ", "ÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴÈÉẸẺẼÊỀẾỆỂỄÌÍỊỈĨÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠÙÚỤỦŨƯỪỨỰỬỮỲÝỴỶỸĐàáạảãâầấậẩẫăằắặẳẵèéẹẻẽêềếệểễìíịỉĩòóọỏõôồốộổỗơờớợởỡùúụủũưừứựửữỳýỵỷỹđ".Length, num) + $"and replaced {num2} loaded text components.")); _installed = true; return true; } private TMP_FontAsset CreateFallback(string fontPath, string assetName) { TMP_FontAsset asset = TMP_FontAsset.CreateFontAsset(fontPath, 0, 64, 8, (GlyphRenderMode)4165, 2048, 2048); return PrepareFallback(asset, assetName); } private TMP_FontAsset CreateFallback(Font source, string assetName) { TMP_FontAsset asset = TMP_FontAsset.CreateFontAsset(source, 64, 8, (GlyphRenderMode)4165, 2048, 2048, (AtlasPopulationMode)1, true); return PrepareFallback(asset, assetName); } private TMP_FontAsset PrepareFallback(TMP_FontAsset asset, string assetName) { if ((Object)(object)asset == (Object)null) { return null; } ((Object)asset).name = assetName; ((Object)asset).hideFlags = (HideFlags)61; string text = default(string); if (!asset.TryAddCharacters("ÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴÈÉẸẺẼÊỀẾỆỂỄÌÍỊỈĨÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠÙÚỤỦŨƯỪỨỰỬỮỲÝỴỶỸĐàáạảãâầấậẩẫăằắặẳẵèéẹẻẽêềếệểễìíịỉĩòóọỏõôồốộổỗơờớợởỡùúụủũưừứựửữỳýỵỷỹđ", ref text, true)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)(assetName + " could not preload: " + text)); } return asset; } private void EnableFallbackMaterialPresetMatching() { if (TMP_Settings.matchMaterialPreset) { return; } FieldInfo field = typeof(TMP_Settings).GetField("m_matchMaterialPreset", BindingFlags.Instance | BindingFlags.NonPublic); if (field == null || (Object)(object)TMP_Settings.instance == (Object)null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not enable TextMeshPro fallback material matching; fallback glyph outlines may differ."); return; } try { field.SetValue(TMP_Settings.instance, true); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not enable TextMeshPro fallback material matching: " + ex.Message)); return; } TMP_Text[] array = Resources.FindObjectsOfTypeAll(); foreach (TMP_Text val in array) { if ((Object)(object)val != (Object)null) { ((Graphic)val).SetAllDirty(); } } ((BaseUnityPlugin)this).Logger.LogInfo((object)"TextMeshPro fallback material preset matching enabled."); } private IEnumerator PatchFontsAsTheyLoad() { WaitForSecondsRealtime wait = new WaitForSecondsRealtime(1f); while (true) { PatchLoadedFontAssets(); ReplaceLoadedFonts(); yield return wait; } } private int PatchLoadedFontAssets() { int num = 0; TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll(); foreach (TMP_FontAsset val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)_customRegular || (Object)(object)val == (Object)(object)_customBold || (Object)(object)val == (Object)(object)_sansFallback || (Object)(object)val == (Object)(object)_serifFallback) { continue; } if (IsValheimAveriaSans(val)) { if (PopulateAveriaAsset(val, _averiaSansPath, _sansFallback)) { num++; } continue; } if (IsValheimAveriaSerif(val)) { if (PopulateAveriaAsset(val, _averiaSerifPath, _serifFallback)) { num++; } continue; } TMP_FontAsset fallback = ((((Object)val).name.IndexOf("Serif", StringComparison.OrdinalIgnoreCase) >= 0) ? _serifFallback : _sansFallback); if (AddAssetFallback(val, fallback)) { num++; } } return num; } private bool PopulateAveriaAsset(TMP_FontAsset asset, string sourcePath, TMP_FontAsset safetyFallback) { if (!_patchedAveriaAssets.Add(((Object)asset).GetInstanceID())) { return false; } FieldInfo field = typeof(TMP_FontAsset).GetField("m_SourceFontFile", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = typeof(TMP_FontAsset).GetField("m_SourceFontFilePath", BindingFlags.Instance | BindingFlags.NonPublic); if (field == null || field2 == null) { ((BaseUnityPlugin)this).Logger.LogError((object)("Could not attach the bundled source font to " + ((Object)asset).name + ".")); AddAssetFallback(asset, safetyFallback); return true; } try { field.SetValue(asset, null); field2.SetValue(asset, sourcePath); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Could not attach the source font to " + ((Object)asset).name + ": " + ex.Message)); AddAssetFallback(asset, safetyFallback); return true; } asset.atlasPopulationMode = (AtlasPopulationMode)1; asset.isMultiAtlasTexturesEnabled = true; string text = new string("ÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴÈÉẸẺẼÊỀẾỆỂỄÌÍỊỈĨÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠÙÚỤỦŨƯỪỨỰỬỮỲÝỴỶỸĐàáạảãâầấậẩẫăằắặẳẵèéẹẻẽêềếệểễìíịỉĩòóọỏõôồốộổỗơờớợởỡùúụủũưừứựửữỳýỵỷỹđ".Where((char character) => !asset.HasCharacter(character, false, false)).ToArray()); if (text.Length > 0) { string text2 = default(string); asset.TryAddCharacters(text, ref text2, true); } string text3 = new string("ÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴÈÉẸẺẼÊỀẾỆỂỄÌÍỊỈĨÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠÙÚỤỦŨƯỪỨỰỬỮỲÝỴỶỸĐàáạảãâầấậẩẫăằắặẳẵèéẹẻẽêềếệểễìíịỉĩòóọỏõôồốộổỗơờớợởỡùúụủũưừứựửữỳýỵỷỹđ".Where((char character) => !asset.HasCharacter(character, false, false)).ToArray()); if (text3.Length > 0) { ((BaseUnityPlugin)this).Logger.LogWarning((object)(((Object)asset).name + " could not add Vietnamese glyphs in place: " + text3)); AddAssetFallback(asset, safetyFallback); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Populated " + ((Object)asset).name + " in place with its original material and " + $"{asset.atlasTextureCount} atlas texture(s).")); } TMP_Text[] array = Resources.FindObjectsOfTypeAll(); foreach (TMP_Text val in array) { if ((Object)(object)val != (Object)null && (Object)(object)val.font == (Object)(object)asset) { ((Graphic)val).SetAllDirty(); } } return true; } private int ReplaceLoadedFonts() { //IL_004a: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Invalid comparison between Unknown and I4 int num = 0; TMP_Text[] array = Resources.FindObjectsOfTypeAll(); foreach (TMP_Text val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.font == (Object)null)) { bool flag = ((Object)val.font).name.IndexOf("bold", StringComparison.OrdinalIgnoreCase) >= 0 || (val.fontStyle & 1) != 0 || (int)val.fontWeight >= 700; TMP_FontAsset val2 = null; if (IsValheimNorse(val.font) && (Object)(object)_customRegular != (Object)null && (Object)(object)_customBold != (Object)null) { val2 = (flag ? _customBold : _customRegular); } if (!((Object)(object)val2 == (Object)null)) { Material fontSharedMaterial = val.fontSharedMaterial; val.font = val2; val.fontSharedMaterial = GetReplacementMaterial(fontSharedMaterial, val2, flag); num++; } } } return num; } private bool IsValheimNorse(TMP_FontAsset asset) { if ((Object)(object)asset != (Object)null && (Object)(object)asset != (Object)(object)_customRegular && (Object)(object)asset != (Object)(object)_customBold) { return ((Object)asset).name.StartsWith("Valheim-Norse", StringComparison.OrdinalIgnoreCase); } return false; } private bool IsValheimAveriaSans(TMP_FontAsset asset) { if ((Object)(object)asset != (Object)null) { return ((Object)asset).name.StartsWith("Valheim-AveriaSansLibre", StringComparison.OrdinalIgnoreCase); } return false; } private bool IsValheimAveriaSerif(TMP_FontAsset asset) { if ((Object)(object)asset != (Object)null) { return ((Object)asset).name.StartsWith("Valheim-AveriaSerifLibre", StringComparison.OrdinalIgnoreCase); } return false; } private Material GetReplacementMaterial(Material source, TMP_FontAsset replacement, bool bold) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown if ((Object)(object)source == (Object)null) { return ((TMP_Asset)replacement).material; } Dictionary dictionary = (bold ? _boldMaterials : _regularMaterials); if (dictionary.TryGetValue(source, out var value) && (Object)(object)value != (Object)null) { return value; } Material val = new Material(((TMP_Asset)replacement).material); TMP_MaterialManager.CopyMaterialPresetProperties(source, val); ((Object)val).name = ((Object)source).name + " (" + ((Object)replacement).name + ")"; ((Object)val).hideFlags = (HideFlags)61; dictionary[source] = val; return val; } private static bool AddAssetFallback(TMP_FontAsset asset, TMP_FontAsset fallback) { if ((Object)(object)asset == (Object)null || (Object)(object)fallback == (Object)null) { return false; } if (asset.fallbackFontAssetTable == null) { asset.fallbackFontAssetTable = new List(); } if (asset.fallbackFontAssetTable.Contains(fallback)) { return false; } asset.fallbackFontAssetTable.Insert(0, fallback); return true; } private static void AddGlobalFallback(TMP_FontAsset fallback) { if (TMP_Settings.fallbackFontAssets == null) { TMP_Settings.fallbackFontAssets = new List(); } if (!TMP_Settings.fallbackFontAssets.Contains(fallback)) { TMP_Settings.fallbackFontAssets.Insert(0, fallback); } } } }