Decompiled source of TrueCC Traduction Competences v0.15.0

TrueCC.Traduction.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using On.RoR2;
using On.RoR2.UI;
using R2API.Utils;
using Rewired;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using RoR2.UI;
using RoR2.UI.MainMenu;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace TrueCC.Traduction;

public struct CreditRect
{
	public float X;

	public float Y;

	public float W;

	public float H;

	public CreditRect(float x, float y, float w, float h)
	{
		X = x;
		Y = y;
		W = w;
		H = h;
	}

	public bool Overlaps(CreditRect r, float gap)
	{
		if (X < r.X + r.W + gap && X + W + gap > r.X && Y < r.Y + r.H + gap)
		{
			return Y + H + gap > r.Y;
		}
		return false;
	}
}
public static class CreditLayout
{
	public static bool TryFind(float width, float height, IList<CreditRect> occupied, out CreditRect result)
	{
		result = default(CreditRect);
		if (width < 640f || height < 360f)
		{
			return false;
		}
		float val = Math.Min(width / 1920f, height / 1080f);
		val = Math.Max(0.7f, val);
		float num = Math.Min(460f * val, width - 48f);
		float num2 = 26f * val;
		float num3 = 20f * val;
		float[] array = new float[3]
		{
			num3,
			num3 + num2 + 10f * val,
			num3 + 2f * (num2 + 10f * val)
		};
		foreach (float y in array)
		{
			float[] array2 = new float[3]
			{
				(width - num) / 2f,
				width - num3 - num,
				num3
			};
			foreach (float x in array2)
			{
				CreditRect creditRect = new CreditRect(x, y, num, num2);
				bool flag = creditRect.Y + creditRect.H < height;
				foreach (CreditRect item in occupied)
				{
					if (creditRect.Overlaps(item, 8f * val))
					{
						flag = false;
						break;
					}
				}
				if (flag)
				{
					result = creditRect;
					return true;
				}
			}
		}
		return false;
	}
}
public sealed class KizunaTextBridge : IDisposable
{
	private const string PatchId = "com.truecc.traductioncompetences.kizunatext";

	private static KizunaTextBridge current;

	private readonly Dictionary<string, string> tokens = new Dictionary<string, string>(StringComparer.Ordinal);

	private readonly TranslationEngine engine;

	private readonly Func<bool> disabled;

	private readonly Harmony harmony;

	private FieldInfo englishField;

	public KizunaTextBridge(TranslationEngine engine, IEnumerable<TranslationEntry> entries, Func<bool> disabled)
	{
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Expected O, but got Unknown
		this.engine = engine;
		this.disabled = disabled;
		foreach (TranslationEntry entry in entries)
		{
			if (entry.externalScope == "KizunaBook")
			{
				tokens.Add(entry.source, entry.token);
			}
		}
		harmony = new Harmony("com.truecc.traductioncompetences.kizunatext");
	}

	public bool Attach(Assembly assembly)
	{
		//IL_0156: Unknown result type (might be due to invalid IL or missing references)
		//IL_0163: Expected O, but got Unknown
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0189: Expected O, but got Unknown
		if (assembly == null || tokens.Count == 0)
		{
			return false;
		}
		Type type = assembly.GetType("KizunaMod.KizunaBook", throwOnError: false);
		Type type2 = assembly.GetType("KizunaMod.KizunaBook+Text", throwOnError: false);
		if (type == null || type2 == null)
		{
			return false;
		}
		PropertyInfo property = type2.GetProperty("Now", BindingFlags.Instance | BindingFlags.Public);
		englishField = type2.GetField("en", BindingFlags.Instance | BindingFlags.Public);
		MethodInfo method = type.GetMethod("Pick", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
		{
			typeof(string),
			typeof(string)
		}, null);
		if (property == null || property.PropertyType != typeof(string) || property.GetIndexParameters().Length != 0 || property.GetGetMethod() == null || englishField == null || englishField.FieldType != typeof(string) || method == null || method.ReturnType != typeof(string))
		{
			return false;
		}
		if (current != null)
		{
			throw new InvalidOperationException("Kizuna text bridge already attached");
		}
		try
		{
			current = this;
			harmony.Patch((MethodBase)property.GetGetMethod(), (HarmonyMethod)null, new HarmonyMethod(typeof(KizunaTextBridge), "TextPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(typeof(KizunaTextBridge), "PickPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}
		catch
		{
			Dispose();
			throw;
		}
	}

	public string Resolve(string english, string original)
	{
		if (disabled() || english == null)
		{
			return original;
		}
		if (!tokens.TryGetValue(english, out var value))
		{
			return original;
		}
		string text = engine.Translate(value, english, disabled: false);
		if (!(text == english))
		{
			return text;
		}
		return original;
	}

	private static void TextPostfix(object __instance, ref string __result)
	{
		KizunaTextBridge kizunaTextBridge = current;
		if (kizunaTextBridge == null)
		{
			return;
		}
		try
		{
			__result = kizunaTextBridge.Resolve((string)kizunaTextBridge.englishField.GetValue(__instance), __result);
		}
		catch
		{
		}
	}

	private static void PickPostfix(string __1, ref string __result)
	{
		KizunaTextBridge kizunaTextBridge = current;
		if (kizunaTextBridge == null)
		{
			return;
		}
		try
		{
			__result = kizunaTextBridge.Resolve(__1, __result);
		}
		catch
		{
		}
	}

	public void Dispose()
	{
		if (current == this)
		{
			harmony.UnpatchSelf();
			current = null;
		}
	}
}
public sealed class MenuCredit : MonoBehaviour
{
	private TextMeshProUGUI label;

	private MainMenuController owner;

	private float nextCheck;

	private void Update()
	{
		if (Time.unscaledTime < nextCheck)
		{
			return;
		}
		nextCheck = Time.unscaledTime + 0.5f;
		try
		{
			RefreshCredit();
		}
		catch (Exception ex)
		{
			if ((Object)(object)label != (Object)null)
			{
				((Component)label).gameObject.SetActive(false);
			}
			((Behaviour)this).enabled = false;
			Debug.LogWarning((object)("TrueCC : crédit masqué, accueil non reconnu : " + ex.Message));
		}
	}

	private void RefreshCredit()
	{
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_0361: Unknown result type (might be due to invalid IL or missing references)
		//IL_0376: Unknown result type (might be due to invalid IL or missing references)
		//IL_037b: Unknown result type (might be due to invalid IL or missing references)
		//IL_037c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0383: Unknown result type (might be due to invalid IL or missing references)
		//IL_0396: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_0279: Unknown result type (might be due to invalid IL or missing references)
		//IL_0280: Expected O, but got Unknown
		//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
		MainMenuController instance = MainMenuController.instance;
		if (!((Object)(object)instance != (Object)null) || !((Behaviour)instance).isActiveAndEnabled || !((Object)(object)instance.currentMenuScreen == (Object)(object)instance.titleMenuScreen) || !Plugin.ShowCredit.Value || Plugin.DisableTranslation.Value)
		{
			if ((Object)(object)label != (Object)null)
			{
				((Component)label).gameObject.SetActive(false);
			}
			return;
		}
		if ((Object)(object)owner != (Object)(object)instance)
		{
			if ((Object)(object)label != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)label).gameObject);
			}
			label = null;
			owner = instance;
		}
		TMP_Text[] componentsInChildren = ((Component)instance).GetComponentsInChildren<TMP_Text>(true);
		TMP_Text val = null;
		Canvas val2 = null;
		TMP_Text[] array = componentsInChildren;
		foreach (TMP_Text val3 in array)
		{
			if (!((Object)(object)val3 == (Object)(object)label) && ((Component)val3).gameObject.activeInHierarchy)
			{
				val2 = ((Component)val3).GetComponentInParent<Canvas>();
				if ((Object)(object)val2 != (Object)null)
				{
					val = val3;
					break;
				}
			}
		}
		if ((Object)(object)val == (Object)null)
		{
			if ((Object)(object)label != (Object)null)
			{
				((Component)label).gameObject.SetActive(false);
			}
			return;
		}
		val2 = val2.rootCanvas;
		Transform transform = ((Component)val2).transform;
		RectTransform val4 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
		if ((Object)(object)val4 == (Object)null)
		{
			return;
		}
		Camera val5 = (((int)val2.renderMode == 0) ? null : val2.worldCamera);
		List<CreditRect> list = new List<CreditRect>();
		TMP_Text[] componentsInChildren2 = ((Component)val2).GetComponentsInChildren<TMP_Text>(false);
		foreach (TMP_Text val6 in componentsInChildren2)
		{
			if ((Object)(object)val6 != (Object)(object)label && ((Behaviour)val6).isActiveAndEnabled)
			{
				Add(list, val6.rectTransform, val5);
			}
		}
		Selectable[] componentsInChildren3 = ((Component)val2).GetComponentsInChildren<Selectable>(false);
		foreach (Selectable val7 in componentsInChildren3)
		{
			if (((Behaviour)val7).isActiveAndEnabled)
			{
				Transform transform2 = ((Component)val7).transform;
				Add(list, (RectTransform)(object)((transform2 is RectTransform) ? transform2 : null), val5);
			}
		}
		if (!CreditLayout.TryFind(Screen.width, Screen.height, list, out var result))
		{
			if ((Object)(object)label != (Object)null)
			{
				((Component)label).gameObject.SetActive(false);
			}
			return;
		}
		if ((Object)(object)label == (Object)null)
		{
			GameObject val8 = new GameObject("TrueCCTranslationCredit", new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(TextMeshProUGUI)
			});
			val8.transform.SetParent((Transform)(object)val4, false);
			label = val8.GetComponent<TextMeshProUGUI>();
			((TMP_Text)label).font = val.font;
			((TMP_Text)label).text = "Trad' by Charlie and PurpleMOOD";
			((Graphic)label).color = new Color(0.76f, 0.57f, 1f, 1f);
			((Graphic)label).raycastTarget = false;
			((TMP_Text)label).alignment = (TextAlignmentOptions)514;
			((TMP_Text)label).enableWordWrapping = false;
			((TMP_Text)label).overflowMode = (TextOverflowModes)1;
		}
		float num = Math.Max(0.01f, val2.scaleFactor);
		RectTransform rectTransform = ((TMP_Text)label).rectTransform;
		Vector2 anchoredPosition = default(Vector2);
		RectTransformUtility.ScreenPointToLocalPointInRectangle(val4, new Vector2(result.X + result.W / 2f, result.Y + result.H / 2f), val5, ref anchoredPosition);
		Vector2 anchorMin = (rectTransform.anchorMax = val4.pivot);
		rectTransform.anchorMin = anchorMin;
		rectTransform.pivot = new Vector2(0.5f, 0.5f);
		rectTransform.anchoredPosition = anchoredPosition;
		rectTransform.sizeDelta = new Vector2(result.W / num, result.H / num);
		((TMP_Text)label).fontSize = 18f * Math.Max(0.7f, Math.Min((float)Screen.width / 1920f, (float)Screen.height / 1080f)) / num;
		((Component)label).gameObject.SetActive(true);
	}

	private static void Add(List<CreditRect> result, RectTransform rect, Camera camera)
	{
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)rect == (Object)null))
		{
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			rect.GetWorldCorners(array);
			float num = float.MaxValue;
			float num2 = float.MaxValue;
			float num3 = float.MinValue;
			float num4 = float.MinValue;
			Vector3[] array2 = array;
			foreach (Vector3 val in array2)
			{
				Vector2 val2 = RectTransformUtility.WorldToScreenPoint(camera, val);
				num = Math.Min(num, val2.x);
				num2 = Math.Min(num2, val2.y);
				num3 = Math.Max(num3, val2.x);
				num4 = Math.Max(num4, val2.y);
			}
			if (num3 - num > 0f && num4 - num2 > 0f)
			{
				result.Add(new CreditRect(num, num2, num3 - num, num4 - num2));
			}
		}
	}

	private void OnDestroy()
	{
		if ((Object)(object)label != (Object)null)
		{
			Object.Destroy((Object)(object)((Component)label).gameObject);
		}
	}
}
internal sealed class MenuInputGuard : IDisposable
{
	private readonly Func<bool> enabled;

	private readonly Action<string> diagnostic;

	private readonly Dictionary<MPEventSystem, MenuInputPolicy> states = new Dictionary<MPEventSystem, MenuInputPolicy>();

	private readonly MethodInfo change = typeof(MPEventSystem).GetMethod("OnLastActiveControllerChanged", BindingFlags.Instance | BindingFlags.NonPublic);

	private int samples;

	internal MenuInputGuard(Func<bool> enabled, Action<string> diagnostic)
	{
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Expected O, but got Unknown
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Expected O, but got Unknown
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Expected O, but got Unknown
		this.enabled = enabled;
		this.diagnostic = diagnostic;
		if (change == null)
		{
			throw new MissingMethodException(typeof(MPEventSystem).FullName, "OnLastActiveControllerChanged");
		}
		MPEventSystem.OnLastActiveControllerChanged += new hook_OnLastActiveControllerChanged(Changed);
		MPEventSystem.Update += new hook_Update(Update);
		MPEventSystem.OnDestroy += new hook_OnDestroy(Destroyed);
	}

	private MenuInputPolicy State(MPEventSystem self)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Invalid comparison between Unknown and I4
		if (!states.TryGetValue(self, out var value))
		{
			states.Add(self, value = new MenuInputPolicy());
			if ((int)self.currentInputSource == 1)
			{
				value.ObserveGamepad();
			}
		}
		return value;
	}

	private static bool InMenu(MPEventSystem self)
	{
		if (self.cursorOpenerCount <= 0)
		{
			return self.cursorOpenerForGamepadCount > 0;
		}
		return true;
	}

	private static Controller DesktopActivity(Player player)
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		if (player == null || !Application.isFocused)
		{
			return null;
		}
		ControllerHelper controllers = ReInput.controllers;
		if (player.controllers.hasMouse && controllers.Mouse != null)
		{
			Mouse mouse = controllers.Mouse;
			if (!((Controller)mouse).GetAnyButtonDown())
			{
				Vector2 screenPositionDelta = mouse.screenPositionDelta;
				if (!(((Vector2)(ref screenPositionDelta)).sqrMagnitude >= 0.25f) && Input.mouseScrollDelta.y == 0f)
				{
					goto IL_005e;
				}
			}
			return (Controller)(object)mouse;
		}
		goto IL_005e;
		IL_005e:
		if (player.controllers.hasKeyboard && controllers.Keyboard != null && ((Controller)controllers.Keyboard).GetAnyButtonDown())
		{
			return (Controller)(object)controllers.Keyboard;
		}
		return null;
	}

	private void Changed(orig_OnLastActiveControllerChanged orig, MPEventSystem self, Player player, Controller controller)
	{
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Invalid comparison between Unknown and I4
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Invalid comparison between Unknown and I4
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Invalid comparison between Unknown and I4
		MenuInputPolicy menuInputPolicy = State(self);
		if (enabled() && InMenu(self) && (int)self.currentInputSource == 1)
		{
			menuInputPolicy.ObserveGamepad();
		}
		if (controller == null)
		{
			orig.Invoke(self, player, controller);
		}
		else if ((int)controller.type == 2)
		{
			menuInputPolicy.ObserveGamepad();
			orig.Invoke(self, player, controller);
		}
		else if ((int)controller.type != 0 && (int)controller.type != 1)
		{
			orig.Invoke(self, player, controller);
		}
		else if (menuInputPolicy.AllowDesktop(enabled(), InMenu(self), DesktopActivity(player) != null))
		{
			orig.Invoke(self, player, controller);
		}
		else if (samples++ < 3)
		{
			diagnostic("Menus : priorité manette conservée, notification clavier/souris sans activité.");
		}
	}

	private void Update(orig_Update orig, MPEventSystem self)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Invalid comparison between Unknown and I4
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Invalid comparison between Unknown and I4
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		MenuInputPolicy menuInputPolicy = State(self);
		bool flag = enabled() && InMenu(self);
		if (flag && (int)self.currentInputSource == 1)
		{
			menuInputPolicy.ObserveGamepad();
		}
		if (menuInputPolicy.GamepadOwns)
		{
			Controller val = (flag ? DesktopActivity(self.player) : null);
			if (!flag || val != null)
			{
				menuInputPolicy.Reset();
				if (!flag && !enabled() && InMenu(self) && self.player != null)
				{
					Controller lastActiveController = self.player.controllers.GetLastActiveController();
					if (lastActiveController != null && ((int)lastActiveController.type == 1 || (int)lastActiveController.type == 0))
					{
						val = lastActiveController;
					}
				}
				if (val != null)
				{
					change.Invoke(self, new object[2] { self.player, val });
				}
			}
		}
		orig.Invoke(self);
	}

	private void Destroyed(orig_OnDestroy orig, MPEventSystem self)
	{
		states.Remove(self);
		orig.Invoke(self);
	}

	public void Dispose()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Expected O, but got Unknown
		MPEventSystem.OnLastActiveControllerChanged -= new hook_OnLastActiveControllerChanged(Changed);
		MPEventSystem.Update -= new hook_Update(Update);
		MPEventSystem.OnDestroy -= new hook_OnDestroy(Destroyed);
		states.Clear();
	}
}
internal sealed class MenuInputPolicy
{
	internal bool GamepadOwns { get; private set; }

	internal void ObserveGamepad()
	{
		GamepadOwns = true;
	}

	internal void Reset()
	{
		GamepadOwns = false;
	}

	internal bool AllowDesktop(bool enabled, bool inMenu, bool realDesktopActivity)
	{
		if (!enabled || !inMenu)
		{
			Reset();
			return true;
		}
		if (!GamepadOwns)
		{
			return true;
		}
		if (!realDesktopActivity)
		{
			return false;
		}
		Reset();
		return true;
	}
}
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("com.truecc.traductioncompetences", "TrueCC — Traduction des compétences", "0.15.0")]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class Plugin : BaseUnityPlugin
{
	public const string Guid = "com.truecc.traductioncompetences";

	public const string Name = "TrueCC — Traduction des compétences";

	public const string Version = "0.15.0";

	internal static ConfigEntry<bool> DisableTranslation;

	internal static ConfigEntry<bool> ShowCredit;

	internal static ConfigEntry<bool> StableMenuInput;

	private TranslationEngine engine;

	private TranslationEntry[] entries;

	private KizunaTextBridge kizuna;

	private MenuInputGuard menuInput;

	private int translatedSamples;

	private int unmatchedSamples;

	private void Awake()
	{
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Expected O, but got Unknown
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Expected O, but got Unknown
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Expected O, but got Unknown
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: Expected O, but got Unknown
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Expected O, but got Unknown
		//IL_0181: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Expected O, but got Unknown
		//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b6: Expected O, but got Unknown
		string text = "configuration";
		try
		{
			TranslationSettings translationSettings = TranslationSettings.Bind(((BaseUnityPlugin)this).Config);
			DisableTranslation = translationSettings.Disable;
			ShowCredit = translationSettings.Credit;
			StableMenuInput = translationSettings.Menu;
			text = "données embarquées";
			using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("TrueCC.Traduction.entries.json"))
			{
				using StreamReader streamReader = new StreamReader(stream);
				TranslationSet translationSet = JsonUtility.FromJson<TranslationSet>(streamReader.ReadToEnd());
				entries = translationSet.entries;
				engine = new TranslationEngine(entries);
			}
			text = "raccord de langue";
			Language.GetLocalizedStringByToken += new hook_GetLocalizedStringByToken(Translate);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Initialisation VF interrompue (" + text + ") : " + ex));
			engine = null;
			return;
		}
		try
		{
			ModSettingsManager.SetModDescription("Descriptions de compétences en français. Noms conservés. Local uniquement / Client-side only. The first checkbox restores original texts.", "com.truecc.traductioncompetences", "TrueCC — Traduction des compétences");
			ConfigEntry<bool> disableTranslation = DisableTranslation;
			CheckBoxConfig val = new CheckBoxConfig();
			((BaseOptionConfig)val).name = "Pour ce feuj de Logan";
			((BaseOptionConfig)val).description = "Désactive la traduction.";
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(disableTranslation, val), "com.truecc.traductioncompetences", "TrueCC — Traduction des compétences");
			ConfigEntry<bool> stableMenuInput = StableMenuInput;
			CheckBoxConfig val2 = new CheckBoxConfig();
			((BaseOptionConfig)val2).name = "Garder la priorité à la manette";
			((BaseOptionConfig)val2).description = "Dans les menus, relâcher la manette ne réactive pas la souris. Un vrai mouvement, clic ou appui clavier reprend la main. Sans effet sur les commandes en partie.";
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(stableMenuInput, val2), "com.truecc.traductioncompetences", "TrueCC — Traduction des compétences");
			ConfigEntry<bool> showCredit = ShowCredit;
			CheckBoxConfig val3 = new CheckBoxConfig();
			((BaseOptionConfig)val3).name = "Crédit à l'accueil";
			((BaseOptionConfig)val3).description = "Affiche le crédit violet à l'accueil.";
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(showCredit, val3), "com.truecc.traductioncompetences", "TrueCC — Traduction des compétences");
		}
		catch (Exception ex2)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Options VF indisponibles, moteur de traduction toujours actif : " + ex2));
		}
		try
		{
			menuInput = new MenuInputGuard(() => StableMenuInput.Value, delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)message);
			});
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Protection de priorité manette dans les menus : " + StableMenuInput.Value));
		}
		catch (Exception ex3)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Protection menus indisponible : " + ex3));
		}
		try
		{
			((Component)this).gameObject.AddComponent<MenuCredit>();
		}
		catch (Exception ex4)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Crédit non affiché : " + ex4.Message));
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)(engine.Count + " règles de traduction chargées ; aucun changement de gameplay ou de réseau. Désactivation locale=" + DisableTranslation.Value));
	}

	private void Start()
	{
		if (engine == null)
		{
			return;
		}
		try
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (!(assembly.GetType("KizunaMod.KizunaBook", throwOnError: false) == null))
				{
					kizuna = new KizunaTextBridge(engine, entries, () => DisableTranslation.Value);
					if (kizuna.Attach(assembly))
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)"Raccord local des descriptions Kizuna actif.");
					}
					break;
				}
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Kizuna conserve ses textes d'origine : " + ex.Message));
			if (kizuna != null)
			{
				kizuna.Dispose();
			}
			kizuna = null;
		}
	}

	private string Translate(orig_GetLocalizedStringByToken orig, Language language, string token)
	{
		string text = orig.Invoke(language, token);
		if (engine == null)
		{
			return text;
		}
		try
		{
			string text2 = engine.Resolve(token, text, DisableTranslation.Value, () => (Language.english != null && !object.ReferenceEquals(Language.english, language)) ? orig.Invoke(Language.english, token) : null);
			if (text2 != text && translatedSamples < 3)
			{
				translatedSamples++;
				((BaseUnityPlugin)this).Logger.LogDebug((object)("VF appliquée : " + token));
			}
			else if (!DisableTranslation.Value && engine.Knows(token) && unmatchedSamples < 3)
			{
				unmatchedSamples++;
				((BaseUnityPlugin)this).Logger.LogDebug((object)("VF : token connu, texte courant non reconnu ou déjà traduit : " + token));
			}
			return text2;
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Texte d'origine conservé pour " + token + " : " + ex.Message));
			return text;
		}
	}

	private void OnDestroy()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		Language.GetLocalizedStringByToken -= new hook_GetLocalizedStringByToken(Translate);
		if (kizuna != null)
		{
			kizuna.Dispose();
		}
		if (menuInput != null)
		{
			menuInput.Dispose();
		}
	}
}
[Serializable]
public sealed class TranslationSet
{
	public TranslationEntry[] entries;
}
[Serializable]
public sealed class TranslationEntry
{
	public string token;

	public string source;

	public string french;

	public string package;

	public string version;

	public string externalScope;

	public bool rawToken;

	public bool allowTemplate;

	public bool suffixOnly;
}
public sealed class TranslationEngine
{
	private sealed class Rule
	{
		internal TranslationEntry Entry;

		internal Regex Pattern;

		internal string Prefix;

		private static string LiteralPattern(string text)
		{
			return Regex.Escape(text).Replace("\\r\\n", "\\n").Replace("\\n", "(?:\\r\\n|\\n)");
		}

		internal Rule(TranslationEntry entry)
		{
			Entry = entry;
			StringBuilder stringBuilder = new StringBuilder(entry.suffixOnly ? "" : "\\A");
			int num = 0;
			HashSet<string> hashSet = new HashSet<string>();
			foreach (Match item in Regex.Matches(entry.source, "\\{(key[0-9]+|input[0-9]+|[0-9]+)\\}"))
			{
				stringBuilder.Append(LiteralPattern(entry.source.Substring(num, item.Index - num)));
				string value = item.Groups[1].Value;
				if (hashSet.Add(value))
				{
					stringBuilder.Append("(?<n").Append(value).Append('>')
						.Append(value.StartsWith("key", StringComparison.Ordinal) ? "(?:Not set|[A-Za-z][A-Za-z0-9]{0,63}(?: \\+ [A-Za-z][A-Za-z0-9]{0,63}){0,7})" : (value.StartsWith("input", StringComparison.Ordinal) ? "(?:[A-Za-z0-9]{1,4}|--|K[./*+=-]|[()\\[\\]`\\\\])" : "[-+]?[0-9]+(?:[.,][0-9]+)?(?:[eE][-+]?[0-9]+)?"))
						.Append(')');
				}
				else
				{
					stringBuilder.Append("\\k<n").Append(value).Append('>');
				}
				num = item.Index + item.Length;
			}
			stringBuilder.Append(LiteralPattern(entry.source.Substring(num))).Append("\\z");
			Match match2 = Regex.Match(entry.source, "\\{(key[0-9]+|input[0-9]+|[0-9]+)\\}");
			Prefix = (match2.Success ? entry.source.Substring(0, match2.Index) : entry.source);
			int num2 = Prefix.IndexOfAny(new char[2] { '\r', '\n' });
			if (num2 >= 0)
			{
				Prefix = Prefix.Substring(0, num2);
			}
			if (entry.suffixOnly)
			{
				Prefix = "";
			}
			Pattern = new Regex(stringBuilder.ToString(), RegexOptions.CultureInvariant, TimeSpan.FromMilliseconds(20.0));
		}

		internal string Translate(Match m)
		{
			return Regex.Replace(Entry.french, "\\{(key[0-9]+|input[0-9]+|[0-9]+)\\}", (Match x) => m.Groups["n" + x.Groups[1].Value].Value);
		}
	}

	private const string PlaceholderPattern = "\\{(key[0-9]+|input[0-9]+|[0-9]+)\\}";

	private const string ShortcutPattern = "(?:Not set|[A-Za-z][A-Za-z0-9]{0,63}(?: \\+ [A-Za-z][A-Za-z0-9]{0,63}){0,7})";

	private const string InputPattern = "(?:[A-Za-z0-9]{1,4}|--|K[./*+=-]|[()\\[\\]`\\\\])";

	private readonly Dictionary<string, List<Rule>> rules = new Dictionary<string, List<Rule>>(StringComparer.Ordinal);

	private readonly List<Rule> rawRules = new List<Rule>();

	private readonly Dictionary<string, string> cache = new Dictionary<string, string>(StringComparer.Ordinal);

	public int Count { get; private set; }

	public TranslationEngine(IEnumerable<TranslationEntry> entries)
	{
		HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
		foreach (TranslationEntry entry in entries)
		{
			if (entry == null || string.IsNullOrEmpty(entry.token) || string.IsNullOrEmpty(entry.source) || string.IsNullOrEmpty(entry.french))
			{
				throw new ArgumentException("Entrée de traduction incomplète.");
			}
			if (entry.allowTemplate && entry.rawToken)
			{
				throw new ArgumentException("Un modèle de formatage exige un token nommé.");
			}
			if (entry.suffixOnly && (entry.rawToken || entry.allowTemplate || !string.IsNullOrEmpty(entry.externalScope) || !entry.source.StartsWith("\n<color=#d299ff>SCEPTER: ", StringComparison.Ordinal)))
			{
				throw new ArgumentException("Suffixe réservé à une amélioration Scepter nommée et exacte.");
			}
			if (!hashSet.Add(entry.token + "\n" + entry.source))
			{
				throw new ArgumentException("Traduction dupliquée : " + entry.token);
			}
			if (!Placeholders(entry.source).SetEquals(Placeholders(entry.french)))
			{
				throw new ArgumentException("Valeurs dynamiques différentes : " + entry.token);
			}
			if (entry.rawToken)
			{
				rawRules.Add(new Rule(entry));
				Count++;
				continue;
			}
			if (!rules.TryGetValue(entry.token, out var value))
			{
				value = new List<Rule>();
				rules.Add(entry.token, value);
			}
			value.Add(new Rule(entry));
			Count++;
		}
	}

	private static HashSet<string> Placeholders(string text)
	{
		HashSet<string> hashSet = new HashSet<string>();
		foreach (Match item in Regex.Matches(text, "\\{(key[0-9]+|input[0-9]+|[0-9]+)\\}"))
		{
			hashSet.Add(item.Groups[1].Value);
		}
		return hashSet;
	}

	public bool Knows(string token)
	{
		if (token != null)
		{
			return rules.ContainsKey(token);
		}
		return false;
	}

	public string Resolve(string token, string original, bool disabled, Func<string> english)
	{
		string text = Translate(token, original, disabled);
		if (disabled || text != original || !Knows(token) || english == null)
		{
			return text;
		}
		try
		{
			string text2 = english();
			if (text2 == null || text2 == original)
			{
				return original;
			}
			string text3 = Translate(token, text2, disabled: false);
			return (text3 == text2) ? original : text3;
		}
		catch
		{
			return original;
		}
	}

	public string Translate(string token, string original, bool disabled)
	{
		if (disabled || token == null || original == null)
		{
			return original;
		}
		lock (cache)
		{
			if (original.Length > 16384)
			{
				return original;
			}
			if (!rules.TryGetValue(token, out var value))
			{
				if (token != original || rawRules.Count == 0)
				{
					return original;
				}
				value = rawRules;
			}
			string key = token + "\0" + original;
			if (cache.TryGetValue(key, out var value2))
			{
				return value2;
			}
			value2 = original;
			foreach (Rule item in value)
			{
				try
				{
					if (original.StartsWith(item.Prefix, StringComparison.Ordinal))
					{
						if (item.Entry.allowTemplate && string.Equals(original, item.Entry.source, StringComparison.Ordinal))
						{
							value2 = item.Entry.french;
							break;
						}
						Match match = item.Pattern.Match(original);
						if (match.Success)
						{
							value2 = (item.Entry.suffixOnly ? original.Substring(0, match.Index) : "") + item.Translate(match);
							break;
						}
					}
				}
				catch (RegexMatchTimeoutException)
				{
					return original;
				}
			}
			if (cache.Count >= 512)
			{
				cache.Clear();
			}
			cache[key] = value2;
			return value2;
		}
	}
}
internal sealed class TranslationSettings
{
	internal const string Section = "Translation";

	internal const string DisableKey = "DisableTranslation";

	internal const string CreditKey = "ShowMenuCredit";

	internal const string MenuKey = "StableMenuInput";

	internal const string DisableTitle = "Pour ce feuj de Logan";

	internal const string DisableDescription = "Désactive la traduction.";

	internal ConfigEntry<bool> Disable;

	internal ConfigEntry<bool> Credit;

	internal ConfigEntry<bool> Menu;

	internal static TranslationSettings Bind(ConfigFile config)
	{
		bool result = false;
		try
		{
			if (File.Exists(config.ConfigFilePath))
			{
				string input = File.ReadAllText(config.ConfigFilePath);
				Match match = Regex.Match(input, "(?m)^Pour Logan\\s*=\\s*(true|false)\\s*$", RegexOptions.IgnoreCase);
				if (match.Success)
				{
					bool.TryParse(match.Groups[1].Value, out result);
				}
			}
		}
		catch (IOException)
		{
		}
		catch (UnauthorizedAccessException)
		{
		}
		TranslationSettings translationSettings = new TranslationSettings();
		translationSettings.Disable = config.Bind<bool>("Translation", "DisableTranslation", result, "Désactive la traduction.");
		translationSettings.Menu = config.Bind<bool>("Translation", "StableMenuInput", true, "Conserve la priorité manette dans les menus jusqu’à un vrai mouvement, clic ou appui clavier. Indépendant de la traduction.");
		translationSettings.Credit = config.Bind<bool>("Translation", "ShowMenuCredit", true, "Affiche le crédit violet à l'accueil quand un emplacement libre est disponible.");
		return translationSettings;
	}
}