using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace SabreTools.Core.Tools
{
///
/// Static utility functions used throughout the library
///
public static class Utilities
{
///
/// Convert a byte array to a hex string
///
/// Byte array to convert
/// Hex string representing the byte array
/// http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa
public static string ByteArrayToString(byte[] bytes)
{
// If we get null in, we send null out
if (bytes == null)
return null;
try
{
string hex = BitConverter.ToString(bytes);
return hex.Replace("-", string.Empty).ToLowerInvariant();
}
catch
{
return null;
}
}
///
/// Convert a hex string to a byte array
///
/// Hex string to convert
/// Byte array represenging the hex string
/// http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa
public static byte[] StringToByteArray(string hex)
{
// If we get null in, we send null out
if (hex == null)
return null;
try
{
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;
}
catch
{
return null;
}
}
///
/// Get a sanitized size from an input string
///
/// String to get value from
/// Size as a long?, if possible
public static long? CleanLong(string input)
{
long? size = null;
if (input != null && input.Contains("0x"))
size = Convert.ToInt64(input, 16);
else if (input != null)
{
if (Int64.TryParse(input, out long longSize))
size = longSize;
}
return size;
}
///
/// Convert .NET DateTime to MS-DOS date format
///
/// .NET DateTime object to convert
/// UInt32 representing the MS-DOS date
///
/// Adapted from 7-zip Source Code: CPP/Windows/TimeUtils.cpp:FileTimeToDosTime
///
public static uint ConvertDateTimeToMsDosTimeFormat(DateTime dateTime)
{
uint year = (uint)((dateTime.Year - 1980) % 128);
uint mon = (uint)dateTime.Month;
uint day = (uint)dateTime.Day;
uint hour = (uint)dateTime.Hour;
uint min = (uint)dateTime.Minute;
uint sec = (uint)dateTime.Second;
return (year << 25) | (mon << 21) | (day << 16) | (hour << 11) | (min << 5) | (sec >> 1);
}
///
/// Convert MS-DOS date format to .NET DateTime
///
/// UInt32 representing the MS-DOS date to convert
/// .NET DateTime object representing the converted date
///
/// Adapted from 7-zip Source Code: CPP/Windows/TimeUtils.cpp:DosTimeToFileTime
///
public static DateTime ConvertMsDosTimeFormatToDateTime(uint msDosDateTime)
{
return new DateTime((int)(1980 + (msDosDateTime >> 25)), (int)((msDosDateTime >> 21) & 0xF), (int)((msDosDateTime >> 16) & 0x1F),
(int)((msDosDateTime >> 11) & 0x1F), (int)((msDosDateTime >> 5) & 0x3F), (int)((msDosDateTime & 0x1F) * 2));
}
///
/// Get a proper romba sub path
///
/// SHA-1 hash to get the path for
/// Positive value representing the depth of the depot
/// Subfolder path for the given hash
public static string GetDepotPath(string hash, int depth)
{
// If the hash isn't the right size, then we return null
if (hash.Length != Constants.SHA1Length)
return null;
// Cap the depth between 0 and 20, for now
if (depth < 0)
depth = 0;
else if (depth > Constants.SHA1ZeroBytes.Length)
depth = Constants.SHA1ZeroBytes.Length;
// Loop through and generate the subdirectory
string path = string.Empty;
for (int i = 0; i < depth; i++)
{
path += hash.Substring(i * 2, 2) + Path.DirectorySeparatorChar;
}
// Now append the filename
path += $"{hash}.gz";
return path;
}
///
/// Get if the given path has a valid DAT extension
///
/// Path to check
/// True if the extension is valid, false otherwise
public static bool HasValidDatExtension(string path)
{
// Get the extension from the path, if possible
string ext = Path.GetExtension(path).TrimStart('.');
// Check against the list of known DAT extensions
switch (ext)
{
case "csv":
case "dat":
case "json":
case "md5":
case "ripemd160":
case "sfv":
case "sha1":
case "sha256":
case "sha384":
case "sha512":
case "ssv":
case "tsv":
case "txt":
case "xml":
return true;
default:
return false;
}
}
/// Indicates whether the specified array is null or has a length of zero
///
/// The array to test
/// true if the array parameter is null or has a length of zero; otherwise, false.
/// https://stackoverflow.com/questions/8560106/isnullorempty-equivalent-for-array-c-sharp
public static bool IsNullOrEmpty(this Array array)
{
return array == null || array.Length == 0;
}
///
/// Remove all chars that are considered path unsafe
///
/// Input string to clean
/// Cleaned string
public static string RemovePathUnsafeCharacters(string s)
{
List invalidPath = Path.GetInvalidPathChars().ToList();
return new string(s.Where(c => !invalidPath.Contains(c)).ToArray());
}
///
/// Returns if the first byte array starts with the second array
///
/// First byte array to compare
/// Second byte array to compare
/// True if the input arrays should match exactly, false otherwise (default)
/// True if the first byte array starts with the second, false otherwise
public static bool StartsWith(this byte[] arr1, byte[] arr2, bool exact = false)
{
// If we have any invalid inputs, we return false
if (arr1 == null || arr2 == null
|| arr1.Length == 0 || arr2.Length == 0
|| arr2.Length > arr1.Length
|| (exact && arr1.Length != arr2.Length))
{
return false;
}
// Otherwise, loop through and see
for (int i = 0; i < arr2.Length; i++)
{
if (arr1[i] != arr2[i])
return false;
}
return true;
}
}
}