Decompiled source of REPO JP TranslationAndFont Updated v4.0.0

REPOJPTranslationAndFontUpdated.dll

Decompiled 7 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using REPOJapaneseTranslation.Localization;
using REPOJapaneseTranslation.Patches;
using TMPro;
using UnityEngine;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.UI;

[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("LocalBuild")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Japanese localization and Checkpoint Revenge font mod for R.E.P.O. v0.4.4.")]
[assembly: AssemblyFileVersion("4.0.0.0")]
[assembly: AssemblyInformationalVersion("4.0.0")]
[assembly: AssemblyProduct("REPO JP Translation and Font Updated")]
[assembly: AssemblyTitle("REPOJPTranslationAndFontUpdated")]
[assembly: AssemblyVersion("4.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 REPOJapaneseTranslation
{
	[BepInPlugin("local.REPOJPTranslationAndFontUpdated", "REPO JP Translation and Font Updated", "4.0.0")]
	[BepInProcess("REPO.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private readonly Harmony _harmony = new Harmony("local.REPOJPTranslationAndFontUpdated");

		private float _nextTextRefreshTime;

		private int _lastObservedTextCount = -1;

		private GameObject? _runtimeRunnerObject;

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal static ConfigEntry<bool> EnableTranslation { get; private set; }

		internal static ConfigEntry<bool> EnableJapaneseFont { get; private set; }

		internal static ConfigEntry<float> FontSizeScale { get; private set; }

		internal static ConfigEntry<float> FontLineHeightScale { get; private set; }

		internal static ConfigEntry<bool> LogUntranslated { get; private set; }

		private void Awake()
		{
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			EnableTranslation = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableTranslation", true, "ゲーム内テキストを日本語へ翻訳します。");
			EnableJapaneseFont = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableJapaneseFont", true, "チェックポイント★リベンジをTextMeshProへ適用します。");
			FontSizeScale = ((BaseUnityPlugin)this).Config.Bind<float>("General", "FontSizeScale", 0.72f, new ConfigDescription("日本語フォントの表示倍率。1.0がゲーム本来のフォントサイズです。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1f), Array.Empty<object>()));
			FontLineHeightScale = ((BaseUnityPlugin)this).Config.Bind<float>("General", "FontLineHeightScale", 1.35f, new ConfigDescription("複数行テキストの行高倍率。文字が重なる場合に大きくします。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 2f), Array.Empty<object>()));
			LogUntranslated = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogUntranslated", false, "未翻訳テキストをBepInExログへ出力します(翻訳追加作業用)。");
			TranslationManager.Initialize(((BaseUnityPlugin)this).Info.Location);
			FontManager.Initialize(((BaseUnityPlugin)this).Info.Location);
			_harmony.PatchAll(typeof(TMPTextTranslationPatch).Assembly);
			InstallTextRefreshHooks();
			InstallTMPComponentHooks();
			InstallUGUITextHooks();
			CreateRuntimeRunner();
			Log.LogInfo((object)"REPO JP Translation and Font Updated v4.0.0 を読み込みました。");
			Log.LogInfo((object)$"翻訳エントリ: {TranslationManager.TranslationCount}件");
		}

		private void CreateRuntimeRunner()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			_runtimeRunnerObject = new GameObject("REPO JP Translation Runtime");
			((Object)_runtimeRunnerObject).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)(object)_runtimeRunnerObject);
			_runtimeRunnerObject.AddComponent<TranslationRuntimeRunner>();
		}

		private void InstallTextRefreshHooks()
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(TextRefreshHook), "Postfix", (Type[])null, (Type[])null);
			Type[] array = new Type[5]
			{
				typeof(CameraMainMenu),
				typeof(MenuPageMain),
				typeof(MenuManager),
				typeof(RunManager),
				typeof(GameDirector)
			};
			HashSet<MethodBase> hashSet = new HashSet<MethodBase>();
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				MethodInfo methodInfo2 = AccessTools.Method(type, "Update", (Type[])null, (Type[])null);
				if (methodInfo2 == null || !hashSet.Add(methodInfo2))
				{
					Log.LogWarning((object)("TMP更新対象が見つかりません: " + type.Name + ".Update"));
					continue;
				}
				_harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.LogInfo((object)("TMP更新対象をパッチしました: " + type.Name + ".Update"));
			}
			Log.LogInfo((object)$"TMP更新パッチ数: {hashSet.Count}");
		}

		private void InstallTMPComponentHooks()
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(TMPComponentRefreshHook), "Postfix", (Type[])null, (Type[])null);
			Type[] array = new Type[2]
			{
				typeof(TextMeshProUGUI),
				typeof(TextMeshPro)
			};
			int num = 0;
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				MethodInfo methodInfo2 = AccessTools.Method(type, "OnEnable", (Type[])null, (Type[])null);
				if (methodInfo2 != null)
				{
					_harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)("TMP表示対象をパッチしました: " + type.Name + ".OnEnable"));
					num++;
				}
				MethodInfo methodInfo3 = AccessTools.Method(type, "Rebuild", new Type[1] { typeof(CanvasUpdate) }, (Type[])null);
				if (methodInfo3 != null)
				{
					_harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)("TMP表示対象をパッチしました: " + type.Name + ".Rebuild"));
					num++;
				}
			}
			Log.LogInfo((object)$"TMP表示パッチ数: {num}");
		}

		private void InstallUGUITextHooks()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(UGUITextRefreshHook), "Postfix", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(UGUITextSetterHook), "Prefix", (Type[])null, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.Method(typeof(Text), "OnEnable", (Type[])null, (Type[])null);
			MethodInfo methodInfo4 = AccessTools.PropertySetter(typeof(Text), "text");
			int num = 0;
			if (methodInfo3 != null)
			{
				_harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.LogInfo((object)"UGUI表示対象をパッチしました: Text.OnEnable");
				num++;
			}
			if (methodInfo4 != null)
			{
				_harmony.Patch((MethodBase)methodInfo4, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.LogInfo((object)"UGUI表示対象をパッチしました: Text.text");
				num++;
			}
			Log.LogInfo((object)$"UGUI表示パッチ数: {num}");
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
			if ((Object)(object)_runtimeRunnerObject != (Object)null)
			{
				Object.Destroy((Object)(object)_runtimeRunnerObject);
			}
		}

		internal void RefreshVisibleTextThrottled()
		{
			if (!(Time.unscaledTime < _nextTextRefreshTime))
			{
				_nextTextRefreshTime = Time.unscaledTime + 0.5f;
				RefreshVisibleText();
			}
		}

		internal bool RefreshTextComponent(TMP_Text textComponent)
		{
			if (!FontManager.ShouldProcess(textComponent))
			{
				return false;
			}
			if (!EnableTranslation.Value || string.IsNullOrEmpty(textComponent.text))
			{
				return false;
			}
			string text = TranslationManager.Translate(textComponent.text);
			bool flag = text != textComponent.text;
			if (flag)
			{
				textComponent.text = text;
			}
			FontManager.AdjustTranslatedLayout(textComponent, text);
			return flag;
		}

		internal bool RefreshTextComponent(Text textComponent)
		{
			if (!EnableTranslation.Value || string.IsNullOrEmpty(textComponent.text))
			{
				return false;
			}
			string text = TranslationManager.Translate(textComponent.text);
			bool flag = text != textComponent.text;
			if (flag)
			{
				textComponent.text = text;
			}
			FontManager.AdjustTranslatedLayout(textComponent, text);
			return flag;
		}

		private void RefreshVisibleText()
		{
			TMP_Text[] array = Object.FindObjectsOfType<TMP_Text>(true);
			Text[] array2 = Object.FindObjectsOfType<Text>(true);
			int num = 0;
			TMP_Text[] array3 = array;
			foreach (TMP_Text textComponent in array3)
			{
				if (RefreshTextComponent(textComponent))
				{
					num++;
				}
			}
			Text[] array4 = array2;
			foreach (Text textComponent2 in array4)
			{
				if (RefreshTextComponent(textComponent2))
				{
					num++;
				}
			}
			int num2 = array.Length + array2.Length;
			if (num > 0 || num2 != _lastObservedTextCount)
			{
				Log.LogInfo((object)($"UI走査: TMP={array.Length}個、UGUI={array2.Length}個、" + $"翻訳更新={num}個"));
				_lastObservedTextCount = num2;
			}
		}
	}
	internal sealed class TranslationRuntimeRunner : MonoBehaviour
	{
		private void Awake()
		{
			Plugin.Log.LogInfo((object)"常駐UI走査ランナーを開始しました。");
		}

		private void Update()
		{
			Plugin.Instance.RefreshVisibleTextThrottled();
		}
	}
	internal static class PluginInfo
	{
		public const string PLUGIN_GUID = "local.REPOJPTranslationAndFontUpdated";

		public const string PLUGIN_NAME = "REPO JP Translation and Font Updated";

		public const string PLUGIN_VERSION = "4.0.0";
	}
}
namespace REPOJapaneseTranslation.Patches
{
	internal static class MenuPageTranslator
	{
		internal static void TranslatePage(MenuPage? menuPage)
		{
			if (!((Object)(object)menuPage == (Object)null))
			{
				menuPage.menuHeaderName = TranslationManager.Translate(menuPage.menuHeaderName, logUntranslated: false);
				TMP_Text[] componentsInChildren = ((Component)menuPage).GetComponentsInChildren<TMP_Text>(true);
				foreach (TMP_Text text in componentsInChildren)
				{
					TranslateTextComponent(text);
				}
			}
		}

		private static void TranslateTextComponent(TMP_Text text)
		{
			if (!((Object)(object)text == (Object)null) && !string.IsNullOrEmpty(text.text) && FontManager.ShouldProcess(text))
			{
				string text2 = TranslationManager.Translate(text.text, logUntranslated: false);
				if (text2 != text.text)
				{
					text.text = text2;
				}
				FontManager.AdjustTranslatedLayout(text, text2);
			}
		}
	}
	[HarmonyPatch(typeof(MenuButton), "Awake")]
	internal static class MenuButtonTranslationPatch
	{
		[HarmonyPrefix]
		private static void Prefix(MenuButton __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !string.IsNullOrEmpty(__instance.buttonTextString))
			{
				__instance.buttonTextString = TranslationManager.Translate(__instance.buttonTextString);
			}
		}
	}
	[HarmonyPatch(typeof(MenuManager), "PageOpen", new Type[]
	{
		typeof(MenuPageIndex),
		typeof(bool)
	})]
	internal static class MenuPageOpenTranslationPatch
	{
		[HarmonyPostfix]
		private static void Postfix(MenuPage __result)
		{
			MenuPageTranslator.TranslatePage(__result);
		}
	}
	[HarmonyPatch(typeof(MenuPage), "Start")]
	internal static class MenuPageStartTranslationPatch
	{
		[HarmonyPostfix]
		private static void Postfix(MenuPage __instance)
		{
			MenuPageTranslator.TranslatePage(__instance);
		}
	}
	[HarmonyPatch(typeof(MenuManager), "PagePopUp")]
	internal static class MenuPopUpTranslationPatch
	{
		[HarmonyPrefix]
		private static void Prefix(ref string headerText, ref string bodyText, ref string buttonText)
		{
			headerText = TranslationManager.Translate(headerText);
			bodyText = TranslationManager.Translate(bodyText);
			buttonText = TranslationManager.Translate(buttonText);
		}
	}
	[HarmonyPatch(typeof(MenuManager), "PagePopUpTwoOptions")]
	internal static class MenuTwoOptionPopUpTranslationPatch
	{
		[HarmonyPrefix]
		private static void Prefix(ref string popUpHeader, ref string popUpText, ref string option1Text, ref string option2Text)
		{
			popUpHeader = TranslationManager.Translate(popUpHeader);
			popUpText = TranslationManager.Translate(popUpText);
			option1Text = TranslationManager.Translate(option1Text);
			option2Text = TranslationManager.Translate(option2Text);
		}
	}
	[HarmonyPatch(typeof(MenuPageSaves), "SaveFileSelected")]
	internal static class MenuPageSavesSaveFileSelectedPatch
	{
		[HarmonyPostfix]
		private static void Postfix(MenuPageSaves __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			string newValue = TranslationManager.Translate("Total Haul:", logUntranslated: false);
			string newValue2 = TranslationManager.Translate("Current Moon:", logUntranslated: false);
			TMP_Text[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(val.text) && FontManager.ShouldProcess(val))
				{
					string text = val.text;
					string text2 = text.Replace("Total Haul:", newValue).Replace("Current Moon:", newValue2);
					if (text2 != text)
					{
						val.text = text2;
					}
					FontManager.AdjustTranslatedLayout(val, text2);
				}
			}
		}
	}
	internal static class TextRefreshHook
	{
		private static bool s_loggedFirstTick;

		internal static void Postfix()
		{
			if (!s_loggedFirstTick)
			{
				s_loggedFirstTick = true;
				Plugin.Log.LogInfo((object)"TMP更新フックが動作しました。");
			}
			Plugin.Instance.RefreshVisibleTextThrottled();
		}
	}
	internal static class UGUITextRefreshHook
	{
		private static bool s_loggedFirstComponent;

		internal static void Postfix(Text __instance)
		{
			if (!s_loggedFirstComponent)
			{
				s_loggedFirstComponent = true;
				Plugin.Log.LogInfo((object)"UGUI Textコンポーネントを検出しました。");
			}
			Plugin.Instance.RefreshTextComponent(__instance);
		}
	}
	internal static class UGUITextSetterHook
	{
		internal static void Prefix(Text __instance, ref string __0)
		{
			__0 = TranslationManager.Translate(__0);
			FontManager.AdjustTranslatedLayout(__instance, __0);
		}
	}
	internal static class TMPComponentRefreshHook
	{
		private static bool s_loggedFirstComponent;

		internal static void Postfix(TMP_Text __instance)
		{
			if (!s_loggedFirstComponent)
			{
				s_loggedFirstComponent = true;
				Plugin.Log.LogInfo((object)("TMP表示コンポーネントを検出しました: " + ((object)__instance).GetType().Name));
			}
			Plugin.Instance.RefreshTextComponent(__instance);
		}
	}
	[HarmonyPatch]
	internal static class TMPFontPatch
	{
		[HarmonyPatch(typeof(RunManager), "Awake")]
		[HarmonyPostfix]
		private static void ApplyFontAfterSceneLoad()
		{
			FontManager.ApplyToAllTextComponents();
		}
	}
	[HarmonyPatch]
	internal static class TMPTextTranslationPatch
	{
		[HarmonyTargetMethods]
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo methodInfo = AccessTools.PropertySetter(typeof(TMP_Text), "text");
			if (methodInfo != null)
			{
				yield return methodInfo;
			}
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(TMP_Text)))
			{
				if (!(declaredMethod.Name != "SetText"))
				{
					ParameterInfo[] parameters = declaredMethod.GetParameters();
					if (parameters.Length != 0 && !(parameters[0].ParameterType != typeof(string)))
					{
						yield return declaredMethod;
					}
				}
			}
		}

		[HarmonyPrefix]
		private static void TranslateText(TMP_Text __instance, ref string __0)
		{
			if (FontManager.ShouldProcess(__instance))
			{
				__0 = TranslationManager.Translate(__0);
				FontManager.AdjustTranslatedLayout(__instance, __0);
			}
		}
	}
	[HarmonyPatch(typeof(TruckScreenText), "UpdateTaxmanNickname")]
	internal static class TruckScreenTextUpdateTaxmanNicknamePatch
	{
		[HarmonyPrefix]
		private static void Prefix(ref string newName)
		{
			newName = TranslationManager.Translate(newName, logUntranslated: false);
		}
	}
	[HarmonyPatch(typeof(TuckScreenLocked), "LockChatToggle")]
	internal static class TuckScreenLockedLockChatTogglePatch
	{
		[HarmonyPrefix]
		private static void Prefix(ref string _lockedText)
		{
			_lockedText = TranslationManager.Translate(_lockedText, logUntranslated: false);
		}
	}
}
namespace REPOJapaneseTranslation.Localization
{
	internal static class FontManager
	{
		private readonly struct LayoutProfile
		{
			internal float Scale { get; }

			internal float MinimumScale { get; }

			internal bool AllowWrapping { get; }

			internal bool AutoSizing { get; }

			internal float AdditionalLineSpacing { get; }

			internal bool ConstrainToBounds { get; }

			internal float AbsoluteSize { get; }

			internal LayoutProfile(float scale, float minimumScale, bool allowWrapping, bool autoSizing = true, float additionalLineSpacing = 0f, bool constrainToBounds = false, float absoluteSize = 0f)
			{
				Scale = scale;
				MinimumScale = minimumScale;
				AllowWrapping = allowWrapping;
				AutoSizing = autoSizing;
				AdditionalLineSpacing = additionalLineSpacing;
				ConstrainToBounds = constrainToBounds;
				AbsoluteSize = absoluteSize;
			}
		}

		private readonly struct TMPFontState
		{
			private WeakReference<TMP_Text> Owner { get; }

			internal TMP_FontAsset Font { get; }

			internal float FontSize { get; }

			internal float FontSizeMin { get; }

			internal float FontSizeMax { get; }

			internal bool AutoSizing { get; }

			internal bool WordWrapping { get; }

			internal TextOverflowModes Overflow { get; }

			internal float LineSpacing { get; }

			internal TMPFontState(TMP_Text component)
			{
				//IL_0056: Unknown result type (might be due to invalid IL or missing references)
				//IL_005b: Unknown result type (might be due to invalid IL or missing references)
				Owner = new WeakReference<TMP_Text>(component);
				Font = component.font;
				FontSize = component.fontSize;
				FontSizeMin = component.fontSizeMin;
				FontSizeMax = component.fontSizeMax;
				AutoSizing = component.enableAutoSizing;
				WordWrapping = component.enableWordWrapping;
				Overflow = component.overflowMode;
				LineSpacing = component.lineSpacing;
			}

			internal bool Matches(TMP_Text component)
			{
				if (Owner.TryGetTarget(out TMP_Text target))
				{
					return target == component;
				}
				return false;
			}

			internal void Restore(TMP_Text component)
			{
				//IL_004a: Unknown result type (might be due to invalid IL or missing references)
				component.font = Font;
				component.fontSize = FontSize;
				component.fontSizeMin = FontSizeMin;
				component.fontSizeMax = FontSizeMax;
				component.enableAutoSizing = AutoSizing;
				component.enableWordWrapping = WordWrapping;
				component.overflowMode = Overflow;
				component.lineSpacing = LineSpacing;
			}
		}

		private readonly struct UGUIFontState
		{
			private WeakReference<Text> Owner { get; }

			internal Font Font { get; }

			internal int FontSize { get; }

			internal int MinSize { get; }

			internal int MaxSize { get; }

			internal bool BestFit { get; }

			internal HorizontalWrapMode Overflow { get; }

			internal UGUIFontState(Text component)
			{
				//IL_004a: Unknown result type (might be due to invalid IL or missing references)
				//IL_004f: Unknown result type (might be due to invalid IL or missing references)
				Owner = new WeakReference<Text>(component);
				Font = component.font;
				FontSize = component.fontSize;
				MinSize = component.resizeTextMinSize;
				MaxSize = component.resizeTextMaxSize;
				BestFit = component.resizeTextForBestFit;
				Overflow = component.horizontalOverflow;
			}

			internal bool Matches(Text component)
			{
				if (Owner.TryGetTarget(out Text target))
				{
					return target == component;
				}
				return false;
			}

			internal void Restore(Text component)
			{
				//IL_003e: Unknown result type (might be due to invalid IL or missing references)
				component.font = Font;
				component.fontSize = FontSize;
				component.resizeTextMinSize = MinSize;
				component.resizeTextMaxSize = MaxSize;
				component.resizeTextForBestFit = BestFit;
				component.horizontalOverflow = Overflow;
			}
		}

		private const string FontResourceName = "REPOJapaneseTranslation.fonts.CP_Revenge.ttf";

		private const string FontEnginePatchId = "local.REPOJPTranslationAndFontUpdated.FontEngine";

		private static readonly Dictionary<Font, byte[]> s_fontBytesByDummyFont = new Dictionary<Font, byte[]>();

		private static readonly Dictionary<int, TMPFontState> s_originalTMPStates = new Dictionary<int, TMPFontState>();

		private static readonly Dictionary<int, UGUIFontState> s_originalUGUIStates = new Dictionary<int, UGUIFontState>();

		private static readonly HashSet<string> s_compactTitles = new HashSet<string>(StringComparer.Ordinal)
		{
			"ゲームプレイ", "グラフィックス", "オーディオ", "コントロール", "パスワード", "ロビー", "メインメニュー", "警告", "自爆", "ランダム",
			"プリセット", "セーブをロード", "ゲームをホストする", "設定を初期化", "デフォルト設定にリセット"
		};

		private static readonly HashSet<string> s_dialogButtons = new HashSet<string>(StringComparer.Ordinal) { "はい", "はい!", "いいえ", "いいえ…", "戻る", "続ける", "スキップ", "わかりました", "セーブをロード", "セーブを削除" };

		private static readonly HashSet<string> s_uniformMenuItems = new HashSet<string>(StringComparer.Ordinal)
		{
			"プライベートゲーム", "パブリックゲーム", "フレンドに参加", "シングルプレイ", "シングルプレイモード", "チュートリアル", "続ける", "カスタマイズ", "設定", "自爆",
			"メインメニュー", "タイトルに戻る", "ゲームを終わる", "ゲームを終了する"
		};

		private static readonly HashSet<string> s_settingsRows = new HashSet<string>(StringComparer.Ordinal)
		{
			"ヒント", "プレイヤー名", "光過敏症設定", "クモ恐怖症", "照準感度", "カメラスムージング", "カメラシェイク", "シャドウ距離", "モーションブラー", "レンズ歪み",
			"ブルーム", "色収差", "グレイン", "グリッチループ", "ピクセル化", "音楽音量", "効果音音量", "近接ボイス音量", "マイク音量", "プッシュトゥトーク",
			"掴む", "回転", "押す", "引く", "ウィンドウモード", "垂直同期", "ガンマ", "最大FPS", "ライト距離", "シャドウ品質",
			"シャドウ距離", "マスター音量", "テキスト読み上げ音量", "オン", "オフ", "デフォルト", "フルスクリーン", "ウィンドウ", "ボーダーレス", "低",
			"中", "高", "最高"
		};

		private static TMP_FontAsset? s_checkpointFont;

		private static Font? s_checkpointUnityFont;

		private static bool s_initialized;

		private static bool s_fontEnginePatched;

		internal static void Initialize(string pluginLocation)
		{
			if (s_initialized)
			{
				return;
			}
			s_initialized = true;
			if (!Plugin.EnableJapaneseFont.Value)
			{
				return;
			}
			try
			{
				Assembly executingAssembly = Assembly.GetExecutingAssembly();
				using Stream stream = executingAssembly.GetManifestResourceStream("REPOJapaneseTranslation.fonts.CP_Revenge.ttf");
				if (stream == null)
				{
					Plugin.Log.LogError((object)"埋め込みフォントが見つかりません: REPOJapaneseTranslation.fonts.CP_Revenge.ttf");
					return;
				}
				byte[] array = new byte[stream.Length];
				int num;
				for (int i = 0; i < array.Length; i += num)
				{
					num = stream.Read(array, i, array.Length - i);
					if (num == 0)
					{
						throw new EndOfStreamException("埋め込みフォントを最後まで読み取れませんでした。");
					}
				}
				PatchFontEngine();
				s_checkpointFont = CreateFontAsset(array);
				if ((Object)(object)s_checkpointFont == (Object)null)
				{
					Plugin.Log.LogError((object)"チェックポイント★リベンジのTMPフォント生成に失敗しました。");
					return;
				}
				ApplyToAllTextComponents();
				Plugin.Log.LogInfo((object)$"チェックポイント★リベンジを読み込みました (埋め込み未改変TTF ({array.Length:N0} bytes))。");
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"フォント初期化に失敗しました: {arg}");
			}
		}

		internal static void ApplyToText(TMP_Text? textComponent)
		{
			if (ShouldProcess(textComponent) && ContainsJapanese(textComponent.text))
			{
				AdjustTranslatedLayout(textComponent, textComponent.text);
			}
		}

		internal static bool ShouldProcess(TMP_Text? textComponent)
		{
			if ((Object)(object)textComponent == (Object)null)
			{
				return false;
			}
			string text = GetHierarchyPath(textComponent.transform).ToLowerInvariant();
			bool flag = text.Contains("monitor") || text.Contains("truckscreen") || (text.Contains("truck") && text.Contains("screen"));
			return !flag;
		}

		internal static void ApplyToText(Text? textComponent)
		{
			if ((Object)(object)textComponent != (Object)null && ContainsJapanese(textComponent.text))
			{
				AdjustTranslatedLayout(textComponent, textComponent.text);
			}
		}

		internal static void ApplyToAllTextComponents()
		{
			if ((Object)(object)s_checkpointFont == (Object)null)
			{
				return;
			}
			TMP_Text[] array = Object.FindObjectsOfType<TMP_Text>(true);
			TMP_Text[] array2 = array;
			foreach (TMP_Text textComponent in array2)
			{
				if (ShouldProcess(textComponent))
				{
					ApplyToText(textComponent);
				}
			}
			Plugin.Log.LogDebug((object)$"{array.Length}個のTMPテキストを確認しました。");
		}

		internal static void AdjustTranslatedLayout(TMP_Text textComponent, string translatedText)
		{
			if (!Plugin.EnableJapaneseFont.Value || (Object)(object)s_checkpointFont == (Object)null || !ShouldProcess(textComponent))
			{
				return;
			}
			int instanceID = ((Object)textComponent).GetInstanceID();
			if (!s_originalTMPStates.TryGetValue(instanceID, out var value) || !value.Matches(textComponent))
			{
				value = new TMPFontState(textComponent);
				s_originalTMPStates[instanceID] = value;
			}
			if (!ContainsJapanese(translatedText))
			{
				value.Restore(textComponent);
				if (IsSettingsHierarchy(GetHierarchyPath(textComponent.transform)) && ContainsDigit(translatedText))
				{
					ApplyOriginalFontScale(textComponent, value, 0.78f);
				}
				return;
			}
			LayoutProfile layoutProfile = ResolveProfile(textComponent, translatedText);
			textComponent.font = s_checkpointFont;
			textComponent.enableAutoSizing = layoutProfile.AutoSizing;
			float val = ((layoutProfile.AbsoluteSize > 0f) ? layoutProfile.AbsoluteSize : (value.FontSize * layoutProfile.Scale));
			float val2 = ((layoutProfile.AbsoluteSize > 0f) ? layoutProfile.AbsoluteSize : (value.FontSizeMax * layoutProfile.Scale));
			float val3 = ((layoutProfile.AbsoluteSize > 0f) ? (layoutProfile.AbsoluteSize * 0.78f) : (value.FontSizeMax * layoutProfile.MinimumScale));
			textComponent.fontSize = Math.Max(1f, val);
			textComponent.fontSizeMax = Math.Max(1f, val2);
			textComponent.fontSizeMin = Math.Max(1f, val3);
			textComponent.enableWordWrapping = layoutProfile.AllowWrapping;
			textComponent.overflowMode = (TextOverflowModes)(layoutProfile.ConstrainToBounds ? 1 : 0);
			textComponent.lineSpacing = value.LineSpacing + layoutProfile.AdditionalLineSpacing;
		}

		internal static void AdjustTranslatedLayout(Text textComponent, string translatedText)
		{
			if (Plugin.EnableJapaneseFont.Value && !((Object)(object)s_checkpointUnityFont == (Object)null) && !((Object)(object)textComponent == (Object)null))
			{
				int instanceID = ((Object)textComponent).GetInstanceID();
				if (!s_originalUGUIStates.TryGetValue(instanceID, out var value) || !value.Matches(textComponent))
				{
					value = new UGUIFontState(textComponent);
					s_originalUGUIStates[instanceID] = value;
				}
				if (!ContainsJapanese(translatedText))
				{
					value.Restore(textComponent);
					return;
				}
				float num = ResolveUGUIScale(translatedText);
				textComponent.font = s_checkpointUnityFont;
				textComponent.fontSize = Math.Max(1, (int)Math.Round((float)value.FontSize * num));
				textComponent.resizeTextForBestFit = true;
				textComponent.resizeTextMinSize = Math.Max(1, (int)Math.Round((float)value.MaxSize * 0.28f));
				textComponent.resizeTextMaxSize = Math.Max(1, (int)Math.Round((float)value.MaxSize * num));
				textComponent.horizontalOverflow = (HorizontalWrapMode)(!translatedText.Contains('\n'));
			}
		}

		private static LayoutProfile ResolveProfile(TMP_Text component, string text)
		{
			string text2 = RemoveSimpleRichText(text).Trim();
			string text3 = GetHierarchyPath(component.transform).ToLowerInvariant();
			switch (text2)
			{
			case "パスワード":
				return new LayoutProfile(0.5f, 0.25f, allowWrapping: false);
			case "ロビー":
				return new LayoutProfile(0.46f, 0.23f, allowWrapping: false);
			case "セーブをロード":
				return new LayoutProfile(0.52f, 0.26f, allowWrapping: false);
			case "光敏感性発作についての警告":
			case "光過敏症の警告":
				return new LayoutProfile(0.62f, 0.52f, allowWrapping: true, autoSizing: false, 12f);
			default:
				if (text2.StartsWith("このゲームには点滅する光が含まれています。", StringComparison.Ordinal))
				{
					return new LayoutProfile(0.65f, 0.54f, allowWrapping: true, autoSizing: false, 14f);
				}
				if (text2.StartsWith("一部の方は、光のパターンや点滅する光", StringComparison.Ordinal))
				{
					return new LayoutProfile(0.58f, 0.46f, allowWrapping: true, autoSizing: false, 24f);
				}
				if (text2 == "メニュー")
				{
					return new LayoutProfile(0.4f, 0.28f, allowWrapping: false);
				}
				if (s_uniformMenuItems.Contains(text2))
				{
					return new LayoutProfile(1f, 0.78f, allowWrapping: false, autoSizing: false, 0f, constrainToBounds: false, 20f);
				}
				if (text2.StartsWith("総回収額:", StringComparison.Ordinal) || text2.StartsWith("現在の月:", StringComparison.Ordinal))
				{
					return new LayoutProfile(0.68f, 0.48f, allowWrapping: false);
				}
				switch (text2)
				{
				case "(クリックして名前変更)":
					return new LayoutProfile(0.52f, 0.36f, allowWrapping: false, autoSizing: true, 0f, constrainToBounds: true);
				case "シングルプレイモード":
					return new LayoutProfile(0.44f, 0.3f, allowWrapping: false, autoSizing: true, 0f, constrainToBounds: true);
				case "ウィンドウモード":
					return new LayoutProfile(0.23f, 0.16f, allowWrapping: false, autoSizing: true, 0f, constrainToBounds: true);
				case "モーションブラー":
					return new LayoutProfile(0.24f, 0.16f, allowWrapping: false, autoSizing: true, 0f, constrainToBounds: true);
				case "カメラスムージング":
				case "カメラの滑らかさ":
					return new LayoutProfile(0.22f, 0.15f, allowWrapping: false, autoSizing: true, 0f, constrainToBounds: true);
				case "カメラアニメーション":
				case "カメラの動き":
					return new LayoutProfile(0.22f, 0.15f, allowWrapping: false, autoSizing: true, 0f, constrainToBounds: true);
				case "アンチエイリアシング":
					return new LayoutProfile(0.22f, 0.15f, allowWrapping: false, autoSizing: true, 0f, constrainToBounds: true);
				case "リッチプレゼンス":
					return new LayoutProfile(0.22f, 0.15f, allowWrapping: false, autoSizing: true, 0f, constrainToBounds: true);
				case "アイテム自動装備":
					return new LayoutProfile(0.22f, 0.15f, allowWrapping: false, autoSizing: true, 0f, constrainToBounds: true);
				case "オン":
				case "オフ":
					return new LayoutProfile(0.48f, 0.34f, allowWrapping: false, autoSizing: true, 0f, constrainToBounds: true);
				default:
				{
					if (IsControlHierarchy(text3))
					{
						return new LayoutProfile(0.52f, 0.34f, allowWrapping: false, autoSizing: true, 0f, constrainToBounds: true);
					}
					if (text2 == "プリセット")
					{
						return new LayoutProfile(0.82f, 0.58f, allowWrapping: false);
					}
					if (text2 == "確認")
					{
						return new LayoutProfile(0.95f, 0.66f, allowWrapping: false);
					}
					if (text2.StartsWith("マイク", StringComparison.Ordinal))
					{
						return new LayoutProfile(0.36f, 0.22f, allowWrapping: true);
					}
					if (s_dialogButtons.Contains(text2))
					{
						return new LayoutProfile(0.58f, 0.3f, allowWrapping: false);
					}
					if (s_settingsRows.Contains(text2))
					{
						return new LayoutProfile(0.3f, 0.18f, allowWrapping: false, autoSizing: true, 0f, constrainToBounds: true);
					}
					if (s_compactTitles.Contains(text2))
					{
						return new LayoutProfile(0.52f, 0.25f, allowWrapping: false);
					}
					bool flag = text3.Contains("popup") || text3.Contains("dialog") || text3.Contains("confirm") || text3.Contains("warning");
					bool flag2 = IsSettingsHierarchy(text3);
					bool flag3 = text3.Contains("password");
					bool flag4 = text3.Contains("lobby");
					bool flag5 = text3.Contains("save");
					if (flag3)
					{
						return new LayoutProfile(0.5f, 0.24f, text.Contains('\n'));
					}
					if (flag4)
					{
						return new LayoutProfile(0.5f, 0.24f, text.Contains('\n'));
					}
					if (flag5)
					{
						return new LayoutProfile(0.62f, 0.34f, text.Length > 18 || text.Contains('\n'));
					}
					if (flag2)
					{
						return new LayoutProfile(0.3f, 0.18f, text.Length > 20 || text.Contains('\n'), autoSizing: true, 0f, constrainToBounds: true);
					}
					if (flag)
					{
						return new LayoutProfile(0.45f, 0.2f, allowWrapping: true, autoSizing: true, 0f, constrainToBounds: true);
					}
					if (text.Contains('\n') || text2.Length >= 18)
					{
						return new LayoutProfile(0.46f, 0.2f, allowWrapping: true, autoSizing: true, 0f, constrainToBounds: true);
					}
					return new LayoutProfile(Plugin.FontSizeScale.Value, 0.28f, allowWrapping: false);
				}
				}
			}
		}

		private static float ResolveUGUIScale(string text)
		{
			string text2 = RemoveSimpleRichText(text).Trim();
			if (s_dialogButtons.Contains(text2))
			{
				return 0.58f;
			}
			if (s_settingsRows.Contains(text2))
			{
				return 0.47f;
			}
			if (s_compactTitles.Contains(text2))
			{
				return 0.52f;
			}
			if (text2.Length < 18 && !text.Contains('\n'))
			{
				return Plugin.FontSizeScale.Value;
			}
			return 0.46f;
		}

		private static bool ContainsJapanese(string? text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			foreach (char c in text)
			{
				if ((c >= '\u3040' && c <= 'ヿ') || (c >= '㐀' && c <= '鿿') || (c >= '豈' && c <= '\ufaff') || (c >= 'ヲ' && c <= '゚'))
				{
					return true;
				}
			}
			return false;
		}

		private static bool ContainsDigit(string? text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			foreach (char c in text)
			{
				if (char.IsDigit(c))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsSettingsHierarchy(string hierarchy)
		{
			string text = hierarchy.ToLowerInvariant();
			if (!text.Contains("setting") && !text.Contains("graphic") && !text.Contains("audio") && !text.Contains("control"))
			{
				return text.Contains("gameplay");
			}
			return true;
		}

		private static bool IsControlHierarchy(string hierarchy)
		{
			string text = hierarchy.ToLowerInvariant();
			if (!text.Contains("control") && !text.Contains("keybind"))
			{
				return text.Contains("input");
			}
			return true;
		}

		private static void ApplyOriginalFontScale(TMP_Text component, TMPFontState original, float scale)
		{
			component.enableAutoSizing = false;
			component.fontSize = Math.Max(1f, original.FontSize * scale);
			component.fontSizeMax = Math.Max(1f, original.FontSizeMax * scale);
			component.fontSizeMin = Math.Max(1f, original.FontSizeMax * scale * 0.65f);
			component.overflowMode = (TextOverflowModes)0;
		}

		private static string GetHierarchyPath(Transform? transform)
		{
			if ((Object)(object)transform == (Object)null)
			{
				return string.Empty;
			}
			string text = ((Object)transform).name;
			Transform parent = transform.parent;
			int num = 0;
			while ((Object)(object)parent != (Object)null && num++ < 12)
			{
				text = ((Object)parent).name + "/" + text;
				parent = parent.parent;
			}
			return text;
		}

		private static string RemoveSimpleRichText(string text)
		{
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			bool flag = false;
			foreach (char c in text)
			{
				if (c == '<')
				{
					flag = true;
				}
				else if (c == '>' && flag)
				{
					flag = false;
				}
				else if (!flag)
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}

		private static TMP_FontAsset? CreateFontAsset(byte[] fontBytes)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			FontEngine.InitializeFontEngine();
			FontEngineError val = FontEngine.LoadFontFace(fontBytes);
			if ((int)val != 0)
			{
				Plugin.Log.LogError((object)$"TTFの検証に失敗しました: {val}");
				return null;
			}
			Font val2 = (s_checkpointUnityFont = new Font("CP_Revenge"));
			s_fontBytesByDummyFont[val2] = fontBytes;
			TMP_FontAsset val3 = TMP_FontAsset.CreateFontAsset(val2);
			if ((Object)(object)val3 == (Object)null)
			{
				s_fontBytesByDummyFont.Remove(val2);
				s_checkpointUnityFont = null;
				Object.Destroy((Object)(object)val2);
				return null;
			}
			((Object)val3).name = "Checkpoint Revenge Dynamic";
			val3.atlasPopulationMode = (AtlasPopulationMode)1;
			FaceInfo faceInfo = val3.faceInfo;
			((FaceInfo)(ref faceInfo)).lineHeight = ((FaceInfo)(ref faceInfo)).lineHeight * Plugin.FontLineHeightScale.Value;
			val3.faceInfo = faceInfo;
			ConfigureTextMeshProMaterial(val3);
			return val3;
		}

		private static void ConfigureTextMeshProMaterial(TMP_FontAsset fontAsset)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			Shader val = Shader.Find("TextMeshPro/Distance Field");
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"TextMeshPro/Distance Fieldシェーダーが見つかりません。");
				return;
			}
			Material material = ((TMP_Asset)fontAsset).material;
			material.shader = val;
			if (material.HasProperty("_GlowColor"))
			{
				material.SetColor("_GlowColor", Color.clear);
			}
			if (material.HasProperty("_GlowPower"))
			{
				material.SetFloat("_GlowPower", 0f);
			}
		}

		private static void PatchFontEngine()
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			if (!s_fontEnginePatched)
			{
				MethodInfo method = typeof(FontEngine).GetMethod("LoadFontFace", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
				{
					typeof(Font),
					typeof(int)
				}, null);
				MethodInfo method2 = typeof(FontManager).GetMethod("LoadFontFacePrefix", BindingFlags.Static | BindingFlags.NonPublic);
				if (method == null || method2 == null)
				{
					throw new MissingMethodException("FontEngine.LoadFontFace(Font, int) が見つかりません。");
				}
				new Harmony("local.REPOJPTranslationAndFontUpdated.FontEngine").Patch((MethodBase)method, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				s_fontEnginePatched = true;
			}
		}

		private static bool LoadFontFacePrefix(Font font, int pointSize, ref FontEngineError __result)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected I4, but got Unknown
			if ((Object)(object)font == (Object)null || !s_fontBytesByDummyFont.TryGetValue(font, out byte[] value))
			{
				return true;
			}
			__result = (FontEngineError)(int)FontEngine.LoadFontFace(value, pointSize);
			return false;
		}
	}
	internal static class TranslationManager
	{
		private sealed class TranslationTemplate
		{
			private readonly Regex _pattern;

			private readonly string _translatedTemplate;

			private readonly string[] _placeholderTokens;

			internal int SourceLength { get; }

			private TranslationTemplate(Regex pattern, string translatedTemplate, string[] placeholderTokens, int sourceLength)
			{
				_pattern = pattern;
				_translatedTemplate = translatedTemplate;
				_placeholderTokens = placeholderTokens;
				SourceLength = sourceLength;
			}

			internal static TranslationTemplate Create(string sourceTemplate, string translatedTemplate)
			{
				MatchCollection matchCollection = s_placeholderTokenRegex.Matches(sourceTemplate);
				StringBuilder stringBuilder = new StringBuilder();
				string[] array = new string[matchCollection.Count];
				stringBuilder.Append('^');
				int num = 0;
				int num2;
				for (int i = 0; i < matchCollection.Count; i++)
				{
					Match match = matchCollection[i];
					num2 = num;
					stringBuilder.Append(Regex.Escape(sourceTemplate.Substring(num2, match.Index - num2)));
					stringBuilder.Append($"(?<p{i}>.+?)");
					array[i] = match.Value;
					num = match.Index + match.Length;
				}
				num2 = num;
				stringBuilder.Append(Regex.Escape(sourceTemplate.Substring(num2, sourceTemplate.Length - num2)));
				stringBuilder.Append('$');
				Regex pattern = new Regex(stringBuilder.ToString(), RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant);
				return new TranslationTemplate(pattern, translatedTemplate, array, sourceTemplate.Length);
			}

			internal bool TryTranslate(string text, out string result)
			{
				Match match = _pattern.Match(text);
				if (!match.Success)
				{
					result = text;
					return false;
				}
				Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
				for (int i = 0; i < _placeholderTokens.Length; i++)
				{
					string key = _placeholderTokens[i];
					if (!dictionary.ContainsKey(key))
					{
						dictionary[key] = match.Groups[$"p{i}"].Value;
					}
				}
				result = _translatedTemplate;
				foreach (KeyValuePair<string, string> item in dictionary)
				{
					item.Deconstruct(out var key2, out var value);
					string oldValue = key2;
					string newValue = value;
					result = result.Replace(oldValue, newValue, StringComparison.Ordinal);
				}
				return true;
			}
		}

		private static readonly Regex s_placeholderTokenRegex = new Regex("\\{[^{}]+\\}|\\[[^\\[\\]]+\\]", RegexOptions.Compiled | RegexOptions.CultureInvariant);

		private static readonly Regex s_actionSuffixRegex = new Regex("^(?<body>.+?)(?<suffix>\\s*(?:<[^>]+>)*\\[[^\\[\\]]+\\](?:</[^>]+>)*\\s*)$", RegexOptions.Compiled | RegexOptions.CultureInvariant);

		private static readonly Regex s_richTextSuffixRegex = new Regex("^(?<body>.+?)(?<suffix>\\s*(?:<[^>]+>)+\\s*)$", RegexOptions.Compiled | RegexOptions.CultureInvariant);

		private static readonly Regex s_labeledPrefixRegex = new Regex("^(?<leadingTags>(?:<[^>]+>)*)(?<label>[^<>]+?)(?<separator>\\s*>\\s*)(?<trailingTags>(?:</[^>]+>)*)(?<body>.+)$", RegexOptions.Compiled | RegexOptions.CultureInvariant);

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

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

		private static readonly List<TranslationTemplate> s_templates = new List<TranslationTemplate>();

		private static readonly HashSet<string> s_loggedUntranslated = new HashSet<string>(StringComparer.Ordinal);

		private static bool s_initialized;

		internal static int TranslationCount => s_translations.Count;

		internal static void Initialize(string pluginLocation)
		{
			if (s_initialized)
			{
				return;
			}
			s_initialized = true;
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			using Stream stream = executingAssembly.GetManifestResourceStream("REPOJapaneseTranslation.translations.ja.json");
			if (stream == null)
			{
				Plugin.Log.LogError((object)"埋め込みリソース 'REPOJapaneseTranslation.translations.ja.json' が見つかりません。");
				return;
			}
			using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8);
			LoadJson(streamReader.ReadToEnd());
			string path = Path.GetDirectoryName(pluginLocation) ?? string.Empty;
			string path2 = Path.Combine(path, "translations.custom.json");
			if (File.Exists(path2))
			{
				LoadJson(File.ReadAllText(path2, Encoding.UTF8));
				Plugin.Log.LogInfo((object)"translations.custom.jsonを追加読み込みしました。");
			}
			Plugin.Log.LogInfo((object)$"翻訳辞書を読み込みました: {s_translations.Count} 件");
		}

		private static void LoadJson(string json)
		{
			//IL_00f3: Expected O, but got Unknown
			try
			{
				Dictionary<string, string> dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
				if (dictionary == null)
				{
					return;
				}
				foreach (var (text3, text4) in dictionary)
				{
					if (!string.IsNullOrEmpty(text3) && text3[0] != '_' && !text3.StartsWith("//", StringComparison.Ordinal))
					{
						string text5 = NormalizeTranslationValue(text4);
						s_translations[text3] = text5;
						string key = NormalizeKey(text3).ToUpperInvariant();
						s_translationsUpper[key] = text5;
						if (s_placeholderTokenRegex.IsMatch(text3))
						{
							s_templates.Add(TranslationTemplate.Create(NormalizeKey(text3), text5));
						}
					}
				}
				s_templates.Sort((TranslationTemplate left, TranslationTemplate right) => right.SourceLength.CompareTo(left.SourceLength));
			}
			catch (JsonException ex)
			{
				JsonException ex2 = ex;
				Plugin.Log.LogError((object)("翻訳JSONのパースに失敗しました: " + ((Exception)(object)ex2).Message));
			}
		}

		internal static string Translate(string text, bool logUntranslated = true)
		{
			if (!Plugin.EnableTranslation.Value || string.IsNullOrEmpty(text))
			{
				return text;
			}
			SplitOuterWhitespace(text, out string leadingWhitespace, out string coreText, out string trailingWhitespace);
			if (coreText.Length == 0)
			{
				return text;
			}
			if (TryTranslateCore(coreText, out string result))
			{
				return leadingWhitespace + result + trailingWhitespace;
			}
			if (logUntranslated && Plugin.LogUntranslated.Value && s_loggedUntranslated.Add(text))
			{
				Plugin.Log.LogInfo((object)("[未翻訳] \"" + text.Replace("\n", "\\n") + "\""));
			}
			return text;
		}

		private static bool TryTranslateCore(string text, out string result)
		{
			if (TryTranslateInlineText(text, out result))
			{
				return true;
			}
			if (TryTranslateMultilineBlock(text, out result))
			{
				return true;
			}
			result = text;
			return false;
		}

		private static bool TryTranslateInlineText(string text, out string result)
		{
			if (TryTranslateLookup(text, out result))
			{
				return true;
			}
			if (TryTranslateTemplate(text, out result))
			{
				return true;
			}
			if (TryTranslateLabeledPrefix(text, out result))
			{
				return true;
			}
			if (TryTranslateRichTextSuffix(text, out result))
			{
				return true;
			}
			if (TryTranslateActionSuffix(text, out result))
			{
				return true;
			}
			if (TryTranslatePhraseSequence(text, out result))
			{
				return true;
			}
			result = text;
			return false;
		}

		private static bool TryTranslateLookup(string text, out string result)
		{
			if (s_translations.TryGetValue(text, out result))
			{
				return true;
			}
			string text2 = NormalizeKey(text);
			if (text2 != text && s_translations.TryGetValue(text2, out result))
			{
				return true;
			}
			string key = text2.ToUpperInvariant();
			if (s_translationsUpper.TryGetValue(key, out result))
			{
				return true;
			}
			result = text;
			return false;
		}

		private static bool TryTranslateTemplate(string text, out string result)
		{
			string text2 = NormalizeKey(text);
			foreach (TranslationTemplate s_template in s_templates)
			{
				if (s_template.TryTranslate(text2, out result))
				{
					return true;
				}
			}
			result = text;
			return false;
		}

		private static bool TryTranslateLabeledPrefix(string text, out string result)
		{
			Match match = s_labeledPrefixRegex.Match(text);
			if (!match.Success)
			{
				result = text;
				return false;
			}
			string value = match.Groups["leadingTags"].Value;
			string value2 = match.Groups["label"].Value;
			string value3 = match.Groups["separator"].Value;
			string value4 = match.Groups["trailingTags"].Value;
			string value5 = match.Groups["body"].Value;
			if (!TryTranslateCore(value5, out string result2))
			{
				result = text;
				return false;
			}
			string result3;
			string text2 = (TryTranslateLookup(value2, out result3) ? result3 : value2);
			result = value + text2 + value3 + value4 + result2;
			return true;
		}

		private static bool TryTranslateActionSuffix(string text, out string result)
		{
			Match match = s_actionSuffixRegex.Match(text);
			if (!match.Success)
			{
				result = text;
				return false;
			}
			string value = match.Groups["body"].Value;
			string value2 = match.Groups["suffix"].Value;
			if (!TryTranslateLookup(value, out string result2) && !TryTranslateTemplate(value, out result2) && !TryTranslatePhraseSequence(value, out result2))
			{
				result = text;
				return false;
			}
			result = result2 + value2;
			return true;
		}

		private static bool TryTranslateRichTextSuffix(string text, out string result)
		{
			Match match = s_richTextSuffixRegex.Match(text);
			if (!match.Success)
			{
				result = text;
				return false;
			}
			string value = match.Groups["body"].Value;
			string value2 = match.Groups["suffix"].Value;
			if (!TryTranslateLookup(value, out string result2) && !TryTranslateTemplate(value, out result2) && !TryTranslatePhraseSequence(value, out result2))
			{
				result = text;
				return false;
			}
			result = result2 + value2;
			return true;
		}

		private static bool TryTranslateMultilineBlock(string text, out string result)
		{
			if (!text.Contains('\n'))
			{
				result = text;
				return false;
			}
			string[] array = text.Split('\n');
			bool flag = false;
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i];
				if (string.IsNullOrWhiteSpace(text2))
				{
					continue;
				}
				SplitOuterWhitespace(text2, out string leadingWhitespace, out string coreText, out string trailingWhitespace);
				if (coreText.Length != 0 && TryTranslateInlineText(coreText, out string result2))
				{
					string text3 = leadingWhitespace + result2 + trailingWhitespace;
					if (!(text3 == text2))
					{
						array[i] = text3;
						flag = true;
					}
				}
			}
			result = (flag ? string.Join("\n", array) : text);
			return flag;
		}

		private static bool TryTranslatePhraseSequence(string text, out string result)
		{
			result = text;
			if (!CanUsePhraseSegmentation(text))
			{
				return false;
			}
			SplitNumericPrefix(text, out string prefix, out string body);
			if (body.Length == 0)
			{
				return false;
			}
			string[] array = body.Split(' ', StringSplitOptions.RemoveEmptyEntries);
			if (array.Length < 2)
			{
				return false;
			}
			List<string> list = new List<string>();
			int matchedTokenCount;
			for (int i = 0; i < array.Length; i += matchedTokenCount)
			{
				if (!TryMatchLongestPhrase(array, i, out matchedTokenCount, out string translatedFragment))
				{
					return false;
				}
				list.Add(translatedFragment);
			}
			if (list.Count < 2)
			{
				return false;
			}
			result = prefix + string.Join("\n", list);
			return true;
		}

		private static bool TryMatchLongestPhrase(IReadOnlyList<string> tokens, int startIndex, out int matchedTokenCount, out string translatedFragment)
		{
			int val = tokens.Count - startIndex;
			int num = Math.Min(5, val);
			for (int num2 = num; num2 >= 1; num2--)
			{
				string text = string.Join(" ", tokens, startIndex, num2);
				if (TryTranslateLookup(text, out translatedFragment))
				{
					matchedTokenCount = num2;
					return true;
				}
			}
			matchedTokenCount = 0;
			translatedFragment = string.Empty;
			return false;
		}

		private static bool CanUsePhraseSegmentation(string text)
		{
			bool result = false;
			foreach (char c in text)
			{
				if (char.IsLetter(c))
				{
					result = true;
				}
				else if (!char.IsDigit(c) && !char.IsWhiteSpace(c) && c != '\'' && c != '-' && c != '(' && c != ')' && c != '&')
				{
					return false;
				}
			}
			return result;
		}

		private static void SplitNumericPrefix(string text, out string prefix, out string body)
		{
			int i;
			for (i = 0; i < text.Length && char.IsDigit(text[i]); i++)
			{
			}
			if (i == 0 || i >= text.Length || !char.IsWhiteSpace(text[i]))
			{
				prefix = string.Empty;
				body = text;
				return;
			}
			for (; i < text.Length && char.IsWhiteSpace(text[i]); i++)
			{
			}
			prefix = text.Substring(0, i);
			int num = i;
			body = text.Substring(num, text.Length - num);
		}

		private static void SplitOuterWhitespace(string text, out string leadingWhitespace, out string coreText, out string trailingWhitespace)
		{
			int i;
			for (i = 0; i < text.Length && char.IsWhiteSpace(text[i]); i++)
			{
			}
			int num = text.Length - 1;
			while (num >= i && char.IsWhiteSpace(text[num]))
			{
				num--;
			}
			leadingWhitespace = text.Substring(0, i);
			string text2;
			if (i > num)
			{
				text2 = string.Empty;
			}
			else
			{
				int num2 = i;
				text2 = text.Substring(num2, num + 1 - num2);
			}
			coreText = text2;
			string text3;
			if (num + 1 >= text.Length)
			{
				text3 = string.Empty;
			}
			else
			{
				int num2 = num + 1;
				text3 = text.Substring(num2, text.Length - num2);
			}
			trailingWhitespace = text3;
		}

		private static string NormalizeKey(string text)
		{
			return text.Replace("\r\n", "\n").Trim();
		}

		private static string NormalizeTranslationValue(string text)
		{
			return text.TrimStart(' ', '\t', '\u3000');
		}
	}
}