using System; using System.IO; using System.Text; using System.Linq; using System.Collections.Generic; using Mono.Cecil; using System.Runtime.CompilerServices; #if NET3 using File = Net3_Proxy.File; #endif namespace IPA.Utilities { /// /// A class providing static utility functions that in any other language would just *exist*. /// public static class Utils { /// /// Converts a hex string to a byte array. /// /// the hex stream /// the corresponding byte array public static byte[] StringToByteArray(string hex) { int numberChars = hex.Length; byte[] bytes = new byte[numberChars / 2]; for (int i = 0; i < numberChars; i += 2) bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); return bytes; } /// /// Converts a byte array to a hex string. /// /// the byte array /// the hex form of the array public static string ByteArrayToString(byte[] ba) { StringBuilder hex = new StringBuilder(ba.Length * 2); foreach (byte b in ba) hex.AppendFormat("{0:x2}", b); return hex.ToString(); } // Copyright (c) 2008-2013 Hafthor Stefansson // Distributed under the MIT/X11 software license // Ref: http://www.opensource.org/licenses/mit-license.php. // From: https://stackoverflow.com/a/8808245/3117125 /// /// Uses unsafe code to compare 2 byte arrays quickly. /// /// array 1 /// array 2 /// whether or not they are byte-for-byte equal public static unsafe bool UnsafeCompare(byte[] a1, byte[] a2) { if (a1 == a2) return true; if (a1 == null || a2 == null || a1.Length != a2.Length) return false; fixed (byte* p1 = a1, p2 = a2) { byte* x1 = p1, x2 = p2; int l = a1.Length; for (int i = 0; i < l / 8; i++, x1 += 8, x2 += 8) if (*((long*)x1) != *((long*)x2)) return false; if ((l & 4) != 0) { if (*((int*)x1) != *((int*)x2)) return false; x1 += 4; x2 += 4; } if ((l & 2) != 0) { if (*((short*)x1) != *((short*)x2)) return false; x1 += 2; x2 += 2; } if ((l & 1) != 0) if (*x1 != *x2) return false; return true; } } /// /// Gets a path relative to the provided folder. /// /// the file to relativize /// the source folder /// a path to get from to public static string GetRelativePath(string file, string folder) { Uri pathUri = new Uri(file); // Folders must end in a slash if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString())) { folder += Path.DirectorySeparatorChar; } Uri folderUri = new Uri(folder); return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar)); } /// /// Copies all files from to . /// /// the source directory /// the destination directory /// the filename of the file to append together /// a delegate called when there is an error copying. Return true to keep going. public static void CopyAll(DirectoryInfo source, DirectoryInfo target, string appendFileName = "", Func onCopyException = null) { if (source.FullName.ToLower() == target.FullName.ToLower()) { return; } // Check if the target directory exists, if not, create it. if (Directory.Exists(target.FullName) == false) { Directory.CreateDirectory(target.FullName); } // Copy each file into it's new directory. foreach (FileInfo fi in source.GetFiles()) { try { if (fi.Name == appendFileName) File.AppendAllLines(Path.Combine(target.ToString(), fi.Name), File.ReadAllLines(fi.FullName)); else fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true); } catch (Exception e) { var keepOn = onCopyException?.Invoke(e, fi); if (!keepOn.Unwrap()) throw; } } // Copy each subdirectory using recursion. foreach (DirectoryInfo diSourceSubDir in source.GetDirectories()) { DirectoryInfo nextTargetSubDir = target.CreateSubdirectory(diSourceSubDir.Name); CopyAll(diSourceSubDir, nextTargetSubDir, appendFileName, onCopyException); } } /// /// Whether you can safely use without Mono throwing a fit. /// /// if you can use safely, otherwise public static bool CanUseDateTimeNowSafely { get; private set; } = true; private static bool DateTimeSafetyUnknown = true; private static ulong UnsafeAdvanceTicks = 1; /// /// Gets the current if supported, otherwise, if Mono would throw a fit, /// returns plus some value, such that each time it is called /// the value will be greater than the previous result. Not suitable for timing. /// /// the current if supported, otherwise some indeterminant increasing value. public static DateTime CurrentTime() { if (DateTimeSafetyUnknown) { DateTime time = DateTime.MinValue; try { time = DateTime.Now; } catch (TimeZoneNotFoundException) { // Mono did a fucky wucky and we need to avoid this call CanUseDateTimeNowSafely = false; } DateTimeSafetyUnknown = false; return time; } else { if (CanUseDateTimeNowSafely) return DateTime.Now; else return DateTime.MinValue.AddTicks((long)UnsafeAdvanceTicks++); // return MinValue as a fallback } } /// /// Compares a pair of s ignoring both the prerelease and build fields. /// /// the left value /// the right value /// < 0 if l is less than r, 0 if they are equal in the numeric portion, or > 0 if l is greater than r public static int VersionCompareNoPrerelease(SemVer.Version l, SemVer.Version r) { var cmpVal = l.Major - r.Major; if (cmpVal != 0) return cmpVal; cmpVal = l.Minor - r.Minor; if (cmpVal != 0) return cmpVal; cmpVal = l.Patch - r.Patch; return cmpVal; } internal static bool HasInterface(this TypeDefinition type, string interfaceFullName) { return (type?.Interfaces?.Any(i => i.InterfaceType.FullName == interfaceFullName) ?? false) || (type?.Interfaces?.Any(t => HasInterface(t?.InterfaceType?.Resolve(), interfaceFullName)) ?? false); } #if NET4 [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static IEnumerable StrJP(this IEnumerable a) => a; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static IEnumerable StrJP(this IEnumerable a) => a.Select(o => $"{o}" /* safer than .ToString() */); #endif #if NET3 [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static string[] StrJP(this IEnumerable a) => a.ToArray(); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static string[] StrJP(this IEnumerable a) => a.Select(o => $"{o}" /* safer than .ToString() */).ToArray(); #endif } }