Decompiled source of NameThis v1.1.0

NameThis.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
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("NameThis")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("NameThis")]
[assembly: AssemblyTitle("NameThis")]
[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 NameThis
{
	internal static class JotunnSource
	{
		internal static void Collect(Action<string, string> claim)
		{
			foreach (CustomItem item in ModRegistry.GetItems())
			{
				if ((Object)(object)((item != null) ? item.ItemPrefab : null) != (Object)null)
				{
					string name = ((Object)item.ItemPrefab).name;
					BepInPlugin sourceMod = ((CustomEntity)item).SourceMod;
					claim(name, (sourceMod != null) ? sourceMod.GUID : null);
				}
			}
			foreach (CustomPrefab prefab in ModRegistry.GetPrefabs())
			{
				if ((Object)(object)((prefab != null) ? prefab.Prefab : null) != (Object)null)
				{
					string name2 = ((Object)prefab.Prefab).name;
					BepInPlugin sourceMod2 = ((CustomEntity)prefab).SourceMod;
					claim(name2, (sourceMod2 != null) ? sourceMod2.GUID : null);
				}
			}
		}
	}
	[BepInPlugin("local.namethis", "NameThis", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "local.namethis";

		public const string NAME = "NameThis";

		public const string VERSION = "1.1.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<string> Format;

		internal static ConfigEntry<string> VanillaLabel;

		internal static ConfigEntry<string> UnknownLabel;

		internal static ConfigEntry<string> Overrides;

		internal static ConfigEntry<bool> DumpToLog;

		private void Awake()
		{
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Ogolne", "Wlaczony", true, "Pokazuje w opisie przedmiotu, z jakiego moda pochodzi.");
			Format = ((BaseUnityPlugin)this).Config.Bind<string>("Ogolne", "Format", "<size=80%><color=#9a9a9a>{0}</color></size>", "Wyglad napisu. {0} = nazwa moda. Obsluguje znaczniki <size>, <color>, <i>.");
			VanillaLabel = ((BaseUnityPlugin)this).Config.Bind<string>("Ogolne", "Napis dla gry", "Vanilla", "Napis dla przedmiotow z samej gry.");
			UnknownLabel = ((BaseUnityPlugin)this).Config.Bind<string>("Ogolne", "Napis dla nieznanych", "Mod", "Napis, gdy przedmiot na pewno nie jest z gry, ale nie da sie ustalic moda.");
			Overrides = ((BaseUnityPlugin)this).Config.Bind<string>("Ogolne", "Wlasne nazwy modow", "", "Zmiana wyswietlanej nazwy moda: GUID=Nazwa, oddzielone ';'. Np. Therzie.Warfare=Therzie Warfare");
			DumpToLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostyka", "Zrzut mapy do logu", false, "Po wczytaniu swiata zapisuje do LogOutput.log, ile przedmiotow przypisano do kazdego moda.");
			Overrides.SettingChanged += delegate
			{
				Sources.ResetNames();
			};
			new Harmony("local.namethis").PatchAll(typeof(Plugin).Assembly);
		}
	}
	internal static class Sources
	{
		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class ObjectDB_Awake
		{
			[HarmonyPriority(800)]
			private static void Prefix(ObjectDB __instance)
			{
				if (_vanillaCaptured || __instance.m_items == null || __instance.m_items.Count == 0)
				{
					return;
				}
				foreach (GameObject item in __instance.m_items)
				{
					if ((Object)(object)item != (Object)null)
					{
						Vanilla.Add(((Object)item).name);
					}
				}
				_vanillaCaptured = true;
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class ZNetScene_Awake
		{
			private static void Postfix()
			{
				_byPrefab = null;
				PrefabByShared.Clear();
				if (!((Object)(object)ZNetScene.instance == (Object)null))
				{
					if (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsDedicated())
					{
						((MonoBehaviour)ZNetScene.instance).StartCoroutine(BuildSoon());
					}
					if (Plugin.DumpToLog.Value)
					{
						((MonoBehaviour)ZNetScene.instance).StartCoroutine(DumpLater());
					}
				}
			}
		}

		private static readonly HashSet<string> Vanilla = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static bool _vanillaCaptured;

		private static Dictionary<string, PluginInfo> _byPrefab;

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

		private static readonly Dictionary<SharedData, string> PrefabByShared = new Dictionary<SharedData, string>();

		private static Dictionary<string, string> _nameOverrides;

		private static readonly HashSet<string> IgnoredOwners = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "com.jotunn.jotunn", "ValheimModding.YamlDotNet", "ValheimModding.JsonDotNET", "local.namethis" };

		internal static void ResetNames()
		{
			Labels.Clear();
			_nameOverrides = null;
		}

		internal static string LabelFor(ItemData item)
		{
			string text = PrefabName(item);
			if (text == null)
			{
				return null;
			}
			if (Vanilla.Contains(text))
			{
				return Plugin.VanillaLabel.Value;
			}
			if (!_vanillaCaptured)
			{
				return null;
			}
			PluginInfo val = OwnerOf(text);
			if (val != null)
			{
				return DisplayName(val);
			}
			return Plugin.UnknownLabel.Value;
		}

		private static string PrefabName(ItemData item)
		{
			if (item == null || item.m_shared == null)
			{
				return null;
			}
			if ((Object)(object)item.m_dropPrefab != (Object)null)
			{
				return ((Object)item.m_dropPrefab).name;
			}
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			if (PrefabByShared.Count == 0)
			{
				foreach (GameObject item2 in instance.m_items)
				{
					ItemDrop val = (((Object)(object)item2 == (Object)null) ? null : item2.GetComponent<ItemDrop>());
					if ((Object)(object)val != (Object)null && val.m_itemData?.m_shared != null)
					{
						PrefabByShared[val.m_itemData.m_shared] = ((Object)item2).name;
					}
				}
			}
			if (PrefabByShared.TryGetValue(item.m_shared, out var value))
			{
				return value;
			}
			foreach (GameObject item3 in instance.m_items)
			{
				ItemDrop val2 = (((Object)(object)item3 == (Object)null) ? null : item3.GetComponent<ItemDrop>());
				if ((Object)(object)val2 != (Object)null && val2.m_itemData?.m_shared?.m_name == item.m_shared.m_name)
				{
					return ((Object)item3).name;
				}
			}
			return null;
		}

		internal static PluginInfo OwnerOf(string prefab)
		{
			if (_byPrefab == null)
			{
				Build();
			}
			if (!_byPrefab.TryGetValue(prefab, out var value))
			{
				return null;
			}
			return value;
		}

		private static void Build()
		{
			_byPrefab = new Dictionary<string, PluginInfo>(StringComparer.OrdinalIgnoreCase);
			Stopwatch stopwatch = Stopwatch.StartNew();
			Dictionary<Assembly, PluginInfo> dictionary = new Dictionary<Assembly, PluginInfo>();
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				if ((Object)(object)((value != null) ? value.Instance : null) != (Object)null && !IgnoredOwners.Contains(value.Metadata.GUID))
				{
					dictionary[((object)value.Instance).GetType().Assembly] = value;
				}
			}
			Dictionary<AssetBundle, PluginInfo> bundleOwner = new Dictionary<AssetBundle, PluginInfo>();
			try
			{
				FromJotunn();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("Jotunn: " + ex.Message));
			}
			try
			{
				FromStaticFields(dictionary, bundleOwner);
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogDebug((object)("Pola: " + ex2.Message));
			}
			try
			{
				FromBundles(bundleOwner);
			}
			catch (Exception ex3)
			{
				Plugin.Log.LogDebug((object)("Paczki: " + ex3.Message));
			}
			Plugin.Log.LogInfo((object)$"Mapa zrodel przedmiotow: {_byPrefab.Count} prefabow z modow, {Vanilla.Count} z gry ({stopwatch.ElapsedMilliseconds} ms).");
		}

		private static void Claim(string prefab, PluginInfo owner)
		{
			if (!string.IsNullOrEmpty(prefab) && owner != null && !Vanilla.Contains(prefab) && !_byPrefab.ContainsKey(prefab))
			{
				_byPrefab[prefab] = owner;
			}
		}

		private static void FromJotunn()
		{
			if (!Chainloader.PluginInfos.ContainsKey("com.jotunn.jotunn"))
			{
				return;
			}
			JotunnSource.Collect(delegate(string prefab, string guid)
			{
				if (guid != null && Chainloader.PluginInfos.TryGetValue(guid, out var value))
				{
					Claim(prefab, value);
				}
			});
		}

		private static void FromStaticFields(Dictionary<Assembly, PluginInfo> byAssembly, Dictionary<AssetBundle, PluginInfo> bundleOwner)
		{
			Dictionary<PluginInfo, HashSet<string>> dictionary = new Dictionary<PluginInfo, HashSet<string>>();
			foreach (KeyValuePair<Assembly, PluginInfo> item in byAssembly)
			{
				HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				int vanillaRefs = 0;
				Type[] array;
				try
				{
					array = item.Key.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					array = ex.Types.Where((Type t) => t != null).ToArray();
				}
				Type[] array2 = array;
				foreach (Type type in array2)
				{
					if (type.ContainsGenericParameters)
					{
						continue;
					}
					FieldInfo[] fields;
					try
					{
						fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					}
					catch
					{
						continue;
					}
					FieldInfo[] array3 = fields;
					foreach (FieldInfo fieldInfo in array3)
					{
						if (!fieldInfo.IsLiteral && Interesting(fieldInfo.FieldType))
						{
							object value;
							try
							{
								value = fieldInfo.GetValue(null);
							}
							catch
							{
								continue;
							}
							Visit(value, 0, hashSet, item.Value, bundleOwner, ref vanillaRefs);
						}
					}
				}
				if (vanillaRefs <= 50 && hashSet.Count > 0)
				{
					dictionary[item.Value] = hashSet;
				}
			}
			Dictionary<string, int> dictionary2 = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
			foreach (HashSet<string> value3 in dictionary.Values)
			{
				foreach (string item2 in value3)
				{
					dictionary2[item2] = ((!dictionary2.TryGetValue(item2, out var value2)) ? 1 : (value2 + 1));
				}
			}
			foreach (KeyValuePair<PluginInfo, HashSet<string>> item3 in dictionary)
			{
				foreach (string item4 in item3.Value)
				{
					if (dictionary2[item4] == 1)
					{
						Claim(item4, item3.Key);
					}
				}
			}
		}

		private static bool Interesting(Type t)
		{
			if (t.IsPrimitive || t.IsEnum || t == typeof(string))
			{
				return false;
			}
			if (typeof(Delegate).IsAssignableFrom(t))
			{
				return false;
			}
			return true;
		}

		private static void Visit(object v, int depth, HashSet<string> names, PluginInfo owner, Dictionary<AssetBundle, PluginInfo> bundleOwner, ref int vanillaRefs)
		{
			if (v == null || depth > 2)
			{
				return;
			}
			GameObject val = (GameObject)((v is GameObject) ? v : null);
			if (val != null)
			{
				if (!((Object)(object)val == (Object)null))
				{
					if (Vanilla.Contains(((Object)val).name))
					{
						vanillaRefs++;
					}
					else if ((Object)(object)val.GetComponent<ItemDrop>() != (Object)null)
					{
						names.Add(((Object)val).name);
					}
				}
				return;
			}
			AssetBundle val2 = (AssetBundle)((v is AssetBundle) ? v : null);
			if (val2 != null)
			{
				if ((Object)(object)val2 != (Object)null && !bundleOwner.ContainsKey(val2))
				{
					bundleOwner[val2] = owner;
				}
			}
			else
			{
				if (v is Object || v is string)
				{
					return;
				}
				if (v is IDictionary dictionary)
				{
					int num = 0;
					{
						foreach (DictionaryEntry item in dictionary)
						{
							if (++num > 5000)
							{
								break;
							}
							Visit(item.Value, depth + 1, names, owner, bundleOwner, ref vanillaRefs);
						}
						return;
					}
				}
				if (v is IEnumerable enumerable)
				{
					int num2 = 0;
					{
						foreach (object item2 in enumerable)
						{
							if (++num2 > 5000)
							{
								break;
							}
							Visit(item2, depth + 1, names, owner, bundleOwner, ref vanillaRefs);
						}
						return;
					}
				}
				if (depth >= 2)
				{
					return;
				}
				Type type = v.GetType();
				if (type.IsValueType || type.Assembly == typeof(object).Assembly)
				{
					return;
				}
				FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (!(fieldInfo.FieldType != typeof(GameObject)) || !(fieldInfo.FieldType != typeof(AssetBundle)))
					{
						object value;
						try
						{
							value = fieldInfo.GetValue(v);
						}
						catch
						{
							continue;
						}
						Visit(value, depth + 1, names, owner, bundleOwner, ref vanillaRefs);
					}
				}
			}
		}

		private static void FromBundles(Dictionary<AssetBundle, PluginInfo> bundleOwner)
		{
			List<PluginInfo> mods = Chainloader.PluginInfos.Values.Where((PluginInfo p) => (Object)(object)((p != null) ? p.Instance : null) != (Object)null && !IgnoredOwners.Contains(p.Metadata.GUID)).ToList();
			foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles())
			{
				if ((Object)(object)allLoadedAssetBundle == (Object)null)
				{
					continue;
				}
				if (!bundleOwner.TryGetValue(allLoadedAssetBundle, out var value))
				{
					value = GuessOwner(((Object)allLoadedAssetBundle).name, mods);
				}
				if (value == null)
				{
					continue;
				}
				string[] allAssetNames;
				try
				{
					allAssetNames = allLoadedAssetBundle.GetAllAssetNames();
				}
				catch
				{
					continue;
				}
				string[] array = allAssetNames;
				foreach (string text in array)
				{
					if (text.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase))
					{
						Claim(Path.GetFileNameWithoutExtension(text), value);
					}
				}
			}
		}

		private static PluginInfo GuessOwner(string bundleName, List<PluginInfo> mods)
		{
			string text = Norm(Path.GetFileNameWithoutExtension(bundleName ?? ""));
			if (text.Length < 3)
			{
				return null;
			}
			PluginInfo result = null;
			int num = 0;
			foreach (PluginInfo mod in mods)
			{
				foreach (string item in Tokens(mod))
				{
					int num2 = 0;
					if (item == text)
					{
						num2 = 100 + item.Length;
					}
					else if (item.Length >= 4 && text.Contains(item))
					{
						num2 = 50 + item.Length;
					}
					else if (text.Length >= 4 && item.Contains(text))
					{
						num2 = 40 + text.Length;
					}
					if (num2 > num)
					{
						num = num2;
						result = mod;
					}
				}
			}
			return result;
		}

		private static IEnumerable<string> Tokens(PluginInfo p)
		{
			yield return Norm(p.Metadata.Name);
			string[] array = p.Metadata.GUID.Split('.', '-', '_');
			foreach (string s in array)
			{
				yield return Norm(s);
			}
			string fileName = Path.GetFileName(Path.GetDirectoryName(p.Location) ?? "");
			array = fileName.Split('-');
			foreach (string s2 in array)
			{
				yield return Norm(s2);
			}
		}

		private static string Norm(string s)
		{
			StringBuilder stringBuilder = new StringBuilder();
			string text = s ?? "";
			foreach (char c in text)
			{
				if (char.IsLetterOrDigit(c))
				{
					stringBuilder.Append(char.ToLowerInvariant(c));
				}
			}
			return stringBuilder.ToString();
		}

		internal static string DisplayName(PluginInfo p)
		{
			if (Labels.TryGetValue(p.Metadata.GUID, out var value))
			{
				return value;
			}
			string text = null;
			if (_nameOverrides == null)
			{
				_nameOverrides = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
				string[] array = Plugin.Overrides.Value.Split(';');
				foreach (string text2 in array)
				{
					int num = text2.IndexOf('=');
					if (num > 0)
					{
						_nameOverrides[text2.Substring(0, num).Trim()] = text2.Substring(num + 1).Trim();
					}
				}
			}
			if (_nameOverrides.TryGetValue(p.Metadata.GUID, out var value2))
			{
				text = value2;
			}
			if (text == null)
			{
				text = ThunderstoreName(p);
			}
			if (string.IsNullOrEmpty(text))
			{
				text = Pretty(p.Metadata.Name);
			}
			Labels[p.Metadata.GUID] = text;
			return text;
		}

		private static string ThunderstoreName(PluginInfo p)
		{
			try
			{
				string directoryName = Path.GetDirectoryName(p.Location);
				for (int i = 0; i < 3; i++)
				{
					if (string.IsNullOrEmpty(directoryName))
					{
						break;
					}
					string path = Path.Combine(directoryName, "manifest.json");
					if (File.Exists(path))
					{
						Match match = Regex.Match(File.ReadAllText(path), "\"name\"\\s*:\\s*\"([^\"]+)\"");
						if (match.Success)
						{
							return Pretty(match.Groups[1].Value);
						}
					}
					if (!Path.GetFileName(Path.GetDirectoryName(directoryName) ?? "").Equals("plugins", StringComparison.OrdinalIgnoreCase))
					{
						directoryName = Path.GetDirectoryName(directoryName);
						continue;
					}
					break;
				}
			}
			catch
			{
			}
			return null;
		}

		internal static string Pretty(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return s;
			}
			s = s.Replace('_', ' ').Trim();
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < s.Length; i++)
			{
				char c = s[i];
				if (i > 0 && char.IsUpper(c) && char.IsLower(s[i - 1]))
				{
					stringBuilder.Append(' ');
				}
				stringBuilder.Append(c);
			}
			return Regex.Replace(stringBuilder.ToString(), "\\s+", " ");
		}

		private static IEnumerator BuildSoon()
		{
			yield return null;
			yield return null;
			if (_byPrefab == null && (Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_items.Count > 0)
			{
				Build();
			}
		}

		private static IEnumerator DumpLater()
		{
			yield return (object)new WaitForSeconds(5f);
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null)
			{
				yield break;
			}
			Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
			foreach (GameObject item in instance.m_items)
			{
				ItemDrop val = (((Object)(object)item == (Object)null) ? null : item.GetComponent<ItemDrop>());
				if (!((Object)(object)val == (Object)null))
				{
					string key = LabelFor(val.m_itemData) ?? "?";
					if (!dictionary.TryGetValue(key, out var value))
					{
						value = (dictionary[key] = new List<string>());
					}
					value.Add(((Object)item).name);
				}
			}
			StringBuilder stringBuilder = new StringBuilder("Zrodla przedmiotow (NameThis):\n");
			foreach (KeyValuePair<string, List<string>> item2 in dictionary.OrderByDescending((KeyValuePair<string, List<string>> k) => k.Value.Count))
			{
				stringBuilder.AppendLine(string.Format("  {0}: {1}  np. {2}", item2.Key, item2.Value.Count, string.Join(", ", item2.Value.Take(6))));
			}
			Plugin.Log.LogInfo((object)stringBuilder.ToString());
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(bool),
		typeof(float),
		typeof(int),
		typeof(bool)
	})]
	internal static class ItemData_GetTooltip
	{
		[HarmonyPriority(0)]
		private static void Postfix(ItemData item, bool appending, ref string __result)
		{
			if (!appending && Plugin.Enabled.Value && item != null && __result != null)
			{
				string text;
				try
				{
					text = Sources.LabelFor(item);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogDebug((object)ex);
					return;
				}
				if (!string.IsNullOrEmpty(text))
				{
					__result = __result.TrimEnd() + "\n\n" + string.Format(Plugin.Format.Value, text);
				}
			}
		}
	}
}