Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of RichEmoji v1.0.0
BepInEx/plugins/RichEmoji.dll
Decompiled 2 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; 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.Configuration; using BepInEx.Logging; using GUIFramework; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.TextCore; using UnityEngine.UI; [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("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+475f0a3a4f8aa8d92d5ec050f7d3a417f59853ff")] [assembly: AssemblyProduct("RichEmoji")] [assembly: AssemblyTitle("RichEmoji")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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 RichEmoji { public class EmojiAutocomplete : MonoBehaviour { private class SuggestionRow { public readonly GameObject Root; private const float UnselectedBgAlpha = 0.45f; private readonly Image _bg; private readonly Color _bgColor; private readonly TextMeshProUGUI _preview; private readonly TextMeshProUGUI _label; public SuggestionRow(GameObject root, Image bg, TextMeshProUGUI preview, TextMeshProUGUI label) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) Root = root; _bg = bg; _bgColor = ((Graphic)bg).color; _preview = preview; _label = label; } public void SetContent(string emojiName, string partial, string fakeChar) { ((TMP_Text)_preview).text = fakeChar ?? "?"; string text = emojiName.Substring(0, Mathf.Min(partial.Length, emojiName.Length)); string text2 = emojiName.Substring(text.Length); ((TMP_Text)_label).text = ":<color=#FFA13C>" + text + "</color>" + text2 + ":"; } public void SetSelected(bool selected) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) Color bgColor = _bgColor; if (!selected) { bgColor.a *= 0.45f; } ((Graphic)_bg).color = bgColor; ((Graphic)_label).color = (selected ? Color.white : GameAssets.ValheimBeige); } } private const int MaxSuggestions = 8; private const int MinQueryLength = 2; private const float RowHeight = 28f; private const float HeaderHeight = 20f; private const float Padding = 10f; private const float PopupWidth = 280f; private GuiInputField _field; private GameObject _popup; private RectTransform _rowContainer; private readonly List<SuggestionRow> _rows = new List<SuggestionRow>(); private List<string> _currentMatches = new List<string>(); private int _selectedIndex = -1; private int _triggerPos = -1; private int _consumedFrame = -1; private static EmojiAutocomplete _lastOpened; public bool IsOpen { get { if (Object.op_Implicit((Object)(object)_popup)) { return _popup.activeSelf; } return false; } } public bool BlocksGameInput { get { if (!IsOpen) { return _consumedFrame == Time.frameCount; } return true; } } public static bool AnyBlocksGameInput { get { if (Object.op_Implicit((Object)(object)_lastOpened)) { return _lastOpened.BlocksGameInput; } return false; } } private TMP_FontAsset FieldFont { get { if (!Object.op_Implicit((Object)(object)((TMP_InputField)_field).textComponent)) { return TMP_Settings.defaultFontAsset; } return ((TMP_InputField)_field).textComponent.font; } } private TMP_FontAsset BodyFont => GameAssets.GetFont("Valheim-AveriaSerifLibre") ?? FieldFont; private TMP_FontAsset HeaderFont => GameAssets.GetFont("Valheim-Norsebold") ?? GameAssets.GetFont("Valheim-Norse") ?? BodyFont; private void Awake() { _field = ((Component)this).GetComponent<GuiInputField>(); } private void OnDisable() { Hide(); } private void OnDestroy() { if (Object.op_Implicit((Object)(object)_popup)) { Object.Destroy((Object)(object)_popup); } } public void OnTextChanged(string text) { if (!RichEmoji.AutocompleteEnabled.Value || !Object.op_Implicit((Object)(object)_field) || !((TMP_InputField)_field).isFocused || RichEmoji.EmojiNameLookup.Count == 0) { Hide(); return; } int caretPos = Mathf.Clamp(((TMP_InputField)_field).stringPosition, 0, text.Length); if (!TryGetPartialShortcode(text, caretPos, out var partial, out var triggerPos)) { Hide(); return; } List<string> list = (from k in RichEmoji.EmojiNameLookup.Keys where k.StartsWith(partial, StringComparison.OrdinalIgnoreCase) orderby k.Length, k select k).Take(8).ToList(); if (list.Count == 0) { Hide(); return; } _triggerPos = triggerPos; _currentMatches = list; _selectedIndex = 0; EnsurePopup(); _popup.SetActive(true); _lastOpened = this; Populate(list, partial); } public bool HandleKey(Event evt) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected I4, but got Unknown //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 if (!IsOpen) { return false; } KeyCode keyCode = evt.keyCode; if ((int)keyCode <= 13) { if ((int)keyCode == 9 || (int)keyCode == 13) { goto IL_0057; } goto IL_0067; } if ((int)keyCode != 27) { switch (keyCode - 271) { case 3: break; case 2: goto IL_004e; case 0: goto IL_0057; default: goto IL_0067; } MoveSelection(1); } else { Hide(); } goto IL_0069; IL_004e: MoveSelection(-1); goto IL_0069; IL_0057: Commit(); goto IL_0069; IL_0067: return false; IL_0069: _consumedFrame = Time.frameCount; return true; } private void MoveSelection(int delta) { if (_currentMatches.Count != 0) { _selectedIndex = (_selectedIndex + delta + _currentMatches.Count) % _currentMatches.Count; for (int i = 0; i < _rows.Count; i++) { _rows[i].SetSelected(i == _selectedIndex); } } } private void Commit() { if (_selectedIndex < 0 || _selectedIndex >= _currentMatches.Count || _triggerPos < 0) { Hide(); return; } string key = _currentMatches[_selectedIndex]; string text = ((TMP_InputField)_field).text; int num = Mathf.Clamp(((TMP_InputField)_field).stringPosition, 0, text.Length); if (_triggerPos < num && RichEmoji.EmojiNameLookup.TryGetValue(key, out var value)) { string text2 = value + " "; string text3 = text.Substring(0, _triggerPos) + text2 + text.Substring(num); int stringPosition = _triggerPos + text2.Length; ((TMP_InputField)_field).SetTextWithoutNotify(text3); ((TMP_InputField)_field).stringPosition = stringPosition; ((UnityEvent<string>)(object)((TMP_InputField)_field).onValueChanged).Invoke(text3); } Hide(); } private void Hide() { if (Object.op_Implicit((Object)(object)_popup)) { _popup.SetActive(false); } _currentMatches.Clear(); _selectedIndex = -1; _triggerPos = -1; } private void Populate(List<string> names, string partial) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) while (_rows.Count < names.Count) { _rows.Add(CreateRow()); } for (int i = 0; i < _rows.Count; i++) { bool flag = i < names.Count; _rows[i].Root.SetActive(flag); if (flag) { string text = names[i]; _rows[i].SetContent(text, partial, RichEmoji.EmojiNameLookup.TryGetValue(text, out var value) ? value : null); _rows[i].SetSelected(i == _selectedIndex); } } ((RectTransform)_popup.transform).sizeDelta = new Vector2(280f, (float)names.Count * 29f + 20f + 20f); } private static void ApplySprite(Image image, string spriteName, Color fallback) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) Sprite sprite = GameAssets.GetSprite(spriteName); if (Object.op_Implicit((Object)(object)sprite)) { image.sprite = sprite; image.type = (Type)1; ((Graphic)image).color = Color.white; } else { ((Graphic)image).color = fallback; } } private void EnsurePopup() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Expected O, but got Unknown //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Expected O, but got Unknown //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)_popup)) { _popup = new GameObject("EmojiAutocomplete", new Type[1] { typeof(RectTransform) }); _popup.SetActive(false); _popup.transform.SetParent(((Component)this).transform, false); _popup.AddComponent<LayoutElement>().ignoreLayout = true; Canvas obj = _popup.AddComponent<Canvas>(); obj.overrideSorting = true; obj.sortingOrder = 30000; RectTransform val = (RectTransform)_popup.transform; val.anchorMin = new Vector2(0f, 1f); val.anchorMax = new Vector2(0f, 1f); val.pivot = new Vector2(0f, 0f); val.anchoredPosition = new Vector2(0f, 6f); Image obj2 = _popup.AddComponent<Image>(); ApplySprite(obj2, "woodpanel_trophys", new Color(0.18f, 0.13f, 0.09f, 0.95f)); ((Graphic)obj2).raycastTarget = false; GameObject val2 = new GameObject("Header", new Type[1] { typeof(RectTransform) }); val2.transform.SetParent(_popup.transform, false); RectTransform val3 = (RectTransform)val2.transform; val3.anchorMin = new Vector2(0f, 1f); val3.anchorMax = new Vector2(1f, 1f); val3.pivot = new Vector2(0f, 1f); val3.offsetMin = new Vector2(14f, -30f); val3.offsetMax = new Vector2(-10f, -10f); TextMeshProUGUI obj3 = val2.AddComponent<TextMeshProUGUI>(); ((TMP_Text)obj3).font = HeaderFont; ((TMP_Text)obj3).text = "Emoji"; ((TMP_Text)obj3).fontSize = 16f; ((Graphic)obj3).color = GameAssets.ValheimOrange; ((TMP_Text)obj3).alignment = (TextAlignmentOptions)4097; ((Graphic)obj3).raycastTarget = false; GameObject val4 = new GameObject("Rows", new Type[1] { typeof(RectTransform) }); val4.transform.SetParent(_popup.transform, false); _rowContainer = (RectTransform)val4.transform; _rowContainer.anchorMin = Vector2.zero; _rowContainer.anchorMax = Vector2.one; _rowContainer.offsetMin = new Vector2(10f, 10f); _rowContainer.offsetMax = new Vector2(-10f, -30f); VerticalLayoutGroup obj4 = val4.AddComponent<VerticalLayoutGroup>(); ((LayoutGroup)obj4).padding = new RectOffset(0, 0, 0, 0); ((HorizontalOrVerticalLayoutGroup)obj4).spacing = 1f; ((HorizontalOrVerticalLayoutGroup)obj4).childControlHeight = false; ((HorizontalOrVerticalLayoutGroup)obj4).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj4).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)obj4).childForceExpandHeight = false; } } private SuggestionRow CreateRow() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Row", new Type[1] { typeof(RectTransform) }); val.transform.SetParent((Transform)(object)_rowContainer, false); ((RectTransform)val.transform).sizeDelta = new Vector2(0f, 28f); Image val2 = val.AddComponent<Image>(); ApplySprite(val2, "item_background_sunken", new Color(0f, 0f, 0f, 0.5f)); ((Graphic)val2).raycastTarget = false; GameObject val3 = new GameObject("Preview", new Type[1] { typeof(RectTransform) }); val3.transform.SetParent(val.transform, false); RectTransform val4 = (RectTransform)val3.transform; val4.anchorMin = new Vector2(0f, 0f); val4.anchorMax = new Vector2(0f, 1f); val4.pivot = new Vector2(0f, 0.5f); val4.anchoredPosition = new Vector2(6f, 0f); val4.sizeDelta = new Vector2(22f, 0f); TextMeshProUGUI val5 = val3.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val5).font = FieldFont; ((TMP_Text)val5).fontSize = 18f; ((TMP_Text)val5).alignment = (TextAlignmentOptions)4098; ((TMP_Text)val5).overflowMode = (TextOverflowModes)0; ((Graphic)val5).raycastTarget = false; GameObject val6 = new GameObject("Label", new Type[1] { typeof(RectTransform) }); val6.transform.SetParent(val.transform, false); RectTransform val7 = (RectTransform)val6.transform; val7.anchorMin = Vector2.zero; val7.anchorMax = Vector2.one; val7.offsetMin = new Vector2(32f, 0f); val7.offsetMax = new Vector2(-6f, 0f); TextMeshProUGUI val8 = val6.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val8).font = BodyFont; ((TMP_Text)val8).fontSize = 16f; ((Graphic)val8).color = GameAssets.ValheimBeige; ((TMP_Text)val8).alignment = (TextAlignmentOptions)4097; ((TMP_Text)val8).overflowMode = (TextOverflowModes)1; ((TMP_Text)val8).textWrappingMode = (TextWrappingModes)0; ((Graphic)val8).raycastTarget = false; ((TMP_Text)val8).textPreprocessor = null; return new SuggestionRow(val, val2, val5, val8); } private static bool TryGetPartialShortcode(string text, int caretPos, out string partial, out int triggerPos) { partial = null; triggerPos = -1; int num = caretPos - 1; while (num >= 0 && caretPos - num <= 40) { char c = text[num]; if (c == ':') { string text2 = text.Substring(num + 1, caretPos - num - 1); if (text2.Length < 2) { return false; } partial = text2; triggerPos = num; return true; } if (!IsShortcodeChar(c)) { return false; } num--; } return false; } private static bool IsShortcodeChar(char c) { switch (c) { case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '_': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': return true; default: return false; } } } 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; string value; if (text2.Contains(":")) { text2 = EmojiNamePattern.Replace(text2, (Match match) => (!RichEmoji.EmojiNameLookup.TryGetValue(match.Groups[1].Value, out value)) ? match.Value : 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 GameAssets { public static readonly Color ValheimOrange = new Color(1f, 0.631f, 0.235f, 1f); public static readonly Color ValheimBeige = new Color(0.95f, 0.87f, 0.72f, 1f); private static readonly Dictionary<string, Sprite> Sprites = new Dictionary<string, Sprite>(); private static readonly Dictionary<string, TMP_FontAsset> Fonts = new Dictionary<string, TMP_FontAsset>(); public static Sprite GetSprite(string name) { if (Sprites.TryGetValue(name, out var value) && Object.op_Implicit((Object)(object)value)) { return value; } Sprite val = ((IEnumerable<Sprite>)Resources.FindObjectsOfTypeAll<Sprite>()).FirstOrDefault((Func<Sprite, bool>)((Sprite s) => ((Object)s).name == name)); if (Object.op_Implicit((Object)(object)val)) { Sprites[name] = val; } else { RichEmoji.Log.LogDebug((object)("Game sprite '" + name + "' not found, using fallback")); } return val; } public static TMP_FontAsset GetFont(string name) { if (Fonts.TryGetValue(name, out var value) && Object.op_Implicit((Object)(object)value)) { return value; } TMP_FontAsset val = ((IEnumerable<TMP_FontAsset>)Resources.FindObjectsOfTypeAll<TMP_FontAsset>()).FirstOrDefault((Func<TMP_FontAsset, bool>)((TMP_FontAsset f) => ((Object)f).name == name)); if (Object.op_Implicit((Object)(object)val)) { Fonts[name] = val; } else { RichEmoji.Log.LogDebug((object)("Game font '" + name + "' not found, using fallback")); } return val; } } 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; } } } } [HarmonyPatch(typeof(GuiInputField), "Awake")] public static class GuiInputFieldAwakePatch { private static void Postfix(GuiInputField __instance) { EmojiAutocomplete autocomplete = ((Component)__instance).gameObject.AddComponent<EmojiAutocomplete>(); Coroutine pending = null; ((UnityEvent<string>)(object)((TMP_InputField)__instance).onValueChanged).AddListener((UnityAction<string>)delegate(string text) { if (pending != null) { ((MonoBehaviour)__instance).StopCoroutine(pending); } string captured = text; pending = ((MonoBehaviour)__instance).StartCoroutine(Routine()); IEnumerator Routine() { yield return null; pending = null; string text2 = EmojiConverter.ToFakeUnicode(captured); if (text2 != captured) { int length = Mathf.Clamp(((TMP_InputField)__instance).stringPosition, 0, captured.Length); int length2 = EmojiConverter.ToFakeUnicode(captured.Substring(0, length)).Length; ((TMP_InputField)__instance).SetTextWithoutNotify(text2); ((TMP_InputField)__instance).stringPosition = length2; } autocomplete.OnTextChanged(((TMP_InputField)__instance).text); } }); } } [HarmonyPatch(typeof(TMP_InputField), "KeyPressed")] public static class TMPInputFieldKeyPressedPatch { private static bool Prefix(TMP_InputField __instance, Event evt, ref EditState __result) { EmojiAutocomplete emojiAutocomplete = default(EmojiAutocomplete); if (!((Component)__instance).TryGetComponent<EmojiAutocomplete>(ref emojiAutocomplete) || !emojiAutocomplete.HandleKey(evt)) { return true; } __result = (EditState)0; return false; } } [HarmonyPatch(typeof(Terminal), "UpdateInput")] public static class TerminalUpdateInputPatch { private static bool Prefix(Terminal __instance) { EmojiAutocomplete emojiAutocomplete = default(EmojiAutocomplete); if (Object.op_Implicit((Object)(object)__instance.m_input) && ((Component)__instance.m_input).TryGetComponent<EmojiAutocomplete>(ref emojiAutocomplete)) { return !emojiAutocomplete.BlocksGameInput; } return true; } } [HarmonyPatch(typeof(InventoryGui), "Update")] public static class InventoryGuiUpdatePatch { private static void Prefix() { if (EmojiAutocomplete.AnyBlocksGameInput) { ZInput.ResetButtonStatus("Inventory"); } } } [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(TextInput), "setText")] public static class TextInputSetTextPatch { private static void Prefix(ref string text) { text = EmojiConverter.ToShortcodes(text); } } [HarmonyPatch(typeof(Minimap), "OnPinTextEntered")] public static class MinimapPinTextPatch { private static void Prefix(Minimap __instance) { ToShortcodesInPlace(__instance.m_nameInput); } } [HarmonyPatch(typeof(Terminal), "SendInput")] public static class TerminalSendInputPatch { private static void Prefix(Terminal __instance) { ToShortcodesInPlace(__instance.m_input); } } [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); } } } private static void ToShortcodesInPlace(GuiInputField field) { if (Object.op_Implicit((Object)(object)field)) { string text = EmojiConverter.ToShortcodes(((TMP_InputField)field).text); if (text != ((TMP_InputField)field).text) { ((TMP_InputField)field).SetTextWithoutNotify(text); } } } } [BepInPlugin("Ron4242.github.RichEmoji", "RichEmoji", "1.0.0")] public sealed class RichEmoji : BaseUnityPlugin { private const int MaxEmojis = 6400; private const int EmojiMaxWidth = 64; private const int EmojiMaxHeight = 64; public static readonly ManualLogSource Log = Logger.CreateLogSource("RichEmoji"); public static TMP_SpriteAsset CustomEmojiAsset; public static ConfigEntry<bool> AutocompleteEnabled; private static readonly List<(Texture2D texture, string emojiName, string unicode)> PendingEmojis = new List<(Texture2D, string, string)>(); 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() { AutocompleteEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Autocomplete", true, "Show a list of matching emojis while typing a shortcode (e.g. :wa)"); Harmony.CreateAndPatchAll(typeof(Patches).Assembly, (string)null); LoadEmojis(); } private void Start() { BuildEmojis(); } private void LoadEmojis() { string source = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "emojis"); string text = Path.Combine(Paths.ConfigPath, "RichEmoji", "emojis"); if (!Directory.Exists(text)) { SeedEmojiFolder(source, text); } int num = AddEmojiFiles(Directory.GetFiles(text, "*.png", SearchOption.TopDirectoryOnly)); if (num > 0) { Log.LogInfo((object)$"Loaded {num} custom emojis"); } string[] directories = Directory.GetDirectories(text); Array.Sort(directories, (IComparer<string>?)StringComparer.OrdinalIgnoreCase); string[] array = directories; foreach (string text2 in array) { int num2 = AddEmojis(text2); if (num2 > 0) { Log.LogInfo((object)$"Loaded {num2} emojis from {Path.GetFileName(text2)}"); } } } private static void SeedEmojiFolder(string source, string destination) { Directory.CreateDirectory(destination); if (Directory.Exists(source)) { int num = 0; string[] files = Directory.GetFiles(source, "*", SearchOption.AllDirectories); foreach (string text in files) { string text2 = Path.Combine(destination, text.Substring(source.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); Directory.CreateDirectory(Path.GetDirectoryName(text2)); File.Copy(text, text2, overwrite: false); num++; } Log.LogInfo((object)$"First launch: copied {num} bundled emoji files to {destination}"); } } public static int AddEmojis(string folder) { if (!Directory.Exists(folder)) { return 0; } return AddEmojiFiles(Directory.GetFiles(folder, "*.png", SearchOption.AllDirectories)); } private static int AddEmojiFiles(string[] files) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown if (files.Length == 0) { return 0; } Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase); byte[][] bytes = new byte[files.Length][]; Parallel.For(0, files.Length, delegate(int i) { bytes[i] = File.ReadAllBytes(files[i]); }); int num = 0; for (int num2 = 0; num2 < files.Length; num2++) { Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!(bool)LoadImageMethod.Invoke(null, new object[2] { val, bytes[num2] })) { Log.LogWarning((object)("Couldn't load " + files[num2] + ", skipping")); Object.Destroy((Object)(object)val); continue; } string[] array = Path.GetFileNameWithoutExtension(files[num2]).Split(new string[1] { "__" }, StringSplitOptions.None); string emojiName = array[0]; string unicode = ((array.Length > 1) ? array[1] : null); AddEmoji(val, emojiName, unicode); num++; } return num; } public static void AddEmoji(Texture2D source, string emojiName, string unicode = null) { Texture2D val = ResizeTexture(source, 64, 64); if ((Object)(object)val != (Object)(object)source) { Object.Destroy((Object)(object)source); } PendingEmojis.Add((val, emojiName, unicode)); } public static void BuildEmojis() { //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012a: 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_01c4: Expected O, but got Unknown //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Expected O, but got Unknown //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Expected O, but got Unknown if (PendingEmojis.Count == 0) { return; } if (Object.op_Implicit((Object)(object)CustomEmojiAsset)) { TMP_Settings.defaultSpriteAsset.fallbackSpriteAssets?.Remove(CustomEmojiAsset); if (Object.op_Implicit((Object)(object)((TMP_Asset)CustomEmojiAsset).material)) { Object.Destroy((Object)(object)((TMP_Asset)CustomEmojiAsset).material); } if (Object.op_Implicit((Object)(object)CustomEmojiAsset.spriteSheet)) { Object.Destroy((Object)(object)CustomEmojiAsset.spriteSheet); Object.Destroy((Object)(object)CustomEmojiAsset); EmojiNameLookup.Clear(); EmojiFakeUnicodeLookup.Clear(); EmojiUnicodeLookup.Clear(); } } if (PendingEmojis.Count > 6400) { Log.LogWarning((object)$"Emoji limit exceeded! Found {PendingEmojis.Count}, but only {6400} are supported. Not all emojis will be loaded."); } int num = Math.Min(PendingEmojis.Count, 6400); List<Texture2D> list = new List<Texture2D>(num); for (int i = 0; i < num; i++) { list.Add(PendingEmojis[i].texture); } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); Rect[] array = val.PackTextures(list.ToArray(), 2, 8192, true); foreach (Texture2D item4 in list) { Object.Destroy((Object)(object)item4); } CustomEmojiAsset = ScriptableObject.CreateInstance<TMP_SpriteAsset>(); ((Object)CustomEmojiAsset).name = "RichEmojiAtlas"; CustomEmojiAsset.spriteSheet = (Texture)(object)val; ((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)val }; CustomEmojiAsset.spriteCharacterTable = new List<TMP_SpriteCharacter>(); CustomEmojiAsset.spriteGlyphTable = new List<TMP_SpriteGlyph>(); for (int j = 0; j < num; j++) { (Texture2D texture, string emojiName, string unicode) tuple = PendingEmojis[j]; string item = tuple.emojiName; string item2 = tuple.unicode; Rect val2 = array[j]; float num2 = ((Rect)(ref val2)).x * (float)((Texture)val).width; float num3 = ((Rect)(ref val2)).y * (float)((Texture)val).height; float num4 = ((Rect)(ref val2)).width * (float)((Texture)val).width; float num5 = ((Rect)(ref val2)).height * (float)((Texture)val).height; TMP_SpriteGlyph val3 = new TMP_SpriteGlyph { index = (uint)j, metrics = new GlyphMetrics(num4, num5, 0f, num5, num4), glyphRect = new GlyphRect((int)num2, (int)num3, (int)num4, (int)num5), scale = 1f }; CustomEmojiAsset.spriteGlyphTable.Add(val3); uint num6 = (uint)(57344 + j); string text = char.ConvertFromUtf32((int)num6); if (!string.IsNullOrWhiteSpace(item2)) { StringBuilder stringBuilder = new StringBuilder(); string[] array2 = item2.Split(new char[1] { '-' }); bool flag = true; for (int k = 0; k < array2.Length; k++) { if (uint.TryParse(array2[k], NumberStyles.HexNumber, null, out var result)) { stringBuilder.Append(char.ConvertFromUtf32((int)result)); continue; } flag = false; break; } if (flag && stringBuilder.Length > 0) { EmojiUnicodeLookup[stringBuilder.ToString()] = text; } } TMP_SpriteCharacter item3 = new TMP_SpriteCharacter(num6, val3) { name = item }; CustomEmojiAsset.spriteCharacterTable.Add(item3); if (EmojiNameLookup.ContainsKey(item)) { Log.LogWarning((object)("Duplicate emoji :" + item + ":, the one loaded last wins")); } EmojiNameLookup[item] = text; EmojiFakeUnicodeLookup[text[0]] = ":" + item + ":"; } CustomEmojiAsset.UpdateLookupTables(); TMP_SpriteAsset val4 = defaultSpriteAsset; if (val4.fallbackSpriteAssets == null) { val4.fallbackSpriteAssets = new List<TMP_SpriteAsset>(); } defaultSpriteAsset.fallbackSpriteAssets.Add(CustomEmojiAsset); Log.LogInfo((object)$"Built emoji atlas with {num} emojis ({((Texture)val).width}x{((Texture)val).height})"); PendingEmojis.Clear(); } 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 = "1.0.0"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }