You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
5.5 KiB

5 years ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Reflection;
  5. using IPA.Logging;
  6. using Mono.Cecil;
  7. namespace IPA.Loader
  8. {
  9. internal class CecilLibLoader : BaseAssemblyResolver
  10. {
  11. public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
  12. {
  13. LibLoader.SetupAssemblyFilenames();
  14. if (LibLoader.FilenameLocations.TryGetValue($"{name.Name}.{name.Version}.dll", out var path))
  15. {
  16. if (File.Exists(path))
  17. return AssemblyDefinition.ReadAssembly(path, parameters);
  18. }
  19. else if (LibLoader.FilenameLocations.TryGetValue($"{name.Name}.dll", out path))
  20. {
  21. if (File.Exists(path))
  22. return AssemblyDefinition.ReadAssembly(path, parameters);
  23. }
  24. return base.Resolve(name, parameters);
  25. }
  26. }
  27. internal static class LibLoader
  28. {
  29. internal static string LibraryPath => Path.Combine(Environment.CurrentDirectory, "Libs");
  30. internal static string NativeLibraryPath => Path.Combine(LibraryPath, "Native");
  31. internal static Dictionary<string, string> FilenameLocations;
  32. internal static void SetupAssemblyFilenames(bool force = false)
  33. {
  34. if (FilenameLocations == null || force)
  35. {
  36. FilenameLocations = new Dictionary<string, string>();
  37. foreach (var fn in TraverseTree(LibraryPath, s => s != NativeLibraryPath))
  38. if (FilenameLocations.ContainsKey(fn.Name))
  39. Log(Logger.Level.Critical, $"Multiple instances of {fn.Name} exist in Libs! Ignoring {fn.FullName}");
  40. else FilenameLocations.Add(fn.Name, fn.FullName);
  41. }
  42. }
  43. public static Assembly AssemblyLibLoader(object source, ResolveEventArgs e)
  44. {
  45. var asmName = new AssemblyName(e.Name);
  46. return LoadLibrary(asmName);
  47. }
  48. internal static Assembly LoadLibrary(AssemblyName asmName)
  49. {
  50. Log(Logger.Level.Debug, $"Resolving library {asmName}");
  51. SetupAssemblyFilenames();
  52. var testFile = $"{asmName.Name}.{asmName.Version}.dll";
  53. Log(Logger.Level.Debug, $"Looking for file {testFile}");
  54. if (FilenameLocations.TryGetValue(testFile, out var path))
  55. {
  56. Log(Logger.Level.Debug, $"Found file {testFile} as {path}");
  57. if (File.Exists(path))
  58. return Assembly.LoadFrom(path);
  59. Log(Logger.Level.Critical, $"but {path} no longer exists!");
  60. }
  61. else if (FilenameLocations.TryGetValue(testFile = $"{asmName.Name}.dll", out path))
  62. {
  63. Log(Logger.Level.Debug, $"Found file {testFile} as {path}");
  64. if (File.Exists(path))
  65. return Assembly.LoadFrom(path);
  66. Log(Logger.Level.Critical, $"but {path} no longer exists!");
  67. }
  68. Log(Logger.Level.Critical, $"No library {asmName} found");
  69. return null;
  70. }
  71. internal static void Log(Logger.Level lvl, string message)
  72. { // multiple proxy methods to delay loading of assemblies until it's done
  73. if (Logger.LogCreated)
  74. AssemblyLibLoaderCallLogger(lvl, message);
  75. else
  76. if (((byte)lvl & (byte)StandardLogger.PrintFilter) != 0)
  77. Console.WriteLine($"[{lvl}] {message}");
  78. }
  79. private static void AssemblyLibLoaderCallLogger(Logger.Level lvl, string message) => Logger.libLoader.Log(lvl, message);
  80. // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/how-to-iterate-through-a-directory-tree
  81. private static IEnumerable<FileInfo> TraverseTree(string root, Func<string, bool> dirValidator = null)
  82. {
  83. if (dirValidator == null) dirValidator = s => true;
  84. Stack<string> dirs = new Stack<string>(32);
  85. if (!Directory.Exists(root))
  86. throw new ArgumentException();
  87. dirs.Push(root);
  88. while (dirs.Count > 0)
  89. {
  90. string currentDir = dirs.Pop();
  91. string[] subDirs;
  92. try
  93. {
  94. subDirs = Directory.GetDirectories(currentDir);
  95. }
  96. catch (UnauthorizedAccessException)
  97. {
  98. continue;
  99. }
  100. catch (DirectoryNotFoundException)
  101. {
  102. continue;
  103. }
  104. string[] files;
  105. try
  106. {
  107. files = Directory.GetFiles(currentDir);
  108. }
  109. catch (UnauthorizedAccessException)
  110. {
  111. continue;
  112. }
  113. catch (DirectoryNotFoundException)
  114. {
  115. continue;
  116. }
  117. foreach (string str in subDirs)
  118. if (dirValidator(str)) dirs.Push(str);
  119. foreach (string file in files)
  120. {
  121. FileInfo nextValue;
  122. try
  123. {
  124. nextValue = new FileInfo(file);
  125. }
  126. catch (FileNotFoundException)
  127. {
  128. continue;
  129. }
  130. yield return nextValue;
  131. }
  132. }
  133. }
  134. }
  135. }