TypeLoader.cs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Text;
  8. using BepInEx.Contract;
  9. using BepInEx.Logging;
  10. using Mono.Cecil;
  11. namespace BepInEx.Bootstrap
  12. {
  13. /// <summary>
  14. /// Provides methods for loading specified types from an assembly.
  15. /// </summary>
  16. public static class TypeLoader
  17. {
  18. private static bool Is(this TypeDefinition self, Type td)
  19. {
  20. if (self.FullName == td.FullName)
  21. return true;
  22. return self.FullName != "System.Object" && (self.BaseType?.Resolve().Is(td) ?? false);
  23. }
  24. private static DefaultAssemblyResolver resolver;
  25. private static ReaderParameters readerParameters;
  26. static TypeLoader()
  27. {
  28. resolver = new DefaultAssemblyResolver();
  29. readerParameters = new ReaderParameters { AssemblyResolver = resolver };
  30. resolver.ResolveFailure += (sender, reference) =>
  31. {
  32. var name = new AssemblyName(reference.FullName);
  33. if (Utility.TryResolveDllAssembly(name, Paths.BepInExAssemblyDirectory, readerParameters, out AssemblyDefinition assembly) || Utility.TryResolveDllAssembly(name, Paths.PluginPath, readerParameters, out assembly) || Utility.TryResolveDllAssembly(name, Paths.ManagedPath, readerParameters, out assembly))
  34. return assembly;
  35. return null;
  36. };
  37. }
  38. /// <summary>
  39. /// Loads a list of types from a directory containing assemblies, that derive from a base type.
  40. /// </summary>
  41. /// <typeparam name="T">The specific base type to search for.</typeparam>
  42. /// <param name="directory">The directory to search for assemblies.</param>
  43. /// <returns>Returns a list of found derivative types.</returns>
  44. public static Dictionary<AssemblyDefinition, IEnumerable<PluginInfo>> FindPluginTypes(string directory)
  45. {
  46. var result = new Dictionary<AssemblyDefinition, IEnumerable<PluginInfo>>();
  47. var pluginType = typeof(BaseUnityPlugin);
  48. string currentProcess = Process.GetCurrentProcess().ProcessName.ToLower();
  49. foreach (string dll in Directory.GetFiles(Path.GetFullPath(directory), "*.dll", SearchOption.AllDirectories))
  50. {
  51. try
  52. {
  53. var ass = AssemblyDefinition.ReadAssembly(dll, readerParameters);
  54. var matchingTypes = ass.MainModule.Types.Where(t => !t.IsInterface && !t.IsAbstract && t.Is(pluginType)).ToList();
  55. if (matchingTypes.Count == 0)
  56. continue;
  57. var pluginInfos = new List<PluginInfo>();
  58. foreach (var pluginTypeDefinition in matchingTypes)
  59. {
  60. var metadata = BepInPlugin.FromCecilType(pluginTypeDefinition);
  61. if (metadata == null)
  62. {
  63. Logger.LogWarning($"Skipping over type [{pluginTypeDefinition.Name}] as no metadata attribute is specified");
  64. continue;
  65. }
  66. //Perform a filter for currently running process
  67. var filters = BepInProcess.FromCecilType(pluginTypeDefinition);
  68. bool invalidProcessName = filters.Any(x => x.ProcessName.ToLower().Replace(".exe", "") == currentProcess);
  69. if (invalidProcessName)
  70. {
  71. Logger.LogInfo($"Skipping over plugin [{metadata.GUID}] due to process filter");
  72. continue;
  73. }
  74. var dependencies = BepInDependency.FromCecilType(pluginTypeDefinition);
  75. pluginInfos.Add(new PluginInfo
  76. {
  77. Metadata = metadata,
  78. Processes = filters,
  79. Dependencies = dependencies,
  80. CecilType = pluginTypeDefinition,
  81. Location = dll
  82. });
  83. }
  84. result[ass] = pluginInfos;
  85. }
  86. catch (Exception e)
  87. {
  88. Logger.LogError(e.ToString());
  89. }
  90. }
  91. return result;
  92. }
  93. private static string TypeLoadExceptionToString(ReflectionTypeLoadException ex)
  94. {
  95. StringBuilder sb = new StringBuilder();
  96. foreach (Exception exSub in ex.LoaderExceptions)
  97. {
  98. sb.AppendLine(exSub.Message);
  99. if (exSub is FileNotFoundException exFileNotFound)
  100. {
  101. if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
  102. {
  103. sb.AppendLine("Fusion Log:");
  104. sb.AppendLine(exFileNotFound.FusionLog);
  105. }
  106. }
  107. else if (exSub is FileLoadException exLoad)
  108. {
  109. if (!string.IsNullOrEmpty(exLoad.FusionLog))
  110. {
  111. sb.AppendLine("Fusion Log:");
  112. sb.AppendLine(exLoad.FusionLog);
  113. }
  114. }
  115. sb.AppendLine();
  116. }
  117. return sb.ToString();
  118. }
  119. }
  120. }