Decompiled source of Tanuki Atlyss FontAssetsManager v1.0.2

BepInEx/plugins/Tanuki.Atlyss.FontAssetsManager/Tanuki.Atlyss.FontAssetsManager.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
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.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Nessie.ATLYSS.EasySettings;
using Newtonsoft.Json;
using TMPro;
using Tanuki.Atlyss.Core.Plugins;
using Tanuki.Atlyss.FontAssetsManager.Managers;
using Tanuki.Atlyss.FontAssetsManager.Models;
using Tanuki.Atlyss.FontAssetsManager.Models.Configuration;
using Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TMP_Text;
using Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TextMeshPro;
using Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TextMeshProUGUI;
using Tanuki.Atlyss.FontAssetsManager.Patches.UnityEngine.UI.Text;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Tanuki.Atlyss.FontAssetsManager")]
[assembly: AssemblyDescription("Plugin for Atlyss that customizes in-game fonts. Use custom fonts, replace existing ones, add fallbacks, and prevent missing characters from being displayed.")]
[assembly: AssemblyCompany("Tanuki")]
[assembly: AssemblyProduct("Tanuki.Atlyss.FontAssetsManager")]
[assembly: AssemblyCopyright("Copyright © Tanuki 2025")]
[assembly: ComVisible(false)]
[assembly: Guid("941c959b-230f-4a52-b0f7-db438c6031e1")]
[assembly: AssemblyFileVersion("1.0.2")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyVersion("1.0.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Tanuki.Atlyss.FontAssetsManager
{
	internal class Configuration
	{
		public static Configuration Instance;

		public General General;

		public Tanuki.Atlyss.FontAssetsManager.Models.Configuration.Debug Debug;

		private Configuration()
		{
		}

		public static void Initialize()
		{
			if (Instance == null)
			{
				Instance = new Configuration();
			}
		}

		public void Load(ConfigFile ConfigFile)
		{
			General = new General(ConfigFile);
			Debug = new Tanuki.Atlyss.FontAssetsManager.Models.Configuration.Debug(ConfigFile);
		}
	}
	[BepInPlugin("941c959b-230f-4a52-b0f7-db438c6031e1", "Tanuki.Atlyss.FontAssetsManager", "1.0.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	internal class Main : Plugin
	{
		internal static Main Instance;

		internal ManualLogSource ManualLogSource;

		private bool Reloaded;

		internal void Awake()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			ManualLogSource = ((BaseUnityPlugin)this).Logger;
			new Harmony("941c959b-230f-4a52-b0f7-db438c6031e1").PatchAll();
			Configuration.Initialize();
			Configuration.Instance.Load(((BaseUnityPlugin)this).Config);
			AssetBundles.Initialize();
			AssetBundles.Instance.OnAssetsRefreshFinished += AssetBundles_OnAssetsRefreshed;
			Replacements.Initialize();
			Fallbacks.Initialize();
			UnknownCharactersReplace.Initialize();
			if (Chainloader.PluginInfos.ContainsKey("EasySettings"))
			{
				NessieEasySettings.Initialize();
			}
		}

		protected override void Load()
		{
			if (Reloaded)
			{
				((BaseUnityPlugin)this).Config.Reload();
				Configuration.Instance.Load(((BaseUnityPlugin)this).Config);
			}
			if (!AssetBundles.Instance.Refreshing)
			{
				AssetBundles.Instance.Refresh();
			}
			if (Configuration.Instance.Debug.TMP_Text_OnEnable.Value)
			{
				Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TextMeshProUGUI.OnEnable_Prefix.OnInvoke += TMP_Text_OnEnable_Log;
				Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TextMeshPro.OnEnable_Prefix.OnInvoke += TMP_Text_OnEnable_Log;
			}
			if (Configuration.Instance.Debug.Text_OnEnable.Value)
			{
				Tanuki.Atlyss.FontAssetsManager.Patches.UnityEngine.UI.Text.OnEnable_Prefix.OnInvoke += Text_OnEnable_Log;
			}
			Tanuki.Atlyss.FontAssetsManager.Patches.UnityEngine.UI.Text.OnEnable_Prefix.OnInvoke += Text_OnEnable_Prefix_OnInvoke;
			Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TextMeshProUGUI.OnEnable_Prefix.OnInvoke += TextMeshProUGUI_OnEnable_Prefix_OnInvoke;
			Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TextMeshPro.OnEnable_Prefix.OnInvoke += TextMeshPro_OnEnable_Prefix_OnInvoke;
			UnknownCharactersReplace.Instance.Load();
		}

		private void AssetBundles_OnAssetsRefreshed()
		{
			Replacements.Instance.Reload();
			Fallbacks.Instance.Reload();
			if (Configuration.Instance.General.UnloadUnusedAssets.Value)
			{
				AssetBundles.Instance.DestroyUnusedAssets();
			}
		}

		private void TMP_Text_OnEnable_Log(TMP_Text Instance)
		{
			((BaseUnityPlugin)this).Logger.LogDebug((object)((Plugin)this).Translate("Debug.TMP_Text.OnEnable", new object[2]
			{
				((Object)Instance).name,
				((Object)Instance.font).name
			}));
		}

		private void Text_OnEnable_Log(Text Instance)
		{
			((BaseUnityPlugin)this).Logger.LogDebug((object)((Plugin)this).Translate("Debug.Text.OnEnable", new object[2]
			{
				((Object)Instance).name,
				((Object)Instance.font).name
			}));
		}

		private void Text_OnEnable_Prefix_OnInvoke(Text Instance)
		{
			if (Instance.font != null)
			{
				Replacements.Instance.Replace(Instance);
			}
		}

		private void TextMeshProUGUI_OnEnable_Prefix_OnInvoke(TextMeshProUGUI Instance)
		{
			Replacements.Instance.Handle((TMP_Text)(object)Instance);
			Fallbacks.Instance.Handle((TMP_Text)(object)Instance);
		}

		private void TextMeshPro_OnEnable_Prefix_OnInvoke(TextMeshPro Instance)
		{
			Replacements.Instance.Handle((TMP_Text)(object)Instance);
			Fallbacks.Instance.Handle((TMP_Text)(object)Instance);
		}

		protected override void Unload()
		{
			Reloaded = true;
			if (Configuration.Instance.Debug.TMP_Text_OnEnable.Value)
			{
				Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TextMeshProUGUI.OnEnable_Prefix.OnInvoke += TMP_Text_OnEnable_Log;
				Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TextMeshPro.OnEnable_Prefix.OnInvoke += TMP_Text_OnEnable_Log;
			}
			if (Configuration.Instance.Debug.Text_OnEnable.Value)
			{
				Tanuki.Atlyss.FontAssetsManager.Patches.UnityEngine.UI.Text.OnEnable_Prefix.OnInvoke += Text_OnEnable_Log;
			}
			Tanuki.Atlyss.FontAssetsManager.Patches.UnityEngine.UI.Text.OnEnable_Prefix.OnInvoke -= Text_OnEnable_Prefix_OnInvoke;
			Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TextMeshProUGUI.OnEnable_Prefix.OnInvoke -= TextMeshProUGUI_OnEnable_Prefix_OnInvoke;
			Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TextMeshPro.OnEnable_Prefix.OnInvoke -= TextMeshPro_OnEnable_Prefix_OnInvoke;
			Replacements.Instance.Reset();
			Fallbacks.Instance.Reset();
			UnknownCharactersReplace.Instance.Unload();
		}
	}
	internal class PluginInfo
	{
		public const string GUID = "941c959b-230f-4a52-b0f7-db438c6031e1";

		public const string Version = "1.0.2";
	}
}
namespace Tanuki.Atlyss.FontAssetsManager.Patches.UnityEngine.UI.Text
{
	[HarmonyPriority(int.MaxValue)]
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	public static class OnEnable_Prefix
	{
		public delegate void EventHandler(Text Instance);

		public static event EventHandler OnInvoke;

		private static void Postfix(Text __instance)
		{
			OnEnable_Prefix.OnInvoke?.Invoke(__instance);
		}
	}
}
namespace Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TextMeshPro
{
	[HarmonyPriority(int.MaxValue)]
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	public static class OnEnable_Prefix
	{
		public delegate void EventHandler(TextMeshPro Instance);

		public static event EventHandler OnInvoke;

		private static void Postfix(TextMeshPro __instance)
		{
			OnEnable_Prefix.OnInvoke?.Invoke(__instance);
		}
	}
}
namespace Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TextMeshProUGUI
{
	[HarmonyPriority(int.MaxValue)]
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	public static class OnEnable_Prefix
	{
		public delegate void EventHandler(TextMeshProUGUI Instance);

		public static event EventHandler OnInvoke;

		private static void Postfix(TextMeshProUGUI __instance)
		{
			OnEnable_Prefix.OnInvoke?.Invoke(__instance);
		}
	}
}
namespace Tanuki.Atlyss.FontAssetsManager.Patches.TMPro.TMP_Text
{
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	public static class Text_Setter_Prefix
	{
		public delegate void EventHandler(TMP_Text __instance, ref string value);

		public static event EventHandler OnInvoke;

		private static void Prefix(TMP_Text __instance, ref string value)
		{
			if (!(__instance.text == value))
			{
				Text_Setter_Prefix.OnInvoke?.Invoke(__instance, ref value);
			}
		}
	}
}
namespace Tanuki.Atlyss.FontAssetsManager.Patches.ChatBehaviour
{
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	public static class UserCode_Rpc_RecieveChatMessage__String__Boolean__ChatChannel_Prefix
	{
		public delegate void EventHandler(ref string Message);

		public static event EventHandler OnInvoke;

		private static void Prefix(ref string message)
		{
			UserCode_Rpc_RecieveChatMessage__String__Boolean__ChatChannel_Prefix.OnInvoke?.Invoke(ref message);
		}
	}
}
namespace Tanuki.Atlyss.FontAssetsManager.Models
{
	public class Asset
	{
		public Object Object;

		public ushort Uses;

		public Asset(Object Asset)
		{
			Object = Asset;
			base..ctor();
		}
	}
	public class Fallback
	{
		public Rule Rule;

		public bool Fixed;

		public HashSet<TMP_FontAsset> Assets;

		public Fallback(Rule Rule, bool Fixed)
		{
			this.Rule = Rule;
			this.Fixed = Fixed;
			Assets = new HashSet<TMP_FontAsset>();
			base..ctor();
		}
	}
	public class Replacement<T>
	{
		public Rule Rule;

		public T Asset;

		public Replacement(Rule Rule, T Asset)
		{
			this.Rule = Rule;
			this.Asset = Asset;
			base..ctor();
		}
	}
	public class Rule : IEquatable<Rule>
	{
		private readonly Regex ObjectName;

		private readonly Regex FontName;

		public Rule(Regex ObjectName, Regex FontName)
		{
			this.ObjectName = ObjectName;
			this.FontName = FontName;
			base..ctor();
		}

		public bool IsMatch(string ObjectName, string FontName)
		{
			if (this.ObjectName.IsMatch(ObjectName))
			{
				return this.FontName.IsMatch(FontName);
			}
			return false;
		}

		public bool Equals(Rule Other)
		{
			if (ObjectName.ToString() == Other.ObjectName.ToString())
			{
				return FontName.ToString() == Other.FontName.ToString();
			}
			return false;
		}
	}
}
namespace Tanuki.Atlyss.FontAssetsManager.Models.Configuration
{
	internal class Debug
	{
		private const string Section = "Debug";

		public ConfigEntry<bool> TMP_Text_OnEnable = ConfigFile.Bind<bool>("Debug", "TMP_Text_OnEnable", false, (ConfigDescription)null);

		public ConfigEntry<bool> Text_OnEnable = ConfigFile.Bind<bool>("Debug", "Text_OnEnable", false, (ConfigDescription)null);

		public Debug(ConfigFile ConfigFile)
		{
		}
	}
	[Serializable]
	internal struct Fallback
	{
		[JsonProperty("Rule")]
		public Rule Rule;

		[JsonProperty("Fixed")]
		public bool Fixed;

		[JsonProperty("Assets")]
		public List<Asset> Assets;
	}
	internal class General
	{
		private const string Section = "General";

		public ConfigEntry<bool> ReplaceUnknownCharactersWithCodes = ConfigFile.Bind<bool>("General", "ReplaceUnknownCharactersWithCodes", true, (ConfigDescription)null);

		public ConfigEntry<bool> UnloadUnusedAssets = ConfigFile.Bind<bool>("General", "DestroyUnusedAssets", true, (ConfigDescription)null);

		public ConfigEntry<bool> RemoveCompletelyUnusedRuleFiles = ConfigFile.Bind<bool>("General", "RemoveCompletelyUnusedRuleFiles", true, (ConfigDescription)null);

		public General(ConfigFile ConfigFile)
		{
		}
	}
	[Serializable]
	internal struct Replacement
	{
		[JsonProperty("Asset")]
		public Asset Asset;

		[JsonProperty("Rule")]
		public Rule Rule;
	}
	[Serializable]
	internal struct Asset
	{
		[JsonProperty("Bundle")]
		public string Bundle;

		[JsonProperty("Object")]
		public string Object;
	}
	[Serializable]
	internal struct Rule
	{
		[JsonProperty("Object")]
		public string Object;

		[JsonProperty("Font")]
		public string Font;
	}
}
namespace Tanuki.Atlyss.FontAssetsManager.Managers
{
	public class Fallbacks
	{
		private const string DirectoryName = "Fallbacks";

		public static Fallbacks Instance;

		private string Directory;

		public readonly List<Tanuki.Atlyss.FontAssetsManager.Models.Fallback> Assets;

		private Fallbacks()
		{
			Assets = new List<Tanuki.Atlyss.FontAssetsManager.Models.Fallback>();
		}

		public static void Initialize()
		{
			if (Instance == null)
			{
				Instance = new Fallbacks
				{
					Directory = Path.Combine(Paths.ConfigPath, ((Plugin)Main.Instance).Name, "Fallbacks")
				};
			}
		}

		public void Reload()
		{
			if (!System.IO.Directory.Exists(Directory))
			{
				System.IO.Directory.CreateDirectory(Directory);
			}
			HashSet<TMP_FontAsset> hashSet = new HashSet<TMP_FontAsset>();
			string[] files = System.IO.Directory.GetFiles(Directory, "*.json");
			foreach (string text in files)
			{
				List<Tanuki.Atlyss.FontAssetsManager.Models.Configuration.Fallback> list = JsonConvert.DeserializeObject<List<Tanuki.Atlyss.FontAssetsManager.Models.Configuration.Fallback>>(File.ReadAllText(text));
				ushort num = 0;
				foreach (Tanuki.Atlyss.FontAssetsManager.Models.Configuration.Fallback item in list)
				{
					Tanuki.Atlyss.FontAssetsManager.Models.Rule rule;
					try
					{
						rule = new Tanuki.Atlyss.FontAssetsManager.Models.Rule(new Regex(item.Rule.Object), new Regex(item.Rule.Font));
					}
					catch
					{
						Main.Instance.ManualLogSource.LogWarning((object)((Plugin)Main.Instance).Translate("Fallback.InvalidRegex", new object[1] { text }));
						continue;
					}
					hashSet.Clear();
					foreach (Tanuki.Atlyss.FontAssetsManager.Models.Configuration.Asset asset in item.Assets)
					{
						TMP_FontAsset assetObject = AssetBundles.Instance.GetAssetObject<TMP_FontAsset>(asset.Bundle, asset.Object);
						if (assetObject == null)
						{
							Main.Instance.ManualLogSource.LogWarning((object)((Plugin)Main.Instance).Translate("Fallback.AssetNotFound", new object[3] { asset.Object, asset.Bundle, text }));
						}
						else
						{
							num++;
							hashSet.Add(assetObject);
						}
					}
					if (hashSet.Count == 0)
					{
						continue;
					}
					Tanuki.Atlyss.FontAssetsManager.Models.Fallback fallback = null;
					foreach (Tanuki.Atlyss.FontAssetsManager.Models.Fallback asset2 in Assets)
					{
						if (rule.Equals(asset2.Rule))
						{
							fallback = asset2;
							break;
						}
					}
					if (fallback == null)
					{
						fallback = new Tanuki.Atlyss.FontAssetsManager.Models.Fallback(rule, item.Fixed);
						Assets.Add(fallback);
					}
					else
					{
						if (fallback.Fixed || item.Fixed)
						{
							foreach (TMP_FontAsset asset3 in fallback.Assets)
							{
								AssetBundles.Instance.Unuse((Object)(object)asset3, PreventUnload: true);
							}
							fallback.Assets.Clear();
						}
						fallback.Fixed = item.Fixed;
					}
					foreach (TMP_FontAsset item2 in hashSet)
					{
						fallback.Assets.Add(item2);
						AssetBundles.Instance.Use((Object)(object)item2);
					}
				}
				if (num <= 0)
				{
					RemoveCompletelyUnusedRulesFile(text);
				}
			}
		}

		private void RemoveCompletelyUnusedRulesFile(string Path)
		{
			if (Configuration.Instance.General.RemoveCompletelyUnusedRuleFiles.Value)
			{
				File.Delete(Path);
			}
		}

		public void Reset()
		{
			Assets.Clear();
		}

		public void Handle(TMP_Text TMP_Text)
		{
			foreach (Tanuki.Atlyss.FontAssetsManager.Models.Fallback asset in Assets)
			{
				if (!asset.Rule.IsMatch(((Object)TMP_Text).name, ((Object)TMP_Text.font).name))
				{
					continue;
				}
				TMP_FontAsset font = TMP_Text.font;
				if (font.fallbackFontAssetTable == null)
				{
					List<TMP_FontAsset> list2 = (font.fallbackFontAssetTable = new List<TMP_FontAsset>());
				}
				{
					foreach (TMP_FontAsset asset2 in asset.Assets)
					{
						if (!((Object)(object)TMP_Text.font == (Object)(object)asset2) && !TMP_Text.font.fallbackFontAssetTable.Contains(asset2))
						{
							TMP_Text.font.fallbackFontAssetTable.Add(asset2);
						}
					}
					break;
				}
			}
		}
	}
	internal class NessieEasySettings
	{
		public const string GUID = "EasySettings";

		internal static NessieEasySettings Instance;

		private NessieEasySettings()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			Settings.OnInitialized.AddListener(new UnityAction(NessieEasySettings_OnInitialize));
			Settings.OnApplySettings.AddListener(new UnityAction(NessieEasySettings_OnApplySettings));
		}

		public static void Initialize()
		{
			if (Instance == null)
			{
				Instance = new NessieEasySettings();
			}
		}

		private void NessieEasySettings_OnInitialize()
		{
			SettingsTab modTab = Settings.ModTab;
			modTab.AddHeader("- TA FontAssetsManager -");
			modTab.AddToggle("Replace unknown characters with codes", Configuration.Instance.General.ReplaceUnknownCharactersWithCodes);
			modTab.AddSpace();
		}

		private void NessieEasySettings_OnApplySettings()
		{
			Configuration.Instance.Load(((BaseUnityPlugin)Main.Instance).Config);
			UnknownCharactersReplace.Instance.Reload();
		}
	}
	public class Replacements
	{
		private const string DirectoryName = "Replacements";

		public static Replacements Instance;

		private string Directory;

		public readonly List<Replacement<Font>> Assets;

		public readonly List<Replacement<TMP_FontAsset>> AssetsTMP;

		private Replacements()
		{
			Assets = new List<Replacement<Font>>();
			AssetsTMP = new List<Replacement<TMP_FontAsset>>();
		}

		public static void Initialize()
		{
			if (Instance == null)
			{
				Instance = new Replacements
				{
					Directory = Path.Combine(Paths.ConfigPath, ((Plugin)Main.Instance).Name, "Replacements")
				};
			}
		}

		public void Reload()
		{
			if (!System.IO.Directory.Exists(Directory))
			{
				System.IO.Directory.CreateDirectory(Directory);
			}
			string[] files = System.IO.Directory.GetFiles(Directory, "*.json");
			foreach (string text in files)
			{
				List<Replacement> list = JsonConvert.DeserializeObject<List<Replacement>>(File.ReadAllText(text));
				ushort num = 0;
				foreach (Replacement item in list)
				{
					TMP_FontAsset assetObject = AssetBundles.Instance.GetAssetObject<TMP_FontAsset>(item.Asset.Bundle, item.Asset.Object);
					Font assetObject2 = AssetBundles.Instance.GetAssetObject<Font>(item.Asset.Bundle, item.Asset.Object);
					if (assetObject2 == null && assetObject == null)
					{
						Main.Instance.ManualLogSource.LogWarning((object)((Plugin)Main.Instance).Translate("Replacement.AssetNotFound", new object[3]
						{
							item.Asset.Object,
							item.Asset.Bundle,
							text
						}));
						continue;
					}
					Tanuki.Atlyss.FontAssetsManager.Models.Rule rule;
					try
					{
						rule = new Tanuki.Atlyss.FontAssetsManager.Models.Rule(new Regex(item.Rule.Object), new Regex(item.Rule.Font));
					}
					catch
					{
						Main.Instance.ManualLogSource.LogWarning((object)((Plugin)Main.Instance).Translate("Replacement.InvalidRegex", new object[1] { text }));
						continue;
					}
					bool flag;
					if (assetObject != null)
					{
						flag = true;
						num++;
						foreach (Replacement<TMP_FontAsset> item2 in AssetsTMP)
						{
							if (!item2.Rule.Equals(rule))
							{
								flag = false;
								AssetBundles.Instance.Unuse((Object)(object)item2.Asset, PreventUnload: true);
								item2.Asset = assetObject;
								break;
							}
						}
						if (flag)
						{
							AssetsTMP.Add(new Replacement<TMP_FontAsset>(rule, assetObject));
						}
						AssetBundles.Instance.Use((Object)(object)assetObject);
					}
					if (assetObject2 == null)
					{
						continue;
					}
					flag = true;
					num++;
					foreach (Replacement<Font> asset in Assets)
					{
						if (asset.Equals(rule))
						{
							flag = false;
							AssetBundles.Instance.Unuse((Object)(object)asset.Asset, PreventUnload: true);
							asset.Asset = assetObject2;
							break;
						}
					}
					if (flag)
					{
						Assets.Add(new Replacement<Font>(rule, assetObject2));
					}
					AssetBundles.Instance.Use((Object)(object)assetObject2);
				}
				if (num <= 0)
				{
					RemoveCompletelyUnusedRulesFile(text);
				}
			}
		}

		private void RemoveCompletelyUnusedRulesFile(string Path)
		{
			if (Configuration.Instance.General.RemoveCompletelyUnusedRuleFiles.Value)
			{
				File.Delete(Path);
			}
		}

		public void Handle(TMP_Text TMP_Text)
		{
			foreach (Replacement<TMP_FontAsset> item in AssetsTMP)
			{
				if ((Object)(object)TMP_Text.font == (Object)(object)item.Asset)
				{
					break;
				}
				if (item.Rule.IsMatch(((Object)TMP_Text).name, ((Object)TMP_Text.font).name))
				{
					TMP_Text.font = item.Asset;
					break;
				}
			}
		}

		public void Replace(Text Text)
		{
			foreach (Replacement<Font> asset in Assets)
			{
				if ((Object)(object)Text.font == (Object)(object)asset.Asset)
				{
					break;
				}
				if (asset.Rule.IsMatch(((Object)Text).name, ((Object)Text.font).name))
				{
					Text.font = asset.Asset;
					break;
				}
			}
		}

		public void Reset()
		{
			Assets.Clear();
			AssetsTMP.Clear();
		}
	}
	public class AssetBundles
	{
		public delegate void AssetsRefreshed();

		public delegate void BeforeAssetsRefresh();

		public static AssetBundles Instance;

		private string AssetBundlesPath;

		private readonly Dictionary<ulong, Tanuki.Atlyss.FontAssetsManager.Models.Asset> Assets;

		private readonly Dictionary<Object, ulong> AssetHashes;

		private ushort PendingRefreshes;

		public bool Refreshing => PendingRefreshes > 0;

		public event AssetsRefreshed OnAssetsRefreshFinished;

		public event BeforeAssetsRefresh OnBeforeAssetsRefresh;

		private AssetBundles()
		{
			Assets = new Dictionary<ulong, Tanuki.Atlyss.FontAssetsManager.Models.Asset>();
			AssetHashes = new Dictionary<Object, ulong>();
		}

		public static void Initialize()
		{
			if (Instance == null)
			{
				Instance = new AssetBundles();
			}
		}

		internal void Refresh()
		{
			if (Assets.Count > 0)
			{
				return;
			}
			PendingRefreshes = 0;
			this.OnBeforeAssetsRefresh?.Invoke();
			string[] directories = Directory.GetDirectories(Paths.PluginPath, "Tanuki.Atlyss.FontAssetsManager.AssetBundles", SearchOption.AllDirectories);
			for (int i = 0; i < directories.Length; i++)
			{
				string[] files = Directory.GetFiles(directories[i], "*.assetbundle", SearchOption.AllDirectories);
				foreach (string obj in files)
				{
					PendingRefreshes++;
					((AsyncOperation)AssetBundle.LoadFromFileAsync(obj)).completed += OnAssetBundleLoaded;
				}
			}
		}

		private void OnAssetBundleLoaded(AsyncOperation AsyncOperation)
		{
			AssetBundle assetBundle = ((AssetBundleCreateRequest)((AsyncOperation is AssetBundleCreateRequest) ? AsyncOperation : null)).assetBundle;
			if (!Object.op_Implicit((Object)(object)assetBundle))
			{
				OnAssetBundleProcessFinished();
				return;
			}
			TMP_FontAsset[] array = assetBundle.LoadAllAssets<TMP_FontAsset>();
			foreach (TMP_FontAsset val in array)
			{
				ulong assetHash = GetAssetHash(((Object)assetBundle).name, ((Object)val).name);
				if (Assets.ContainsKey(assetHash))
				{
					Main.Instance.ManualLogSource.LogWarning((object)((Plugin)Main.Instance).Translate("AssetBundle.Duplicate", new object[3]
					{
						((Object)val).name,
						"name",
						((Object)assetBundle).name
					}));
				}
				else
				{
					Assets.Add(assetHash, new Tanuki.Atlyss.FontAssetsManager.Models.Asset((Object)(object)val));
					AssetHashes.Add((Object)(object)val, assetHash);
				}
			}
			Font[] array2 = assetBundle.LoadAllAssets<Font>();
			foreach (Font val2 in array2)
			{
				ulong assetHash = GetAssetHash(((Object)assetBundle).name, ((Object)val2).name);
				if (Assets.ContainsKey(assetHash))
				{
					Main.Instance.ManualLogSource.LogWarning((object)((Plugin)Main.Instance).Translate("AssetBundle.Duplicate", new object[3]
					{
						((Object)val2).name,
						"name",
						((Object)assetBundle).name
					}));
				}
				else
				{
					Assets.Add(assetHash, new Tanuki.Atlyss.FontAssetsManager.Models.Asset((Object)(object)val2));
					AssetHashes.Add((Object)(object)val2, assetHash);
				}
			}
			((AsyncOperation)assetBundle.UnloadAsync(false)).completed += OnAssetBundleUnloaded;
		}

		private void OnAssetBundleUnloaded(AsyncOperation AsyncOperation)
		{
			OnAssetBundleProcessFinished();
		}

		private void OnAssetBundleProcessFinished()
		{
			PendingRefreshes--;
			if (PendingRefreshes <= 0)
			{
				this.OnAssetsRefreshFinished?.Invoke();
			}
		}

		private ulong GetAssetHash(string AssetBundle, string AssetName)
		{
			return (ulong)(((long)AssetBundle.GetHashCode() << 32) | (uint)AssetName.GetHashCode());
		}

		public T GetAssetObject<T>(string AssetBundle, string AssetName) where T : class
		{
			Assets.TryGetValue(GetAssetHash(AssetBundle, AssetName), out var value);
			if (value == null)
			{
				return null;
			}
			if (!(value.Object is T))
			{
				return null;
			}
			return value.Object as T;
		}

		public void Use(Object Object)
		{
			if (AssetHashes.TryGetValue(Object, out var value))
			{
				Assets[value].Uses++;
			}
		}

		public void Unuse(Object Object, bool PreventUnload)
		{
			if (AssetHashes.TryGetValue(Object, out var value))
			{
				Tanuki.Atlyss.FontAssetsManager.Models.Asset asset = Assets[value];
				if (asset.Uses > 0)
				{
					asset.Uses--;
				}
				if (!(asset.Uses > 0 || PreventUnload))
				{
					Assets.Remove(value);
					AssetHashes.Remove(Object);
					Object.Destroy(Object);
				}
			}
		}

		public void DestroyUnusedAssets()
		{
			List<ulong> list = new List<ulong>();
			foreach (KeyValuePair<ulong, Tanuki.Atlyss.FontAssetsManager.Models.Asset> asset in Assets)
			{
				if (asset.Value.Uses <= 0)
				{
					list.Add(asset.Key);
				}
			}
			foreach (ulong item in list)
			{
				Object @object = Assets[item].Object;
				Assets.Remove(item);
				AssetHashes.Remove(@object);
				Object.Destroy(@object);
			}
		}
	}
	public class UnknownCharactersReplace
	{
		public static UnknownCharactersReplace Instance;

		private UnknownCharactersReplace()
		{
		}

		public static void Initialize()
		{
			if (Instance == null)
			{
				Instance = new UnknownCharactersReplace();
			}
		}

		private void TMPro_TMP_Text_Text_Setter_Prefix_OnInvoke(TMP_Text __instance, ref string value)
		{
			if (string.IsNullOrEmpty(value) || !Object.op_Implicit((Object)(object)__instance.font))
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			string text = value;
			foreach (char c in text)
			{
				if (__instance.font.HasCharacter(c, true, false))
				{
					stringBuilder.Append(c);
				}
				else
				{
					stringBuilder.Append($"\\u{Convert.ToInt32(c)}");
				}
			}
			value = stringBuilder.ToString();
		}

		public void Load()
		{
			if (Configuration.Instance.General.ReplaceUnknownCharactersWithCodes.Value)
			{
				Text_Setter_Prefix.OnInvoke += TMPro_TMP_Text_Text_Setter_Prefix_OnInvoke;
			}
		}

		public void Unload()
		{
			Text_Setter_Prefix.OnInvoke -= TMPro_TMP_Text_Text_Setter_Prefix_OnInvoke;
		}

		public void Reload()
		{
			Unload();
			Load();
		}
	}
}