Decompiled source of ArabicChatFix v1.0.7

plugins/ArabicRepo.dll

Decompiled 14 hours ago
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 <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.0.5")]
	public class Plugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private sealed class <SmoothFontRoutine>d__23 : 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__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 <WaitForChatThenHook>d__27 : 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__27(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();
			}
		}

		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<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;

		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<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.");
			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(<SmoothFontRoutine>d__23))]
		private IEnumerator SmoothFontRoutine()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SmoothFontRoutine>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(<WaitForChatThenHook>d__27))]
		private IEnumerator WaitForChatThenHook()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <WaitForChatThenHook>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<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));
			}
		}

		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
			{
			}
		}
	}
}