using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using Mono.Cecil;
using MonoMod.Utils;
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);
///
/// Returns the parent directory of a path, optionally specifying the amount of levels.
///
/// The path to get the parent directory of.
/// The amount of levels to traverse. Defaults to 1
/// The parent directory.
public static string ParentDirectory(string path, int levels = 1)
{
for (int i = 0; i < levels; i++)
path = Path.GetDirectoryName(path);
return path;
}
///
/// 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[] potentialPaths = new[]
{
$"{assemblyName.Name}.dll",
$"{assemblyName.Name}.exe"
};
foreach (var potentialPath in potentialPaths)
{
string path = Path.Combine(subDirectory, potentialPath);
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;
}
}
public static IEnumerable EnumerateAllMethods(this TypeDefinition type)
{
var currentType = type;
while (currentType != null)
{
foreach (var method in currentType.Methods)
yield return method;
currentType = currentType.BaseType?.Resolve();
}
}
public static string ByteArrayToString(byte[] data)
{
StringBuilder builder = new StringBuilder(data.Length * 2);
foreach (byte b in data)
builder.AppendFormat("{0:x2}", b);
return builder.ToString();
}
public static Platform CurrentPlatform { get; private set; } = CheckPlatform();
// Adapted from https://github.com/MonoMod/MonoMod.Common/blob/master/Utils/PlatformHelper.cs#L13
private static Platform CheckPlatform()
{
var pPlatform = typeof(Environment).GetProperty("Platform", BindingFlags.NonPublic | BindingFlags.Static);
string platId = pPlatform != null ? pPlatform.GetValue(null, new object[0]).ToString() : Environment.OSVersion.Platform.ToString();
platId = platId.ToLowerInvariant();
var cur = Platform.Unknown;
if (platId.Contains("win"))
cur = Platform.Windows;
else if (platId.Contains("mac") || platId.Contains("osx"))
cur = Platform.MacOS;
else if (platId.Contains("lin") || platId.Contains("unix"))
cur = Platform.Linux;
if (IntPtr.Size == 8)
cur |= Platform.Bits64;
return cur;
}
}
}