using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace RoundsRussianLocalization;
public static class EmbeddedByteReader
{
public static byte[] ReadAllAndDispose(Stream stream)
{
if (stream == null)
{
throw new ArgumentNullException("stream");
}
using (stream)
{
using MemoryStream memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
}
public sealed class TranslationDictionary
{
private readonly Dictionary<string, string> translations = new Dictionary<string, string>(StringComparer.Ordinal);
public int Count
{
[CompilerGenerated]
get
{
return translations.Count;
}
}
public void Add(string source, string target)
{
if (!string.IsNullOrEmpty(source) && !string.IsNullOrEmpty(target) && TokenValidator.HasSameTokens(source, target))
{
translations[source] = target;
}
}
public void Merge(TranslationDictionary other)
{
if (other == null)
{
return;
}
foreach (KeyValuePair<string, string> translation in other.translations)
{
translations[translation.Key] = translation.Value;
}
}
public string Translate(string source)
{
if (string.IsNullOrEmpty(source))
{
return source;
}
string value;
return (!translations.TryGetValue(source, out value)) ? source : value;
}
public string TranslateTemplate(string source)
{
return Translate(source);
}
public static TranslationDictionary Parse(IEnumerable<string> lines)
{
TranslationDictionary translationDictionary = new TranslationDictionary();
foreach (string item in lines ?? Enumerable.Empty<string>())
{
string text = item.Trim();
if (text.Length != 0 && !text.StartsWith("#", StringComparison.Ordinal))
{
int num = FindUnescapedEquals(text);
if (num > 0 && num != text.Length - 1)
{
translationDictionary.Add(Unescape(text.Substring(0, num)), Unescape(text.Substring(num + 1)));
}
}
}
return translationDictionary;
}
private static int FindUnescapedEquals(string value)
{
bool flag = false;
for (int i = 0; i < value.Length; i++)
{
if (flag)
{
flag = false;
}
else if (value[i] == '\\')
{
flag = true;
}
else if (value[i] == '=')
{
return i;
}
}
return -1;
}
private static string Unescape(string value)
{
StringBuilder stringBuilder = new StringBuilder(value.Length);
bool flag = false;
foreach (char c in value)
{
if (flag)
{
stringBuilder.Append((c != 'n') ? c : '\n');
flag = false;
}
else if (c == '\\')
{
flag = true;
}
else
{
stringBuilder.Append(c);
}
}
if (flag)
{
stringBuilder.Append('\\');
}
return stringBuilder.ToString();
}
}
public static class TokenValidator
{
private static readonly Regex TokenPattern = new Regex("\\{\\d+(?:[^}]*)?\\}|</?[A-Za-z][^>]*>|\\\\n", RegexOptions.Compiled | RegexOptions.CultureInvariant);
public static bool HasSameTokens(string source, string target)
{
return Tokens(source).SequenceEqual<string>(Tokens(target), StringComparer.Ordinal);
}
private static IEnumerable<string> Tokens(string value)
{
return (from Match match in TokenPattern.Matches(value ?? string.Empty)
select NormalizeRichTextToken(match.Value)).OrderBy<string, string>((string token) => token, StringComparer.Ordinal);
}
private static string NormalizeRichTextToken(string token)
{
if (!token.StartsWith("<", StringComparison.Ordinal))
{
return token;
}
Match match = Regex.Match(token, "^<\\s*(/?)\\s*([A-Za-z]+)");
return (!match.Success) ? token : ("<" + match.Groups[1].Value + match.Groups[2].Value.ToLowerInvariant() + ">");
}
}
public static class CardTextSanitizer
{
private static readonly Regex AbsoluteSizeTag = new Regex("</?size(?:=[^>]+)?>", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static string Normalize(string value)
{
return (!string.IsNullOrEmpty(value)) ? AbsoluteSizeTag.Replace(value, string.Empty) : value;
}
}
public enum TextRole
{
Title,
Description,
Stat,
Other
}
public enum OverflowPolicy
{
Truncate
}
public struct TextLayout
{
public bool AutoSize;
public bool WordWrapping;
public float MinFontSize;
public float MaxFontSize;
public int MaxVisibleLines;
public OverflowPolicy Overflow;
}
public struct CardRegion
{
public float Bottom;
public float Top;
public OverflowPolicy Overflow;
}
public static class CardLayoutPlanner
{
public static CardRegion Plan(TextRole role, float cardHeight)
{
float num = Math.Max(1f, cardHeight);
return role switch
{
TextRole.Title => new CardRegion
{
Bottom = num * 0.8f,
Top = num * 0.98f,
Overflow = OverflowPolicy.Truncate
},
TextRole.Description => new CardRegion
{
Bottom = num * 0.36f,
Top = num * 0.77f,
Overflow = OverflowPolicy.Truncate
},
TextRole.Stat => new CardRegion
{
Bottom = num * 0.03f,
Top = num * 0.33f,
Overflow = OverflowPolicy.Truncate
},
_ => new CardRegion
{
Bottom = num * 0.03f,
Top = num * 0.98f,
Overflow = OverflowPolicy.Truncate
},
};
}
}
public static class TextLayoutPolicy
{
public static TextLayout For(TextRole role, int textLength, float prefabFontSize)
{
float num = role switch
{
TextRole.Title => 34f,
TextRole.Description => 22f,
_ => 20f,
};
float num2 = ((!(prefabFontSize > 0f)) ? num : prefabFontSize);
float num3;
int maxVisibleLines;
switch (role)
{
case TextRole.Title:
num3 = ((textLength > 30) ? 0.55f : ((textLength <= 20) ? 0.7f : 0.62f));
maxVisibleLines = 3;
break;
case TextRole.Description:
num3 = ((textLength > 140) ? 0.5f : ((textLength <= 100) ? 0.62f : 0.55f));
maxVisibleLines = 7;
break;
case TextRole.Stat:
num3 = ((textLength <= 35) ? 0.65f : 0.55f);
maxVisibleLines = 2;
break;
default:
num3 = 0.6f;
maxVisibleLines = 5;
break;
}
return new TextLayout
{
AutoSize = true,
WordWrapping = true,
MinFontSize = Math.Max(1f, num2 * num3),
MaxFontSize = num2,
MaxVisibleLines = maxVisibleLines,
Overflow = OverflowPolicy.Truncate
};
}
}
internal static class EmbeddedResources
{
private const string TranslationsName = "RoundsRussianLocalization.Resources.ru.txt";
private const string FontBundleName = "RoundsRussianLocalization.Resources.arialuni_sdf_u2018";
private const string FontAssetName = "ARIALUNI SDF";
internal static Stream OpenTranslations()
{
return Open("RoundsRussianLocalization.Resources.ru.txt");
}
internal static AssetBundle LoadFontBundle()
{
byte[] array = EmbeddedByteReader.ReadAllAndDispose(Open("RoundsRussianLocalization.Resources.arialuni_sdf_u2018"));
return AssetBundle.LoadFromMemory(array);
}
internal static TMP_FontAsset LoadFont(AssetBundle bundle)
{
return (!((Object)(object)bundle == (Object)null)) ? bundle.LoadAsset<TMP_FontAsset>("ARIALUNI SDF") : null;
}
private static Stream Open(string name)
{
Stream manifestResourceStream = typeof(EmbeddedResources).Assembly.GetManifestResourceStream(name);
if (manifestResourceStream == null)
{
throw new InvalidOperationException("Missing embedded resource: " + name);
}
return manifestResourceStream;
}
}
[BepInPlugin("thehell.rounds.russianlocalization", "ROUNDS Russian Localization", "2.1.2")]
public sealed class LocalizationPlugin : BaseUnityPlugin
{
public const string PluginId = "thehell.rounds.russianlocalization";
public const string PluginName = "ROUNDS Russian Localization";
public const string PluginVersion = "2.1.2";
private Harmony harmony;
private AssetBundle fontBundle;
private float nextUiScan;
internal static TranslationDictionary Dictionary { get; private set; }
internal static TMP_FontAsset RussianFont { get; private set; }
internal static ManualLogSource Log { get; private set; }
private void Awake()
{
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
Dictionary = LoadTranslations();
fontBundle = EmbeddedResources.LoadFontBundle();
RussianFont = EmbeddedResources.LoadFont(fontBundle);
if ((Object)(object)RussianFont == (Object)null)
{
throw new InvalidOperationException("Embedded Cyrillic TMP font was not found.");
}
harmony = new Harmony("thehell.rounds.russianlocalization");
harmony.PatchAll(typeof(LocalizationPlugin).Assembly);
((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded {Dictionary.Count} embedded Russian translations and Cyrillic font.");
}
private void OnDestroy()
{
if (harmony != null)
{
harmony.UnpatchSelf();
}
if ((Object)(object)fontBundle != (Object)null)
{
fontBundle.Unload(false);
}
}
private void Update()
{
//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)
if (Time.unscaledTime < nextUiScan)
{
return;
}
nextUiScan = Time.unscaledTime + 0.25f;
TMP_Text[] array = Resources.FindObjectsOfTypeAll<TMP_Text>();
foreach (TMP_Text val in array)
{
if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null)
{
continue;
}
Scene scene = ((Component)val).gameObject.scene;
if (((Scene)(ref scene)).IsValid())
{
string text = val.text;
string text2 = Dictionary.TranslateTemplate(text);
if (!(text2 == text))
{
val.font = RussianFont;
val.text = text2;
}
}
}
}
private static TranslationDictionary LoadTranslations()
{
return ParseResource(EmbeddedResources.OpenTranslations());
}
private static TranslationDictionary ParseResource(Stream stream)
{
using (stream)
{
using StreamReader streamReader = new StreamReader(stream);
List<string> list = new List<string>();
string item;
while ((item = streamReader.ReadLine()) != null)
{
list.Add(item);
}
return TranslationDictionary.Parse(list);
}
}
}
[HarmonyPatch(typeof(CardInfoDisplayer), "DrawCard")]
internal static class DrawCardPatch
{
private static bool firstSuccessfulDrawLogged;
private static void Prefix(ref CardInfoStat[] stats, ref string cardName, ref string description)
{
TranslationDictionary dictionary = LocalizationPlugin.Dictionary;
if (dictionary == null)
{
return;
}
cardName = CardTextSanitizer.Normalize(dictionary.Translate(cardName));
description = CardTextSanitizer.Normalize(dictionary.Translate(description));
if (stats == null)
{
return;
}
CardInfoStat[] array = stats;
foreach (CardInfoStat val in array)
{
if (val != null)
{
val.amount = CardTextSanitizer.Normalize(dictionary.Translate(val.amount));
val.stat = CardTextSanitizer.Normalize(dictionary.Translate(val.stat));
}
}
}
private static void Postfix(CardInfoDisplayer __instance)
{
CardTextFitter.Fit(__instance);
if (!firstSuccessfulDrawLogged)
{
firstSuccessfulDrawLogged = true;
ManualLogSource log = LocalizationPlugin.Log;
if (log != null)
{
log.LogInfo((object)"Localized and fitted the first rendered card successfully.");
}
}
}
}
internal static class CardTextFitter
{
public static void Fit(CardInfoDisplayer displayer)
{
if ((Object)(object)displayer == (Object)null)
{
return;
}
if ((Object)(object)displayer.nameText != (Object)null)
{
Apply((TMP_Text)(object)displayer.nameText, TextRole.Title);
}
if ((Object)(object)displayer.effectText != (Object)null)
{
Apply((TMP_Text)(object)displayer.effectText, TextRole.Description);
}
if ((Object)(object)displayer.grid == (Object)null)
{
return;
}
TMP_Text[] componentsInChildren = displayer.grid.GetComponentsInChildren<TMP_Text>(true);
foreach (TMP_Text val in componentsInChildren)
{
if ((Object)(object)val != (Object)null)
{
Apply(val, TextRole.Stat);
}
}
}
private static void Apply(TMP_Text text, TextRole role)
{
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00af: Unknown result type (might be due to invalid IL or missing references)
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00df: Unknown result type (might be due to invalid IL or missing references)
//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)LocalizationPlugin.RussianFont != (Object)null)
{
text.font = LocalizationPlugin.RussianFont;
}
TextLayout textLayout = TextLayoutPolicy.For(role, (text.text ?? string.Empty).Length, text.fontSize);
text.enableAutoSizing = textLayout.AutoSize;
text.enableWordWrapping = textLayout.WordWrapping;
text.fontSizeMin = textLayout.MinFontSize;
text.fontSizeMax = textLayout.MaxFontSize;
text.maxVisibleLines = textLayout.MaxVisibleLines;
text.overflowMode = (TextOverflowModes)1;
text.enableKerning = true;
text.extraPadding = true;
text.margin = new Vector4(Math.Max(text.margin.x, 2f), Math.Max(text.margin.y, 2f), Math.Max(text.margin.z, 2f), Math.Max(text.margin.w, 2f));
text.ForceMeshUpdate(true);
}
}