using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.CommandLine;
using SabreTools.CommandLine.Inputs;
using SabreTools.Hashing;
namespace Hasher.Features
{
internal sealed class MainFeature : Feature
{
#region Feature Definition
public const string DisplayName = "main";
/// Flags are unused
private static readonly string[] _flags = [];
/// Description is unused
private const string _description = "";
#endregion
#region Inputs
private const string _debugName = "debug";
internal readonly FlagInput DebugInput = new(_debugName, ["-d", "--debug"], "Enable debug mode");
private const string _typeName = "type";
internal readonly StringListInput TypeInput = new(_typeName, ["-t", "--type"], "Select included hashes");
#endregion
public MainFeature()
: base(DisplayName, _flags, _description)
{
RequiresInputs = true;
Add(DebugInput);
Add(TypeInput);
}
///
public override bool Execute()
{
// If there are no inputs
if (Inputs.Count == 0)
{
Console.WriteLine("No inputs were provided");
return false;
}
// Get the required variables
bool debug = GetBoolean(_debugName);
var hashTypeStrings = GetStringList(_typeName);
// Get the hash types from the inputs
List hashTypes = GetHashTypes(hashTypeStrings);
if (hashTypes.Count == 0)
{
Console.WriteLine($"The provided hash types were not recognized: {string.Join(", ", [.. hashTypeStrings])}");
return false;
}
// Loop through all of the input files
for (int i = 0; i < Inputs.Count; i++)
{
string arg = Inputs[i];
PrintPathHashes(arg, hashTypes, debug);
}
return true;
}
///
public override bool VerifyInputs() => true;
///
/// Derive a list of hash types from a list of strings
///
private static List GetHashTypes(List types)
{
// No provided types
if (types.Count == 0)
return [.. HashType.StandardHashes];
// All possible hashes
if (types.Contains("all"))
return [.. HashType.AllHashes];
// If a list is defined
List hashTypes = [];
foreach (string typeString in types)
{
HashType? hashType = ToHashType(typeString);
if (hashType is not null && !hashTypes.Contains(hashType))
hashTypes.Add(item: hashType);
}
return hashTypes;
}
///
/// Get the hash type associated to a string, if possible
///
/// TODO: This should be automated instead of hardcoded
private static HashType? ToHashType(string? str)
{
// Ignore invalid strings
if (string.IsNullOrEmpty(str))
return null;
// Normalize the string before matching
str = str!.Replace("-", string.Empty);
str = str.Replace(" ", string.Empty);
str = str.Replace("/", "_");
str = str.Replace("\\", "_");
str = str.ToLowerInvariant();
// Match based on potential names
return str switch
{
"adler" or "adler32" => HashType.Adler32,
#if NET7_0_OR_GREATER
"blake3" => HashType.BLAKE3,
#endif
"crc1_0" or "crc1_zero" => HashType.CRC1_ZERO,
"crc1_1" or "crc1_one" => HashType.CRC1_ONE,
"crc3_gsm" => HashType.CRC3_GSM,
"crc3_rohc" => HashType.CRC3_ROHC,
"crc4_g704" or "crc4_itu" => HashType.CRC4_G704,
"crc4_interlaken" => HashType.CRC4_INTERLAKEN,
"crc5_epc" or "crc5_epcc1g2" => HashType.CRC5_EPCC1G2,
"crc5_g704" or "crc5_itu" => HashType.CRC5_G704,
"crc5_usb" => HashType.CRC5_USB,
"crc6_cdma2000a" => HashType.CRC6_CDMA2000A,
"crc6_cdma2000b" => HashType.CRC6_CDMA2000B,
"crc6_darc" => HashType.CRC6_DARC,
"crc6_g704" or "crc6_itu" => HashType.CRC6_G704,
"crc6_gsm" => HashType.CRC6_GSM,
"crc7" or "crc7_mmc" => HashType.CRC7_MMC,
"crc7_rohc" => HashType.CRC7_ROHC,
"crc7_umts" => HashType.CRC7_UMTS,
"crc8" => HashType.CRC8,
"crc8_autosar" => HashType.CRC8_AUTOSAR,
"crc8_bluetooth" => HashType.CRC8_BLUETOOTH,
"crc8_cdma2000" => HashType.CRC8_CDMA2000,
"crc8_darc" => HashType.CRC8_DARC,
"crc8_dvbs2" => HashType.CRC8_DVBS2,
"crc8_gsma" => HashType.CRC8_GSMA,
"crc8_gsmb" => HashType.CRC8_GSMB,
"crc8_hitag" => HashType.CRC8_HITAG,
"crc8_i4321" or "crc8_itu" => HashType.CRC8_I4321,
"crc8_icode" => HashType.CRC8_ICODE,
"crc8_lte" => HashType.CRC8_LTE,
"crc8_maximdow" or "crc8_maxim" or "dowcrc" => HashType.CRC8_MAXIMDOW,
"crc8_mifaremad" => HashType.CRC8_MIFAREMAD,
"crc8_nrsc5" => HashType.CRC8_NRSC5,
"crc8_opensafety" => HashType.CRC8_OPENSAFETY,
"crc8_rohc" => HashType.CRC8_ROHC,
"crc8_saej1850" => HashType.CRC8_SAEJ1850,
"crc8_smbus" => HashType.CRC8_SMBUS,
"crc8_tech3250" or "crc8_aes" or "crc8_ebu" => HashType.CRC8_TECH3250,
"crc8_wcdma" => HashType.CRC8_WCDMA,
"crc10_atm" or "crc10" or "crc10_i610" => HashType.CRC10_ATM,
"crc10_cdma2000" => HashType.CRC10_CDMA2000,
"crc10_gsm" => HashType.CRC10_GSM,
"crc11_flexray" or "crc11" => HashType.CRC11_FLEXRAY,
"crc11_umts" => HashType.CRC11_UMTS,
"crc12_cdma2000" => HashType.CRC12_CDMA2000,
"crc12_dect" or "xcrc12" => HashType.CRC12_DECT,
"crc12_gsm" => HashType.CRC12_GSM,
"crc12_umts" or "crc12_3gpp" => HashType.CRC12_UMTS,
"crc13_bbc" => HashType.CRC13_BBC,
"crc14_darc" => HashType.CRC14_DARC,
"crc14_gsm" => HashType.CRC14_GSM,
"crc15_can" or "crc15" => HashType.CRC15_CAN,
"crc15_mpt1327" => HashType.CRC15_MPT1327,
"crc16" => HashType.CRC16,
"crc16_arc" or "arc" or "crc16_lha" or "crcibm" => HashType.CRC16_ARC,
"crc16_cdma2000" => HashType.CRC16_CDMA2000,
"crc16_cms" => HashType.CRC16_CMS,
"crc16_dds110" => HashType.CRC16_DDS110,
"crc16_dectr" or "rcrc16" => HashType.CRC16_DECTR,
"crc16_dectx" or "xcrc16" => HashType.CRC16_DECTX,
"crc16_dnp" => HashType.CRC16_DNP,
"crc16_en13757" => HashType.CRC16_EN13757,
"crc16_genibus" or "crc16_darc" or "crc16_epc" or "crc16_epcc1g2" or "crc16_icode" => HashType.CRC16_GENIBUS,
"crc16_gsm" => HashType.CRC16_GSM,
"crc16_ibm3740" or "crc16_autosar" or "crc16_cittfalse" => HashType.CRC16_IBM3740,
"crc16_ibmsdlc" or "crc16_isohdlc" or "crc16_isoiec144433b" or "crc16_x25" or "crcb" or "x25" => HashType.CRC16_IBMSDLC,
"crc16_isoiec144433a" or "crca" => HashType.CRC16_ISOIEC144433A,
"crc16_kermit" or "crc16_bluetooth" or "crc16_ccitt" or "crc16_ccitttrue" or "crc16_v41lsb" or "crcccitt" or "kermit" => HashType.CRC16_KERMIT,
"crc16_lj1200" => HashType.CRC16_LJ1200,
"crc16_m17" => HashType.CRC16_M17,
"crc16_maximdow" or "crc16_maxim" => HashType.CRC16_MAXIMDOW,
"crc16_mcrf4xx" => HashType.CRC16_MCRF4XX,
"crc16_modbus" or "modbus" => HashType.CRC16_MODBUS,
"crc16_nrsc5" => HashType.CRC16_NRSC5,
"crc16_opensafetya" => HashType.CRC16_OPENSAFETYA,
"crc16_opensafetyb" => HashType.CRC16_OPENSAFETYB,
"crc16_profibus" or "crc16_iec611582" => HashType.CRC16_PROFIBUS,
"crc16_riello" => HashType.CRC16_RIELLO,
"crc16_spifujitsu" or "crc16_augccitt" => HashType.CRC16_SPIFUJITSU,
"crc16_t10dif" => HashType.CRC16_T10DIF,
"crc16_teledisk" => HashType.CRC16_TELEDISK,
"crc16_tms37157" => HashType.CRC16_TMS37157,
"crc16_umts" or "crc16_buypass" or "crc16_verifone" => HashType.CRC16_UMTS,
"crc16_usb" => HashType.CRC16_USB,
"crc16_xmodem" or "crc16_acorn" or "crc16_lte" or "crc16_v41msb" or "xmodem" or "zmodem" => HashType.CRC16_XMODEM,
"crc17_canfd" => HashType.CRC17_CANFD,
"crc21_canfd" => HashType.CRC21_CANFD,
"crc24_ble" => HashType.CRC24_BLE,
"crc24_flexraya" => HashType.CRC24_FLEXRAYA,
"crc24_flexrayb" => HashType.CRC24_FLEXRAYB,
"crc24_interlaken" => HashType.CRC24_INTERLAKEN,
"crc24_ltea" => HashType.CRC24_LTEA,
"crc24_lteb" => HashType.CRC24_LTEB,
"crc24_openpgp" => HashType.CRC24_OPENPGP,
"crc24_os9" => HashType.CRC24_OS9,
"crc30_cdma" => HashType.CRC30_CDMA,
"crc31_philips" => HashType.CRC31_PHILIPS,
"crc32" => HashType.CRC32,
"crc32_aixm" => HashType.CRC32_AIXM,
"crc32_autosar" => HashType.CRC32_AUTOSAR,
"crc32_base91d" => HashType.CRC32_BASE91D,
"crc32_bzip2" => HashType.CRC32_BZIP2,
"crc32_cdromedc" => HashType.CRC32_CDROMEDC,
"crc32_cksum" => HashType.CRC32_CKSUM,
"crc32_dvdromedc" => HashType.CRC32_DVDROMEDC,
"crc32_iscsi" => HashType.CRC32_ISCSI,
"crc32_isohdlc" => HashType.CRC32_ISOHDLC,
"crc32_jamcrc" => HashType.CRC32_JAMCRC,
"crc32_mef" => HashType.CRC32_MEF,
"crc32_mpeg2" => HashType.CRC32_MPEG2,
"crc32_xfer" => HashType.CRC32_XFER,
"crc40_gsm" => HashType.CRC40_GSM,
"crc64" => HashType.CRC64,
"crc64_ecma182" => HashType.CRC64_ECMA182,
"crc64_goiso" => HashType.CRC64_GOISO,
"crc64_ms" => HashType.CRC64_MS,
"crc64_nvme" => HashType.CRC64_NVME,
"crc64_redis" => HashType.CRC64_REDIS,
"crc64_we" => HashType.CRC64_WE,
"crc64_xz" => HashType.CRC64_XZ,
#if NET7_0_OR_GREATER
"crc82_darc" => HashType.CRC82_DARC,
#endif
"fletcher16" => HashType.Fletcher16,
"fletcher32" => HashType.Fletcher32,
"fletcher64" => HashType.Fletcher64,
"fnv0_32" => HashType.FNV0_32,
"fnv0_64" => HashType.FNV0_64,
"fnv1_32" => HashType.FNV1_32,
"fnv1_64" => HashType.FNV1_64,
"fnv1a_32" => HashType.FNV1a_32,
"fnv1a_64" => HashType.FNV1a_64,
"meka" or "mekacrc" or "meka_crc" => HashType.MekaCrc,
"md2" => HashType.MD2,
"md4" => HashType.MD4,
"md5" => HashType.MD5,
"ripemd128" => HashType.RIPEMD128,
"ripemd160" => HashType.RIPEMD160,
"ripemd256" => HashType.RIPEMD256,
"ripemd320" => HashType.RIPEMD320,
"sha1" => HashType.SHA1,
"sha256" => HashType.SHA256,
"sha384" => HashType.SHA384,
"sha512" => HashType.SHA512,
#if NET8_0_OR_GREATER
"sha3_256" => HashType.SHA3_256,
"sha3_384" => HashType.SHA3_384,
"sha3_512" => HashType.SHA3_512,
"shake128" => HashType.SHAKE128,
"shake256" => HashType.SHAKE256,
#endif
"spamsum" => HashType.SpamSum,
"tiger128_3" or "tiger_128_3" => HashType.Tiger128_3,
"tiger128_4" or "tiger_128_4" => HashType.Tiger128_4,
"tiger160_3" or "tiger_160_3" => HashType.Tiger160_3,
"tiger160_4" or "tiger_160_4" => HashType.Tiger160_4,
"tiger192_3" or "tiger_192_3" => HashType.Tiger192_3,
"tiger192_4" or "tiger_192_4" => HashType.Tiger192_4,
"tiger2_128_3" => HashType.Tiger2_128_3,
"tiger2_128_4" => HashType.Tiger2_128_4,
"tiger2_160_3" => HashType.Tiger2_160_3,
"tiger2_160_4" => HashType.Tiger2_160_4,
"tiger2_192_3" => HashType.Tiger2_192_3,
"tiger2_192_4" => HashType.Tiger2_192_4,
"xxh" or "xxh32" or "xxh_32" or "xxhash" or "xxhash32" or "xxhash_32" => HashType.XxHash32,
"xxh64" or "xxh_64" or "xxhash64" or "xxhash_64" => HashType.XxHash64,
#if NET462_OR_GREATER || NETCOREAPP
"xxh3" or "xxh3_64" or "xxhash3" or "xxhash_3" => HashType.XxHash3,
"xxh128" or "xxh_128" or "xxhash128" or "xxhash_128" => HashType.XxHash128,
#endif
_ => null,
};
}
///
/// Wrapper to print hashes for a single path
///
/// File or directory path
/// Set of hashes to retrieve
/// Enable debug output
private static void PrintPathHashes(string path, List hashTypes, bool debug)
{
Console.WriteLine($"Checking possible path: {path}");
// Check if the file or directory exists
if (File.Exists(path))
{
PrintFileHashes(path, hashTypes, debug);
}
else if (Directory.Exists(path))
{
foreach (string file in Directory.GetFiles(path, "*", SearchOption.AllDirectories))
{
PrintFileHashes(file, hashTypes, debug);
}
}
else
{
Console.WriteLine($"{path} does not exist, skipping...");
}
}
///
/// Print information for a single file, if possible
///
/// File path
/// Set of hashes to retrieve
/// Enable debug output
private static void PrintFileHashes(string file, List hashTypes, bool debug)
{
Console.WriteLine($"Attempting to hash {file}, this may take a while...");
Console.WriteLine();
// If the file doesn't exist
if (!File.Exists(file))
{
Console.WriteLine($"{file} does not exist, skipping...");
return;
}
try
{
// Get all file hashes for flexibility
var hashes = HashTool.GetFileHashes(file);
if (hashes is null)
{
if (debug) Console.WriteLine($"Hashes for {file} could not be retrieved");
return;
}
// Output subset of available hashes
var builder = new StringBuilder();
foreach (HashType hashType in hashTypes)
{
if (hashes.TryGetValue(hashType, out string? hash) && hash is not null)
builder.AppendLine($"{hashType.Name}: {hash}");
}
// Create and print the output data
string hashData = builder.ToString();
Console.WriteLine(hashData);
}
catch (Exception ex)
{
Console.WriteLine(debug ? ex : "[Exception opening file, please try again]");
return;
}
}
}
}