using System; using System.IO; using System.Text; using SabreTools.CommandLine; using SabreTools.CommandLine.Inputs; using SabreTools.Hashing; using SabreTools.IO.Extensions; using SabreTools.Serialization; using SabreTools.Serialization.Wrappers; namespace InfoPrint.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 _fileOnlyName = "file-only"; internal readonly FlagInput FileOnlyInput = new(_fileOnlyName, ["-f", "--file"], "Print to file only"); private const string _hashName = "hash"; internal readonly FlagInput HashInput = new(_hashName, ["-c", "--hash"], "Output file hashes"); #if NETCOREAPP private const string _jsonName = "json"; internal readonly FlagInput JsonInput = new(_jsonName, ["-j", "--json"], "Print info as JSON"); #endif #endregion /// /// Enable debug output for relevant operations /// public bool Debug { get; private set; } /// /// Output information to file only, skip printing to console /// public bool FileOnly { get; private set; } /// /// Print external file hashes /// public bool Hash { get; private set; } #if NETCOREAPP /// /// Enable JSON output /// public bool Json { get; private set; } #endif public MainFeature() : base(DisplayName, _flags, _description) { RequiresInputs = true; Add(DebugInput); Add(HashInput); Add(FileOnlyInput); #if NETCOREAPP Add(JsonInput); #endif } /// public override bool Execute() { // Get the options from the arguments Debug = GetBoolean(_debugName); Hash = GetBoolean(_hashName); FileOnly = GetBoolean(_fileOnlyName); #if NETCOREAPP Json = GetBoolean(_jsonName); #endif // Loop through the input paths for (int i = 0; i < Inputs.Count; i++) { string arg = Inputs[i]; PrintPathInfo(arg); } return true; } /// public override bool VerifyInputs() => Inputs.Count > 0; /// /// Wrapper to print information for a single path /// /// File or directory path private void PrintPathInfo(string path) { Console.WriteLine($"Checking possible path: {path}"); // Check if the file or directory exists if (File.Exists(path)) { PrintFileInfo(path); } else if (Directory.Exists(path)) { foreach (string file in path.SafeEnumerateFiles("*", SearchOption.AllDirectories)) { PrintFileInfo(file); } } else { Console.WriteLine($"{path} does not exist, skipping..."); } } /// /// Print information for a single file, if possible /// /// File path private void PrintFileInfo(string file) { Console.WriteLine($"Attempting to print info for {file}"); // Get the base info output name string filenameBase = $"info-{DateTime.Now:yyyy-MM-dd_HHmmss.ffff}"; // If we have the hash flag if (Hash) { var hashBuilder = PrintHashInfo(file); if (hashBuilder is not null) { // Create the output data string hashData = hashBuilder.ToString(); // Write the output data using var hsw = new StreamWriter(File.OpenWrite($"{filenameBase}.hashes")); hsw.WriteLine(hashData); hsw.Flush(); } } try { using Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // Read the first 8 bytes byte[] magic = stream.PeekBytes(8); // Get the file type string extension = Path.GetExtension(file).TrimStart('.'); WrapperType ft = WrapperFactory.GetFileType(magic ?? [], extension); // Print out the file format Console.WriteLine($"File format found: {ft}"); // Setup the wrapper to print var wrapper = WrapperFactory.CreateWrapper(ft, stream); // If we don't have a wrapper if (wrapper is null) { Console.WriteLine($"Either {ft} is not supported or something went wrong during parsing!"); Console.WriteLine(); return; } #if NETCOREAPP // If we have the JSON flag if (Json) { // Create the output data string serializedData = wrapper.ExportJSON(); // Write the output data using var jsw = new StreamWriter(File.OpenWrite($"{filenameBase}.json")); jsw.WriteLine(serializedData); jsw.Flush(); } #endif // Create the output data var builder = wrapper.ExportStringBuilder(); if (builder is null) { Console.WriteLine("No item information could be generated"); return; } // Only print to console if enabled if (!FileOnly) Console.WriteLine(builder); using var sw = new StreamWriter(File.OpenWrite($"{filenameBase}.txt")); sw.WriteLine(file); sw.WriteLine(); sw.WriteLine(builder.ToString()); sw.Flush(); } catch (Exception ex) { Console.WriteLine(Debug ? ex : "[Exception opening file, please try again]"); Console.WriteLine(); } } /// /// Print hash information for a single file, if possible /// /// File path /// StringBuilder representing the hash information, if possible private StringBuilder? PrintHashInfo(string file) { // Ignore missing files if (!File.Exists(file)) return null; Console.WriteLine($"Attempting to hash {file}, this may take a while..."); 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 null; } // Output subset of available hashes var builder = new StringBuilder(); if (hashes.TryGetValue(HashType.CRC16, out string? crc16) && crc16 is not null) builder.AppendLine($"CRC-16 checksum: {crc16}"); if (hashes.TryGetValue(HashType.CRC32, out string? crc32) && crc32 is not null) builder.AppendLine($"CRC-32 checksum: {crc32}"); if (hashes.TryGetValue(HashType.CRC64, out string? crc64) && crc64 is not null) builder.AppendLine($"CRC-64 checksum: {crc64}"); if (hashes.TryGetValue(HashType.MD2, out string? md2) && md2 is not null) builder.AppendLine($"MD2 hash: {md2}"); if (hashes.TryGetValue(HashType.MD4, out string? md4) && md4 is not null) builder.AppendLine($"MD4 hash: {md4}"); if (hashes.TryGetValue(HashType.MD5, out string? md5) && md5 is not null) builder.AppendLine($"MD5 hash: {md5}"); if (hashes.TryGetValue(HashType.RIPEMD128, out string? ripemd128) && ripemd128 is not null) builder.AppendLine($"RIPEMD-128 hash: {ripemd128}"); if (hashes.TryGetValue(HashType.RIPEMD160, out string? ripemd160) && ripemd160 is not null) builder.AppendLine($"RIPEMD-160 hash: {ripemd160}"); if (hashes.TryGetValue(HashType.SHA1, out string? sha1) && sha1 is not null) builder.AppendLine($"SHA-1 hash: {sha1}"); if (hashes.TryGetValue(HashType.SHA256, out string? sha256) && sha256 is not null) builder.AppendLine($"SHA-256 hash: {sha256}"); if (hashes.TryGetValue(HashType.SHA384, out string? sha384) && sha384 is not null) builder.AppendLine($"SHA-384 hash: {sha384}"); if (hashes.TryGetValue(HashType.SHA512, out string? sha512) && sha512 is not null) builder.AppendLine($"SHA-512 hash: {sha512}"); return builder; } catch (Exception ex) { Console.WriteLine(Debug ? ex : "[Exception opening file, please try again]"); return null; } } } }