Decompiled source of RichEmoji v0.3.1

BepInEx\plugins\RichEmoji.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Logging;
using GUIFramework;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.TextCore;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("gui_framework")]
[assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Ron4242")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © Ron4242 2026")]
[assembly: AssemblyDescription("Enhances emojis all across Valheim, adds new ones, and even lets you add your own!")]
[assembly: AssemblyFileVersion("0.3.1.0")]
[assembly: AssemblyInformationalVersion("0.3.1+fff5df7fb70a928f13c313b76f379e4502759588")]
[assembly: AssemblyProduct("RichEmoji")]
[assembly: AssemblyTitle("RichEmoji")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.1.0")]
[module: UnverifiableCode]
[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 RichEmoji
{
	public static class EmojiConverter
	{
		public class EmojiTextPreprocessor : ITextPreprocessor
		{
			public static readonly EmojiTextPreprocessor Instance = new EmojiTextPreprocessor();

			public string PreprocessText(string text)
			{
				return ToFakeUnicode(text);
			}
		}

		private const string BaseEmoji = "(?:(?:\\uD83C[\\uDDE6-\\uDDFF]){2}|[\\uD83C-\\uD83F][\\uDC00-\\uDFFF]|[\\u2000-\\u3299]|[\\u00A9\\u00AE]|[0-9#*]\\uFE0F?\\u20E3)(?:\\uD83C[\\uDFFB-\\uDFFF])?\\uFE0F?";

		public static readonly Regex EmojiNamePattern = new Regex(":([a-zA-Z0-9-_]+):", RegexOptions.Compiled);

		public static readonly Regex EmojiUnicodePattern = new Regex("(?:(?:\\uD83C[\\uDDE6-\\uDDFF]){2}|[\\uD83C-\\uD83F][\\uDC00-\\uDFFF]|[\\u2000-\\u3299]|[\\u00A9\\u00AE]|[0-9#*]\\uFE0F?\\u20E3)(?:\\uD83C[\\uDFFB-\\uDFFF])?\\uFE0F?(?:\\u200D(?:(?:\\uD83C[\\uDDE6-\\uDDFF]){2}|[\\uD83C-\\uD83F][\\uDC00-\\uDFFF]|[\\u2000-\\u3299]|[\\u00A9\\u00AE]|[0-9#*]\\uFE0F?\\u20E3)(?:\\uD83C[\\uDFFB-\\uDFFF])?\\uFE0F?)*", RegexOptions.Compiled);

		public static string ToFakeUnicode(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			string text2 = text;
			if (text2.Contains(":"))
			{
				text2 = EmojiNamePattern.Replace(text2, (Match match) => (!RichEmoji.EmojiNameLookup.TryGetValue(match.Groups[1].Value, out var value2)) ? match.Value : value2);
			}
			string value;
			return EmojiUnicodePattern.Replace(text2, (Match match) => (!RichEmoji.EmojiUnicodeLookup.TryGetValue(match.Value, out value)) ? match.Value : value);
		}

		public static string ToShortcodes(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			StringBuilder stringBuilder = new StringBuilder();
			foreach (char c in text)
			{
				if (RichEmoji.EmojiFakeUnicodeLookup.TryGetValue(c, out var value))
				{
					stringBuilder.Append(value);
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}
	}
	public static class Patches
	{
		[HarmonyPatch(typeof(GuiInputField), "ActivateInputField")]
		public static class GuiInputFieldActivatePatch
		{
			private static void Postfix(GuiInputField __instance)
			{
				if (!string.IsNullOrEmpty(((TMP_InputField)__instance).text))
				{
					string text = EmojiConverter.ToFakeUnicode(((TMP_InputField)__instance).text);
					if (!(text == ((TMP_InputField)__instance).text))
					{
						((TMP_InputField)__instance).SetTextWithoutNotify(text);
						((TMP_InputField)__instance).m_OriginalText = text;
						((TMP_InputField)__instance).caretPosition = ((TMP_InputField)__instance).text.Length;
					}
				}
			}
		}

		[HarmonyPatch(typeof(GuiInputField), "Awake")]
		public static class GuiInputFieldAwakePatch
		{
			[CompilerGenerated]
			private sealed class <>c__DisplayClass0_0
			{
				public Coroutine pending;

				public GuiInputField __instance;

				internal void <Postfix>b__0(string text)
				{
					<>c__DisplayClass0_1 CS$<>8__locals0 = new <>c__DisplayClass0_1
					{
						CS$<>8__locals1 = this
					};
					if (pending != null)
					{
						((MonoBehaviour)__instance).StopCoroutine(pending);
					}
					CS$<>8__locals0.captured = text;
					pending = ((MonoBehaviour)__instance).StartCoroutine(Routine());
					[IteratorStateMachine(typeof(<>c__DisplayClass0_1.<<Postfix>g__Routine|1>d))]
					IEnumerator Routine()
					{
						//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
						return new <>c__DisplayClass0_1.<<Postfix>g__Routine|1>d(0)
						{
							<>4__this = CS$<>8__locals0
						};
					}
				}
			}

			[CompilerGenerated]
			private sealed class <>c__DisplayClass0_1
			{
				public string captured;

				public <>c__DisplayClass0_0 CS$<>8__locals1;
			}

			private static void Postfix(GuiInputField __instance)
			{
				Coroutine pending = null;
				<>c__DisplayClass0_0 CS$<>8__locals0;
				((UnityEvent<string>)(object)((TMP_InputField)__instance).onValueChanged).AddListener((UnityAction<string>)delegate(string text)
				{
					<>c__DisplayClass0_1 CS$<>8__locals1 = new <>c__DisplayClass0_1();
					CS$<>8__locals1.CS$<>8__locals1 = CS$<>8__locals0;
					if (pending != null)
					{
						((MonoBehaviour)__instance).StopCoroutine(pending);
					}
					CS$<>8__locals1.captured = text;
					pending = ((MonoBehaviour)__instance).StartCoroutine(Routine());
					[IteratorStateMachine(typeof(<>c__DisplayClass0_1.<<Postfix>g__Routine|1>d))]
					IEnumerator Routine()
					{
						//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
						return new <>c__DisplayClass0_1.<<Postfix>g__Routine|1>d(0)
						{
							<>4__this = CS$<>8__locals1
						};
					}
				});
			}
		}

		[HarmonyPatch(typeof(GuiInputField), "onInputSubmit")]
		public static class GuiInputFieldSubmitPatch
		{
			private static void Prefix(GuiInputField __instance, ref string text)
			{
				string text2 = EmojiConverter.ToShortcodes(text);
				if (!(text2 == text))
				{
					((TMP_InputField)__instance).SetTextWithoutNotify(text2);
					text = text2;
				}
			}
		}

		[HarmonyPatch(typeof(TextMeshProUGUI), "Awake")]
		public static class TMPTextAwakePatch
		{
			private static void Postfix(TextMeshProUGUI __instance)
			{
				if (((TMP_Text)__instance).textPreprocessor == null)
				{
					ITextPreprocessor val = (((TMP_Text)__instance).textPreprocessor = (ITextPreprocessor)(object)EmojiConverter.EmojiTextPreprocessor.Instance);
				}
			}
		}
	}
	[BepInPlugin("Ron4242.github.RichEmoji", "RichEmoji", "0.3.1")]
	public sealed class RichEmoji : BaseUnityPlugin
	{
		public const int MaxEmojis = 6400;

		public static readonly ManualLogSource Log = Logger.CreateLogSource("RichEmoji");

		public static TMP_SpriteAsset CustomEmojiAsset;

		public static readonly Dictionary<string, string> EmojiNameLookup = new Dictionary<string, string>();

		public static readonly Dictionary<char, string> EmojiFakeUnicodeLookup = new Dictionary<char, string>();

		public static readonly Dictionary<string, string> EmojiUnicodeLookup = new Dictionary<string, string>();

		private static MethodInfo LoadImageMethod { get; } = AccessTools.Method(typeof(ImageConversion), "LoadImage", new Type[2]
		{
			typeof(Texture2D),
			typeof(byte[])
		}, (Type[])null);


		private void Awake()
		{
			Harmony.CreateAndPatchAll(typeof(RichEmoji).Assembly, (string)null);
			LoadEmojis();
		}

		private void LoadEmojis()
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Expected O, but got Unknown
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Expected O, but got Unknown
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Expected O, but got Unknown
			//IL_0397: Unknown result type (might be due to invalid IL or missing references)
			//IL_039c: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a6: Expected O, but got Unknown
			string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "emojis");
			if (!Directory.Exists(text))
			{
				Log.LogWarning((object)("Emojis folder not found at " + text));
				return;
			}
			string[] files = Directory.GetFiles(text, "*.png", SearchOption.AllDirectories);
			int num = files.Length;
			if (files.Length == 0)
			{
				return;
			}
			byte[][] bytes = new byte[num][];
			Parallel.For(0, num, delegate(int i)
			{
				bytes[i] = File.ReadAllBytes(files[i]);
			});
			List<Texture2D> list = new List<Texture2D>(num);
			List<string> list2 = new List<string>(num);
			for (int j = 0; j < num; j++)
			{
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				LoadImageMethod.Invoke(null, new object[2]
				{
					val,
					bytes[j]
				});
				bytes[j] = null;
				Texture2D val2 = ResizeTexture(val, 96, 96);
				list.Add(val2);
				list2.Add(Path.GetFileNameWithoutExtension(files[j]));
				if ((Object)(object)val2 != (Object)(object)val)
				{
					Object.Destroy((Object)(object)val);
				}
			}
			Texture2D val3 = new Texture2D(2, 2, (TextureFormat)4, false);
			Rect[] array = val3.PackTextures(list.ToArray(), 2, 8192);
			foreach (Texture2D item2 in list)
			{
				Object.Destroy((Object)(object)item2);
			}
			list.Clear();
			CustomEmojiAsset = ScriptableObject.CreateInstance<TMP_SpriteAsset>();
			((Object)CustomEmojiAsset).name = "RichEmojiAtlas";
			CustomEmojiAsset.spriteSheet = (Texture)(object)val3;
			((TMP_Asset)CustomEmojiAsset).m_Version = "1.1.0";
			TMP_SpriteAsset defaultSpriteAsset = TMP_Settings.defaultSpriteAsset;
			((TMP_Asset)CustomEmojiAsset).material = new Material(((TMP_Asset)defaultSpriteAsset).material)
			{
				mainTexture = (Texture)(object)val3
			};
			CustomEmojiAsset.spriteCharacterTable = new List<TMP_SpriteCharacter>();
			CustomEmojiAsset.spriteGlyphTable = new List<TMP_SpriteGlyph>();
			int num2 = Math.Min(array.Length, 6400);
			if (array.Length > 6400)
			{
				Log.LogWarning((object)$"Emoji limit exceeded! Found {array.Length}, but only up to {6400} emojis are supported. Not all emojis will be loaded.");
			}
			for (int k = 0; k < num2; k++)
			{
				Rect val4 = array[k];
				string text2 = list2[k];
				float num3 = ((Rect)(ref val4)).x * (float)((Texture)val3).width;
				float num4 = ((Rect)(ref val4)).y * (float)((Texture)val3).height;
				float num5 = ((Rect)(ref val4)).width * (float)((Texture)val3).width;
				float num6 = ((Rect)(ref val4)).height * (float)((Texture)val3).height;
				TMP_SpriteGlyph val5 = new TMP_SpriteGlyph
				{
					index = (uint)k,
					metrics = new GlyphMetrics(num5, num6, 0f, num6, num5),
					glyphRect = new GlyphRect((int)num3, (int)num4, (int)num5, (int)num6),
					scale = 1f
				};
				CustomEmojiAsset.spriteGlyphTable.Add(val5);
				string[] array2 = text2.Split(new string[1] { "__" }, StringSplitOptions.None);
				string text3 = array2[0];
				StringBuilder stringBuilder = new StringBuilder();
				uint num7 = (uint)(57344 + k);
				if (array2.Length > 1)
				{
					string[] array3 = array2[1].Split(new char[1] { '-' });
					bool flag = true;
					string[] array4 = array3;
					for (int l = 0; l < array4.Length; l++)
					{
						if (uint.TryParse(array4[l], NumberStyles.HexNumber, null, out var result))
						{
							stringBuilder.Append(char.ConvertFromUtf32((int)result));
							continue;
						}
						flag = false;
						break;
					}
					if (flag && stringBuilder.Length > 0)
					{
						EmojiUnicodeLookup[stringBuilder.ToString()] = char.ConvertFromUtf32((int)num7);
					}
				}
				TMP_SpriteCharacter item = new TMP_SpriteCharacter(num7, val5)
				{
					name = text3
				};
				CustomEmojiAsset.spriteCharacterTable.Add(item);
				EmojiNameLookup[text3] = char.ConvertFromUtf32((int)num7);
				EmojiFakeUnicodeLookup[char.ConvertFromUtf32((int)num7)[0]] = ":" + text3 + ":";
			}
			CustomEmojiAsset.UpdateLookupTables();
			TMP_SpriteAsset val6 = defaultSpriteAsset;
			if (val6.fallbackSpriteAssets == null)
			{
				val6.fallbackSpriteAssets = new List<TMP_SpriteAsset>();
			}
			defaultSpriteAsset.fallbackSpriteAssets.Add(CustomEmojiAsset);
			Log.LogInfo((object)$"Loaded {files.Length} emojis!");
		}

		private static Texture2D ResizeTexture(Texture2D source, int maxWidth, int maxHeight)
		{
			//IL_005a: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			int width = ((Texture)source).width;
			int height = ((Texture)source).height;
			if (width > maxWidth || height > maxHeight)
			{
				float num = (float)width / (float)height;
				if (width > height)
				{
					width = maxWidth;
					height = Mathf.RoundToInt((float)width / num);
				}
				else
				{
					height = maxHeight;
					width = Mathf.RoundToInt((float)height * num);
				}
				RenderTexture val = (RenderTexture.active = RenderTexture.GetTemporary(width, height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)0));
				Graphics.Blit((Texture)(object)source, val);
				Texture2D val2 = new Texture2D(width, height, (TextureFormat)4, false);
				val2.ReadPixels(new Rect(0f, 0f, (float)width, (float)height), 0, 0);
				val2.Apply();
				RenderTexture.active = null;
				RenderTexture.ReleaseTemporary(val);
				return val2;
			}
			return source;
		}
	}
	internal static class ThisPluginInfo
	{
		public const string PLUGIN_GUID = "Ron4242.github.RichEmoji";

		public const string PLUGIN_NAME = "RichEmoji";

		public const string PLUGIN_VERSION = "0.3.1";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}