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 BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Localization.Settings; using UnityEngine.Localization.Tables; using UnityEngine.TextCore; [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("SweetRussianTranslate")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("SweetRussianTranslate")] [assembly: AssemblyTitle("SweetRussianTranslate")] [assembly: AssemblyVersion("0.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SweetRussianTranslate { [BepInPlugin("sweet.russiantranslate", "Sweet Russian Translate", "0.1.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "sweet.russiantranslate"; internal static ManualLogSource Log; internal static string ModFolder; private void Awake() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; ModFolder = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); new Harmony("sweet.russiantranslate").PatchAll(Assembly.GetExecutingAssembly()); Log.LogInfo((object)("Sweet Russian Translate loaded from " + ModFolder)); } } [HarmonyPatch(typeof(SemiUI), "Start")] internal static class HardcodedLabelPatch { private static Dictionary labels; [HarmonyPostfix] private static void Postfix(SemiUI __instance) { if (labels == null) { labels = LoadLabels(); } if (labels.Count == 0) { return; } TMP_Text[] componentsInChildren = ((Component)__instance).GetComponentsInChildren(true); foreach (TMP_Text val in componentsInChildren) { if (val.text != null && labels.TryGetValue(val.text, out var value)) { val.text = value; } } } private static Dictionary LoadLabels() { Dictionary dictionary = new Dictionary(); string path = Path.Combine(Plugin.ModFolder, "Hardcoded.tsv"); if (!File.Exists(path)) { return dictionary; } try { string[] array = File.ReadAllLines(path, Encoding.UTF8); foreach (string text in array) { if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("#")) { string[] array2 = text.Split('\t'); if (array2.Length == 2 && !string.IsNullOrWhiteSpace(array2[1])) { dictionary[array2[0]] = array2[1].Replace("\\n", "\n"); } } } Plugin.Log.LogInfo((object)("Loaded " + dictionary.Count + " hardcoded label replacements.")); } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to read Hardcoded.tsv: " + ex.Message)); } return dictionary; } } [HarmonyPatch(typeof(LocalizationManager), "Awake")] internal static class LocalizationOverridePatch { private static readonly string[] TableNames = new string[3] { "HUD", "Menu", "Game" }; private static bool fontLoaded; [HarmonyPostfix] private static void Postfix() { LoadCyrillicFallbackFont(); string[] tableNames = TableNames; foreach (string text in tableNames) { try { ApplyTable(text); } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to apply " + text + ".tsv: " + ex.Message)); } } } private static void ApplyTable(string tableName) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) string text = Path.Combine(Plugin.ModFolder, tableName + ".tsv"); if (!File.Exists(text)) { Plugin.Log.LogWarning((object)("Missing translation file: " + text)); return; } StringTable table = ((LocalizedDatabase)(object)LocalizationSettings.StringDatabase).GetTable(TableReference.op_Implicit(tableName), LocalizationSettings.ProjectLocale); if ((Object)(object)table == (Object)null) { Plugin.Log.LogError((object)("String table not found: " + tableName)); return; } int num = 0; string[] array = File.ReadAllLines(text, Encoding.UTF8); foreach (string text2 in array) { if (string.IsNullOrWhiteSpace(text2) || text2.StartsWith("#")) { continue; } string[] array2 = text2.Split('\t'); if (array2.Length == 2 && !string.IsNullOrWhiteSpace(array2[1])) { string text3 = array2[1].Replace("\\n", "\n"); StringTableEntry entry = ((DetailedLocalizationTable)(object)table).GetEntry(array2[0]); if (entry != null) { entry.Value = text3; } else { ((DetailedLocalizationTable)(object)table).AddEntry(array2[0], text3); } num++; } } Plugin.Log.LogInfo((object)("Applied " + num + " strings to table " + tableName + ".")); } private static void LoadCyrillicFallbackFont() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown if (fontLoaded) { return; } fontLoaded = true; string text = Path.Combine(Plugin.ModFolder, "Teko-Cyrillic.ttf"); if (!File.Exists(text)) { Plugin.Log.LogError((object)("Font not found: " + text)); return; } try { TMP_FontAsset val = FindGameTekoAsset(); TMP_FontAsset obj; if (!((Object)(object)val != (Object)null)) { obj = TMP_FontAsset.CreateFontAsset(new Font(text)); } else { Font val2 = new Font(text); FaceInfo faceInfo = val.faceInfo; obj = TMP_FontAsset.CreateFontAsset(val2, ((FaceInfo)(ref faceInfo)).pointSize, val.atlasPadding, val.atlasRenderMode, val.atlasWidth, val.atlasHeight, (AtlasPopulationMode)1, true); } TMP_FontAsset val3 = obj; ((Object)val3).name = "Teko-Cyrillic SDF"; ((Object)val3).hideFlags = (HideFlags)32; if ((Object)(object)val != (Object)null) { CopyVerticalMetrics(val, val3); } TMP_Settings.fallbackFontAssets?.Insert(0, val3); TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll(); foreach (TMP_FontAsset val4 in array) { if (!((Object)(object)val4 == (Object)(object)val3) && ((Object)val4).name.Contains("Teko")) { if (val4.fallbackFontAssetTable == null) { val4.fallbackFontAssetTable = new List(); } val4.fallbackFontAssetTable.Insert(0, val3); } } Plugin.Log.LogInfo((object)("Cyrillic fallback font registered" + (((Object)(object)val != (Object)null) ? (" (matched to " + ((Object)val).name + ").") : " (no Teko reference found)."))); } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to load Cyrillic font: " + ex)); } } private static void CopyVerticalMetrics(TMP_FontAsset from, TMP_FontAsset to) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: 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_00ef: Unknown result type (might be due to invalid IL or missing references) FaceInfo faceInfo = from.faceInfo; FaceInfo faceInfo2 = to.faceInfo; ((FaceInfo)(ref faceInfo2)).scale = ((FaceInfo)(ref faceInfo)).scale; ((FaceInfo)(ref faceInfo2)).lineHeight = ((FaceInfo)(ref faceInfo)).lineHeight; ((FaceInfo)(ref faceInfo2)).ascentLine = ((FaceInfo)(ref faceInfo)).ascentLine; ((FaceInfo)(ref faceInfo2)).capLine = ((FaceInfo)(ref faceInfo)).capLine; ((FaceInfo)(ref faceInfo2)).meanLine = ((FaceInfo)(ref faceInfo)).meanLine; ((FaceInfo)(ref faceInfo2)).baseline = ((FaceInfo)(ref faceInfo)).baseline; ((FaceInfo)(ref faceInfo2)).descentLine = ((FaceInfo)(ref faceInfo)).descentLine; ((FaceInfo)(ref faceInfo2)).superscriptOffset = ((FaceInfo)(ref faceInfo)).superscriptOffset; ((FaceInfo)(ref faceInfo2)).superscriptSize = ((FaceInfo)(ref faceInfo)).superscriptSize; ((FaceInfo)(ref faceInfo2)).subscriptOffset = ((FaceInfo)(ref faceInfo)).subscriptOffset; ((FaceInfo)(ref faceInfo2)).subscriptSize = ((FaceInfo)(ref faceInfo)).subscriptSize; ((FaceInfo)(ref faceInfo2)).underlineOffset = ((FaceInfo)(ref faceInfo)).underlineOffset; ((FaceInfo)(ref faceInfo2)).underlineThickness = ((FaceInfo)(ref faceInfo)).underlineThickness; ((FaceInfo)(ref faceInfo2)).strikethroughOffset = ((FaceInfo)(ref faceInfo)).strikethroughOffset; ((FaceInfo)(ref faceInfo2)).strikethroughThickness = ((FaceInfo)(ref faceInfo)).strikethroughThickness; ((FaceInfo)(ref faceInfo2)).tabWidth = ((FaceInfo)(ref faceInfo)).tabWidth; to.faceInfo = faceInfo2; Plugin.Log.LogInfo((object)$"Metrics aligned to {((Object)from).name}: pointSize {((FaceInfo)(ref faceInfo)).pointSize}, lineHeight {((FaceInfo)(ref faceInfo)).lineHeight}, baseline {((FaceInfo)(ref faceInfo)).baseline}, ascent {((FaceInfo)(ref faceInfo)).ascentLine}, descent {((FaceInfo)(ref faceInfo)).descentLine}."); } private static TMP_FontAsset FindGameTekoAsset() { TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll(); foreach (TMP_FontAsset val in array) { if (((Object)val).name.Contains("Teko")) { return val; } } return null; } } }