using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using SweetRu; using UnityEngine; [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("SweetRuSillyValuables")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SweetRuSillyValuables")] [assembly: AssemblyTitle("SweetRuSillyValuables")] [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 SweetRu { internal static class Core { internal static ManualLogSource Log; internal static string Folder; internal static readonly Dictionary ItemNames = new Dictionary(); internal static readonly Dictionary EnemyNames = new Dictionary(); internal static readonly Dictionary ChatLines = new Dictionary(); internal static bool UseLocalizedNames; internal static Dictionary LoadTsv(string fileName, Dictionary into = null) { Dictionary dictionary = into ?? new Dictionary(); string text = Path.Combine(Folder, fileName); if (!File.Exists(text)) { Log.LogWarning((object)("Missing translation file: " + text)); return dictionary; } try { string[] array = File.ReadAllLines(text, Encoding.UTF8); foreach (string text2 in array) { if (!string.IsNullOrWhiteSpace(text2) && !text2.StartsWith("#")) { string[] array2 = text2.Split('\t'); if (array2.Length == 2 && !string.IsNullOrWhiteSpace(array2[1])) { dictionary[Unescape(array2[0])] = Unescape(array2[1]); } } } Log.LogInfo((object)("Loaded " + dictionary.Count + " lines from " + fileName + ".")); } catch (Exception ex) { Log.LogError((object)("Failed to read " + fileName + ": " + ex.Message)); } return dictionary; } private static string Unescape(string value) { return value.Replace("\\n", "\n").Replace("\\t", "\t").Replace("\\s", " "); } internal static Assembly AssemblyOfPlugin(string pluginGuid) { if (Chainloader.PluginInfos.TryGetValue(pluginGuid, out var value) && (Object)(object)value.Instance != (Object)null) { return ((object)value.Instance).GetType().Assembly; } Log.LogError((object)("Plugin " + pluginGuid + " is not loaded.")); return null; } } internal static class Literals { private const BindingFlags AllMembers = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private const byte LdstrOpcode = 114; private const int StringTokenType = 1879048192; private static Dictionary map; internal static int Apply(Harmony harmony, Assembly target, Dictionary dictionary) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown if (target == null) { Core.Log.LogError((object)"Target assembly not loaded, nothing to rewrite."); return 0; } if (dictionary.Count == 0) { return 0; } map = dictionary; HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(Literals), "Transpiler", (Type[])null, (Type[])null)); int num = 0; foreach (Type item in TypesOf(target)) { if (item == null || item.IsGenericTypeDefinition) { continue; } foreach (MethodBase item2 in MembersOf(item)) { if (LoadsTranslatedString(item2, item.Module)) { try { harmony.Patch(item2, (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null); num++; } catch (Exception ex) { Core.Log.LogWarning((object)("Could not rewrite " + item.FullName + "." + item2.Name + ": " + ex.Message)); } } } } Core.Log.LogInfo((object)("Rewrote text in " + num + " methods of " + target.GetName().Name + ".")); return num; } private static IEnumerable TypesOf(Assembly assembly) { try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types.Where((Type t) => t != null); } } private static IEnumerable MembersOf(Type type) { return from m in type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Cast().Concat(type.GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) where !m.IsAbstract && !m.ContainsGenericParameters select m; } private static bool LoadsTranslatedString(MethodBase method, Module module) { byte[] array; try { array = method.GetMethodBody()?.GetILAsByteArray(); } catch { return false; } if (array == null) { return false; } for (int i = 0; i + 4 < array.Length; i++) { if (array[i] != 114) { continue; } int num = BitConverter.ToInt32(array, i + 1); if ((num & -16777216) != 1879048192) { continue; } try { if (map.ContainsKey(module.ResolveString(num))) { return true; } } catch { } } return false; } private static IEnumerable Transpiler(IEnumerable instructions) { foreach (CodeInstruction instruction in instructions) { if (instruction.opcode == OpCodes.Ldstr && instruction.operand is string key && map.TryGetValue(key, out var value)) { instruction.operand = value; } yield return instruction; } } } [HarmonyPatch(typeof(ItemAttributes), "ShowingInfo")] internal static class ItemNamePatch { private const string TagMarker = " ["; [HarmonyPostfix] private static void Postfix(ref string ___promptName) { if (!string.IsNullOrEmpty(___promptName)) { int num = ___promptName.IndexOf(" [", StringComparison.Ordinal); string key = ((num >= 0) ? ___promptName.Substring(0, num) : ___promptName); if (Core.ItemNames.TryGetValue(key, out var value)) { ___promptName = ((num >= 0) ? (value + ___promptName.Substring(num)) : value); } } } } [HarmonyPatch(typeof(EnemyParent), "Awake")] internal static class EnemyNamePatch { [HarmonyPostfix] private static void Postfix(EnemyParent __instance) { if (Core.UseLocalizedNames && (Object)(object)__instance.enemyNameLocalized != (Object)null) { string text = null; try { text = __instance.enemyNameLocalized.GetLocalizedString(); } catch { } if (!string.IsNullOrEmpty(text)) { __instance.enemyName = text; } } if (__instance.enemyName != null && Core.EnemyNames.TryGetValue(__instance.enemyName, out var value)) { __instance.enemyName = value; } } } [HarmonyPatch(typeof(ChatManager), "PossessChat")] internal static class ChatLinePatch { [HarmonyPrefix] private static void Prefix(ref string message) { if (message != null && Core.ChatLines.TryGetValue(message, out var value)) { message = value; } } } } namespace SweetRuSillyValuables { [BepInPlugin("sweet.ru.sillyvaluables", "Sweet RU: SillyValuables", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string Guid = "sweet.ru.sillyvaluables"; public const string Version = "1.0.0"; private const string TargetGuid = "Sangrento.SillyValuables"; private const string EspeakGuid = "Lavighju.espeakTTS"; private const string EspeakDefaultLanguage = "en"; private const int EspeakDefaultSpeed = 175; private const int RussianSpeed = 100; private void Awake() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown Core.Log = ((BaseUnityPlugin)this).Logger; Core.Folder = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); Harmony val = new Harmony("sweet.ru.sillyvaluables"); Core.LoadTsv("Items.tsv", Core.ItemNames); if (Core.ItemNames.Count > 0) { val.PatchAll(typeof(ItemNamePatch)); } Core.LoadTsv("Chat.tsv", Core.ChatLines); if (Core.ChatLines.Count > 0) { val.PatchAll(typeof(ChatLinePatch)); } TuneEspeak(); } private void TuneEspeak() { if (((BaseUnityPlugin)this).Config.Bind("Speech", "Tune espeakTTS for Russian speech", true, "Настроить espeakTTS под русский, чтобы говорящие предметы читались вслух внятно: язык en -> ru, скорость 175 -> 100 слов в минуту. Трогаются только значения по умолчанию: то, что ты выбрал сам, мод не перебивает. Выключи, если хочешь настраивать espeakTTS сам.").Value) { if (!Chainloader.PluginInfos.TryGetValue("Lavighju.espeakTTS", out var value) || (Object)(object)value.Instance == (Object)null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"espeakTTS is not installed, talking props will stay silent in Russian."); return; } ConfigFile config = value.Instance.Config; Retune(config, "Language", "en", "ru"); Retune(config, "Speed", 175, 100); } } private static void Retune(ConfigFile config, string key, T untouched, T wanted) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown ConfigEntry val = default(ConfigEntry); if (!config.TryGetEntry(new ConfigDefinition("General", key), ref val)) { Core.Log.LogWarning((object)("espeakTTS has no General/" + key + " setting, leaving it alone.")); return; } if (!object.Equals(val.Value, untouched)) { Core.Log.LogInfo((object)("espeakTTS " + key + " is already " + val.Value?.ToString() + ", leaving it alone.")); return; } val.Value = wanted; ManualLogSource log = Core.Log; string[] obj = new string[7] { "espeakTTS ", key, " switched from ", null, null, null, null }; T val2 = untouched; obj[3] = val2?.ToString(); obj[4] = " to "; val2 = wanted; obj[5] = val2?.ToString(); obj[6] = "."; log.LogInfo((object)string.Concat(obj)); } } }