Migrate to Spectre.Console.Cli.

This commit is contained in:
2025-08-16 20:08:00 +01:00
parent 8ac6e1ccc4
commit d08fe04df0
44 changed files with 7373 additions and 10151 deletions

View File

@@ -1,18 +1,23 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Aaru" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/Aaru/bin/Debug/net9.0/aaru.exe" />
<option name="PROGRAM_PARAMETERS" value="m dump --first-pregap --metadata false -p 1 --fix-subchannel-position --retry-subchannel --fix-subchannel --fix-subchannel-crc --generate-subchannels --eject -O compress=false --trim=false --title-keys=false g: &quot;Super Mario Galaxy 2 for Beginners (RVL-9B4P-EUR-B0).aif&quot;" />
<option name="WORKING_DIRECTORY" value="F:/" />
<option name="EXE_PATH" value="$PROJECT_DIR$/Aaru/bin/Debug/net10.0/aaru" />
<option name="PROGRAM_PARAMETERS" value="archive extract --help" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Aaru/bin/Debug/net10.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="ENV_FILE_PATHS" value="" />
<option name="REDIRECT_INPUT_PATH" value="" />
<option name="PTY_MODE" value="Auto" />
<option name="MIXED_MODE_DEBUG" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="AUTO_ATTACH_CHILDREN" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Aaru/Aaru.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="0" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net9.0" />
<option name="PROJECT_TFM" value="net10.0" />
<method v="2">
<option name="Build" />
</method>

File diff suppressed because it is too large Load Diff

View File

@@ -60,9 +60,6 @@
</data>
<data name="Automation_Device_Serial_Number" xml:space="preserve">
<value>Número de serie del dispositivo de automatización</value>
</data>
<data name="Barcode_of_the_media_by_image" xml:space="preserve">
<value>Código de barras del medio representado por la imagen.</value>
</data>
<data name="Base_Mechanical_Serial_Number" xml:space="preserve">
<value>Número de serie de la mecánica base</value>
@@ -621,9 +618,6 @@
</data>
<data name="Decoder_for_sector_tag_type_0_not_yet_implemented_sorry" xml:space="preserve">
<value>Aún no se ha implementado un descodificador para la etiqueta de sector "{0}".</value>
</data>
<data name="Decode_media_tags" xml:space="preserve">
<value>Descodificar etiquetas del medio.</value>
</data>
<data name="Decode_sector_tags" xml:space="preserve">
<value>Descodificar etiquetas de sector.</value>
@@ -1555,9 +1549,6 @@
</data>
<data name="Media_image_format_statistics" xml:space="preserve">
<value>Estadísticas de formatos de imagen de medio</value>
</data>
<data name="Media_image_path" xml:space="preserve">
<value>Ruta de la imagen</value>
</data>
<data name="Media_Info_Command_Description" xml:space="preserve">
<value>Obtiene información sobre el medio introducido en un dispositivo.</value>
@@ -1630,9 +1621,6 @@
</data>
<data name="Migrating_local_database" xml:space="preserve">
<value>Migrando base de datos local...</value>
</data>
<data name="Model_of_drive_read_the_media_by_image" xml:space="preserve">
<value>Modelo de la uinidad usada para leer el medio representado en la imagen.</value>
</data>
<data name="Model_of_media_by_image" xml:space="preserve">
<value>Modelo del medio representado en la imagen.</value>
@@ -2104,9 +2092,6 @@
</data>
<data name="Tape_argument_input_help" xml:space="preserve">
<value>Al usarse indica que la entrada es una carpeta conteniendo los ficheros ordenados alfabéticamente extraídos de una cinta lineal de bloques de tamaño fijo (por ej. una unidad de cinta SCSI).</value>
</data>
<data name="Tape_block_size_argument_help" xml:space="preserve">
<value>Sólo usado para cintas, indica el tamaño del bloque. Los ficheros en la carpeta cuyo tamaño no sea múltiple de este serán ignorados.</value>
</data>
<data name="Text_Authors" xml:space="preserve">
<value>Desarrolladores:

View File

@@ -505,9 +505,6 @@ In you are unsure, please press N to not continue.</value>
</data>
<data name="Searches_and_interprets_partitions" xml:space="preserve">
<value>Searches and interprets partitions.</value>
</data>
<data name="Media_image_path" xml:space="preserve">
<value>Media image path</value>
</data>
<data name="Image_format_not_identified_not_proceeding_with_analysis" xml:space="preserve">
<value>Image format not identified, not proceeding with analysis.</value>
@@ -897,9 +894,6 @@ In you are unsure, please press N to not continue.</value>
</data>
<data name="Manufacturer_of_drive_read_the_media_by_image" xml:space="preserve">
<value>Manufacturer of the drive used to read the media represented by the image.</value>
</data>
<data name="Model_of_drive_read_the_media_by_image" xml:space="preserve">
<value>Model of the drive used to read the media represented by the image.</value>
</data>
<data name="Firmware_revision_of_drive_read_the_media_by_image" xml:space="preserve">
<value>Firmware revision of the drive used to read the media represented by the image.</value>
@@ -912,9 +906,6 @@ In you are unsure, please press N to not continue.</value>
</data>
<data name="Format_of_the_output_image_as_plugin_name_or_plugin_id" xml:space="preserve">
<value>Format of the output image, as plugin name or plugin id. If not present, will try to detect it from output image extension.</value>
</data>
<data name="Barcode_of_the_media_by_image" xml:space="preserve">
<value>Barcode of the media represented by the image.</value>
</data>
<data name="Last_media_of_sequence_by_image" xml:space="preserve">
<value>Last media of the sequence the media represented by the image corresponds to.</value>
@@ -1171,9 +1162,6 @@ In you are unsure, please press N to not continue.</value>
</data>
<data name="Conversion_done" xml:space="preserve">
<value>Conversion done.</value>
</data>
<data name="Tape_block_size_argument_help" xml:space="preserve">
<value>Only used for tapes, indicates block size. Files in the folder whose size is not a multiple of this value will simply be ignored.</value>
</data>
<data name="Tape_argument_input_help" xml:space="preserve">
<value>When used indicates that input is a folder containing alphabetically sorted files extracted from a linear block-based tape with fixed block size (e.g. a SCSI tape device).</value>
@@ -1186,9 +1174,6 @@ In you are unsure, please press N to not continue.</value>
</data>
<data name="The_specified_input_file_cannot_be_found" xml:space="preserve">
<value>The specified input file cannot be found.</value>
</data>
<data name="Decode_media_tags" xml:space="preserve">
<value>Decode media tags.</value>
</data>
<data name="Parameter_response_all_sectors" xml:space="preserve">
<value>all</value>
@@ -3111,4 +3096,7 @@ Do you want to continue?</value>
<data name="Dialog_Aaru_Resume" xml:space="preserve">
<value>Aaru resume file</value>
</data>
<data name="Archive_List_Command_Description" xml:space="preserve">
<value>List the contents of an archive file.</value>
</data>
</root>

View File

@@ -76,8 +76,7 @@
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite"/>
<PackageReference Include="Spectre.Console"/>
<PackageReference Include="System.CommandLine"/>
<PackageReference Include="System.CommandLine.NamingConventionBinder"/>
<PackageReference Include="Spectre.Console.Cli"/>
<PackageReference Include="System.Text.Encoding.CodePages"/>
<PackageReference Include="System.Text.Json"/>
<PackageReference Include="System.ValueTuple"/>

View File

@@ -31,19 +31,6 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System.CommandLine;
using Aaru.Localization;
namespace Aaru.Commands.Archive;
sealed class ArchiveFamily : Command
{
internal ArchiveFamily() : base("archive", UI.Archive_Command_Family_Description)
{
AddAlias("arc");
AddCommand(new ArchiveInfoCommand());
AddCommand(new ArchiveListCommand());
AddCommand(new ArchiveExtractCommand());
}
}
class ArchiveFamily : BaseSettings {}

View File

@@ -32,8 +32,7 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
@@ -46,45 +45,20 @@ using Aaru.Core;
using Aaru.Helpers;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Archive;
sealed class ArchiveExtractCommand : Command
sealed class ArchiveExtractCommand : Command<ArchiveExtractCommand.Settings>
{
const int BUFFER_SIZE = 16777216;
const string MODULE_NAME = "Extract-Files command";
public ArchiveExtractCommand() : base("extract", UI.Archive_Extract_Command_Description)
{
AddAlias("x");
Add(new Option<string>(["--encoding", "-e"], () => null, UI.Name_of_character_encoding_to_use));
Add(new Option<bool>(["--xattrs", "-x"], () => false, UI.Extract_extended_attributes_if_present));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Archive_file_path,
Name = "archive-path"
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Directory_where_extracted_files_will_be_created,
Name = "output-dir"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string encoding, bool xattrs, string archivePath,
string outputDir)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -102,7 +76,7 @@ sealed class ArchiveExtractCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -115,19 +89,19 @@ sealed class ArchiveExtractCommand : Command
Statistics.AddCommand("archive-extract");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(archivePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(outputDir ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--xattrs={0}", xattrs);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.Path ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(settings.OutputDir ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--xattrs={0}", settings.XAttrs);
IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(archivePath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.Path);
});
if(inputFilter == null)
@@ -139,13 +113,13 @@ sealed class ArchiveExtractCommand : Command
Encoding encodingClass = null;
if(encoding != null)
if(settings.Encoding != null)
{
try
{
encodingClass = Claunia.Encoding.Encoding.GetEncoding(encoding);
encodingClass = Claunia.Encoding.Encoding.GetEncoding(settings.Encoding);
if(verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
if(settings.Verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
}
catch(ArgumentException)
{
@@ -172,7 +146,7 @@ sealed class ArchiveExtractCommand : Command
return (int)ErrorNumber.UnrecognizedFormat;
}
if(verbose)
if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Archive_format_identified_by_0_1, archive.Name, archive.Id);
else
AaruConsole.WriteLine(UI.Archive_format_identified_by_0, archive.Name);
@@ -211,7 +185,7 @@ sealed class ArchiveExtractCommand : Command
}
for(var i = 0; i < archive.NumberOfEntries; i++)
for(int i = 0; i < archive.NumberOfEntries; i++)
{
ErrorNumber errno = archive.GetFilename(i, out string fileName);
@@ -264,7 +238,7 @@ sealed class ArchiveExtractCommand : Command
// Prevent absolute path attack
fileName = fileName.TrimStart('\\').TrimStart('/');
string outputPath = Path.Combine(outputDir, fileName);
string outputPath = Path.Combine(settings.OutputDir, fileName);
string destinationDir = Path.GetDirectoryName(outputPath);
if(File.Exists(destinationDir))
@@ -284,7 +258,7 @@ sealed class ArchiveExtractCommand : Command
.Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn())
.Start(ctx =>
{
var position = 0;
int position = 0;
var outputFile =
new FileStream(outputPath,
@@ -296,7 +270,7 @@ sealed class ArchiveExtractCommand : Command
ctx.AddTask(string.Format(UI.Reading_file_0, Markup.Escape(fileName)));
task.MaxValue = uncompressedSize;
var outBuf = new byte[BUFFER_SIZE];
byte[] outBuf = new byte[BUFFER_SIZE];
Stream inputFile = filter.GetDataForkStream();
while(position < stat.Length)
@@ -357,7 +331,7 @@ sealed class ArchiveExtractCommand : Command
else
AaruConsole.ErrorWriteLine(UI.Cannot_write_file_0_output_exists, Markup.Escape(fileName));
if(!xattrs) continue;
if(!settings.XAttrs) continue;
errno = archive.ListXAttr(i, out List<string> xattrNames);
@@ -389,7 +363,7 @@ sealed class ArchiveExtractCommand : Command
continue;
}
outputPath = Path.Combine(outputDir, ".xattrs", xattrName, fileName);
outputPath = Path.Combine(settings.OutputDir, ".xattrs", xattrName, fileName);
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
@@ -469,4 +443,29 @@ sealed class ArchiveExtractCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : ArchiveFamily
{
[Description("Name of character encoding to use.")]
[DefaultValue(null)]
[CommandOption("-e|--encoding")]
public string Encoding { get; init; }
[Description("Extract extended attributes if present.")]
[DefaultValue(false)]
[CommandOption("-x|--xattrs")]
public bool XAttrs { get; init; }
[Description("Archive file path")]
[CommandArgument(0, "<path>")]
public string Path { get; init; }
[Description("Directory where extracted files will be created. Will abort if it exists")]
[CommandArgument(1, "<output>")]
public string OutputDir { get; init; }
}
#endregion
}

View File

@@ -32,8 +32,7 @@
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.Text;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
@@ -42,33 +41,19 @@ using Aaru.Console;
using Aaru.Core;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Archive;
sealed class ArchiveInfoCommand : Command
sealed class ArchiveInfoCommand : Command<ArchiveInfoCommand.Settings>
{
const string MODULE_NAME = "Analyze command";
public ArchiveInfoCommand() : base("info", UI.Archive_Info_Command_Description)
{
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Archive_file_path,
Name = "archive-path"
});
Add(new Option<string>(["--encoding", "-e"], () => null, UI.Name_of_character_encoding_to_use));
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string archivePath, string encoding)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -86,7 +71,7 @@ sealed class ArchiveInfoCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -99,17 +84,17 @@ sealed class ArchiveInfoCommand : Command
Statistics.AddCommand("archive-info");
AaruConsole.DebugWriteLine(MODULE_NAME, "debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "input={0}", Markup.Escape(archivePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "encoding={0}", Markup.Escape(encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "input={0}", Markup.Escape(settings.Path ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "encoding={0}", Markup.Escape(settings.Encoding ?? ""));
IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(archivePath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.Path);
});
if(inputFilter == null)
@@ -121,13 +106,13 @@ sealed class ArchiveInfoCommand : Command
Encoding encodingClass = null;
if(encoding != null)
if(settings.Encoding != null)
{
try
{
encodingClass = Claunia.Encoding.Encoding.GetEncoding(encoding);
encodingClass = Claunia.Encoding.Encoding.GetEncoding(settings.Encoding);
if(verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
if(settings.Verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
}
catch(ArgumentException)
{
@@ -188,4 +173,20 @@ sealed class ArchiveInfoCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : ArchiveFamily
{
[CommandOption("-e|--encoding")]
[Description("Name of character encoding to use.")]
[DefaultValue(null)]
public string Encoding { get; init; }
[Description("Archive file path")]
[CommandArgument(0, "<path>")]
public string Path { get; init; }
}
#endregion
}

View File

@@ -31,8 +31,7 @@
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.Text;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
@@ -42,36 +41,19 @@ using Aaru.Console;
using Aaru.Core;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Archive;
sealed class ArchiveListCommand : Command
sealed class ArchiveListCommand : Command<ArchiveListCommand.Settings>
{
const string MODULE_NAME = "Archive list command";
public ArchiveListCommand() : base("list", "Lists files contained in an archive.")
{
AddAlias("l");
Add(new Option<string>(["--encoding", "-e"], () => null, UI.Name_of_character_encoding_to_use));
Add(new Option<bool>(["--long-format", "-l"], () => false, UI.Use_long_format));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Archive_file_path,
Name = "archive-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string encoding, string archivePath, bool longFormat)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -89,7 +71,7 @@ sealed class ArchiveListCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -100,11 +82,11 @@ sealed class ArchiveListCommand : Command
};
}
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--long-format={0}", longFormat);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(archivePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--long-format={0}", settings.LongFormat);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.Path ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
Statistics.AddCommand("archive-list");
IFilter inputFilter = null;
@@ -112,7 +94,7 @@ sealed class ArchiveListCommand : Command
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(archivePath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.Path);
});
if(inputFilter == null)
@@ -124,13 +106,13 @@ sealed class ArchiveListCommand : Command
Encoding encodingClass = null;
if(encoding != null)
if(settings.Encoding != null)
{
try
{
encodingClass = Claunia.Encoding.Encoding.GetEncoding(encoding);
encodingClass = Claunia.Encoding.Encoding.GetEncoding(settings.Encoding);
if(verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
if(settings.Verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
}
catch(ArgumentException)
{
@@ -159,7 +141,7 @@ sealed class ArchiveListCommand : Command
return (int)ErrorNumber.UnrecognizedFormat;
}
if(verbose)
if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Archive_format_identified_by_0_1, archive.Name, archive.Id);
else
AaruConsole.WriteLine(UI.Archive_format_identified_by_0, archive.Name);
@@ -197,9 +179,9 @@ sealed class ArchiveListCommand : Command
return (int)ErrorNumber.CannotOpenFormat;
}
if(!longFormat)
if(!settings.LongFormat)
{
for(var i = 0; i < archive.NumberOfEntries; i++)
for(int i = 0; i < archive.NumberOfEntries; i++)
{
ErrorNumber errno = archive.GetFilename(i, out string fileName);
@@ -218,8 +200,8 @@ sealed class ArchiveListCommand : Command
}
var table = new Table();
var files = 0;
var folders = 0;
int files = 0;
int folders = 0;
long totalSize = 0;
long totalUncompressed = 0;
@@ -278,7 +260,7 @@ sealed class ArchiveListCommand : Command
ctx.Refresh();
for(var i = 0; i < archive.NumberOfEntries; i++)
for(int i = 0; i < archive.NumberOfEntries; i++)
{
ErrorNumber errno = archive.GetFilename(i, out string fileName);
@@ -302,7 +284,7 @@ sealed class ArchiveListCommand : Command
continue;
}
var attr = new char[5];
char[] attr = new char[5];
if(stat.Attributes.HasFlag(FileAttributes.Directory))
{
@@ -422,4 +404,25 @@ sealed class ArchiveListCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : ArchiveFamily
{
[CommandOption("-e|--encoding")]
[Description("Name of character encoding to use.")]
[DefaultValue(null)]
public string Encoding { get; init; }
[CommandOption("-l|--long-format")]
[Description("Use long format.")]
[DefaultValue(false)]
public bool LongFormat { get; init; }
[Description("Archive file path")]
[CommandArgument(0, "<path>")]
public string Path { get; init; }
}
#endregion
}

View File

@@ -0,0 +1,25 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Aaru.Commands;
public class BaseSettings : CommandSettings
{
[CommandOption("-v|--verbose")]
[Description("Shows verbose output.")]
[DefaultValue(false)]
public bool Verbose { get; init; }
[CommandOption("-d|--debug")]
[Description("Shows debug output from plugins.")]
[DefaultValue(false)]
public bool Debug { get; init; }
// TODO: Add support for this
/*
[CommandOption("--pause")]
[Description("Pauses before exiting.")]
[DefaultValue(false)]
public bool Pause { get; init; }
*/
}

View File

@@ -30,27 +30,22 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using Aaru.CommonTypes.Enums;
using Aaru.Console;
using Aaru.Localization;
using Aaru.Settings;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands;
sealed class ConfigureCommand : Command
sealed class ConfigureCommand : Command<ConfigureCommand.Settings>
{
public ConfigureCommand() : base("configure", UI.Configure_Command_Description) =>
Handler = CommandHandler.Create((Func<bool, bool, int>)Invoke);
int Invoke(bool debug, bool verbose)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -68,7 +63,7 @@ sealed class ConfigureCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -101,7 +96,7 @@ sealed class ConfigureCommand : Command
AaruConsole.WriteLine(UI.Configure_enable_decryption_disclaimer);
Settings.Settings.Current.EnableDecryption =
Aaru.Settings.Settings.Current.EnableDecryption =
AnsiConsole.Confirm($"[italic]{UI.Do_you_want_to_enable_decryption_of_copy_protected_media_Q}[/]");
#region Device reports
@@ -110,14 +105,14 @@ sealed class ConfigureCommand : Command
AaruConsole.WriteLine(UI.Configure_Device_Report_information_disclaimer);
Settings.Settings.Current.SaveReportsGlobally = AnsiConsole.Confirm($"[italic]{UI.
Aaru.Settings.Settings.Current.SaveReportsGlobally = AnsiConsole.Confirm($"[italic]{UI.
Configure_Do_you_want_to_save_device_reports_in_shared_folder_of_your_computer_Q}[/]");
AaruConsole.WriteLine();
AaruConsole.WriteLine(UI.Configure_share_report_disclaimer);
Settings.Settings.Current.ShareReports =
Aaru.Settings.Settings.Current.ShareReports =
AnsiConsole.Confirm($"[italic]{UI.Do_you_want_to_share_your_device_reports_with_us_Q}[/]");
#endregion Device reports
@@ -130,7 +125,7 @@ sealed class ConfigureCommand : Command
if(AnsiConsole.Confirm($"[italic]{UI.Do_you_want_to_save_stats_about_your_Aaru_usage_Q}[/]"))
{
Settings.Settings.Current.Stats = new StatsSettings
Aaru.Settings.Settings.Current.Stats = new StatsSettings
{
ShareStats = AnsiConsole.Confirm($"[italic]{UI.Do_you_want_to_share_your_stats__anonymously_Q}[/]"),
CommandStats =
@@ -156,13 +151,19 @@ sealed class ConfigureCommand : Command
};
}
else
Settings.Settings.Current.Stats = null;
Aaru.Settings.Settings.Current.Stats = null;
#endregion Statistics
Settings.Settings.Current.GdprCompliance = DicSettings.GDPR_LEVEL;
Settings.Settings.SaveSettings();
Aaru.Settings.Settings.Current.GdprCompliance = DicSettings.GDPR_LEVEL;
Aaru.Settings.Settings.SaveSettings();
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : BaseSettings {}
#endregion
}

View File

@@ -30,18 +30,6 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System.CommandLine;
using Aaru.Localization;
namespace Aaru.Commands.Database;
sealed class DatabaseFamily : Command
{
internal DatabaseFamily(bool mainDbUpdate) : base("database", UI.Database_Command_Family_Description)
{
AddAlias("db");
AddCommand(new StatisticsCommand());
AddCommand(new UpdateCommand(mainDbUpdate));
}
}
class DatabaseFamily : BaseSettings {}

View File

@@ -30,8 +30,6 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System;
using System.CommandLine.NamingConventionBinder;
using System.Linq;
using Aaru.CommonTypes.Enums;
using Aaru.Console;
@@ -39,20 +37,19 @@ using Aaru.Database;
using Aaru.Database.Models;
using Aaru.Localization;
using Spectre.Console;
using Command = System.CommandLine.Command;
using Spectre.Console.Cli;
using Command = Aaru.Database.Models.Command;
namespace Aaru.Commands.Database;
sealed class StatisticsCommand : Command
sealed class StatisticsCommand : Command<StatisticsCommand.Settings>
{
public StatisticsCommand() : base("stats", UI.Database_Stats_Command_Description) =>
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
public override int Execute(CommandContext context, Settings settings)
public static int Invoke(bool debug, bool verbose)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -70,7 +67,7 @@ sealed class StatisticsCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -81,7 +78,7 @@ sealed class StatisticsCommand : Command
};
}
var ctx = AaruContext.Create(Settings.Settings.LocalDbPath);
var ctx = AaruContext.Create(Aaru.Settings.Settings.LocalDbPath);
if(!ctx.Commands.Any() &&
!ctx.Filesystems.Any() &&
@@ -96,7 +93,7 @@ sealed class StatisticsCommand : Command
return (int)ErrorNumber.NothingFound;
}
var thereAreStats = false;
bool thereAreStats = false;
Table table;
if(ctx.Commands.Any())
@@ -112,7 +109,7 @@ sealed class StatisticsCommand : Command
if(ctx.Commands.Any(c => c.Name == "analyze"))
{
foreach(Aaru.Database.Models.Command oldAnalyze in ctx.Commands.Where(c => c.Name == "analyze"))
foreach(Command oldAnalyze in ctx.Commands.Where(c => c.Name == "analyze"))
{
oldAnalyze.Name = "fs-info";
ctx.Commands.Update(oldAnalyze);
@@ -120,8 +117,7 @@ sealed class StatisticsCommand : Command
ulong count = 0;
foreach(Aaru.Database.Models.Command fsInfo in ctx.Commands.Where(c => c.Name == "fs-info" &&
c.Synchronized))
foreach(Command fsInfo in ctx.Commands.Where(c => c.Name == "fs-info" && c.Synchronized))
{
count += fsInfo.Count;
ctx.Remove(fsInfo);
@@ -129,7 +125,7 @@ sealed class StatisticsCommand : Command
if(count > 0)
{
ctx.Commands.Add(new Aaru.Database.Models.Command
ctx.Commands.Add(new Command
{
Count = count,
Name = "fs-info",
@@ -366,4 +362,10 @@ sealed class StatisticsCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : DatabaseFamily {}
#endregion
}

View File

@@ -31,8 +31,7 @@
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
@@ -43,31 +42,20 @@ using Aaru.Database;
using Aaru.Localization;
using Microsoft.EntityFrameworkCore;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Database;
sealed class UpdateCommand : Command
sealed class UpdateCommand : AsyncCommand<UpdateCommand.Settings>
{
const string MODULE_NAME = "Update command";
readonly bool _mainDbUpdate;
public UpdateCommand(bool mainDbUpdate) : base("update", UI.Database_Update_Command_Description)
public override async Task<int> ExecuteAsync(CommandContext context, Settings settings)
{
_mainDbUpdate = mainDbUpdate;
Add(new Option<bool>("--clear", () => false, UI.Clear_existing_main_database));
Add(new Option<bool>("--clear-all", () => false, UI.Clear_existing_main_and_local_database));
Handler = CommandHandler.Create((Func<bool, bool, bool, bool, Task<int>>)InvokeAsync);
}
async Task<int> InvokeAsync(bool debug, bool verbose, bool clear, bool clearAll)
{
if(_mainDbUpdate) return (int)ErrorNumber.NoError;
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -85,7 +73,7 @@ sealed class UpdateCommand : Command
AaruConsole.WriteExceptionEvent += ex => stderrConsole.WriteException(ex);
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -96,16 +84,16 @@ sealed class UpdateCommand : Command
};
}
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
if(clearAll)
if(settings.ClearAll)
{
try
{
File.Delete(Settings.Settings.LocalDbPath);
File.Delete(Aaru.Settings.Settings.LocalDbPath);
var ctx = AaruContext.Create(Settings.Settings.LocalDbPath);
var ctx = AaruContext.Create(Aaru.Settings.Settings.LocalDbPath);
await ctx.Database.MigrateAsync();
await ctx.SaveChangesAsync();
}
@@ -117,11 +105,11 @@ sealed class UpdateCommand : Command
}
}
if(clear || clearAll)
if(settings.Clear || settings.ClearAll)
{
try
{
File.Delete(Settings.Settings.MainDbPath);
File.Delete(Aaru.Settings.Settings.MainDbPath);
}
catch(Exception) when(!Debugger.IsAttached)
{
@@ -131,7 +119,7 @@ sealed class UpdateCommand : Command
}
}
await DoUpdateAsync(clear || clearAll);
await DoUpdateAsync(settings.Clear || settings.ClearAll);
return (int)ErrorNumber.NoError;
}
@@ -141,4 +129,20 @@ sealed class UpdateCommand : Command
await Remote.UpdateMainDatabaseAsync(create);
Statistics.AddCommand("update");
}
#region Nested type: Settings
public class Settings : DatabaseFamily
{
[CommandOption("--clear")]
[Description("Clear existing main database.")]
[DefaultValue(false)]
public bool Clear { get; init; }
[CommandOption("--clear-all")]
[Description("Clear existing main and local database.")]
[DefaultValue(false)]
public bool ClearAll { get; init; }
}
#endregion
}

View File

@@ -30,19 +30,6 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System.CommandLine;
using Aaru.Localization;
namespace Aaru.Commands.Device;
sealed class DeviceFamily : Command
{
public DeviceFamily() : base("device", UI.Device_Command_Family_Description)
{
AddAlias("dev");
AddCommand(new DeviceInfoCommand());
AddCommand(new DeviceReportCommand());
AddCommand(new ListDevicesCommand());
}
}
class DeviceFamily : BaseSettings {}

View File

@@ -32,8 +32,7 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text.Json;
@@ -52,34 +51,20 @@ using Aaru.Decoders.SCSI.MMC;
using Aaru.Helpers;
using Aaru.Localization;
using Spectre.Console;
using Command = System.CommandLine.Command;
using Spectre.Console.Cli;
using Profile = Aaru.Decoders.SCSI.MMC.Profile;
namespace Aaru.Commands.Device;
sealed class DeviceReportCommand : Command
sealed class DeviceReportCommand : AsyncCommand<DeviceReportCommand.Settings>
{
const string MODULE_NAME = "Device-Report command";
public DeviceReportCommand() : base("report", UI.Device_Report_Command_Description)
{
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Device_path,
Name = "device-path"
});
Add(new Option<bool>(["--trap-disc", "-t"], () => false, UI.Device_report_using_trap_disc));
Handler = CommandHandler.Create(GetType().GetMethod(nameof(InvokeAsync)) ?? throw new NullReferenceException());
}
public static async Task<int> InvokeAsync(bool debug, bool verbose, string devicePath, bool trapDisc)
public override async Task<int> ExecuteAsync(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -97,7 +82,7 @@ sealed class DeviceReportCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -110,9 +95,11 @@ sealed class DeviceReportCommand : Command
Statistics.AddCommand("device-report");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(devicePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(settings.Path ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
string devicePath = settings.Path;
if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0]))
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
@@ -163,7 +150,7 @@ sealed class DeviceReportCommand : Command
Type = dev.Type
};
var removable = false;
bool removable = false;
string jsonFile;
switch(string.IsNullOrWhiteSpace(dev.Manufacturer))
@@ -189,7 +176,7 @@ sealed class DeviceReportCommand : Command
jsonFile = jsonFile.Replace('\\', '_').Replace('/', '_').Replace('?', '_');
if(trapDisc && dev.ScsiType != PeripheralDeviceTypes.MultiMediaDevice)
if(settings.TrapDisc && dev.ScsiType != PeripheralDeviceTypes.MultiMediaDevice)
{
AaruConsole.ErrorWriteLine(UI.Device_does_not_report_with_trap_discs);
@@ -400,7 +387,7 @@ sealed class DeviceReportCommand : Command
string mediumManufacturer;
byte[] senseBuffer = [];
var sense = true;
bool sense = true;
switch(dev.ScsiType)
{
@@ -416,7 +403,7 @@ sealed class DeviceReportCommand : Command
dev.Manufacturer.Equals("iomega", StringComparison.InvariantCultureIgnoreCase) &&
dev.Model.StartsWith("rrd", StringComparison.InvariantCultureIgnoreCase);
if(trapDisc)
if(settings.TrapDisc)
{
if(iomegaRev)
{
@@ -699,7 +686,8 @@ sealed class DeviceReportCommand : Command
tryNec |= dev.Manufacturer.Equals("nec", StringComparison.InvariantCultureIgnoreCase);
tryLiteOn |= dev.Manufacturer.Equals("lite-on", StringComparison.InvariantCultureIgnoreCase);
tryLiteOn |=
dev.Manufacturer.Equals("lite-on", StringComparison.InvariantCultureIgnoreCase);
if(!iomegaRev)
{
@@ -755,7 +743,7 @@ sealed class DeviceReportCommand : Command
System.Console.ReadKey(true);
var mediaIsRecognized = true;
bool mediaIsRecognized = true;
await AnsiConsole.Status()
.StartAsync(Localization.Core.Waiting_for_drive_to_become_ready,
@@ -778,7 +766,7 @@ sealed class DeviceReportCommand : Command
case 0x04 when decSense.Value.ASCQ == 0x01:
case 0x28:
{
var leftRetries = 50;
int leftRetries = 50;
while(leftRetries > 0)
{
@@ -857,7 +845,7 @@ sealed class DeviceReportCommand : Command
task.MaxValue = ushort.MaxValue;
for(var i = (ushort)(mediaTest.BlockSize ?? 0);; i++)
for(ushort i = (ushort)(mediaTest.BlockSize ?? 0);; i++)
{
task.Description =
string.Format(Localization.Core
@@ -969,7 +957,7 @@ sealed class DeviceReportCommand : Command
mediumModel = AnsiConsole.Ask<string>(Localization.Core.Please_write_media_model);
var mediaIsRecognized = true;
bool mediaIsRecognized = true;
await AnsiConsole.Status()
.StartAsync(Localization.Core.Waiting_for_drive_to_become_ready,
@@ -996,7 +984,7 @@ sealed class DeviceReportCommand : Command
case 0x04 when decSense.Value.ASCQ == 0x01:
case 0x28:
{
var leftRetries = 50;
int leftRetries = 50;
while(leftRetries > 0)
{
@@ -1088,7 +1076,7 @@ sealed class DeviceReportCommand : Command
System.Console.ReadKey(true);
var mediaIsRecognized = true;
bool mediaIsRecognized = true;
await AnsiConsole.Status()
.StartAsync(Localization.Core.Waiting_for_drive_to_become_ready,
@@ -1111,7 +1099,7 @@ sealed class DeviceReportCommand : Command
case 0x04 when decSense.Value.ASCQ == 0x01:
case 0x28:
{
var leftRetries = 50;
int leftRetries = 50;
while(leftRetries > 0)
{
@@ -1178,7 +1166,7 @@ sealed class DeviceReportCommand : Command
task.MaxValue = ushort.MaxValue;
for(var i = (ushort)(mediaTest.BlockSize ?? 0);; i++)
for(ushort i = (ushort)(mediaTest.BlockSize ?? 0);; i++)
{
task.Value = i;
@@ -1302,7 +1290,7 @@ sealed class DeviceReportCommand : Command
mediumModel = AnsiConsole.Ask<string>(Localization.Core.Please_write_media_model);
var mediaIsRecognized = true;
bool mediaIsRecognized = true;
await AnsiConsole.Status()
.StartAsync(Localization.Core.Waiting_for_drive_to_become_ready,
@@ -1323,7 +1311,7 @@ sealed class DeviceReportCommand : Command
case 0x3A:
case 0x04 when decSense.Value.ASCQ == 0x01:
{
var leftRetries = 20;
int leftRetries = 20;
while(leftRetries > 0)
{
@@ -1379,7 +1367,7 @@ sealed class DeviceReportCommand : Command
task.MaxValue = ushort.MaxValue;
for(var i = (ushort)(mediaTest.BlockSize ?? 0);; i++)
for(ushort i = (ushort)(mediaTest.BlockSize ?? 0);; i++)
{
task.Value = i;
@@ -1496,7 +1484,8 @@ sealed class DeviceReportCommand : Command
task.MaxValue = ushort.MaxValue;
for(var i = (ushort)(report.SCSI.ReadCapabilities.BlockSize ?? 0);; i++)
for(ushort i = (ushort)(report.SCSI.ReadCapabilities.BlockSize ?? 0);;
i++)
{
task.Value = i;
@@ -1592,15 +1581,30 @@ sealed class DeviceReportCommand : Command
jsonFs.Close();
await using(var ctx = AaruContext.Create(Settings.Settings.LocalDbPath))
await using(var ctx = AaruContext.Create(Aaru.Settings.Settings.LocalDbPath))
{
ctx.Reports.Add(new Report(report));
await ctx.SaveChangesAsync();
}
// TODO:
if(Settings.Settings.Current.ShareReports) await Remote.SubmitReportAsync(report);
if(Aaru.Settings.Settings.Current.ShareReports) await Remote.SubmitReportAsync(report);
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : DeviceFamily
{
[Description("Does a device report using a trap disc.")]
[CommandOption("-t|--trap-disc")]
public bool TrapDisc { get; init; }
[Description("Device path")]
[CommandArgument(0, "<device-path>")]
public string Path { get; init; }
}
#endregion
}

View File

@@ -33,8 +33,7 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.Linq;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
@@ -55,36 +54,23 @@ using Aaru.Localization;
using Humanizer;
using Humanizer.Localisation;
using Spectre.Console;
using Command = System.CommandLine.Command;
using Spectre.Console.Cli;
using DeviceInfo = Aaru.Core.Devices.Info.DeviceInfo;
using Inquiry = Aaru.Decoders.SCSI.Inquiry;
using Tuple = Aaru.Decoders.PCMCIA.Tuple;
namespace Aaru.Commands.Device;
sealed class DeviceInfoCommand : Command
[Description("Gets information about a device.")]
sealed class DeviceInfoCommand : Command<DeviceInfoCommand.Settings>
{
const string MODULE_NAME = "Device-Info command";
public DeviceInfoCommand() : base("info", UI.Device_Info_Command_Description)
{
Add(new Option<string>(["--output-prefix", "-w"], () => null, UI.Prefix_for_saving_binary_information));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Device_path,
Name = "device-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string devicePath, string outputPrefix)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -102,7 +88,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -115,10 +101,12 @@ sealed class DeviceInfoCommand : Command
Statistics.AddCommand("device-info");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(devicePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output-prefix={0}", Markup.Escape(outputPrefix ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(settings.Path ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output-prefix={0}", Markup.Escape(settings.OutputPrefix ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
string devicePath = settings.Path;
if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0]))
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
@@ -282,7 +270,11 @@ sealed class DeviceInfoCommand : Command
if(devInfo.AtaIdentify != null)
{
DataFile.WriteTo(MODULE_NAME, outputPrefix, "_ata_identify.bin", "ATA IDENTIFY", devInfo.AtaIdentify);
DataFile.WriteTo(MODULE_NAME,
settings.OutputPrefix,
"_ata_identify.bin",
"ATA IDENTIFY",
devInfo.AtaIdentify);
Identify.IdentifyDevice? decodedIdentify = Identify.Decode(devInfo.AtaIdentify);
AaruConsole.WriteLine(Decoders.ATA.Identify.Prettify(decodedIdentify));
@@ -323,8 +315,8 @@ sealed class DeviceInfoCommand : Command
if((devInfo.AtaMcptError.Value.DeviceHead & 0x08) == 0x08)
AaruConsole.WriteLine(Localization.Core.Media_card_is_write_protected);
var specificData = (ushort)(devInfo.AtaMcptError.Value.CylinderHigh * 0x100 +
devInfo.AtaMcptError.Value.CylinderLow);
ushort specificData = (ushort)(devInfo.AtaMcptError.Value.CylinderHigh * 0x100 +
devInfo.AtaMcptError.Value.CylinderLow);
if(specificData != 0) AaruConsole.WriteLine(Localization.Core.Card_specific_data_0, specificData);
}
@@ -368,7 +360,11 @@ sealed class DeviceInfoCommand : Command
if(devInfo.AtapiIdentify != null)
{
DataFile.WriteTo(MODULE_NAME, outputPrefix, "_atapi_identify.bin", "ATAPI IDENTIFY", devInfo.AtapiIdentify);
DataFile.WriteTo(MODULE_NAME,
settings.OutputPrefix,
"_atapi_identify.bin",
"ATAPI IDENTIFY",
devInfo.AtapiIdentify);
AaruConsole.WriteLine(Decoders.ATA.Identify.Prettify(devInfo.AtapiIdentify));
}
@@ -378,7 +374,7 @@ sealed class DeviceInfoCommand : Command
if(dev.Type != DeviceType.ATAPI) AaruConsole.WriteLine($"[bold]{UI.Title_SCSI_device}[/]");
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
"_scsi_inquiry.bin",
UI.Title_SCSI_INQUIRY,
devInfo.ScsiInquiryData);
@@ -396,7 +392,7 @@ sealed class DeviceInfoCommand : Command
page.Key,
EVPD.DecodeASCIIPage(page.Value));
DataFile.WriteTo(MODULE_NAME, outputPrefix, page.Value);
DataFile.WriteTo(MODULE_NAME, settings.OutputPrefix, page.Value);
break;
case 0x80:
@@ -404,7 +400,7 @@ sealed class DeviceInfoCommand : Command
EVPD.DecodePage80(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -414,7 +410,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_81(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -425,7 +421,7 @@ sealed class DeviceInfoCommand : Command
EVPD.DecodePage82(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -435,7 +431,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_83(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -445,7 +441,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_84(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -455,7 +451,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_85(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -465,7 +461,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_86(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -475,7 +471,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_89(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -485,7 +481,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_B0(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -496,7 +492,7 @@ sealed class DeviceInfoCommand : Command
EVPD.DecodePageB1(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -507,7 +503,7 @@ sealed class DeviceInfoCommand : Command
EVPD.DecodePageB2(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -518,7 +514,7 @@ sealed class DeviceInfoCommand : Command
EVPD.DecodePageB3(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -529,7 +525,7 @@ sealed class DeviceInfoCommand : Command
EVPD.DecodePageB4(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -542,7 +538,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_Quantum(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -555,7 +551,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_Seagate(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -568,7 +564,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_IBM(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -581,7 +577,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C1_IBM(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -595,7 +591,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_C1_Certance(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -609,7 +605,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C2_C3_C4_C5_C6_Certance(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -623,7 +619,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_to_C5_HP(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -636,7 +632,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_DF_Certance(page.Value));
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -651,7 +647,7 @@ sealed class DeviceInfoCommand : Command
page.Key);
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value);
@@ -665,7 +661,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.ScsiModeSense6 != null)
{
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
"_scsi_modesense6.bin",
"SCSI MODE SENSE",
devInfo.ScsiModeSense6);
@@ -674,7 +670,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.ScsiModeSense10 != null)
{
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
"_scsi_modesense10.bin",
"SCSI MODE SENSE",
devInfo.ScsiModeSense10);
@@ -690,7 +686,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.MmcConfiguration != null)
{
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
"_mmc_getconfiguration.bin",
"MMC GET CONFIGURATION",
devInfo.MmcConfiguration);
@@ -966,7 +962,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.PlextorFeatures?.Eeprom != null)
{
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
"_plextor_eeprom.bin",
"PLEXTOR READ EEPROM",
devInfo.PlextorFeatures.Eeprom);
@@ -1144,7 +1140,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.BlockLimits != null)
{
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
"_ssc_readblocklimits.bin",
"SSC READ BLOCK LIMITS",
devInfo.BlockLimits);
@@ -1156,7 +1152,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.DensitySupport != null)
{
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
"_ssc_reportdensitysupport.bin",
"SSC REPORT DENSITY SUPPORT",
devInfo.DensitySupport);
@@ -1171,7 +1167,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.MediumDensitySupport != null)
{
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
"_ssc_reportdensitysupport_medium.bin",
"SSC REPORT DENSITY SUPPORT (MEDIUM)",
devInfo.MediumDensitySupport);
@@ -1190,26 +1186,26 @@ sealed class DeviceInfoCommand : Command
{
case DeviceType.MMC:
{
var noInfo = true;
bool noInfo = true;
if(devInfo.CID != null)
{
noInfo = false;
DataFile.WriteTo(MODULE_NAME, outputPrefix, "_mmc_cid.bin", "MMC CID", devInfo.CID);
DataFile.WriteTo(MODULE_NAME, settings.OutputPrefix, "_mmc_cid.bin", "MMC CID", devInfo.CID);
AaruConsole.WriteLine("{0}", Decoders.MMC.Decoders.PrettifyCID(devInfo.CID));
}
if(devInfo.CSD != null)
{
noInfo = false;
DataFile.WriteTo(MODULE_NAME, outputPrefix, "_mmc_csd.bin", "MMC CSD", devInfo.CSD);
DataFile.WriteTo(MODULE_NAME, settings.OutputPrefix, "_mmc_csd.bin", "MMC CSD", devInfo.CSD);
AaruConsole.WriteLine("{0}", Decoders.MMC.Decoders.PrettifyCSD(devInfo.CSD));
}
if(devInfo.OCR != null)
{
noInfo = false;
DataFile.WriteTo(MODULE_NAME, outputPrefix, "_mmc_ocr.bin", "MMC OCR", devInfo.OCR);
DataFile.WriteTo(MODULE_NAME, settings.OutputPrefix, "_mmc_ocr.bin", "MMC OCR", devInfo.OCR);
AaruConsole.WriteLine("{0}", Decoders.MMC.Decoders.PrettifyOCR(devInfo.OCR));
}
@@ -1218,7 +1214,7 @@ sealed class DeviceInfoCommand : Command
noInfo = false;
DataFile.WriteTo(MODULE_NAME,
outputPrefix,
settings.OutputPrefix,
"_mmc_ecsd.bin",
"MMC Extended CSD",
devInfo.ExtendedCSD);
@@ -1232,13 +1228,17 @@ sealed class DeviceInfoCommand : Command
break;
case DeviceType.SecureDigital:
{
var noInfo = true;
bool noInfo = true;
if(devInfo.CID != null)
{
noInfo = false;
DataFile.WriteTo(MODULE_NAME, outputPrefix, "_sd_cid.bin", "SecureDigital CID", devInfo.CID);
DataFile.WriteTo(MODULE_NAME,
settings.OutputPrefix,
"_sd_cid.bin",
"SecureDigital CID",
devInfo.CID);
AaruConsole.WriteLine("{0}", Decoders.SecureDigital.Decoders.PrettifyCID(devInfo.CID));
}
@@ -1247,7 +1247,11 @@ sealed class DeviceInfoCommand : Command
{
noInfo = false;
DataFile.WriteTo(MODULE_NAME, outputPrefix, "_sd_csd.bin", "SecureDigital CSD", devInfo.CSD);
DataFile.WriteTo(MODULE_NAME,
settings.OutputPrefix,
"_sd_csd.bin",
"SecureDigital CSD",
devInfo.CSD);
AaruConsole.WriteLine("{0}", Decoders.SecureDigital.Decoders.PrettifyCSD(devInfo.CSD));
}
@@ -1256,7 +1260,11 @@ sealed class DeviceInfoCommand : Command
{
noInfo = false;
DataFile.WriteTo(MODULE_NAME, outputPrefix, "_sd_ocr.bin", "SecureDigital OCR", devInfo.OCR);
DataFile.WriteTo(MODULE_NAME,
settings.OutputPrefix,
"_sd_ocr.bin",
"SecureDigital OCR",
devInfo.OCR);
AaruConsole.WriteLine("{0}", Decoders.SecureDigital.Decoders.PrettifyOCR(devInfo.OCR));
}
@@ -1265,7 +1273,11 @@ sealed class DeviceInfoCommand : Command
{
noInfo = false;
DataFile.WriteTo(MODULE_NAME, outputPrefix, "_sd_scr.bin", "SecureDigital SCR", devInfo.SCR);
DataFile.WriteTo(MODULE_NAME,
settings.OutputPrefix,
"_sd_scr.bin",
"SecureDigital SCR",
devInfo.SCR);
AaruConsole.WriteLine("{0}", Decoders.SecureDigital.Decoders.PrettifySCR(devInfo.SCR));
}
@@ -1281,7 +1293,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine();
// Open main database
var ctx = AaruContext.Create(Settings.Settings.MainDbPath);
var ctx = AaruContext.Create(Aaru.Settings.Settings.MainDbPath);
// Search for device in main database
Aaru.Database.Models.Device dbDev =
@@ -1313,4 +1325,19 @@ sealed class DeviceInfoCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : DeviceFamily
{
[Description("Prefix for saving binary information from device.")]
[CommandOption("-w|--output-prefix")]
public string OutputPrefix { get; init; }
[Description("Device path")]
[CommandArgument(0, "<device-path>")]
public string Path { get; init; }
}
#endregion
}

View File

@@ -30,9 +30,7 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.Linq;
using Aaru.CommonTypes.Enums;
using Aaru.Console;
@@ -41,30 +39,20 @@ using Aaru.Devices;
using Aaru.Localization;
using JetBrains.Annotations;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Device;
sealed class ListDevicesCommand : Command
sealed class ListDevicesCommand : Command<ListDevicesCommand.Settings>
{
const string MODULE_NAME = "List-Devices command";
public ListDevicesCommand() : base("list", UI.Device_List_Command_Description)
{
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ZeroOrOne,
Description = UI.aaruremote_host,
Name = "aaru-remote-host"
});
public override int Execute(CommandContext context, Settings settings)
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, [CanBeNull] string aaruRemoteHost)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -82,7 +70,7 @@ sealed class ListDevicesCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -95,8 +83,8 @@ sealed class ListDevicesCommand : Command
Statistics.AddCommand("list-devices");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
DeviceInfo[] devices = Devices.Device.ListDevices(out bool isRemote,
out string serverApplication,
@@ -104,7 +92,7 @@ sealed class ListDevicesCommand : Command
out string serverOperatingSystem,
out string serverOperatingSystemVersion,
out string serverArchitecture,
aaruRemoteHost);
settings.AaruRemoteHost);
if(isRemote)
{
@@ -142,4 +130,17 @@ sealed class ListDevicesCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : DeviceFamily
{
[CanBeNull]
[Description("aaruremote host")]
[CommandArgument(0, "[aaru-remote-host]")]
[DefaultValue(null)]
public string AaruRemoteHost { get; init; }
}
#endregion
}

View File

@@ -32,8 +32,7 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
@@ -46,50 +45,21 @@ using Aaru.Core;
using Aaru.Localization;
using JetBrains.Annotations;
using Spectre.Console;
using Spectre.Console.Cli;
using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes;
namespace Aaru.Commands.Filesystem;
sealed class ExtractFilesCommand : Command
sealed class ExtractFilesCommand : Command<ExtractFilesCommand.Settings>
{
const long BUFFER_SIZE = 16777216;
const string MODULE_NAME = "Extract-Files command";
public ExtractFilesCommand() : base("extract", UI.Filesystem_Extract_Command_Description)
{
Add(new Option<string>(["--encoding", "-e"], () => null, UI.Name_of_character_encoding_to_use));
Add(new Option<string>(["--options", "-O"],
() => null,
UI.Comma_separated_name_value_pairs_of_filesystem_options));
Add(new Option<bool>(["--xattrs", "-x"], () => false, UI.Extract_extended_attributes_if_present));
Add(new Option<string>(["--namespace", "-n"], () => null, UI.Namespace_to_use_for_filenames));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Disc_image_path,
Name = "image-path"
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Directory_where_extracted_files_will_be_created,
Name = "output-dir"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string encoding, bool xattrs, string imagePath,
string @namespace, string outputDir, string options)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -107,7 +77,7 @@ sealed class ExtractFilesCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -120,29 +90,29 @@ sealed class ExtractFilesCommand : Command
Statistics.AddCommand("extract-files");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(options ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(outputDir ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--xattrs={0}", xattrs);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(settings.Options ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(settings.OutputDir ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--xattrs={0}", settings.Xattrs);
IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
});
Dictionary<string, string> parsedOptions = Core.Options.Parse(options);
Dictionary<string, string> parsedOptions = Options.Parse(settings.Options);
AaruConsole.DebugWriteLine(MODULE_NAME, UI.Parsed_options);
foreach(KeyValuePair<string, string> parsedOption in parsedOptions)
AaruConsole.DebugWriteLine(MODULE_NAME, "{0} = {1}", parsedOption.Key, parsedOption.Value);
parsedOptions.Add("debug", debug.ToString());
parsedOptions.Add("debug", settings.Debug.ToString());
if(inputFilter == null)
{
@@ -153,13 +123,13 @@ sealed class ExtractFilesCommand : Command
Encoding encodingClass = null;
if(encoding != null)
if(settings.Encoding != null)
{
try
{
encodingClass = Claunia.Encoding.Encoding.GetEncoding(encoding);
encodingClass = Claunia.Encoding.Encoding.GetEncoding(settings.Encoding);
if(verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
if(settings.Verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
}
catch(ArgumentException)
{
@@ -197,26 +167,26 @@ sealed class ExtractFilesCommand : Command
return (int)ErrorNumber.InvalidArgument;
}
if(verbose)
if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Image_format_identified_by_0_1, imageFormat.Name, imageFormat.Id);
else
AaruConsole.WriteLine(UI.Image_format_identified_by_0, imageFormat.Name);
if(outputDir == null)
if(settings.OutputDir == null)
{
AaruConsole.WriteLine(UI.Output_directory_missing);
return (int)ErrorNumber.MissingArgument;
}
if(Directory.Exists(outputDir) || File.Exists(outputDir))
if(Directory.Exists(settings.OutputDir) || File.Exists(settings.OutputDir))
{
AaruConsole.ErrorWriteLine(UI.Destination_exists_aborting);
return (int)ErrorNumber.FileExists;
}
Directory.CreateDirectory(outputDir);
Directory.CreateDirectory(settings.OutputDir);
try
{
@@ -288,7 +258,7 @@ sealed class ExtractFilesCommand : Command
AaruConsole.WriteLine(UI._0_partitions_found, partitions.Count);
for(var i = 0; i < partitions.Count; i++)
for(int i = 0; i < partitions.Count; i++)
{
AaruConsole.WriteLine();
AaruConsole.WriteLine($"[bold]{string.Format(UI.Partition_0, partitions[i].Sequence)}[/]");
@@ -326,7 +296,11 @@ sealed class ExtractFilesCommand : Command
{
ctx.AddTask(UI.Mounting_filesystem).IsIndeterminate();
error = fs.Mount(imageFormat, partitions[i], encodingClass, parsedOptions, @namespace);
error = fs.Mount(imageFormat,
partitions[i],
encodingClass,
parsedOptions,
settings.Namespace);
});
if(error == ErrorNumber.NoError)
@@ -335,7 +309,7 @@ sealed class ExtractFilesCommand : Command
? "NO NAME"
: fs.Metadata.VolumeName;
ExtractFilesInDir("/", fs, volumeName, outputDir, xattrs);
ExtractFilesInDir("/", fs, volumeName, settings.OutputDir, settings.Xattrs);
Statistics.AddFilesystem(fs.Metadata.Type);
}
@@ -354,7 +328,12 @@ sealed class ExtractFilesCommand : Command
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Mounting_filesystem).IsIndeterminate();
error = fs.Mount(imageFormat, partitions[i], encodingClass, parsedOptions, @namespace);
error = fs.Mount(imageFormat,
partitions[i],
encodingClass,
parsedOptions,
settings.Namespace);
});
if(error == ErrorNumber.NoError)
@@ -363,7 +342,7 @@ sealed class ExtractFilesCommand : Command
? "NO NAME"
: fs.Metadata.VolumeName;
ExtractFilesInDir("/", fs, volumeName, outputDir, xattrs);
ExtractFilesInDir("/", fs, volumeName, settings.OutputDir, settings.Xattrs);
Statistics.AddFilesystem(fs.Metadata.Type);
}
@@ -630,7 +609,7 @@ sealed class ExtractFilesCommand : Command
ctx.AddTask(string.Format(UI.Reading_file_0, Markup.Escape(entry)));
task.MaxValue = stat.Length;
var outBuf = new byte[BUFFER_SIZE];
byte[] outBuf = new byte[BUFFER_SIZE];
error = fs.OpenFile(path + "/" + entry, out IFileNode fileNode);
if(error == ErrorNumber.NoError)
@@ -710,4 +689,34 @@ sealed class ExtractFilesCommand : Command
fs.CloseDir(node);
}
#region Nested type: Settings
public class Settings : FilesystemFamily
{
[CommandOption("-e|--encoding")]
[Description("Name of character encoding to use.")]
[DefaultValue(null)]
public string Encoding { get; init; }
[CommandOption("-O|--options")]
[Description("Comma separated name=value pairs of options to pass to filesystem plugin.")]
[DefaultValue(null)]
public string Options { get; init; }
[CommandOption("-x|--xattrs")]
[Description("Extract extended attributes if present.")]
[DefaultValue(false)]
public bool Xattrs { get; init; }
[CommandOption("-n|--namespace")]
[Description("Namespace to use for filenames.")]
[DefaultValue(null)]
public string Namespace { get; init; }
[CommandArgument(1, "<output-dir>")]
[Description("Directory where extracted files will be created. Will abort if it exists")]
public string OutputDir { get; init; }
[CommandArgument(0, "<image-path>")]
[Description("Disc image path")]
public string ImagePath { get; init; }
}
#endregion
}

View File

@@ -30,21 +30,6 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System.CommandLine;
using Aaru.Localization;
namespace Aaru.Commands.Filesystem;
sealed class FilesystemFamily : Command
{
public FilesystemFamily() : base("filesystem", UI.Filesystem_Command_Family_Description)
{
AddAlias("fi");
AddAlias("fs");
AddCommand(new FilesystemInfoCommand());
AddCommand(new ListOptionsCommand());
AddCommand(new ExtractFilesCommand());
AddCommand(new LsCommand());
}
}
class FilesystemFamily : BaseSettings {}

View File

@@ -32,8 +32,7 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.Text;
using Aaru.CommonTypes;
using Aaru.CommonTypes.AaruMetadata;
@@ -43,40 +42,20 @@ using Aaru.Console;
using Aaru.Core;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
using Partition = Aaru.CommonTypes.Partition;
namespace Aaru.Commands.Filesystem;
sealed class FilesystemInfoCommand : Command
sealed class FilesystemInfoCommand : Command<FilesystemInfoCommand.Settings>
{
const string MODULE_NAME = "Fs-info command";
public FilesystemInfoCommand() : base("info", UI.Filesystem_Info_Command_Description)
{
Add(new Option<string>(["--encoding", "-e"], () => null, UI.Name_of_character_encoding_to_use));
Add(new Option<bool>(["--filesystems", "-f"],
() => true,
UI.Searches_and_prints_information_about_filesystems));
Add(new Option<bool>(["--partitions", "-p"], () => true, UI.Searches_and_interprets_partitions));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Media_image_path,
Name = "image-path"
});
Handler = CommandHandler.Create(typeof(FilesystemInfoCommand).GetMethod(nameof(Invoke)));
}
public static int Invoke(bool verbose, bool debug, string encoding, bool filesystems, bool partitions,
string imagePath)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -94,7 +73,7 @@ sealed class FilesystemInfoCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -107,19 +86,19 @@ sealed class FilesystemInfoCommand : Command
Statistics.AddCommand("fs-info");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--filesystems={0}", filesystems);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--partitions={0}", partitions);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--filesystems={0}", settings.Filesystems);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--partitions={0}", settings.Partitions);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
});
if(inputFilter == null)
@@ -131,13 +110,13 @@ sealed class FilesystemInfoCommand : Command
Encoding encodingClass = null;
if(encoding != null)
if(settings.Encoding != null)
{
try
{
encodingClass = Claunia.Encoding.Encoding.GetEncoding(encoding);
encodingClass = Claunia.Encoding.Encoding.GetEncoding(settings.Encoding);
if(verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
if(settings.Verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
}
catch(ArgumentException)
{
@@ -149,7 +128,7 @@ sealed class FilesystemInfoCommand : Command
PluginRegister plugins = PluginRegister.Singleton;
var checkRaw = false;
bool checkRaw = false;
try
{
@@ -177,7 +156,7 @@ sealed class FilesystemInfoCommand : Command
return (int)ErrorNumber.InvalidArgument;
}
if(verbose)
if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Image_format_identified_by_0_1, imageFormat.Name, imageFormat.Id);
else
AaruConsole.WriteLine(UI.Image_format_identified_by_0, imageFormat.Name);
@@ -202,7 +181,7 @@ sealed class FilesystemInfoCommand : Command
return (int)opened;
}
if(verbose)
if(settings.Verbose)
{
ImageInfo.PrintImageInfo(imageFormat);
AaruConsole.WriteLine();
@@ -225,7 +204,7 @@ sealed class FilesystemInfoCommand : Command
IFilesystem fs;
string information;
if(partitions)
if(settings.Partitions)
{
List<Partition> partitionsList = null;
@@ -241,7 +220,7 @@ sealed class FilesystemInfoCommand : Command
{
AaruConsole.DebugWriteLine(MODULE_NAME, UI.No_partitions_found);
if(!filesystems)
if(!settings.Filesystems)
{
AaruConsole.WriteLine(UI.No_partitions_found_not_searching_for_filesystems);
@@ -254,7 +233,7 @@ sealed class FilesystemInfoCommand : Command
{
AaruConsole.WriteLine(UI._0_partitions_found, partitionsList.Count);
for(var i = 0; i < partitionsList.Count; i++)
for(int i = 0; i < partitionsList.Count; i++)
{
Table table = new()
{
@@ -283,7 +262,7 @@ sealed class FilesystemInfoCommand : Command
AnsiConsole.Write(table);
if(!filesystems) continue;
if(!settings.Filesystems) continue;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
@@ -425,4 +404,27 @@ sealed class FilesystemInfoCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : FilesystemFamily
{
[Description("Name of character encoding to use.")]
[DefaultValue(null)]
[CommandOption("-e|--encoding")]
public string Encoding { get; init; }
[Description("Searches and prints information about filesystems.")]
[DefaultValue(true)]
[CommandOption("-p|--partitions")]
public bool Partitions { get; init; }
[Description("Searches and interprets partitions.")]
[CommandOption("-f|--filesystems")]
[DefaultValue(true)]
public bool Filesystems { get; init; }
[Description("Media image path")]
[CommandArgument(0, "<image-path>")]
public string ImagePath { get; init; }
}
#endregion
}

View File

@@ -32,8 +32,7 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.Linq;
using System.Text;
using Aaru.CommonTypes;
@@ -45,43 +44,19 @@ using Aaru.Core;
using Aaru.Localization;
using JetBrains.Annotations;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Filesystem;
sealed class LsCommand : Command
sealed class LsCommand : Command<LsCommand.Settings>
{
const string MODULE_NAME = "Ls command";
public LsCommand() : base("list", UI.Filesystem_List_Command_Description)
{
AddAlias("ls");
Add(new Option<string>(["--encoding", "-e"], () => null, UI.Name_of_character_encoding_to_use));
Add(new Option<bool>(["--long-format", "-l"], () => true, UI.Use_long_format));
Add(new Option<string>(["--options", "-O"],
() => null,
UI.Comma_separated_name_value_pairs_of_filesystem_options));
Add(new Option<string>(["--namespace", "-n"], () => null, UI.Namespace_to_use_for_filenames));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Media_image_path,
Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string encoding, string imagePath, bool longFormat,
string @namespace, string options)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -99,7 +74,7 @@ sealed class LsCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -110,11 +85,11 @@ sealed class LsCommand : Command
};
}
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(options ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(settings.Options ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
Statistics.AddCommand("ls");
IFilter inputFilter = null;
@@ -122,16 +97,16 @@ sealed class LsCommand : Command
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
});
Dictionary<string, string> parsedOptions = Core.Options.Parse(options);
Dictionary<string, string> parsedOptions = Options.Parse(settings.Options);
AaruConsole.DebugWriteLine(MODULE_NAME, UI.Parsed_options);
foreach(KeyValuePair<string, string> parsedOption in parsedOptions)
AaruConsole.DebugWriteLine(MODULE_NAME, "{0} = {1}", parsedOption.Key, parsedOption.Value);
parsedOptions.Add("debug", debug.ToString());
parsedOptions.Add("debug", settings.Debug.ToString());
if(inputFilter == null)
{
@@ -142,13 +117,13 @@ sealed class LsCommand : Command
Encoding encodingClass = null;
if(encoding != null)
if(settings.Encoding != null)
{
try
{
encodingClass = Claunia.Encoding.Encoding.GetEncoding(encoding);
encodingClass = Claunia.Encoding.Encoding.GetEncoding(settings.Encoding);
if(verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
if(settings.Verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
}
catch(ArgumentException)
{
@@ -186,7 +161,7 @@ sealed class LsCommand : Command
return (int)ErrorNumber.InvalidArgument;
}
if(verbose)
if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Image_format_identified_by_0_1, imageFormat.Name, imageFormat.Id);
else
AaruConsole.WriteLine(UI.Image_format_identified_by_0, imageFormat.Name);
@@ -261,7 +236,7 @@ sealed class LsCommand : Command
AaruConsole.WriteLine(UI._0_partitions_found, partitions.Count);
for(var i = 0; i < partitions.Count; i++)
for(int i = 0; i < partitions.Count; i++)
{
AaruConsole.WriteLine();
AaruConsole.WriteLine($"[bold]{string.Format(UI.Partition_0, partitions[i].Sequence)}[/]");
@@ -298,12 +273,16 @@ sealed class LsCommand : Command
{
ctx.AddTask(UI.Mounting_filesystem).IsIndeterminate();
error = fs.Mount(imageFormat, partitions[i], encodingClass, parsedOptions, @namespace);
error = fs.Mount(imageFormat,
partitions[i],
encodingClass,
parsedOptions,
settings.Namespace);
});
if(error == ErrorNumber.NoError)
{
ListFilesInDir("/", fs, longFormat);
ListFilesInDir("/", fs, settings.LongFormat);
Statistics.AddFilesystem(fs.Metadata.Type);
}
@@ -322,12 +301,17 @@ sealed class LsCommand : Command
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Mounting_filesystem).IsIndeterminate();
error = fs.Mount(imageFormat, partitions[i], encodingClass, parsedOptions, @namespace);
error = fs.Mount(imageFormat,
partitions[i],
encodingClass,
parsedOptions,
settings.Namespace);
});
if(error == ErrorNumber.NoError)
{
ListFilesInDir("/", fs, longFormat);
ListFilesInDir("/", fs, settings.LongFormat);
Statistics.AddFilesystem(fs.Metadata.Type);
}
@@ -442,4 +426,31 @@ sealed class LsCommand : Command
stats.Where(e => e.Value?.Attributes.HasFlag(FileAttributes.Directory) == true))
ListFilesInDir(path + "/" + subdirectory.Key, fs, longFormat);
}
#region Nested type: Settings
public class Settings : FilesystemFamily
{
[Description("Name of character encoding to use.")]
[CommandOption("-e|--encoding")]
[DefaultValue(null)]
public string Encoding { get; init; }
[Description("Use long format.")]
[CommandOption("-l|--long-format")]
[DefaultValue(true)]
public bool LongFormat { get; init; }
[Description("Comma separated name=value pairs of options to pass to filesystem plugin.")]
[CommandOption("-O|--options")]
[DefaultValue(null)]
public string Options { get; init; }
[Description("Namespace to use for filenames.")]
[CommandOption("-n|--namespace")]
[DefaultValue(null)]
public string Namespace { get; init; }
[Description("Media image path")]
[CommandArgument(0, "<image-path>")]
public string ImagePath { get; init; }
}
#endregion
}

View File

@@ -31,8 +31,6 @@
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.Linq;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
@@ -42,21 +40,20 @@ using Aaru.Core;
using Aaru.Localization;
using JetBrains.Annotations;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Filesystem;
sealed class ListOptionsCommand : Command
sealed class ListOptionsCommand : Command<ListOptionsCommand.Settings>
{
const string MODULE_NAME = "List-Options command";
public ListOptionsCommand() : base("options", UI.Filesystem_Options_Command_Description) =>
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
public override int Execute(CommandContext context, Settings settings)
public static int Invoke(bool debug, bool verbose)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -74,7 +71,7 @@ sealed class ListOptionsCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -85,8 +82,8 @@ sealed class ListOptionsCommand : Command
};
}
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
Statistics.AddCommand("list-options");
PluginRegister plugins = PluginRegister.Singleton;
@@ -141,4 +138,10 @@ sealed class ListOptionsCommand : Command
return type == typeof(string) ? UI.TypeToString_string : type.ToString();
}
#region Nested type: Settings
public class Settings : FilesystemFamily {}
#endregion
}

View File

@@ -30,9 +30,6 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.Linq;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
@@ -41,21 +38,20 @@ using Aaru.Console;
using Aaru.Core;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands;
sealed class FormatsCommand : Command
sealed class FormatsCommand : Command<FormatsCommand.Settings>
{
const string MODULE_NAME = "Formats command";
public FormatsCommand() : base("formats", UI.List_Formats_Command_Description) =>
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
public override int Execute(CommandContext context, Settings settings)
public static int Invoke(bool verbose, bool debug)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -73,7 +69,7 @@ sealed class FormatsCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -86,8 +82,8 @@ sealed class FormatsCommand : Command
Statistics.AddCommand("formats");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
PluginRegister plugins = PluginRegister.Singleton;
@@ -96,13 +92,13 @@ sealed class FormatsCommand : Command
Title = new TableTitle(string.Format(UI.Supported_filters_0, PluginRegister.Singleton.Filters.Count))
};
if(verbose) table.AddColumn(UI.Title_GUID);
if(settings.Verbose) table.AddColumn(UI.Title_GUID);
table.AddColumn(UI.Title_Filter);
foreach(IFilter filter in PluginRegister.Singleton.Filters.Values)
{
if(verbose)
if(settings.Verbose)
table.AddRow(filter.Id.ToString(), Markup.Escape(filter.Name));
else
table.AddRow(Markup.Escape(filter.Name));
@@ -119,14 +115,14 @@ sealed class FormatsCommand : Command
.ContainsKey(t.Key))))
};
if(verbose) table.AddColumn(UI.Title_GUID);
if(settings.Verbose) table.AddColumn(UI.Title_GUID);
table.AddColumn(UI.Title_Media_image_format);
foreach(IMediaImage imagePlugin in
plugins.MediaImages.Values.Where(t => !plugins.WritableImages.ContainsKey(t.Name)))
{
if(verbose)
if(settings.Verbose)
table.AddRow(imagePlugin.Id.ToString(), Markup.Escape(imagePlugin.Name));
else
table.AddRow(Markup.Escape(imagePlugin.Name));
@@ -141,7 +137,7 @@ sealed class FormatsCommand : Command
Title = new TableTitle(string.Format(UI.Read_write_media_image_formats_0, plugins.WritableImages.Count))
};
if(verbose) table.AddColumn(UI.Title_GUID);
if(settings.Verbose) table.AddColumn(UI.Title_GUID);
table.AddColumn(UI.Title_Media_image_format);
@@ -149,7 +145,7 @@ sealed class FormatsCommand : Command
{
if(plugin is null) continue;
if(verbose)
if(settings.Verbose)
table.AddRow(plugin.Id.ToString(), Markup.Escape(plugin.Name));
else
table.AddRow(Markup.Escape(plugin.Name));
@@ -170,13 +166,13 @@ sealed class FormatsCommand : Command
idOnlyFilesystems.Count))
};
if(verbose) table.AddColumn(UI.Title_GUID);
if(settings.Verbose) table.AddColumn(UI.Title_GUID);
table.AddColumn(UI.Title_Filesystem);
foreach(IFilesystem fs in idOnlyFilesystems)
{
if(verbose)
if(settings.Verbose)
table.AddRow(fs.Id.ToString(), Markup.Escape(fs.Name));
else
table.AddRow(Markup.Escape(fs.Name));
@@ -192,7 +188,7 @@ sealed class FormatsCommand : Command
plugins.ReadOnlyFilesystems.Count))
};
if(verbose) table.AddColumn(UI.Title_GUID);
if(settings.Verbose) table.AddColumn(UI.Title_GUID);
table.AddColumn(UI.Title_Filesystem);
@@ -200,7 +196,7 @@ sealed class FormatsCommand : Command
{
if(fs is null) continue;
if(verbose)
if(settings.Verbose)
table.AddRow(fs.Id.ToString(), Markup.Escape(fs.Name));
else
table.AddRow(Markup.Escape(fs.Name));
@@ -215,7 +211,7 @@ sealed class FormatsCommand : Command
Title = new TableTitle(string.Format(UI.Supported_partitioning_schemes_0, plugins.Partitions.Count))
};
if(verbose) table.AddColumn(UI.Title_GUID);
if(settings.Verbose) table.AddColumn(UI.Title_GUID);
table.AddColumn(UI.Title_Scheme);
@@ -223,7 +219,7 @@ sealed class FormatsCommand : Command
{
if(plugin is null) continue;
if(verbose)
if(settings.Verbose)
table.AddRow(plugin.Id.ToString(), Markup.Escape(plugin.Name));
else
table.AddRow(Markup.Escape(plugin.Name));
@@ -238,7 +234,7 @@ sealed class FormatsCommand : Command
Title = new TableTitle(string.Format(UI.Supported_archive_formats_0, plugins.Archives.Count))
};
if(verbose) table.AddColumn(UI.Title_GUID);
if(settings.Verbose) table.AddColumn(UI.Title_GUID);
table.AddColumn("Archive format");
@@ -246,7 +242,7 @@ sealed class FormatsCommand : Command
{
if(archive is null) continue;
if(verbose)
if(settings.Verbose)
table.AddRow(archive.Id.ToString(), Markup.Escape(archive.Name));
else
table.AddRow(Markup.Escape(archive.Name));
@@ -256,4 +252,10 @@ sealed class FormatsCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : BaseSettings {}
#endregion
}

View File

@@ -32,8 +32,7 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
@@ -42,10 +41,11 @@ using Aaru.Console;
using Aaru.Core;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image;
sealed class ChecksumCommand : Command
sealed class ChecksumCommand : Command<ChecksumCommand.Settings>
{
// How many sectors to read at once
const uint SECTORS_TO_READ = 256;
@@ -54,51 +54,11 @@ sealed class ChecksumCommand : Command
const int BYTES_TO_READ = 65536;
const string MODULE_NAME = "Checksum command";
public ChecksumCommand() : base("checksum", UI.Image_Checksum_Command_Description)
{
AddAlias("chk");
Add(new Option<bool>(["--adler32", "-a"], () => false, UI.Calculates_Adler_32));
Add(new Option<bool>("--crc16", () => true, UI.Calculates_CRC16));
Add(new Option<bool>(["--crc32", "-c"], () => true, UI.Calculates_CRC32));
Add(new Option<bool>("--crc64", () => true, UI.Calculates_CRC64_ECMA));
Add(new Option<bool>("--fletcher16", () => false, UI.Calculates_Fletcher_16));
Add(new Option<bool>("--fletcher32", () => false, UI.Calculates_Fletcher_32));
Add(new Option<bool>(["--md5", "-m"], () => true, UI.Calculates_MD5));
Add(new Option<bool>(["--separated-tracks", "-t"], () => true, UI.Checksums_each_track_separately));
Add(new Option<bool>(["--sha1", "-s"], () => true, UI.Calculates_SHA1));
Add(new Option<bool>("--sha256", () => false, UI.Calculates_SHA256));
Add(new Option<bool>("--sha384", () => false, UI.Calculates_SHA384));
Add(new Option<bool>("--sha512", () => true, UI.Calculates_SHA512));
Add(new Option<bool>(["--spamsum", "-f"], () => true, UI.Calculates_SpamSum_fuzzy_hash));
Add(new Option<bool>(["--whole-disc", "-w"], () => true, UI.Checksums_the_whole_disc));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Media_image_path,
Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, bool adler32, bool crc16, bool crc32, bool crc64,
bool fletcher16, bool fletcher32, bool md5, bool sha1, bool sha256, bool sha384,
bool sha512, bool spamSum, string imagePath, bool separatedTracks, bool wholeDisc)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -116,7 +76,7 @@ sealed class ChecksumCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -129,30 +89,30 @@ sealed class ChecksumCommand : Command
Statistics.AddCommand("checksum");
AaruConsole.DebugWriteLine(MODULE_NAME, "--adler32={0}", adler32);
AaruConsole.DebugWriteLine(MODULE_NAME, "--crc16={0}", crc16);
AaruConsole.DebugWriteLine(MODULE_NAME, "--crc32={0}", crc32);
AaruConsole.DebugWriteLine(MODULE_NAME, "--crc64={0}", crc64);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fletcher16={0}", fletcher16);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fletcher32={0}", fletcher32);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--md5={0}", md5);
AaruConsole.DebugWriteLine(MODULE_NAME, "--separated-tracks={0}", separatedTracks);
AaruConsole.DebugWriteLine(MODULE_NAME, "--sha1={0}", sha1);
AaruConsole.DebugWriteLine(MODULE_NAME, "--sha256={0}", sha256);
AaruConsole.DebugWriteLine(MODULE_NAME, "--sha384={0}", sha384);
AaruConsole.DebugWriteLine(MODULE_NAME, "--sha512={0}", sha512);
AaruConsole.DebugWriteLine(MODULE_NAME, "--spamsum={0}", spamSum);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--whole-disc={0}", wholeDisc);
AaruConsole.DebugWriteLine(MODULE_NAME, "--adler32={0}", settings.Adler32);
AaruConsole.DebugWriteLine(MODULE_NAME, "--crc16={0}", settings.Crc16);
AaruConsole.DebugWriteLine(MODULE_NAME, "--crc32={0}", settings.Crc32);
AaruConsole.DebugWriteLine(MODULE_NAME, "--crc64={0}", settings.Crc64);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fletcher16={0}", settings.Fletcher16);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fletcher32={0}", settings.Fletcher32);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--md5={0}", settings.Md5);
AaruConsole.DebugWriteLine(MODULE_NAME, "--separated-tracks={0}", settings.SeparatedTracks);
AaruConsole.DebugWriteLine(MODULE_NAME, "--sha1={0}", settings.Sha1);
AaruConsole.DebugWriteLine(MODULE_NAME, "--sha256={0}", settings.Sha256);
AaruConsole.DebugWriteLine(MODULE_NAME, "--sha384={0}", settings.Sha384);
AaruConsole.DebugWriteLine(MODULE_NAME, "--sha512={0}", settings.Sha512);
AaruConsole.DebugWriteLine(MODULE_NAME, "--spamsum={0}", settings.SpamSum);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--whole-disc={0}", settings.WholeDisc);
IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
});
if(inputFilter == null)
@@ -198,29 +158,29 @@ sealed class ChecksumCommand : Command
Statistics.AddFilter(inputFilter.Name);
var enabledChecksums = new EnableChecksum();
if(adler32) enabledChecksums |= EnableChecksum.Adler32;
if(settings.Adler32) enabledChecksums |= EnableChecksum.Adler32;
if(crc16) enabledChecksums |= EnableChecksum.Crc16;
if(settings.Crc16) enabledChecksums |= EnableChecksum.Crc16;
if(crc32) enabledChecksums |= EnableChecksum.Crc32;
if(settings.Crc32) enabledChecksums |= EnableChecksum.Crc32;
if(crc64) enabledChecksums |= EnableChecksum.Crc64;
if(settings.Crc64) enabledChecksums |= EnableChecksum.Crc64;
if(md5) enabledChecksums |= EnableChecksum.Md5;
if(settings.Md5) enabledChecksums |= EnableChecksum.Md5;
if(sha1) enabledChecksums |= EnableChecksum.Sha1;
if(settings.Sha1) enabledChecksums |= EnableChecksum.Sha1;
if(sha256) enabledChecksums |= EnableChecksum.Sha256;
if(settings.Sha256) enabledChecksums |= EnableChecksum.Sha256;
if(sha384) enabledChecksums |= EnableChecksum.Sha384;
if(settings.Sha384) enabledChecksums |= EnableChecksum.Sha384;
if(sha512) enabledChecksums |= EnableChecksum.Sha512;
if(settings.Sha512) enabledChecksums |= EnableChecksum.Sha512;
if(spamSum) enabledChecksums |= EnableChecksum.SpamSum;
if(settings.SpamSum) enabledChecksums |= EnableChecksum.SpamSum;
if(fletcher16) enabledChecksums |= EnableChecksum.Fletcher16;
if(settings.Fletcher16) enabledChecksums |= EnableChecksum.Fletcher16;
if(fletcher32) enabledChecksums |= EnableChecksum.Fletcher32;
if(settings.Fletcher32) enabledChecksums |= EnableChecksum.Fletcher32;
Checksum mediaChecksum = null;
@@ -233,7 +193,7 @@ sealed class ChecksumCommand : Command
{
Checksum trackChecksum = null;
if(wholeDisc) mediaChecksum = new Checksum(enabledChecksums);
if(settings.WholeDisc) mediaChecksum = new Checksum(enabledChecksums);
List<Track> inputTracks = opticalInput.Tracks;
@@ -273,7 +233,7 @@ sealed class ChecksumCommand : Command
currentTrack.StartSector,
currentTrack.EndSector);
if(separatedTracks) trackChecksum = new Checksum(enabledChecksums);
if(settings.SeparatedTracks) trackChecksum = new Checksum(enabledChecksums);
ulong sectors = currentTrack.EndSector - currentTrack.StartSector + 1;
@@ -342,9 +302,9 @@ sealed class ChecksumCommand : Command
doneSectors += sectors - doneSectors;
}
if(wholeDisc) mediaChecksum?.Update(sector);
if(settings.WholeDisc) mediaChecksum?.Update(sector);
if(separatedTracks) trackChecksum?.Update(sector);
if(settings.SeparatedTracks) trackChecksum?.Update(sector);
trackTask.Value = doneSectors;
}
@@ -352,7 +312,7 @@ sealed class ChecksumCommand : Command
trackTask.StopTask();
AaruConsole.WriteLine();
if(!separatedTracks) continue;
if(!settings.SeparatedTracks) continue;
if(trackChecksum == null) continue;
@@ -376,7 +336,7 @@ sealed class ChecksumCommand : Command
}
*/
if(!wholeDisc) return;
if(!settings.WholeDisc) return;
if(mediaChecksum == null) return;
@@ -393,7 +353,7 @@ sealed class ChecksumCommand : Command
}
catch(Exception ex)
{
if(debug)
if(settings.Debug)
AaruConsole.DebugWriteLine(Localization.Core.Could_not_get_tracks_because_0, ex.Message);
else
AaruConsole.WriteLine("Unable to get separate tracks, not checksumming them");
@@ -405,7 +365,7 @@ sealed class ChecksumCommand : Command
{
Checksum trackChecksum = null;
if(wholeDisc) mediaChecksum = new Checksum(enabledChecksums);
if(settings.WholeDisc) mediaChecksum = new Checksum(enabledChecksums);
ulong previousFileEnd = 0;
@@ -423,7 +383,7 @@ sealed class ChecksumCommand : Command
tapeTask.Description =
string.Format(UI.Hashing_file_0_of_1, currentFile.File, tapeImage.Files.Count);
if(currentFile.FirstBlock - previousFileEnd != 0 && wholeDisc)
if(currentFile.FirstBlock - previousFileEnd != 0 && settings.WholeDisc)
{
ProgressTask preFileTask = ctx.AddTask(UI.Hashing_sector);
preFileTask.MaxValue = currentFile.FirstBlock - previousFileEnd;
@@ -457,7 +417,7 @@ sealed class ChecksumCommand : Command
currentFile.FirstBlock,
currentFile.LastBlock);
if(separatedTracks) trackChecksum = new Checksum(enabledChecksums);
if(settings.SeparatedTracks) trackChecksum = new Checksum(enabledChecksums);
ulong sectors = currentFile.LastBlock - currentFile.FirstBlock + 1;
ulong doneSectors = 0;
@@ -524,15 +484,15 @@ sealed class ChecksumCommand : Command
fileTask.Value = doneSectors;
if(wholeDisc) mediaChecksum?.Update(sector);
if(settings.WholeDisc) mediaChecksum?.Update(sector);
if(separatedTracks) trackChecksum?.Update(sector);
if(settings.SeparatedTracks) trackChecksum?.Update(sector);
}
fileTask.StopTask();
AaruConsole.WriteLine();
if(separatedTracks)
if(settings.SeparatedTracks)
{
if(trackChecksum != null)
{
@@ -549,7 +509,7 @@ sealed class ChecksumCommand : Command
tapeTask.Increment(1);
}
if(tapeImage.Info.Sectors - previousFileEnd == 0 || !wholeDisc) return;
if(tapeImage.Info.Sectors - previousFileEnd == 0 || !settings.WholeDisc) return;
ProgressTask postFileTask = ctx.AddTask(UI.Hashing_sector);
postFileTask.MaxValue = tapeImage.Info.Sectors - previousFileEnd;
@@ -576,7 +536,7 @@ sealed class ChecksumCommand : Command
if(errno != ErrorNumber.NoError) return (int)errno;
if(wholeDisc && mediaChecksum != null)
if(settings.WholeDisc && mediaChecksum != null)
{
AaruConsole.WriteLine();
@@ -603,8 +563,8 @@ sealed class ChecksumCommand : Command
ProgressTask imageTask = ctx.AddTask(UI.Hashing_image);
ulong length = byteAddressableImage.Info.Sectors;
imageTask.MaxValue = length;
ulong doneBytes = 0;
var data = new byte[BYTES_TO_READ];
ulong doneBytes = 0;
byte[] data = new byte[BYTES_TO_READ];
while(doneBytes < length)
{
@@ -763,4 +723,71 @@ sealed class ChecksumCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : ImageFamily
{
[Description("Calculates Adler-32.")]
[CommandOption("-a|--adler32")]
[DefaultValue(false)]
public bool Adler32 { get; init; }
[Description("Calculates CRC16.")]
[CommandOption("--crc16")]
[DefaultValue(true)]
public bool Crc16 { get; init; }
[Description("Calculates CRC32.")]
[CommandOption("-c|--crc32")]
[DefaultValue(true)]
public bool Crc32 { get; init; }
[Description("Calculates CRC64 (ECMA).")]
[CommandOption("--crc64")]
[DefaultValue(true)]
public bool Crc64 { get; init; }
[Description("Calculates Fletcher-16.")]
[CommandOption("--fletcher16")]
[DefaultValue(false)]
public bool Fletcher16 { get; init; }
[Description("Calculates Fletcher-32.")]
[CommandOption("--fletcher32")]
[DefaultValue(false)]
public bool Fletcher32 { get; init; }
[Description("Calculates MD5.")]
[CommandOption("-m|--md5")]
[DefaultValue(true)]
public bool Md5 { get; init; }
[Description("Calculates SHA1.")]
[CommandOption("-s|--sha1")]
[DefaultValue(true)]
public bool Sha1 { get; init; }
[Description("Calculates SHA256.")]
[CommandOption("--sha256")]
[DefaultValue(false)]
public bool Sha256 { get; init; }
[Description("Calculates SHA384.")]
[CommandOption("--sha384")]
[DefaultValue(false)]
public bool Sha384 { get; init; }
[Description("Calculates SHA512.")]
[CommandOption("--sha512")]
[DefaultValue(true)]
public bool Sha512 { get; init; }
[Description("Calculates SpamSum fuzzy hash.")]
[CommandOption("-f|--spamsum")]
[DefaultValue(true)]
public bool SpamSum { get; init; }
[Description("Checksums the whole disc.")]
[CommandOption("-w|--whole-disc")]
[DefaultValue(true)]
public bool WholeDisc { get; init; }
[Description("Checksums each track separately.")]
[CommandOption("-t|--separated-tracks")]
[DefaultValue(true)]
public bool SeparatedTracks { get; init; }
[Description("Media image path")]
[CommandArgument(0, "<image-path>")]
public string ImagePath { get; init; }
}
#endregion
}

View File

@@ -32,8 +32,7 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
@@ -45,40 +44,20 @@ using Aaru.Core;
using Aaru.Helpers;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
using ImageInfo = Aaru.CommonTypes.Structs.ImageInfo;
namespace Aaru.Commands.Image;
sealed class CompareCommand : Command
sealed class CompareCommand : Command<CompareCommand.Settings>
{
const string MODULE_NAME = "Compare command";
public CompareCommand() : base("compare", UI.Image_Compare_Command_Description)
{
AddAlias("cmp");
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.First_media_image_path,
Name = "image-path1"
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Second_media_image_path,
Name = "image-path2"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string imagePath1, string imagePath2)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -96,7 +75,7 @@ sealed class CompareCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -109,10 +88,10 @@ sealed class CompareCommand : Command
Statistics.AddCommand("compare");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input1={0}", Markup.Escape(imagePath1 ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input2={0}", Markup.Escape(imagePath2 ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input1={0}", Markup.Escape(settings.ImagePath1 ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input2={0}", Markup.Escape(settings.ImagePath2 ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
IFilter inputFilter1 = null;
IFilter inputFilter2 = null;
@@ -120,13 +99,13 @@ sealed class CompareCommand : Command
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_first_file_filter).IsIndeterminate();
inputFilter1 = PluginRegister.Singleton.GetFilter(imagePath1);
inputFilter1 = PluginRegister.Singleton.GetFilter(settings.ImagePath1);
});
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_second_file_filter).IsIndeterminate();
inputFilter2 = PluginRegister.Singleton.GetFilter(imagePath2);
inputFilter2 = PluginRegister.Singleton.GetFilter(settings.ImagePath2);
});
if(inputFilter1 == null)
@@ -165,7 +144,7 @@ sealed class CompareCommand : Command
return (int)ErrorNumber.UnrecognizedFormat;
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.VerboseWriteLine(UI.First_input_file_format_identified_by_0_1,
input1Format.Name,
@@ -181,7 +160,7 @@ sealed class CompareCommand : Command
return (int)ErrorNumber.UnrecognizedFormat;
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.VerboseWriteLine(UI.Second_input_file_format_identified_by_0_1,
input2Format.Name,
@@ -235,18 +214,18 @@ sealed class CompareCommand : Command
table.AddColumn(UI.Title_Second_Media_image);
table.Columns[0].RightAligned();
if(verbose)
if(settings.Verbose)
{
table.AddRow(UI.Title_File, Markup.Escape(imagePath1), Markup.Escape(imagePath2));
table.AddRow(UI.Title_Media_image_format, input1Format.Name, input2Format.Name);
table.AddRow(UI.Title_File, Markup.Escape(settings.ImagePath1), Markup.Escape(settings.ImagePath2));
table.AddRow(UI.Title_Media_image_format, input1Format.Name, input2Format.Name);
}
else
{
sb.AppendFormat($"[bold]{UI.Title_First_Media_image}:[/] {imagePath1}").AppendLine();
sb.AppendFormat($"[bold]{UI.Title_Second_Media_image}:[/] {imagePath2}").AppendLine();
sb.AppendFormat($"[bold]{UI.Title_First_Media_image}:[/] {settings.ImagePath1}").AppendLine();
sb.AppendFormat($"[bold]{UI.Title_Second_Media_image}:[/] {settings.ImagePath2}").AppendLine();
}
var imagesDiffer = false;
bool imagesDiffer = false;
ErrorNumber errno;
ImageInfo image1Info = input1Format.Info;
@@ -276,7 +255,7 @@ sealed class CompareCommand : Command
}
}
if(verbose)
if(settings.Verbose)
{
table.AddRow(UI.Has_partitions_Question,
image1Info.HasPartitions.ToString(),
@@ -373,35 +352,35 @@ sealed class CompareCommand : Command
{
imagesDiffer = true;
if(!verbose) sb.AppendLine(UI.Image_partitioned_status_differ);
if(!settings.Verbose) sb.AppendLine(UI.Image_partitioned_status_differ);
}
if(image1Info.HasSessions != image2Info.HasSessions)
{
imagesDiffer = true;
if(!verbose) sb.AppendLine(UI.Image_session_status_differ);
if(!settings.Verbose) sb.AppendLine(UI.Image_session_status_differ);
}
if(image1Info.Sectors != image2Info.Sectors)
{
imagesDiffer = true;
if(!verbose) sb.AppendLine(UI.Image_sectors_differ);
if(!settings.Verbose) sb.AppendLine(UI.Image_sectors_differ);
}
if(image1Info.SectorSize != image2Info.SectorSize)
{
imagesDiffer = true;
if(!verbose) sb.AppendLine(UI.Image_sector_size_differ);
if(!settings.Verbose) sb.AppendLine(UI.Image_sector_size_differ);
}
if(image1Info.MediaType != image2Info.MediaType)
{
imagesDiffer = true;
if(!verbose) sb.AppendLine(UI.Media_type_differs);
if(!settings.Verbose) sb.AppendLine(UI.Media_type_differs);
}
if(image1Info.Sectors < image2Info.Sectors)
@@ -409,14 +388,14 @@ sealed class CompareCommand : Command
imagesDiffer = true;
leastSectors = image1Info.Sectors;
if(!verbose) sb.AppendLine(UI.Second_image_has_more_sectors);
if(!settings.Verbose) sb.AppendLine(UI.Second_image_has_more_sectors);
}
else if(image1Info.Sectors > image2Info.Sectors)
{
imagesDiffer = true;
leastSectors = image2Info.Sectors;
if(!verbose) sb.AppendLine(UI.First_image_has_more_sectors);
if(!settings.Verbose) sb.AppendLine(UI.First_image_has_more_sectors);
}
else
leastSectors = image1Info.Sectors;
@@ -506,8 +485,8 @@ sealed class CompareCommand : Command
ProgressTask task = ctx.AddTask(UI.Comparing_images);
task.IsIndeterminate = true;
var data1 = new byte[input1ByteAddressable.Info.Sectors];
var data2 = new byte[input2ByteAddressable.Info.Sectors];
byte[] data1 = new byte[input1ByteAddressable.Info.Sectors];
byte[] data2 = new byte[input2ByteAddressable.Info.Sectors];
byte[] tmp;
input1ByteAddressable.ReadBytes(data1, 0, data1.Length, out int bytesRead);
@@ -540,11 +519,25 @@ sealed class CompareCommand : Command
sb.AppendLine(imagesDiffer ? UI.Images_differ : UI.Images_do_not_differ);
if(verbose)
if(settings.Verbose)
AnsiConsole.Write(table);
else
AaruConsole.WriteLine(sb.ToString());
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : ImageFamily
{
[Description("First media image path")]
[CommandArgument(0, "<image-path1>")]
public string ImagePath1 { get; init; }
[Description("Second media image path")]
[CommandArgument(1, "<image-path1>")]
public string ImagePath2 { get; init; }
}
#endregion
}

View File

@@ -32,8 +32,7 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
@@ -52,6 +51,7 @@ using Aaru.Devices;
using Aaru.Localization;
using Schemas;
using Spectre.Console;
using Spectre.Console.Cli;
using File = System.IO.File;
using ImageInfo = Aaru.CommonTypes.Structs.ImageInfo;
using MediaType = Aaru.CommonTypes.MediaType;
@@ -63,95 +63,15 @@ using Version = Aaru.CommonTypes.Interop.Version;
namespace Aaru.Commands.Image;
sealed class ConvertImageCommand : Command
sealed class ConvertImageCommand : Command<ConvertImageCommand.Settings>
{
const string MODULE_NAME = "Convert-image command";
public ConvertImageCommand() : base("convert", UI.Image_Convert_Command_Description)
{
Add(new Option<string>(["--cicm-xml", "-x"], () => null, UI.Take_metadata_from_existing_CICM_XML_sidecar));
Add(new Option<string>("--comments", () => null, UI.Image_comments));
Add(new Option<int>(["--count", "-c"], () => 64, UI.How_many_sectors_to_convert_at_once));
Add(new Option<string>("--creator", () => null, UI.Who_person_created_the_image));
Add(new Option<string>("--drive-manufacturer", () => null, UI.Manufacturer_of_drive_read_the_media_by_image));
Add(new Option<string>("--drive-model", () => null, UI.Model_of_drive_read_the_media_by_image));
Add(new Option<string>("--drive-revision", () => null, UI.Firmware_revision_of_drive_read_the_media_by_image));
Add(new Option<string>("--drive-serial", () => null, UI.Serial_number_of_drive_read_the_media_by_image));
Add(new Option<bool>(["--force", "-f"], UI.Continue_conversion_even_if_data_lost));
Add(new Option<string>(["--format", "-p"],
() => null,
UI.Format_of_the_output_image_as_plugin_name_or_plugin_id));
Add(new Option<string>("--media-barcode", () => null, UI.Barcode_of_the_media_by_image));
Add(new Option<int>("--media-lastsequence", () => 0, UI.Last_media_of_sequence_by_image));
Add(new Option<string>("--media-manufacturer", () => null, UI.Manufacturer_of_media_by_image));
Add(new Option<string>("--media-model", () => null, UI.Model_of_media_by_image));
Add(new Option<string>("--media-partnumber", () => null, UI.Part_number_of_media_by_image));
Add(new Option<int>("--media-sequence", () => 0, UI.Number_in_sequence_for_media_by_image));
Add(new Option<string>("--media-serial", () => null, UI.Serial_number_of_media_by_image));
Add(new Option<string>("--media-title", () => null, UI.Title_of_media_represented_by_image));
Add(new Option<string>(["--options", "-O"], () => null, UI.Comma_separated_name_value_pairs_of_image_options));
Add(new Option<string>(["--resume-file", "-r"], () => null, UI.Take_dump_hardware_from_existing_resume));
Add(new Option<string>(["--geometry", "-g"], () => null, UI.Force_geometry_help));
Add(new Option<bool>(["--fix-subchannel-position"], () => true, UI.Fix_subchannel_position_help));
Add(new Option<bool>(["--fix-subchannel"], () => false, UI.Fix_subchannel_help));
Add(new Option<bool>(["--fix-subchannel-crc"], () => false, UI.Fix_subchannel_crc_help));
Add(new Option<bool>(["--generate-subchannels"], () => false, UI.Generates_subchannels_help));
Add(new Option<bool>(["--decrypt"], () => false, UI.Decrypt_sectors_help));
Add(new Option<string>(["--aaru-metadata", "-m"],
() => null,
"Take metadata from existing Aaru Metadata sidecar."));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Input_image_path,
Name = "input-path"
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Output_image_path,
Name = "output-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool verbose, bool debug, string cicmXml, string comments, int count, string creator,
string driveFirmwareRevision, string driveManufacturer, string driveModel,
string driveSerialNumber, bool force, string inputPath, int lastMediaSequence,
string mediaBarcode, string mediaManufacturer, string mediaModel, string mediaPartNumber,
int mediaSequence, string mediaSerialNumber, string mediaTitle, string outputPath,
string options, string resumeFile, string format, string geometry,
bool fixSubchannelPosition, bool fixSubchannel, bool fixSubchannelCrc,
bool generateSubchannels, bool decrypt, string aaruMetadata)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -169,7 +89,7 @@ sealed class ConvertImageCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -180,51 +100,71 @@ sealed class ConvertImageCommand : Command
};
}
bool fixSubchannel = settings.FixSubchannel;
bool fixSubchannelCrc = settings.FixSubchannelCrc;
bool fixSubchannelPosition = settings.FixSubchannelPosition;
if(fixSubchannelCrc) fixSubchannel = true;
if(fixSubchannel) fixSubchannelPosition = true;
Statistics.AddCommand("convert-image");
AaruConsole.DebugWriteLine(MODULE_NAME, "--cicm-xml={0}", Markup.Escape(cicmXml ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--comments={0}", Markup.Escape(comments ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--count={0}", count);
AaruConsole.DebugWriteLine(MODULE_NAME, "--creator={0}", Markup.Escape(creator ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--drive-manufacturer={0}", Markup.Escape(driveManufacturer ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--drive-model={0}", Markup.Escape(driveModel ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--drive-revision={0}", Markup.Escape(driveFirmwareRevision ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--drive-serial={0}", Markup.Escape(driveSerialNumber ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--force={0}", force);
AaruConsole.DebugWriteLine(MODULE_NAME, "--format={0}", Markup.Escape(format ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--geometry={0}", Markup.Escape(geometry ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(inputPath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-barcode={0}", Markup.Escape(mediaBarcode ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-lastsequence={0}", lastMediaSequence);
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-manufacturer={0}", Markup.Escape(mediaManufacturer ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-model={0}", Markup.Escape(mediaModel ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-partnumber={0}", Markup.Escape(mediaPartNumber ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-sequence={0}", mediaSequence);
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-serial={0}", Markup.Escape(mediaSerialNumber ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-title={0}", Markup.Escape(mediaTitle ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(options ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(outputPath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--resume-file={0}", Markup.Escape(resumeFile ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--cicm-xml={0}", Markup.Escape(settings.CicmXml ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--comments={0}", Markup.Escape(settings.Comments ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--count={0}", settings.Count);
AaruConsole.DebugWriteLine(MODULE_NAME, "--creator={0}", Markup.Escape(settings.Creator ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME,
"--drive-manufacturer={0}",
Markup.Escape(settings.DriveManufacturer ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--drive-model={0}", Markup.Escape(settings.DriveModel ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME,
"--drive-revision={0}",
Markup.Escape(settings.DriveFirmwareRevision ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--drive-serial={0}", Markup.Escape(settings.DriveSerialNumber ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--force={0}", settings.Force);
AaruConsole.DebugWriteLine(MODULE_NAME, "--format={0}", Markup.Escape(settings.Format ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--geometry={0}", Markup.Escape(settings.Geometry ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.InputPath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-barcode={0}", Markup.Escape(settings.MediaBarcode ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-lastsequence={0}", settings.LastMediaSequence);
AaruConsole.DebugWriteLine(MODULE_NAME,
"--media-manufacturer={0}",
Markup.Escape(settings.MediaManufacturer ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-model={0}", Markup.Escape(settings.MediaModel ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME,
"--media-partnumber={0}",
Markup.Escape(settings.MediaPartNumber ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-sequence={0}", settings.MediaSequence);
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-serial={0}", Markup.Escape(settings.MediaSerialNumber ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-title={0}", Markup.Escape(settings.MediaTitle ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(settings.Options ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(settings.OutputPath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--resume-file={0}", Markup.Escape(settings.ResumeFile ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel-position={0}", fixSubchannelPosition);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel={0}", fixSubchannel);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel-crc={0}", fixSubchannelCrc);
AaruConsole.DebugWriteLine(MODULE_NAME, "--generate-subchannels={0}", generateSubchannels);
AaruConsole.DebugWriteLine(MODULE_NAME, "--decrypt={0}", decrypt);
AaruConsole.DebugWriteLine(MODULE_NAME, "--aaru-metadata={0}", Markup.Escape(aaruMetadata ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--generate-subchannels={0}", settings.GenerateSubchannels);
AaruConsole.DebugWriteLine(MODULE_NAME, "--decrypt={0}", settings.Decrypt);
AaruConsole.DebugWriteLine(MODULE_NAME, "--aaru-metadata={0}", Markup.Escape(settings.AaruMetadata ?? ""));
Dictionary<string, string> parsedOptions = Core.Options.Parse(options);
Dictionary<string, string> parsedOptions = Options.Parse(settings.Options);
AaruConsole.DebugWriteLine(MODULE_NAME, UI.Parsed_options);
foreach(KeyValuePair<string, string> parsedOption in parsedOptions)
AaruConsole.DebugWriteLine(MODULE_NAME, "{0} = {1}", parsedOption.Key, parsedOption.Value);
if(count == 0)
if(settings.Count == 0)
{
AaruConsole.ErrorWriteLine(UI.Need_to_specify_more_than_zero_sectors_to_copy_at_once);
@@ -233,11 +173,11 @@ sealed class ConvertImageCommand : Command
(uint cylinders, uint heads, uint sectors)? geometryValues = null;
if(geometry != null)
if(settings.Geometry != null)
{
string[] geometryPieces = geometry.Split('/');
string[] geometryPieces = settings.Geometry.Split('/');
if(geometryPieces.Length == 0) geometryPieces = geometry.Split('-');
if(geometryPieces.Length == 0) geometryPieces = settings.Geometry.Split('-');
if(geometryPieces.Length != 3)
{
@@ -274,14 +214,14 @@ sealed class ConvertImageCommand : Command
Metadata sidecar = null;
MediaType mediaType;
if(aaruMetadata != null)
if(settings.AaruMetadata != null)
{
if(File.Exists(aaruMetadata))
if(File.Exists(settings.AaruMetadata))
{
try
{
var fs = new FileStream(aaruMetadata, FileMode.Open);
var fs = new FileStream(settings.AaruMetadata, FileMode.Open);
sidecar =
(JsonSerializer.Deserialize(fs, typeof(MetadataJson), MetadataJsonContext.Default) as
@@ -305,9 +245,9 @@ sealed class ConvertImageCommand : Command
}
}
else if(cicmXml != null)
else if(settings.CicmXml != null)
{
if(File.Exists(cicmXml))
if(File.Exists(settings.CicmXml))
{
try
{
@@ -316,7 +256,7 @@ sealed class ConvertImageCommand : Command
var xs = new XmlSerializer(typeof(CICMMetadataType));
#pragma warning restore IL2026
var sr = new StreamReader(cicmXml);
var sr = new StreamReader(settings.CicmXml);
// Should be covered by virtue of being the same exact class as the JSON above
#pragma warning disable IL2026
@@ -341,15 +281,15 @@ sealed class ConvertImageCommand : Command
}
}
if(resumeFile != null)
if(settings.ResumeFile != null)
{
if(File.Exists(resumeFile))
if(File.Exists(settings.ResumeFile))
{
try
{
if(resumeFile.EndsWith(".metadata.json", StringComparison.CurrentCultureIgnoreCase))
if(settings.ResumeFile.EndsWith(".metadata.json", StringComparison.CurrentCultureIgnoreCase))
{
var fs = new FileStream(resumeFile, FileMode.Open);
var fs = new FileStream(settings.ResumeFile, FileMode.Open);
resume =
(JsonSerializer.Deserialize(fs,
@@ -365,7 +305,7 @@ sealed class ConvertImageCommand : Command
var xs = new XmlSerializer(typeof(Resume));
#pragma warning restore IL2026
var sr = new StreamReader(resumeFile);
var sr = new StreamReader(settings.ResumeFile);
// Bypassed by JSON source generator used above
#pragma warning disable IL2026
@@ -396,7 +336,7 @@ sealed class ConvertImageCommand : Command
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(inputPath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.InputPath);
});
if(inputFilter == null)
@@ -406,7 +346,7 @@ sealed class ConvertImageCommand : Command
return (int)ErrorNumber.CannotOpenFile;
}
if(File.Exists(outputPath))
if(File.Exists(settings.OutputPath))
{
AaruConsole.ErrorWriteLine(UI.Output_file_already_exists);
@@ -439,7 +379,7 @@ sealed class ConvertImageCommand : Command
return (int)ErrorNumber.InvalidArgument;
}
if(verbose)
if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Input_image_format_identified_by_0_1, inputFormat.Name, inputFormat.Id);
else
AaruConsole.WriteLine(UI.Input_image_format_identified_by_0, inputFormat.Name);
@@ -499,16 +439,16 @@ sealed class ConvertImageCommand : Command
List<IBaseWritableImage> candidates = [];
// Try extension
if(string.IsNullOrEmpty(format))
if(string.IsNullOrEmpty(settings.Format))
{
candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null
where plugin.KnownExtensions.Contains(Path.GetExtension(outputPath))
where plugin.KnownExtensions.Contains(Path.GetExtension(settings.OutputPath))
select plugin);
}
// Try Id
else if(Guid.TryParse(format, out Guid outId))
else if(Guid.TryParse(settings.Format, out Guid outId))
{
candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null
@@ -521,7 +461,7 @@ sealed class ConvertImageCommand : Command
{
candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null
where plugin.Name.Equals(format, StringComparison.InvariantCultureIgnoreCase)
where plugin.Name.Equals(settings.Format, StringComparison.InvariantCultureIgnoreCase)
select plugin);
}
@@ -539,7 +479,7 @@ sealed class ConvertImageCommand : Command
IBaseWritableImage outputFormat = candidates[0];
if(verbose)
if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Output_image_format_0_1, outputFormat.Name, outputFormat.Id);
else
AaruConsole.WriteLine(UI.Output_image_format_0, outputFormat.Name);
@@ -552,7 +492,7 @@ sealed class ConvertImageCommand : Command
}
foreach(MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags.Where(mediaTag =>
!outputFormat.SupportedMediaTags.Contains(mediaTag) && !force))
!outputFormat.SupportedMediaTags.Contains(mediaTag) && !settings.Force))
{
AaruConsole.ErrorWriteLine(UI.Converting_image_will_lose_media_tag_0, mediaTag);
AaruConsole.ErrorWriteLine(UI.If_you_dont_care_use_force_option);
@@ -565,7 +505,7 @@ sealed class ConvertImageCommand : Command
foreach(SectorTagType sectorTag in inputFormat.Info.ReadableSectorTags.Where(sectorTag =>
!outputFormat.SupportedSectorTags.Contains(sectorTag)))
{
if(force)
if(settings.Force)
{
if(sectorTag != SectorTagType.CdTrackFlags &&
sectorTag != SectorTagType.CdTrackIsrc &&
@@ -593,7 +533,7 @@ sealed class ConvertImageCommand : Command
return (int)ErrorNumber.UnsupportedMedia;
}
var ret = false;
bool ret = false;
if(inputTape?.IsTape == true && outputTape != null)
{
@@ -641,13 +581,13 @@ sealed class ConvertImageCommand : Command
AaruConsole.ErrorWriteLine("Output format does not support sessions, this will end in a loss of data, continuing...");*/
}
var created = false;
bool created = false;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Invoke_Opening_image_file).IsIndeterminate();
created = outputFormat.Create(outputPath,
created = outputFormat.Create(settings.OutputPath,
mediaType,
parsedOptions,
inputFormat.Info.Sectors,
@@ -665,25 +605,26 @@ sealed class ConvertImageCommand : Command
{
Application = "Aaru",
ApplicationVersion = Version.GetVersion(),
Comments = comments ?? inputFormat.Info.Comments,
Creator = creator ?? inputFormat.Info.Creator,
DriveFirmwareRevision = driveFirmwareRevision ?? inputFormat.Info.DriveFirmwareRevision,
DriveManufacturer = driveManufacturer ?? inputFormat.Info.DriveManufacturer,
DriveModel = driveModel ?? inputFormat.Info.DriveModel,
DriveSerialNumber = driveSerialNumber ?? inputFormat.Info.DriveSerialNumber,
LastMediaSequence = lastMediaSequence != 0 ? lastMediaSequence : inputFormat.Info.LastMediaSequence,
MediaBarcode = mediaBarcode ?? inputFormat.Info.MediaBarcode,
MediaManufacturer = mediaManufacturer ?? inputFormat.Info.MediaManufacturer,
MediaModel = mediaModel ?? inputFormat.Info.MediaModel,
MediaPartNumber = mediaPartNumber ?? inputFormat.Info.MediaPartNumber,
MediaSequence = mediaSequence != 0 ? mediaSequence : inputFormat.Info.MediaSequence,
MediaSerialNumber = mediaSerialNumber ?? inputFormat.Info.MediaSerialNumber,
MediaTitle = mediaTitle ?? inputFormat.Info.MediaTitle
Comments = settings.Comments ?? inputFormat.Info.Comments,
Creator = settings.Creator ?? inputFormat.Info.Creator,
DriveFirmwareRevision = settings.DriveFirmwareRevision ?? inputFormat.Info.DriveFirmwareRevision,
DriveManufacturer = settings.DriveManufacturer ?? inputFormat.Info.DriveManufacturer,
DriveModel = settings.DriveModel ?? inputFormat.Info.DriveModel,
DriveSerialNumber = settings.DriveSerialNumber ?? inputFormat.Info.DriveSerialNumber,
LastMediaSequence =
settings.LastMediaSequence != 0 ? settings.LastMediaSequence : inputFormat.Info.LastMediaSequence,
MediaBarcode = settings.MediaBarcode ?? inputFormat.Info.MediaBarcode,
MediaManufacturer = settings.MediaManufacturer ?? inputFormat.Info.MediaManufacturer,
MediaModel = settings.MediaModel ?? inputFormat.Info.MediaModel,
MediaPartNumber = settings.MediaPartNumber ?? inputFormat.Info.MediaPartNumber,
MediaSequence = settings.MediaSequence != 0 ? settings.MediaSequence : inputFormat.Info.MediaSequence,
MediaSerialNumber = settings.MediaSerialNumber ?? inputFormat.Info.MediaSerialNumber,
MediaTitle = settings.MediaTitle ?? inputFormat.Info.MediaTitle
};
if(!outputFormat.SetImageInfo(imageInfo))
{
if(!force)
if(!settings.Force)
{
AaruConsole.ErrorWriteLine(UI.Error_0_setting_metadata_not_continuing, outputFormat.ErrorMessage);
@@ -696,8 +637,8 @@ sealed class ConvertImageCommand : Command
Metadata metadata = inputFormat.AaruMetadata;
List<DumpHardware> dumpHardware = inputFormat.DumpHardware;
foreach(MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags.Where(mediaTag =>
!force || outputFormat.SupportedMediaTags.Contains(mediaTag)))
foreach(MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags.Where(mediaTag => !settings.Force ||
outputFormat.SupportedMediaTags.Contains(mediaTag)))
{
ErrorNumber errorNumber = ErrorNumber.NoError;
@@ -712,7 +653,7 @@ sealed class ConvertImageCommand : Command
if(errno != ErrorNumber.NoError)
{
if(force)
if(settings.Force)
AaruConsole.ErrorWriteLine(UI.Error_0_reading_media_tag, errno);
else
{
@@ -726,7 +667,7 @@ sealed class ConvertImageCommand : Command
if((outputFormat as IWritableImage)?.WriteMediaTag(tag, mediaTag) == true) return;
if(force)
if(settings.Force)
AaruConsole.ErrorWriteLine(UI.Error_0_writing_media_tag, outputFormat.ErrorMessage);
else
{
@@ -756,7 +697,7 @@ sealed class ConvertImageCommand : Command
ErrorNumber errno = ErrorNumber.NoError;
if(decrypt) AaruConsole.WriteLine("Decrypting encrypted sectors.");
if(settings.Decrypt) AaruConsole.WriteLine("Decrypting encrypted sectors.");
AnsiConsole.Progress()
.AutoClear(true)
@@ -786,8 +727,8 @@ sealed class ConvertImageCommand : Command
uint sectorsToDo;
if(trackSectors - doneSectors >= (ulong)count)
sectorsToDo = (uint)count;
if(trackSectors - doneSectors >= (ulong)settings.Count)
sectorsToDo = (uint)settings.Count;
else
sectorsToDo = (uint)(trackSectors - doneSectors);
@@ -796,8 +737,8 @@ sealed class ConvertImageCommand : Command
doneSectors + sectorsToDo + track.StartSector,
track.Sequence);
var useNotLong = false;
var result = false;
bool useNotLong = false;
bool result = false;
if(useLong)
{
@@ -821,7 +762,7 @@ sealed class ConvertImageCommand : Command
{
result = true;
if(force)
if(settings.Force)
{
AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing,
errno,
@@ -841,7 +782,7 @@ sealed class ConvertImageCommand : Command
if(!result && sector.Length % 2352 != 0)
{
if(!force)
if(!settings.Force)
{
AaruConsole
.ErrorWriteLine(UI
@@ -871,7 +812,7 @@ sealed class ConvertImageCommand : Command
or MediaType.DVDRDL
or MediaType.DVDPR
or MediaType.DVDPRDL &&
decrypt)
settings.Decrypt)
{
// Only sectors which are MPEG packets can be encrypted.
if(Mpeg.ContainsMpegPackets(sector, sectorsToDo))
@@ -1009,7 +950,7 @@ sealed class ConvertImageCommand : Command
{
result = true;
if(force)
if(settings.Force)
{
AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing,
errno,
@@ -1030,7 +971,7 @@ sealed class ConvertImageCommand : Command
if(!result)
{
if(force)
if(settings.Force)
{
AaruConsole.ErrorWriteLine(UI.Error_0_writing_sector_1_continuing,
outputOptical.ErrorMessage,
@@ -1066,7 +1007,7 @@ sealed class ConvertImageCommand : Command
Dictionary<byte, int> smallestPregapLbaPerTrack = new();
var tracks = new Track[inputOptical.Tracks.Count];
for(var i = 0; i < tracks.Length; i++)
for(int i = 0; i < tracks.Length; i++)
{
tracks[i] = new Track
{
@@ -1143,7 +1084,7 @@ sealed class ConvertImageCommand : Command
continue;
}
if(force && !outputOptical.SupportedSectorTags.Contains(tag)) continue;
if(settings.Force && !outputOptical.SupportedSectorTags.Contains(tag)) continue;
errno = ErrorNumber.NoError;
@@ -1186,7 +1127,7 @@ sealed class ConvertImageCommand : Command
break;
default:
{
if(force)
if(settings.Force)
{
AaruConsole.ErrorWriteLine(UI.Error_0_writing_tag_continuing,
outputOptical.ErrorMessage);
@@ -1205,7 +1146,7 @@ sealed class ConvertImageCommand : Command
if(!result)
{
if(force)
if(settings.Force)
{
AaruConsole.ErrorWriteLine(UI.Error_0_writing_tag_continuing,
outputOptical.ErrorMessage);
@@ -1231,8 +1172,8 @@ sealed class ConvertImageCommand : Command
{
uint sectorsToDo;
if(trackSectors - doneSectors >= (ulong)count)
sectorsToDo = (uint)count;
if(trackSectors - doneSectors >= (ulong)settings.Count)
sectorsToDo = (uint)settings.Count;
else
sectorsToDo = (uint)(trackSectors - doneSectors);
@@ -1290,7 +1231,7 @@ sealed class ConvertImageCommand : Command
{
result = true;
if(force)
if(settings.Force)
{
AaruConsole
.ErrorWriteLine(UI.Error_0_reading_tag_for_sector_1_continuing,
@@ -1358,7 +1299,7 @@ sealed class ConvertImageCommand : Command
{
result = true;
if(force)
if(settings.Force)
{
AaruConsole
.ErrorWriteLine(UI.Error_0_reading_tag_for_sector_1_continuing,
@@ -1380,7 +1321,7 @@ sealed class ConvertImageCommand : Command
if(!result)
{
if(force)
if(settings.Force)
{
AaruConsole
.ErrorWriteLine(UI.Error_0_writing_tag_for_sector_1_continuing,
@@ -1409,13 +1350,11 @@ sealed class ConvertImageCommand : Command
}
});
if(errno != ErrorNumber.NoError && !force) return (int)errno;
if(errno != ErrorNumber.NoError && !settings.Force) return (int)errno;
}
foreach(KeyValuePair<byte, string> isrc in isrcs)
{
outputOptical.WriteSectorTag(Encoding.UTF8.GetBytes(isrc.Value), isrc.Key, SectorTagType.CdTrackIsrc);
}
if(trackFlags.Count > 0)
{
@@ -1466,7 +1405,7 @@ sealed class ConvertImageCommand : Command
or MediaType.VideoNowColor
or MediaType.VideoNowXp
or MediaType.CVD &&
generateSubchannels)
settings.GenerateSubchannels)
{
Core.Spectre.ProgressSingleSpinner(ctx =>
{
@@ -1527,8 +1466,8 @@ sealed class ConvertImageCommand : Command
if(inputTape?.IsTape == true)
sectorsToDo = 1;
else if(inputFormat.Info.Sectors - doneSectors >= (ulong)count)
sectorsToDo = (uint)count;
else if(inputFormat.Info.Sectors - doneSectors >= (ulong)settings.Count)
sectorsToDo = (uint)settings.Count;
else
sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors);
@@ -1553,7 +1492,7 @@ sealed class ConvertImageCommand : Command
{
result = true;
if(force)
if(settings.Force)
{
AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing,
errno,
@@ -1585,7 +1524,7 @@ sealed class ConvertImageCommand : Command
{
result = true;
if(force)
if(settings.Force)
{
AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing,
errno,
@@ -1604,7 +1543,7 @@ sealed class ConvertImageCommand : Command
if(!result)
{
if(force)
if(settings.Force)
{
AaruConsole.ErrorWriteLine(UI.Error_0_writing_sector_1_continuing,
outputMedia.ErrorMessage,
@@ -1644,7 +1583,7 @@ sealed class ConvertImageCommand : Command
continue;
}
if(force && !outputMedia.SupportedSectorTags.Contains(tag)) continue;
if(settings.Force && !outputMedia.SupportedSectorTags.Contains(tag)) continue;
doneSectors = 0;
@@ -1655,8 +1594,8 @@ sealed class ConvertImageCommand : Command
{
uint sectorsToDo;
if(inputFormat.Info.Sectors - doneSectors >= (ulong)count)
sectorsToDo = (uint)count;
if(inputFormat.Info.Sectors - doneSectors >= (ulong)settings.Count)
sectorsToDo = (uint)settings.Count;
else
sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors);
@@ -1684,7 +1623,7 @@ sealed class ConvertImageCommand : Command
{
result = true;
if(force)
if(settings.Force)
{
AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing,
errno,
@@ -1702,7 +1641,7 @@ sealed class ConvertImageCommand : Command
if(!result)
{
if(force)
if(settings.Force)
{
AaruConsole.ErrorWriteLine(UI.Error_0_writing_sector_1_continuing,
outputMedia.ErrorMessage,
@@ -1836,7 +1775,7 @@ sealed class ConvertImageCommand : Command
if(ret) AaruConsole.WriteLine(UI.Written_Aaru_Metadata_to_output_image);
}
var closed = false;
bool closed = false;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
@@ -1856,4 +1795,126 @@ sealed class ConvertImageCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : ImageFamily
{
[Description("Take metadata from existing CICM XML sidecar.")]
[DefaultValue(null)]
[CommandOption("-x|--cicm-xml")]
public string CicmXml { get; init; }
[Description("Image comments.")]
[DefaultValue(null)]
[CommandOption("--comments")]
public string Comments { get; init; }
[Description("How many sectors to convert at once.")]
[DefaultValue(64)]
[CommandOption("-c|--count")]
public int Count { get; init; }
[Description("Who (person) created the image?")]
[DefaultValue(null)]
[CommandOption("--creator")]
public string Creator { get; init; }
[Description("Manufacturer of the drive used to read the media represented by the image.")]
[DefaultValue(null)]
[CommandOption("--drive-manufacturer")]
public string DriveManufacturer { get; init; }
[Description("Model of the drive used to read the media represented by the image.")]
[DefaultValue(null)]
[CommandOption("--drive-model")]
public string DriveModel { get; init; }
[Description("Firmware revision of the drive used to read the media represented by the image.")]
[DefaultValue(null)]
[CommandOption("--drive-revision")]
public string DriveFirmwareRevision { get; init; }
[Description("Serial number of the drive used to read the media represented by the image.")]
[DefaultValue(null)]
[CommandOption("--drive-serial")]
public string DriveSerialNumber { get; init; }
[Description("Continue conversion even if sector or media tags will be lost in the process.")]
[DefaultValue(false)]
[CommandOption("-f|--force")]
public bool Force { get; init; }
[Description("Format of the output image, as plugin name or plugin id. If not present, will try to detect it from output image extension.")]
[DefaultValue(null)]
[CommandOption("-p|--format")]
public string Format { get; init; }
[Description("Barcode of the media represented by the image.")]
[DefaultValue(null)]
[CommandOption("--media-barcode")]
public string MediaBarcode { get; init; }
[Description("Last media of the sequence the media represented by the image corresponds to.")]
[DefaultValue(0)]
[CommandOption("--media-lastsequence")]
public int LastMediaSequence { get; init; }
[Description("Manufacturer of the media represented by the image.")]
[DefaultValue(null)]
[CommandOption("--media-manufacturer")]
public string MediaManufacturer { get; init; }
[Description("Model of the media represented by the image.")]
[DefaultValue(null)]
[CommandOption("--media-model")]
public string MediaModel { get; init; }
[Description("Part number of the media represented by the image.")]
[DefaultValue(null)]
[CommandOption("--media-partnumber")]
public string MediaPartNumber { get; init; }
[Description("Number in sequence for the media represented by the image.")]
[DefaultValue(0)]
[CommandOption("--media-sequence")]
public int MediaSequence { get; init; }
[Description("Serial number of the media represented by the image.")]
[DefaultValue(null)]
[CommandOption("--media-serial")]
public string MediaSerialNumber { get; init; }
[Description("Title of the media represented by the image.")]
[DefaultValue(null)]
[CommandOption("--media-title")]
public string MediaTitle { get; init; }
[Description("Comma separated name=value pairs of options to pass to output image plugin.")]
[DefaultValue(null)]
[CommandOption("-O|--options")]
public string Options { get; init; }
[Description("Take list of dump hardware from existing resume file.")]
[DefaultValue(null)]
[CommandOption("-r|--resume-file")]
public string ResumeFile { get; init; }
[Description("Force geometry, only supported in not tape block media. Specify as C/H/S.")]
[DefaultValue(null)]
[CommandOption("-g|--geometry")]
public string Geometry { get; init; }
[Description("Store subchannel according to the sector they describe.")]
[DefaultValue(true)]
[CommandOption("--fix-subchannel-position")]
public bool FixSubchannelPosition { get; init; }
[Description("Try to fix subchannel. Implies fixing subchannel position.")]
[DefaultValue(false)]
[CommandOption("--fix-subchannel")]
public bool FixSubchannel { get; init; }
[Description("If subchannel looks OK but CRC fails, rewrite it. Implies fixing subchannel.")]
[DefaultValue(false)]
[CommandOption("--fix-subchannel-crc")]
public bool FixSubchannelCrc { get; init; }
[Description("Generates missing subchannels.")]
[DefaultValue(false)]
[CommandOption("--generate-subchannels")]
public bool GenerateSubchannels { get; init; }
[Description("Try to decrypt encrypted sectors.")]
[DefaultValue(false)]
[CommandOption("--decrypt")]
public bool Decrypt { get; init; }
[Description("Take metadata from existing Aaru Metadata sidecar.")]
[DefaultValue(null)]
[CommandOption("-m|--aaru-metadata")]
public string AaruMetadata { get; init; }
[Description("Input image path")]
[CommandArgument(0, "<input-image>")]
public string InputPath { get; init; }
[Description("Output image path")]
[CommandArgument(1, "<output-image>")]
public string OutputPath { get; init; }
}
#endregion
}

View File

@@ -31,8 +31,7 @@
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
@@ -44,43 +43,24 @@ using Aaru.CommonTypes.Interfaces;
using Aaru.Console;
using Aaru.Core;
using Aaru.Localization;
using JetBrains.Annotations;
using Spectre.Console;
using Spectre.Console.Cli;
using Directory = System.IO.Directory;
using File = System.IO.File;
namespace Aaru.Commands.Image;
sealed class CreateSidecarCommand : Command
sealed class CreateSidecarCommand : Command<CreateSidecarCommand.Settings>
{
const string MODULE_NAME = "Create sidecar command";
static ProgressTask _progressTask1;
static ProgressTask _progressTask2;
public CreateSidecarCommand() : base("create-sidecar", UI.Image_Create_Sidecar_Command_Description)
{
Add(new Option<int>(["--block-size", "-b"], () => 512, UI.Tape_block_size_argument_help));
Add(new Option<string>(["--encoding", "-e"], () => null, UI.Name_of_character_encoding_to_use));
Add(new Option<bool>(["--tape", "-t"], () => false, UI.Tape_argument_input_help));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Media_image_path,
Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, uint blockSize, [CanBeNull] string encodingName,
string imagePath, bool tape)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -98,7 +78,7 @@ sealed class CreateSidecarCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -111,22 +91,22 @@ sealed class CreateSidecarCommand : Command
Statistics.AddCommand("create-sidecar");
AaruConsole.DebugWriteLine(MODULE_NAME, "--block-size={0}", blockSize);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encodingName ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--tape={0}", tape);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--block-size={0}", settings.BlockSize);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--tape={0}", settings.Tape);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
Encoding encodingClass = null;
if(encodingName != null)
if(settings.Encoding != null)
{
try
{
encodingClass = Claunia.Encoding.Encoding.GetEncoding(encodingName);
encodingClass = Claunia.Encoding.Encoding.GetEncoding(settings.Encoding);
if(verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
if(settings.Verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
}
catch(ArgumentException)
{
@@ -136,9 +116,9 @@ sealed class CreateSidecarCommand : Command
}
}
if(File.Exists(imagePath))
if(File.Exists(settings.ImagePath))
{
if(tape)
if(settings.Tape)
{
AaruConsole.ErrorWriteLine(UI.You_cannot_use_tape_option_when_input_is_a_file);
@@ -150,7 +130,7 @@ sealed class CreateSidecarCommand : Command
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
});
if(inputFilter == null)
@@ -177,7 +157,7 @@ sealed class CreateSidecarCommand : Command
return (int)ErrorNumber.UnrecognizedFormat;
}
if(verbose)
if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Image_format_identified_by_0_1, imageFormat.Name, imageFormat.Id);
else
AaruConsole.WriteLine(UI.Image_format_identified_by_0, imageFormat.Name);
@@ -214,7 +194,7 @@ sealed class CreateSidecarCommand : Command
Statistics.AddMediaFormat(imageFormat.Format);
Statistics.AddFilter(inputFilter.Name);
var sidecarClass = new Sidecar(imageFormat, imagePath, inputFilter.Id, encodingClass);
var sidecarClass = new Sidecar(imageFormat, settings.ImagePath, inputFilter.Id, encodingClass);
Metadata sidecar = new();
AnsiConsole.Progress()
@@ -274,9 +254,10 @@ sealed class CreateSidecarCommand : Command
ctx.AddTask(Localization.Core.Writing_metadata_sidecar).IsIndeterminate();
var jsonFs =
new FileStream(Path.Combine(Path.GetDirectoryName(imagePath) ??
new FileStream(Path.Combine(Path.GetDirectoryName(settings.ImagePath) ??
throw new InvalidOperationException(),
Path.GetFileNameWithoutExtension(imagePath) + ".metadata.json"),
Path.GetFileNameWithoutExtension(settings.ImagePath) +
".metadata.json"),
FileMode.Create);
JsonSerializer.Serialize(jsonFs,
@@ -298,17 +279,17 @@ sealed class CreateSidecarCommand : Command
return (int)ErrorNumber.UnexpectedException;
}
}
else if(Directory.Exists(imagePath))
else if(Directory.Exists(settings.ImagePath))
{
if(!tape)
if(!settings.Tape)
{
AaruConsole.ErrorWriteLine(Localization.Core.Cannot_create_a_sidecar_from_a_directory);
return (int)ErrorNumber.IsDirectory;
}
string[] contents = Directory.GetFiles(imagePath, "*", SearchOption.TopDirectoryOnly);
var files = contents.Where(file => new FileInfo(file).Length % blockSize == 0).ToList();
string[] contents = Directory.GetFiles(settings.ImagePath, "*", SearchOption.TopDirectoryOnly);
var files = contents.Where(file => new FileInfo(file).Length % settings.BlockSize == 0).ToList();
files.Sort(StringComparer.CurrentCultureIgnoreCase);
@@ -361,7 +342,9 @@ sealed class CreateSidecarCommand : Command
sidecarClass.Abort();
};
sidecar = sidecarClass.BlockTape(Path.GetFileName(imagePath), files, blockSize);
sidecar = sidecarClass.BlockTape(Path.GetFileName(settings.ImagePath),
files,
settings.BlockSize);
});
Core.Spectre.ProgressSingleSpinner(ctx =>
@@ -369,9 +352,10 @@ sealed class CreateSidecarCommand : Command
ctx.AddTask(Localization.Core.Writing_metadata_sidecar).IsIndeterminate();
var jsonFs =
new FileStream(Path.Combine(Path.GetDirectoryName(imagePath) ??
new FileStream(Path.Combine(Path.GetDirectoryName(settings.ImagePath) ??
throw new InvalidOperationException(),
Path.GetFileNameWithoutExtension(imagePath) + ".metadata.json"),
Path.GetFileNameWithoutExtension(settings.ImagePath) +
".metadata.json"),
FileMode.Create);
JsonSerializer.Serialize(jsonFs,
@@ -390,4 +374,27 @@ sealed class CreateSidecarCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : ImageFamily
{
[CommandOption("-b|--block-size")]
[Description("Only used for tapes, indicates block size. Files in the folder whose size is not a multiple of this value will simply be ignored.")]
[DefaultValue(512)]
public uint BlockSize { get; init; }
[CommandOption("-e|--encoding")]
[Description("Name of character encoding to use.")]
[DefaultValue(null)]
public string Encoding { get; init; }
[CommandOption("-t|--tape")]
[Description("When used indicates that input is a folder containing alphabetically sorted files extracted from a linear block-based tape with fixed block size (e.g. a SCSI tape device).")]
[DefaultValue(false)]
public bool Tape { get; init; }
[Description("Media image path")]
[CommandArgument(0, "<image-path>")]
public string ImagePath { get; init; }
}
#endregion
}

View File

@@ -30,9 +30,7 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.Globalization;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
@@ -44,41 +42,19 @@ using Aaru.Decoders.CD;
using Aaru.Decoders.SCSI;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image;
sealed class DecodeCommand : Command
sealed class DecodeCommand : Command<DecodeCommand.Settings>
{
const string MODULE_NAME = "Decode command";
public DecodeCommand() : base("decode", UI.Image_Decode_Command_Description)
{
Add(new Option<bool>(["--disk-tags", "-f"], () => true, UI.Decode_media_tags));
Add(new Option<string>(["--length", "-l"],
() => UI.Parameter_response_all_sectors,
UI.How_many_sectors_to_decode_or_all));
Add(new Option<bool>(["--sector-tags", "-p"], () => true, UI.Decode_sector_tags));
Add(new Option<ulong>(["--start", "-s"], () => 0, UI.Sector_to_start_decoding_from));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Media_image_path,
Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool verbose, bool debug, bool diskTags, string imagePath, string length, bool sectorTags,
ulong startSector)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -96,7 +72,7 @@ sealed class DecodeCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -109,20 +85,20 @@ sealed class DecodeCommand : Command
Statistics.AddCommand("decode");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--disk-tags={0}", diskTags);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--length={0}", Markup.Escape(length ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--sector-tags={0}", sectorTags);
AaruConsole.DebugWriteLine(MODULE_NAME, "--start={0}", startSector);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--disk-tags={0}", settings.DiskTags);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--length={0}", Markup.Escape(settings.Length ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--sector-tags={0}", settings.SectorTags);
AaruConsole.DebugWriteLine(MODULE_NAME, "--start={0}", settings.StartSector);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
});
if(inputFilter == null)
@@ -176,7 +152,7 @@ sealed class DecodeCommand : Command
Statistics.AddMedia(inputFormat.Info.MediaType, false);
Statistics.AddFilter(inputFilter.Name);
if(diskTags)
if(settings.DiskTags)
{
if(inputFormat.Info.ReadableMediaTags.Count == 0)
AaruConsole.WriteLine(UI.There_are_no_media_tags_in_chosen_disc_image);
@@ -393,14 +369,14 @@ sealed class DecodeCommand : Command
}
}
if(!sectorTags) return (int)ErrorNumber.NoError;
if(!settings.SectorTags) return (int)ErrorNumber.NoError;
if(length.ToLower(CultureInfo.CurrentUICulture) == UI.Parameter_response_all_sectors) {}
if(settings.Length.ToLower(CultureInfo.CurrentUICulture) == UI.Parameter_response_all_sectors) {}
else
{
if(!ulong.TryParse(length, out ulong _))
if(!ulong.TryParse(settings.Length, out ulong _))
{
AaruConsole.WriteLine(UI.Value_0_is_not_a_valid_number_for_length, length);
AaruConsole.WriteLine(UI.Value_0_is_not_a_valid_number_for_length, settings.Length);
AaruConsole.WriteLine(UI.Not_decoding_sectors_tags);
return 3;
@@ -429,4 +405,31 @@ sealed class DecodeCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : ImageFamily
{
[Description("Decode media tags.")]
[DefaultValue(true)]
[CommandOption("-f|--disk-tags")]
public bool DiskTags { get; init; }
[Description("How many sectors to decode, or \"all\".")]
[DefaultValue("all")]
[CommandOption("-l|--length")]
public string Length { get; init; }
[Description("Decode sector tags.")]
[DefaultValue(true)]
[CommandOption("-p|--sector-tags")]
public bool SectorTags { get; init; }
[Description("Sector to start decoding from.")]
[DefaultValue(0)]
[CommandOption("-s|--start")]
public ulong StartSector { get; init; }
[Description("Media image path")]
[CommandArgument(0, "<image-path>")]
public string ImagePath { get; init; }
}
#endregion
}

View File

@@ -30,9 +30,7 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
@@ -40,43 +38,21 @@ using Aaru.Console;
using Aaru.Core;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image;
sealed class EntropyCommand : Command
sealed class EntropyCommand : Command<EntropyCommand.Settings>
{
const string MODULE_NAME = "Entropy command";
static ProgressTask _progressTask1;
static ProgressTask _progressTask2;
public EntropyCommand() : base("entropy", UI.Image_Entropy_Command_Description)
{
Add(new Option<bool>(["--duplicated-sectors", "-p"],
() => true,
UI.Calculates_how_many_sectors_are_duplicated));
Add(new Option<bool>(["--separated-tracks", "-t"],
() => true,
UI.Calculates_entropy_for_each_track_separately));
Add(new Option<bool>(["--whole-disc", "-w"], () => true, UI.Calculates_entropy_for_the_whole_disc));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Media_image_path,
Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, bool duplicatedSectors, string imagePath, bool separatedTracks,
bool wholeDisc)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -94,7 +70,7 @@ sealed class EntropyCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -107,19 +83,19 @@ sealed class EntropyCommand : Command
Statistics.AddCommand("entropy");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--duplicated-sectors={0}", duplicatedSectors);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--separated-tracks={0}", separatedTracks);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--whole-disc={0}", wholeDisc);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--duplicated-sectors={0}", settings.DuplicatedSectors);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--separated-tracks={0}", settings.SeparatedTracks);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--whole-disc={0}", settings.WholeDisc);
IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
});
if(inputFilter == null)
@@ -164,7 +140,10 @@ sealed class EntropyCommand : Command
Statistics.AddMedia(inputFormat.Info.MediaType, false);
Statistics.AddFilter(inputFilter.Name);
var entropyCalculator = new Entropy(debug, inputFormat);
bool separatedTracks = settings.SeparatedTracks;
bool wholeDisc = settings.WholeDisc;
var entropyCalculator = new Entropy(settings.Debug, inputFormat);
AnsiConsole.Progress()
.AutoClear(true)
@@ -204,7 +183,7 @@ sealed class EntropyCommand : Command
_progressTask2 = null;
};
if(wholeDisc && inputFormat is IOpticalMediaImage opticalFormat)
if(settings.WholeDisc && inputFormat is IOpticalMediaImage opticalFormat)
{
if(opticalFormat.Sessions?.Count > 1)
{
@@ -221,7 +200,7 @@ sealed class EntropyCommand : Command
if(separatedTracks)
{
EntropyResults[] tracksEntropy =
entropyCalculator.CalculateTracksEntropy(duplicatedSectors);
entropyCalculator.CalculateTracksEntropy(settings.DuplicatedSectors);
foreach(EntropyResults trackEntropy in tracksEntropy)
{
@@ -243,7 +222,8 @@ sealed class EntropyCommand : Command
EntropyResults entropy = inputFormat.Info.MetadataMediaType == MetadataMediaType.LinearMedia
? entropyCalculator.CalculateLinearMediaEntropy()
: entropyCalculator.CalculateMediaEntropy(duplicatedSectors);
: entropyCalculator.CalculateMediaEntropy(settings
.DuplicatedSectors);
AaruConsole.WriteLine(UI.Entropy_for_disk_is_0, entropy.Entropy);
@@ -257,4 +237,27 @@ sealed class EntropyCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : ImageFamily
{
[Description("Calculates how many sectors are duplicated (have same exact data in user area).")]
[DefaultValue(true)]
[CommandOption("-p|--duplicated-sectors")]
public bool DuplicatedSectors { get; init; }
[Description("Calculates entropy for each track separately.")]
[DefaultValue(true)]
[CommandOption("-t|--separated-tracks")]
public bool SeparatedTracks { get; init; }
[Description("Calculates entropy for the whole disc.")]
[DefaultValue(true)]
[CommandOption("-w|--whole-disc")]
public bool WholeDisc { get; init; }
[Description("Media image path")]
[CommandArgument(0, "<image-path>")]
public string ImagePath { get; init; }
}
#endregion
}

View File

@@ -30,26 +30,6 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System.CommandLine;
using Aaru.Localization;
namespace Aaru.Commands.Image;
sealed class ImageFamily : Command
{
public ImageFamily() : base("image", UI.Image_Command_Family_Description)
{
AddAlias("i");
AddCommand(new ChecksumCommand());
AddCommand(new CompareCommand());
AddCommand(new ConvertImageCommand());
AddCommand(new CreateSidecarCommand());
AddCommand(new DecodeCommand());
AddCommand(new EntropyCommand());
AddCommand(new ImageInfoCommand());
AddCommand(new ListOptionsCommand());
AddCommand(new PrintHexCommand());
AddCommand(new VerifyCommand());
}
}
class ImageFamily : BaseSettings {}

View File

@@ -31,8 +31,7 @@
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
@@ -40,30 +39,19 @@ using Aaru.Console;
using Aaru.Core;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image;
sealed class ImageInfoCommand : Command
sealed class ImageInfoCommand : Command<ImageInfoCommand.Settings>
{
const string MODULE_NAME = "Image-info command";
public ImageInfoCommand() : base("info", UI.Image_Info_Command_Description)
{
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Media_image_path,
Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string imagePath)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -81,7 +69,7 @@ sealed class ImageInfoCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -94,16 +82,16 @@ sealed class ImageInfoCommand : Command
Statistics.AddCommand("image-info");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
});
if(inputFilter == null)
@@ -176,4 +164,15 @@ sealed class ImageInfoCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : ImageFamily
{
[Description("Media image path")]
[CommandArgument(0, "<image-path>")]
public string ImagePath { get; init; }
}
#endregion
}

View File

@@ -31,8 +31,6 @@
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.Linq;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
@@ -42,21 +40,20 @@ using Aaru.Core;
using Aaru.Localization;
using JetBrains.Annotations;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image;
sealed class ListOptionsCommand : Command
sealed class ListOptionsCommand : Command<ListOptionsCommand.Settings>
{
const string MODULE_NAME = "List-Options command";
public ListOptionsCommand() : base("options", UI.Image_Options_Command_Description) =>
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
public override int Execute(CommandContext context, Settings settings)
public static int Invoke(bool debug, bool verbose)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -74,7 +71,7 @@ sealed class ListOptionsCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -85,8 +82,8 @@ sealed class ListOptionsCommand : Command
};
}
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
Statistics.AddCommand("list-options");
PluginRegister plugins = PluginRegister.Singleton;
@@ -144,4 +141,10 @@ sealed class ListOptionsCommand : Command
return type == typeof(string) ? UI.TypeToString_string : type.ToString();
}
#region Nested type: Settings
public class Settings : ImageFamily {}
#endregion
}

View File

@@ -31,8 +31,7 @@
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
@@ -41,39 +40,20 @@ using Aaru.Core;
using Aaru.Helpers;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image;
sealed class PrintHexCommand : Command
sealed class PrintHexCommand : Command<PrintHexCommand.Settings>
{
const string MODULE_NAME = "PrintHex command";
public PrintHexCommand() : base("print", UI.Image_Print_Command_Description)
{
Add(new Option<ulong>(["--length", "-l"], () => 1, UI.How_many_sectors_to_print));
public override int Execute(CommandContext context, Settings settings)
Add(new Option<bool>(["--long-sectors", "-r"], () => false, UI.Print_sectors_with_tags_included));
Add(new Option<ulong>(["--start", "-s"], UI.Starting_sector));
Add(new Option<ushort>(["--width", "-w"], () => 32, UI.How_many_bytes_to_print_per_line));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Media_image_path,
Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string imagePath, ulong length, bool longSectors, ulong start,
ushort width)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -91,7 +71,7 @@ sealed class PrintHexCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -104,20 +84,20 @@ sealed class PrintHexCommand : Command
Statistics.AddCommand("print-hex");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--length={0}", length);
AaruConsole.DebugWriteLine(MODULE_NAME, "--long-sectors={0}", longSectors);
AaruConsole.DebugWriteLine(MODULE_NAME, "--start={0}", start);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--width={0}", width);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--length={0}", settings.Length);
AaruConsole.DebugWriteLine(MODULE_NAME, "--long-sectors={0}", settings.LongSectors);
AaruConsole.DebugWriteLine(MODULE_NAME, "--start={0}", settings.Start);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--width={0}", settings.Width);
IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
});
if(inputFilter == null)
@@ -129,6 +109,8 @@ sealed class PrintHexCommand : Command
IBaseImage inputFormat = null;
bool longSectors = settings.LongSectors;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_image_format).IsIndeterminate();
@@ -162,36 +144,40 @@ sealed class PrintHexCommand : Command
{
var byteAddressableImage = inputFormat as IByteAddressableImage;
AaruConsole.WriteLine($"[bold][italic]{string.Format(UI.Start_0_as_in_sector_start, start)}[/][/]");
AaruConsole.WriteLine($"[bold][italic]{string.Format(UI.Start_0_as_in_sector_start, settings.Start)}[/][/]");
var data = new byte[length];
byte[] data = new byte[settings.Length];
ErrorNumber errno = ErrorNumber.NoError;
var bytesRead = 0;
int bytesRead = 0;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Reading_data).IsIndeterminate();
errno = byteAddressableImage?.ReadBytesAt((long)start, data, 0, (int)length, out bytesRead) ??
errno = byteAddressableImage?.ReadBytesAt((long)settings.Start,
data,
0,
(int)settings.Length,
out bytesRead) ??
ErrorNumber.InvalidArgument;
});
// TODO: Span
if(bytesRead != (int)length)
if(bytesRead != (int)settings.Length)
{
var tmp = new byte[bytesRead];
byte[] tmp = new byte[bytesRead];
Array.Copy(data, 0, tmp, 0, bytesRead);
data = tmp;
}
if(errno == ErrorNumber.NoError)
AaruConsole.WriteLine(Markup.Escape(PrintHex.ByteArrayToHexArrayString(data, width, true)));
AaruConsole.WriteLine(Markup.Escape(PrintHex.ByteArrayToHexArrayString(data, settings.Width, true)));
else
AaruConsole.ErrorWriteLine(string.Format(UI.Error_0_reading_data_from_1, errno, start));
AaruConsole.ErrorWriteLine(string.Format(UI.Error_0_reading_data_from_1, errno, settings.Start));
}
else
{
for(ulong i = 0; i < length; i++)
for(ulong i = 0; i < settings.Length; i++)
{
if(inputFormat is not IMediaImage blockImage)
{
@@ -200,8 +186,9 @@ sealed class PrintHexCommand : Command
break;
}
AaruConsole.WriteLine($"[bold][italic]{string.Format(UI.Sector_0_as_in_sector_number, start)}[/][/]" +
i);
AaruConsole
.WriteLine($"[bold][italic]{string.Format(UI.Sector_0_as_in_sector_number, settings.Start)}[/][/]" +
i);
if(blockImage.Info.ReadableSectorTags == null)
{
@@ -227,17 +214,46 @@ sealed class PrintHexCommand : Command
ctx.AddTask(UI.Reading_sector).IsIndeterminate();
errno = longSectors
? blockImage.ReadSectorLong(start + i, out sector)
: blockImage.ReadSector(start + i, out sector);
? blockImage.ReadSectorLong(settings.Start + i, out sector)
: blockImage.ReadSector(settings.Start + i, out sector);
});
if(errno == ErrorNumber.NoError)
AaruConsole.WriteLine(Markup.Escape(PrintHex.ByteArrayToHexArrayString(sector, width, true)));
AaruConsole.WriteLine(Markup.Escape(PrintHex.ByteArrayToHexArrayString(sector,
settings.Width,
true)));
else
AaruConsole.ErrorWriteLine(string.Format(UI.Error_0_reading_sector_1, errno, start + i));
AaruConsole.ErrorWriteLine(string.Format(UI.Error_0_reading_sector_1, errno, settings.Start + i));
}
}
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : ImageFamily
{
[Description("How many sectors to print.")]
[DefaultValue(1)]
[CommandOption("-l|--length")]
public ulong Length { get; init; }
[Description("Print sectors with tags included.")]
[DefaultValue(false)]
[CommandOption("-r|--long-sectors")]
public bool LongSectors { get; init; }
[Description("Starting sector.")]
[DefaultValue(0)]
[CommandOption("-s|--start")]
public ulong Start { get; init; }
[Description("How many bytes to print per line.")]
[DefaultValue(32)]
[CommandOption("-w|--width")]
public ushort Width { get; init; }
[Description("Media image path")]
[CommandArgument(0, "<image-path>")]
public string ImagePath { get; init; }
}
#endregion
}

View File

@@ -30,10 +30,8 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using Aaru.CommonTypes;
@@ -47,39 +45,19 @@ using Aaru.Localization;
using Humanizer;
using Humanizer.Localisation;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image;
sealed class VerifyCommand : Command
sealed class VerifyCommand : Command<VerifyCommand.Settings>
{
const string MODULE_NAME = "Verify command";
public VerifyCommand() : base("verify", UI.Image_Verify_Command_Description)
{
Add(new Option<bool>(["--verify-disc", "-w"], () => true, UI.Verify_media_image_if_supported));
Add(new Option<bool>(["--verify-sectors", "-s"], () => true, UI.Verify_all_sectors_if_supported));
Add(new Option<bool>(["--create-graph", "-g"], () => true, UI.Create_graph_of_verified_disc));
Add(new Option<uint>(["--dimensions"], () => 1080, UI.Verify_dimensions_paramater_help));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Disc_image_path,
Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string imagePath, bool verifyDisc, bool verifySectors,
bool createGraph, uint dimensions)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -97,7 +75,7 @@ sealed class VerifyCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -110,20 +88,20 @@ sealed class VerifyCommand : Command
Statistics.AddCommand("verify");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verify-disc={0}", verifyDisc);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verify-sectors={0}", verifySectors);
AaruConsole.DebugWriteLine(MODULE_NAME, "--create-graph={0}", createGraph);
AaruConsole.DebugWriteLine(MODULE_NAME, "--dimensions={0}", dimensions);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verify-disc={0}", settings.VerifyDisc);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verify-sectors={0}", settings.VerifySectors);
AaruConsole.DebugWriteLine(MODULE_NAME, "--create-graph={0}", settings.CreateGraph);
AaruConsole.DebugWriteLine(MODULE_NAME, "--dimensions={0}", settings.Dimensions);
IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath);
inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
});
if(inputFilter == null)
@@ -183,7 +161,7 @@ sealed class VerifyCommand : Command
var chkWatch = new Stopwatch();
if(verifyDisc && verifiableImage != null)
if(settings.VerifyDisc && verifiableImage != null)
{
bool? discCheckStatus = null;
@@ -218,7 +196,7 @@ sealed class VerifyCommand : Command
chkWatch.Elapsed.Humanize(minUnit: TimeUnit.Second));
}
if(!verifySectors)
if(!settings.VerifySectors)
{
return correctImage switch
{
@@ -237,17 +215,20 @@ sealed class VerifyCommand : Command
{
Spiral.DiscParameters spiralParameters = null;
if(createGraph) spiralParameters = Spiral.DiscParametersFromMediaType(opticalMediaImage.Info.MediaType);
if(settings.CreateGraph)
spiralParameters = Spiral.DiscParametersFromMediaType(opticalMediaImage.Info.MediaType);
if(spiralParameters is not null)
{
mediaGraph = new Spiral((int)dimensions,
(int)dimensions,
mediaGraph = new Spiral((int)settings.Dimensions,
(int)settings.Dimensions,
spiralParameters,
opticalMediaImage.Info.Sectors);
}
else if(createGraph)
mediaGraph = new BlockMap((int)dimensions, (int)dimensions, opticalMediaImage.Info.Sectors);
else if(settings.CreateGraph)
mediaGraph = new BlockMap((int)settings.Dimensions,
(int)settings.Dimensions,
opticalMediaImage.Info.Sectors);
List<Track> inputTracks = opticalMediaImage.Tracks;
ulong currentSectorAll = 0;
@@ -439,23 +420,25 @@ sealed class VerifyCommand : Command
AaruConsole.VerboseWriteLine(UI.Checking_sector_checksums_took_0,
stopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second));
if(verbose)
if(settings.Verbose)
{
AaruConsole.VerboseWriteLine($"[red]{UI.LBAs_with_error}[/]");
if(failingLbas.Count == (int)inputFormat.Info.Sectors)
AaruConsole.VerboseWriteLine($"\t[red]{UI.all_sectors}[/]");
else
foreach(ulong t in failingLbas)
AaruConsole.VerboseWriteLine("\t{0}", t);
{
foreach(ulong t in failingLbas) AaruConsole.VerboseWriteLine("\t{0}", t);
}
AaruConsole.WriteLine($"[yellow3_1]{UI.LBAs_without_checksum}[/]");
if(unknownLbas.Count == (int)inputFormat.Info.Sectors)
AaruConsole.VerboseWriteLine($"\t[yellow3_1]{UI.all_sectors}[/]");
else
foreach(ulong t in unknownLbas)
AaruConsole.VerboseWriteLine("\t{0}", t);
{
foreach(ulong t in unknownLbas) AaruConsole.VerboseWriteLine("\t{0}", t);
}
}
// TODO: Convert to table
@@ -483,4 +466,31 @@ sealed class VerifyCommand : Command
true => (int)ErrorNumber.NoError
};
}
#region Nested type: Settings
public class Settings : ImageFamily
{
[Description("Verify media image if supported.")]
[DefaultValue(true)]
[CommandOption("-w|--verify-disc")]
public bool VerifyDisc { get; init; }
[Description("Verify all sectors if supported.")]
[DefaultValue(true)]
[CommandOption("-s|--verify-sectors")]
public bool VerifySectors { get; init; }
[Description("Create graph of verified disc (currently only implemented for optical discs).")]
[DefaultValue(true)]
[CommandOption("-g|--create-graph")]
public bool CreateGraph { get; init; }
[Description("Dimensions, as a square, in pixels, for the graph of verified media.")]
[DefaultValue(1080)]
[CommandOption("-d|--dimensions")]
public uint Dimensions { get; init; }
[Description("Disc image path")]
[CommandArgument(0, "<image-path>")]
public string ImagePath { get; init; }
}
#endregion
}

View File

@@ -30,9 +30,6 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.Linq;
using System.Text;
using Aaru.CommonTypes.Enums;
@@ -40,21 +37,20 @@ using Aaru.Console;
using Aaru.Core;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands;
sealed class ListEncodingsCommand : Command
sealed class ListEncodingsCommand : Command<ListEncodingsCommand.Settings>
{
const string MODULE_NAME = "List-Encodings command";
public ListEncodingsCommand() : base("list-encodings", UI.List_Encodings_Command_Description) =>
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
public override int Execute(CommandContext context, Settings settings)
public static int Invoke(bool debug, bool verbose)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -72,7 +68,7 @@ sealed class ListEncodingsCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -85,8 +81,8 @@ sealed class ListEncodingsCommand : Command
Statistics.AddCommand("list-encodings");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
var encodings = Encoding.GetEncodings()
.Select(info => new CommonEncodingInfo
@@ -123,5 +119,11 @@ sealed class ListEncodingsCommand : Command
public string DisplayName;
}
#endregion
#region Nested type: Settings
public class Settings : BaseSettings {}
#endregion
}

View File

@@ -30,10 +30,7 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.Linq;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
@@ -42,21 +39,19 @@ using Aaru.Console;
using Aaru.Core;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands;
sealed class ListNamespacesCommand : Command
sealed class ListNamespacesCommand : Command<ListNamespacesCommand.Settings>
{
const string MODULE_NAME = "List-Namespaces command";
public ListNamespacesCommand() : base("list-namespaces", UI.List_Namespaces_Command_Description) =>
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
public static int Invoke(bool debug, bool verbose)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -74,7 +69,7 @@ sealed class ListNamespacesCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -85,8 +80,8 @@ sealed class ListNamespacesCommand : Command
};
}
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
Statistics.AddCommand("list-namespaces");
PluginRegister plugins = PluginRegister.Singleton;
@@ -112,4 +107,10 @@ sealed class ListNamespacesCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : BaseSettings {}
#endregion
}

View File

@@ -33,8 +33,7 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -56,121 +55,24 @@ using Aaru.Core.Logging;
using Aaru.Localization;
using Schemas;
using Spectre.Console;
using Spectre.Console.Cli;
using Dump = Aaru.Core.Devices.Dumping.Dump;
using File = System.IO.File;
namespace Aaru.Commands.Media;
// TODO: Add raw dumping
sealed class DumpMediaCommand : Command
sealed class DumpMediaCommand : Command<DumpMediaCommand.Settings>
{
const string MODULE_NAME = "Dump-Media command";
static ProgressTask _progressTask1;
static ProgressTask _progressTask2;
public DumpMediaCommand() : base("dump", UI.Media_Dump_Command_Description)
{
Add(new Option<string>(["--cicm-xml", "-x"], () => null, UI.Take_metadata_from_existing_CICM_XML_sidecar));
Add(new Option<string>(["--encoding", "-e"], () => null, UI.Name_of_character_encoding_to_use));
Add(new Option<bool>("--first-pregap", () => false, UI.Try_to_read_first_track_pregap));
Add(new Option<bool>("--fix-offset", () => true, UI.Fix_audio_tracks_offset));
Add(new Option<bool>(["--force", "-f"], () => false, UI.Continue_dumping_whatever_happens));
Add(new Option<string>(["--format", "-t"],
() => null,
UI.Format_of_the_output_image_as_plugin_name_or_plugin_id));
Add(new Option<bool>("--metadata", () => true, UI.Enables_creating_Aaru_Metadata_sidecar));
Add(new Option<bool>("--trim", () => true, UI.Enables_trimming_errored_from_skipped_sectors));
Add(new Option<string>(["--options", "-O"], () => null, UI.Comma_separated_name_value_pairs_of_image_options));
Add(new Option<bool>("--persistent", () => false, UI.Try_to_recover_partial_or_incorrect_data));
Add(new Option<bool>(["--resume", "-r"], () => true, UI.Create_or_use_resume_mapfile));
Add(new Option<ushort>(["--retry-passes", "-p"], () => 5, UI.How_many_retry_passes_to_do));
Add(new Option<uint>(["--skip", "-k"],
() => 512,
UI.When_an_unreadable_sector_is_found_skip_this_many_sectors));
Add(new Option<bool>(["--stop-on-error", "-s"], () => false, UI.Stop_media_dump_on_first_error));
Add(new Option<string>("--subchannel", () => UI.Subchannel_name_any, UI.Subchannel_to_dump_help));
Add(new Option<byte>("--speed", () => 0, UI.Speed_to_dump));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Device_path,
Name = "device-path"
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Output_image_path_Dump_help,
Name = "output-path"
});
Add(new Option<bool>(["--private"], () => false, UI.Do_not_store_paths_and_serial_numbers_in_log_or_metadata));
Add(new Option<bool>(["--fix-subchannel-position"], () => true, UI.Fix_subchannel_position_help));
Add(new Option<bool>(["--retry-subchannel"], () => true, UI.Retry_subchannel_help));
Add(new Option<bool>(["--fix-subchannel"], () => false, UI.Fix_subchannel_help));
Add(new Option<bool>(["--fix-subchannel-crc"], () => false, UI.Fix_subchannel_crc_help));
Add(new Option<bool>(["--generate-subchannels"], () => false, UI.Generate_subchannels_dump_help));
Add(new Option<bool>(["--skip-cdiready-hole"], () => true, UI.Skip_CDi_Ready_hole_help));
Add(new Option<bool>(["--eject"], () => false, UI.Eject_media_after_dump_finishes));
Add(new Option<uint>(["--max-blocks"], () => 64, UI.Maximum_number_of_blocks_to_read_at_once));
Add(new Option<bool>(["--use-buffered-reads"], () => true, UI.OS_buffered_reads_help));
Add(new Option<bool>(["--store-encrypted"], () => true, UI.Store_encrypted_data_as_is));
Add(new Option<bool>(["--title-keys"], () => true, UI.Try_to_read_the_title_keys_from_CSS_DVDs));
Add(new Option<uint>(["--ignore-cdr-runouts"],
() => 10,
UI.How_many_CDRW_run_out_sectors_to_ignore_and_regenerate));
Add(new Option<bool>(["--create-graph", "-g"], () => true, UI.Create_graph_of_dumped_media));
Add(new Option<uint>(["--dimensions"], () => 1080, UI.Dump_graph_dimensions_argument_help));
Add(new Option<string>(["--aaru-metadata", "-m"],
() => null,
"Take metadata from existing Aaru Metadata sidecar."));
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string cicmXml, string devicePath, bool resume, string encoding,
bool firstPregap, bool fixOffset, bool force, bool metadata, bool trim, string outputPath,
string options, bool persistent, ushort retryPasses, uint skip, byte speed,
bool stopOnError, string format, string subchannel, bool @private,
bool fixSubchannelPosition, bool retrySubchannel, bool fixSubchannel,
bool fixSubchannelCrc, bool generateSubchannels, bool skipCdiReadyHole, bool eject,
uint maxBlocks, bool useBufferedReads, bool storeEncrypted, bool titleKeys,
uint ignoreCdrRunOuts, bool createGraph, uint dimensions, string aaruMetadata)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -188,7 +90,7 @@ sealed class DumpMediaCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -199,53 +101,60 @@ sealed class DumpMediaCommand : Command
};
}
bool fixSubchannel = settings.FixSubchannel;
bool fixSubchannelCrc = settings.FixSubchannelCrc;
bool fixSubchannelPosition = settings.FixSubchannelPosition;
uint maxBlocks = settings.MaxBlocks;
bool eject = settings.Eject;
fixSubchannel |= fixSubchannelCrc;
fixSubchannelPosition |= retrySubchannel || fixSubchannel;
fixSubchannelPosition |= settings.RetrySubchannel || fixSubchannel;
if(maxBlocks == 0) maxBlocks = 64;
Statistics.AddCommand("dump-media");
AaruConsole.DebugWriteLine(MODULE_NAME, "--cicm-xml={0}", Markup.Escape(cicmXml ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(devicePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--first-pregap={0}", firstPregap);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-offset={0}", fixOffset);
AaruConsole.DebugWriteLine(MODULE_NAME, "--force={0}", force);
AaruConsole.DebugWriteLine(MODULE_NAME, "--format={0}", Markup.Escape(format ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--metadata={0}", metadata);
AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(options ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(outputPath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--persistent={0}", persistent);
AaruConsole.DebugWriteLine(MODULE_NAME, "--resume={0}", resume);
AaruConsole.DebugWriteLine(MODULE_NAME, "--retry-passes={0}", retryPasses);
AaruConsole.DebugWriteLine(MODULE_NAME, "--skip={0}", skip);
AaruConsole.DebugWriteLine(MODULE_NAME, "--stop-on-error={0}", stopOnError);
AaruConsole.DebugWriteLine(MODULE_NAME, "--trim={0}", trim);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--subchannel={0}", Markup.Escape(subchannel ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "----private={0}", @private);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel-position={0}", fixSubchannelPosition);
AaruConsole.DebugWriteLine(MODULE_NAME, "--retry-subchannel={0}", retrySubchannel);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel={0}", fixSubchannel);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel-crc={0}", fixSubchannelCrc);
AaruConsole.DebugWriteLine(MODULE_NAME, "--generate-subchannels={0}", generateSubchannels);
AaruConsole.DebugWriteLine(MODULE_NAME, "--skip-cdiready-hole={0}", skipCdiReadyHole);
AaruConsole.DebugWriteLine(MODULE_NAME, "--eject={0}", eject);
AaruConsole.DebugWriteLine(MODULE_NAME, "--max-blocks={0}", maxBlocks);
AaruConsole.DebugWriteLine(MODULE_NAME, "--use-buffered-reads={0}", useBufferedReads);
AaruConsole.DebugWriteLine(MODULE_NAME, "--store-encrypted={0}", storeEncrypted);
AaruConsole.DebugWriteLine(MODULE_NAME, "--title-keys={0}", titleKeys);
AaruConsole.DebugWriteLine(MODULE_NAME, "--ignore-cdr-runouts={0}", ignoreCdrRunOuts);
AaruConsole.DebugWriteLine(MODULE_NAME, "--create-graph={0}", createGraph);
AaruConsole.DebugWriteLine(MODULE_NAME, "--dimensions={0}", dimensions);
AaruConsole.DebugWriteLine(MODULE_NAME, "--aaru-metadata={0}", Markup.Escape(aaruMetadata ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--cicm-xml={0}", Markup.Escape(settings.CicmXml ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(settings.DevicePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--first-pregap={0}", settings.FirstPregap);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-offset={0}", settings.FixOffset);
AaruConsole.DebugWriteLine(MODULE_NAME, "--force={0}", settings.Force);
AaruConsole.DebugWriteLine(MODULE_NAME, "--format={0}", Markup.Escape(settings.Format ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--metadata={0}", settings.Metadata);
AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(settings.Options ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(settings.OutputPath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--persistent={0}", settings.Persistent);
AaruConsole.DebugWriteLine(MODULE_NAME, "--resume={0}", settings.Resume);
AaruConsole.DebugWriteLine(MODULE_NAME, "--retry-passes={0}", settings.RetryPasses);
AaruConsole.DebugWriteLine(MODULE_NAME, "--skip={0}", settings.Skip);
AaruConsole.DebugWriteLine(MODULE_NAME, "--stop-on-error={0}", settings.StopOnError);
AaruConsole.DebugWriteLine(MODULE_NAME, "--trim={0}", settings.Trim);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--subchannel={0}", Markup.Escape(settings.Subchannel ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "----private={0}", settings.Private);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel-position={0}", settings.FixSubchannelPosition);
AaruConsole.DebugWriteLine(MODULE_NAME, "--retry-subchannel={0}", settings.RetrySubchannel);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel={0}", fixSubchannel);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel-crc={0}", fixSubchannelCrc);
AaruConsole.DebugWriteLine(MODULE_NAME, "--generate-subchannels={0}", settings.GenerateSubchannels);
AaruConsole.DebugWriteLine(MODULE_NAME, "--skip-cdiready-hole={0}", settings.SkipCdiReadyHole);
AaruConsole.DebugWriteLine(MODULE_NAME, "--eject={0}", eject);
AaruConsole.DebugWriteLine(MODULE_NAME, "--max-blocks={0}", maxBlocks);
AaruConsole.DebugWriteLine(MODULE_NAME, "--use-buffered-reads={0}", settings.UseBufferedReads);
AaruConsole.DebugWriteLine(MODULE_NAME, "--store-encrypted={0}", settings.StoreEncrypted);
AaruConsole.DebugWriteLine(MODULE_NAME, "--title-keys={0}", settings.TitleKeys);
AaruConsole.DebugWriteLine(MODULE_NAME, "--ignore-cdr-runouts={0}", settings.IgnoreCdrRunOuts);
AaruConsole.DebugWriteLine(MODULE_NAME, "--create-graph={0}", settings.CreateGraph);
AaruConsole.DebugWriteLine(MODULE_NAME, "--dimensions={0}", settings.Dimensions);
AaruConsole.DebugWriteLine(MODULE_NAME, "--aaru-metadata={0}", Markup.Escape(settings.AaruMetadata ?? ""));
// TODO: Disabled temporarily
//AaruConsole.DebugWriteLine(MODULE_NAME, "--raw={0}", Markup.Escape(raw ?? ""));
Dictionary<string, string> parsedOptions = Core.Options.Parse(options);
Dictionary<string, string> parsedOptions = Options.Parse(settings.Options);
AaruConsole.DebugWriteLine(MODULE_NAME, UI.Parsed_options);
foreach(KeyValuePair<string, string> parsedOption in parsedOptions)
@@ -253,13 +162,13 @@ sealed class DumpMediaCommand : Command
Encoding encodingClass = null;
if(encoding != null)
if(settings.Encoding != null)
{
try
{
encodingClass = Claunia.Encoding.Encoding.GetEncoding(encoding);
encodingClass = Claunia.Encoding.Encoding.GetEncoding(settings.Encoding);
if(verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
if(settings.Verbose) AaruConsole.VerboseWriteLine(UI.encoding_for_0, encodingClass.EncodingName);
}
catch(ArgumentException)
{
@@ -271,43 +180,44 @@ sealed class DumpMediaCommand : Command
DumpSubchannel wantedSubchannel = DumpSubchannel.Any;
if(subchannel?.ToLower(CultureInfo.CurrentUICulture) == UI.Subchannel_name_any || subchannel is null)
if(settings.Subchannel?.ToLower(CultureInfo.CurrentUICulture) == UI.Subchannel_name_any ||
settings.Subchannel is null)
wantedSubchannel = DumpSubchannel.Any;
else if(subchannel?.ToLowerInvariant() == UI.Subchannel_name_rw)
else if(settings.Subchannel?.ToLowerInvariant() == UI.Subchannel_name_rw)
wantedSubchannel = DumpSubchannel.Rw;
else if(subchannel?.ToLowerInvariant() == UI.Subchannel_name_rw_or_pq)
else if(settings.Subchannel?.ToLowerInvariant() == UI.Subchannel_name_rw_or_pq)
wantedSubchannel = DumpSubchannel.RwOrPq;
else if(subchannel?.ToLowerInvariant() == UI.Subchannel_name_pq)
else if(settings.Subchannel?.ToLowerInvariant() == UI.Subchannel_name_pq)
wantedSubchannel = DumpSubchannel.Pq;
else if(subchannel?.ToLowerInvariant() == UI.Subchannel_name_none)
else if(settings.Subchannel?.ToLowerInvariant() == UI.Subchannel_name_none)
wantedSubchannel = DumpSubchannel.None;
else
AaruConsole.WriteLine(UI.Incorrect_subchannel_type_0_requested, subchannel);
AaruConsole.WriteLine(UI.Incorrect_subchannel_type_0_requested, settings.Subchannel);
string filename = Path.GetFileNameWithoutExtension(outputPath);
string filename = Path.GetFileNameWithoutExtension(settings.OutputPath);
bool isResponse = filename.StartsWith("#", StringComparison.OrdinalIgnoreCase) &&
File.Exists(Path.Combine(Path.GetDirectoryName(outputPath),
Path.GetFileNameWithoutExtension(outputPath)));
File.Exists(Path.Combine(Path.GetDirectoryName(settings.OutputPath),
Path.GetFileNameWithoutExtension(settings.OutputPath)));
TextReader resReader;
if(isResponse)
{
resReader = new StreamReader(Path.Combine(Path.GetDirectoryName(outputPath),
Path.GetFileNameWithoutExtension(outputPath)));
resReader = new StreamReader(Path.Combine(Path.GetDirectoryName(settings.OutputPath),
Path.GetFileNameWithoutExtension(settings.OutputPath)));
}
else
resReader = new StringReader(Path.GetFileNameWithoutExtension(outputPath));
resReader = new StringReader(Path.GetFileNameWithoutExtension(settings.OutputPath));
if(isResponse) eject = true;
PluginRegister plugins = PluginRegister.Singleton;
List<IBaseWritableImage> candidates = [];
string extension = Path.GetExtension(outputPath);
string extension = Path.GetExtension(settings.OutputPath);
// Try extension
if(string.IsNullOrEmpty(format))
if(string.IsNullOrEmpty(settings.Format))
{
candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null
@@ -316,7 +226,7 @@ sealed class DumpMediaCommand : Command
}
// Try Id
else if(Guid.TryParse(format, out Guid outId))
else if(Guid.TryParse(settings.Format, out Guid outId))
{
candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null
@@ -329,7 +239,7 @@ sealed class DumpMediaCommand : Command
{
candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null
where plugin.Name.Equals(format, StringComparison.InvariantCultureIgnoreCase)
where plugin.Name.Equals(settings.Format, StringComparison.InvariantCultureIgnoreCase)
select plugin);
}
@@ -381,6 +291,8 @@ sealed class DumpMediaCommand : Command
.Replace('*', '');
}
string devicePath = settings.DevicePath;
if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0]))
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
@@ -424,11 +336,11 @@ sealed class DumpMediaCommand : Command
Statistics.AddDevice(dev);
string outputPrefix = Path.Combine(Path.GetDirectoryName(outputPath), responseLine);
string outputPrefix = Path.Combine(Path.GetDirectoryName(settings.OutputPath), responseLine);
Resume resumeClass = null;
if(resume)
if(settings.Resume)
{
try
{
@@ -445,7 +357,7 @@ sealed class DumpMediaCommand : Command
}
// DEPRECATED: To be removed in Aaru 7
else if(File.Exists(outputPrefix + ".resume.xml") && resume)
else if(File.Exists(outputPrefix + ".resume.xml") && settings.Resume)
{
// Should be covered by virtue of being the same exact class as the JSON above
#pragma warning disable IL2026
@@ -488,13 +400,13 @@ sealed class DumpMediaCommand : Command
Metadata sidecar = null;
if(aaruMetadata != null)
if(settings.AaruMetadata != null)
{
if(File.Exists(aaruMetadata))
if(File.Exists(settings.AaruMetadata))
{
try
{
var fs = new FileStream(aaruMetadata, FileMode.Open);
var fs = new FileStream(settings.AaruMetadata, FileMode.Open);
sidecar =
(JsonSerializer.Deserialize(fs, typeof(MetadataJson), MetadataJsonContext.Default) as
@@ -520,13 +432,13 @@ sealed class DumpMediaCommand : Command
return (int)ErrorNumber.NoSuchFile;
}
}
else if(cicmXml != null)
else if(settings.CicmXml != null)
{
if(File.Exists(cicmXml))
if(File.Exists(settings.CicmXml))
{
try
{
var sr = new StreamReader(cicmXml);
var sr = new StreamReader(settings.CicmXml);
// Bypassed by JSON source generator used above
#pragma warning disable IL2026
@@ -563,16 +475,16 @@ sealed class DumpMediaCommand : Command
candidates = [];
// Try extension
if(string.IsNullOrEmpty(format))
if(string.IsNullOrEmpty(settings.Format))
{
candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null
where plugin.KnownExtensions.Contains(Path.GetExtension(outputPath))
where plugin.KnownExtensions.Contains(Path.GetExtension(settings.OutputPath))
select plugin);
}
// Try Id
else if(Guid.TryParse(format, out Guid outId))
else if(Guid.TryParse(settings.Format, out Guid outId))
{
candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null
@@ -585,15 +497,16 @@ sealed class DumpMediaCommand : Command
{
candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null
where plugin.Name.Equals(format, StringComparison.InvariantCultureIgnoreCase)
where plugin.Name.Equals(settings.Format,
StringComparison.InvariantCultureIgnoreCase)
select plugin);
}
IBaseWritableImage outputFormat = candidates[0];
var dumpLog = new DumpLog(outputPrefix + ".log", dev, @private);
var dumpLog = new DumpLog(outputPrefix + ".log", dev, settings.Private);
if(verbose)
if(settings.Verbose)
{
dumpLog.WriteLine(UI.Output_image_format_0_1, outputFormat.Name, outputFormat.Id);
AaruConsole.VerboseWriteLine(UI.Output_image_format_0_1, outputFormat.Name, outputFormat.Id);
@@ -606,15 +519,15 @@ sealed class DumpMediaCommand : Command
var errorLog = new ErrorLog(outputPrefix + ".error.log");
var dumper = new Dump(resume,
var dumper = new Dump(settings.Resume,
dev,
devicePath,
outputFormat,
retryPasses,
force,
settings.RetryPasses,
settings.Force,
false,
persistent,
stopOnError,
settings.Persistent,
settings.StopOnError,
resumeClass,
dumpLog,
encodingClass,
@@ -622,29 +535,29 @@ sealed class DumpMediaCommand : Command
outputPrefix + extension,
parsedOptions,
sidecar,
skip,
metadata,
trim,
firstPregap,
fixOffset,
debug,
settings.Skip,
settings.Metadata,
settings.Trim,
settings.FirstPregap,
settings.FixOffset,
settings.Debug,
wantedSubchannel,
speed,
@private,
settings.Speed,
settings.Private,
fixSubchannelPosition,
retrySubchannel,
settings.RetrySubchannel,
fixSubchannel,
fixSubchannelCrc,
skipCdiReadyHole,
settings.SkipCdiReadyHole,
errorLog,
generateSubchannels,
settings.GenerateSubchannels,
maxBlocks,
useBufferedReads,
storeEncrypted,
titleKeys,
ignoreCdrRunOuts,
createGraph,
dimensions);
settings.UseBufferedReads,
settings.StoreEncrypted,
settings.TitleKeys,
settings.IgnoreCdrRunOuts,
settings.CreateGraph,
settings.Dimensions);
AnsiConsole.Progress()
.AutoClear(true)
@@ -765,4 +678,146 @@ sealed class DumpMediaCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : MediaFamily
{
[Description("Take metadata from existing CICM XML sidecar.")]
[CommandOption("-x|--cicm-xml")]
[DefaultValue(null)]
public string CicmXml { get; init; }
[Description("Name of character encoding to use.")]
[CommandOption("-e|--encoding")]
[DefaultValue(null)]
public string Encoding { get; init; }
[Description("Try to read first track pregap. Only applicable to CD/DDCD/GD.")]
[CommandOption("--first-pregap")]
[DefaultValue(false)]
public bool FirstPregap { get; init; }
[Description("Fix audio tracks offset. Only applicable to CD/GD.")]
[CommandOption("--fix-offset")]
[DefaultValue(true)]
public bool FixOffset { get; init; }
[Description("Continue dumping whatever happens.")]
[CommandOption("-f|--force")]
[DefaultValue(false)]
public bool Force { get; init; }
[Description("Format of the output image, as plugin name or plugin id. If not present, will try to detect it from output image extension.")]
[CommandOption("-t|--format")]
[DefaultValue(null)]
public string Format { get; init; }
[Description("Enables creating Aaru Metadata sidecar.")]
[CommandOption("--metadata")]
[DefaultValue(true)]
public bool Metadata { get; init; }
[Description("Enables trimming errored from skipped sectors.")]
[CommandOption("--trim")]
[DefaultValue(true)]
public bool Trim { get; init; }
[Description("Comma separated name=value pairs of options to pass to output image plugin.")]
[CommandOption("-O|--options")]
[DefaultValue(null)]
public string Options { get; init; }
[Description("Try to recover partial or incorrect data.")]
[CommandOption("--persistent")]
[DefaultValue(false)]
public bool Persistent { get; init; }
[Description("Create/use resume mapfile.")]
[CommandOption("-r|--resume")]
[DefaultValue(true)]
public bool Resume { get; init; }
[Description("How many retry passes to do.")]
[CommandOption("-p|--retry-passes")]
[DefaultValue(5)]
public ushort RetryPasses { get; init; }
[Description("When an unreadable sector is found skip this many sectors.")]
[CommandOption("-k|--skip")]
[DefaultValue(512)]
public uint Skip { get; init; }
[Description("Stop media dump on first error.")]
[CommandOption("-s|--stop-on-error")]
[DefaultValue(false)]
public bool StopOnError { get; init; }
[Description("Subchannel to dump. Only applicable to CD/GD. Values: any, rw, rw-or-pq, pq, none.")]
[CommandOption("--subchannel")]
[DefaultValue("any")]
public string Subchannel { get; init; }
[Description("Speed to dump. Only applicable to optical drives, 0 for maximum.")]
[CommandOption("--speed")]
[DefaultValue(0)]
public byte Speed { get; init; }
[Description("Do not store paths and serial numbers in log or metadata.")]
[CommandOption("--private")]
[DefaultValue(false)]
public bool Private { get; init; }
[Description("Store subchannel according to the sector they describe.")]
[CommandOption("--fix-subchannel-position")]
[DefaultValue(true)]
public bool FixSubchannelPosition { get; init; }
[Description("Retry subchannel. Implies fixing subchannel position.")]
[CommandOption("--retry-subchannel")]
[DefaultValue(true)]
public bool RetrySubchannel { get; init; }
[Description("Try to fix subchannel. Implies fixing subchannel position.")]
[CommandOption("--fix-subchannel")]
[DefaultValue(false)]
public bool FixSubchannel { get; init; }
[Description("If subchannel looks OK but CRC fails, rewrite it. Implies fixing subchannel.")]
[CommandOption("--fix-subchannel-crc")]
[DefaultValue(false)]
public bool FixSubchannelCrc { get; init; }
[Description("Generates missing subchannels (they don\'t count as dumped in resume file).")]
[CommandOption("--generate-subchannels")]
[DefaultValue(false)]
public bool GenerateSubchannels { get; init; }
[Description("Skip the hole between data and audio in a CD-i Ready disc.")]
[CommandOption("--skip-cdiready-hole")]
[DefaultValue(true)]
public bool SkipCdiReadyHole { get; init; }
[Description("Eject media after dump finishes.")]
[CommandOption("--eject")]
[DefaultValue(false)]
public bool Eject { get; init; }
[Description("Maximum number of blocks to read at once.")]
[CommandOption("--max-blocks")]
[DefaultValue(64)]
public uint MaxBlocks { get; init; }
[Description("Use OS buffered reads if CMD23 is not supported. Only applicable to MMC/SD.")]
[CommandOption("--use-buffered-reads")]
[DefaultValue(true)]
public bool UseBufferedReads { get; init; }
[Description("Store encrypted data as is.")]
[CommandOption("--store-encrypted")]
[DefaultValue(true)]
public bool StoreEncrypted { get; init; }
[Description("Try to read the title keys from CSS encrypted DVDs (very slow).")]
[CommandOption("--title-keys")]
[DefaultValue(true)]
public bool TitleKeys { get; init; }
[Description("How many CD-R(W) run-out sectors to ignore and regenerate (0 for none).")]
[CommandOption("--ignore-cdr-runouts")]
[DefaultValue(10)]
public uint IgnoreCdrRunOuts { get; init; }
[Description("Create graph of dumped media. Currently only supported for CD/DVD/BD/GD/UMD.")]
[CommandOption("-g|--create-graph")]
[DefaultValue(true)]
public bool CreateGraph { get; init; }
[Description("Dimensions in pixels of the square that will contain the graph of dumped media.")]
[CommandOption("--dimensions")]
[DefaultValue(1080)]
public uint Dimensions { get; init; }
[Description("Take metadata from existing Aaru Metadata sidecar.")]
[CommandOption("--aaru-metadata")]
[DefaultValue(null)]
public string AaruMetadata { get; init; }
[Description("Device path")]
[CommandArgument(0, "<device-path>")]
public string DevicePath { get; init; }
[Description("Output image path. If filename starts with # and exists, it will be read as a list of output images, its extension will be used to detect the image output format, each media will be ejected and confirmation for the next one will be asked.")]
[CommandArgument(1, "<output-path>")]
public string OutputPath { get; init; }
}
#endregion
}

View File

@@ -32,8 +32,7 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using System.Linq;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Structs;
@@ -53,9 +52,9 @@ using Aaru.Decoders.Xbox;
using Aaru.Localization;
using Humanizer.Bytes;
using Spectre.Console;
using Spectre.Console.Cli;
using BCA = Aaru.Decoders.Bluray.BCA;
using Cartridge = Aaru.Decoders.DVD.Cartridge;
using Command = System.CommandLine.Command;
using DDS = Aaru.Decoders.DVD.DDS;
using DMI = Aaru.Decoders.Xbox.DMI;
using Session = Aaru.Decoders.CD.Session;
@@ -63,29 +62,16 @@ using Spare = Aaru.Decoders.DVD.Spare;
namespace Aaru.Commands.Media;
sealed class MediaInfoCommand : Command
sealed class MediaInfoCommand : Command<MediaInfoCommand.Settings>
{
const string MODULE_NAME = "Media-Info command";
public MediaInfoCommand() : base("info", UI.Media_Info_Command_Description)
{
Add(new Option<string>(["--output-prefix", "-w"], () => null, UI.Prefix_for_saving_binary_information));
public override int Execute(CommandContext context, Settings settings)
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Device_path,
Name = "device-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string devicePath, string outputPrefix)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -103,7 +89,7 @@ sealed class MediaInfoCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -116,10 +102,12 @@ sealed class MediaInfoCommand : Command
Statistics.AddCommand("media-info");
AaruConsole.DebugWriteLine(MODULE_NAME, "debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "device={0}", Markup.Escape(devicePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "output-prefix={0}", Markup.Escape(outputPrefix ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "device={0}", Markup.Escape(settings.DevicePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "output-prefix={0}", Markup.Escape(settings.OutputPrefix ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "verbose={0}", settings.Verbose);
string devicePath = settings.DevicePath;
if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0]))
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
@@ -170,12 +158,12 @@ sealed class MediaInfoCommand : Command
break;
case DeviceType.NVMe:
DoNvmeMediaInfo(outputPrefix, dev);
DoNvmeMediaInfo(settings.OutputPrefix, dev);
break;
case DeviceType.ATAPI:
case DeviceType.SCSI:
DoScsiMediaInfo(debug, outputPrefix, dev);
DoScsiMediaInfo(settings.Debug, settings.OutputPrefix, dev);
break;
default:
@@ -827,7 +815,7 @@ sealed class MediaInfoCommand : Command
AaruConsole.Write($"[bold]{Localization.Core.Media_Serial_Number}:[/] ");
for(var i = 4; i < scsiInfo.MediaSerialNumber.Length; i++)
for(int i = 4; i < scsiInfo.MediaSerialNumber.Length; i++)
AaruConsole.Write("{0:X2}", scsiInfo.MediaSerialNumber[i]);
AaruConsole.WriteLine();
@@ -843,13 +831,13 @@ sealed class MediaInfoCommand : Command
if(tracks != null)
{
var firstLba = (uint)tracks.Min(t => t.StartSector);
uint firstLba = (uint)tracks.Min(t => t.StartSector);
bool supportsPqSubchannel = Dump.SupportsPqSubchannel(dev, null, null, firstLba);
bool supportsRwSubchannel = Dump.SupportsRwSubchannel(dev, null, null, firstLba);
// Open main database
var ctx = AaruContext.Create(Settings.Settings.MainDbPath);
var ctx = AaruContext.Create(Aaru.Settings.Settings.MainDbPath);
// Search for device in main database
Aaru.Database.Models.Device dbDev =
@@ -867,7 +855,7 @@ sealed class MediaInfoCommand : Command
out bool inexactPositioning,
false);
for(var t = 1; t < tracks.Length; t++) tracks[t - 1].EndSector = tracks[t].StartSector - 1;
for(int t = 1; t < tracks.Length; t++) tracks[t - 1].EndSector = tracks[t].StartSector - 1;
tracks[^1].EndSector = (ulong)lastSector;
@@ -969,4 +957,19 @@ sealed class MediaInfoCommand : Command
dev.Close();
}
#region Nested type: Settings
public class Settings : MediaFamily
{
[Description("Prefix for saving binary information from device.")]
[DefaultValue(null)]
[CommandOption("-w|--output-prefix")]
public string OutputPrefix { get; init; }
[Description("Device path")]
[CommandArgument(0, "<device-path>")]
public string DevicePath { get; init; }
}
#endregion
}

View File

@@ -30,19 +30,6 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System.CommandLine;
using Aaru.Localization;
namespace Aaru.Commands.Media;
sealed class MediaFamily : Command
{
public MediaFamily() : base("media", UI.Media_Command_Family_Description)
{
AddAlias("m");
AddCommand(new DumpMediaCommand());
AddCommand(new MediaInfoCommand());
AddCommand(new MediaScanCommand());
}
}
class MediaFamily : BaseSettings {}

View File

@@ -30,9 +30,7 @@
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using Aaru.CommonTypes.Enums;
using Aaru.Console;
using Aaru.Core;
@@ -42,42 +40,20 @@ using Humanizer;
using Humanizer.Bytes;
using Humanizer.Localisation;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Media;
sealed class MediaScanCommand : Command
sealed class MediaScanCommand : Command<MediaScanCommand.Settings>
{
const string MODULE_NAME = "Media-Scan command";
static ProgressTask _progressTask1;
public MediaScanCommand() : base("scan", UI.Media_Scan_Command_Description)
{
Add(new Option<string>(["--mhdd-log", "-m"],
() => null,
UI.Write_a_log_of_the_scan_in_the_format_used_by_MHDD));
Add(new Option<string>(["--ibg-log", "-b"],
() => null,
UI.Write_a_log_of_the_scan_in_the_format_used_by_ImgBurn));
Add(new Option<bool>(["--use-buffered-reads"], () => true, UI.OS_buffered_reads_help));
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = UI.Device_path,
Name = "device-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string devicePath, string ibgLog, string mhddLog,
bool useBufferedReads)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -95,7 +71,7 @@ sealed class MediaScanCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -108,12 +84,14 @@ sealed class MediaScanCommand : Command
Statistics.AddCommand("media-scan");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(devicePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--ibg-log={0}", Markup.Escape(ibgLog ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--mhdd-log={0}", Markup.Escape(mhddLog ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--use-buffered-reads={0}", useBufferedReads);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(settings.DevicePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--ibg-log={0}", Markup.Escape(settings.IbgLog ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--mhdd-log={0}", Markup.Escape(settings.MhddLog ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--use-buffered-reads={0}", settings.UseBufferedReads);
string devicePath = settings.DevicePath;
if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0]))
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
@@ -152,7 +130,7 @@ sealed class MediaScanCommand : Command
Statistics.AddDevice(dev);
var scanner = new MediaScan(mhddLog, ibgLog, devicePath, dev, useBufferedReads);
var scanner = new MediaScan(settings.MhddLog, settings.IbgLog, devicePath, dev, settings.UseBufferedReads);
ScanResults results = new();
AnsiConsole.Progress()
@@ -254,4 +232,27 @@ sealed class MediaScanCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : MediaFamily
{
[Description("Write a log of the scan in the format used by MHDD.")]
[DefaultValue(null)]
[CommandOption("-m|--mhdd-log")]
public string MhddLog { get; init; }
[Description("Write a log of the scan in the format used by ImgBurn.")]
[DefaultValue(null)]
[CommandOption("-b|--ibg-log")]
public string IbgLog { get; init; }
[Description("For MMC/SD, use OS buffered reads if CMD23 is not supported.")]
[DefaultValue(true)]
[CommandOption("--use-buffered-reads")]
public bool UseBufferedReads { get; init; }
[Description("Media device path")]
[CommandArgument(0, "<device-path>")]
public string DevicePath { get; init; }
}
#endregion
}

View File

@@ -33,38 +33,25 @@
// TODO: Fix errors returned
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.ComponentModel;
using Aaru.CommonTypes.Enums;
using Aaru.Console;
using Aaru.Core;
using Aaru.Localization;
using Spectre.Console;
using Spectre.Console.Cli;
using Remote = Aaru.Devices.Remote.Remote;
namespace Aaru.Commands;
sealed class RemoteCommand : Command
sealed class RemoteCommand : Command<RemoteCommand.Settings>
{
const string MODULE_NAME = "Remote command";
public RemoteCommand() : base("remote", UI.Remote_Command_Description)
{
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne,
Description = "aaru host",
Name = "host"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, string host)
public override int Execute(CommandContext context, Settings settings)
{
MainClass.PrintCopyright();
if(debug)
if(settings.Debug)
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
@@ -82,7 +69,7 @@ sealed class RemoteCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
}
if(verbose)
if(settings.Verbose)
{
AaruConsole.WriteEvent += (format, objects) =>
{
@@ -95,13 +82,13 @@ sealed class RemoteCommand : Command
Statistics.AddCommand("remote");
AaruConsole.DebugWriteLine(MODULE_NAME, "debug={0}", debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "host={0}", Markup.Escape(host ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "verbose={0}", verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "host={0}", Markup.Escape(settings.Host ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "verbose={0}", settings.Verbose);
try
{
var remote = new Remote(new Uri(host));
var remote = new Remote(new Uri(settings.Host));
Statistics.AddRemote(remote.ServerApplication,
remote.ServerVersion,
@@ -138,4 +125,15 @@ sealed class RemoteCommand : Command
return (int)ErrorNumber.NoError;
}
#region Nested type: Settings
public class Settings : BaseSettings
{
[CommandArgument(0, "<host>")]
[Description("Aaru host")]
public string Host { get; init; }
}
#endregion
}

View File

@@ -32,7 +32,6 @@
// ****************************************************************************/
using System;
using System.CommandLine;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -54,6 +53,8 @@ using Aaru.Settings;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Spectre.Console;
using Spectre.Console.Cli;
using ListOptionsCommand = Aaru.Commands.Filesystem.ListOptionsCommand;
namespace Aaru;
@@ -176,7 +177,7 @@ class MainClass
await ctx.SaveChangesAsync();
var mainDbUpdate = false;
bool mainDbUpdate = false;
if(!File.Exists(Settings.Settings.MainDbPath))
{
@@ -222,40 +223,179 @@ class MainClass
// There are too many places that depend on this being inited to be sure all are covered, so init it here.
PluginBase.Init();
var rootCommand = new RootCommand();
var app = new CommandApp();
rootCommand.AddGlobalOption(new Option<bool>(["--verbose", "-v"], () => false, UI.Shows_verbose_output));
app.Configure(config =>
{
config.UseAssemblyInformationalVersion();
rootCommand.AddGlobalOption(new Option<bool>(["--debug", "-d"],
() => false,
UI.Shows_debug_output_from_plugins));
config.AddBranch<ArchiveFamily>("archive",
archive =>
{
archive.SetDescription(UI.Archive_Command_Family_Description);
Option<bool> pauseOption = new(["--pause"], () => false, UI.Pauses_before_exiting);
archive.AddCommand<ArchiveListCommand>("list")
.WithAlias("l")
.WithAlias("ls")
.WithDescription(UI.Archive_List_Command_Description);
rootCommand.AddGlobalOption(pauseOption);
archive.AddCommand<ArchiveExtractCommand>("extract")
.WithAlias("x")
.WithDescription(UI.Archive_Extract_Command_Description);
rootCommand.Description = $"{_assemblyTitle} {_assemblyVersion?.InformationalVersion}\n{_assemblyCopyright}";
archive.AddCommand<ArchiveInfoCommand>("info")
.WithAlias("i")
.WithDescription(UI.Archive_Info_Command_Description);
})
.WithAlias("arc");
rootCommand.AddCommand(new DatabaseFamily(mainDbUpdate));
rootCommand.AddCommand(new DeviceFamily());
rootCommand.AddCommand(new FilesystemFamily());
rootCommand.AddCommand(new ImageFamily());
rootCommand.AddCommand(new MediaFamily());
rootCommand.AddCommand(new ArchiveFamily());
rootCommand.AddCommand(new ConfigureCommand());
rootCommand.AddCommand(new FormatsCommand());
rootCommand.AddCommand(new ListEncodingsCommand());
rootCommand.AddCommand(new ListNamespacesCommand());
rootCommand.AddCommand(new RemoteCommand());
config.AddBranch<DeviceFamily>("device",
device =>
{
device.SetDescription(UI.Device_Command_Family_Description);
int ret = await rootCommand.InvokeAsync(args);
device.AddCommand<DeviceReportCommand>("report")
.WithDescription(UI.Device_Report_Command_Description);
device.AddCommand<DeviceInfoCommand>("info")
.WithAlias("i")
.WithDescription(UI.Device_Info_Command_Description);
device.AddCommand<ListDevicesCommand>("list")
.WithAlias("l")
.WithAlias("ls")
.WithDescription(UI.Device_List_Command_Description);
})
.WithAlias("dev");
config.AddBranch<FilesystemFamily>("filesystem",
fs =>
{
fs.SetDescription(UI.Filesystem_Command_Family_Description);
fs.AddCommand<ExtractFilesCommand>("extract")
.WithAlias("x")
.WithDescription(UI.Filesystem_Extract_Command_Description);
fs.AddCommand<FilesystemInfoCommand>("info")
.WithAlias("i")
.WithDescription(UI.Filesystem_Info_Command_Description);
fs.AddCommand<LsCommand>("list")
.WithAlias("ls")
.WithDescription(UI.Filesystem_List_Command_Description);
fs.AddCommand<ListOptionsCommand>("options")
.WithDescription(UI.Filesystem_Options_Command_Description);
})
.WithAlias("fs")
.WithAlias("fi");
config.AddBranch<ImageFamily>("image",
image =>
{
image.SetDescription(UI.Image_Command_Family_Description);
image.AddCommand<ChecksumCommand>("checksum")
.WithAlias("chk")
.WithDescription(UI.Image_Checksum_Command_Description);
image.AddCommand<CompareCommand>("compate")
.WithAlias("cmp")
.WithDescription(UI.Image_Compare_Command_Description);
image.AddCommand<ConvertImageCommand>("convert")
.WithAlias("cvt")
.WithDescription(UI.Image_Convert_Command_Description);
image.AddCommand<CreateSidecarCommand>("create-sidecar")
.WithAlias("cs")
.WithDescription(UI.Image_Create_Sidecar_Command_Description);
image.AddCommand<DecodeCommand>("decode")
.WithDescription(UI.Image_Decode_Command_Description);
image.AddCommand<EntropyCommand>("entropy")
.WithDescription(UI.Image_Entropy_Command_Description);
image.AddCommand<ImageInfoCommand>("info")
.WithAlias("i")
.WithDescription(UI.Image_Info_Command_Description);
image.AddCommand<Commands.Image.ListOptionsCommand>("options")
.WithDescription(UI.Image_Options_Command_Description);
image.AddCommand<PrintHexCommand>("print-hex")
.WithAlias("ph")
.WithDescription(UI.Image_Print_Command_Description);
image.AddCommand<VerifyCommand>("verify")
.WithAlias("v")
.WithDescription(UI.Image_Verify_Command_Description);
})
.WithAlias("i")
.WithAlias("img");
config.AddBranch<MediaFamily>("media",
media =>
{
media.SetDescription(UI.Media_Command_Family_Description);
media.AddCommand<MediaInfoCommand>("info")
.WithAlias("i")
.WithDescription(UI.Media_Info_Command_Description);
media.AddCommand<MediaScanCommand>("scan")
.WithAlias("s")
.WithDescription(UI.Media_Scan_Command_Description);
media.AddCommand<DumpMediaCommand>("dump")
.WithAlias("d")
.WithDescription(UI.Media_Dump_Command_Description);
})
.WithAlias("m");
config.AddBranch<DatabaseFamily>("database",
db =>
{
db.SetDescription(UI.Database_Command_Family_Description);
db.AddCommand<StatisticsCommand>("stats")
.WithDescription(UI.Database_Stats_Command_Description);
db.AddCommand<UpdateCommand>("update")
.WithDescription(UI.Database_Update_Command_Description);
})
.WithAlias("db");
config.AddCommand<ConfigureCommand>("configure")
.WithAlias("cfg")
.WithDescription(UI.Configure_Command_Description);
config.AddCommand<FormatsCommand>("formats")
.WithAlias("fmt")
.WithDescription(UI.List_Formats_Command_Description);
config.AddCommand<ListEncodingsCommand>("list-encodings")
.WithAlias("le")
.WithDescription(UI.List_Encodings_Command_Description);
config.AddCommand<ListNamespacesCommand>("list-namespaces")
.WithAlias("ln")
.WithDescription(UI.List_Namespaces_Command_Description);
config.AddCommand<RemoteCommand>("remote").WithAlias("rem").WithDescription(UI.Remote_Command_Description);
});
PrintCopyright();
int ret = await app.RunAsync(args);
await Statistics.SaveStatsAsync();
if(!rootCommand.Parse(args).RootCommandResult.GetValueForOption(pauseOption)) return ret;
AaruConsole.WriteLine(UI.Press_any_key_to_exit);
System.Console.ReadKey();
// TODO: Return pause functionality
// AaruConsole.WriteLine(UI.Press_any_key_to_exit);
// System.Console.ReadKey();
return ret;
}

View File

@@ -1,70 +1,69 @@
<Project ToolsVersion="15.0">
<ItemGroup>
<PackageVersion Include="Aaru.Checksums.Native" Version="6.0.0-alpha9"/>
<PackageVersion Include="Aaru.Compression.Native" Version="6.0.0-alpha.10"/>
<PackageVersion Include="AsyncFixer" Version="1.6.0"/>
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.3"/>
<PackageVersion Include="Avalonia.Desktop" Version="11.3.3"/>
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.3"/>
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.3"/>
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.3.3"/>
<PackageVersion Include="Avalonia" Version="11.3.3"/>
<PackageVersion Include="Claunia.Encoding" Version="1.9.2"/>
<PackageVersion Include="Claunia.RsrcFork" Version="1.2.0"/>
<PackageVersion Include="DotNetZip" Version="1.16.0"/>
<PackageVersion Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2"/>
<PackageVersion Include="ErrorProne.NET.Structs" Version="0.1.2"/>
<PackageVersion Include="FluentAssertions.Analyzers" Version="0.34.1"/>
<PackageVersion Include="FluentAssertions" Version="8.6.0"/>
<PackageVersion Include="Humanizer.Core" Version="2.14.1"/>
<PackageVersion Include="Humanizer" Version="2.14.1"/>
<PackageVersion Include="JetBrains.Annotations" Version="2025.2.0"/>
<PackageVersion Include="Macross.Json.Extensions" Version="3.0.0"/>
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0-preview.7.25380.108"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="10.0.0-preview.7.25380.108"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0-preview.7.25380.108"/>
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0-preview.7.25380.108"/>
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-preview.7.25380.108"/>
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
<PackageVersion Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.15"/>
<PackageVersion Include="Microsoft.Win32.Registry" Version="6.0.0-preview.5.21301.5"/>
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3"/>
<PackageVersion Include="NUnit.Analyzers" Version="4.10.0"/>
<PackageVersion Include="nunit" Version="4.4.0"/>
<PackageVersion Include="NUnit3TestAdapter" Version="5.1.0"/>
<PackageVersion Include="Philips.CodeAnalysis.MaintainabilityAnalyzers" Version="1.8.0"/>
<PackageVersion Include="plist-cil" Version="2.3.1"/>
<PackageVersion Include="Roslynator.Analyzers" Version="4.14.0"/>
<PackageVersion Include="Roslynator.CodeAnalysis.Analyzers" Version="4.14.0"/>
<PackageVersion Include="Roslynator.Formatting.Analyzers" Version="4.14.0"/>
<PackageVersion Include="SharpCompress" Version="0.40.0"/>
<PackageVersion Include="SkiaSharp" Version="2.88.8"/>
<PackageVersion Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31"/>
<PackageVersion Include="Spectre.Console.Analyzer" Version="1.0.0"/>
<PackageVersion Include="Spectre.Console" Version="0.50.0"/>
<PackageVersion Include="System.Collections" Version="4.3.0"/>
<PackageVersion Include="System.CommandLine.NamingConventionBinder" Version="2.0.0-beta4.22272.1"/>
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1"/>
<PackageVersion Include="System.ComponentModel.Annotations" Version="6.0.0-preview.4.21253.7"/>
<PackageVersion Include="System.Diagnostics.Debug" Version="4.3.0"/>
<PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0"/>
<PackageVersion Include="System.IO.FileSystem" Version="4.3.0"/>
<PackageVersion Include="System.Management" Version="10.0.0-preview.7.25380.108"/>
<PackageVersion Include="System.Memory" Version="4.6.3"/>
<PackageVersion Include="System.Net.Primitives" Version="4.3.1"/>
<PackageVersion Include="System.Resources.Extensions" Version="10.0.0-preview.7.25380.108"/>
<PackageVersion Include="System.Runtime.Extensions" Version="4.3.1"/>
<PackageVersion Include="System.Runtime.Handles" Version="4.3.0"/>
<PackageVersion Include="System.Runtime.InteropServices" Version="4.3.0"/>
<PackageVersion Include="System.Security.Principal.Windows" Version="6.0.0-preview.5.21301.5"/>
<PackageVersion Include="System.Text.Encoding.CodePages" Version="10.0.0-preview.7.25380.108"/>
<PackageVersion Include="System.Text.Json" Version="10.0.0-preview.7.25380.108"/>
<PackageVersion Include="System.ValueTuple" Version="4.6.1"/>
<PackageVersion Include="Text.Analyzers" Version="4.14.0"/>
<PackageVersion Include="Unclassified.NetRevisionTask" Version="0.4.4"/>
<PackageVersion Update="Packaging.Targets" Version="0.1.226"/>
<PackageVersion Include="Aaru.Checksums.Native" Version="6.0.0-alpha9" />
<PackageVersion Include="Aaru.Compression.Native" Version="6.0.0-alpha.10" />
<PackageVersion Include="AsyncFixer" Version="1.6.0" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.3" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.3" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.3" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.3" />
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.3.3" />
<PackageVersion Include="Avalonia" Version="11.3.3" />
<PackageVersion Include="Claunia.Encoding" Version="1.9.2" />
<PackageVersion Include="Claunia.RsrcFork" Version="1.2.0" />
<PackageVersion Include="DotNetZip" Version="1.16.0" />
<PackageVersion Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2" />
<PackageVersion Include="ErrorProne.NET.Structs" Version="0.1.2" />
<PackageVersion Include="FluentAssertions.Analyzers" Version="0.34.1" />
<PackageVersion Include="FluentAssertions" Version="8.6.0" />
<PackageVersion Include="Humanizer.Core" Version="2.14.1" />
<PackageVersion Include="Humanizer" Version="2.14.1" />
<PackageVersion Include="JetBrains.Annotations" Version="2025.2.0" />
<PackageVersion Include="Macross.Json.Extensions" Version="3.0.0" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0-preview.7.25380.108" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="10.0.0-preview.7.25380.108" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0-preview.7.25380.108" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0-preview.7.25380.108" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-preview.7.25380.108" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageVersion Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.15" />
<PackageVersion Include="Microsoft.Win32.Registry" Version="6.0.0-preview.5.21301.5" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="NUnit.Analyzers" Version="4.10.0" />
<PackageVersion Include="nunit" Version="4.4.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="5.1.0" />
<PackageVersion Include="Philips.CodeAnalysis.MaintainabilityAnalyzers" Version="1.8.0" />
<PackageVersion Include="plist-cil" Version="2.3.1" />
<PackageVersion Include="Roslynator.Analyzers" Version="4.14.0" />
<PackageVersion Include="Roslynator.CodeAnalysis.Analyzers" Version="4.14.0" />
<PackageVersion Include="Roslynator.Formatting.Analyzers" Version="4.14.0" />
<PackageVersion Include="SharpCompress" Version="0.40.0" />
<PackageVersion Include="SkiaSharp" Version="2.88.8" />
<PackageVersion Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
<PackageVersion Include="Spectre.Console.Analyzer" Version="1.0.0" />
<PackageVersion Include="Spectre.Console" Version="0.50.0" />
<PackageVersion Include="Spectre.Console.Cli" Version="0.50.0" />
<PackageVersion Include="System.Collections" Version="4.3.0" />
<PackageVersion Include="System.ComponentModel.Annotations" Version="6.0.0-preview.4.21253.7" />
<PackageVersion Include="System.Diagnostics.Debug" Version="4.3.0" />
<PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageVersion Include="System.IO.FileSystem" Version="4.3.0" />
<PackageVersion Include="System.Management" Version="10.0.0-preview.7.25380.108" />
<PackageVersion Include="System.Memory" Version="4.6.3" />
<PackageVersion Include="System.Net.Primitives" Version="4.3.1" />
<PackageVersion Include="System.Resources.Extensions" Version="10.0.0-preview.7.25380.108" />
<PackageVersion Include="System.Runtime.Extensions" Version="4.3.1" />
<PackageVersion Include="System.Runtime.Handles" Version="4.3.0" />
<PackageVersion Include="System.Runtime.InteropServices" Version="4.3.0" />
<PackageVersion Include="System.Security.Principal.Windows" Version="6.0.0-preview.5.21301.5" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="10.0.0-preview.7.25380.108" />
<PackageVersion Include="System.Text.Json" Version="10.0.0-preview.7.25380.108" />
<PackageVersion Include="System.ValueTuple" Version="4.6.1" />
<PackageVersion Include="Text.Analyzers" Version="4.14.0" />
<PackageVersion Include="Unclassified.NetRevisionTask" Version="0.4.4" />
<PackageVersion Update="Packaging.Targets" Version="0.1.226" />
</ItemGroup>
</Project>