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 ArabicChatFix v1.1.0
plugins/ArabicRepo.dll
Decompiled 3 weeks agousing 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 ArabicRt; 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.7.0")] [assembly: AssemblyInformationalVersion("1.0.7")] [assembly: AssemblyProduct("Arabic Chat Fix")] [assembly: AssemblyTitle("ArabicRepo")] [assembly: AssemblyVersion("1.0.7.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 ArabicRt { public sealed class Options { public bool CombineAllah; public bool KeepLtrRuns = true; public bool ReverseWordOrder = true; public string WordJoiner = " "; public bool PreventWordSplit; public int MaxLineChars; public static Options Game => new Options { CombineAllah = true, WordJoiner = "\u00a0", PreventWordSplit = true }; } public static class Arabic { 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 <WrapLogical>d__20 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable { private int <>1__state; private string <>2__current; private int <>l__initialThreadId; private int max; public int <>3__max; private string line; public string <>3__line; private StringBuilder <sb>5__2; private string[] <>7__wrap2; private int <>7__wrap3; private string <w>5__5; string IEnumerator<string>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WrapLogical>d__20(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <sb>5__2 = null; <>7__wrap2 = null; <w>5__5 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (max <= 0 || line.Length <= max) { <>2__current = line; <>1__state = 1; return true; } <sb>5__2 = new StringBuilder(); <>7__wrap2 = line.Split(' '); <>7__wrap3 = 0; goto IL_015f; case 1: <>1__state = -1; return false; case 2: <>1__state = -1; <sb>5__2.Length = 0; <sb>5__2.Append(<w>5__5); goto IL_014a; case 3: { <>1__state = -1; break; } IL_0151: <>7__wrap3++; goto IL_015f; IL_014a: <w>5__5 = null; goto IL_0151; IL_015f: if (<>7__wrap3 < <>7__wrap2.Length) { <w>5__5 = <>7__wrap2[<>7__wrap3]; if (<w>5__5.Length != 0) { if (<sb>5__2.Length == 0) { <sb>5__2.Append(<w>5__5); } else { if (<sb>5__2.Length + 1 + <w>5__5.Length > max) { <>2__current = <sb>5__2.ToString(); <>1__state = 2; return true; } <sb>5__2.Append(' ').Append(<w>5__5); } goto IL_014a; } goto IL_0151; } <>7__wrap2 = null; if (<sb>5__2.Length > 0) { <>2__current = <sb>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<string> IEnumerable<string>.GetEnumerator() { <WrapLogical>d__20 <WrapLogical>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <WrapLogical>d__ = this; } else { <WrapLogical>d__ = new <WrapLogical>d__20(0); } <WrapLogical>d__.line = <>3__line; <WrapLogical>d__.max = <>3__max; return <WrapLogical>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<string>)this).GetEnumerator(); } } private static readonly Dictionary<int, Glyph> Forms = new Dictionary<int, Glyph> { { 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<int, int[]> LamAlef = new Dictionary<int, int[]> { { 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 const string AllahStr = "الله"; private static readonly Dictionary<int, int> ToBase = BuildInverse(); private static readonly Dictionary<int, string> LamAlefInverse = BuildLamAlefInverse(); private static Dictionary<int, int> BuildInverse() { Dictionary<int, int> dictionary = new Dictionary<int, int>(); foreach (KeyValuePair<int, Glyph> form in Forms) { Glyph value = form.Value; int[] array = new int[4] { value.Iso, value.Ini, value.Med, value.Fin }; foreach (int num in array) { if (num != 0 && !dictionary.ContainsKey(num)) { dictionary[num] = form.Key; } } } return dictionary; } private static Dictionary<int, string> BuildLamAlefInverse() { Dictionary<int, string> dictionary = new Dictionary<int, string>(); foreach (KeyValuePair<int, int[]> item in LamAlef) { int[] value = item.Value; foreach (int key in value) { dictionary[key] = "ل" + (char)item.Key; } } return dictionary; } 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 IsShaped(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 int PrevNT(int[] c, int i) { int num = i - 1; while (num >= 0 && JType(c[num]) == 'T') { num--; } return num; } private static int NextNT(int[] c, int n, int i) { int j; for (j = i + 1; j < n && JType(c[j]) == 'T'; j++) { } return j; } 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 flag = num == 0 || !IsArabicLetter(text[num - 1]); int num3 = num + "الله".Length; bool flag2 = num3 >= text.Length || !IsArabicLetter(text[num3]); stringBuilder.Append(text, num2, num - num2); stringBuilder.Append((flag && flag2) ? "ﷲ" : "الله"); num2 = num3; num = text.IndexOf("الله", num2, StringComparison.Ordinal); } stringBuilder.Append(text, num2, text.Length - num2); return stringBuilder.ToString(); } public static string Shape(string text, bool combineAllah = false) { if (string.IsNullOrEmpty(text)) { return 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 = NextNT(array, length, num); if (num3 < length && LamAlef.ContainsKey(array[num3])) { int num4 = PrevNT(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 = PrevNT(array, num); int num6 = NextNT(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)) ? ((!flag2) ? ((!flag3) ? value.Iso : ((value.Ini != 0) ? value.Ini : value.Iso)) : ((value.Fin != 0) ? value.Fin : value.Iso)) : ((value.Med != 0) ? value.Med : ((value.Fin != 0) ? value.Fin : value.Iso))); stringBuilder.Append((char)num7); num++; } return stringBuilder.ToString(); } private static bool IsLtr(int c) { if ((c < 65 || c > 90) && (c < 97 || c > 122) && (c < 192 || c > 591)) { return c == 8206; } return true; } private static bool IsDigit(int c) { if ((c < 48 || c > 57) && (c < 1632 || c > 1641)) { if (c >= 1776) { return c <= 1785; } return false; } return true; } private static bool IsGlue(int c) { switch ((char)(ushort)c) { case '#': case '%': case '&': case '+': case ',': case '-': case '.': case '/': case ':': case '=': case '?': case '@': case '_': return true; default: return false; } } private static string BidiLine(string s, bool keepLtr) { 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 (keepLtr && flag) { int num3 = num; while (num3 < length) { int num4 = array[num3]; if (IsLtr(num4) || IsDigit(num4)) { num3++; continue; } if ((num4 != 32 && !IsGlue(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(); } [IteratorStateMachine(typeof(<WrapLogical>d__20))] private static IEnumerable<string> WrapLogical(string line, int max) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WrapLogical>d__20(-2) { <>3__line = line, <>3__max = max }; } private static string ProcessLine(string line, Options o) { line = line.Trim(' ', '\t'); if (line.Length == 0) { return line; } string text; if (o.ReverseWordOrder) { text = BidiLine(Shape(line, o.CombineAllah), o.KeepLtrRuns).Trim(' ', '\t'); } else { string[] array = line.Split(' '); for (int i = 0; i < array.Length; i++) { if (ContainsArabic(array[i])) { array[i] = BidiLine(Shape(array[i], o.CombineAllah), o.KeepLtrRuns); } } text = string.Join(" ", array); } if (o.PreventWordSplit && o.WordJoiner != " ") { text = text.Replace(" ", o.WordJoiner); } return text; } public static string Fix(string text, Options opts = null) { if (string.IsNullOrEmpty(text)) { return text; } Options options = opts ?? new Options(); if (!ContainsArabic(text)) { return text; } if (IsShaped(text)) { return text; } string[] array = text.Replace("\r", "").Split('\n'); List<string> list = new List<string>(); string[] array2 = array; for (int i = 0; i < array2.Length; i++) { foreach (string item in WrapLogical(array2[i], options.MaxLineChars)) { list.Add(ProcessLine(item, options)); } } return string.Join("\n", list); } public static string Unfix(string text) { if (string.IsNullOrEmpty(text) || !IsShaped(text)) { return text; } string[] array = text.Replace("\r", "").Split('\n'); List<string> list = new List<string>(); string[] array2 = array; for (int i = 0; i < array2.Length; i++) { string text2 = BidiLine(array2[i], keepLtr: true); StringBuilder stringBuilder = new StringBuilder(text2.Length); string text3 = text2; foreach (char c in text3) { int num = c; string value; int value2; if (num == 65010) { stringBuilder.Append("الله"); } else if (LamAlefInverse.TryGetValue(num, out value)) { stringBuilder.Append(value); } else if (ToBase.TryGetValue(num, out value2)) { stringBuilder.Append((char)value2); } else if (c == '\u00a0') { stringBuilder.Append(' '); } else { stringBuilder.Append(c); } } list.Add(stringBuilder.ToString().Trim()); } return string.Join(" ", list).Trim(); } } } 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 <WrapLogical>d__22 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable { private int <>1__state; private string <>2__current; private int <>l__initialThreadId; private string line; public string <>3__line; private StringBuilder <sb>5__2; private string[] <>7__wrap2; private int <>7__wrap3; private string <w>5__5; string IEnumerator<string>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WrapLogical>d__22(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <sb>5__2 = null; <>7__wrap2 = null; <w>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(' '); <sb>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; <sb>5__2.Length = 0; <sb>5__2.Append(<w>5__5); goto IL_0149; case 3: { <>1__state = -1; break; } IL_0150: <>7__wrap3++; goto IL_015e; IL_0149: <w>5__5 = null; goto IL_0150; IL_015e: if (<>7__wrap3 < <>7__wrap2.Length) { <w>5__5 = <>7__wrap2[<>7__wrap3]; if (<w>5__5.Length != 0) { if (<sb>5__2.Length == 0) { <sb>5__2.Append(<w>5__5); } else { if (<sb>5__2.Length + 1 + <w>5__5.Length > MaxLineChars) { <>2__current = <sb>5__2.ToString(); <>1__state = 2; return true; } <sb>5__2.Append(' ').Append(<w>5__5); } goto IL_0149; } goto IL_0150; } <>7__wrap2 = null; if (<sb>5__2.Length > 0) { <>2__current = <sb>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<string> IEnumerable<string>.GetEnumerator() { <WrapLogical>d__22 <WrapLogical>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <WrapLogical>d__ = this; } else { <WrapLogical>d__ = new <WrapLogical>d__22(0); } <WrapLogical>d__.line = <>3__line; return <WrapLogical>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<string>)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<int, Glyph> Forms = new Dictionary<int, Glyph> { { 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<int, int[]> LamAlef = new Dictionary<int, int[]> { { 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<string> list = new List<string>(); 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(<WrapLogical>d__22))] private static IEnumerable<string> WrapLogical(string line) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WrapLogical>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.1.0")] public class Plugin : BaseUnityPlugin { [CompilerGenerated] private sealed class <SmoothFontRoutine>d__30 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Plugin <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <SmoothFontRoutine>d__30(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 <WaitForChatThenHook>d__34 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Plugin <>4__this; private Type <t>5__2; private float <deadline>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForChatThenHook>d__34(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <t>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; <t>5__2 = null; <deadline>5__3 = Time.realtimeSinceStartup + 120f; break; case 1: <>1__state = -1; break; } if (Time.realtimeSinceStartup < <deadline>5__3) { <t>5__2 = AccessTools.TypeByName(plugin._cfgChatType.Value); if (!(<t>5__2 != null)) { <>2__current = (object)new WaitForSeconds(0.5f); <>1__state = 1; return true; } } if (<t>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(<t>5__2); } plugin.ResolveMessageField(<t>5__2); plugin.HookSendMethods(<t>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(); } } [CompilerGenerated] private sealed class <WaitForEspeakThenHook>d__40 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Plugin <>4__this; private Type <t>5__2; private float <deadline>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForEspeakThenHook>d__40(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <t>5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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(2f); <>1__state = 1; return true; case 1: <>1__state = -1; <t>5__2 = null; <deadline>5__3 = Time.realtimeSinceStartup + 30f; break; case 2: <>1__state = -1; break; } if (Time.realtimeSinceStartup < <deadline>5__3) { <t>5__2 = FindTypeQuiet(plugin._cfgEspeakType.Value); if (!(<t>5__2 != null)) { <>2__current = (object)new WaitForSeconds(1f); <>1__state = 2; return true; } } if (<t>5__2 == null) { Log.LogInfo((object)"espeakTTS not detected; Arabic TTS un-bake inactive (fine if you don't use espeakTTS)."); return false; } Log.LogInfo((object)("espeakTTS detected (" + <t>5__2.FullName + "); enabling Arabic TTS un-bake.")); plugin.HookEspeakMethods(<t>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.1.0"; internal static ManualLogSource Log; private Harmony _harmony; private ConfigEntry<bool> _cfgFixOutgoing; private ConfigEntry<bool> _cfgFixDisplay; private ConfigEntry<bool> _cfgCombineAllah; private ConfigEntry<bool> _cfgKeepLtrRuns; private ConfigEntry<bool> _cfgReverseWordOrder; private ConfigEntry<string> _cfgWordSeparator; private ConfigEntry<int> _cfgMaxLineChars; private ConfigEntry<bool> _cfgSmoothFont; private ConfigEntry<string> _cfgSmoothFontName; private ConfigEntry<string> _cfgChatType; private ConfigEntry<string> _cfgSendMethods; private ConfigEntry<string> _cfgMessageField; private ConfigEntry<bool> _cfgAggressive; private ConfigEntry<bool> _cfgDump; private ConfigEntry<bool> _cfgDebug; private ConfigEntry<bool> _cfgUnbakeEspeak; private ConfigEntry<string> _cfgEspeakType; private ConfigEntry<string> _cfgEspeakMethods; private ConfigEntry<bool> _cfgForceArabicVoice; private ConfigEntry<string> _cfgArabicLang; internal static FieldInfo MessageField; internal static bool DebugLog = false; internal static bool ForceArabicVoice = true; internal static string ArabicLang = "ar"; private void Awake() { //IL_0401: Unknown result type (might be due to invalid IL or missing references) //IL_040b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _cfgFixOutgoing = ((BaseUnityPlugin)this).Config.Bind<bool>("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<bool>("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<bool>("1. Behaviour", "CombineAllahLigature", true, "Collapse the standalone word الله into the single ﷲ ligature glyph."); _cfgKeepLtrRuns = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Behaviour", "KeepLatinAndNumbersInOrder", true, "Keep Latin words, numbers, emails and URLs in normal reading order inside Arabic text."); _cfgReverseWordOrder = ((BaseUnityPlugin)this).Config.Bind<bool>("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<string>("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<int>("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<bool>("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<string>("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<string>("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<string>("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<string>("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<bool>("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<bool>("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<bool>("2. Hook (advanced)", "DebugLogging", true, "Log the raw and converted text on every send (char codes), for troubleshooting."); _cfgUnbakeEspeak = ((BaseUnityPlugin)this).Config.Bind<bool>("4. TTS", "UnbakeForEspeak", true, "Un-bake Arabic right before espeakTTS speaks it so the VOICE is pronounced correctly. The on-screen text is unaffected (everyone still reads it normally)."); _cfgEspeakType = ((BaseUnityPlugin)this).Config.Bind<string>("4. TTS", "EspeakTypeName", "repo_espeak.punEspeak", "The espeakTTS class that generates the audio. Change only if espeakTTS is updated and renames it."); _cfgEspeakMethods = ((BaseUnityPlugin)this).Config.Bind<string>("4. TTS", "EspeakMethodNames", "eSpeakChatMessageSendRPC", "Comma-separated espeakTTS method(s) to hook. The FIRST parameter of each must be the message string."); _cfgForceArabicVoice = ((BaseUnityPlugin)this).Config.Bind<bool>("4. TTS", "ForceArabicVoice", true, "Automatically speak Arabic messages with espeak's Arabic voice, even if espeakTTS's own Language isn't set to Arabic. Only affects Arabic messages; other languages are left alone."); _cfgArabicLang = ((BaseUnityPlugin)this).Config.Bind<string>("4. TTS", "ArabicLanguageCode", "ar", "espeak language code used for Arabic messages (default: ar)."); DebugLog = _cfgDebug.Value; ForceArabicVoice = _cfgForceArabicVoice.Value; ArabicLang = (string.IsNullOrEmpty(_cfgArabicLang.Value) ? "ar" : _cfgArabicLang.Value.Trim()); 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()); } if (_cfgUnbakeEspeak.Value) { ((MonoBehaviour)this).StartCoroutine(WaitForEspeakThenHook()); } Log.LogInfo((object)"Arabic Chat Fix v1.1.0 loaded."); } [IteratorStateMachine(typeof(<SmoothFontRoutine>d__30))] private IEnumerator SmoothFontRoutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <SmoothFontRoutine>d__30(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(<WaitForChatThenHook>d__34))] private IEnumerator WaitForChatThenHook() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForChatThenHook>d__34(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<MethodInfo> list = new List<MethodInfo>(); 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)); } } [IteratorStateMachine(typeof(<WaitForEspeakThenHook>d__40))] private IEnumerator WaitForEspeakThenHook() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForEspeakThenHook>d__40(0) { <>4__this = this }; } private static Type FindTypeQuiet(string name) { if (string.IsNullOrEmpty(name)) { return null; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { Type type = assembly.GetType(name, throwOnError: false); if (type != null) { return type; } } catch { } } assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { try { Type[] types = assembly2.GetTypes(); foreach (Type type2 in types) { if (type2.FullName == name || type2.Name == name) { return type2; } } } catch { } } return null; } private void HookEspeakMethods(Type t) { //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) //IL_0027: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown HarmonyMethod val = new HarmonyMethod(typeof(Plugin).GetMethod("EspeakUnbakePrefix", BindingFlags.Static | BindingFlags.NonPublic)) { priority = 800 }; HarmonyMethod val2 = new HarmonyMethod(typeof(Plugin).GetMethod("ForceLangPrefix", BindingFlags.Static | BindingFlags.NonPublic)) { priority = 800 }; int num = 0; string[] array = (_cfgEspeakMethods.Value ?? "").Split(','); foreach (string text in array) { string name = text.Trim(); if (name.Length == 0) { continue; } MethodInfo[] array2; try { array2 = Array.FindAll(t.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic), (MethodInfo m) => m.Name == name); } catch (Exception ex) { Log.LogError((object)("espeak reflection on '" + name + "': " + ex.Message)); continue; } if (array2.Length == 0) { Log.LogWarning((object)("espeak method '" + name + "' not found on " + t.Name)); continue; } MethodInfo[] array3 = array2; foreach (MethodInfo methodInfo in array3) { try { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 0 || parameters[0].ParameterType != typeof(string)) { if (DebugLog) { Log.LogInfo((object)("Skipping espeak " + name + "(…) — first parameter is not the message string.")); } continue; } _harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; Log.LogInfo((object)("Hooked espeak " + t.Name + "." + name + "(" + Sig(methodInfo) + ")")); if (ForceArabicVoice && parameters.Length > 2 && parameters[2].ParameterType == typeof(string)) { try { _harmony.Patch((MethodBase)methodInfo, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)(" + Arabic voice language forcing enabled on " + name + ".")); } catch (Exception ex2) { Log.LogWarning((object)("Could not enable language forcing on " + name + ": " + ex2.Message)); } } } catch (Exception ex3) { Log.LogError((object)("Failed to hook espeak " + name + ": " + ex3)); } } } if (num == 0) { Log.LogWarning((object)"Arabic TTS un-bake: hooked 0 espeak methods. Set EspeakMethodNames in the config."); } else { Log.LogInfo((object)("Arabic TTS un-bake active — " + num + " espeak method(s) hooked.")); } } [HarmonyPriority(800)] private static void EspeakUnbakePrefix(ref string __0) { try { if (string.IsNullOrEmpty(__0) || !Arabic.IsShaped(__0)) { return; } string text = Arabic.Unfix(__0); if (!string.Equals(text, __0, StringComparison.Ordinal)) { if (DebugLog) { Log.LogInfo((object)("[tts] un-baked for espeak: \"" + __0 + "\" -> \"" + text + "\"")); } __0 = text; } } catch (Exception ex) { Log.LogError((object)("EspeakUnbakePrefix: " + ex)); } } [HarmonyPriority(800)] private static void ForceLangPrefix(string __0, ref string __2) { try { if (ForceArabicVoice && !string.IsNullOrEmpty(__0) && (Arabic.ContainsArabic(__0) || Arabic.IsShaped(__0)) && !string.Equals(__2, ArabicLang, StringComparison.Ordinal)) { if (DebugLog) { Log.LogInfo((object)("[tts] forcing voice language -> " + ArabicLang + " for Arabic message.")); } __2 = ArabicLang; } } catch (Exception ex) { Log.LogError((object)("ForceLangPrefix: " + 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 { } } } }