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 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.<g__Routine|1>d))] IEnumerator Routine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <>c__DisplayClass0_1.<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)(object)((TMP_InputField)__instance).onValueChanged).AddListener((UnityAction)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.<g__Routine|1>d))] IEnumerator Routine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <>c__DisplayClass0_1.<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 EmojiNameLookup = new Dictionary(); public static readonly Dictionary EmojiFakeUnicodeLookup = new Dictionary(); public static readonly Dictionary EmojiUnicodeLookup = new Dictionary(); 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 list = new List(num); List list2 = new List(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(); ((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(); CustomEmojiAsset.spriteGlyphTable = new List(); 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(); } 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) { } } }