diff --git a/IPA.Injector/IPA.Injector.csproj b/IPA.Injector/IPA.Injector.csproj
index 3b616354..11ad1325 100644
--- a/IPA.Injector/IPA.Injector.csproj
+++ b/IPA.Injector/IPA.Injector.csproj
@@ -49,6 +49,7 @@
+
diff --git a/IPA.Injector/Injector.cs b/IPA.Injector/Injector.cs
index 2cd57b08..02bb9211 100644
--- a/IPA.Injector/Injector.cs
+++ b/IPA.Injector/Injector.cs
@@ -19,10 +19,10 @@ namespace IPA.Injector
injected = true;
#region Add Library load locations
- AppDomain.CurrentDomain.AssemblyResolve += AssemblyLibLoader;
+ AppDomain.CurrentDomain.AssemblyResolve += LibLoader.AssemblyLibLoader;
try
{
- if (!SetDllDirectory(Path.Combine(Environment.CurrentDirectory, "Libs", "Native")))
+ if (!SetDllDirectory(LibLoader.NativeDir))
{
libLoader.Warn("Unable to add native library path to load path");
}
@@ -39,40 +39,6 @@ namespace IPA.Injector
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetDllDirectory(string lpPathName);
- #region Managed library loader
- private static string libsDir;
- private static Assembly AssemblyLibLoader(object source, ResolveEventArgs e)
- {
- if (libsDir == null)
- libsDir = Path.Combine(Environment.CurrentDirectory, "Libs");
-
- var asmName = new AssemblyName(e.Name);
- Log(Level.Debug, $"Resolving library {asmName}");
-
- var testFilen = Path.Combine(libsDir, $"{asmName.Name}.{asmName.Version}.dll");
- Log(Level.Debug, $"Looking for file {testFilen}");
-
- if (File.Exists(testFilen))
- return Assembly.LoadFile(testFilen);
-
- Log(Level.Critical, $"Could not load library {asmName}");
-
- return null;
- }
- private static void Log(Level lvl, string message)
- { // multiple proxy methods to delay loading of assemblies until it's done
- if (LogCreated)
- AssemblyLibLoaderCallLogger(lvl, message);
- else
- if (((byte)lvl & (byte)StandardLogger.PrintFilter) != 0)
- Console.WriteLine($"[{lvl}] {message}");
- }
- private static void AssemblyLibLoaderCallLogger(Level lvl, string message)
- {
- libLoader.Log(lvl, message);
- }
- #endregion
-
private static void Bootstrapper_Destroyed()
{
PluginComponent.Create();
diff --git a/IPA.Injector/LibLoader.cs b/IPA.Injector/LibLoader.cs
new file mode 100644
index 00000000..61343826
--- /dev/null
+++ b/IPA.Injector/LibLoader.cs
@@ -0,0 +1,159 @@
+using IPA.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using static IPA.Logging.Logger;
+
+namespace IPA.Injector
+{
+ internal class LibLoader
+ {
+ public static string LibsDir { get; set; } = Path.Combine(Environment.CurrentDirectory, "Libs");
+ public static string NativeDir { get; set; } = Path.Combine(LibsDir, "Native");
+ private static Dictionary filenameLocations = null;
+
+ public static Assembly AssemblyLibLoader(object source, ResolveEventArgs e)
+ {
+ var asmName = new AssemblyName(e.Name);
+ Log(Level.Debug, $"Resolving library {asmName}");
+
+ if (filenameLocations == null)
+ {
+ filenameLocations = new Dictionary();
+
+ foreach (var fn in TraverseTree(LibsDir, s => s != NativeDir))
+ filenameLocations.Add(fn.Name, fn.FullName);
+ }
+
+ var testFilen = $"{asmName.Name}.{asmName.Version}.dll";
+ Log(Level.Debug, $"Looking for file {testFilen}");
+
+ if (filenameLocations.TryGetValue(testFilen, out string path))
+ {
+ Log(Level.Debug, $"Found file {testFilen} as {path}");
+ if (File.Exists(path))
+ {
+ return Assembly.LoadFrom(path);
+ }
+ else
+ {
+ Log(Level.Critical, $"but {path} no longer exists!");
+ }
+ }
+
+ Log(Level.Critical, $"No library {asmName} found");
+
+ return null;
+ }
+
+ private static void Log(Level lvl, string message)
+ { // multiple proxy methods to delay loading of assemblies until it's done
+ if (LogCreated)
+ AssemblyLibLoaderCallLogger(lvl, message);
+ else
+ if (((byte)lvl & (byte)StandardLogger.PrintFilter) != 0)
+ Console.WriteLine($"[{lvl}] {message}");
+ }
+
+ private static void AssemblyLibLoaderCallLogger(Level lvl, string message)
+ {
+ libLoader.Log(lvl, message);
+ }
+
+ // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/how-to-iterate-through-a-directory-tree
+ private static IEnumerable TraverseTree(string root, Func dirValidator = null)
+ {
+ if (dirValidator == null) dirValidator = (s) => true;
+
+ // Data structure to hold names of subfolders to be
+ // examined for files.
+ Stack dirs = new Stack(32);
+
+ if (!System.IO.Directory.Exists(root))
+ {
+ throw new ArgumentException();
+ }
+ dirs.Push(root);
+
+ while (dirs.Count > 0)
+ {
+ string currentDir = dirs.Pop();
+ string[] subDirs;
+ try
+ {
+ subDirs = System.IO.Directory.GetDirectories(currentDir);
+ }
+ // An UnauthorizedAccessException exception will be thrown if we do not have
+ // discovery permission on a folder or file. It may or may not be acceptable
+ // to ignore the exception and continue enumerating the remaining files and
+ // folders. It is also possible (but unlikely) that a DirectoryNotFound exception
+ // will be raised. This will happen if currentDir has been deleted by
+ // another application or thread after our call to Directory.Exists. The
+ // choice of which exceptions to catch depends entirely on the specific task
+ // you are intending to perform and also on how much you know with certainty
+ // about the systems on which this code will run.
+ catch (UnauthorizedAccessException e)
+ {
+ //Console.WriteLine(e.Message);
+ continue;
+ }
+ catch (System.IO.DirectoryNotFoundException e)
+ {
+ //Console.WriteLine(e.Message);
+ continue;
+ }
+
+ string[] files = null;
+ try
+ {
+ files = System.IO.Directory.GetFiles(currentDir);
+ }
+
+ catch (UnauthorizedAccessException e)
+ {
+
+ //Console.WriteLine(e.Message);
+ continue;
+ }
+
+ catch (System.IO.DirectoryNotFoundException e)
+ {
+ //Console.WriteLine(e.Message);
+ continue;
+ }
+
+ // Push the subdirectories onto the stack for traversal.
+ // This could also be done before handing the files.
+ foreach (string str in subDirs)
+ if (dirValidator(str)) dirs.Push(str);
+
+ // Perform the required action on each file here.
+ // Modify this block to perform your required task.
+ foreach (string file in files)
+ {
+ FileInfo nextValue = null;
+ try
+ {
+ // Perform whatever action is required in your scenario.
+ nextValue = new System.IO.FileInfo(file);
+ //Console.WriteLine("{0}: {1}, {2}", fi.Name, fi.Length, fi.CreationTime);
+ }
+ catch (System.IO.FileNotFoundException e)
+ {
+ // If file was deleted by a separate application
+ // or thread since the call to TraverseTree()
+ // then just continue.
+ //Console.WriteLine(e.Message);
+ continue;
+ }
+
+ yield return nextValue;
+ }
+ }
+ }
+ }
+}