Decompiled source of Valheim Viet Hoa v0.2.11

plugins/ValheimVietHoa/ValheimVietnameseFont.dll

Decompiled a day ago
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<int> _patchedAveriaAssets = new HashSet<int>();

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

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

		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>();
			Font val = ((IEnumerable<Font>)source).FirstOrDefault((Func<Font, bool>)((Font font) => ((Object)font).name == "NotoSans-Regular"));
			Font val2 = ((IEnumerable<Font>)source).FirstOrDefault((Func<Font, bool>)((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<TMP_Text>();
			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<TMP_FontAsset>();
			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<TMP_Text>();
			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<TMP_Text>();
			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<Material, Material> 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<TMP_FontAsset>();
			}
			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<TMP_FontAsset>();
			}
			if (!TMP_Settings.fallbackFontAssets.Contains(fallback))
			{
				TMP_Settings.fallbackFontAssets.Insert(0, fallback);
			}
		}
	}
}