Decompiled source of ArabicLocalization v2.0.0

Arabic.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("TABSArabic")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("TABSArabic")]
[assembly: AssemblyTitle("TABSArabic")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TABSKorean
{
	public static class KoreanLanguage
	{
		public const int LanguageIndex = 9;

		public static bool Enabled { get; private set; }

		public static void Enable()
		{
			Enabled = true;
		}

		public static void Disable()
		{
			Enabled = false;
		}
	}
	[HarmonyPatch(typeof(GlobalSettingsHandler), "GetSettingsInstance", new Type[] { typeof(string) })]
	internal static class GlobalSettingsHandler_GetSettingsInstance_Patch
	{
		private static void Postfix(string settingsKey, ref SettingsInstance __result)
		{
			if (__result != null && !(settingsKey != "VIDEO_LANGUAGE"))
			{
				AddKoreanOption(__result);
			}
		}

		private static void AddKoreanOption(SettingsInstance settings)
		{
			if (settings.options == null)
			{
				Plugin.Log.LogWarning((object)"VIDEO_LANGUAGE has no options array.");
			}
			else
			{
				if (settings.options.Length > 9 && settings.options[9] == "Arabic")
				{
					return;
				}
				if (settings.options.Length > 9)
				{
					Plugin.Log.LogWarning((object)"VIDEO_LANGUAGE already has an option at index 9. Arabic option was not overwritten.");
					return;
				}
				string[] array = new string[10];
				for (int i = 0; i < settings.options.Length; i++)
				{
					array[i] = settings.options[i];
				}
				array[9] = "Arabic";
				settings.options = array;
				Plugin.Log.LogInfo((object)"Added Arabic language option to VIDEO_LANGUAGE.");
			}
		}
	}
	internal static class KoreanLanguageSwitchPath
	{
		public const int LanguageIndex = 9;

		public static bool Enabled { get; private set; }

		public static void Enable()
		{
			Enabled = true;
		}

		public static void Disable()
		{
			Enabled = false;
		}
	}
	[HarmonyPatch(typeof(Localizer), "SetLanguage")]
	internal static class Localizer_SetLanguage_Patch
	{
		private static readonly FieldInfo CallbacksField;

		private static bool Prefix(Language lang)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected I4, but got Unknown
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Invalid comparison between Unknown and I4
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected I4, but got Unknown
			Plugin.Log.LogInfo((object)$"Localizer.SetLanguage called: {(int)lang}");
			if ((int)lang == 9)
			{
				KoreanLanguage.Enable();
				SetKoreanCulture();
				RefreshLocalization();
				Plugin.Log.LogInfo((object)"Korean language enabled.");
				return false;
			}
			KoreanLanguage.Disable();
			Plugin.Log.LogInfo((object)$"Arabic language disabled. Using TABS language {(int)lang}.");
			return true;
		}

		private static void SetKoreanCulture()
		{
			try
			{
				CultureInfo cultureInfo = CultureInfo.GetCultureInfo("ar-AR");
				Thread.CurrentThread.CurrentCulture = cultureInfo;
				Thread.CurrentThread.CurrentUICulture = cultureInfo;
			}
			catch (CultureNotFoundException)
			{
				Plugin.Log.LogWarning((object)"Could not set Arabic culture.");
			}
		}

		private static void RefreshLocalization()
		{
			if (CallbacksField == null)
			{
				Plugin.Log.LogWarning((object)"Could not find Localizer.m_callbacks.");
				return;
			}
			try
			{
				object value = CallbacksField.GetValue(null);
				if (value == null)
				{
					Plugin.Log.LogWarning((object)"Localizer.m_callbacks is null.");
					return;
				}
				if (!(value is IDictionary dictionary))
				{
					Plugin.Log.LogWarning((object)"Localizer.m_callbacks is not an IDictionary.");
					return;
				}
				List<Delegate> list = new List<Delegate>();
				foreach (DictionaryEntry item2 in dictionary)
				{
					if (item2.Value is Delegate item)
					{
						list.Add(item);
					}
				}
				Plugin.Log.LogInfo((object)$"Refreshing {list.Count} localization callbacks.");
				foreach (Delegate item3 in list)
				{
					try
					{
						item3.DynamicInvoke();
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)("Localization callback failed: " + ex.Message));
					}
				}
			}
			catch (Exception ex2)
			{
				ManualLogSource log = Plugin.Log;
				string text = "Failed to refresh localization: ";
				log.LogError((object)(text + ex2));
			}
		}

		static Localizer_SetLanguage_Patch()
		{
			CallbacksField = typeof(Localizer).GetField("m_callbacks", BindingFlags.Static | BindingFlags.NonPublic);
		}
	}
	internal static class KoreanLocalization
	{
		private static readonly Dictionary<string, string> Translations = new Dictionary<string, string>(StringComparer.Ordinal);

		private static string FilePath;

		public static int Count => Translations.Count;

		public static void Initialize()
		{
			try
			{
				string? directoryName = Path.GetDirectoryName(typeof(KoreanLocalization).Assembly.Location);
				FilePath = Path.Combine(directoryName, "Localization", "tabs_ar.txt");
				LoadFile(FilePath);
				ApplyArabicFont(directoryName);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to initialize Arabic localization: {arg}");
			}
		}

		private static void LoadFile(string path)
		{
			Translations.Clear();
			if (!File.Exists(path))
			{
				Plugin.Log.LogError((object)"Arabic localization file was not found.");
				Plugin.Log.LogError((object)("Expected path: " + path));
				return;
			}
			string[] array = File.ReadAllLines(path, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			int num = 0;
			int num2 = 0;
			string[] array2 = array;
			foreach (string text in array2)
			{
				string text2 = text.Trim();
				if (string.IsNullOrEmpty(text2) || text2.StartsWith("#"))
				{
					continue;
				}
				int num3 = text2.IndexOf('=');
				if (num3 <= 0)
				{
					num2++;
					Plugin.Log.LogWarning((object)("Invalid localization line: " + text));
					continue;
				}
				string text3 = text2.Substring(0, num3).Trim();
				string text4 = text2.Substring(num3 + 1);
				if (string.IsNullOrEmpty(text3))
				{
					num2++;
					continue;
				}
				if (IsMenuKey(text3) && !text4.Contains("<size="))
				{
					text4 = "<size=130%>" + text4 + "</size>";
				}
				Translations[text3] = text4;
				num++;
			}
			Plugin.Log.LogInfo((object)$"Arabic localization loaded: {num} entries.");
			if (num2 > 0)
			{
				Plugin.Log.LogWarning((object)$"Skipped {num2} invalid localization lines.");
			}
		}

		public static bool TryGet(string key, out string value)
		{
			return Translations.TryGetValue(key, out value);
		}

		public static string Translate(string phraseID, string[] args)
		{
			if (string.IsNullOrEmpty(phraseID))
			{
				return phraseID;
			}
			if (!TryGet(phraseID, out var value))
			{
				return null;
			}
			value = ResolveNestedLocalization(value);
			if (args != null && args.Length != 0)
			{
				string[] array = new string[args.Length];
				for (int i = 0; i < args.Length; i++)
				{
					array[i] = args[i];
					if (!string.IsNullOrEmpty(array[i]) && array[i].Length > 1 && array[i][0] == '$')
					{
						string text = Translate(array[i].Substring(1), null);
						if (text != null)
						{
							array[i] = text;
						}
					}
				}
				try
				{
					string format = value;
					object[] args2 = array;
					value = string.Format(format, args2);
				}
				catch (FormatException)
				{
					Plugin.Log.LogWarning((object)("Invalid format string for key: " + phraseID));
				}
			}
			return value;
		}

		private static string ResolveNestedLocalization(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			for (int i = 0; i < 32; i++)
			{
				int num = text.IndexOf('$');
				if (num < 0)
				{
					break;
				}
				int num2 = text.IndexOf('$', num + 1);
				if (num2 < 0)
				{
					break;
				}
				string text2 = text.Substring(num + 1, num2 - num - 1);
				if (string.IsNullOrEmpty(text2) || !TryGet(text2, out var value))
				{
					break;
				}
				value = ResolveNestedLocalization(value);
				text = text.Replace(text.Substring(num, num2 - num + 1), value);
			}
			return text;
		}

		private static bool IsMenuKey(string key)
		{
			string[] array = new string[17]
			{
				"MENU_", "BUTTON_", "TITLE_", "SETTINGS_", "VIDEO_", "AUDIO_", "GAMEPLAY_", "CONTROLS_", "POPUP_", "PAUSE_",
				"MAIN_", "EXIT_", "QUIT_", "RESUME_", "RESTART_", "LOAD_", "SAVE_"
			};
			for (int i = 0; i < array.Length; i++)
			{
				if (key.StartsWith(array[i], StringComparison.Ordinal))
				{
					return true;
				}
			}
			return false;
		}

		private static void ApplyArabicFont(string modDirectory)
		{
			string text = Path.Combine(modDirectory, "Fonts", "notoarabic_assets");
			if (!File.Exists(text))
			{
				Plugin.Log.LogWarning((object)("Noto Sans Arabic AssetBundle not found: " + text));
				Plugin.Log.LogWarning((object)"Create a TMP_FontAsset AssetBundle named 'notoarabic_assets' and place it in Fonts.");
				return;
			}
			AssetBundle val = AssetBundle.LoadFromFile(text);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogError((object)"Could not load Noto Sans Arabic AssetBundle.");
				return;
			}
			TMP_FontAsset val2 = val.LoadAsset<TMP_FontAsset>("NotoSansArabic SDF");
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = val.LoadAsset<TMP_FontAsset>("NotoSansArabic");
			}
			if ((Object)(object)val2 == (Object)null)
			{
				Plugin.Log.LogError((object)"The AssetBundle does not contain a TMP_FontAsset named NotoSansArabic or NotoSansArabic SDF.");
				return;
			}
			ApplyToScene(val2);
			Plugin.Log.LogInfo((object)"Noto Sans Arabic font applied to the current UI.");
		}

		public static void ApplyToScene(TMP_FontAsset font)
		{
			TMP_Text[] array = Resources.FindObjectsOfTypeAll<TMP_Text>();
			for (int i = 0; i < array.Length; i++)
			{
				if (!((Object)(object)array[i] == (Object)null) && !((Object)(object)((Component)array[i]).gameObject == (Object)null))
				{
					array[i].font = font;
					if (IsMenuObject(((Component)array[i]).gameObject))
					{
						TMP_Text obj = array[i];
						obj.fontSize *= 1.3f;
					}
				}
			}
		}

		private static bool IsMenuObject(GameObject obj)
		{
			string text = ((Object)obj).name.ToLowerInvariant();
			if (!text.Contains("menu") && !text.Contains("button") && !text.Contains("settings") && !text.Contains("popup") && !text.Contains("pause") && !text.Contains("options"))
			{
				return text.Contains("title");
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Localizer), "GetSinglePhrase", new Type[]
	{
		typeof(string),
		typeof(string[])
	})]
	internal static class Localizer_GetSinglePhrase_Patch
	{
		private static bool Prefix(string phraseID, string[] args, ref string __result)
		{
			if (!KoreanLanguage.Enabled)
			{
				return true;
			}
			string text = KoreanLocalization.Translate(phraseID, args);
			if (text == null)
			{
				return true;
			}
			__result = text;
			return false;
		}
	}
	[BepInPlugin("com.anhyeongseop.tabs.arabic", "Arabic", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "com.anhyeongseop.tabs.arabic";

		public const string NAME = "Arabic";

		public const string VERSION = "1.0.0";

		internal static ManualLogSource Log;

		internal static Harmony HarmonyInstance;

		private void Awake()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"=================================");
			Log.LogInfo((object)"TABS Arabic starting...");
			Log.LogInfo((object)"=================================");
			KoreanLocalization.Initialize();
			HarmonyInstance = new Harmony("com.anhyeongseop.tabs.arabic");
			HarmonyInstance.PatchAll();
			Log.LogInfo((object)"Arabic loaded successfully.");
			Log.LogInfo((object)$"Loaded Arabic strings: {KoreanLocalization.Count}");
		}

		private void OnDestroy()
		{
			if (HarmonyInstance != null)
			{
				HarmonyInstance.UnpatchSelf();
				HarmonyInstance = null;
			}
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)"Arabic unloaded.");
			}
		}
	}
}