using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using Mono.Cecil;
namespace BepInEx
{
///
/// Generic helper properties and methods.
///
public static class Utility
{
///
/// Whether current Common Language Runtime supports dynamic method generation using namespace.
///
public static bool CLRSupportsDynamicAssemblies { get; }
static Utility()
{
try
{
CLRSupportsDynamicAssemblies = true;
// ReSharper disable once AssignNullToNotNullAttribute
var m = new CustomAttributeBuilder(null, new object[0]);
}
catch (PlatformNotSupportedException)
{
CLRSupportsDynamicAssemblies = false;
}
catch (ArgumentNullException)
{
// Suppress ArgumentNullException
}
}
///
/// Try to perform an action.
///
/// Action to perform.
/// Possible exception that gets returned.
/// True, if action succeeded, false if an exception occured.
public static bool TryDo(Action action, out Exception exception)
{
exception = null;
try
{
action();
return true;
}
catch (Exception e)
{
exception = e;
return false;
}
}
///
/// Combines multiple paths together, as the specific method is not available in .NET 3.5.
///
/// The multiple paths to combine together.
/// A combined path.
public static string CombinePaths(params string[] parts) => parts.Aggregate(Path.Combine);
///
/// Tries to parse a bool, with a default value if unable to parse.
///
/// The string to parse
/// The value to return if parsing is unsuccessful.
/// Boolean value of input if able to be parsed, otherwise default value.
public static bool SafeParseBool(string input, bool defaultValue = false)
{
return Boolean.TryParse(input, out bool result) ? result : defaultValue;
}
///
/// Converts a file path into a UnityEngine.WWW format.
///
/// The file path to convert.
/// A converted file path.
public static string ConvertToWWWFormat(string path)
{
return $"file://{path.Replace('\\', '/')}";
}
///
/// Indicates whether a specified string is null, empty, or consists only of white-space characters.
///
/// The string to test.
/// True if the value parameter is null or empty, or if value consists exclusively of white-space characters.
public static bool IsNullOrWhiteSpace(this string self)
{
return self == null || self.All(Char.IsWhiteSpace);
}
public static IEnumerable TopologicalSort(IEnumerable nodes, Func> dependencySelector)
{
List sorted_list = new List();
HashSet visited = new HashSet();
HashSet sorted = new HashSet();
foreach (TNode input in nodes)
{
Stack currentStack = new Stack();
if (!Visit(input, currentStack))
{
throw new Exception("Cyclic Dependency:\r\n" + currentStack.Select(x => $" - {x}") //append dashes
.Aggregate((a, b) => $"{a}\r\n{b}")); //add new lines inbetween
}
}
return sorted_list;
bool Visit(TNode node, Stack stack)
{
if (visited.Contains(node))
{
if (!sorted.Contains(node))
{
return false;
}
}
else
{
visited.Add(node);
stack.Push(node);
foreach (var dep in dependencySelector(node))
if (!Visit(dep, stack))
return false;
sorted.Add(node);
sorted_list.Add(node);
stack.Pop();
}
return true;
}
}
///
/// Try to resolve and load the given assembly DLL.
///
/// Name of the assembly, of the type .
/// Directory to search the assembly from.
/// The loaded assembly.
/// True, if the assembly was found and loaded. Otherwise, false.
private static bool TryResolveDllAssembly(AssemblyName assemblyName, string directory, Func loader, out T assembly) where T : class
{
assembly = null;
var potentialDirectories = new List { directory };
potentialDirectories.AddRange(Directory.GetDirectories(directory, "*", SearchOption.AllDirectories));
foreach (string subDirectory in potentialDirectories)
{
string path = Path.Combine(subDirectory, $"{assemblyName.Name}.dll");
if (!File.Exists(path))
continue;
try
{
assembly = loader(path);
}
catch (Exception)
{
continue;
}
return true;
}
return false;
}
public static bool IsSubtypeOf(this TypeDefinition self, Type td)
{
if (self.FullName == td.FullName)
return true;
return self.FullName != "System.Object" && (self.BaseType?.Resolve()?.IsSubtypeOf(td) ?? false);
}
///
/// Try to resolve and load the given assembly DLL.
///
/// Name of the assembly, of the type .
/// Directory to search the assembly from.
/// The loaded assembly.
/// True, if the assembly was found and loaded. Otherwise, false.
public static bool TryResolveDllAssembly(AssemblyName assemblyName, string directory, out Assembly assembly)
{
return TryResolveDllAssembly(assemblyName, directory, Assembly.LoadFile, out assembly);
}
///
/// Try to resolve and load the given assembly DLL.
///
/// Name of the assembly, of the type .
/// Directory to search the assembly from.
/// The loaded assembly.
/// True, if the assembly was found and loaded. Otherwise, false.
public static bool TryResolveDllAssembly(AssemblyName assemblyName, string directory, ReaderParameters readerParameters, out AssemblyDefinition assembly)
{
return TryResolveDllAssembly(assemblyName, directory, s => AssemblyDefinition.ReadAssembly(s, readerParameters), out assembly);
}
///
/// Tries to create a file with the given name
///
/// Path of the file to create
/// File open mode
/// Resulting filestream
/// File access options
/// File share options
///
public static bool TryOpenFileStream(string path, FileMode mode, out FileStream fileStream, FileAccess access = FileAccess.ReadWrite, FileShare share = FileShare.Read)
{
try
{
fileStream = new FileStream(path, mode, access, share);
return true;
}
catch (IOException)
{
fileStream = null;
return false;
}
}
}
}