Decompiled source of Sweet RU RepoRoles v1.0.0

SweetRuRepoRoles.dll

Decompiled 17 hours ago
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<string, string> ItemNames = new Dictionary<string, string>();

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

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

		internal static bool UseLocalizedNames;

		internal static Dictionary<string, string> LoadTsv(string fileName, Dictionary<string, string> into = null)
		{
			Dictionary<string, string> dictionary = into ?? new Dictionary<string, string>();
			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<string, string> map;

		internal static int Apply(Harmony harmony, Assembly target, Dictionary<string, string> 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<Type> TypesOf(Assembly assembly)
		{
			try
			{
				return assembly.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				return ex.Types.Where((Type t) => t != null);
			}
		}

		private static IEnumerable<MethodBase> MembersOf(Type type)
		{
			return from m in type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Cast<MethodBase>().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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> 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 = " <color=#FFFFFF>[";

		[HarmonyPostfix]
		private static void Postfix(ref string ___promptName)
		{
			if (!string.IsNullOrEmpty(___promptName))
			{
				int num = ___promptName.IndexOf(" <color=#FFFFFF>[", 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<string, string> dictionary = Core.LoadTsv("Roles.tsv");
			int num = 0;
			foreach (FieldInfo declaredField in AccessTools.GetDeclaredFields(type))
			{
				if (declaredField.IsStatic && !(declaredField.FieldType != typeof(ConfigEntry<string>)) && !(declaredField.Name == "savedRole") && declaredField.GetValue(null) is ConfigEntry<string> 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;
		}
	}
}