Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of Sweet Russian ScheduleI v0.1.0
Mods/Sweet_Russian_ScheduleI.dll
Decompiled 2 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem.Collections.Generic; using Il2CppTMPro; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using SweetRu; using UnityEngine; using UnityEngine.TextCore.LowLevel; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Main), "Sweet_Russian_ScheduleI", "0.1.0", "Sweet_team", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("Sweet_Russian_ScheduleI")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+3940cfe718a226dfe3ec614f878b707838c66e46")] [assembly: AssemblyProduct("Sweet_Russian_ScheduleI")] [assembly: AssemblyTitle("Sweet_Russian_ScheduleI")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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 SweetRu { internal static class Fonts { private static readonly HashSet<int> Done = new HashSet<int>(); private static readonly Dictionary<string, TMP_FontAsset> Made = new Dictionary<string, TMP_FontAsset>(); private static readonly (string game, string fallback)[] Map = new(string, string)[9] { ("ComicNeue", "file:Pangolin-Regular.ttf"), ("Handlee", "file:Pangolin-Regular.ttf"), ("Caveat", "file:Caveat-Regular.ttf"), ("VPPixel", "file:SweetPixel.ttf"), ("OpenSans-Bold", "OpenSans-Bold"), ("OpenSans-SemiBold", "OpenSans-SemiBold"), ("OpenSans-Medium", "OpenSans-Medium"), ("OpenSans-Light", "OpenSans-Light"), ("", "OpenSans-SemiBold") }; public static void Fix() { foreach (TMP_FontAsset fa in Resources.FindObjectsOfTypeAll<TMP_FontAsset>()) { if ((Object)(object)fa == (Object)null || !Done.Add(((Object)fa).GetInstanceID()) || ((Object)fa).name.StartsWith("SweetRu_")) { continue; } try { bool flag = Array.Exists(Map, ((string game, string fallback) e) => e.game.Length > 0 && !e.game.StartsWith("OpenSans") && ((Object)fa).name.StartsWith(e.game, StringComparison.OrdinalIgnoreCase)); bool flag2; try { flag2 = fa.HasCharacter('Ж', !flag, true); } catch { try { flag2 = fa.HasCharacter('Ж', !flag, false); } catch { flag2 = false; } } if (flag2) { continue; } TMP_FontAsset val = Fallback(((Object)fa).name); if ((Object)(object)val == (Object)null) { Main.Log.Warning("Нет запасного шрифта для " + ((Object)fa).name); continue; } if (fa.fallbackFontAssetTable == null) { fa.fallbackFontAssetTable = new List<TMP_FontAsset>(); } fa.fallbackFontAssetTable.Insert(0, val); Main.Log.Msg("Кириллица для " + ((Object)fa).name + ": " + ((Object)val).name); } catch (Exception ex) { Main.Log.Warning("Шрифт " + ((Object)fa).name + ": " + ex.Message); } } } private static TMP_FontAsset Fallback(string gameFont) { (string, string)[] map = Map; for (int i = 0; i < map.Length; i++) { var (value, src) = map[i]; if (gameFont.StartsWith(value, StringComparison.OrdinalIgnoreCase)) { TMP_FontAsset val = Make(src); if ((Object)(object)val != (Object)null) { return val; } } } return Make("OpenSans-SemiBold") ?? Make("LiberationSans"); } private static TMP_FontAsset Make(string src) { if (Made.TryGetValue(src, out var value)) { return value; } TMP_FontAsset val = null; if (src.StartsWith("file:")) { string text = Extract(src.Substring(5)); if (text != null) { try { val = TMP_FontAsset.CreateFontAsset(text, 0, 90, 9, (GlyphRenderMode)4165, 1024, 1024, (AtlasPopulationMode)1, true); if (val != null) { val.TryAddCharacters("АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя", false); } } catch (Exception ex) { Main.Log.Warning("Шрифт " + src + " не создался: " + ex.Message); } } } else if (src.StartsWith("pixel:")) { string text2 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), src.Substring(6)); try { if (File.Exists(text2)) { val = TMP_FontAsset.CreateFontAsset(text2, 0, 12, 1, (GlyphRenderMode)4122, 256, 256, (AtlasPopulationMode)1, true); } } catch (Exception ex2) { Main.Log.Warning("Пиксельный шрифт из файла не создался: " + ex2.Message); } if ((Object)(object)val == (Object)null) { try { Font val2 = Font.CreateDynamicFontFromOSFont(Il2CppStringArray.op_Implicit(new string[1] { "Tahoma" }), 12); if ((Object)(object)val2 != (Object)null) { val = TMP_FontAsset.CreateFontAsset(val2, 12, 1, (GlyphRenderMode)4122, 256, 256, (AtlasPopulationMode)1, true); } } catch (Exception ex3) { Main.Log.Warning("Пиксельный шрифт из Windows не создался: " + ex3.Message); } } try { Il2CppReferenceArray<Texture2D> val3 = ((val != null) ? val.atlasTextures : null); if (val3 != null) { foreach (Texture2D item in (Il2CppArrayBase<Texture2D>)(object)val3) { if ((Object)(object)item != (Object)null) { ((Texture)item).filterMode = (FilterMode)0; } } } if ((Object)(object)val != (Object)null && (Object)(object)val.atlasTexture != (Object)null) { ((Texture)val.atlasTexture).filterMode = (FilterMode)0; } } catch { } } else { foreach (Font item2 in Resources.FindObjectsOfTypeAll<Font>()) { if (((Object)item2).name == src) { val = TMP_FontAsset.CreateFontAsset(item2); break; } } } if ((Object)(object)val != (Object)null) { ((Object)val).name = "SweetRu_" + src.Replace("file:", "").Replace("pixel:", ""); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)61; Done.Add(((Object)val).GetInstanceID()); } Made[src] = val; return val; } private static string Extract(string file) { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("font." + file); if (stream == null) { return null; } string text = Path.Combine(Main.Dir, "fonts", file); Directory.CreateDirectory(Path.GetDirectoryName(text)); if (!File.Exists(text) || new FileInfo(text).Length != stream.Length) { using FileStream destination = File.Create(text); stream.CopyTo(destination); } return text; } } public class Main : MelonMod { public const string Version = "0.1.0"; internal static Instance Log; internal static string Dir; private float nextFontCheck; private float nextTexCheck = 30f; public override void OnInitializeMelon() { Log = ((MelonBase)this).LoggerInstance; Dir = Path.Combine(MelonEnvironment.UserDataDirectory, "Sweet_Russian_ScheduleI"); Directory.CreateDirectory(Dir); MelonPreferences_Category obj = MelonPreferences.CreateCategory("Sweet_Russian_ScheduleI"); MelonPreferences_Entry<bool> val = obj.CreateEntry<bool>("LogUntranslated", false, (string)null, "Записывать непереведённые строки в UserData/Sweet_Russian_ScheduleI/untranslated.txt", false, false, (ValueValidator)null, (string)null); MelonPreferences_Entry<bool> obj2 = obj.CreateEntry<bool>("TranslateTextures", true, (string)null, "Подменять картинки с надписями на русские (читается при запуске игры)", false, false, (ValueValidator)null, (string)null); Tr.LogMisses = val.Value; Tr.Load(Dir); Textures.Enabled = obj2.Value; Textures.Load(); ImgPatches.Install(((MelonBase)this).HarmonyInstance); Log.Msg($"Перевод загружен: {Tr.ExactCount} строк, {Tr.TemplateCount} шаблонов, {Textures.Count} картинок. Версия {"0.1.0"}"); } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { Fonts.Fix(); Patches.RetranslateAll(); MelonCoroutines.Start(Textures.AfterScene()); } public override void OnUpdate() { if (!(Time.unscaledTime < nextFontCheck)) { nextFontCheck = Time.unscaledTime + 5f; Fonts.Fix(); if (Time.unscaledTime >= nextTexCheck) { nextTexCheck = Time.unscaledTime + 15f; Textures.Apply(); } } } } [HarmonyPatch] internal static class Patches { private static readonly Dictionary<int, float> BaseSize = new Dictionary<int, float>(); [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPrefix] private static void TmpText(TMP_Text __instance, ref string value) { string text = value; value = Tr.Get(value); if ((object)text != value) { Fit(__instance, text, value); } } internal static void Fit(TMP_Text t, string en, string ru) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: 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) try { if ((Object)(object)t == (Object)null || en == null || ru == null || t.enableAutoSizing) { return; } bool flag = (Object)(object)((Il2CppObjectBase)t).TryCast<TextMeshPro>() != (Object)null; if (!flag && (t.enableWordWrapping || ru.IndexOf('\n') >= 0)) { return; } int instanceID = ((Object)t).GetInstanceID(); if (!BaseSize.TryGetValue(instanceID, out var value)) { value = (BaseSize[instanceID] = t.fontSize); } t.fontSize = value; float x = t.GetPreferredValues(ru).x; if (flag) { float x2 = t.GetPreferredValues(en).x; if (x2 > 0f && x > x2 * 1.02f) { t.fontSize = value * x2 / x; } return; } Rect rect = t.rectTransform.rect; float width = ((Rect)(ref rect)).width; float num = Math.Max(width, t.GetPreferredValues(en).x); if (width > 1f && x > num * 1.02f) { t.fontSize = Math.Max(value * 0.6f, value * num / x); } } catch { } } [HarmonyPatch(typeof(TMP_Text), "SetText", new Type[] { typeof(string), typeof(bool) })] [HarmonyPrefix] private static void TmpSetText(ref string sourceText) { sourceText = Tr.Get(sourceText); } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPrefix] private static void UiText(ref string value) { value = Tr.Get(value); } [HarmonyPatch(typeof(TextMeshProUGUI), "OnEnable")] [HarmonyPrefix] private static void UguiEnable(TextMeshProUGUI __instance) { Fix((TMP_Text)(object)__instance); } [HarmonyPatch(typeof(TextMeshPro), "OnEnable")] [HarmonyPrefix] private static void TmpEnable(TextMeshPro __instance) { Fix((TMP_Text)(object)__instance); } private static void Fix(TMP_Text t) { try { string text = t.m_text; string text2 = Tr.Get(text); if ((object)text2 != text && text2 != text) { t.text = text2; Fit(t, text, text2); } } catch { } } internal static void RetranslateAll() { int num = 0; foreach (TMP_Text item in Resources.FindObjectsOfTypeAll<TMP_Text>()) { string text = item.m_text; string text2 = Tr.Get(text); if (text2 != text) { item.text = text2; Fit(item, text, text2); num++; } } foreach (Text item2 in Resources.FindObjectsOfTypeAll<Text>()) { string text3 = item2.m_Text; string text4 = Tr.Get(text3); if (text4 != text3) { item2.text = text4; num++; } } Main.Log.Msg($"Надписей в сцене переведено: {num}"); } } internal static class ImgPatches { private static void ImgEnable(Image __instance) { try { Sprite sprite = __instance.sprite; if ((Object)(object)sprite != (Object)null) { Textures.ApplyOne(sprite.texture); } } catch { } } private static void ImgSprite(Image __instance) { try { Sprite sprite = __instance.sprite; if ((Object)(object)sprite != (Object)null) { Textures.ApplyOne(sprite.texture); } } catch { } } private static void RawEnable(RawImage __instance) { try { Texture texture = __instance.texture; Textures.ApplyOne((texture != null) ? ((Il2CppObjectBase)texture).TryCast<Texture2D>() : null); } catch { } } private static void Rollout(ref string text) { try { text = Tr.Get(text); } catch { } } internal static void Install(Harmony h) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown P(AccessTools.Method(typeof(Image), "OnEnable", (Type[])null, (Type[])null), "ImgEnable"); P(AccessTools.PropertySetter(typeof(Image), "sprite"), "ImgSprite"); P(AccessTools.Method(typeof(RawImage), "OnEnable", (Type[])null, (Type[])null), "RawEnable"); Type type = AccessTools.TypeByName("Il2CppScheduleOne.UI.DialogueCanvas"); if (type != null) { try { h.Patch((MethodBase)AccessTools.Method(type, "RolloutDialogue", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ImgPatches), "Rollout", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return; } catch (Exception ex) { Main.Log.Warning("Перехват диалогов не встал: " + ex.Message); return; } } Main.Log.Warning("Не нашёл DialogueCanvas — диалоги могут обрезаться"); void P(MethodBase m, string post) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown try { h.Patch(m, (HarmonyMethod)null, new HarmonyMethod(typeof(ImgPatches), post, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex2) { Main.Log.Warning("Перехват картинок " + post + " не встал: " + ex2.Message); } } } } internal static class Textures { private static readonly Dictionary<string, List<(int w, int h, string res)>> ByName = new Dictionary<string, List<(int, int, string)>>(StringComparer.Ordinal); private static readonly HashSet<int> Done = new HashSet<int>(); private static readonly Dictionary<string, int> Sig = new Dictionary<string, int>(); public static bool Enabled = true; private const float MaxColorDist = 40f; public static int Count; private static readonly Dictionary<int, Texture2D> Swaps = new Dictionary<int, Texture2D>(); private static readonly HashSet<int> Seen = new HashSet<int>(); private static string ByColor(Texture2D t, List<(int w, int h, string res)> list, out float bd) { //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_0081: 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_00b6: Expected O, but got Unknown //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) RenderTexture active = RenderTexture.active; FilterMode filterMode = ((Texture)t).filterMode; ((Texture)t).filterMode = (FilterMode)1; Texture val = (Texture)(object)t; RenderTexture val2 = null; int num = ((Texture)t).width; int num2 = ((Texture)t).height; while (num > 64 || num2 > 64) { num = Math.Max(1, num / 2); num2 = Math.Max(1, num2 / 2); RenderTexture temporary = RenderTexture.GetTemporary(num, num2, 0, (RenderTextureFormat)0); ((Texture)temporary).filterMode = (FilterMode)1; Graphics.Blit(val, temporary); if ((Object)(object)val2 != (Object)null) { RenderTexture.ReleaseTemporary(val2); } val2 = temporary; val = (Texture)(object)temporary; } ((Texture)t).filterMode = filterMode; if ((Object)(object)val2 == (Object)null) { val2 = RenderTexture.GetTemporary(num, num2, 0, (RenderTextureFormat)0); Graphics.Blit((Texture)(object)t, val2); } RenderTexture.active = val2; Texture2D val3 = new Texture2D(num, num2, (TextureFormat)4, false); val3.ReadPixels(new Rect(0f, 0f, (float)num, (float)num2), 0, 0); RenderTexture.active = active; RenderTexture.ReleaseTemporary(val2); float num3 = 0f; float num4 = 0f; float num5 = 0f; float num6 = 0f; Il2CppStructArray<Color> pixels = val3.GetPixels(); foreach (Color item in (Il2CppArrayBase<Color>)(object)pixels) { num3 += Mathf.GammaToLinearSpace(item.r) * item.a; num4 += Mathf.GammaToLinearSpace(item.g) * item.a; num5 += Mathf.GammaToLinearSpace(item.b) * item.a; num6 += item.a; } if (num6 < 0.5f) { num3 = (num4 = (num5 = 0f)); num6 = ((Il2CppArrayBase<Color>)(object)pixels).Length; foreach (Color item2 in (Il2CppArrayBase<Color>)(object)pixels) { num3 += Mathf.GammaToLinearSpace(item2.r); num4 += Mathf.GammaToLinearSpace(item2.g); num5 += Mathf.GammaToLinearSpace(item2.b); } } Object.Destroy((Object)(object)val3); num3 = Mathf.LinearToGammaSpace(num3 / num6) * 255f; num4 = Mathf.LinearToGammaSpace(num4 / num6) * 255f; num5 = Mathf.LinearToGammaSpace(num5 / num6) * 255f; string result = null; bd = float.MaxValue; foreach (var item3 in list) { if (item3.w == ((Texture)t).width && item3.h == ((Texture)t).height && Sig.TryGetValue(item3.res, out var value)) { float num7 = Math.Abs((float)((value >> 16) & 0xFF) - num3) + Math.Abs((float)((value >> 8) & 0xFF) - num4) + Math.Abs((float)(value & 0xFF) - num5); if (num7 < bd) { bd = num7; result = item3.res; } } } return result; } public static void Load() { string[] manifestResourceNames = Assembly.GetExecutingAssembly().GetManifestResourceNames(); foreach (string text in manifestResourceNames) { if (!text.StartsWith("tex.") || !text.EndsWith(".png")) { continue; } string text2 = text.Substring(4, text.Length - 8); string text3 = null; Match match = Regex.Match(text2, "__s([0-9A-Fa-f]{6})$"); if (match.Success) { text3 = match.Groups[1].Value; text2 = text2.Substring(0, match.Index); } int num = text2.LastIndexOf("__", StringComparison.Ordinal); if (num < 0) { continue; } string[] array = text2.Substring(num + 2).Split('x'); if (array.Length == 2 && int.TryParse(array[0], out var result) && int.TryParse(array[1], out var result2)) { string key = text2.Substring(0, num); if (!ByName.TryGetValue(key, out List<(int, int, string)> value)) { value = (ByName[key] = new List<(int, int, string)>()); } value.Add((result, result2, text)); if (text3 != null) { Sig[text] = Convert.ToInt32(text3, 16); } Count++; } } } public static IEnumerator AfterScene() { Apply(); yield return (object)new WaitForSeconds(3f); Apply(); yield return (object)new WaitForSeconds(7f); Apply(); } public static void Apply() { if (!Enabled || Count == 0) { return; } int num = 0; foreach (Texture2D item in Resources.FindObjectsOfTypeAll<Texture2D>()) { if (ApplyOne(item)) { num++; } } if (num > 0) { Main.Log.Msg($"Картинок заменено: {num}"); } if (Swaps.Count > 0) { SwapMaterials(); } } private static void SwapMaterials() { foreach (Material item in Resources.FindObjectsOfTypeAll<Material>()) { try { foreach (int item2 in (Il2CppArrayBase<int>)(object)item.GetTexturePropertyNameIDs()) { Texture texture = item.GetTexture(item2); if ((Object)(object)texture != (Object)null && Swaps.TryGetValue(((Object)texture).GetInstanceID(), out var value)) { item.SetTexture(item2, (Texture)(object)value); } } } catch { } } } public static bool ApplyOne(Texture2D t) { //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Expected O, but got Unknown //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) if (!Enabled || Count == 0 || (Object)(object)t == (Object)null) { return false; } int instanceID = ((Object)t).GetInstanceID(); if (Done.Contains(instanceID) || Seen.Contains(instanceID)) { return false; } if (!ByName.TryGetValue(((Object)t).name, out List<(int, int, string)> value)) { Seen.Add(instanceID); return false; } bool flag = false; foreach (var item in value) { if (item.Item1 == ((Texture)t).width && item.Item2 == ((Texture)t).height) { flag = true; } } if (!flag) { Seen.Add(instanceID); return false; } float bd; string text = ByColor(t, value, out bd); if (text == null || bd > 40f) { Done.Add(instanceID); return false; } try { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(text); byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); Done.Add(instanceID); bool flag2 = false; try { flag2 = t.streamingMipmaps; } catch { } if (!flag2 && ImageConversion.LoadImage(t, new Il2CppStructArray<byte>(array), true)) { return true; } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true); Done.Add(((Object)val).GetInstanceID()); if (!ImageConversion.LoadImage(val, new Il2CppStructArray<byte>(array), true)) { return false; } ((Object)val).name = ((Object)t).name; ((Texture)val).filterMode = ((Texture)t).filterMode; ((Texture)val).wrapMode = ((Texture)t).wrapMode; ((Texture)val).anisoLevel = ((Texture)t).anisoLevel; Object.DontDestroyOnLoad((Object)(object)val); Swaps[instanceID] = val; SwapMaterials(); return true; } catch (Exception ex) { Main.Log.Warning("Картинка " + ((Object)t).name + ": " + ex.Message); Done.Add(instanceID); return false; } } } internal static class Tr { private static readonly Dictionary<string, string> Exact = new Dictionary<string, string>(StringComparer.Ordinal); private static readonly List<(Regex re, string ru, string[] slots)> Templates = new List<(Regex, string, string[])>(); private static readonly Dictionary<string, string> Cache = new Dictionary<string, string>(StringComparer.Ordinal); private static readonly HashSet<string> Missed = new HashSet<string>(StringComparer.Ordinal); private static readonly Regex Slot = new Regex("\\{\\d\\}|\\{[A-Za-z]+\\}|<[A-Z][A-Z0-9_]+>|<Input_[A-Za-z0-9_]+>", RegexOptions.Compiled); private static readonly Regex OpenTag = new Regex("<h\\d>|<color=[^>]+>", RegexOptions.Compiled); private static readonly Dictionary<string, string> ByNorm = new Dictionary<string, string>(StringComparer.Ordinal); private static readonly Regex Latin = new Regex("[A-Za-z]{2}", RegexOptions.Compiled); private static readonly Regex Cyr = new Regex("[А-Яа-яЁё]", RegexOptions.Compiled); private static string missPath; public static bool LogMisses = true; private static readonly Regex Pronoun = new Regex("\\b(?:they're|them|they|their|They)\\b", RegexOptions.Compiled); private static readonly Regex Bullet = new Regex("^([•\\-\\*]\\s*)(.+)$", RegexOptions.Compiled | RegexOptions.Singleline); private static readonly Regex Time12 = new Regex("\\b(\\d{1,2})(?::(\\d\\d))? ?(AM|PM)\\b", RegexOptions.Compiled); private static readonly Regex Counter = new Regex("^(.+?)(\\s*\\(\\d+\\s*/\\s*\\d+\\))$", RegexOptions.Compiled | RegexOptions.Singleline); private static readonly Regex Day = new Regex("\\b(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)\\b", RegexOptions.Compiled); private static readonly Dictionary<string, string> Days = new Dictionary<string, string> { ["Monday"] = "Понедельник", ["Tuesday"] = "Вторник", ["Wednesday"] = "Среда", ["Thursday"] = "Четверг", ["Friday"] = "Пятница", ["Saturday"] = "Суббота", ["Sunday"] = "Воскресенье" }; private static readonly Regex Mins = new Regex("\\b(\\d+) ?(?:mins?|minutes?)\\b", RegexOptions.Compiled); private static readonly Regex Hrs = new Regex("\\b(\\d+) ?(?:hrs?|hours?)\\b", RegexOptions.Compiled); private static readonly Regex Wrap = new Regex("^((?:<[^>]+>)*)(.*?)((?:</[^>]+>)*)$", RegexOptions.Compiled | RegexOptions.Singleline); public static int ExactCount => Exact.Count; public static int TemplateCount => Templates.Count; private static string Norm(string s) { return OpenTag.Replace(s, "<h>").Replace("</color>", "</h>"); } public static void Load(string userDir) { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string[] manifestResourceNames = executingAssembly.GetManifestResourceNames(); Array.Sort(manifestResourceNames, (IComparer<string>?)StringComparer.Ordinal); string[] array = manifestResourceNames; foreach (string text in array) { if (text.StartsWith("tsv.")) { using StreamReader streamReader = new StreamReader(executingAssembly.GetManifestResourceStream(text), Encoding.UTF8); Parse(streamReader.ReadToEnd()); } } if (Directory.Exists(userDir)) { array = Directory.GetFiles(userDir, "*.tsv"); for (int i = 0; i < array.Length; i++) { Parse(File.ReadAllText(array[i], Encoding.UTF8)); } } missPath = Path.Combine(userDir, "untranslated.txt"); if (File.Exists(missPath)) { array = File.ReadAllLines(missPath, Encoding.UTF8); foreach (string item in array) { Missed.Add(item); } } } private static string Unesc(string s) { return s.Replace("\\n", "\n").Replace("\\t", "\t"); } private static void Parse(string text) { string[] array = text.Split('\n'); for (int i = 0; i < array.Length; i++) { string text2 = array[i].TrimEnd('\r'); if (text2.Length == 0 || text2[0] == '#') { continue; } int num = text2.IndexOf('\t'); if (num <= 0) { continue; } string text3 = Unesc(text2.Substring(0, num)).Trim(); string text4 = Unesc(text2.Substring(num + 1)); int num2 = text4.IndexOf('\t'); if (num2 >= 0) { text4 = text4.Substring(0, num2); } if (text4.Length == 0 || text3.Length == 0) { continue; } if (Slot.IsMatch(text3)) { AddTemplate(text3, text4); continue; } Exact[text3] = text4; if (text3.IndexOf("<h", StringComparison.Ordinal) >= 0) { ByNorm[Norm(text3)] = text4; } } } private static void AddTemplate(string en, string ru) { List<string> list = new List<string>(); StringBuilder stringBuilder = new StringBuilder("^"); int num = 0; foreach (Match item in Slot.Matches(en)) { stringBuilder.Append(Regex.Escape(en.Substring(num, item.Index - num))); stringBuilder.Append("(.+?)"); list.Add(item.Value); num = item.Index + item.Length; } stringBuilder.Append(Regex.Escape(en.Substring(num))).Append('$'); string input = Pronoun.Replace(stringBuilder.ToString(), (Match m) => m.Value switch { "them" => "(?:them|him|her)", "they" => "(?:they|he|she)", "their" => "(?:their|his|her)", "they're" => "(?:they're|he's|she's)", "They" => "(?:They|He|She)", _ => m.Value, }); input = Regex.Replace(input, "<h\\d>", "(?:<h\\d>|<color=[^>]+>)").Replace("</h>", "(?:</h>|</color>)"); input = Regex.Replace(input, "<color=(?:\\\\.|[^>\\\\])+>", "<color=[^>]+>"); Templates.Add((new Regex(input, RegexOptions.Compiled | RegexOptions.Singleline), ru, list.ToArray())); } public static string Get(string s) { if (string.IsNullOrEmpty(s) || s.Length > 4000) { return s; } if (Cache.TryGetValue(s, out var value)) { return value; } string text = s; if (Latin.IsMatch(s)) { text = Post(Find(s, top: true) ?? s); } if (Cache.Count > 30000) { Cache.Clear(); } Cache[s] = text; return text; } private static string Find(string s, bool top) { if (Exact.TryGetValue(s, out var value)) { return value; } if (s.IndexOf("<color", StringComparison.Ordinal) >= 0 && ByNorm.TryGetValue(Norm(s), out value)) { MatchCollection tags = OpenTag.Matches(s); int k = 0; return OpenTag.Replace(value, (Match m) => (k >= tags.Count) ? m.Value : tags[k++].Value).Replace("</h>", "</color>"); } string text = s.Trim(); if (text.Length != s.Length && text.Length > 0) { string text2 = Find(text, top: false); if (text2 != null) { return s.Substring(0, s.IndexOf(text, StringComparison.Ordinal)) + text2 + s.Substring(s.IndexOf(text, StringComparison.Ordinal) + text.Length); } return Miss(s, top); } if (s[0] == '<' || s[s.Length - 1] == '>') { Match match = Wrap.Match(s); if (match.Success && match.Groups[2].Length > 0 && match.Groups[2].Length < s.Length) { string text3 = Find(match.Groups[2].Value, top: false); if (text3 != null) { return match.Groups[1].Value + text3 + match.Groups[3].Value; } } } Match match2 = Bullet.Match(s); if (match2.Success) { string text4 = Find(match2.Groups[2].Value, top: false); if (text4 != null) { return match2.Groups[1].Value + text4; } } Match match3 = Counter.Match(s); if (match3.Success) { string text5 = Find(match3.Groups[1].Value, top: false); if (text5 != null) { return text5 + match3.Groups[2].Value; } } foreach (var template in Templates) { Regex item = template.re; string item2 = template.ru; string[] item3 = template.slots; Match match4 = item.Match(s); if (!match4.Success) { continue; } string text6 = item2; for (int num = 0; num < item3.Length; num++) { text6 = text6.Replace(item3[num], Slot1(match4.Groups[num + 1].Value)); } if (s.IndexOf("<color", StringComparison.Ordinal) >= 0) { MatchCollection tags2 = OpenTag.Matches(s); int k2 = 0; text6 = OpenTag.Replace(text6, (Match mm) => (k2 >= tags2.Count) ? mm.Value : tags2[k2++].Value).Replace("</h>", "</color>"); } return text6; } if (s.IndexOf('\n') >= 0) { string[] array = s.Split('\n'); bool flag = false; for (int num2 = 0; num2 < array.Length; num2++) { string text7 = array[num2].TrimEnd('\r'); if (text7.Length != 0 && Latin.IsMatch(text7)) { string text8 = Find(text7, top: false); if (text8 != null) { array[num2] = text8; flag = true; } else { Miss(text7, top: true); } } } if (!flag) { return null; } return string.Join("\n", array); } return Miss(s, top); } private static string Post(string s) { s = Time12.Replace(s, delegate(Match m) { int num = int.Parse(m.Groups[1].Value) % 12; if (m.Groups[3].Value == "PM") { num += 12; } return num + ":" + (m.Groups[2].Success ? m.Groups[2].Value : "00"); }); s = Day.Replace(s, (Match m) => Days[m.Value]); return Hrs.Replace(Mins.Replace(s, "$1 мин"), "$1 ч"); } private static string Slot1(string v) { if (Exact.TryGetValue(v.Trim(), out var value)) { return value; } Match match = Wrap.Match(v); if (match.Success && Exact.TryGetValue(match.Groups[2].Value.Trim(), out value)) { return match.Groups[1].Value + value + match.Groups[3].Value; } string text = (match.Success ? match.Groups[2].Value.Trim() : v.Trim()); if (text.Length > 1 && char.IsLower(text[0]) && Exact.TryGetValue(char.ToUpper(text[0]) + text.Substring(1), out value)) { return (match.Success ? match.Groups[1].Value : "") + value + (match.Success ? match.Groups[3].Value : ""); } string text2 = v.TrimEnd(); if (text2.Length > 1 && ".!?,;:".IndexOf(text2[text2.Length - 1]) >= 0 && Exact.TryGetValue(text2.Substring(0, text2.Length - 1), out value)) { return value + text2.Substring(text2.Length - 1); } return v; } private static string Miss(string s, bool top) { if (!top || !LogMisses || missPath == null || Cyr.IsMatch(s)) { return null; } string text = s.Replace("\r", "").Replace("\n", "\\n").Replace("\t", "\\t"); if (Missed.Add(text)) { try { File.AppendAllText(missPath, text + "\n", Encoding.UTF8); } catch { } } return null; } } }