This commit is contained in:
Gurge44 committed 2026-07-21 18:36:29 +02:00
1 parent 0d9e4d11c7
commit 59be8d587e
24 files changed
+300 -311

No files matched your search

+240 -215
View File
@@ -1,56 +1,50 @@
using System;
using System.Collections.Generic;
using System.Text;
// ReSharper disable InconsistentNaming
namespace ArabicSupport
namespace EHR.ArabicSupport
{
public class ArabicFixer
public static class ArabicFixer
{
public static string Fix(string str)
{
return Fix(str, false, true);
}
public static string Fix(string str, bool rtl)
{
if (rtl)
{
return Fix(str);
}
else
string[] words = str.Split(' ');
string result = "";
string arabicToIgnore = "";
foreach (string word in words)
{
string[] words = str.Split(' ');
string result = "";
string arabicToIgnore = "";
foreach (string word in words)
if (word.Length > 0 && char.IsLower(word.ToLower()[word.Length / 2]))
{
if (word.Length > 0 && char.IsLower(word.ToLower()[word.Length / 2]))
{
result += Fix(arabicToIgnore) + word + " ";
arabicToIgnore = "";
}
else
{
arabicToIgnore += word + " ";
}
result += Fix(arabicToIgnore) + word + " ";
arabicToIgnore = "";
}
else
{
arabicToIgnore += word + " ";
}
if (arabicToIgnore != "")
result += Fix(arabicToIgnore);
return result;
}
if (arabicToIgnore != "")
result += Fix(arabicToIgnore);
return result;
}
public static string Fix(string str, bool showTashkeel, bool useHinduNumbers)
// ReSharper disable once MethodOverloadWithOptionalParameter
private static string Fix(string str, bool showTashkeel = false, bool useHinduNumbers = true)
{
ArabicFixerTool.showTashkeel = showTashkeel;
ArabicFixerTool.useHinduNumbers = useHinduNumbers;
if (str.Contains("\n"))
if (str.Contains('\n'))
str = str.Replace("\n", Environment.NewLine);
if (str.Contains(Environment.NewLine))
{
string[] stringSeparators = new string[] { Environment.NewLine };
string[] stringSeparators = [Environment.NewLine];
string[] strSplit = str.Split(stringSeparators, StringSplitOptions.None);
if (strSplit.Length <= 1)
return ArabicFixerTool.FixLine(str);
@@ -64,16 +58,8 @@ namespace ArabicSupport
}
return outputString;
}
else
{
return ArabicFixerTool.FixLine(str);
}
}
public static string Fix(string str, bool showTashkeel, bool combineTashkeel, bool useHinduNumbers)
{
ArabicFixerTool.combineTashkeel = combineTashkeel;
return Fix(str, showTashkeel, useHinduNumbers);
return ArabicFixerTool.FixLine(str);
}
}
@@ -105,152 +91,135 @@ namespace ArabicSupport
PersianGaf = 0x06AF, PersianGaf2 = 0x06A9, PersianYeh = 0x06CC,
}
internal struct ArabicMapping
internal static class ArabicTable
{
public int from;
public int to;
public ArabicMapping(int from, int to) { this.from = from; this.to = to; }
private static readonly Dictionary<int, int> Map = new()
{
[(int)GeneralArabicLetters.Hamza] = (int)IsolatedArabicLetters.Hamza,
[(int)GeneralArabicLetters.Alef] = (int)IsolatedArabicLetters.Alef,
[(int)GeneralArabicLetters.AlefHamza] = (int)IsolatedArabicLetters.AlefHamza,
[(int)GeneralArabicLetters.WawHamza] = (int)IsolatedArabicLetters.WawHamza,
[(int)GeneralArabicLetters.AlefMaksoor] = (int)IsolatedArabicLetters.AlefMaksoor,
[(int)GeneralArabicLetters.AlefMagsora] = (int)IsolatedArabicLetters.AlefMaksora,
[(int)GeneralArabicLetters.HamzaNabera] = (int)IsolatedArabicLetters.HamzaNabera,
[(int)GeneralArabicLetters.Ba] = (int)IsolatedArabicLetters.Ba,
[(int)GeneralArabicLetters.Ta] = (int)IsolatedArabicLetters.Ta,
[(int)GeneralArabicLetters.Tha2] = (int)IsolatedArabicLetters.Tha2,
[(int)GeneralArabicLetters.Jeem] = (int)IsolatedArabicLetters.Jeem,
[(int)GeneralArabicLetters.H7aa] = (int)IsolatedArabicLetters.H7aa,
[(int)GeneralArabicLetters.Khaa2] = (int)IsolatedArabicLetters.Khaa2,
[(int)GeneralArabicLetters.Dal] = (int)IsolatedArabicLetters.Dal,
[(int)GeneralArabicLetters.Thal] = (int)IsolatedArabicLetters.Thal,
[(int)GeneralArabicLetters.Ra2] = (int)IsolatedArabicLetters.Ra2,
[(int)GeneralArabicLetters.Zeen] = (int)IsolatedArabicLetters.Zeen,
[(int)GeneralArabicLetters.Seen] = (int)IsolatedArabicLetters.Seen,
[(int)GeneralArabicLetters.Sheen] = (int)IsolatedArabicLetters.Sheen,
[(int)GeneralArabicLetters.S9a] = (int)IsolatedArabicLetters.S9a,
[(int)GeneralArabicLetters.Dha] = (int)IsolatedArabicLetters.Dha,
[(int)GeneralArabicLetters.T6a] = (int)IsolatedArabicLetters.T6a,
[(int)GeneralArabicLetters.T6ha] = (int)IsolatedArabicLetters.T6ha,
[(int)GeneralArabicLetters.Ain] = (int)IsolatedArabicLetters.Ain,
[(int)GeneralArabicLetters.Gain] = (int)IsolatedArabicLetters.Gain,
[(int)GeneralArabicLetters.Fa] = (int)IsolatedArabicLetters.Fa,
[(int)GeneralArabicLetters.Gaf] = (int)IsolatedArabicLetters.Gaf,
[(int)GeneralArabicLetters.Kaf] = (int)IsolatedArabicLetters.Kaf,
[(int)GeneralArabicLetters.Lam] = (int)IsolatedArabicLetters.Lam,
[(int)GeneralArabicLetters.Meem] = (int)IsolatedArabicLetters.Meem,
[(int)GeneralArabicLetters.Noon] = (int)IsolatedArabicLetters.Noon,
[(int)GeneralArabicLetters.Ha] = (int)IsolatedArabicLetters.Ha,
[(int)GeneralArabicLetters.Waw] = (int)IsolatedArabicLetters.Waw,
[(int)GeneralArabicLetters.Ya] = (int)IsolatedArabicLetters.Ya,
[(int)GeneralArabicLetters.AlefMad] = (int)IsolatedArabicLetters.AlefMad,
[(int)GeneralArabicLetters.TaMarboota] = (int)IsolatedArabicLetters.TaMarboota,
[(int)GeneralArabicLetters.PersianPe] = (int)IsolatedArabicLetters.PersianPe,
[(int)GeneralArabicLetters.PersianChe] = (int)IsolatedArabicLetters.PersianChe,
[(int)GeneralArabicLetters.PersianZe] = (int)IsolatedArabicLetters.PersianZe,
[(int)GeneralArabicLetters.PersianGaf] = (int)IsolatedArabicLetters.PersianGaf,
[(int)GeneralArabicLetters.PersianGaf2] = (int)IsolatedArabicLetters.PersianGaf2,
[(int)GeneralArabicLetters.PersianYeh] = (int)IsolatedArabicLetters.PersianYeh
};
internal static int Convert(int value) => Map.GetValueOrDefault(value, value);
}
internal class ArabicTable
internal class TashkeelLocation(char tashkeel, int position)
{
private static ArabicMapping[] mapList;
private static ArabicTable arabicMapper;
private ArabicTable()
{
mapList = new[] {
new ArabicMapping((int)GeneralArabicLetters.Hamza, (int)IsolatedArabicLetters.Hamza),
new ArabicMapping((int)GeneralArabicLetters.Alef, (int)IsolatedArabicLetters.Alef),
new ArabicMapping((int)GeneralArabicLetters.AlefHamza, (int)IsolatedArabicLetters.AlefHamza),
new ArabicMapping((int)GeneralArabicLetters.WawHamza, (int)IsolatedArabicLetters.WawHamza),
new ArabicMapping((int)GeneralArabicLetters.AlefMaksoor, (int)IsolatedArabicLetters.AlefMaksoor),
new ArabicMapping((int)GeneralArabicLetters.AlefMagsora, (int)IsolatedArabicLetters.AlefMaksora),
new ArabicMapping((int)GeneralArabicLetters.HamzaNabera, (int)IsolatedArabicLetters.HamzaNabera),
new ArabicMapping((int)GeneralArabicLetters.Ba, (int)IsolatedArabicLetters.Ba),
new ArabicMapping((int)GeneralArabicLetters.Ta, (int)IsolatedArabicLetters.Ta),
new ArabicMapping((int)GeneralArabicLetters.Tha2, (int)IsolatedArabicLetters.Tha2),
new ArabicMapping((int)GeneralArabicLetters.Jeem, (int)IsolatedArabicLetters.Jeem),
new ArabicMapping((int)GeneralArabicLetters.H7aa, (int)IsolatedArabicLetters.H7aa),
new ArabicMapping((int)GeneralArabicLetters.Khaa2, (int)IsolatedArabicLetters.Khaa2),
new ArabicMapping((int)GeneralArabicLetters.Dal, (int)IsolatedArabicLetters.Dal),
new ArabicMapping((int)GeneralArabicLetters.Thal, (int)IsolatedArabicLetters.Thal),
new ArabicMapping((int)GeneralArabicLetters.Ra2, (int)IsolatedArabicLetters.Ra2),
new ArabicMapping((int)GeneralArabicLetters.Zeen, (int)IsolatedArabicLetters.Zeen),
new ArabicMapping((int)GeneralArabicLetters.Seen, (int)IsolatedArabicLetters.Seen),
new ArabicMapping((int)GeneralArabicLetters.Sheen, (int)IsolatedArabicLetters.Sheen),
new ArabicMapping((int)GeneralArabicLetters.S9a, (int)IsolatedArabicLetters.S9a),
new ArabicMapping((int)GeneralArabicLetters.Dha, (int)IsolatedArabicLetters.Dha),
new ArabicMapping((int)GeneralArabicLetters.T6a, (int)IsolatedArabicLetters.T6a),
new ArabicMapping((int)GeneralArabicLetters.T6ha, (int)IsolatedArabicLetters.T6ha),
new ArabicMapping((int)GeneralArabicLetters.Ain, (int)IsolatedArabicLetters.Ain),
new ArabicMapping((int)GeneralArabicLetters.Gain, (int)IsolatedArabicLetters.Gain),
new ArabicMapping((int)GeneralArabicLetters.Fa, (int)IsolatedArabicLetters.Fa),
new ArabicMapping((int)GeneralArabicLetters.Gaf, (int)IsolatedArabicLetters.Gaf),
new ArabicMapping((int)GeneralArabicLetters.Kaf, (int)IsolatedArabicLetters.Kaf),
new ArabicMapping((int)GeneralArabicLetters.Lam, (int)IsolatedArabicLetters.Lam),
new ArabicMapping((int)GeneralArabicLetters.Meem, (int)IsolatedArabicLetters.Meem),
new ArabicMapping((int)GeneralArabicLetters.Noon, (int)IsolatedArabicLetters.Noon),
new ArabicMapping((int)GeneralArabicLetters.Ha, (int)IsolatedArabicLetters.Ha),
new ArabicMapping((int)GeneralArabicLetters.Waw, (int)IsolatedArabicLetters.Waw),
new ArabicMapping((int)GeneralArabicLetters.Ya, (int)IsolatedArabicLetters.Ya),
new ArabicMapping((int)GeneralArabicLetters.AlefMad, (int)IsolatedArabicLetters.AlefMad),
new ArabicMapping((int)GeneralArabicLetters.TaMarboota, (int)IsolatedArabicLetters.TaMarboota),
new ArabicMapping((int)GeneralArabicLetters.PersianPe, (int)IsolatedArabicLetters.PersianPe),
new ArabicMapping((int)GeneralArabicLetters.PersianChe, (int)IsolatedArabicLetters.PersianChe),
new ArabicMapping((int)GeneralArabicLetters.PersianZe, (int)IsolatedArabicLetters.PersianZe),
new ArabicMapping((int)GeneralArabicLetters.PersianGaf, (int)IsolatedArabicLetters.PersianGaf),
new ArabicMapping((int)GeneralArabicLetters.PersianGaf2, (int)IsolatedArabicLetters.PersianGaf2),
new ArabicMapping((int)GeneralArabicLetters.PersianYeh, (int)IsolatedArabicLetters.PersianYeh)
};
}
internal static ArabicTable ArabicMapper
{
get
{
if (arabicMapper == null)
arabicMapper = new ArabicTable();
return arabicMapper;
}
}
internal int Convert(int toBeConverted)
{
for (int i = 0; i < mapList.Length; i++)
{
var arabicMap = mapList[i];
if (arabicMap.from == toBeConverted)
return arabicMap.to;
}
return toBeConverted;
}
public char tashkeel = tashkeel;
public readonly int position = position;
}
internal class TashkeelLocation
{
public char tashkeel;
public int position;
public TashkeelLocation(char tashkeel, int position) { this.tashkeel = tashkeel; this.position = position; }
}
internal class ArabicFixerTool
internal static class ArabicFixerTool
{
internal static bool showTashkeel = true;
internal static bool combineTashkeel = true;
internal static bool useHinduNumbers = false;
internal static StringBuilder internalStringBuilder = new StringBuilder();
private const bool combineTashkeel = true;
internal static bool useHinduNumbers;
private static readonly StringBuilder internalStringBuilder = new();
internal static void RemoveTashkeel(ref string str, out List<TashkeelLocation> tashkeelLocation)
private static void RemoveTashkeel(ref string str, out List<TashkeelLocation> tashkeelLocation)
{
tashkeelLocation = new List<TashkeelLocation>();
tashkeelLocation = [];
var lastSplitIndex = 0;
internalStringBuilder.Clear();
internalStringBuilder.EnsureCapacity(str.Length);
int index = 0;
void IncrementSB(ref string s, int i)
{
if (i - lastSplitIndex > 0)
internalStringBuilder.Append(s, lastSplitIndex, i - lastSplitIndex);
lastSplitIndex = i + 1;
}
for (int i = 0; i < str.Length; i++)
{
if (str[i] == (char)0x064B) { tashkeelLocation.Add(new TashkeelLocation((char)0x064B, i)); index++; IncrementSB(ref str, i); }
else if (str[i] == (char)0x064C) { tashkeelLocation.Add(new TashkeelLocation((char)0x064C, i)); index++; IncrementSB(ref str, i); }
else if (str[i] == (char)0x064D) { tashkeelLocation.Add(new TashkeelLocation((char)0x064D, i)); index++; IncrementSB(ref str, i); }
else if (str[i] == (char)0x064E)
switch (str[i])
{
if (index > 0 && combineTashkeel && tashkeelLocation[index - 1].tashkeel == (char)0x0651) { tashkeelLocation[index - 1].tashkeel = (char)0xFC60; IncrementSB(ref str, i); continue; }
tashkeelLocation.Add(new TashkeelLocation((char)0x064E, i)); index++; IncrementSB(ref str, i);
}
else if (str[i] == (char)0x064F)
{
if (index > 0 && combineTashkeel && tashkeelLocation[index - 1].tashkeel == (char)0x0651) { tashkeelLocation[index - 1].tashkeel = (char)0xFC61; IncrementSB(ref str, i); continue; }
tashkeelLocation.Add(new TashkeelLocation((char)0x064F, i)); index++; IncrementSB(ref str, i);
}
else if (str[i] == (char)0x0650)
{
if (index > 0 && combineTashkeel && tashkeelLocation[index - 1].tashkeel == (char)0x0651) { tashkeelLocation[index - 1].tashkeel = (char)0xFC62; IncrementSB(ref str, i); continue; }
tashkeelLocation.Add(new TashkeelLocation((char)0x0650, i)); index++; IncrementSB(ref str, i);
}
else if (str[i] == (char)0x0651)
{
if (index > 0 && combineTashkeel)
case (char)0x064B:
tashkeelLocation.Add(new TashkeelLocation((char)0x064B, i)); index++; IncrementSB(ref str, i);
break;
case (char)0x064C:
tashkeelLocation.Add(new TashkeelLocation((char)0x064C, i)); index++; IncrementSB(ref str, i);
break;
case (char)0x064D:
tashkeelLocation.Add(new TashkeelLocation((char)0x064D, i)); index++; IncrementSB(ref str, i);
break;
case (char)0x064E when index > 0 && combineTashkeel && tashkeelLocation[index - 1].tashkeel == (char)0x0651:
tashkeelLocation[index - 1].tashkeel = (char)0xFC60; IncrementSB(ref str, i); continue;
case (char)0x064E:
tashkeelLocation.Add(new TashkeelLocation((char)0x064E, i)); index++; IncrementSB(ref str, i);
break;
case (char)0x064F when index > 0 && combineTashkeel && tashkeelLocation[index - 1].tashkeel == (char)0x0651:
tashkeelLocation[index - 1].tashkeel = (char)0xFC61; IncrementSB(ref str, i); continue;
case (char)0x064F:
tashkeelLocation.Add(new TashkeelLocation((char)0x064F, i)); index++; IncrementSB(ref str, i);
break;
case (char)0x0650 when index > 0 && combineTashkeel && tashkeelLocation[index - 1].tashkeel == (char)0x0651:
tashkeelLocation[index - 1].tashkeel = (char)0xFC62; IncrementSB(ref str, i); continue;
case (char)0x0650:
tashkeelLocation.Add(new TashkeelLocation((char)0x0650, i)); index++; IncrementSB(ref str, i);
break;
case (char)0x0651:
{
if (tashkeelLocation[index - 1].tashkeel == (char)0x064E) { tashkeelLocation[index - 1].tashkeel = (char)0xFC60; IncrementSB(ref str, i); continue; }
if (tashkeelLocation[index - 1].tashkeel == (char)0x064F) { tashkeelLocation[index - 1].tashkeel = (char)0xFC61; IncrementSB(ref str, i); continue; }
if (tashkeelLocation[index - 1].tashkeel == (char)0x0650) { tashkeelLocation[index - 1].tashkeel = (char)0xFC62; IncrementSB(ref str, i); continue; }
if (index > 0 && combineTashkeel)
{
switch (tashkeelLocation[index - 1].tashkeel)
{
case (char)0x064E:
tashkeelLocation[index - 1].tashkeel = (char)0xFC60; IncrementSB(ref str, i); continue;
case (char)0x064F:
tashkeelLocation[index - 1].tashkeel = (char)0xFC61; IncrementSB(ref str, i); continue;
case (char)0x0650:
tashkeelLocation[index - 1].tashkeel = (char)0xFC62; IncrementSB(ref str, i); continue;
}
}
tashkeelLocation.Add(new TashkeelLocation((char)0x0651, i)); index++; IncrementSB(ref str, i);
break;
}
tashkeelLocation.Add(new TashkeelLocation((char)0x0651, i)); index++; IncrementSB(ref str, i);
case (char)0x0652:
tashkeelLocation.Add(new TashkeelLocation((char)0x0652, i)); index++; IncrementSB(ref str, i);
break;
case (char)0x0653:
tashkeelLocation.Add(new TashkeelLocation((char)0x0653, i)); index++; IncrementSB(ref str, i);
break;
case (char)0xFC60:
case (char)0xFC61:
case (char)0xFC62:
IncrementSB(ref str, i);
break;
}
else if (str[i] == (char)0x0652) { tashkeelLocation.Add(new TashkeelLocation((char)0x0652, i)); index++; IncrementSB(ref str, i); }
else if (str[i] == (char)0x0653) { tashkeelLocation.Add(new TashkeelLocation((char)0x0653, i)); index++; IncrementSB(ref str, i); }
else if (str[i] == (char)0xFC60) IncrementSB(ref str, i);
else if (str[i] == (char)0xFC61) IncrementSB(ref str, i);
else if (str[i] == (char)0xFC62) IncrementSB(ref str, i);
}
if (lastSplitIndex != 0)
@@ -258,14 +227,23 @@ namespace ArabicSupport
IncrementSB(ref str, str.Length);
str = internalStringBuilder.ToString();
}
return;
void IncrementSB(ref string s, int i)
{
if (i - lastSplitIndex > 0)
internalStringBuilder.Append(s, lastSplitIndex, i - lastSplitIndex);
lastSplitIndex = i + 1;
}
}
internal static void ReturnTashkeel(ref char[] letters, List<TashkeelLocation> tashkeelLocation)
private static void ReturnTashkeel(ref char[] letters, List<TashkeelLocation> tashkeelLocation)
{
Array.Resize(ref letters, letters.Length + tashkeelLocation.Count);
for (int i = 0; i < tashkeelLocation.Count; i++)
foreach (TashkeelLocation tl in tashkeelLocation)
{
var tl = tashkeelLocation[i];
for (int j = letters.Length - 1; j > tl.position; j--)
letters[j] = letters[j - 1];
letters[tl.position] = tl.tashkeel;
@@ -280,7 +258,7 @@ namespace ArabicSupport
char[] lettersFinal = str.ToCharArray();
for (int i = 0; i < lettersOrigin.Length; i++)
lettersOrigin[i] = (char)ArabicTable.ArabicMapper.Convert(str[i]);
lettersOrigin[i] = (char)ArabicTable.Convert(str[i]);
for (int i = 0; i < lettersOrigin.Length; i++)
{
@@ -288,10 +266,21 @@ namespace ArabicSupport
if (lettersOrigin[i] == (char)IsolatedArabicLetters.Lam && i < lettersOrigin.Length - 1)
{
if (lettersOrigin[i + 1] == (char)IsolatedArabicLetters.AlefMaksoor) { lettersOrigin[i] = (char)0xFEF7; lettersFinal[i + 1] = (char)0xFFFF; skip = true; }
else if (lettersOrigin[i + 1] == (char)IsolatedArabicLetters.Alef) { lettersOrigin[i] = (char)0xFEF9; lettersFinal[i + 1] = (char)0xFFFF; skip = true; }
else if (lettersOrigin[i + 1] == (char)IsolatedArabicLetters.AlefHamza) { lettersOrigin[i] = (char)0xFEF5; lettersFinal[i + 1] = (char)0xFFFF; skip = true; }
else if (lettersOrigin[i + 1] == (char)IsolatedArabicLetters.AlefMad) { lettersOrigin[i] = (char)0xFEF3; lettersFinal[i + 1] = (char)0xFFFF; skip = true; }
switch (lettersOrigin[i + 1])
{
case (char)IsolatedArabicLetters.AlefMaksoor:
lettersOrigin[i] = (char)0xFEF7; lettersFinal[i + 1] = (char)0xFFFF; skip = true;
break;
case (char)IsolatedArabicLetters.Alef:
lettersOrigin[i] = (char)0xFEF9; lettersFinal[i + 1] = (char)0xFFFF; skip = true;
break;
case (char)IsolatedArabicLetters.AlefHamza:
lettersOrigin[i] = (char)0xFEF5; lettersFinal[i + 1] = (char)0xFFFF; skip = true;
break;
case (char)IsolatedArabicLetters.AlefMad:
lettersOrigin[i] = (char)0xFEF3; lettersFinal[i + 1] = (char)0xFFFF; skip = true;
break;
}
}
if (!IsIgnoredCharacter(lettersOrigin[i]))
@@ -315,29 +304,36 @@ namespace ArabicSupport
internalStringBuilder.EnsureCapacity(lettersFinal.Length);
List<char> numberList = null;
void AddNumber(char value) { numberList ??= new List<char>(); numberList.Add(value); }
void AppendNumbers()
{
if (numberList != null && numberList.Count > 0)
{
for (int j = 0; j < numberList.Count; j++)
internalStringBuilder.Append(numberList[numberList.Count - 1 - j]);
numberList.Clear();
}
}
for (int i = lettersFinal.Length - 1; i >= 0; i--)
{
if (char.IsPunctuation(lettersFinal[i]) && i > 0 && i < lettersFinal.Length - 1 &&
(char.IsPunctuation(lettersFinal[i - 1]) || char.IsPunctuation(lettersFinal[i + 1])))
{
if (lettersFinal[i] == '(') internalStringBuilder.Append(')');
else if (lettersFinal[i] == ')') internalStringBuilder.Append('(');
else if (lettersFinal[i] == '<') internalStringBuilder.Append('>');
else if (lettersFinal[i] == '>') internalStringBuilder.Append('<');
else if (lettersFinal[i] == '[') internalStringBuilder.Append(']');
else if (lettersFinal[i] == ']') internalStringBuilder.Append('[');
else if (lettersFinal[i] != 0xFFFF) internalStringBuilder.Append(lettersFinal[i]);
switch (lettersFinal[i])
{
case '(':
internalStringBuilder.Append(')');
break;
case ')':
internalStringBuilder.Append('(');
break;
case '<':
internalStringBuilder.Append('>');
break;
case '>':
internalStringBuilder.Append('<');
break;
case '[':
internalStringBuilder.Append(']');
break;
case ']':
internalStringBuilder.Append('[');
break;
default:
if (lettersFinal[i] != 0xFFFF) internalStringBuilder.Append(lettersFinal[i]);
break;
}
}
else if (lettersFinal[i] == ' ' && i > 0 && i < lettersFinal.Length - 1 &&
(char.IsLower(lettersFinal[i - 1]) || char.IsUpper(lettersFinal[i - 1]) || char.IsNumber(lettersFinal[i - 1])) &&
@@ -348,13 +344,30 @@ namespace ArabicSupport
else if (char.IsNumber(lettersFinal[i]) || char.IsLower(lettersFinal[i]) || char.IsUpper(lettersFinal[i]) ||
char.IsSymbol(lettersFinal[i]) || char.IsPunctuation(lettersFinal[i]))
{
if (lettersFinal[i] == '(') AddNumber(')');
else if (lettersFinal[i] == ')') AddNumber('(');
else if (lettersFinal[i] == '<') AddNumber('>');
else if (lettersFinal[i] == '>') AddNumber('<');
else if (lettersFinal[i] == '[') internalStringBuilder.Append(']');
else if (lettersFinal[i] == ']') internalStringBuilder.Append('[');
else AddNumber(lettersFinal[i]);
switch (lettersFinal[i])
{
case '(':
AddNumber(')');
break;
case ')':
AddNumber('(');
break;
case '<':
AddNumber('>');
break;
case '>':
AddNumber('<');
break;
case '[':
internalStringBuilder.Append(']');
break;
case ']':
internalStringBuilder.Append('[');
break;
default:
AddNumber(lettersFinal[i]);
break;
}
}
else if ((lettersFinal[i] >= (char)0xD800 && lettersFinal[i] <= (char)0xDBFF) || (lettersFinal[i] >= (char)0xDC00 && lettersFinal[i] <= (char)0xDFFF))
{
@@ -369,40 +382,52 @@ namespace ArabicSupport
AppendNumbers();
return internalStringBuilder.ToString();
}
internal static ushort HandleInduNumber(ushort letterOrigin, ushort letterFinal)
{
switch (letterOrigin)
void AppendNumbers()
{
case 0x0030: return 0x0660;
case 0x0031: return 0x0661;
case 0x0032: return 0x0662;
case 0x0033: return 0x0663;
case 0x0034: return 0x0664;
case 0x0035: return 0x0665;
case 0x0036: return 0x0666;
case 0x0037: return 0x0667;
case 0x0038: return 0x0668;
case 0x0039: return 0x0669;
default: return letterFinal;
if (numberList is { Count: > 0 })
{
for (int j = 0; j < numberList.Count; j++)
internalStringBuilder.Append(numberList[numberList.Count - 1 - j]);
numberList.Clear();
}
}
void AddNumber(char value) { numberList ??= []; numberList.Add(value); }
}
internal static bool IsIgnoredCharacter(char ch)
private static ushort HandleInduNumber(ushort letterOrigin, ushort letterFinal)
{
return letterOrigin switch
{
0x0030 => 0x0660,
0x0031 => 0x0661,
0x0032 => 0x0662,
0x0033 => 0x0663,
0x0034 => 0x0664,
0x0035 => 0x0665,
0x0036 => 0x0666,
0x0037 => 0x0667,
0x0038 => 0x0668,
0x0039 => 0x0669,
_ => letterFinal
};
}
private static bool IsIgnoredCharacter(char ch)
{
bool isPunctuation = char.IsPunctuation(ch);
bool isNumber = char.IsNumber(ch);
bool isLower = char.IsLower(ch);
bool isUpper = char.IsUpper(ch);
bool isSymbol = char.IsSymbol(ch);
bool isPersianCharacter = ch == (char)0xFB56 || ch == (char)0xFB7A || ch == (char)0xFB8A || ch == (char)0xFB92 || ch == (char)0xFB8E;
bool isPresentationFormB = (ch <= (char)0xFEFF && ch >= (char)0xFE70);
bool isPersianCharacter = ch is (char)0xFB56 or (char)0xFB7A or (char)0xFB8A or (char)0xFB92 or (char)0xFB8E;
bool isPresentationFormB = ch is <= (char)0xFEFF and >= (char)0xFE70;
bool isAcceptableCharacter = isPresentationFormB || isPersianCharacter || ch == (char)0xFBFC;
return isPunctuation || isNumber || isLower || isUpper || isSymbol || !isAcceptableCharacter || ch == 'a' || ch == '>' || ch == '<' || ch == (char)0x061B;
}
internal static bool IsLeadingLetter(char[] letters, int index)
private static bool IsLeadingLetter(char[] letters, int index)
{
bool lettersThatCannotBeBeforeALeadingLetter = index == 0
|| letters[index - 1] == ' ' || letters[index - 1] == '*' || letters[index - 1] == 'A'
@@ -431,7 +456,7 @@ namespace ArabicSupport
return lettersThatCannotBeBeforeALeadingLetter && lettersThatCannotBeALeadingLetter && lettersThatCannotBeAfterLeadingLetter;
}
internal static bool IsFinishingLetter(char[] letters, int index)
private static bool IsFinishingLetter(char[] letters, int index)
{
bool lettersThatCannotBeBeforeAFinishingLetter = index != 0 &&
letters[index - 1] != ' ' && letters[index - 1] != (int)IsolatedArabicLetters.Dal
@@ -448,7 +473,7 @@ namespace ArabicSupport
return lettersThatCannotBeBeforeAFinishingLetter && lettersThatCannotBeFinishingLetters;
}
internal static bool IsMiddleLetter(char[] letters, int index)
private static bool IsMiddleLetter(char[] letters, int index)
{
bool lettersThatCannotBeMiddleLetters = index != 0 &&
letters[index] != (int)IsolatedArabicLetters.Alef && letters[index] != (int)IsolatedArabicLetters.Dal
+2 -3
View File
@@ -1,8 +1,7 @@
using System.Text;
using HarmonyLib;
using TMPro;
namespace ArabicSupport
namespace EHR.ArabicSupport
{
[HarmonyPatch(typeof(TMP_Text))]
public static class ArabicTextPatch
@@ -22,7 +21,7 @@ namespace ArabicSupport
{
foreach (char c in s)
{
if ((c >= '\u0600' && c <= '\u06FF') || (c >= '\uFB50' && c <= '\uFEFF'))
if (c is >= '\u0600' and <= '\u06FF' or >= '\uFB50' and <= '\uFEFF')
return true;
}
return false;
+18 -26
View File
@@ -1,38 +1,30 @@
# Thank you so much for finding and using EHR!
# FULL MANUAL REINSTALLATION REQUIRED!!
> [!CAUTION]
> The latest Among Us update introduced a lot (and I mean a LOT) of issues with the base game itself, which also affects all mods. Expect some bugs! We did our best to fix the ones we could, but there are still some that we haven't figured out. Sorry for the inconvenience....
> **YOU MUST FULLY REINSTALL THE MOD FOR THIS UPDATE TO WORK!!!!** Just replacing EHR.dll does NOT fix the issues!
> [!NOTE]
> While at the time of releasing this, the mod should more-less work on InnerSloth's regions, **I still recommend playing on modded regions** (MEU/MAS/MNA/Niko-EU/Niko-AS/Niko-NA).
### This release fixes the crashes introduced by the latest Among Us update.
#### The necessary changes are NOT in EHR.dll, hence why you need to completely reinstall the mod for the fixes to take effect.
Shoutout to Pietro for finding the reason and the fix!
## Bug Fixes
- **Fixed the main reason of reported crashes** (thank you!)
- Further reduced the number of RPCs to fix most of the kicking on vanilla regions
- Fixed large messages not being sent on Niko regions
- Fixed `/jailtalk` working after death or when nobody is jailed
> [!IMPORTANT]
> Make sure to back up your templates, player data, banlist, presets, etc. before you reinstall! We're happy to assist you in our Discord server.
## Changes
- Commited -> Committed
- Auto Play Again was moved to Client Options and is available for non-host modded clients too - by Newholiday
- Improved consent popup at the start - by HayashiUme
- Added more custom languages
- Exorcist ability ignores messages starting with /cmd
- Markseeker now uses `/mark {id}` instead of the vote mechanic - by Newholiday
## Additions
- New role: Gatekeeper (Impostor Support)
- New role: Bouncer (Crewmate Support)
- New role: Revenant (Crewmate Investigate) - by Newholiday
- New setting for Snowdown: `Snowball Size` - by Newholiday
- Client Control GUI: Reordering & New option: Broadcast Role Info - by Zypherus
- Fallback fonts: All characters can be rendered now, including Arabic/Persian/Hungarian/Slovak/etc. characters - huge thanks to Atony for the code and resources!
- Changeling can now use `/choose {role}` instead of the cycling mechanic - by Newholiday
- New command: `/startnow` - Starts the game immediately, without the countdown - Can be used by The Host & Admins In Lobby
- New settings for Impartial: `Change role when winning is impossible` & `Role to change to`
##### Other changes
- Optimizations, both FPS-wise and network-wise
- New add-on: Focused (Killers-Only Mixed)
- New setting: `Enable use of /anagram` (Mod Settings > Commands)
- Added non-host modded client sync for Committed, Haunter, Markseeker, President, and Curser
- Players can no longer use ghost roles' abilities after meetings before the cooldown is applied
- Inquisitor inspecting a neutral who only knows their own role now shows the same message as a crewmate
> [!WARNING]
> If you're joining a lobby hosted on vanilla regions (NA/EU/AS), please make sure you have a vanilla region selected in the Create Game menu, otherwise authentication will fail. ("Sabotage! The Among Us servers could not authenticate you!")
I'm saying this now, it's guaranteed that we'll get someone who skipped reading this complaining about this exact issue.
> [!TIP]
> **Play on modded regions** (MEU/MAS/MNA/Niko-EU/Niko-AS/Niko-NA) for an overall better experience. Mobile and PC users can install them [from my website](https://ehr.gurge44.eu/regions).
---
+2 -2
View File
@@ -41,8 +41,8 @@ public class Main : BasePlugin
private const string DebugKeyHash = "c0fd562955ba56af3ae20d7ec9e64c664f0facecef4b3e366e109306adeae29d";
private const string DebugKeySalt = "59687b";
public const string PluginGuid = "com.gurge44.endlesshostroles";
public const string PluginVersion = "7.7.1";
public const string PluginDisplayVersion = "7.7.1";
public const string PluginVersion = "7.8.0";
public const string PluginDisplayVersion = "7.8.0";
public const bool TestBuild = false;
public const string NeutralColor = "#ffab1b";
+1 -1
View File
@@ -159,7 +159,7 @@ namespace EHR
writer.Recycle();
AmongUsClient.Instance.RemoveNetObject(playerControl);
ObjectHelper.Destroy(playerControl.gameObject);
Object.Destroy(playerControl.gameObject);
}
AllObjects.Remove(this);
-27
View File
@@ -2,39 +2,12 @@ using System;
using System.Linq.Expressions;
using System.Reflection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppSystem.Collections.Generic;
using UnityEngine;
namespace EHR;
public static class ObjectHelper
{
private static readonly List<Behaviour> BehaviourCache = new(16);
public static void Destroy(GameObject go)
{
if (!go || go.Pointer == IntPtr.Zero) return;
BehaviourCache.Clear();
go.GetComponents(BehaviourCache);
for (int i = 0; i < BehaviourCache.Count; i++)
{
try
{
Behaviour behaviour = BehaviourCache[i];
if (behaviour && behaviour.Pointer != IntPtr.Zero)
Object.Destroy(behaviour);
}
catch { }
}
BehaviourCache.Clear();
Object.Destroy(go);
}
public static void DestroyTranslator(this GameObject obj)
{
if (!obj) return;
+9 -9
View File
@@ -819,9 +819,9 @@ public static class GuessManager
Transform smallButtonTemplate = __instance.playerStates[0].Buttons.transform.Find("CancelButton");
TextTemplate.enabled = true;
Transform roleTextMeeting = TextTemplate.transform.FindChild("RoleTextMeeting");
if (roleTextMeeting) ObjectHelper.Destroy(roleTextMeeting.gameObject);
if (roleTextMeeting) Object.Destroy(roleTextMeeting.gameObject);
Transform deathReasonTextMeeting = TextTemplate.transform.FindChild("DeathReasonTextMeeting");
if (deathReasonTextMeeting) ObjectHelper.Destroy(deathReasonTextMeeting.gameObject);
if (deathReasonTextMeeting) Object.Destroy(deathReasonTextMeeting.gameObject);
Transform exitButtonParent = new GameObject().transform;
exitButtonParent.SetParent(container);
@@ -839,7 +839,7 @@ public static class GuessManager
passiveButton.OnClick.AddListener((Action)(() =>
{
__instance.playerStates.ToList().ForEach(x => x.gameObject.SetActive(true));
ObjectHelper.Destroy(container.gameObject);
Object.Destroy(container.gameObject);
}));
@@ -1052,7 +1052,7 @@ public static class GuessManager
// Reset the GUI
__instance.playerStates.ToList().ForEach(x => x.gameObject.SetActive(true));
ObjectHelper.Destroy(container.gameObject);
Object.Destroy(container.gameObject);
TextTemplate.enabled = false;
}
}));
@@ -1215,10 +1215,10 @@ public static class GuessManager
[SuppressMessage("ReSharper", "UnusedMember.Local")]
public static void Postfix()
{
if (TextTemplate && TextTemplate.gameObject) ObjectHelper.Destroy(TextTemplate.gameObject);
if (TextTemplate && TextTemplate.gameObject) Object.Destroy(TextTemplate.gameObject);
TextTemplate = null;
if (GuesserUI) ObjectHelper.Destroy(GuesserUI);
if (GuesserUI) Object.Destroy(GuesserUI);
GuesserUI = null;
if (RoleButtons != null)
@@ -1228,7 +1228,7 @@ public static class GuessManager
foreach (Transform transform in roleButtonsValue)
{
if (transform && transform.gameObject)
ObjectHelper.Destroy(transform.gameObject);
Object.Destroy(transform.gameObject);
}
}
@@ -1240,7 +1240,7 @@ public static class GuessManager
foreach (SpriteRenderer spriteRenderer in RoleSelectButtons.Values)
{
if (spriteRenderer && spriteRenderer.gameObject)
ObjectHelper.Destroy(spriteRenderer.gameObject);
Object.Destroy(spriteRenderer.gameObject);
}
RoleSelectButtons = null;
@@ -1251,7 +1251,7 @@ public static class GuessManager
foreach (SpriteRenderer spriteRenderer in PageButtons)
{
if (spriteRenderer && spriteRenderer.gameObject)
ObjectHelper.Destroy(spriteRenderer.gameObject);
Object.Destroy(spriteRenderer.gameObject);
}
PageButtons = null;
+5 -5
View File
@@ -47,9 +47,9 @@ public static class OnlinePresetsManager
upload.SetClickMask(menu.ButtonClickMask);
upload.SetUpFromData(null, 20);
ObjectHelper.Destroy(upload.transform.FindChild("Value_TMP (1)").gameObject);
ObjectHelper.Destroy(upload.transform.FindChild("ValueBox").gameObject);
ObjectHelper.Destroy(upload.PlusBtn.gameObject);
Object.Destroy(upload.transform.FindChild("Value_TMP (1)").gameObject);
Object.Destroy(upload.transform.FindChild("ValueBox").gameObject);
Object.Destroy(upload.PlusBtn.gameObject);
upload.OnValueChanged = new Action<OptionBehaviour>(menu.ValueChanged);
upload.MinusBtn.OnClick = new();
@@ -93,8 +93,8 @@ public static class OnlinePresetsManager
row.SetClickMask(menu.ButtonClickMask);
row.SetUpFromData(null, 20);
ObjectHelper.Destroy(row.transform.FindChild("Value_TMP (1)").gameObject);
ObjectHelper.Destroy(row.transform.FindChild("ValueBox").gameObject);
Object.Destroy(row.transform.FindChild("Value_TMP (1)").gameObject);
Object.Destroy(row.transform.FindChild("ValueBox").gameObject);
row.OnValueChanged = new Action<OptionBehaviour>(menu.ValueChanged);
row.LabelBackground.transform.localScale += new Vector3(1f, 0f, 0f);
+2 -2
View File
@@ -76,8 +76,8 @@ public static class Prompt
private static void HidePromt()
{
ObjectHelper.Destroy(YesButton?.Button.gameObject);
ObjectHelper.Destroy(NoButton?.Button.gameObject);
Object.Destroy(YesButton?.Button.gameObject);
Object.Destroy(NoButton?.Button.gameObject);
YesButton = null;
NoButton = null;
+1 -1
View File
@@ -5261,7 +5261,7 @@ public static class Utils
writer.WritePacked(playerControl.NetId);
writer.EndMessage();
AmongUsClient.Instance.RemoveNetObject(playerControl);
ObjectHelper.Destroy(playerControl.gameObject);
Object.Destroy(playerControl.gameObject);
sender.EndMessage();
sender.SendMessage();
});
+3 -3
View File
@@ -562,9 +562,9 @@ public static class InGameRoleInfoMenu
Menu.name = "EHR Role Info Menu Page";
Menu.transform.SetLocalZ(-990f);
ObjectHelper.Destroy(Menu.transform.FindChild("Title Text").gameObject);
ObjectHelper.Destroy(Menu.transform.FindChild("BackButton").gameObject);
ObjectHelper.Destroy(Menu.transform.FindChild("EvenMoreInfo").gameObject);
Object.Destroy(Menu.transform.FindChild("Title Text").gameObject);
Object.Destroy(Menu.transform.FindChild("BackButton").gameObject);
Object.Destroy(Menu.transform.FindChild("EvenMoreInfo").gameObject);
MainInfo = Menu.transform.FindChild("InfoText_TMP").gameObject;
MainInfo.name = "Main Role Info";
+1 -1
View File
@@ -112,7 +112,7 @@ internal static class Crowded
{
ImpostorsOptionButton secondButton = __instance.ImpostorButtons[1];
secondButton.SpriteRenderer.enabled = false;
ObjectHelper.Destroy(secondButton.transform.FindChild("ConsoleHighlight").gameObject);
Object.Destroy(secondButton.transform.FindChild("ConsoleHighlight").gameObject);
Object.Destroy(secondButton.PassiveButton);
Object.Destroy(secondButton.BoxCollider);
TextMeshPro secondButtonText = secondButton.TextMesh;
+1 -1
View File
@@ -122,7 +122,7 @@ public static class EndGameManagerPatch
if (CountdownText)
{
ObjectHelper.Destroy(CountdownText);
Object.Destroy(CountdownText);
CountdownText = null;
}
}
+2 -2
View File
@@ -67,7 +67,7 @@ internal static class HudManagerPatch
}
else if (!PingTrackerUpdatePatch.Instance && SettingsText)
{
ObjectHelper.Destroy(SettingsText.gameObject);
Object.Destroy(SettingsText.gameObject);
SettingsText = null;
}
@@ -79,7 +79,7 @@ internal static class HudManagerPatch
}
else if (SettingsText)
{
ObjectHelper.Destroy(SettingsText.gameObject);
Object.Destroy(SettingsText.gameObject);
SettingsText = null;
}
+1 -1
View File
@@ -140,7 +140,7 @@ static class CoShowIntroPatch
yield return introCutscene.ShowRole();
ShipStatus.Instance.StartSFX();
ObjectHelper.Destroy(introCutscene.gameObject);
Object.Destroy(introCutscene.gameObject);
}
}
}
+1 -1
View File
@@ -505,7 +505,7 @@ public static class LobbyViewSettingsPanePatch
{
viewSettings.currentTab = tabName;
for (var i = 0; i < viewSettings.settingsInfo.Count; i++)
ObjectHelper.Destroy(viewSettings.settingsInfo[i].gameObject);
Object.Destroy(viewSettings.settingsInfo[i].gameObject);
viewSettings.settingsInfo.Clear();
SetTabPatch_Postfix(viewSettings);
}
+2 -2
View File
@@ -979,7 +979,7 @@ internal static class MeetingHudStartPatch
deathReasonTextMeeting.enableWordWrapping = false;
deathReasonTextMeeting.enabled = seer.KnowDeathReason(target);
Transform child = deathReasonTextMeeting.transform.FindChild("RoleTextMeeting");
if (child) ObjectHelper.Destroy(child.gameObject);
if (child) Object.Destroy(child.gameObject);
byte id = pva.TargetPlayerId;
@@ -1219,7 +1219,7 @@ internal static class MeetingHudUpdatePatch
{
__instance.playerStates.DoIf(
x => (forceAll || !Main.PlayerStates.TryGetValue(x.TargetPlayerId, out PlayerState ps) || ps.IsDead) && x.transform.FindChild("ShootButton"),
x => ObjectHelper.Destroy(x.transform.FindChild("ShootButton").gameObject));
x => Object.Destroy(x.transform.FindChild("ShootButton").gameObject));
}
public static bool Prefix(MeetingHud __instance)
+3 -3
View File
@@ -147,8 +147,8 @@ internal static class ChangeRoleSettings
try
{
SabotageMapPatch.TimerTexts.Values.DoIf(x => x, x => ObjectHelper.Destroy(x.gameObject));
MapRoomDoorsUpdatePatch.DoorTimerTexts.Values.DoIf(x => x, x => ObjectHelper.Destroy(x.gameObject));
SabotageMapPatch.TimerTexts.Values.DoIf(x => x, x => Object.Destroy(x.gameObject));
MapRoomDoorsUpdatePatch.DoorTimerTexts.Values.DoIf(x => x, x => Object.Destroy(x.gameObject));
}
catch (Exception e) { Utils.ThrowException(e); }
@@ -456,7 +456,7 @@ internal static class StartGameHostPatch
AUClient.SendOrDisconnect(writer);
writer.Recycle();
AUClient.RemoveNetObject(LobbyBehaviour.Instance);
ObjectHelper.Destroy(LobbyBehaviour.Instance.gameObject);
Object.Destroy(LobbyBehaviour.Instance.gameObject);
}
else
{
+1 -1
View File
@@ -702,7 +702,7 @@ internal static class SetEverythingUpPatch
yield return null;
ObjectHelper.Destroy(roleSummaryObject);
Object.Destroy(roleSummaryObject);
yield return null;
+1 -1
View File
@@ -54,7 +54,7 @@ internal static class AddVotePatch
LateTask.New(() =>
{
AmongUsClient.Instance.RemoveNetObject(__instance);
ObjectHelper.Destroy(__instance.gameObject);
Object.Destroy(__instance.gameObject);
}, 5f);
return false;
@@ -71,7 +71,7 @@ public class Imitator : RoleBase
foreach (PlayerVoteArea pva in MeetingHud.Instance.playerStates)
{
Transform button = pva.transform.FindChild("ImitatorButton");
if (button != null) ObjectHelper.Destroy(button.gameObject);
if (button != null) Object.Destroy(button.gameObject);
}
}
}
+1 -1
View File
@@ -266,7 +266,7 @@ public class Swapper : RoleBase
__instance.playerStates.ToList().ForEach(x =>
{
Transform swapButton = x.transform.FindChild("ShootButton");
if (swapButton != null) ObjectHelper.Destroy(swapButton.gameObject);
if (swapButton != null) Object.Destroy(swapButton.gameObject);
});
CreateSwapperButton(__instance);
@@ -61,7 +61,7 @@ public class Exorcist : RoleBase
private static void CreateExorcistButton(MeetingHud __instance)
{
GameObject existingButton = GameObject.Find("ExorcistButton");
if (existingButton) ObjectHelper.Destroy(existingButton.gameObject);
if (existingButton) Object.Destroy(existingButton.gameObject);
if (!PlayerControl.LocalPlayer || !PlayerControl.LocalPlayer.IsAlive()) return;
GameObject parent = GameObject.Find("Main Camera").transform.Find("Hud").Find("ChatUi").Find("ChatScreenRoot").Find("ChatScreenContainer").gameObject;
+1 -1
View File
@@ -107,7 +107,7 @@ public class Starspawn : RoleBase
private static void CreateStarspawnButton(MeetingHud __instance)
{
GameObject existingButton = GameObject.Find("StarspawnButton");
if (existingButton) ObjectHelper.Destroy(existingButton.gameObject);
if (existingButton) Object.Destroy(existingButton.gameObject);
if (!PlayerControl.LocalPlayer || !PlayerControl.LocalPlayer.IsAlive()) return;
GameObject parent = GameObject.Find("Main Camera").transform.Find("Hud").Find("ChatUi").Find("ChatScreenRoot").Find("ChatScreenContainer").gameObject;