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("SweetRuRepoRoles")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SweetRuRepoRoles")] [assembly: AssemblyTitle("SweetRuRepoRoles")] [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 SweetRuRepoRoles { [BepInPlugin("sweet.ru.reporoles", "Sweet RU: REPO Roles Classic", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string Guid = "sweet.ru.reporoles"; public const string Version = "1.0.0"; private const string TargetGuid = "R3Labs.Repo_Roles.Classic"; private const string RolesType = "Repo_Roles.RepoRoles"; private const string GuiType = "R.E.P.O.Roles.patches.guiManager"; private const string RoleIdEntry = "savedRole"; private static readonly string[] OverlayFonts = new string[4] { "Liberation Sans Narrow", "Arial Narrow", "Segoe UI", "Arial" }; private static FieldInfo customFontField; private static Font cyrillicFont; 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.reporoles"); Core.LoadTsv("Items.tsv", Core.ItemNames); if (Core.ItemNames.Count > 0) { val.PatchAll(typeof(ItemNamePatch)); } Literals.Apply(val, Core.AssemblyOfPlugin("R3Labs.Repo_Roles.Classic"), Core.LoadTsv("Hardcoded.tsv")); TranslateRoleConfig(); ReplaceOverlayFont(val); Core.UseLocalizedNames = true; val.PatchAll(typeof(EnemyNamePatch)); } private void TranslateRoleConfig() { Type type = AccessTools.TypeByName("Repo_Roles.RepoRoles"); if (type == null) { Core.Log.LogError((object)"Type Repo_Roles.RepoRoles not found, role names stay English."); return; } Dictionary dictionary = Core.LoadTsv("Roles.tsv"); int num = 0; foreach (FieldInfo declaredField in AccessTools.GetDeclaredFields(type)) { if (declaredField.IsStatic && !(declaredField.FieldType != typeof(ConfigEntry)) && !(declaredField.Name == "savedRole") && declaredField.GetValue(null) is ConfigEntry val && val.Value != null && dictionary.TryGetValue(val.Value, out var value)) { val.Value = value; num++; } } Core.Log.LogInfo((object)("Translated " + num + " role names and descriptions.")); } private void ReplaceOverlayFont(Harmony harmony) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown Type type = AccessTools.TypeByName("R.E.P.O.Roles.patches.guiManager"); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "loadFont", (Type[])null, (Type[])null) : null); customFontField = ((type != null) ? AccessTools.Field(type, "customFont") : null); if (methodInfo == null || customFontField == null) { Core.Log.LogError((object)"guiManager.loadFont not found, the overlay keeps its Latin-only font."); } else { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(Plugin), "AfterLoadFont", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private static void AfterLoadFont() { Font val = CyrillicFont(); if ((Object)(object)val != (Object)null) { customFontField.SetValue(null, val); } } private static Font CyrillicFont() { if ((Object)(object)cyrillicFont != (Object)null) { return cyrillicFont; } cyrillicFont = Font.CreateDynamicFontFromOSFont(OverlayFonts, 24); if ((Object)(object)cyrillicFont == (Object)null) { Core.Log.LogError((object)"No system font could be loaded, the overlay keeps its own font."); return null; } ((Object)cyrillicFont).hideFlags = (HideFlags)32; Core.Log.LogInfo((object)("Overlay font set to " + ((Object)cyrillicFont).name + " (dynamic: " + cyrillicFont.dynamic + ").")); return cyrillicFont; } } }