Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions BepInEx.Core/Bootstrap/TypeLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,38 @@ public static class TypeLoader

public static HashSet<string> SearchDirectories = new();

private static readonly Dictionary<string, string> AssemblyPathByName = new(StringComparer.InvariantCultureIgnoreCase);

/// <summary>
/// Maps every managed assembly file in a directory to its assembly name, so files whose
/// filename does not match the assembly name (e.g. renamed by the user) still resolve.
/// First file wins when two files share an assembly name.
/// </summary>
public static void RegisterAssemblyPaths(string directory)
{
if (!Directory.Exists(directory))
return;

foreach (var dll in Directory.GetFiles(Path.GetFullPath(directory), "*.dll", SearchOption.AllDirectories))
{
AssemblyName name;
try
{
name = AssemblyName.GetAssemblyName(dll);
}
catch (Exception)
{
continue;
}

if (!AssemblyPathByName.ContainsKey(name.Name))
AssemblyPathByName[name.Name] = dll;
}
}

public static bool TryGetRegisteredAssemblyPath(string name, out string path) =>
AssemblyPathByName.TryGetValue(name, out path) && File.Exists(path);

#region Config

private static readonly ConfigEntry<bool> EnableAssemblyCache = ConfigFile.CoreConfig.Bind(
Expand Down Expand Up @@ -104,6 +136,15 @@ public static AssemblyDefinition CecilResolveOnFailure(object sender, AssemblyNa
return assembly;
}

if (TryGetRegisteredAssemblyPath(name.Name, out var mappedPath))
{
try
{
return AssemblyDefinition.ReadAssembly(mappedPath, ReaderParameters);
}
catch (BadImageFormatException) { }
}

return AssemblyResolve?.Invoke(sender, reference);
}

Expand Down
19 changes: 17 additions & 2 deletions BepInEx.Core/Contract/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,27 @@ public static class MetadataHelper
{
internal static bool TypeInheretsFrom(TypeReference derived, Type type)
{
var td = derived.Resolve();
TypeDefinition td;
try
{
td = derived.Resolve();
}
catch (AssemblyResolutionException)
{
return false;
}
while (td != null)
{
if (td.FullName == type.FullName)
return true;
td = td.BaseType?.Resolve();
try
{
td = td.BaseType?.Resolve();
}
catch (AssemblyResolutionException)
{
return false;
}
}
return false;
}
Expand Down
15 changes: 7 additions & 8 deletions BepInEx.Preloader.Core/Patching/AssemblyPatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ namespace BepInEx.Preloader.Core.Patching;
/// </summary>
public class AssemblyPatcher : IDisposable
{
private static readonly string CurrentAssemblyName = Assembly.GetExecutingAssembly().GetName().Name;

private Func<byte[], string, Assembly> assemblyLoader;

public AssemblyPatcher(Func<byte[], string, Assembly> assemblyLoader)
Expand Down Expand Up @@ -115,12 +113,11 @@ private PatcherPluginMetadata ToPatcherPlugin(TypeDefinition type, string assemb

private bool HasPatcherPlugins(AssemblyDefinition ass)
{
if (ass.MainModule.AssemblyReferences.All(r => r.Name != CurrentAssemblyName) &&
ass.Name.Name != CurrentAssemblyName)
return false;
if (ass.MainModule.GetTypeReferences().All(r => r.FullName != typeof(BasePatcher).FullName))
return false;

var typeReferences = ass.MainModule.GetTypeReferences().ToList();
if (typeReferences.All(r => r.FullName != typeof(BasePatcher).FullName))
{
return typeReferences.Any(r => MetadataHelper.TypeInheretsFrom(r, typeof(BasePatcher)));
}
return true;
}

Expand All @@ -135,6 +132,8 @@ public void AddPatchersFromDirectory(string directory)

var sortedPatchers = new List<PatchDefinition>();

TypeLoader.RegisterAssemblyPaths(directory);

var patchers = TypeLoader.FindPluginTypes(directory, ToPatcherPlugin, HasPatcherPlugins);

// TODO: Add dependency ordering and process attribute filtering
Expand Down
2 changes: 1 addition & 1 deletion BepInEx.Preloader.Core/Patching/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private static Version TryParseLongVersion(string version)

internal static PatcherPluginInfoAttribute FromCecilType(TypeDefinition td)
{
var attr = MetadataHelper.GetCustomAttributes<PatcherPluginInfoAttribute>(td, false).FirstOrDefault();
var attr = MetadataHelper.GetCustomAttributes<PatcherPluginInfoAttribute>(td, false, true).FirstOrDefault();

if (attr == null)
return null;
Expand Down
9 changes: 9 additions & 0 deletions Runtimes/NET/BepInEx.NET.Shared/SharedEntrypoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ public static Assembly LocalResolve(object sender, ResolveEventArgs args)
|| LocalUtility.TryResolveDllAssembly(assemblyName, Paths.PluginPath, out foundAssembly))
return foundAssembly;

if (Bootstrap.TypeLoader.TryGetRegisteredAssemblyPath(assemblyName.Name, out var mappedPath))
{
try
{
return Utility.LoadContext.LoadFromAssemblyPath(mappedPath);
}
catch (Exception) { }
}

return null;
}
}
Expand Down
Loading