Decompiled source of REPOFurryRU v1.0.2

plugins/REPOFurryRU/REPOFurryRU.dll

Decompiled 6 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization.Tables;
using UnityEngine.SceneManagement;
using UnityEngine.TextCore.LowLevel;

[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("REPOFurryRU")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("REPOFurryRU")]
[assembly: AssemblyTitle("REPOFurryRU")]
[assembly: AssemblyVersion("1.0.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 REPOFurry
{
	[BepInPlugin("clauderushub.repofurry", "REPOFurryRU", "1.0.2")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "clauderushub.repofurry";

		internal static ManualLogSource Log;

		internal static string Dir;

		private void Awake()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Dir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			Texts.Load(Path.Combine(Dir, "texts.json"));
			CodeStrings.Load(Path.Combine(Dir, "code.json"));
			Harmony val = new Harmony("clauderushub.repofurry");
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(Assembly.GetExecutingAssembly());
			foreach (Type type in typesFromAssembly)
			{
				if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0)
				{
					try
					{
						val.CreateClassProcessor(type).Patch();
					}
					catch (Exception ex)
					{
						Log.LogError((object)("перехват " + type.Name + ": " + (ex.InnerException?.Message ?? ex.Message)));
					}
				}
			}
			CodeStrings.Patch(val);
			SceneManager.sceneLoaded += delegate
			{
				Sweep();
			};
			Log.LogInfo((object)$"готово: строк {Texts.Count}, в коде {CodeStrings.Patched} методов");
		}

		internal static void Sweep()
		{
			Safe("шрифты", Fonts.Apply);
			Safe("текст сцены", Texts.ApplyScene);
		}

		internal static void Safe(string what, Action a)
		{
			try
			{
				a();
			}
			catch (Exception ex)
			{
				Log.LogError((object)(what + ": " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(LocalizationManager), "LoadLocalizationOverrides")]
	internal static class Tables
	{
		private static void Postfix()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			string[] array = new string[3] { "HUD", "Menu", "Game" };
			foreach (string text in array)
			{
				string path = Path.Combine(Plugin.Dir, text + ".tsv");
				if (!File.Exists(path))
				{
					continue;
				}
				try
				{
					StringTable table = ((LocalizedDatabase<StringTable, StringTableEntry>)(object)LocalizationSettings.StringDatabase).GetTable(TableReference.op_Implicit(text), LocalizationSettings.ProjectLocale);
					string[] array2 = File.ReadAllLines(path, Encoding.UTF8);
					for (int j = 0; j < array2.Length; j++)
					{
						string[] array3 = array2[j].Split('\t');
						if (array3.Length == 2)
						{
							string text2 = array3[1].Replace("\\n", "\n");
							StringTableEntry entry = ((DetailedLocalizationTable<StringTableEntry>)(object)table).GetEntry(array3[0]);
							if (entry != null)
							{
								entry.Value = text2;
							}
							else
							{
								((DetailedLocalizationTable<StringTableEntry>)(object)table).AddEntry(array3[0], text2);
							}
							num++;
						}
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("таблица " + text + ": " + ex.Message));
				}
			}
			Plugin.Log.LogInfo((object)$"таблицы: строк {num}");
			Plugin.Safe("шрифты", Fonts.Apply);
		}
	}
	internal static class Texts
	{
		private static Dictionary<string, string> exact = new Dictionary<string, string>();

		public static int Count => exact.Count;

		public static void Load(string path)
		{
			if (File.Exists(path))
			{
				exact = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(path));
			}
		}

		public static string Translate(string s)
		{
			if (string.IsNullOrEmpty(s) || exact.Count == 0)
			{
				return s;
			}
			if (exact.TryGetValue(s, out var value))
			{
				return value;
			}
			string text = s.Trim();
			if (text.Length != s.Length && text.Length > 0 && exact.TryGetValue(text, out value))
			{
				return s.Substring(0, s.IndexOf(text, StringComparison.Ordinal)) + value + s.Substring(s.IndexOf(text, StringComparison.Ordinal) + text.Length);
			}
			return s;
		}

		public static void ApplyScene()
		{
			TMP_Text[] array = Resources.FindObjectsOfTypeAll<TMP_Text>();
			foreach (TMP_Text val in array)
			{
				string text = Translate(val.text);
				if ((object)text != val.text)
				{
					val.text = text;
				}
			}
			CosmeticAsset[] array2 = Resources.FindObjectsOfTypeAll<CosmeticAsset>();
			foreach (CosmeticAsset obj in array2)
			{
				obj.assetName = Translate(obj.assetName);
			}
			SemiColor[] array3 = Resources.FindObjectsOfTypeAll<SemiColor>();
			foreach (SemiColor obj2 in array3)
			{
				obj2.colorName = Translate(obj2.colorName);
			}
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class TmpTextSetter
	{
		private static void Prefix(ref string value)
		{
			value = Texts.Translate(value);
		}
	}
	[HarmonyPatch]
	internal static class TmpSetText
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type[][] array = new Type[2][]
			{
				new Type[1] { typeof(string) },
				new Type[2]
				{
					typeof(string),
					typeof(bool)
				}
			};
			foreach (Type[] array2 in array)
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(TMP_Text), "SetText", array2, (Type[])null);
				if (methodInfo != null)
				{
					yield return methodInfo;
				}
			}
		}

		private static void Prefix(ref string sourceText)
		{
			sourceText = Texts.Translate(sourceText);
		}
	}
	internal static class CodeStrings
	{
		private static Dictionary<string, Dictionary<string, string>> map = new Dictionary<string, Dictionary<string, string>>();

		private static readonly Dictionary<MethodBase, Dictionary<string, string>> byMethod = new Dictionary<MethodBase, Dictionary<string, string>>();

		public static int Patched;

		public static void Load(string path)
		{
			if (File.Exists(path))
			{
				map = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>(File.ReadAllText(path));
			}
		}

		public static void Patch(Harmony harmony)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(CodeStrings), "Transpiler", (Type[])null, (Type[])null));
			foreach (KeyValuePair<string, Dictionary<string, string>> item in map)
			{
				if (item.Key.StartsWith("_"))
				{
					continue;
				}
				int num = item.Key.LastIndexOf("::", StringComparison.Ordinal);
				Type type = AccessTools.TypeByName(item.Key.Substring(0, num));
				string text = item.Key.Substring(num + 2);
				if (type == null)
				{
					Plugin.Log.LogWarning((object)("нет типа: " + item.Key));
					continue;
				}
				foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
				{
					if (!(declaredMethod.Name != text) && !declaredMethod.IsAbstract)
					{
						byMethod[declaredMethod] = item.Value;
						try
						{
							harmony.Patch((MethodBase)declaredMethod, (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
							Patched++;
						}
						catch (Exception ex)
						{
							Plugin.Log.LogError((object)("код " + item.Key + ": " + ex.Message));
						}
					}
				}
			}
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> code, MethodBase original)
		{
			byMethod.TryGetValue(original, out var strings);
			foreach (CodeInstruction item in code)
			{
				if (strings != null && item.opcode == OpCodes.Ldstr && item.operand is string key && strings.TryGetValue(key, out var value))
				{
					item.operand = value;
				}
				yield return item;
			}
		}
	}
	internal static class Phrases
	{
		private static readonly Random rnd = new Random();

		private static readonly string[] LeftBehind = new string[15]
		{
			"Меня бросили...", "Они ушли без меня.", "Экипаж уехал, а я осталась лапками кверху.", "Никто не подождал пушистика...", "Меня забыли в темноте.", "Грузовичок уехал без меня.", "Я думал, мы одна команда.", "Почему они не подождали?", "Что я сделал не так?", "Теперь я совсем один.",
			"Они были моей последней надеждой.", "Так нечестно, фыр.", "Бросили маленького робота...", "Ушли и даже хвостиком не махнули.", "Я так и знал."
		};

		private static readonly string[] Love = new string[14]
		{
			"{0} такой милашка, аж мурчу!", "Не могу перестать думать о {0}.", "{0}, ты лучше всех!", "Хочу обнимашек с {0}!", "Каждый раз, когда вижу {0}, виляю хвостиком.", "{0} — моё солнышко.", "С {0} даже бяки не страшны.", "Кажется, я влюбился в {0}...", "{0}, ты самый пушистый!", "Пойдём собирать блестяшки вместе, {0}?",
			"Сердечко пищит, когда рядом {0}.", "{0} — мой лучший друг навсегда!", "Хочу почесать {0} за ушком.", "Ня! {0} сегодня особенно мил."
		};

		private static string Pick(string[] a)
		{
			return a[rnd.Next(a.Length)];
		}

		public static string GetLeftBehind()
		{
			return Pick(LeftBehind);
		}

		public static string GetLove(string name)
		{
			return string.Format(Pick(Love), name);
		}
	}
	[HarmonyPatch(typeof(SemiFunc), "MessageGeneratedGetLeftBehind")]
	internal static class LeftBehindPatch
	{
		private static bool Prefix(ref string __result)
		{
			__result = Phrases.GetLeftBehind();
			return false;
		}
	}
	[HarmonyPatch(typeof(ValuableLovePotion), "GenerateAffectionateSentence")]
	internal static class LovePotionPatch
	{
		private static bool Prefix(ValuableLovePotion __instance, ref string __result)
		{
			string name = (AccessTools.Field(typeof(ValuableLovePotion), "playerName")?.GetValue(__instance) as string) ?? "";
			__result = Phrases.GetLove(name);
			return false;
		}
	}
	internal static class Translit
	{
		private static readonly Dictionary<char, string> map = new Dictionary<char, string>
		{
			['а'] = "a",
			['б'] = "b",
			['в'] = "v",
			['г'] = "g",
			['д'] = "d",
			['е'] = "ye",
			['ё'] = "yo",
			['ж'] = "zh",
			['з'] = "z",
			['и'] = "ee",
			['й'] = "y",
			['к'] = "k",
			['л'] = "l",
			['м'] = "m",
			['н'] = "n",
			['о'] = "o",
			['п'] = "p",
			['р'] = "r",
			['с'] = "s",
			['т'] = "t",
			['у'] = "oo",
			['ф'] = "f",
			['х'] = "kh",
			['ц'] = "ts",
			['ч'] = "ch",
			['ш'] = "sh",
			['щ'] = "sch",
			['ъ'] = "",
			['ы'] = "i",
			['ь'] = "",
			['э'] = "e",
			['ю'] = "yu",
			['я'] = "ya"
		};

		internal static readonly Dictionary<string, string> back = new Dictionary<string, string>();

		public static string Word(string w)
		{
			StringBuilder stringBuilder = new StringBuilder();
			bool flag = false;
			foreach (char c in w)
			{
				char key = char.ToLowerInvariant(c);
				if (map.TryGetValue(key, out var value))
				{
					flag = true;
					stringBuilder.Append((char.IsUpper(c) && value.Length > 0) ? (char.ToUpperInvariant(value[0]) + value.Substring(1)) : value);
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			if (!flag)
			{
				return w;
			}
			string text = stringBuilder.ToString();
			back[text] = w;
			return text;
		}

		public static string Text(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return s;
			}
			string[] array = s.Split(' ');
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = Word(array[i]);
			}
			return string.Join(" ", array);
		}
	}
	[HarmonyPatch(typeof(TTSVoice), "StartSpeakingWithHighlight")]
	internal static class TtsSpeak
	{
		private static void Prefix(ref string text)
		{
			Translit.back.Clear();
			text = Translit.Text(text);
		}
	}
	[HarmonyPatch(typeof(TTSVoice), "VoiceText")]
	internal static class TtsShow
	{
		private static void Prefix(ref string text)
		{
			if (text != null && Translit.back.TryGetValue(text.Trim(), out var value))
			{
				text = value;
			}
		}
	}
	internal static class Fonts
	{
		internal const string Marker = "REPOFurry_";

		internal static readonly Dictionary<string, string> files = new Dictionary<string, string>();

		private static TMP_FontAsset oswald;

		public static void Apply()
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_007d: Expected O, but got Unknown
			if ((Object)(object)oswald == (Object)null)
			{
				string text = Path.Combine(Plugin.Dir, "fonts", "OswaldTeko.ttf");
				if (!File.Exists(text))
				{
					Plugin.Log.LogWarning((object)("нет шрифта: " + text));
					return;
				}
				files["REPOFurry_Oswald"] = text;
				Font val = new Font("REPOFurry_Oswald");
				Object.DontDestroyOnLoad((Object)val);
				oswald = TMP_FontAsset.CreateFontAsset(val, 90, 9, (GlyphRenderMode)4165, 1024, 1024, (AtlasPopulationMode)1, true);
				if ((Object)(object)oswald == (Object)null)
				{
					Plugin.Log.LogError((object)"шрифт Oswald не создан");
					return;
				}
				((Object)oswald).name = "REPOFurry Oswald";
				Object.DontDestroyOnLoad((Object)(object)oswald);
			}
			List<string> list = new List<string>();
			TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			foreach (TMP_FontAsset val2 in array)
			{
				if (val2 != oswald && ((Object)val2).name != null && ((Object)val2).name.StartsWith("Teko"))
				{
					if (val2.fallbackFontAssetTable == null)
					{
						val2.fallbackFontAssetTable = new List<TMP_FontAsset>();
					}
					if (!val2.fallbackFontAssetTable.Contains(oswald))
					{
						val2.fallbackFontAssetTable.Insert(0, oswald);
						list.Add(((Object)val2).name);
					}
				}
			}
			if (list.Count > 0)
			{
				Plugin.Log.LogInfo((object)("шрифты: Oswald -> " + string.Join(", ", list)));
			}
		}
	}
	[HarmonyPatch(typeof(FontEngine), "LoadFontFace", new Type[]
	{
		typeof(Font),
		typeof(int)
	})]
	internal static class LoadFace
	{
		private static bool Prefix(Font font, int pointSize, ref FontEngineError __result)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected I4, but got Unknown
			if ((Object)(object)font == (Object)null || ((Object)font).name == null || !Fonts.files.TryGetValue(((Object)font).name, out var value))
			{
				return true;
			}
			__result = (FontEngineError)(int)FontEngine.LoadFontFace(value, pointSize);
			return false;
		}
	}
}