using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ArabicRepo")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.5.0")] [assembly: AssemblyInformationalVersion("1.0.5")] [assembly: AssemblyProduct("Arabic Chat Fix")] [assembly: AssemblyTitle("ArabicRepo")] [assembly: AssemblyVersion("1.0.5.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 ArabicRepo { public static class ArabicFixer { private struct Glyph { public char Join; public int Iso; public int Ini; public int Med; public int Fin; public Glyph(char j, int iso, int ini, int med, int fin) { Join = j; Iso = iso; Ini = ini; Med = med; Fin = fin; } } [CompilerGenerated] private sealed class d__22 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private string <>2__current; private int <>l__initialThreadId; private string line; public string <>3__line; private StringBuilder 5__2; private string[] <>7__wrap2; private int <>7__wrap3; private string 5__5; string IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__22(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { 5__2 = null; <>7__wrap2 = null; 5__5 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: { <>1__state = -1; if (MaxLineChars <= 0 || line.Length <= MaxLineChars) { <>2__current = line; <>1__state = 1; return true; } string[] array = line.Split(' '); 5__2 = new StringBuilder(); <>7__wrap2 = array; <>7__wrap3 = 0; goto IL_015e; } case 1: <>1__state = -1; return false; case 2: <>1__state = -1; 5__2.Length = 0; 5__2.Append(5__5); goto IL_0149; case 3: { <>1__state = -1; break; } IL_0150: <>7__wrap3++; goto IL_015e; IL_0149: 5__5 = null; goto IL_0150; IL_015e: if (<>7__wrap3 < <>7__wrap2.Length) { 5__5 = <>7__wrap2[<>7__wrap3]; if (5__5.Length != 0) { if (5__2.Length == 0) { 5__2.Append(5__5); } else { if (5__2.Length + 1 + 5__5.Length > MaxLineChars) { <>2__current = 5__2.ToString(); <>1__state = 2; return true; } 5__2.Append(' ').Append(5__5); } goto IL_0149; } goto IL_0150; } <>7__wrap2 = null; if (5__2.Length > 0) { <>2__current = 5__2.ToString(); <>1__state = 3; return true; } break; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__22 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__22(0); } d__.line = <>3__line; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } public static bool CombineAllah = true; public static bool KeepLtrRuns = true; public static bool ReverseWordOrder = true; public static string WordJoiner = "\u00a0"; public static bool PreventWordSplit = true; public static int MaxLineChars = 0; private static readonly Dictionary Forms = new Dictionary { { 1569, new Glyph('U', 65152, 0, 0, 0) }, { 1570, new Glyph('R', 65153, 0, 0, 65154) }, { 1571, new Glyph('R', 65155, 0, 0, 65156) }, { 1572, new Glyph('R', 65157, 0, 0, 65158) }, { 1573, new Glyph('R', 65159, 0, 0, 65160) }, { 1574, new Glyph('D', 65161, 65163, 65164, 65162) }, { 1575, new Glyph('R', 65165, 0, 0, 65166) }, { 1576, new Glyph('D', 65167, 65169, 65170, 65168) }, { 1577, new Glyph('R', 65171, 0, 0, 65172) }, { 1578, new Glyph('D', 65173, 65175, 65176, 65174) }, { 1579, new Glyph('D', 65177, 65179, 65180, 65178) }, { 1580, new Glyph('D', 65181, 65183, 65184, 65182) }, { 1581, new Glyph('D', 65185, 65187, 65188, 65186) }, { 1582, new Glyph('D', 65189, 65191, 65192, 65190) }, { 1583, new Glyph('R', 65193, 0, 0, 65194) }, { 1584, new Glyph('R', 65195, 0, 0, 65196) }, { 1585, new Glyph('R', 65197, 0, 0, 65198) }, { 1586, new Glyph('R', 65199, 0, 0, 65200) }, { 1587, new Glyph('D', 65201, 65203, 65204, 65202) }, { 1588, new Glyph('D', 65205, 65207, 65208, 65206) }, { 1589, new Glyph('D', 65209, 65211, 65212, 65210) }, { 1590, new Glyph('D', 65213, 65215, 65216, 65214) }, { 1591, new Glyph('D', 65217, 65219, 65220, 65218) }, { 1592, new Glyph('D', 65221, 65223, 65224, 65222) }, { 1593, new Glyph('D', 65225, 65227, 65228, 65226) }, { 1594, new Glyph('D', 65229, 65231, 65232, 65230) }, { 1600, new Glyph('C', 1600, 1600, 1600, 1600) }, { 1601, new Glyph('D', 65233, 65235, 65236, 65234) }, { 1602, new Glyph('D', 65237, 65239, 65240, 65238) }, { 1603, new Glyph('D', 65241, 65243, 65244, 65242) }, { 1604, new Glyph('D', 65245, 65247, 65248, 65246) }, { 1605, new Glyph('D', 65249, 65251, 65252, 65250) }, { 1606, new Glyph('D', 65253, 65255, 65256, 65254) }, { 1607, new Glyph('D', 65257, 65259, 65260, 65258) }, { 1608, new Glyph('R', 65261, 0, 0, 65262) }, { 1609, new Glyph('R', 65263, 0, 0, 65264) }, { 1610, new Glyph('D', 65265, 65267, 65268, 65266) } }; private static readonly Dictionary LamAlef = new Dictionary { { 1570, new int[2] { 65269, 65270 } }, { 1571, new int[2] { 65271, 65272 } }, { 1573, new int[2] { 65273, 65274 } }, { 1575, new int[2] { 65275, 65276 } } }; private static char JType(int cp) { if (Forms.TryGetValue(cp, out var value)) { return value.Join; } if (cp < 1611 || cp > 1631) { switch (cp) { default: if ((cp < 1759 || cp > 1764) && (cp < 1767 || cp > 1768) && (cp < 1770 || cp > 1773)) { return 'U'; } break; case 1648: case 1750: case 1751: case 1752: case 1753: case 1754: case 1755: case 1756: break; } } return 'T'; } private static bool IsArabicLetter(int cp) { if (cp < 1536 || cp > 1791) { if (cp >= 1872) { return cp <= 1919; } return false; } return true; } public static bool ContainsArabic(string s) { if (string.IsNullOrEmpty(s)) { return false; } for (int i = 0; i < s.Length; i++) { if (IsArabicLetter(s[i])) { return true; } } return false; } public static bool IsAlreadyShaped(string s) { if (string.IsNullOrEmpty(s)) { return false; } foreach (char c in s) { if ((c >= 'ﭐ' && c <= '\ufdff') || (c >= 'ﹰ' && c <= '\ufeff')) { return true; } } return false; } private static string Reshape(string text) { if (CombineAllah) { text = CollapseAllah(text); } int length = text.Length; int[] array = new int[length]; for (int i = 0; i < length; i++) { array[i] = text[i]; } StringBuilder stringBuilder = new StringBuilder(length); int num = 0; while (num < length) { int num2 = array[num]; char c = JType(num2); if (num2 == 1604) { int num3 = NextNonTransparent(array, length, num); if (num3 < length && LamAlef.ContainsKey(array[num3])) { int num4 = PrevNonTransparent(array, num); char c2 = ((num4 >= 0) ? JType(array[num4]) : 'U'); bool flag = c2 == 'D' || c2 == 'C'; int[] array2 = LamAlef[array[num3]]; stringBuilder.Append((char)(flag ? array2[1] : array2[0])); for (int j = num + 1; j < num3; j++) { stringBuilder.Append((char)array[j]); } num = num3 + 1; continue; } } if (!Forms.TryGetValue(num2, out var value)) { stringBuilder.Append((char)num2); num++; continue; } int num5 = PrevNonTransparent(array, num); int num6 = NextNonTransparent(array, length, num); char c3 = ((num5 >= 0) ? JType(array[num5]) : 'U'); char c4 = ((num6 < length) ? JType(array[num6]) : 'U'); bool flag2 = (c == 'D' || c == 'R') && (c3 == 'D' || c3 == 'C'); bool flag3 = (c == 'D' || c == 'C') && (c4 == 'D' || c4 == 'R' || c4 == 'C'); int num7 = ((flag2 && flag3) ? ((value.Med != 0) ? value.Med : ((value.Fin != 0) ? value.Fin : value.Iso)) : (flag2 ? ((value.Fin != 0) ? value.Fin : value.Iso) : ((!flag3) ? value.Iso : ((value.Ini != 0) ? value.Ini : value.Iso)))); stringBuilder.Append((char)num7); num++; } return stringBuilder.ToString(); } private static int PrevNonTransparent(int[] cps, int idx) { int num = idx - 1; while (num >= 0 && JType(cps[num]) == 'T') { num--; } return num; } private static int NextNonTransparent(int[] cps, int n, int idx) { int i; for (i = idx + 1; i < n && JType(cps[i]) == 'T'; i++) { } return i; } private static string CollapseAllah(string text) { int num = text.IndexOf("الله", StringComparison.Ordinal); if (num < 0) { return text; } StringBuilder stringBuilder = new StringBuilder(text.Length); int num2 = 0; while (num >= 0) { bool num3 = num == 0 || !IsArabicLetter(text[num - 1]); int num4 = num + "الله".Length; bool flag = num4 >= text.Length || !IsArabicLetter(text[num4]); stringBuilder.Append(text, num2, num - num2); if (num3 && flag) { stringBuilder.Append('ﷲ'); } else { stringBuilder.Append("الله"); } num2 = num4; num = text.IndexOf("الله", num2, StringComparison.Ordinal); } stringBuilder.Append(text, num2, text.Length - num2); return stringBuilder.ToString(); } private static bool IsLtr(int cp) { if ((cp < 65 || cp > 90) && (cp < 97 || cp > 122) && (cp < 192 || cp > 591)) { return cp == 8206; } return true; } private static bool IsDigit(int cp) { if ((cp < 48 || cp > 57) && (cp < 1632 || cp > 1641)) { if (cp >= 1776) { return cp <= 1785; } return false; } return true; } private static bool IsLtrGlue(int cp) { switch ((char)(ushort)cp) { case '#': case '%': case '&': case '+': case ',': case '-': case '.': case '/': case ':': case '=': case '?': case '@': case '_': return true; default: return false; } } private static string BidiLine(string s) { int length = s.Length; int[] array = new int[length]; for (int i = 0; i < length; i++) { array[i] = s[length - 1 - i]; } StringBuilder stringBuilder = new StringBuilder(length); int num = 0; while (num < length) { int num2 = array[num]; bool flag = IsLtr(num2) || IsDigit(num2); if (KeepLtrRuns && flag) { int num3 = num; while (num3 < length) { int num4 = array[num3]; if (IsLtr(num4) || IsDigit(num4)) { num3++; continue; } if ((num4 != 32 && !IsLtrGlue(num4)) || num3 + 1 >= length || (!IsLtr(array[num3 + 1]) && !IsDigit(array[num3 + 1]))) { break; } num3++; } for (int num5 = num3 - 1; num5 >= num; num5--) { stringBuilder.Append((char)array[num5]); } num = num3; } else { stringBuilder.Append((char)num2); num++; } } return stringBuilder.ToString(); } public static string Fix(string text) { if (string.IsNullOrEmpty(text)) { return text; } if (!ContainsArabic(text)) { return text; } if (IsAlreadyShaped(text)) { return text; } string[] array = text.Replace("\r", "").Split('\n'); List list = new List(); string[] array2 = array; for (int i = 0; i < array2.Length; i++) { foreach (string item in WrapLogical(array2[i])) { list.Add(ProcessLine(item)); } } return string.Join("\n", list); } [IteratorStateMachine(typeof(d__22))] private static IEnumerable WrapLogical(string line) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__22(-2) { <>3__line = line }; } private static string ProcessLine(string line) { line = line.Trim(' ', '\t'); if (line.Length == 0) { return line; } string text; if (ReverseWordOrder) { text = BidiLine(Reshape(line)).Trim(' ', '\t'); } else { string[] array = line.Split(' '); for (int i = 0; i < array.Length; i++) { if (ContainsArabic(array[i])) { array[i] = BidiLine(Reshape(array[i])); } } text = string.Join(" ", array); } if (PreventWordSplit && WordJoiner != " ") { text = text.Replace(" ", WordJoiner); } return text; } } [BepInPlugin("bandar.arabicrepo", "Arabic Chat Fix", "1.0.5")] public class Plugin : BaseUnityPlugin { [CompilerGenerated] private sealed class d__23 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Plugin <>4__this; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__23(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown int num = <>1__state; Plugin plugin = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(4f); <>1__state = 1; return true; case 1: <>1__state = -1; plugin.TrySmoothFont(); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__27 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Plugin <>4__this; private Type 5__2; private float 5__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__27(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown int num = <>1__state; Plugin plugin = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; 5__2 = null; 5__3 = Time.realtimeSinceStartup + 120f; break; case 1: <>1__state = -1; break; } if (Time.realtimeSinceStartup < 5__3) { 5__2 = AccessTools.TypeByName(plugin._cfgChatType.Value); if (!(5__2 != null)) { <>2__current = (object)new WaitForSeconds(0.5f); <>1__state = 1; return true; } } if (5__2 == null) { Log.LogWarning((object)("Chat class '" + plugin._cfgChatType.Value + "' not found after 120s. If your chat comes from another mod, set ChatTypeName in the config.")); return false; } if (plugin._cfgDump.Value) { plugin.DumpType(5__2); } plugin.ResolveMessageField(5__2); plugin.HookSendMethods(5__2); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public const string Guid = "bandar.arabicrepo"; public const string Name = "Arabic Chat Fix"; public const string Version = "1.0.5"; internal static ManualLogSource Log; private Harmony _harmony; private ConfigEntry _cfgFixOutgoing; private ConfigEntry _cfgFixDisplay; private ConfigEntry _cfgCombineAllah; private ConfigEntry _cfgKeepLtrRuns; private ConfigEntry _cfgReverseWordOrder; private ConfigEntry _cfgWordSeparator; private ConfigEntry _cfgMaxLineChars; private ConfigEntry _cfgSmoothFont; private ConfigEntry _cfgSmoothFontName; private ConfigEntry _cfgChatType; private ConfigEntry _cfgSendMethods; private ConfigEntry _cfgMessageField; private ConfigEntry _cfgAggressive; private ConfigEntry _cfgDump; private ConfigEntry _cfgDebug; internal static FieldInfo MessageField; internal static bool DebugLog; private void Awake() { //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _cfgFixOutgoing = ((BaseUnityPlugin)this).Config.Bind("1. Behaviour", "FixOutgoing", true, "Convert your typed Arabic to its baked form on send, so EVERYONE (even players without the mod) sees it correctly."); _cfgFixDisplay = ((BaseUnityPlugin)this).Config.Bind("1. Behaviour", "FixIncomingDisplay", true, "Also reshape raw Arabic in text the game displays locally, so you can read Arabic typed by players who DON'T have the mod."); _cfgCombineAllah = ((BaseUnityPlugin)this).Config.Bind("1. Behaviour", "CombineAllahLigature", true, "Collapse the standalone word الله into the single ﷲ ligature glyph."); _cfgKeepLtrRuns = ((BaseUnityPlugin)this).Config.Bind("1. Behaviour", "KeepLatinAndNumbersInOrder", true, "Keep Latin words, numbers, emails and URLs in normal reading order inside Arabic text."); _cfgReverseWordOrder = ((BaseUnityPlugin)this).Config.Bind("1. Behaviour", "ReverseWordOrder", true, "true = full right-to-left word order. false = shape each word but keep words in the order you typed them (use this if multi-word messages stall on the first word in-game)."); _cfgWordSeparator = ((BaseUnityPlugin)this).Config.Bind("1. Behaviour", "WordSeparator", "nbsp", "How to separate words so R.E.P.O.'s word-by-word chat reader doesn't stop at the first space. Options: nbsp (non-breaking space, default), narrow (narrower no-break space), thin, none (words touch), space (normal space - only if the game handles it)."); _cfgMaxLineChars = ((BaseUnityPlugin)this).Config.Bind("1. Behaviour", "MaxLineChars", 18, "0 = let the game wrap long messages (the start can end up on the bottom line). Set to a number (try ~22) to wrap lines yourself: the first words stay on the TOP line and each line reads right-to-left. Lower it if lines still wrap oddly, raise it for fewer line breaks."); _cfgSmoothFont = ((BaseUnityPlugin)this).Config.Bind("3. Appearance", "SmoothFont", true, "Render Arabic from a clean system font so the letters look genuine. Affects only screens that have this mod (you and modded friends); players without the mod still use the game's font."); _cfgSmoothFontName = ((BaseUnityPlugin)this).Config.Bind("3. Appearance", "SmoothFontName", "Tahoma", "Name of an installed system font with good Arabic. Try: Tahoma, Segoe UI, Arial, Sakkal Majalla, Traditional Arabic, Dubai. Use the exact font name as installed on your PC."); _cfgChatType = ((BaseUnityPlugin)this).Config.Bind("2. Hook (advanced)", "ChatTypeName", "ChatManager", "The game class that handles chat. Change only if a game update renames it (see the member dump in the log)."); _cfgSendMethods = ((BaseUnityPlugin)this).Config.Bind("2. Hook (advanced)", "SendMethodOverride", "", "Comma-separated method name(s) on the chat class to treat as the 'send' point. Leave empty to auto-detect."); _cfgMessageField = ((BaseUnityPlugin)this).Config.Bind("2. Hook (advanced)", "MessageFieldOverride", "", "Name of the string field that holds the text you typed. Leave empty to auto-detect."); _cfgAggressive = ((BaseUnityPlugin)this).Config.Bind("2. Hook (advanced)", "AggressiveAutoHook", true, "If no curated send method is found, hook every plausible send-like method. Safe because the conversion is a no-op on non-Arabic / already-converted text."); _cfgDump = ((BaseUnityPlugin)this).Config.Bind("2. Hook (advanced)", "DumpChatClassToLog", true, "Write all fields/methods of the chat class to the BepInEx log once, to help diagnose hooks after a game update."); _cfgDebug = ((BaseUnityPlugin)this).Config.Bind("2. Hook (advanced)", "DebugLogging", true, "Log the raw and converted text on every send (char codes), for troubleshooting."); DebugLog = _cfgDebug.Value; ArabicFixer.CombineAllah = _cfgCombineAllah.Value; ArabicFixer.KeepLtrRuns = _cfgKeepLtrRuns.Value; ArabicFixer.ReverseWordOrder = _cfgReverseWordOrder.Value; ArabicFixer.MaxLineChars = _cfgMaxLineChars.Value; switch ((_cfgWordSeparator.Value ?? "nbsp").Trim().ToLowerInvariant()) { case "space": ArabicFixer.WordJoiner = " "; ArabicFixer.PreventWordSplit = false; break; case "none": ArabicFixer.WordJoiner = ""; ArabicFixer.PreventWordSplit = true; break; case "narrow": ArabicFixer.WordJoiner = "\u202f"; ArabicFixer.PreventWordSplit = true; break; case "thin": ArabicFixer.WordJoiner = "\u2009"; ArabicFixer.PreventWordSplit = true; break; default: ArabicFixer.WordJoiner = "\u00a0"; ArabicFixer.PreventWordSplit = true; break; } _harmony = new Harmony("bandar.arabicrepo"); if (_cfgFixDisplay.Value) { TryHookTmpDisplay(); } if (_cfgFixOutgoing.Value) { ((MonoBehaviour)this).StartCoroutine(WaitForChatThenHook()); } else { Log.LogInfo((object)"FixOutgoing disabled; only local display reshaping is active."); } if (_cfgSmoothFont.Value) { ((MonoBehaviour)this).StartCoroutine(SmoothFontRoutine()); } Log.LogInfo((object)"Arabic Chat Fix v1.0.5 loaded."); } [IteratorStateMachine(typeof(d__23))] private IEnumerator SmoothFontRoutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__23(0) { <>4__this = this }; } private void TrySmoothFont() { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("TMPro.TMP_FontAsset"); Type type2 = AccessTools.TypeByName("TMPro.TMP_Settings"); if (type == null || type2 == null) { Log.LogWarning((object)"TMP not found; font smoothing skipped."); return; } string text = (string.IsNullOrEmpty(_cfgSmoothFontName.Value) ? "Tahoma" : _cfgSmoothFontName.Value); Font val = null; try { val = Font.CreateDynamicFontFromOSFont(text, 90); } catch { } if ((Object)(object)val == (Object)null) { try { val = new Font(text); } catch { } } if ((Object)(object)val == (Object)null) { Log.LogWarning((object)("System font '" + text + "' not found; font smoothing skipped.")); return; } MethodInfo methodInfo = null; MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo2 in methods) { if (!(methodInfo2.Name != "CreateFontAsset")) { ParameterInfo[] parameters = methodInfo2.GetParameters(); if (parameters.Length >= 1 && parameters[0].ParameterType == typeof(Font)) { methodInfo = methodInfo2; break; } } } if (methodInfo == null) { Log.LogWarning((object)"TMP_FontAsset.CreateFontAsset(Font) not available."); return; } ParameterInfo[] parameters2 = methodInfo.GetParameters(); object[] array = new object[parameters2.Length]; array[0] = val; for (int j = 1; j < parameters2.Length; j++) { array[j] = (parameters2[j].HasDefaultValue ? parameters2[j].DefaultValue : (parameters2[j].ParameterType.IsValueType ? Activator.CreateInstance(parameters2[j].ParameterType) : null)); } object obj3 = methodInfo.Invoke(null, array); if (obj3 == null) { Log.LogWarning((object)"Could not create TMP font asset."); return; } PropertyInfo property = type2.GetProperty("fallbackFontAssets", BindingFlags.Static | BindingFlags.Public); IList list = ((property != null) ? (property.GetValue(null) as IList) : null); if (list == null) { object obj4 = type2.GetProperty("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); FieldInfo field = type2.GetField("m_fallbackFontAssets", BindingFlags.Instance | BindingFlags.NonPublic); if (obj4 != null && field != null) { list = field.GetValue(obj4) as IList; } } if (list == null) { Log.LogWarning((object)"Could not access TMP fallback list; font smoothing skipped."); return; } list.Add(obj3); Log.LogInfo((object)("Font smoothing on: Arabic will render from '" + text + "' (local view only).")); } catch (Exception ex) { Log.LogError((object)("Font smoothing failed: " + ex.Message)); } } private void TryHookTmpDisplay() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("TMPro.TMP_Text"); if (type == null) { Log.LogWarning((object)"TMPro.TMP_Text not found; display reshaping off."); return; } MethodInfo methodInfo = AccessTools.PropertySetter(type, "text"); if (methodInfo == null) { Log.LogWarning((object)"TMP_Text.text setter not found; display reshaping off."); return; } _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin).GetMethod("TmpTextSetterPrefix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Display reshaping hooked onto TMP_Text.text."); } catch (Exception ex) { Log.LogError((object)("Display hook failed: " + ex)); } } private static void TmpTextSetterPrefix(ref string value) { try { if (!string.IsNullOrEmpty(value) && ArabicFixer.ContainsArabic(value) && !ArabicFixer.IsAlreadyShaped(value)) { value = ArabicFixer.Fix(value); } } catch { } } [IteratorStateMachine(typeof(d__27))] private IEnumerator WaitForChatThenHook() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__27(0) { <>4__this = this }; } private void ResolveMessageField(Type t) { try { if (!string.IsNullOrEmpty(_cfgMessageField.Value)) { MessageField = AccessTools.Field(t, _cfgMessageField.Value); if (MessageField != null) { Log.LogInfo((object)("Message field (override): " + MessageField.Name)); return; } Log.LogWarning((object)("MessageFieldOverride '" + _cfgMessageField.Value + "' not found; auto-detecting.")); } string[] obj = new string[7] { "chatMessage", "chatText", "message", "currentText", "typedText", "input", "text" }; FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); string[] array = obj; foreach (string b in array) { FieldInfo[] array2 = fields; foreach (FieldInfo fieldInfo in array2) { if (fieldInfo.FieldType == typeof(string) && string.Equals(fieldInfo.Name, b, StringComparison.OrdinalIgnoreCase)) { MessageField = fieldInfo; Log.LogInfo((object)("Message field (auto): " + fieldInfo.Name)); return; } } } Log.LogInfo((object)"No obvious message string field; relying on string-argument hooks."); } catch (Exception ex) { Log.LogError((object)("ResolveMessageField failed: " + ex)); } } private void HookSendMethods(Type t) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown List list = new List(); HarmonyMethod val = new HarmonyMethod(typeof(Plugin).GetMethod("SendArgPrefix", BindingFlags.Static | BindingFlags.NonPublic)); HarmonyMethod val2 = new HarmonyMethod(typeof(Plugin).GetMethod("SendFieldPrefix", BindingFlags.Static | BindingFlags.NonPublic)); if (!string.IsNullOrEmpty(_cfgSendMethods.Value)) { string[] array = _cfgSendMethods.Value.Split(','); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0) { continue; } try { MethodInfo methodInfo = AccessTools.DeclaredMethod(t, text, (Type[])null, (Type[])null); if (methodInfo != null) { list.Add(methodInfo); } else { Log.LogWarning((object)("SendMethodOverride '" + text + "' not found on " + t.Name)); } } catch (Exception ex) { Log.LogWarning((object)("override '" + text + "' skipped: " + ex.Message)); } } } if (list.Count == 0) { string[] array = new string[9] { "MessageSend", "ForceSendMessage", "ForceConfirmChat", "SendChatMessage", "ChatSend", "SendChat", "SubmitMessage", "ConfirmMessage", "PostMessage" }; foreach (string text2 in array) { try { MethodInfo methodInfo2 = AccessTools.DeclaredMethod(t, text2, (Type[])null, (Type[])null); if (methodInfo2 != null && methodInfo2.ReturnType == typeof(void)) { list.Add(methodInfo2); } } catch (Exception ex2) { Log.LogWarning((object)("curated '" + text2 + "' skipped: " + ex2.Message)); } } } if (list.Count == 0 && _cfgAggressive.Value) { MethodInfo[] methods = t.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo3 in methods) { if (!(methodInfo3.ReturnType != typeof(void)) && !methodInfo3.IsAbstract && !(methodInfo3.GetBaseDefinition().DeclaringType != t)) { string text3 = methodInfo3.Name.ToLowerInvariant(); bool num = text3.Contains("send") || text3.Contains("submit") || text3.Contains("confirm") || text3.Contains("post") || text3 == "say"; ParameterInfo[] parameters = methodInfo3.GetParameters(); bool flag = parameters.Length >= 1 && parameters[0].ParameterType == typeof(string); if (num || (flag && text3.Contains("chat"))) { list.Add(methodInfo3); } } } } if (list.Count == 0) { Log.LogWarning((object)"Could not locate a chat send method automatically. Open the member dump in the log, find the method called when you press Enter, and set 'SendMethodOverride' in the config."); return; } foreach (MethodInfo item in list) { try { bool flag2 = false; ParameterInfo[] parameters2 = item.GetParameters(); for (int i = 0; i < parameters2.Length; i++) { if (parameters2[i].ParameterType == typeof(string)) { flag2 = true; break; } } _harmony.Patch((MethodBase)item, flag2 ? val : ((MessageField != null) ? val2 : val), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); if (!flag2 && MessageField != null) { Log.LogInfo((object)("Hooked send (field-mode): " + item.Name)); continue; } Log.LogInfo((object)("Hooked send (arg-mode): " + item.Name + "(" + Sig(item) + ")")); } catch (Exception ex3) { Log.LogError((object)("Failed to hook " + item.Name + ": " + ex3)); } } } private static string Dump(string s) { StringBuilder stringBuilder = new StringBuilder(); foreach (char c in s) { if (stringBuilder.Length > 0) { stringBuilder.Append(' '); } int num = c; stringBuilder.Append(num.ToString("X4")); } return stringBuilder.ToString(); } private static void SendArgPrefix(object[] __args) { try { if (__args == null) { return; } for (int i = 0; i < __args.Length; i++) { if (!(__args[i] is string text) || string.IsNullOrEmpty(text)) { continue; } if (DebugLog) { Log.LogInfo((object)("[send-arg] IN len=" + text.Length + " : " + Dump(text))); } if (ArabicFixer.ContainsArabic(text) && !ArabicFixer.IsAlreadyShaped(text)) { string text2 = (string)(__args[i] = ArabicFixer.Fix(text)); if (DebugLog) { Log.LogInfo((object)("[send-arg] OUT len=" + text2.Length + " : " + Dump(text2))); } } break; } } catch (Exception ex) { Log.LogError((object)("SendArgPrefix: " + ex)); } } private static void SendFieldPrefix(object __instance) { try { if (MessageField == null || __instance == null) { return; } string text = MessageField.GetValue(__instance) as string; if (string.IsNullOrEmpty(text)) { return; } if (DebugLog) { Log.LogInfo((object)("[send-field] IN len=" + text.Length + " : " + Dump(text))); } if (ArabicFixer.ContainsArabic(text) && !ArabicFixer.IsAlreadyShaped(text)) { string text2 = ArabicFixer.Fix(text); MessageField.SetValue(__instance, text2); if (DebugLog) { Log.LogInfo((object)("[send-field] OUT len=" + text2.Length + " : " + Dump(text2))); } } } catch (Exception ex) { Log.LogError((object)("SendFieldPrefix: " + ex)); } } private static string Sig(MethodInfo m) { StringBuilder stringBuilder = new StringBuilder(); ParameterInfo[] parameters = m.GetParameters(); foreach (ParameterInfo parameterInfo in parameters) { if (stringBuilder.Length > 0) { stringBuilder.Append(", "); } stringBuilder.Append(parameterInfo.ParameterType.Name).Append(' ').Append(parameterInfo.Name); } return stringBuilder.ToString(); } private void DumpType(Type t) { try { Log.LogInfo((object)("===== members of " + t.FullName + " (use for SendMethodOverride / MessageFieldOverride) =====")); Log.LogInfo((object)"-- string fields --"); FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(string)) { Log.LogInfo((object)(" " + fieldInfo.Name)); } } Log.LogInfo((object)"-- void instance methods --"); MethodInfo[] methods = t.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.ReturnType == typeof(void) && methodInfo.GetBaseDefinition().DeclaringType == t) { Log.LogInfo((object)(" " + methodInfo.Name + "(" + Sig(methodInfo) + ")")); } } Log.LogInfo((object)"===== end member dump ====="); } catch (Exception ex) { Log.LogError((object)("DumpType failed: " + ex)); } } private void OnDestroy() { try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch { } } } }