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.

189 lines
7.1 KiB

  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. var testFile = $"{name.Name}.{name.Version}.dll";
  15. if (LibLoader.FilenameLocations.TryGetValue(testFile, out string path))
  16. {
  17. if (File.Exists(path))
  18. {
  19. return AssemblyDefinition.ReadAssembly(path, parameters);
  20. }
  21. }
  22. return base.Resolve(name, parameters);
  23. }
  24. }
  25. internal static class LibLoader
  26. {
  27. internal static string LibraryPath => Path.Combine(Environment.CurrentDirectory, "Libs");
  28. internal static string NativeLibraryPath => Path.Combine(LibraryPath, "Native");
  29. internal static Dictionary<string, string> FilenameLocations;
  30. internal static void SetupAssemblyFilenames(bool force = false)
  31. {
  32. if (FilenameLocations == null || force)
  33. {
  34. FilenameLocations = new Dictionary<string, string>();
  35. foreach (var fn in TraverseTree(LibraryPath, s => s != NativeLibraryPath))
  36. if (FilenameLocations.ContainsKey(fn.Name))
  37. Log(Logger.Level.Critical, $"Multiple instances of {fn.Name} exist in Libs! Ignoring {fn.FullName}");
  38. else FilenameLocations.Add(fn.Name, fn.FullName);
  39. }
  40. }
  41. public static Assembly AssemblyLibLoader(object source, ResolveEventArgs e)
  42. {
  43. var asmName = new AssemblyName(e.Name);
  44. Log(Logger.Level.Debug, $"Resolving library {asmName}");
  45. SetupAssemblyFilenames();
  46. var testFile = $"{asmName.Name}.{asmName.Version}.dll";
  47. Log(Logger.Level.Debug, $"Looking for file {testFile}");
  48. if (FilenameLocations.TryGetValue(testFile, out var path))
  49. {
  50. Log(Logger.Level.Debug, $"Found file {testFile} as {path}");
  51. if (File.Exists(path))
  52. return Assembly.LoadFrom(path);
  53. Log(Logger.Level.Critical, $"but {path} no longer exists!");
  54. }
  55. else if (FilenameLocations.TryGetValue(testFile = $"{asmName.Name}.dll", out path))
  56. {
  57. Log(Logger.Level.Debug, $"Found file {testFile} as {path}");
  58. if (File.Exists(path))
  59. return Assembly.LoadFrom(path);
  60. Log(Logger.Level.Critical, $"but {path} no longer exists!");
  61. }
  62. Log(Logger.Level.Critical, $"No library {asmName} found");
  63. return null;
  64. }
  65. internal static void Log(Logger.Level lvl, string message)
  66. { // multiple proxy methods to delay loading of assemblies until it's done
  67. if (Logger.LogCreated)
  68. AssemblyLibLoaderCallLogger(lvl, message);
  69. else
  70. if (((byte)lvl & (byte)StandardLogger.PrintFilter) != 0)
  71. Console.WriteLine($"[{lvl}] {message}");
  72. }
  73. private static void AssemblyLibLoaderCallLogger(Logger.Level lvl, string message)
  74. {
  75. Logger.libLoader.Log(lvl, message);
  76. }
  77. // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/how-to-iterate-through-a-directory-tree
  78. private static IEnumerable<FileInfo> TraverseTree(string root, Func<string, bool> dirValidator = null)
  79. {
  80. if (dirValidator == null) dirValidator = s => true;
  81. // Data structure to hold names of subfolders to be
  82. // examined for files.
  83. Stack<string> dirs = new Stack<string>(32);
  84. if (!Directory.Exists(root))
  85. {
  86. throw new ArgumentException();
  87. }
  88. dirs.Push(root);
  89. while (dirs.Count > 0)
  90. {
  91. string currentDir = dirs.Pop();
  92. string[] subDirs;
  93. try
  94. {
  95. subDirs = Directory.GetDirectories(currentDir);
  96. }
  97. // An UnauthorizedAccessException exception will be thrown if we do not have
  98. // discovery permission on a folder or file. It may or may not be acceptable
  99. // to ignore the exception and continue enumerating the remaining files and
  100. // folders. It is also possible (but unlikely) that a DirectoryNotFound exception
  101. // will be raised. This will happen if currentDir has been deleted by
  102. // another application or thread after our call to Directory.Exists. The
  103. // choice of which exceptions to catch depends entirely on the specific task
  104. // you are intending to perform and also on how much you know with certainty
  105. // about the systems on which this code will run.
  106. catch (UnauthorizedAccessException)
  107. {
  108. //Console.WriteLine(e.Message);
  109. continue;
  110. }
  111. catch (DirectoryNotFoundException)
  112. {
  113. //Console.WriteLine(e.Message);
  114. continue;
  115. }
  116. string[] files;
  117. try
  118. {
  119. files = Directory.GetFiles(currentDir);
  120. }
  121. catch (UnauthorizedAccessException)
  122. {
  123. //Console.WriteLine(e.Message);
  124. continue;
  125. }
  126. catch (DirectoryNotFoundException)
  127. {
  128. //Console.WriteLine(e.Message);
  129. continue;
  130. }
  131. // Push the subdirectories onto the stack for traversal.
  132. // This could also be done before handing the files.
  133. foreach (string str in subDirs)
  134. if (dirValidator(str)) dirs.Push(str);
  135. // Perform the required action on each file here.
  136. // Modify this block to perform your required task.
  137. foreach (string file in files)
  138. {
  139. FileInfo nextValue;
  140. try
  141. {
  142. // Perform whatever action is required in your scenario.
  143. nextValue = new FileInfo(file);
  144. //Console.WriteLine("{0}: {1}, {2}", fi.Name, fi.Length, fi.CreationTime);
  145. }
  146. catch (FileNotFoundException)
  147. {
  148. // If file was deleted by a separate application
  149. // or thread since the call to TraverseTree()
  150. // then just continue.
  151. //Console.WriteLine(e.Message);
  152. continue;
  153. }
  154. yield return nextValue;
  155. }
  156. }
  157. }
  158. }
  159. }