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"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Aaru" type="DotNetProject" factoryName=".NET Project"> <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="EXE_PATH" value="$PROJECT_DIR$/Aaru/bin/Debug/net10.0/aaru" />
<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="PROGRAM_PARAMETERS" value="archive extract --help" />
<option name="WORKING_DIRECTORY" value="F:/" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Aaru/bin/Debug/net10.0" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <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="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" /> <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_PATH" value="$PROJECT_DIR$/Aaru/Aaru.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" /> <option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_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_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net9.0" /> <option name="PROJECT_TFM" value="net10.0" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" />
</method> </method>

File diff suppressed because it is too large Load Diff

View File

@@ -60,9 +60,6 @@
</data> </data>
<data name="Automation_Device_Serial_Number" xml:space="preserve"> <data name="Automation_Device_Serial_Number" xml:space="preserve">
<value>Número de serie del dispositivo de automatización</value> <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>
<data name="Base_Mechanical_Serial_Number" xml:space="preserve"> <data name="Base_Mechanical_Serial_Number" xml:space="preserve">
<value>Número de serie de la mecánica base</value> <value>Número de serie de la mecánica base</value>
@@ -621,9 +618,6 @@
</data> </data>
<data name="Decoder_for_sector_tag_type_0_not_yet_implemented_sorry" xml:space="preserve"> <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> <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>
<data name="Decode_sector_tags" xml:space="preserve"> <data name="Decode_sector_tags" xml:space="preserve">
<value>Descodificar etiquetas de sector.</value> <value>Descodificar etiquetas de sector.</value>
@@ -1555,9 +1549,6 @@
</data> </data>
<data name="Media_image_format_statistics" xml:space="preserve"> <data name="Media_image_format_statistics" xml:space="preserve">
<value>Estadísticas de formatos de imagen de medio</value> <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>
<data name="Media_Info_Command_Description" xml:space="preserve"> <data name="Media_Info_Command_Description" xml:space="preserve">
<value>Obtiene información sobre el medio introducido en un dispositivo.</value> <value>Obtiene información sobre el medio introducido en un dispositivo.</value>
@@ -1630,9 +1621,6 @@
</data> </data>
<data name="Migrating_local_database" xml:space="preserve"> <data name="Migrating_local_database" xml:space="preserve">
<value>Migrando base de datos local...</value> <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>
<data name="Model_of_media_by_image" xml:space="preserve"> <data name="Model_of_media_by_image" xml:space="preserve">
<value>Modelo del medio representado en la imagen.</value> <value>Modelo del medio representado en la imagen.</value>
@@ -2104,9 +2092,6 @@
</data> </data>
<data name="Tape_argument_input_help" xml:space="preserve"> <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> <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>
<data name="Text_Authors" xml:space="preserve"> <data name="Text_Authors" xml:space="preserve">
<value>Desarrolladores: <value>Desarrolladores:

View File

@@ -505,9 +505,6 @@ In you are unsure, please press N to not continue.</value>
</data> </data>
<data name="Searches_and_interprets_partitions" xml:space="preserve"> <data name="Searches_and_interprets_partitions" xml:space="preserve">
<value>Searches and interprets partitions.</value> <value>Searches and interprets partitions.</value>
</data>
<data name="Media_image_path" xml:space="preserve">
<value>Media image path</value>
</data> </data>
<data name="Image_format_not_identified_not_proceeding_with_analysis" xml:space="preserve"> <data name="Image_format_not_identified_not_proceeding_with_analysis" xml:space="preserve">
<value>Image format not identified, not proceeding with analysis.</value> <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>
<data name="Manufacturer_of_drive_read_the_media_by_image" xml:space="preserve"> <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> <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>
<data name="Firmware_revision_of_drive_read_the_media_by_image" xml:space="preserve"> <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> <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>
<data name="Format_of_the_output_image_as_plugin_name_or_plugin_id" xml:space="preserve"> <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> <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>
<data name="Last_media_of_sequence_by_image" xml:space="preserve"> <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> <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>
<data name="Conversion_done" xml:space="preserve"> <data name="Conversion_done" xml:space="preserve">
<value>Conversion done.</value> <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>
<data name="Tape_argument_input_help" xml:space="preserve"> <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> <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>
<data name="The_specified_input_file_cannot_be_found" xml:space="preserve"> <data name="The_specified_input_file_cannot_be_found" xml:space="preserve">
<value>The specified input file cannot be found.</value> <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>
<data name="Parameter_response_all_sectors" xml:space="preserve"> <data name="Parameter_response_all_sectors" xml:space="preserve">
<value>all</value> <value>all</value>
@@ -3111,4 +3096,7 @@ Do you want to continue?</value>
<data name="Dialog_Aaru_Resume" xml:space="preserve"> <data name="Dialog_Aaru_Resume" xml:space="preserve">
<value>Aaru resume file</value> <value>Aaru resume file</value>
</data> </data>
<data name="Archive_List_Command_Description" xml:space="preserve">
<value>List the contents of an archive file.</value>
</data>
</root> </root>

View File

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

View File

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

View File

@@ -32,8 +32,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@@ -46,45 +45,20 @@ using Aaru.Core;
using Aaru.Helpers; using Aaru.Helpers;
using Aaru.Localization; using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Archive; namespace Aaru.Commands.Archive;
sealed class ArchiveExtractCommand : Command sealed class ArchiveExtractCommand : Command<ArchiveExtractCommand.Settings>
{ {
const int BUFFER_SIZE = 16777216; const int BUFFER_SIZE = 16777216;
const string MODULE_NAME = "Extract-Files command"; const string MODULE_NAME = "Extract-Files command";
public ArchiveExtractCommand() : base("extract", UI.Archive_Extract_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -102,7 +76,7 @@ sealed class ArchiveExtractCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -115,19 +89,19 @@ sealed class ArchiveExtractCommand : Command
Statistics.AddCommand("archive-extract"); Statistics.AddCommand("archive-extract");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encoding ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(archivePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.Path ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(outputDir ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(settings.OutputDir ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--xattrs={0}", xattrs); AaruConsole.DebugWriteLine(MODULE_NAME, "--xattrs={0}", settings.XAttrs);
IFilter inputFilter = null; IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(archivePath); inputFilter = PluginRegister.Singleton.GetFilter(settings.Path);
}); });
if(inputFilter == null) if(inputFilter == null)
@@ -139,13 +113,13 @@ sealed class ArchiveExtractCommand : Command
Encoding encodingClass = null; Encoding encodingClass = null;
if(encoding != null) if(settings.Encoding != null)
{ {
try 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) catch(ArgumentException)
{ {
@@ -172,7 +146,7 @@ sealed class ArchiveExtractCommand : Command
return (int)ErrorNumber.UnrecognizedFormat; return (int)ErrorNumber.UnrecognizedFormat;
} }
if(verbose) if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Archive_format_identified_by_0_1, archive.Name, archive.Id); AaruConsole.VerboseWriteLine(UI.Archive_format_identified_by_0_1, archive.Name, archive.Id);
else else
AaruConsole.WriteLine(UI.Archive_format_identified_by_0, archive.Name); 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); ErrorNumber errno = archive.GetFilename(i, out string fileName);
@@ -264,7 +238,7 @@ sealed class ArchiveExtractCommand : Command
// Prevent absolute path attack // Prevent absolute path attack
fileName = fileName.TrimStart('\\').TrimStart('/'); fileName = fileName.TrimStart('\\').TrimStart('/');
string outputPath = Path.Combine(outputDir, fileName); string outputPath = Path.Combine(settings.OutputDir, fileName);
string destinationDir = Path.GetDirectoryName(outputPath); string destinationDir = Path.GetDirectoryName(outputPath);
if(File.Exists(destinationDir)) if(File.Exists(destinationDir))
@@ -284,7 +258,7 @@ sealed class ArchiveExtractCommand : Command
.Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()) .Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn())
.Start(ctx => .Start(ctx =>
{ {
var position = 0; int position = 0;
var outputFile = var outputFile =
new FileStream(outputPath, new FileStream(outputPath,
@@ -296,7 +270,7 @@ sealed class ArchiveExtractCommand : Command
ctx.AddTask(string.Format(UI.Reading_file_0, Markup.Escape(fileName))); ctx.AddTask(string.Format(UI.Reading_file_0, Markup.Escape(fileName)));
task.MaxValue = uncompressedSize; task.MaxValue = uncompressedSize;
var outBuf = new byte[BUFFER_SIZE]; byte[] outBuf = new byte[BUFFER_SIZE];
Stream inputFile = filter.GetDataForkStream(); Stream inputFile = filter.GetDataForkStream();
while(position < stat.Length) while(position < stat.Length)
@@ -357,7 +331,7 @@ sealed class ArchiveExtractCommand : Command
else else
AaruConsole.ErrorWriteLine(UI.Cannot_write_file_0_output_exists, Markup.Escape(fileName)); 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); errno = archive.ListXAttr(i, out List<string> xattrNames);
@@ -389,7 +363,7 @@ sealed class ArchiveExtractCommand : Command
continue; continue;
} }
outputPath = Path.Combine(outputDir, ".xattrs", xattrName, fileName); outputPath = Path.Combine(settings.OutputDir, ".xattrs", xattrName, fileName);
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
@@ -469,4 +443,29 @@ sealed class ArchiveExtractCommand : Command
return (int)ErrorNumber.NoError; 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;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.Text; using System.Text;
using Aaru.CommonTypes; using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
@@ -42,33 +41,19 @@ using Aaru.Console;
using Aaru.Core; using Aaru.Core;
using Aaru.Localization; using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Archive; namespace Aaru.Commands.Archive;
sealed class ArchiveInfoCommand : Command sealed class ArchiveInfoCommand : Command<ArchiveInfoCommand.Settings>
{ {
const string MODULE_NAME = "Analyze command"; const string MODULE_NAME = "Analyze command";
public ArchiveInfoCommand() : base("info", UI.Archive_Info_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -86,7 +71,7 @@ sealed class ArchiveInfoCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -99,17 +84,17 @@ sealed class ArchiveInfoCommand : Command
Statistics.AddCommand("archive-info"); Statistics.AddCommand("archive-info");
AaruConsole.DebugWriteLine(MODULE_NAME, "debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "input={0}", Markup.Escape(archivePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "input={0}", Markup.Escape(settings.Path ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "encoding={0}", Markup.Escape(encoding ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "encoding={0}", Markup.Escape(settings.Encoding ?? ""));
IFilter inputFilter = null; IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(archivePath); inputFilter = PluginRegister.Singleton.GetFilter(settings.Path);
}); });
if(inputFilter == null) if(inputFilter == null)
@@ -121,13 +106,13 @@ sealed class ArchiveInfoCommand : Command
Encoding encodingClass = null; Encoding encodingClass = null;
if(encoding != null) if(settings.Encoding != null)
{ {
try 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) catch(ArgumentException)
{ {
@@ -188,4 +173,20 @@ sealed class ArchiveInfoCommand : Command
return (int)ErrorNumber.NoError; 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;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.Text; using System.Text;
using Aaru.CommonTypes; using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
@@ -42,36 +41,19 @@ using Aaru.Console;
using Aaru.Core; using Aaru.Core;
using Aaru.Localization; using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Archive; namespace Aaru.Commands.Archive;
sealed class ArchiveListCommand : Command sealed class ArchiveListCommand : Command<ArchiveListCommand.Settings>
{ {
const string MODULE_NAME = "Archive list command"; const string MODULE_NAME = "Archive list command";
public ArchiveListCommand() : base("list", "Lists files contained in an archive.") public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -89,7 +71,7 @@ sealed class ArchiveListCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -100,11 +82,11 @@ sealed class ArchiveListCommand : Command
}; };
} }
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encoding ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--long-format={0}", longFormat); AaruConsole.DebugWriteLine(MODULE_NAME, "--long-format={0}", settings.LongFormat);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(archivePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.Path ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
Statistics.AddCommand("archive-list"); Statistics.AddCommand("archive-list");
IFilter inputFilter = null; IFilter inputFilter = null;
@@ -112,7 +94,7 @@ sealed class ArchiveListCommand : Command
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(archivePath); inputFilter = PluginRegister.Singleton.GetFilter(settings.Path);
}); });
if(inputFilter == null) if(inputFilter == null)
@@ -124,13 +106,13 @@ sealed class ArchiveListCommand : Command
Encoding encodingClass = null; Encoding encodingClass = null;
if(encoding != null) if(settings.Encoding != null)
{ {
try 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) catch(ArgumentException)
{ {
@@ -159,7 +141,7 @@ sealed class ArchiveListCommand : Command
return (int)ErrorNumber.UnrecognizedFormat; return (int)ErrorNumber.UnrecognizedFormat;
} }
if(verbose) if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Archive_format_identified_by_0_1, archive.Name, archive.Id); AaruConsole.VerboseWriteLine(UI.Archive_format_identified_by_0_1, archive.Name, archive.Id);
else else
AaruConsole.WriteLine(UI.Archive_format_identified_by_0, archive.Name); AaruConsole.WriteLine(UI.Archive_format_identified_by_0, archive.Name);
@@ -197,9 +179,9 @@ sealed class ArchiveListCommand : Command
return (int)ErrorNumber.CannotOpenFormat; 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); ErrorNumber errno = archive.GetFilename(i, out string fileName);
@@ -218,8 +200,8 @@ sealed class ArchiveListCommand : Command
} }
var table = new Table(); var table = new Table();
var files = 0; int files = 0;
var folders = 0; int folders = 0;
long totalSize = 0; long totalSize = 0;
long totalUncompressed = 0; long totalUncompressed = 0;
@@ -278,7 +260,7 @@ sealed class ArchiveListCommand : Command
ctx.Refresh(); 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); ErrorNumber errno = archive.GetFilename(i, out string fileName);
@@ -302,7 +284,7 @@ sealed class ArchiveListCommand : Command
continue; continue;
} }
var attr = new char[5]; char[] attr = new char[5];
if(stat.Attributes.HasFlag(FileAttributes.Directory)) if(stat.Attributes.HasFlag(FileAttributes.Directory))
{ {
@@ -422,4 +404,25 @@ sealed class ArchiveListCommand : Command
return (int)ErrorNumber.NoError; 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 // Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
using Aaru.Console; using Aaru.Console;
using Aaru.Localization; using Aaru.Localization;
using Aaru.Settings; using Aaru.Settings;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands; namespace Aaru.Commands;
sealed class ConfigureCommand : Command sealed class ConfigureCommand : Command<ConfigureCommand.Settings>
{ {
public ConfigureCommand() : base("configure", UI.Configure_Command_Description) => public override int Execute(CommandContext context, Settings settings)
Handler = CommandHandler.Create((Func<bool, bool, int>)Invoke);
int Invoke(bool debug, bool verbose)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -68,7 +63,7 @@ sealed class ConfigureCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -101,7 +96,7 @@ sealed class ConfigureCommand : Command
AaruConsole.WriteLine(UI.Configure_enable_decryption_disclaimer); 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}[/]"); AnsiConsole.Confirm($"[italic]{UI.Do_you_want_to_enable_decryption_of_copy_protected_media_Q}[/]");
#region Device reports #region Device reports
@@ -110,14 +105,14 @@ sealed class ConfigureCommand : Command
AaruConsole.WriteLine(UI.Configure_Device_Report_information_disclaimer); 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}[/]"); Configure_Do_you_want_to_save_device_reports_in_shared_folder_of_your_computer_Q}[/]");
AaruConsole.WriteLine(); AaruConsole.WriteLine();
AaruConsole.WriteLine(UI.Configure_share_report_disclaimer); 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}[/]"); AnsiConsole.Confirm($"[italic]{UI.Do_you_want_to_share_your_device_reports_with_us_Q}[/]");
#endregion Device reports #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}[/]")) 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}[/]"), ShareStats = AnsiConsole.Confirm($"[italic]{UI.Do_you_want_to_share_your_stats__anonymously_Q}[/]"),
CommandStats = CommandStats =
@@ -156,13 +151,19 @@ sealed class ConfigureCommand : Command
}; };
} }
else else
Settings.Settings.Current.Stats = null; Aaru.Settings.Settings.Current.Stats = null;
#endregion Statistics #endregion Statistics
Settings.Settings.Current.GdprCompliance = DicSettings.GDPR_LEVEL; Aaru.Settings.Settings.Current.GdprCompliance = DicSettings.GDPR_LEVEL;
Settings.Settings.SaveSettings(); Aaru.Settings.Settings.SaveSettings();
return (int)ErrorNumber.NoError; return (int)ErrorNumber.NoError;
} }
#region Nested type: Settings
public class Settings : BaseSettings {}
#endregion
} }

View File

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

View File

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

View File

@@ -31,8 +31,7 @@
// ****************************************************************************/ // ****************************************************************************/
using System; using System;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -43,31 +42,20 @@ using Aaru.Database;
using Aaru.Localization; using Aaru.Localization;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Database; namespace Aaru.Commands.Database;
sealed class UpdateCommand : Command sealed class UpdateCommand : AsyncCommand<UpdateCommand.Settings>
{ {
const string MODULE_NAME = "Update command"; const string MODULE_NAME = "Update command";
readonly bool _mainDbUpdate; 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(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -85,7 +73,7 @@ sealed class UpdateCommand : Command
AaruConsole.WriteExceptionEvent += ex => stderrConsole.WriteException(ex); AaruConsole.WriteExceptionEvent += ex => stderrConsole.WriteException(ex);
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -96,16 +84,16 @@ sealed class UpdateCommand : Command
}; };
} }
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
if(clearAll) if(settings.ClearAll)
{ {
try 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.Database.MigrateAsync();
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }
@@ -117,11 +105,11 @@ sealed class UpdateCommand : Command
} }
} }
if(clear || clearAll) if(settings.Clear || settings.ClearAll)
{ {
try try
{ {
File.Delete(Settings.Settings.MainDbPath); File.Delete(Aaru.Settings.Settings.MainDbPath);
} }
catch(Exception) when(!Debugger.IsAttached) 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; return (int)ErrorNumber.NoError;
} }
@@ -141,4 +129,20 @@ sealed class UpdateCommand : Command
await Remote.UpdateMainDatabaseAsync(create); await Remote.UpdateMainDatabaseAsync(create);
Statistics.AddCommand("update"); 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 // Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System.CommandLine;
using Aaru.Localization;
namespace Aaru.Commands.Device; namespace Aaru.Commands.Device;
sealed class DeviceFamily : Command class DeviceFamily : BaseSettings {}
{
public DeviceFamily() : base("device", UI.Device_Command_Family_Description)
{
AddAlias("dev");
AddCommand(new DeviceInfoCommand());
AddCommand(new DeviceReportCommand());
AddCommand(new ListDevicesCommand());
}
}

View File

@@ -32,8 +32,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
@@ -52,34 +51,20 @@ using Aaru.Decoders.SCSI.MMC;
using Aaru.Helpers; using Aaru.Helpers;
using Aaru.Localization; using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Command = System.CommandLine.Command; using Spectre.Console.Cli;
using Profile = Aaru.Decoders.SCSI.MMC.Profile; using Profile = Aaru.Decoders.SCSI.MMC.Profile;
namespace Aaru.Commands.Device; namespace Aaru.Commands.Device;
sealed class DeviceReportCommand : Command sealed class DeviceReportCommand : AsyncCommand<DeviceReportCommand.Settings>
{ {
const string MODULE_NAME = "Device-Report command"; const string MODULE_NAME = "Device-Report command";
public DeviceReportCommand() : base("report", UI.Device_Report_Command_Description) public override async Task<int> ExecuteAsync(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -97,7 +82,7 @@ sealed class DeviceReportCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -110,9 +95,11 @@ sealed class DeviceReportCommand : Command
Statistics.AddCommand("device-report"); Statistics.AddCommand("device-report");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(devicePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(settings.Path ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
string devicePath = settings.Path;
if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0])) if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0]))
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':'; devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
@@ -163,7 +150,7 @@ sealed class DeviceReportCommand : Command
Type = dev.Type Type = dev.Type
}; };
var removable = false; bool removable = false;
string jsonFile; string jsonFile;
switch(string.IsNullOrWhiteSpace(dev.Manufacturer)) switch(string.IsNullOrWhiteSpace(dev.Manufacturer))
@@ -189,7 +176,7 @@ sealed class DeviceReportCommand : Command
jsonFile = jsonFile.Replace('\\', '_').Replace('/', '_').Replace('?', '_'); 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); AaruConsole.ErrorWriteLine(UI.Device_does_not_report_with_trap_discs);
@@ -400,7 +387,7 @@ sealed class DeviceReportCommand : Command
string mediumManufacturer; string mediumManufacturer;
byte[] senseBuffer = []; byte[] senseBuffer = [];
var sense = true; bool sense = true;
switch(dev.ScsiType) switch(dev.ScsiType)
{ {
@@ -416,7 +403,7 @@ sealed class DeviceReportCommand : Command
dev.Manufacturer.Equals("iomega", StringComparison.InvariantCultureIgnoreCase) && dev.Manufacturer.Equals("iomega", StringComparison.InvariantCultureIgnoreCase) &&
dev.Model.StartsWith("rrd", StringComparison.InvariantCultureIgnoreCase); dev.Model.StartsWith("rrd", StringComparison.InvariantCultureIgnoreCase);
if(trapDisc) if(settings.TrapDisc)
{ {
if(iomegaRev) if(iomegaRev)
{ {
@@ -699,7 +686,8 @@ sealed class DeviceReportCommand : Command
tryNec |= dev.Manufacturer.Equals("nec", StringComparison.InvariantCultureIgnoreCase); 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) if(!iomegaRev)
{ {
@@ -755,7 +743,7 @@ sealed class DeviceReportCommand : Command
System.Console.ReadKey(true); System.Console.ReadKey(true);
var mediaIsRecognized = true; bool mediaIsRecognized = true;
await AnsiConsole.Status() await AnsiConsole.Status()
.StartAsync(Localization.Core.Waiting_for_drive_to_become_ready, .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 0x04 when decSense.Value.ASCQ == 0x01:
case 0x28: case 0x28:
{ {
var leftRetries = 50; int leftRetries = 50;
while(leftRetries > 0) while(leftRetries > 0)
{ {
@@ -857,7 +845,7 @@ sealed class DeviceReportCommand : Command
task.MaxValue = ushort.MaxValue; task.MaxValue = ushort.MaxValue;
for(var i = (ushort)(mediaTest.BlockSize ?? 0);; i++) for(ushort i = (ushort)(mediaTest.BlockSize ?? 0);; i++)
{ {
task.Description = task.Description =
string.Format(Localization.Core string.Format(Localization.Core
@@ -969,7 +957,7 @@ sealed class DeviceReportCommand : Command
mediumModel = AnsiConsole.Ask<string>(Localization.Core.Please_write_media_model); mediumModel = AnsiConsole.Ask<string>(Localization.Core.Please_write_media_model);
var mediaIsRecognized = true; bool mediaIsRecognized = true;
await AnsiConsole.Status() await AnsiConsole.Status()
.StartAsync(Localization.Core.Waiting_for_drive_to_become_ready, .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 0x04 when decSense.Value.ASCQ == 0x01:
case 0x28: case 0x28:
{ {
var leftRetries = 50; int leftRetries = 50;
while(leftRetries > 0) while(leftRetries > 0)
{ {
@@ -1088,7 +1076,7 @@ sealed class DeviceReportCommand : Command
System.Console.ReadKey(true); System.Console.ReadKey(true);
var mediaIsRecognized = true; bool mediaIsRecognized = true;
await AnsiConsole.Status() await AnsiConsole.Status()
.StartAsync(Localization.Core.Waiting_for_drive_to_become_ready, .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 0x04 when decSense.Value.ASCQ == 0x01:
case 0x28: case 0x28:
{ {
var leftRetries = 50; int leftRetries = 50;
while(leftRetries > 0) while(leftRetries > 0)
{ {
@@ -1178,7 +1166,7 @@ sealed class DeviceReportCommand : Command
task.MaxValue = ushort.MaxValue; task.MaxValue = ushort.MaxValue;
for(var i = (ushort)(mediaTest.BlockSize ?? 0);; i++) for(ushort i = (ushort)(mediaTest.BlockSize ?? 0);; i++)
{ {
task.Value = i; task.Value = i;
@@ -1302,7 +1290,7 @@ sealed class DeviceReportCommand : Command
mediumModel = AnsiConsole.Ask<string>(Localization.Core.Please_write_media_model); mediumModel = AnsiConsole.Ask<string>(Localization.Core.Please_write_media_model);
var mediaIsRecognized = true; bool mediaIsRecognized = true;
await AnsiConsole.Status() await AnsiConsole.Status()
.StartAsync(Localization.Core.Waiting_for_drive_to_become_ready, .StartAsync(Localization.Core.Waiting_for_drive_to_become_ready,
@@ -1323,7 +1311,7 @@ sealed class DeviceReportCommand : Command
case 0x3A: case 0x3A:
case 0x04 when decSense.Value.ASCQ == 0x01: case 0x04 when decSense.Value.ASCQ == 0x01:
{ {
var leftRetries = 20; int leftRetries = 20;
while(leftRetries > 0) while(leftRetries > 0)
{ {
@@ -1379,7 +1367,7 @@ sealed class DeviceReportCommand : Command
task.MaxValue = ushort.MaxValue; task.MaxValue = ushort.MaxValue;
for(var i = (ushort)(mediaTest.BlockSize ?? 0);; i++) for(ushort i = (ushort)(mediaTest.BlockSize ?? 0);; i++)
{ {
task.Value = i; task.Value = i;
@@ -1496,7 +1484,8 @@ sealed class DeviceReportCommand : Command
task.MaxValue = ushort.MaxValue; 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; task.Value = i;
@@ -1592,15 +1581,30 @@ sealed class DeviceReportCommand : Command
jsonFs.Close(); 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)); ctx.Reports.Add(new Report(report));
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }
// TODO: // TODO:
if(Settings.Settings.Current.ShareReports) await Remote.SubmitReportAsync(report); if(Aaru.Settings.Settings.Current.ShareReports) await Remote.SubmitReportAsync(report);
return (int)ErrorNumber.NoError; 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.Linq; using System.Linq;
using Aaru.CommonTypes; using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
@@ -55,36 +54,23 @@ using Aaru.Localization;
using Humanizer; using Humanizer;
using Humanizer.Localisation; using Humanizer.Localisation;
using Spectre.Console; using Spectre.Console;
using Command = System.CommandLine.Command; using Spectre.Console.Cli;
using DeviceInfo = Aaru.Core.Devices.Info.DeviceInfo; using DeviceInfo = Aaru.Core.Devices.Info.DeviceInfo;
using Inquiry = Aaru.Decoders.SCSI.Inquiry; using Inquiry = Aaru.Decoders.SCSI.Inquiry;
using Tuple = Aaru.Decoders.PCMCIA.Tuple; using Tuple = Aaru.Decoders.PCMCIA.Tuple;
namespace Aaru.Commands.Device; 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"; const string MODULE_NAME = "Device-Info command";
public DeviceInfoCommand() : base("info", UI.Device_Info_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -102,7 +88,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -115,10 +101,12 @@ sealed class DeviceInfoCommand : Command
Statistics.AddCommand("device-info"); Statistics.AddCommand("device-info");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(devicePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(settings.Path ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output-prefix={0}", Markup.Escape(outputPrefix ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--output-prefix={0}", Markup.Escape(settings.OutputPrefix ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
string devicePath = settings.Path;
if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0])) if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0]))
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':'; devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
@@ -282,7 +270,11 @@ sealed class DeviceInfoCommand : Command
if(devInfo.AtaIdentify != null) 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); Identify.IdentifyDevice? decodedIdentify = Identify.Decode(devInfo.AtaIdentify);
AaruConsole.WriteLine(Decoders.ATA.Identify.Prettify(decodedIdentify)); AaruConsole.WriteLine(Decoders.ATA.Identify.Prettify(decodedIdentify));
@@ -323,8 +315,8 @@ sealed class DeviceInfoCommand : Command
if((devInfo.AtaMcptError.Value.DeviceHead & 0x08) == 0x08) if((devInfo.AtaMcptError.Value.DeviceHead & 0x08) == 0x08)
AaruConsole.WriteLine(Localization.Core.Media_card_is_write_protected); AaruConsole.WriteLine(Localization.Core.Media_card_is_write_protected);
var specificData = (ushort)(devInfo.AtaMcptError.Value.CylinderHigh * 0x100 + ushort specificData = (ushort)(devInfo.AtaMcptError.Value.CylinderHigh * 0x100 +
devInfo.AtaMcptError.Value.CylinderLow); devInfo.AtaMcptError.Value.CylinderLow);
if(specificData != 0) AaruConsole.WriteLine(Localization.Core.Card_specific_data_0, specificData); if(specificData != 0) AaruConsole.WriteLine(Localization.Core.Card_specific_data_0, specificData);
} }
@@ -368,7 +360,11 @@ sealed class DeviceInfoCommand : Command
if(devInfo.AtapiIdentify != null) 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)); 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}[/]"); if(dev.Type != DeviceType.ATAPI) AaruConsole.WriteLine($"[bold]{UI.Title_SCSI_device}[/]");
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
"_scsi_inquiry.bin", "_scsi_inquiry.bin",
UI.Title_SCSI_INQUIRY, UI.Title_SCSI_INQUIRY,
devInfo.ScsiInquiryData); devInfo.ScsiInquiryData);
@@ -396,7 +392,7 @@ sealed class DeviceInfoCommand : Command
page.Key, page.Key,
EVPD.DecodeASCIIPage(page.Value)); EVPD.DecodeASCIIPage(page.Value));
DataFile.WriteTo(MODULE_NAME, outputPrefix, page.Value); DataFile.WriteTo(MODULE_NAME, settings.OutputPrefix, page.Value);
break; break;
case 0x80: case 0x80:
@@ -404,7 +400,7 @@ sealed class DeviceInfoCommand : Command
EVPD.DecodePage80(page.Value)); EVPD.DecodePage80(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -414,7 +410,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_81(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_81(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -425,7 +421,7 @@ sealed class DeviceInfoCommand : Command
EVPD.DecodePage82(page.Value)); EVPD.DecodePage82(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -435,7 +431,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_83(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_83(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -445,7 +441,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_84(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_84(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -455,7 +451,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_85(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_85(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -465,7 +461,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_86(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_86(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -475,7 +471,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_89(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_89(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -485,7 +481,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_B0(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_B0(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -496,7 +492,7 @@ sealed class DeviceInfoCommand : Command
EVPD.DecodePageB1(page.Value)); EVPD.DecodePageB1(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -507,7 +503,7 @@ sealed class DeviceInfoCommand : Command
EVPD.DecodePageB2(page.Value)); EVPD.DecodePageB2(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -518,7 +514,7 @@ sealed class DeviceInfoCommand : Command
EVPD.DecodePageB3(page.Value)); EVPD.DecodePageB3(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -529,7 +525,7 @@ sealed class DeviceInfoCommand : Command
EVPD.DecodePageB4(page.Value)); EVPD.DecodePageB4(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -542,7 +538,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_Quantum(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_Quantum(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -555,7 +551,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_Seagate(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_Seagate(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -568,7 +564,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_IBM(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_IBM(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -581,7 +577,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C1_IBM(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C1_IBM(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -595,7 +591,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_C1_Certance(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_C1_Certance(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -609,7 +605,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C2_C3_C4_C5_C6_Certance(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C2_C3_C4_C5_C6_Certance(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -623,7 +619,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_to_C5_HP(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_C0_to_C5_HP(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -636,7 +632,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_DF_Certance(page.Value)); AaruConsole.WriteLine("{0}", EVPD.PrettifyPage_DF_Certance(page.Value));
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -651,7 +647,7 @@ sealed class DeviceInfoCommand : Command
page.Key); page.Key);
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
$"_scsi_evpd_{page.Key:X2}h.bin", $"_scsi_evpd_{page.Key:X2}h.bin",
$"SCSI INQUIRY EVPD {page.Key:X2}h", $"SCSI INQUIRY EVPD {page.Key:X2}h",
page.Value); page.Value);
@@ -665,7 +661,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.ScsiModeSense6 != null) if(devInfo.ScsiModeSense6 != null)
{ {
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
"_scsi_modesense6.bin", "_scsi_modesense6.bin",
"SCSI MODE SENSE", "SCSI MODE SENSE",
devInfo.ScsiModeSense6); devInfo.ScsiModeSense6);
@@ -674,7 +670,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.ScsiModeSense10 != null) if(devInfo.ScsiModeSense10 != null)
{ {
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
"_scsi_modesense10.bin", "_scsi_modesense10.bin",
"SCSI MODE SENSE", "SCSI MODE SENSE",
devInfo.ScsiModeSense10); devInfo.ScsiModeSense10);
@@ -690,7 +686,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.MmcConfiguration != null) if(devInfo.MmcConfiguration != null)
{ {
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
"_mmc_getconfiguration.bin", "_mmc_getconfiguration.bin",
"MMC GET CONFIGURATION", "MMC GET CONFIGURATION",
devInfo.MmcConfiguration); devInfo.MmcConfiguration);
@@ -966,7 +962,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.PlextorFeatures?.Eeprom != null) if(devInfo.PlextorFeatures?.Eeprom != null)
{ {
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
"_plextor_eeprom.bin", "_plextor_eeprom.bin",
"PLEXTOR READ EEPROM", "PLEXTOR READ EEPROM",
devInfo.PlextorFeatures.Eeprom); devInfo.PlextorFeatures.Eeprom);
@@ -1144,7 +1140,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.BlockLimits != null) if(devInfo.BlockLimits != null)
{ {
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
"_ssc_readblocklimits.bin", "_ssc_readblocklimits.bin",
"SSC READ BLOCK LIMITS", "SSC READ BLOCK LIMITS",
devInfo.BlockLimits); devInfo.BlockLimits);
@@ -1156,7 +1152,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.DensitySupport != null) if(devInfo.DensitySupport != null)
{ {
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
"_ssc_reportdensitysupport.bin", "_ssc_reportdensitysupport.bin",
"SSC REPORT DENSITY SUPPORT", "SSC REPORT DENSITY SUPPORT",
devInfo.DensitySupport); devInfo.DensitySupport);
@@ -1171,7 +1167,7 @@ sealed class DeviceInfoCommand : Command
if(devInfo.MediumDensitySupport != null) if(devInfo.MediumDensitySupport != null)
{ {
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
"_ssc_reportdensitysupport_medium.bin", "_ssc_reportdensitysupport_medium.bin",
"SSC REPORT DENSITY SUPPORT (MEDIUM)", "SSC REPORT DENSITY SUPPORT (MEDIUM)",
devInfo.MediumDensitySupport); devInfo.MediumDensitySupport);
@@ -1190,26 +1186,26 @@ sealed class DeviceInfoCommand : Command
{ {
case DeviceType.MMC: case DeviceType.MMC:
{ {
var noInfo = true; bool noInfo = true;
if(devInfo.CID != null) if(devInfo.CID != null)
{ {
noInfo = false; 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)); AaruConsole.WriteLine("{0}", Decoders.MMC.Decoders.PrettifyCID(devInfo.CID));
} }
if(devInfo.CSD != null) if(devInfo.CSD != null)
{ {
noInfo = false; 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)); AaruConsole.WriteLine("{0}", Decoders.MMC.Decoders.PrettifyCSD(devInfo.CSD));
} }
if(devInfo.OCR != null) if(devInfo.OCR != null)
{ {
noInfo = false; 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)); AaruConsole.WriteLine("{0}", Decoders.MMC.Decoders.PrettifyOCR(devInfo.OCR));
} }
@@ -1218,7 +1214,7 @@ sealed class DeviceInfoCommand : Command
noInfo = false; noInfo = false;
DataFile.WriteTo(MODULE_NAME, DataFile.WriteTo(MODULE_NAME,
outputPrefix, settings.OutputPrefix,
"_mmc_ecsd.bin", "_mmc_ecsd.bin",
"MMC Extended CSD", "MMC Extended CSD",
devInfo.ExtendedCSD); devInfo.ExtendedCSD);
@@ -1232,13 +1228,17 @@ sealed class DeviceInfoCommand : Command
break; break;
case DeviceType.SecureDigital: case DeviceType.SecureDigital:
{ {
var noInfo = true; bool noInfo = true;
if(devInfo.CID != null) if(devInfo.CID != null)
{ {
noInfo = false; 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)); AaruConsole.WriteLine("{0}", Decoders.SecureDigital.Decoders.PrettifyCID(devInfo.CID));
} }
@@ -1247,7 +1247,11 @@ sealed class DeviceInfoCommand : Command
{ {
noInfo = false; 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)); AaruConsole.WriteLine("{0}", Decoders.SecureDigital.Decoders.PrettifyCSD(devInfo.CSD));
} }
@@ -1256,7 +1260,11 @@ sealed class DeviceInfoCommand : Command
{ {
noInfo = false; 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)); AaruConsole.WriteLine("{0}", Decoders.SecureDigital.Decoders.PrettifyOCR(devInfo.OCR));
} }
@@ -1265,7 +1273,11 @@ sealed class DeviceInfoCommand : Command
{ {
noInfo = false; 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)); AaruConsole.WriteLine("{0}", Decoders.SecureDigital.Decoders.PrettifySCR(devInfo.SCR));
} }
@@ -1281,7 +1293,7 @@ sealed class DeviceInfoCommand : Command
AaruConsole.WriteLine(); AaruConsole.WriteLine();
// Open main database // Open main database
var ctx = AaruContext.Create(Settings.Settings.MainDbPath); var ctx = AaruContext.Create(Aaru.Settings.Settings.MainDbPath);
// Search for device in main database // Search for device in main database
Aaru.Database.Models.Device dbDev = Aaru.Database.Models.Device dbDev =
@@ -1313,4 +1325,19 @@ sealed class DeviceInfoCommand : Command
return (int)ErrorNumber.NoError; 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 // Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System; using System.ComponentModel;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.Linq; using System.Linq;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
using Aaru.Console; using Aaru.Console;
@@ -41,30 +39,20 @@ using Aaru.Devices;
using Aaru.Localization; using Aaru.Localization;
using JetBrains.Annotations; using JetBrains.Annotations;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Device; namespace Aaru.Commands.Device;
sealed class ListDevicesCommand : Command sealed class ListDevicesCommand : Command<ListDevicesCommand.Settings>
{ {
const string MODULE_NAME = "List-Devices command"; const string MODULE_NAME = "List-Devices command";
public ListDevicesCommand() : base("list", UI.Device_List_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ZeroOrOne,
Description = UI.aaruremote_host,
Name = "aaru-remote-host"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
}
public static int Invoke(bool debug, bool verbose, [CanBeNull] string aaruRemoteHost)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -82,7 +70,7 @@ sealed class ListDevicesCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -95,8 +83,8 @@ sealed class ListDevicesCommand : Command
Statistics.AddCommand("list-devices"); Statistics.AddCommand("list-devices");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
DeviceInfo[] devices = Devices.Device.ListDevices(out bool isRemote, DeviceInfo[] devices = Devices.Device.ListDevices(out bool isRemote,
out string serverApplication, out string serverApplication,
@@ -104,7 +92,7 @@ sealed class ListDevicesCommand : Command
out string serverOperatingSystem, out string serverOperatingSystem,
out string serverOperatingSystemVersion, out string serverOperatingSystemVersion,
out string serverArchitecture, out string serverArchitecture,
aaruRemoteHost); settings.AaruRemoteHost);
if(isRemote) if(isRemote)
{ {
@@ -142,4 +130,17 @@ sealed class ListDevicesCommand : Command
return (int)ErrorNumber.NoError; 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@@ -46,50 +45,21 @@ using Aaru.Core;
using Aaru.Localization; using Aaru.Localization;
using JetBrains.Annotations; using JetBrains.Annotations;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes; using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes;
namespace Aaru.Commands.Filesystem; namespace Aaru.Commands.Filesystem;
sealed class ExtractFilesCommand : Command sealed class ExtractFilesCommand : Command<ExtractFilesCommand.Settings>
{ {
const long BUFFER_SIZE = 16777216; const long BUFFER_SIZE = 16777216;
const string MODULE_NAME = "Extract-Files command"; const string MODULE_NAME = "Extract-Files command";
public ExtractFilesCommand() : base("extract", UI.Filesystem_Extract_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -107,7 +77,7 @@ sealed class ExtractFilesCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -120,29 +90,29 @@ sealed class ExtractFilesCommand : Command
Statistics.AddCommand("extract-files"); Statistics.AddCommand("extract-files");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encoding ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(options ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(settings.Options ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(outputDir ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(settings.OutputDir ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--xattrs={0}", xattrs); AaruConsole.DebugWriteLine(MODULE_NAME, "--xattrs={0}", settings.Xattrs);
IFilter inputFilter = null; IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); 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); AaruConsole.DebugWriteLine(MODULE_NAME, UI.Parsed_options);
foreach(KeyValuePair<string, string> parsedOption in parsedOptions) foreach(KeyValuePair<string, string> parsedOption in parsedOptions)
AaruConsole.DebugWriteLine(MODULE_NAME, "{0} = {1}", parsedOption.Key, parsedOption.Value); AaruConsole.DebugWriteLine(MODULE_NAME, "{0} = {1}", parsedOption.Key, parsedOption.Value);
parsedOptions.Add("debug", debug.ToString()); parsedOptions.Add("debug", settings.Debug.ToString());
if(inputFilter == null) if(inputFilter == null)
{ {
@@ -153,13 +123,13 @@ sealed class ExtractFilesCommand : Command
Encoding encodingClass = null; Encoding encodingClass = null;
if(encoding != null) if(settings.Encoding != null)
{ {
try 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) catch(ArgumentException)
{ {
@@ -197,26 +167,26 @@ sealed class ExtractFilesCommand : Command
return (int)ErrorNumber.InvalidArgument; return (int)ErrorNumber.InvalidArgument;
} }
if(verbose) if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Image_format_identified_by_0_1, imageFormat.Name, imageFormat.Id); AaruConsole.VerboseWriteLine(UI.Image_format_identified_by_0_1, imageFormat.Name, imageFormat.Id);
else else
AaruConsole.WriteLine(UI.Image_format_identified_by_0, imageFormat.Name); AaruConsole.WriteLine(UI.Image_format_identified_by_0, imageFormat.Name);
if(outputDir == null) if(settings.OutputDir == null)
{ {
AaruConsole.WriteLine(UI.Output_directory_missing); AaruConsole.WriteLine(UI.Output_directory_missing);
return (int)ErrorNumber.MissingArgument; 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); AaruConsole.ErrorWriteLine(UI.Destination_exists_aborting);
return (int)ErrorNumber.FileExists; return (int)ErrorNumber.FileExists;
} }
Directory.CreateDirectory(outputDir); Directory.CreateDirectory(settings.OutputDir);
try try
{ {
@@ -288,7 +258,7 @@ sealed class ExtractFilesCommand : Command
AaruConsole.WriteLine(UI._0_partitions_found, partitions.Count); 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();
AaruConsole.WriteLine($"[bold]{string.Format(UI.Partition_0, partitions[i].Sequence)}[/]"); 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(); 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) if(error == ErrorNumber.NoError)
@@ -335,7 +309,7 @@ sealed class ExtractFilesCommand : Command
? "NO NAME" ? "NO NAME"
: fs.Metadata.VolumeName; : fs.Metadata.VolumeName;
ExtractFilesInDir("/", fs, volumeName, outputDir, xattrs); ExtractFilesInDir("/", fs, volumeName, settings.OutputDir, settings.Xattrs);
Statistics.AddFilesystem(fs.Metadata.Type); Statistics.AddFilesystem(fs.Metadata.Type);
} }
@@ -354,7 +328,12 @@ sealed class ExtractFilesCommand : Command
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Mounting_filesystem).IsIndeterminate(); 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) if(error == ErrorNumber.NoError)
@@ -363,7 +342,7 @@ sealed class ExtractFilesCommand : Command
? "NO NAME" ? "NO NAME"
: fs.Metadata.VolumeName; : fs.Metadata.VolumeName;
ExtractFilesInDir("/", fs, volumeName, outputDir, xattrs); ExtractFilesInDir("/", fs, volumeName, settings.OutputDir, settings.Xattrs);
Statistics.AddFilesystem(fs.Metadata.Type); Statistics.AddFilesystem(fs.Metadata.Type);
} }
@@ -630,7 +609,7 @@ sealed class ExtractFilesCommand : Command
ctx.AddTask(string.Format(UI.Reading_file_0, Markup.Escape(entry))); ctx.AddTask(string.Format(UI.Reading_file_0, Markup.Escape(entry)));
task.MaxValue = stat.Length; task.MaxValue = stat.Length;
var outBuf = new byte[BUFFER_SIZE]; byte[] outBuf = new byte[BUFFER_SIZE];
error = fs.OpenFile(path + "/" + entry, out IFileNode fileNode); error = fs.OpenFile(path + "/" + entry, out IFileNode fileNode);
if(error == ErrorNumber.NoError) if(error == ErrorNumber.NoError)
@@ -710,4 +689,34 @@ sealed class ExtractFilesCommand : Command
fs.CloseDir(node); 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 // Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System.CommandLine;
using Aaru.Localization;
namespace Aaru.Commands.Filesystem; namespace Aaru.Commands.Filesystem;
sealed class FilesystemFamily : Command class FilesystemFamily : BaseSettings {}
{
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());
}
}

View File

@@ -32,8 +32,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.Text; using System.Text;
using Aaru.CommonTypes; using Aaru.CommonTypes;
using Aaru.CommonTypes.AaruMetadata; using Aaru.CommonTypes.AaruMetadata;
@@ -43,40 +42,20 @@ using Aaru.Console;
using Aaru.Core; using Aaru.Core;
using Aaru.Localization; using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
using Partition = Aaru.CommonTypes.Partition; using Partition = Aaru.CommonTypes.Partition;
namespace Aaru.Commands.Filesystem; namespace Aaru.Commands.Filesystem;
sealed class FilesystemInfoCommand : Command sealed class FilesystemInfoCommand : Command<FilesystemInfoCommand.Settings>
{ {
const string MODULE_NAME = "Fs-info command"; const string MODULE_NAME = "Fs-info command";
public FilesystemInfoCommand() : base("info", UI.Filesystem_Info_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -94,7 +73,7 @@ sealed class FilesystemInfoCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -107,19 +86,19 @@ sealed class FilesystemInfoCommand : Command
Statistics.AddCommand("fs-info"); Statistics.AddCommand("fs-info");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encoding ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--filesystems={0}", filesystems); AaruConsole.DebugWriteLine(MODULE_NAME, "--filesystems={0}", settings.Filesystems);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--partitions={0}", partitions); AaruConsole.DebugWriteLine(MODULE_NAME, "--partitions={0}", settings.Partitions);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
IFilter inputFilter = null; IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath); inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
}); });
if(inputFilter == null) if(inputFilter == null)
@@ -131,13 +110,13 @@ sealed class FilesystemInfoCommand : Command
Encoding encodingClass = null; Encoding encodingClass = null;
if(encoding != null) if(settings.Encoding != null)
{ {
try 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) catch(ArgumentException)
{ {
@@ -149,7 +128,7 @@ sealed class FilesystemInfoCommand : Command
PluginRegister plugins = PluginRegister.Singleton; PluginRegister plugins = PluginRegister.Singleton;
var checkRaw = false; bool checkRaw = false;
try try
{ {
@@ -177,7 +156,7 @@ sealed class FilesystemInfoCommand : Command
return (int)ErrorNumber.InvalidArgument; return (int)ErrorNumber.InvalidArgument;
} }
if(verbose) if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Image_format_identified_by_0_1, imageFormat.Name, imageFormat.Id); AaruConsole.VerboseWriteLine(UI.Image_format_identified_by_0_1, imageFormat.Name, imageFormat.Id);
else else
AaruConsole.WriteLine(UI.Image_format_identified_by_0, imageFormat.Name); AaruConsole.WriteLine(UI.Image_format_identified_by_0, imageFormat.Name);
@@ -202,7 +181,7 @@ sealed class FilesystemInfoCommand : Command
return (int)opened; return (int)opened;
} }
if(verbose) if(settings.Verbose)
{ {
ImageInfo.PrintImageInfo(imageFormat); ImageInfo.PrintImageInfo(imageFormat);
AaruConsole.WriteLine(); AaruConsole.WriteLine();
@@ -225,7 +204,7 @@ sealed class FilesystemInfoCommand : Command
IFilesystem fs; IFilesystem fs;
string information; string information;
if(partitions) if(settings.Partitions)
{ {
List<Partition> partitionsList = null; List<Partition> partitionsList = null;
@@ -241,7 +220,7 @@ sealed class FilesystemInfoCommand : Command
{ {
AaruConsole.DebugWriteLine(MODULE_NAME, UI.No_partitions_found); AaruConsole.DebugWriteLine(MODULE_NAME, UI.No_partitions_found);
if(!filesystems) if(!settings.Filesystems)
{ {
AaruConsole.WriteLine(UI.No_partitions_found_not_searching_for_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); 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() Table table = new()
{ {
@@ -283,7 +262,7 @@ sealed class FilesystemInfoCommand : Command
AnsiConsole.Write(table); AnsiConsole.Write(table);
if(!filesystems) continue; if(!settings.Filesystems) continue;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
@@ -425,4 +404,27 @@ sealed class FilesystemInfoCommand : Command
return (int)ErrorNumber.NoError; 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Aaru.CommonTypes; using Aaru.CommonTypes;
@@ -45,43 +44,19 @@ using Aaru.Core;
using Aaru.Localization; using Aaru.Localization;
using JetBrains.Annotations; using JetBrains.Annotations;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Filesystem; namespace Aaru.Commands.Filesystem;
sealed class LsCommand : Command sealed class LsCommand : Command<LsCommand.Settings>
{ {
const string MODULE_NAME = "Ls command"; const string MODULE_NAME = "Ls command";
public LsCommand() : base("list", UI.Filesystem_List_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -99,7 +74,7 @@ sealed class LsCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -110,11 +85,11 @@ sealed class LsCommand : Command
}; };
} }
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encoding ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(options ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(settings.Options ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
Statistics.AddCommand("ls"); Statistics.AddCommand("ls");
IFilter inputFilter = null; IFilter inputFilter = null;
@@ -122,16 +97,16 @@ sealed class LsCommand : Command
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); 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); AaruConsole.DebugWriteLine(MODULE_NAME, UI.Parsed_options);
foreach(KeyValuePair<string, string> parsedOption in parsedOptions) foreach(KeyValuePair<string, string> parsedOption in parsedOptions)
AaruConsole.DebugWriteLine(MODULE_NAME, "{0} = {1}", parsedOption.Key, parsedOption.Value); AaruConsole.DebugWriteLine(MODULE_NAME, "{0} = {1}", parsedOption.Key, parsedOption.Value);
parsedOptions.Add("debug", debug.ToString()); parsedOptions.Add("debug", settings.Debug.ToString());
if(inputFilter == null) if(inputFilter == null)
{ {
@@ -142,13 +117,13 @@ sealed class LsCommand : Command
Encoding encodingClass = null; Encoding encodingClass = null;
if(encoding != null) if(settings.Encoding != null)
{ {
try 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) catch(ArgumentException)
{ {
@@ -186,7 +161,7 @@ sealed class LsCommand : Command
return (int)ErrorNumber.InvalidArgument; return (int)ErrorNumber.InvalidArgument;
} }
if(verbose) if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Image_format_identified_by_0_1, imageFormat.Name, imageFormat.Id); AaruConsole.VerboseWriteLine(UI.Image_format_identified_by_0_1, imageFormat.Name, imageFormat.Id);
else else
AaruConsole.WriteLine(UI.Image_format_identified_by_0, imageFormat.Name); 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); 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();
AaruConsole.WriteLine($"[bold]{string.Format(UI.Partition_0, partitions[i].Sequence)}[/]"); 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(); 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) if(error == ErrorNumber.NoError)
{ {
ListFilesInDir("/", fs, longFormat); ListFilesInDir("/", fs, settings.LongFormat);
Statistics.AddFilesystem(fs.Metadata.Type); Statistics.AddFilesystem(fs.Metadata.Type);
} }
@@ -322,12 +301,17 @@ sealed class LsCommand : Command
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Mounting_filesystem).IsIndeterminate(); 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) if(error == ErrorNumber.NoError)
{ {
ListFilesInDir("/", fs, longFormat); ListFilesInDir("/", fs, settings.LongFormat);
Statistics.AddFilesystem(fs.Metadata.Type); Statistics.AddFilesystem(fs.Metadata.Type);
} }
@@ -442,4 +426,31 @@ sealed class LsCommand : Command
stats.Where(e => e.Value?.Attributes.HasFlag(FileAttributes.Directory) == true)) stats.Where(e => e.Value?.Attributes.HasFlag(FileAttributes.Directory) == true))
ListFilesInDir(path + "/" + subdirectory.Key, fs, longFormat); 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;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.Linq; using System.Linq;
using Aaru.CommonTypes; using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
@@ -42,21 +40,20 @@ using Aaru.Core;
using Aaru.Localization; using Aaru.Localization;
using JetBrains.Annotations; using JetBrains.Annotations;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Filesystem; namespace Aaru.Commands.Filesystem;
sealed class ListOptionsCommand : Command sealed class ListOptionsCommand : Command<ListOptionsCommand.Settings>
{ {
const string MODULE_NAME = "List-Options command"; const string MODULE_NAME = "List-Options command";
public ListOptionsCommand() : base("options", UI.Filesystem_Options_Command_Description) => 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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -74,7 +71,7 @@ sealed class ListOptionsCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -85,8 +82,8 @@ sealed class ListOptionsCommand : Command
}; };
} }
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
Statistics.AddCommand("list-options"); Statistics.AddCommand("list-options");
PluginRegister plugins = PluginRegister.Singleton; PluginRegister plugins = PluginRegister.Singleton;
@@ -141,4 +138,10 @@ sealed class ListOptionsCommand : Command
return type == typeof(string) ? UI.TypeToString_string : type.ToString(); 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 // Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.Linq; using System.Linq;
using Aaru.CommonTypes; using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
@@ -41,21 +38,20 @@ using Aaru.Console;
using Aaru.Core; using Aaru.Core;
using Aaru.Localization; using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands; namespace Aaru.Commands;
sealed class FormatsCommand : Command sealed class FormatsCommand : Command<FormatsCommand.Settings>
{ {
const string MODULE_NAME = "Formats command"; const string MODULE_NAME = "Formats command";
public FormatsCommand() : base("formats", UI.List_Formats_Command_Description) => public override int Execute(CommandContext context, Settings settings)
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)) ?? throw new NullReferenceException());
public static int Invoke(bool verbose, bool debug)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -73,7 +69,7 @@ sealed class FormatsCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -86,8 +82,8 @@ sealed class FormatsCommand : Command
Statistics.AddCommand("formats"); Statistics.AddCommand("formats");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
PluginRegister plugins = PluginRegister.Singleton; 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)) 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); table.AddColumn(UI.Title_Filter);
foreach(IFilter filter in PluginRegister.Singleton.Filters.Values) foreach(IFilter filter in PluginRegister.Singleton.Filters.Values)
{ {
if(verbose) if(settings.Verbose)
table.AddRow(filter.Id.ToString(), Markup.Escape(filter.Name)); table.AddRow(filter.Id.ToString(), Markup.Escape(filter.Name));
else else
table.AddRow(Markup.Escape(filter.Name)); table.AddRow(Markup.Escape(filter.Name));
@@ -119,14 +115,14 @@ sealed class FormatsCommand : Command
.ContainsKey(t.Key)))) .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); table.AddColumn(UI.Title_Media_image_format);
foreach(IMediaImage imagePlugin in foreach(IMediaImage imagePlugin in
plugins.MediaImages.Values.Where(t => !plugins.WritableImages.ContainsKey(t.Name))) plugins.MediaImages.Values.Where(t => !plugins.WritableImages.ContainsKey(t.Name)))
{ {
if(verbose) if(settings.Verbose)
table.AddRow(imagePlugin.Id.ToString(), Markup.Escape(imagePlugin.Name)); table.AddRow(imagePlugin.Id.ToString(), Markup.Escape(imagePlugin.Name));
else else
table.AddRow(Markup.Escape(imagePlugin.Name)); 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)) 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); table.AddColumn(UI.Title_Media_image_format);
@@ -149,7 +145,7 @@ sealed class FormatsCommand : Command
{ {
if(plugin is null) continue; if(plugin is null) continue;
if(verbose) if(settings.Verbose)
table.AddRow(plugin.Id.ToString(), Markup.Escape(plugin.Name)); table.AddRow(plugin.Id.ToString(), Markup.Escape(plugin.Name));
else else
table.AddRow(Markup.Escape(plugin.Name)); table.AddRow(Markup.Escape(plugin.Name));
@@ -170,13 +166,13 @@ sealed class FormatsCommand : Command
idOnlyFilesystems.Count)) idOnlyFilesystems.Count))
}; };
if(verbose) table.AddColumn(UI.Title_GUID); if(settings.Verbose) table.AddColumn(UI.Title_GUID);
table.AddColumn(UI.Title_Filesystem); table.AddColumn(UI.Title_Filesystem);
foreach(IFilesystem fs in idOnlyFilesystems) foreach(IFilesystem fs in idOnlyFilesystems)
{ {
if(verbose) if(settings.Verbose)
table.AddRow(fs.Id.ToString(), Markup.Escape(fs.Name)); table.AddRow(fs.Id.ToString(), Markup.Escape(fs.Name));
else else
table.AddRow(Markup.Escape(fs.Name)); table.AddRow(Markup.Escape(fs.Name));
@@ -192,7 +188,7 @@ sealed class FormatsCommand : Command
plugins.ReadOnlyFilesystems.Count)) plugins.ReadOnlyFilesystems.Count))
}; };
if(verbose) table.AddColumn(UI.Title_GUID); if(settings.Verbose) table.AddColumn(UI.Title_GUID);
table.AddColumn(UI.Title_Filesystem); table.AddColumn(UI.Title_Filesystem);
@@ -200,7 +196,7 @@ sealed class FormatsCommand : Command
{ {
if(fs is null) continue; if(fs is null) continue;
if(verbose) if(settings.Verbose)
table.AddRow(fs.Id.ToString(), Markup.Escape(fs.Name)); table.AddRow(fs.Id.ToString(), Markup.Escape(fs.Name));
else else
table.AddRow(Markup.Escape(fs.Name)); 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)) 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); table.AddColumn(UI.Title_Scheme);
@@ -223,7 +219,7 @@ sealed class FormatsCommand : Command
{ {
if(plugin is null) continue; if(plugin is null) continue;
if(verbose) if(settings.Verbose)
table.AddRow(plugin.Id.ToString(), Markup.Escape(plugin.Name)); table.AddRow(plugin.Id.ToString(), Markup.Escape(plugin.Name));
else else
table.AddRow(Markup.Escape(plugin.Name)); 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)) 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"); table.AddColumn("Archive format");
@@ -246,7 +242,7 @@ sealed class FormatsCommand : Command
{ {
if(archive is null) continue; if(archive is null) continue;
if(verbose) if(settings.Verbose)
table.AddRow(archive.Id.ToString(), Markup.Escape(archive.Name)); table.AddRow(archive.Id.ToString(), Markup.Escape(archive.Name));
else else
table.AddRow(Markup.Escape(archive.Name)); table.AddRow(Markup.Escape(archive.Name));
@@ -256,4 +252,10 @@ sealed class FormatsCommand : Command
return (int)ErrorNumber.NoError; return (int)ErrorNumber.NoError;
} }
#region Nested type: Settings
public class Settings : BaseSettings {}
#endregion
} }

View File

@@ -32,8 +32,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using Aaru.CommonTypes; using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Interfaces;
@@ -42,10 +41,11 @@ using Aaru.Console;
using Aaru.Core; using Aaru.Core;
using Aaru.Localization; using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image; namespace Aaru.Commands.Image;
sealed class ChecksumCommand : Command sealed class ChecksumCommand : Command<ChecksumCommand.Settings>
{ {
// How many sectors to read at once // How many sectors to read at once
const uint SECTORS_TO_READ = 256; const uint SECTORS_TO_READ = 256;
@@ -54,51 +54,11 @@ sealed class ChecksumCommand : Command
const int BYTES_TO_READ = 65536; const int BYTES_TO_READ = 65536;
const string MODULE_NAME = "Checksum command"; const string MODULE_NAME = "Checksum command";
public ChecksumCommand() : base("checksum", UI.Image_Checksum_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -116,7 +76,7 @@ sealed class ChecksumCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -129,30 +89,30 @@ sealed class ChecksumCommand : Command
Statistics.AddCommand("checksum"); Statistics.AddCommand("checksum");
AaruConsole.DebugWriteLine(MODULE_NAME, "--adler32={0}", adler32); AaruConsole.DebugWriteLine(MODULE_NAME, "--adler32={0}", settings.Adler32);
AaruConsole.DebugWriteLine(MODULE_NAME, "--crc16={0}", crc16); AaruConsole.DebugWriteLine(MODULE_NAME, "--crc16={0}", settings.Crc16);
AaruConsole.DebugWriteLine(MODULE_NAME, "--crc32={0}", crc32); AaruConsole.DebugWriteLine(MODULE_NAME, "--crc32={0}", settings.Crc32);
AaruConsole.DebugWriteLine(MODULE_NAME, "--crc64={0}", crc64); AaruConsole.DebugWriteLine(MODULE_NAME, "--crc64={0}", settings.Crc64);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fletcher16={0}", fletcher16); AaruConsole.DebugWriteLine(MODULE_NAME, "--fletcher16={0}", settings.Fletcher16);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fletcher32={0}", fletcher32); AaruConsole.DebugWriteLine(MODULE_NAME, "--fletcher32={0}", settings.Fletcher32);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--md5={0}", md5); AaruConsole.DebugWriteLine(MODULE_NAME, "--md5={0}", settings.Md5);
AaruConsole.DebugWriteLine(MODULE_NAME, "--separated-tracks={0}", separatedTracks); AaruConsole.DebugWriteLine(MODULE_NAME, "--separated-tracks={0}", settings.SeparatedTracks);
AaruConsole.DebugWriteLine(MODULE_NAME, "--sha1={0}", sha1); AaruConsole.DebugWriteLine(MODULE_NAME, "--sha1={0}", settings.Sha1);
AaruConsole.DebugWriteLine(MODULE_NAME, "--sha256={0}", sha256); AaruConsole.DebugWriteLine(MODULE_NAME, "--sha256={0}", settings.Sha256);
AaruConsole.DebugWriteLine(MODULE_NAME, "--sha384={0}", sha384); AaruConsole.DebugWriteLine(MODULE_NAME, "--sha384={0}", settings.Sha384);
AaruConsole.DebugWriteLine(MODULE_NAME, "--sha512={0}", sha512); AaruConsole.DebugWriteLine(MODULE_NAME, "--sha512={0}", settings.Sha512);
AaruConsole.DebugWriteLine(MODULE_NAME, "--spamsum={0}", spamSum); AaruConsole.DebugWriteLine(MODULE_NAME, "--spamsum={0}", settings.SpamSum);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--whole-disc={0}", wholeDisc); AaruConsole.DebugWriteLine(MODULE_NAME, "--whole-disc={0}", settings.WholeDisc);
IFilter inputFilter = null; IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath); inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
}); });
if(inputFilter == null) if(inputFilter == null)
@@ -198,29 +158,29 @@ sealed class ChecksumCommand : Command
Statistics.AddFilter(inputFilter.Name); Statistics.AddFilter(inputFilter.Name);
var enabledChecksums = new EnableChecksum(); 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; Checksum mediaChecksum = null;
@@ -233,7 +193,7 @@ sealed class ChecksumCommand : Command
{ {
Checksum trackChecksum = null; Checksum trackChecksum = null;
if(wholeDisc) mediaChecksum = new Checksum(enabledChecksums); if(settings.WholeDisc) mediaChecksum = new Checksum(enabledChecksums);
List<Track> inputTracks = opticalInput.Tracks; List<Track> inputTracks = opticalInput.Tracks;
@@ -273,7 +233,7 @@ sealed class ChecksumCommand : Command
currentTrack.StartSector, currentTrack.StartSector,
currentTrack.EndSector); currentTrack.EndSector);
if(separatedTracks) trackChecksum = new Checksum(enabledChecksums); if(settings.SeparatedTracks) trackChecksum = new Checksum(enabledChecksums);
ulong sectors = currentTrack.EndSector - currentTrack.StartSector + 1; ulong sectors = currentTrack.EndSector - currentTrack.StartSector + 1;
@@ -342,9 +302,9 @@ sealed class ChecksumCommand : Command
doneSectors += sectors - doneSectors; 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; trackTask.Value = doneSectors;
} }
@@ -352,7 +312,7 @@ sealed class ChecksumCommand : Command
trackTask.StopTask(); trackTask.StopTask();
AaruConsole.WriteLine(); AaruConsole.WriteLine();
if(!separatedTracks) continue; if(!settings.SeparatedTracks) continue;
if(trackChecksum == null) continue; if(trackChecksum == null) continue;
@@ -376,7 +336,7 @@ sealed class ChecksumCommand : Command
} }
*/ */
if(!wholeDisc) return; if(!settings.WholeDisc) return;
if(mediaChecksum == null) return; if(mediaChecksum == null) return;
@@ -393,7 +353,7 @@ sealed class ChecksumCommand : Command
} }
catch(Exception ex) catch(Exception ex)
{ {
if(debug) if(settings.Debug)
AaruConsole.DebugWriteLine(Localization.Core.Could_not_get_tracks_because_0, ex.Message); AaruConsole.DebugWriteLine(Localization.Core.Could_not_get_tracks_because_0, ex.Message);
else else
AaruConsole.WriteLine("Unable to get separate tracks, not checksumming them"); AaruConsole.WriteLine("Unable to get separate tracks, not checksumming them");
@@ -405,7 +365,7 @@ sealed class ChecksumCommand : Command
{ {
Checksum trackChecksum = null; Checksum trackChecksum = null;
if(wholeDisc) mediaChecksum = new Checksum(enabledChecksums); if(settings.WholeDisc) mediaChecksum = new Checksum(enabledChecksums);
ulong previousFileEnd = 0; ulong previousFileEnd = 0;
@@ -423,7 +383,7 @@ sealed class ChecksumCommand : Command
tapeTask.Description = tapeTask.Description =
string.Format(UI.Hashing_file_0_of_1, currentFile.File, tapeImage.Files.Count); 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); ProgressTask preFileTask = ctx.AddTask(UI.Hashing_sector);
preFileTask.MaxValue = currentFile.FirstBlock - previousFileEnd; preFileTask.MaxValue = currentFile.FirstBlock - previousFileEnd;
@@ -457,7 +417,7 @@ sealed class ChecksumCommand : Command
currentFile.FirstBlock, currentFile.FirstBlock,
currentFile.LastBlock); currentFile.LastBlock);
if(separatedTracks) trackChecksum = new Checksum(enabledChecksums); if(settings.SeparatedTracks) trackChecksum = new Checksum(enabledChecksums);
ulong sectors = currentFile.LastBlock - currentFile.FirstBlock + 1; ulong sectors = currentFile.LastBlock - currentFile.FirstBlock + 1;
ulong doneSectors = 0; ulong doneSectors = 0;
@@ -524,15 +484,15 @@ sealed class ChecksumCommand : Command
fileTask.Value = doneSectors; 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(); fileTask.StopTask();
AaruConsole.WriteLine(); AaruConsole.WriteLine();
if(separatedTracks) if(settings.SeparatedTracks)
{ {
if(trackChecksum != null) if(trackChecksum != null)
{ {
@@ -549,7 +509,7 @@ sealed class ChecksumCommand : Command
tapeTask.Increment(1); 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); ProgressTask postFileTask = ctx.AddTask(UI.Hashing_sector);
postFileTask.MaxValue = tapeImage.Info.Sectors - previousFileEnd; postFileTask.MaxValue = tapeImage.Info.Sectors - previousFileEnd;
@@ -576,7 +536,7 @@ sealed class ChecksumCommand : Command
if(errno != ErrorNumber.NoError) return (int)errno; if(errno != ErrorNumber.NoError) return (int)errno;
if(wholeDisc && mediaChecksum != null) if(settings.WholeDisc && mediaChecksum != null)
{ {
AaruConsole.WriteLine(); AaruConsole.WriteLine();
@@ -603,8 +563,8 @@ sealed class ChecksumCommand : Command
ProgressTask imageTask = ctx.AddTask(UI.Hashing_image); ProgressTask imageTask = ctx.AddTask(UI.Hashing_image);
ulong length = byteAddressableImage.Info.Sectors; ulong length = byteAddressableImage.Info.Sectors;
imageTask.MaxValue = length; imageTask.MaxValue = length;
ulong doneBytes = 0; ulong doneBytes = 0;
var data = new byte[BYTES_TO_READ]; byte[] data = new byte[BYTES_TO_READ];
while(doneBytes < length) while(doneBytes < length)
{ {
@@ -763,4 +723,71 @@ sealed class ChecksumCommand : Command
return (int)ErrorNumber.NoError; 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -45,40 +44,20 @@ using Aaru.Core;
using Aaru.Helpers; using Aaru.Helpers;
using Aaru.Localization; using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
using ImageInfo = Aaru.CommonTypes.Structs.ImageInfo; using ImageInfo = Aaru.CommonTypes.Structs.ImageInfo;
namespace Aaru.Commands.Image; namespace Aaru.Commands.Image;
sealed class CompareCommand : Command sealed class CompareCommand : Command<CompareCommand.Settings>
{ {
const string MODULE_NAME = "Compare command"; const string MODULE_NAME = "Compare command";
public CompareCommand() : base("compare", UI.Image_Compare_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -96,7 +75,7 @@ sealed class CompareCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -109,10 +88,10 @@ sealed class CompareCommand : Command
Statistics.AddCommand("compare"); Statistics.AddCommand("compare");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input1={0}", Markup.Escape(imagePath1 ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input1={0}", Markup.Escape(settings.ImagePath1 ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input2={0}", Markup.Escape(imagePath2 ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input2={0}", Markup.Escape(settings.ImagePath2 ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
IFilter inputFilter1 = null; IFilter inputFilter1 = null;
IFilter inputFilter2 = null; IFilter inputFilter2 = null;
@@ -120,13 +99,13 @@ sealed class CompareCommand : Command
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_first_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_first_file_filter).IsIndeterminate();
inputFilter1 = PluginRegister.Singleton.GetFilter(imagePath1); inputFilter1 = PluginRegister.Singleton.GetFilter(settings.ImagePath1);
}); });
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_second_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_second_file_filter).IsIndeterminate();
inputFilter2 = PluginRegister.Singleton.GetFilter(imagePath2); inputFilter2 = PluginRegister.Singleton.GetFilter(settings.ImagePath2);
}); });
if(inputFilter1 == null) if(inputFilter1 == null)
@@ -165,7 +144,7 @@ sealed class CompareCommand : Command
return (int)ErrorNumber.UnrecognizedFormat; return (int)ErrorNumber.UnrecognizedFormat;
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.VerboseWriteLine(UI.First_input_file_format_identified_by_0_1, AaruConsole.VerboseWriteLine(UI.First_input_file_format_identified_by_0_1,
input1Format.Name, input1Format.Name,
@@ -181,7 +160,7 @@ sealed class CompareCommand : Command
return (int)ErrorNumber.UnrecognizedFormat; return (int)ErrorNumber.UnrecognizedFormat;
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.VerboseWriteLine(UI.Second_input_file_format_identified_by_0_1, AaruConsole.VerboseWriteLine(UI.Second_input_file_format_identified_by_0_1,
input2Format.Name, input2Format.Name,
@@ -235,18 +214,18 @@ sealed class CompareCommand : Command
table.AddColumn(UI.Title_Second_Media_image); table.AddColumn(UI.Title_Second_Media_image);
table.Columns[0].RightAligned(); table.Columns[0].RightAligned();
if(verbose) if(settings.Verbose)
{ {
table.AddRow(UI.Title_File, Markup.Escape(imagePath1), Markup.Escape(imagePath2)); table.AddRow(UI.Title_File, Markup.Escape(settings.ImagePath1), Markup.Escape(settings.ImagePath2));
table.AddRow(UI.Title_Media_image_format, input1Format.Name, input2Format.Name); table.AddRow(UI.Title_Media_image_format, input1Format.Name, input2Format.Name);
} }
else else
{ {
sb.AppendFormat($"[bold]{UI.Title_First_Media_image}:[/] {imagePath1}").AppendLine(); sb.AppendFormat($"[bold]{UI.Title_First_Media_image}:[/] {settings.ImagePath1}").AppendLine();
sb.AppendFormat($"[bold]{UI.Title_Second_Media_image}:[/] {imagePath2}").AppendLine(); sb.AppendFormat($"[bold]{UI.Title_Second_Media_image}:[/] {settings.ImagePath2}").AppendLine();
} }
var imagesDiffer = false; bool imagesDiffer = false;
ErrorNumber errno; ErrorNumber errno;
ImageInfo image1Info = input1Format.Info; ImageInfo image1Info = input1Format.Info;
@@ -276,7 +255,7 @@ sealed class CompareCommand : Command
} }
} }
if(verbose) if(settings.Verbose)
{ {
table.AddRow(UI.Has_partitions_Question, table.AddRow(UI.Has_partitions_Question,
image1Info.HasPartitions.ToString(), image1Info.HasPartitions.ToString(),
@@ -373,35 +352,35 @@ sealed class CompareCommand : Command
{ {
imagesDiffer = true; 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) if(image1Info.HasSessions != image2Info.HasSessions)
{ {
imagesDiffer = true; 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) if(image1Info.Sectors != image2Info.Sectors)
{ {
imagesDiffer = true; imagesDiffer = true;
if(!verbose) sb.AppendLine(UI.Image_sectors_differ); if(!settings.Verbose) sb.AppendLine(UI.Image_sectors_differ);
} }
if(image1Info.SectorSize != image2Info.SectorSize) if(image1Info.SectorSize != image2Info.SectorSize)
{ {
imagesDiffer = true; 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) if(image1Info.MediaType != image2Info.MediaType)
{ {
imagesDiffer = true; imagesDiffer = true;
if(!verbose) sb.AppendLine(UI.Media_type_differs); if(!settings.Verbose) sb.AppendLine(UI.Media_type_differs);
} }
if(image1Info.Sectors < image2Info.Sectors) if(image1Info.Sectors < image2Info.Sectors)
@@ -409,14 +388,14 @@ sealed class CompareCommand : Command
imagesDiffer = true; imagesDiffer = true;
leastSectors = image1Info.Sectors; 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) else if(image1Info.Sectors > image2Info.Sectors)
{ {
imagesDiffer = true; imagesDiffer = true;
leastSectors = image2Info.Sectors; 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 else
leastSectors = image1Info.Sectors; leastSectors = image1Info.Sectors;
@@ -506,8 +485,8 @@ sealed class CompareCommand : Command
ProgressTask task = ctx.AddTask(UI.Comparing_images); ProgressTask task = ctx.AddTask(UI.Comparing_images);
task.IsIndeterminate = true; task.IsIndeterminate = true;
var data1 = new byte[input1ByteAddressable.Info.Sectors]; byte[] data1 = new byte[input1ByteAddressable.Info.Sectors];
var data2 = new byte[input2ByteAddressable.Info.Sectors]; byte[] data2 = new byte[input2ByteAddressable.Info.Sectors];
byte[] tmp; byte[] tmp;
input1ByteAddressable.ReadBytes(data1, 0, data1.Length, out int bytesRead); 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); sb.AppendLine(imagesDiffer ? UI.Images_differ : UI.Images_do_not_differ);
if(verbose) if(settings.Verbose)
AnsiConsole.Write(table); AnsiConsole.Write(table);
else else
AaruConsole.WriteLine(sb.ToString()); AaruConsole.WriteLine(sb.ToString());
return (int)ErrorNumber.NoError; 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -52,6 +51,7 @@ using Aaru.Devices;
using Aaru.Localization; using Aaru.Localization;
using Schemas; using Schemas;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
using File = System.IO.File; using File = System.IO.File;
using ImageInfo = Aaru.CommonTypes.Structs.ImageInfo; using ImageInfo = Aaru.CommonTypes.Structs.ImageInfo;
using MediaType = Aaru.CommonTypes.MediaType; using MediaType = Aaru.CommonTypes.MediaType;
@@ -63,95 +63,15 @@ using Version = Aaru.CommonTypes.Interop.Version;
namespace Aaru.Commands.Image; namespace Aaru.Commands.Image;
sealed class ConvertImageCommand : Command sealed class ConvertImageCommand : Command<ConvertImageCommand.Settings>
{ {
const string MODULE_NAME = "Convert-image command"; const string MODULE_NAME = "Convert-image command";
public ConvertImageCommand() : base("convert", UI.Image_Convert_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -169,7 +89,7 @@ sealed class ConvertImageCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => 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(fixSubchannelCrc) fixSubchannel = true;
if(fixSubchannel) fixSubchannelPosition = true; if(fixSubchannel) fixSubchannelPosition = true;
Statistics.AddCommand("convert-image"); Statistics.AddCommand("convert-image");
AaruConsole.DebugWriteLine(MODULE_NAME, "--cicm-xml={0}", Markup.Escape(cicmXml ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--cicm-xml={0}", Markup.Escape(settings.CicmXml ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--comments={0}", Markup.Escape(comments ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--comments={0}", Markup.Escape(settings.Comments ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--count={0}", count); AaruConsole.DebugWriteLine(MODULE_NAME, "--count={0}", settings.Count);
AaruConsole.DebugWriteLine(MODULE_NAME, "--creator={0}", Markup.Escape(creator ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--creator={0}", Markup.Escape(settings.Creator ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.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,
AaruConsole.DebugWriteLine(MODULE_NAME, "--drive-revision={0}", Markup.Escape(driveFirmwareRevision ?? "")); "--drive-manufacturer={0}",
AaruConsole.DebugWriteLine(MODULE_NAME, "--drive-serial={0}", Markup.Escape(driveSerialNumber ?? "")); Markup.Escape(settings.DriveManufacturer ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--force={0}", force);
AaruConsole.DebugWriteLine(MODULE_NAME, "--format={0}", Markup.Escape(format ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--drive-model={0}", Markup.Escape(settings.DriveModel ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--geometry={0}", Markup.Escape(geometry ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(inputPath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME,
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-barcode={0}", Markup.Escape(mediaBarcode ?? "")); "--drive-revision={0}",
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-lastsequence={0}", lastMediaSequence); Markup.Escape(settings.DriveFirmwareRevision ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-manufacturer={0}", Markup.Escape(mediaManufacturer ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-model={0}", Markup.Escape(mediaModel ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--drive-serial={0}", Markup.Escape(settings.DriveSerialNumber ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-partnumber={0}", Markup.Escape(mediaPartNumber ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--force={0}", settings.Force);
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-sequence={0}", mediaSequence); AaruConsole.DebugWriteLine(MODULE_NAME, "--format={0}", Markup.Escape(settings.Format ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-serial={0}", Markup.Escape(mediaSerialNumber ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--geometry={0}", Markup.Escape(settings.Geometry ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--media-title={0}", Markup.Escape(mediaTitle ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.InputPath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(options ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--media-barcode={0}", Markup.Escape(settings.MediaBarcode ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(outputPath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--media-lastsequence={0}", settings.LastMediaSequence);
AaruConsole.DebugWriteLine(MODULE_NAME, "--resume-file={0}", Markup.Escape(resumeFile ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); 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-position={0}", fixSubchannelPosition);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel={0}", fixSubchannel); AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel={0}", fixSubchannel);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel-crc={0}", fixSubchannelCrc); AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel-crc={0}", fixSubchannelCrc);
AaruConsole.DebugWriteLine(MODULE_NAME, "--generate-subchannels={0}", generateSubchannels); AaruConsole.DebugWriteLine(MODULE_NAME, "--generate-subchannels={0}", settings.GenerateSubchannels);
AaruConsole.DebugWriteLine(MODULE_NAME, "--decrypt={0}", decrypt); AaruConsole.DebugWriteLine(MODULE_NAME, "--decrypt={0}", settings.Decrypt);
AaruConsole.DebugWriteLine(MODULE_NAME, "--aaru-metadata={0}", Markup.Escape(aaruMetadata ?? "")); 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); AaruConsole.DebugWriteLine(MODULE_NAME, UI.Parsed_options);
foreach(KeyValuePair<string, string> parsedOption in parsedOptions) foreach(KeyValuePair<string, string> parsedOption in parsedOptions)
AaruConsole.DebugWriteLine(MODULE_NAME, "{0} = {1}", parsedOption.Key, parsedOption.Value); 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); 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; (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) if(geometryPieces.Length != 3)
{ {
@@ -274,14 +214,14 @@ sealed class ConvertImageCommand : Command
Metadata sidecar = null; Metadata sidecar = null;
MediaType mediaType; MediaType mediaType;
if(aaruMetadata != null) if(settings.AaruMetadata != null)
{ {
if(File.Exists(aaruMetadata)) if(File.Exists(settings.AaruMetadata))
{ {
try try
{ {
var fs = new FileStream(aaruMetadata, FileMode.Open); var fs = new FileStream(settings.AaruMetadata, FileMode.Open);
sidecar = sidecar =
(JsonSerializer.Deserialize(fs, typeof(MetadataJson), MetadataJsonContext.Default) as (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 try
{ {
@@ -316,7 +256,7 @@ sealed class ConvertImageCommand : Command
var xs = new XmlSerializer(typeof(CICMMetadataType)); var xs = new XmlSerializer(typeof(CICMMetadataType));
#pragma warning restore IL2026 #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 // Should be covered by virtue of being the same exact class as the JSON above
#pragma warning disable IL2026 #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 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 = resume =
(JsonSerializer.Deserialize(fs, (JsonSerializer.Deserialize(fs,
@@ -365,7 +305,7 @@ sealed class ConvertImageCommand : Command
var xs = new XmlSerializer(typeof(Resume)); var xs = new XmlSerializer(typeof(Resume));
#pragma warning restore IL2026 #pragma warning restore IL2026
var sr = new StreamReader(resumeFile); var sr = new StreamReader(settings.ResumeFile);
// Bypassed by JSON source generator used above // Bypassed by JSON source generator used above
#pragma warning disable IL2026 #pragma warning disable IL2026
@@ -396,7 +336,7 @@ sealed class ConvertImageCommand : Command
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(inputPath); inputFilter = PluginRegister.Singleton.GetFilter(settings.InputPath);
}); });
if(inputFilter == null) if(inputFilter == null)
@@ -406,7 +346,7 @@ sealed class ConvertImageCommand : Command
return (int)ErrorNumber.CannotOpenFile; return (int)ErrorNumber.CannotOpenFile;
} }
if(File.Exists(outputPath)) if(File.Exists(settings.OutputPath))
{ {
AaruConsole.ErrorWriteLine(UI.Output_file_already_exists); AaruConsole.ErrorWriteLine(UI.Output_file_already_exists);
@@ -439,7 +379,7 @@ sealed class ConvertImageCommand : Command
return (int)ErrorNumber.InvalidArgument; return (int)ErrorNumber.InvalidArgument;
} }
if(verbose) if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Input_image_format_identified_by_0_1, inputFormat.Name, inputFormat.Id); AaruConsole.VerboseWriteLine(UI.Input_image_format_identified_by_0_1, inputFormat.Name, inputFormat.Id);
else else
AaruConsole.WriteLine(UI.Input_image_format_identified_by_0, inputFormat.Name); AaruConsole.WriteLine(UI.Input_image_format_identified_by_0, inputFormat.Name);
@@ -499,16 +439,16 @@ sealed class ConvertImageCommand : Command
List<IBaseWritableImage> candidates = []; List<IBaseWritableImage> candidates = [];
// Try extension // Try extension
if(string.IsNullOrEmpty(format)) if(string.IsNullOrEmpty(settings.Format))
{ {
candidates.AddRange(from plugin in plugins.WritableImages.Values candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null where plugin is not null
where plugin.KnownExtensions.Contains(Path.GetExtension(outputPath)) where plugin.KnownExtensions.Contains(Path.GetExtension(settings.OutputPath))
select plugin); select plugin);
} }
// Try Id // 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 candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null where plugin is not null
@@ -521,7 +461,7 @@ sealed class ConvertImageCommand : Command
{ {
candidates.AddRange(from plugin in plugins.WritableImages.Values candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null where plugin is not null
where plugin.Name.Equals(format, StringComparison.InvariantCultureIgnoreCase) where plugin.Name.Equals(settings.Format, StringComparison.InvariantCultureIgnoreCase)
select plugin); select plugin);
} }
@@ -539,7 +479,7 @@ sealed class ConvertImageCommand : Command
IBaseWritableImage outputFormat = candidates[0]; IBaseWritableImage outputFormat = candidates[0];
if(verbose) if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Output_image_format_0_1, outputFormat.Name, outputFormat.Id); AaruConsole.VerboseWriteLine(UI.Output_image_format_0_1, outputFormat.Name, outputFormat.Id);
else else
AaruConsole.WriteLine(UI.Output_image_format_0, outputFormat.Name); 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 => 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.Converting_image_will_lose_media_tag_0, mediaTag);
AaruConsole.ErrorWriteLine(UI.If_you_dont_care_use_force_option); 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 => foreach(SectorTagType sectorTag in inputFormat.Info.ReadableSectorTags.Where(sectorTag =>
!outputFormat.SupportedSectorTags.Contains(sectorTag))) !outputFormat.SupportedSectorTags.Contains(sectorTag)))
{ {
if(force) if(settings.Force)
{ {
if(sectorTag != SectorTagType.CdTrackFlags && if(sectorTag != SectorTagType.CdTrackFlags &&
sectorTag != SectorTagType.CdTrackIsrc && sectorTag != SectorTagType.CdTrackIsrc &&
@@ -593,7 +533,7 @@ sealed class ConvertImageCommand : Command
return (int)ErrorNumber.UnsupportedMedia; return (int)ErrorNumber.UnsupportedMedia;
} }
var ret = false; bool ret = false;
if(inputTape?.IsTape == true && outputTape != null) 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...");*/ 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 => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Invoke_Opening_image_file).IsIndeterminate(); ctx.AddTask(UI.Invoke_Opening_image_file).IsIndeterminate();
created = outputFormat.Create(outputPath, created = outputFormat.Create(settings.OutputPath,
mediaType, mediaType,
parsedOptions, parsedOptions,
inputFormat.Info.Sectors, inputFormat.Info.Sectors,
@@ -665,25 +605,26 @@ sealed class ConvertImageCommand : Command
{ {
Application = "Aaru", Application = "Aaru",
ApplicationVersion = Version.GetVersion(), ApplicationVersion = Version.GetVersion(),
Comments = comments ?? inputFormat.Info.Comments, Comments = settings.Comments ?? inputFormat.Info.Comments,
Creator = creator ?? inputFormat.Info.Creator, Creator = settings.Creator ?? inputFormat.Info.Creator,
DriveFirmwareRevision = driveFirmwareRevision ?? inputFormat.Info.DriveFirmwareRevision, DriveFirmwareRevision = settings.DriveFirmwareRevision ?? inputFormat.Info.DriveFirmwareRevision,
DriveManufacturer = driveManufacturer ?? inputFormat.Info.DriveManufacturer, DriveManufacturer = settings.DriveManufacturer ?? inputFormat.Info.DriveManufacturer,
DriveModel = driveModel ?? inputFormat.Info.DriveModel, DriveModel = settings.DriveModel ?? inputFormat.Info.DriveModel,
DriveSerialNumber = driveSerialNumber ?? inputFormat.Info.DriveSerialNumber, DriveSerialNumber = settings.DriveSerialNumber ?? inputFormat.Info.DriveSerialNumber,
LastMediaSequence = lastMediaSequence != 0 ? lastMediaSequence : inputFormat.Info.LastMediaSequence, LastMediaSequence =
MediaBarcode = mediaBarcode ?? inputFormat.Info.MediaBarcode, settings.LastMediaSequence != 0 ? settings.LastMediaSequence : inputFormat.Info.LastMediaSequence,
MediaManufacturer = mediaManufacturer ?? inputFormat.Info.MediaManufacturer, MediaBarcode = settings.MediaBarcode ?? inputFormat.Info.MediaBarcode,
MediaModel = mediaModel ?? inputFormat.Info.MediaModel, MediaManufacturer = settings.MediaManufacturer ?? inputFormat.Info.MediaManufacturer,
MediaPartNumber = mediaPartNumber ?? inputFormat.Info.MediaPartNumber, MediaModel = settings.MediaModel ?? inputFormat.Info.MediaModel,
MediaSequence = mediaSequence != 0 ? mediaSequence : inputFormat.Info.MediaSequence, MediaPartNumber = settings.MediaPartNumber ?? inputFormat.Info.MediaPartNumber,
MediaSerialNumber = mediaSerialNumber ?? inputFormat.Info.MediaSerialNumber, MediaSequence = settings.MediaSequence != 0 ? settings.MediaSequence : inputFormat.Info.MediaSequence,
MediaTitle = mediaTitle ?? inputFormat.Info.MediaTitle MediaSerialNumber = settings.MediaSerialNumber ?? inputFormat.Info.MediaSerialNumber,
MediaTitle = settings.MediaTitle ?? inputFormat.Info.MediaTitle
}; };
if(!outputFormat.SetImageInfo(imageInfo)) if(!outputFormat.SetImageInfo(imageInfo))
{ {
if(!force) if(!settings.Force)
{ {
AaruConsole.ErrorWriteLine(UI.Error_0_setting_metadata_not_continuing, outputFormat.ErrorMessage); AaruConsole.ErrorWriteLine(UI.Error_0_setting_metadata_not_continuing, outputFormat.ErrorMessage);
@@ -696,8 +637,8 @@ sealed class ConvertImageCommand : Command
Metadata metadata = inputFormat.AaruMetadata; Metadata metadata = inputFormat.AaruMetadata;
List<DumpHardware> dumpHardware = inputFormat.DumpHardware; List<DumpHardware> dumpHardware = inputFormat.DumpHardware;
foreach(MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags.Where(mediaTag => foreach(MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags.Where(mediaTag => !settings.Force ||
!force || outputFormat.SupportedMediaTags.Contains(mediaTag))) outputFormat.SupportedMediaTags.Contains(mediaTag)))
{ {
ErrorNumber errorNumber = ErrorNumber.NoError; ErrorNumber errorNumber = ErrorNumber.NoError;
@@ -712,7 +653,7 @@ sealed class ConvertImageCommand : Command
if(errno != ErrorNumber.NoError) if(errno != ErrorNumber.NoError)
{ {
if(force) if(settings.Force)
AaruConsole.ErrorWriteLine(UI.Error_0_reading_media_tag, errno); AaruConsole.ErrorWriteLine(UI.Error_0_reading_media_tag, errno);
else else
{ {
@@ -726,7 +667,7 @@ sealed class ConvertImageCommand : Command
if((outputFormat as IWritableImage)?.WriteMediaTag(tag, mediaTag) == true) return; if((outputFormat as IWritableImage)?.WriteMediaTag(tag, mediaTag) == true) return;
if(force) if(settings.Force)
AaruConsole.ErrorWriteLine(UI.Error_0_writing_media_tag, outputFormat.ErrorMessage); AaruConsole.ErrorWriteLine(UI.Error_0_writing_media_tag, outputFormat.ErrorMessage);
else else
{ {
@@ -756,7 +697,7 @@ sealed class ConvertImageCommand : Command
ErrorNumber errno = ErrorNumber.NoError; ErrorNumber errno = ErrorNumber.NoError;
if(decrypt) AaruConsole.WriteLine("Decrypting encrypted sectors."); if(settings.Decrypt) AaruConsole.WriteLine("Decrypting encrypted sectors.");
AnsiConsole.Progress() AnsiConsole.Progress()
.AutoClear(true) .AutoClear(true)
@@ -786,8 +727,8 @@ sealed class ConvertImageCommand : Command
uint sectorsToDo; uint sectorsToDo;
if(trackSectors - doneSectors >= (ulong)count) if(trackSectors - doneSectors >= (ulong)settings.Count)
sectorsToDo = (uint)count; sectorsToDo = (uint)settings.Count;
else else
sectorsToDo = (uint)(trackSectors - doneSectors); sectorsToDo = (uint)(trackSectors - doneSectors);
@@ -796,8 +737,8 @@ sealed class ConvertImageCommand : Command
doneSectors + sectorsToDo + track.StartSector, doneSectors + sectorsToDo + track.StartSector,
track.Sequence); track.Sequence);
var useNotLong = false; bool useNotLong = false;
var result = false; bool result = false;
if(useLong) if(useLong)
{ {
@@ -821,7 +762,7 @@ sealed class ConvertImageCommand : Command
{ {
result = true; result = true;
if(force) if(settings.Force)
{ {
AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing, AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing,
errno, errno,
@@ -841,7 +782,7 @@ sealed class ConvertImageCommand : Command
if(!result && sector.Length % 2352 != 0) if(!result && sector.Length % 2352 != 0)
{ {
if(!force) if(!settings.Force)
{ {
AaruConsole AaruConsole
.ErrorWriteLine(UI .ErrorWriteLine(UI
@@ -871,7 +812,7 @@ sealed class ConvertImageCommand : Command
or MediaType.DVDRDL or MediaType.DVDRDL
or MediaType.DVDPR or MediaType.DVDPR
or MediaType.DVDPRDL && or MediaType.DVDPRDL &&
decrypt) settings.Decrypt)
{ {
// Only sectors which are MPEG packets can be encrypted. // Only sectors which are MPEG packets can be encrypted.
if(Mpeg.ContainsMpegPackets(sector, sectorsToDo)) if(Mpeg.ContainsMpegPackets(sector, sectorsToDo))
@@ -1009,7 +950,7 @@ sealed class ConvertImageCommand : Command
{ {
result = true; result = true;
if(force) if(settings.Force)
{ {
AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing, AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing,
errno, errno,
@@ -1030,7 +971,7 @@ sealed class ConvertImageCommand : Command
if(!result) if(!result)
{ {
if(force) if(settings.Force)
{ {
AaruConsole.ErrorWriteLine(UI.Error_0_writing_sector_1_continuing, AaruConsole.ErrorWriteLine(UI.Error_0_writing_sector_1_continuing,
outputOptical.ErrorMessage, outputOptical.ErrorMessage,
@@ -1066,7 +1007,7 @@ sealed class ConvertImageCommand : Command
Dictionary<byte, int> smallestPregapLbaPerTrack = new(); Dictionary<byte, int> smallestPregapLbaPerTrack = new();
var tracks = new Track[inputOptical.Tracks.Count]; 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 tracks[i] = new Track
{ {
@@ -1143,7 +1084,7 @@ sealed class ConvertImageCommand : Command
continue; continue;
} }
if(force && !outputOptical.SupportedSectorTags.Contains(tag)) continue; if(settings.Force && !outputOptical.SupportedSectorTags.Contains(tag)) continue;
errno = ErrorNumber.NoError; errno = ErrorNumber.NoError;
@@ -1186,7 +1127,7 @@ sealed class ConvertImageCommand : Command
break; break;
default: default:
{ {
if(force) if(settings.Force)
{ {
AaruConsole.ErrorWriteLine(UI.Error_0_writing_tag_continuing, AaruConsole.ErrorWriteLine(UI.Error_0_writing_tag_continuing,
outputOptical.ErrorMessage); outputOptical.ErrorMessage);
@@ -1205,7 +1146,7 @@ sealed class ConvertImageCommand : Command
if(!result) if(!result)
{ {
if(force) if(settings.Force)
{ {
AaruConsole.ErrorWriteLine(UI.Error_0_writing_tag_continuing, AaruConsole.ErrorWriteLine(UI.Error_0_writing_tag_continuing,
outputOptical.ErrorMessage); outputOptical.ErrorMessage);
@@ -1231,8 +1172,8 @@ sealed class ConvertImageCommand : Command
{ {
uint sectorsToDo; uint sectorsToDo;
if(trackSectors - doneSectors >= (ulong)count) if(trackSectors - doneSectors >= (ulong)settings.Count)
sectorsToDo = (uint)count; sectorsToDo = (uint)settings.Count;
else else
sectorsToDo = (uint)(trackSectors - doneSectors); sectorsToDo = (uint)(trackSectors - doneSectors);
@@ -1290,7 +1231,7 @@ sealed class ConvertImageCommand : Command
{ {
result = true; result = true;
if(force) if(settings.Force)
{ {
AaruConsole AaruConsole
.ErrorWriteLine(UI.Error_0_reading_tag_for_sector_1_continuing, .ErrorWriteLine(UI.Error_0_reading_tag_for_sector_1_continuing,
@@ -1358,7 +1299,7 @@ sealed class ConvertImageCommand : Command
{ {
result = true; result = true;
if(force) if(settings.Force)
{ {
AaruConsole AaruConsole
.ErrorWriteLine(UI.Error_0_reading_tag_for_sector_1_continuing, .ErrorWriteLine(UI.Error_0_reading_tag_for_sector_1_continuing,
@@ -1380,7 +1321,7 @@ sealed class ConvertImageCommand : Command
if(!result) if(!result)
{ {
if(force) if(settings.Force)
{ {
AaruConsole AaruConsole
.ErrorWriteLine(UI.Error_0_writing_tag_for_sector_1_continuing, .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) foreach(KeyValuePair<byte, string> isrc in isrcs)
{
outputOptical.WriteSectorTag(Encoding.UTF8.GetBytes(isrc.Value), isrc.Key, SectorTagType.CdTrackIsrc); outputOptical.WriteSectorTag(Encoding.UTF8.GetBytes(isrc.Value), isrc.Key, SectorTagType.CdTrackIsrc);
}
if(trackFlags.Count > 0) if(trackFlags.Count > 0)
{ {
@@ -1466,7 +1405,7 @@ sealed class ConvertImageCommand : Command
or MediaType.VideoNowColor or MediaType.VideoNowColor
or MediaType.VideoNowXp or MediaType.VideoNowXp
or MediaType.CVD && or MediaType.CVD &&
generateSubchannels) settings.GenerateSubchannels)
{ {
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
@@ -1527,8 +1466,8 @@ sealed class ConvertImageCommand : Command
if(inputTape?.IsTape == true) if(inputTape?.IsTape == true)
sectorsToDo = 1; sectorsToDo = 1;
else if(inputFormat.Info.Sectors - doneSectors >= (ulong)count) else if(inputFormat.Info.Sectors - doneSectors >= (ulong)settings.Count)
sectorsToDo = (uint)count; sectorsToDo = (uint)settings.Count;
else else
sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors); sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors);
@@ -1553,7 +1492,7 @@ sealed class ConvertImageCommand : Command
{ {
result = true; result = true;
if(force) if(settings.Force)
{ {
AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing, AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing,
errno, errno,
@@ -1585,7 +1524,7 @@ sealed class ConvertImageCommand : Command
{ {
result = true; result = true;
if(force) if(settings.Force)
{ {
AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing, AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing,
errno, errno,
@@ -1604,7 +1543,7 @@ sealed class ConvertImageCommand : Command
if(!result) if(!result)
{ {
if(force) if(settings.Force)
{ {
AaruConsole.ErrorWriteLine(UI.Error_0_writing_sector_1_continuing, AaruConsole.ErrorWriteLine(UI.Error_0_writing_sector_1_continuing,
outputMedia.ErrorMessage, outputMedia.ErrorMessage,
@@ -1644,7 +1583,7 @@ sealed class ConvertImageCommand : Command
continue; continue;
} }
if(force && !outputMedia.SupportedSectorTags.Contains(tag)) continue; if(settings.Force && !outputMedia.SupportedSectorTags.Contains(tag)) continue;
doneSectors = 0; doneSectors = 0;
@@ -1655,8 +1594,8 @@ sealed class ConvertImageCommand : Command
{ {
uint sectorsToDo; uint sectorsToDo;
if(inputFormat.Info.Sectors - doneSectors >= (ulong)count) if(inputFormat.Info.Sectors - doneSectors >= (ulong)settings.Count)
sectorsToDo = (uint)count; sectorsToDo = (uint)settings.Count;
else else
sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors); sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors);
@@ -1684,7 +1623,7 @@ sealed class ConvertImageCommand : Command
{ {
result = true; result = true;
if(force) if(settings.Force)
{ {
AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing, AaruConsole.ErrorWriteLine(UI.Error_0_reading_sector_1_continuing,
errno, errno,
@@ -1702,7 +1641,7 @@ sealed class ConvertImageCommand : Command
if(!result) if(!result)
{ {
if(force) if(settings.Force)
{ {
AaruConsole.ErrorWriteLine(UI.Error_0_writing_sector_1_continuing, AaruConsole.ErrorWriteLine(UI.Error_0_writing_sector_1_continuing,
outputMedia.ErrorMessage, outputMedia.ErrorMessage,
@@ -1836,7 +1775,7 @@ sealed class ConvertImageCommand : Command
if(ret) AaruConsole.WriteLine(UI.Written_Aaru_Metadata_to_output_image); if(ret) AaruConsole.WriteLine(UI.Written_Aaru_Metadata_to_output_image);
} }
var closed = false; bool closed = false;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
@@ -1856,4 +1795,126 @@ sealed class ConvertImageCommand : Command
return (int)ErrorNumber.NoError; 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;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -44,43 +43,24 @@ using Aaru.CommonTypes.Interfaces;
using Aaru.Console; using Aaru.Console;
using Aaru.Core; using Aaru.Core;
using Aaru.Localization; using Aaru.Localization;
using JetBrains.Annotations;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
using Directory = System.IO.Directory; using Directory = System.IO.Directory;
using File = System.IO.File; using File = System.IO.File;
namespace Aaru.Commands.Image; namespace Aaru.Commands.Image;
sealed class CreateSidecarCommand : Command sealed class CreateSidecarCommand : Command<CreateSidecarCommand.Settings>
{ {
const string MODULE_NAME = "Create sidecar command"; const string MODULE_NAME = "Create sidecar command";
static ProgressTask _progressTask1; static ProgressTask _progressTask1;
static ProgressTask _progressTask2; static ProgressTask _progressTask2;
public CreateSidecarCommand() : base("create-sidecar", UI.Image_Create_Sidecar_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -98,7 +78,7 @@ sealed class CreateSidecarCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -111,22 +91,22 @@ sealed class CreateSidecarCommand : Command
Statistics.AddCommand("create-sidecar"); Statistics.AddCommand("create-sidecar");
AaruConsole.DebugWriteLine(MODULE_NAME, "--block-size={0}", blockSize); AaruConsole.DebugWriteLine(MODULE_NAME, "--block-size={0}", settings.BlockSize);
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encodingName ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--tape={0}", tape); AaruConsole.DebugWriteLine(MODULE_NAME, "--tape={0}", settings.Tape);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
Encoding encodingClass = null; Encoding encodingClass = null;
if(encodingName != null) if(settings.Encoding != null)
{ {
try 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) 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); 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 => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath); inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
}); });
if(inputFilter == null) if(inputFilter == null)
@@ -177,7 +157,7 @@ sealed class CreateSidecarCommand : Command
return (int)ErrorNumber.UnrecognizedFormat; return (int)ErrorNumber.UnrecognizedFormat;
} }
if(verbose) if(settings.Verbose)
AaruConsole.VerboseWriteLine(UI.Image_format_identified_by_0_1, imageFormat.Name, imageFormat.Id); AaruConsole.VerboseWriteLine(UI.Image_format_identified_by_0_1, imageFormat.Name, imageFormat.Id);
else else
AaruConsole.WriteLine(UI.Image_format_identified_by_0, imageFormat.Name); AaruConsole.WriteLine(UI.Image_format_identified_by_0, imageFormat.Name);
@@ -214,7 +194,7 @@ sealed class CreateSidecarCommand : Command
Statistics.AddMediaFormat(imageFormat.Format); Statistics.AddMediaFormat(imageFormat.Format);
Statistics.AddFilter(inputFilter.Name); 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(); Metadata sidecar = new();
AnsiConsole.Progress() AnsiConsole.Progress()
@@ -274,9 +254,10 @@ sealed class CreateSidecarCommand : Command
ctx.AddTask(Localization.Core.Writing_metadata_sidecar).IsIndeterminate(); ctx.AddTask(Localization.Core.Writing_metadata_sidecar).IsIndeterminate();
var jsonFs = var jsonFs =
new FileStream(Path.Combine(Path.GetDirectoryName(imagePath) ?? new FileStream(Path.Combine(Path.GetDirectoryName(settings.ImagePath) ??
throw new InvalidOperationException(), throw new InvalidOperationException(),
Path.GetFileNameWithoutExtension(imagePath) + ".metadata.json"), Path.GetFileNameWithoutExtension(settings.ImagePath) +
".metadata.json"),
FileMode.Create); FileMode.Create);
JsonSerializer.Serialize(jsonFs, JsonSerializer.Serialize(jsonFs,
@@ -298,17 +279,17 @@ sealed class CreateSidecarCommand : Command
return (int)ErrorNumber.UnexpectedException; 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); AaruConsole.ErrorWriteLine(Localization.Core.Cannot_create_a_sidecar_from_a_directory);
return (int)ErrorNumber.IsDirectory; return (int)ErrorNumber.IsDirectory;
} }
string[] contents = Directory.GetFiles(imagePath, "*", SearchOption.TopDirectoryOnly); string[] contents = Directory.GetFiles(settings.ImagePath, "*", SearchOption.TopDirectoryOnly);
var files = contents.Where(file => new FileInfo(file).Length % blockSize == 0).ToList(); var files = contents.Where(file => new FileInfo(file).Length % settings.BlockSize == 0).ToList();
files.Sort(StringComparer.CurrentCultureIgnoreCase); files.Sort(StringComparer.CurrentCultureIgnoreCase);
@@ -361,7 +342,9 @@ sealed class CreateSidecarCommand : Command
sidecarClass.Abort(); sidecarClass.Abort();
}; };
sidecar = sidecarClass.BlockTape(Path.GetFileName(imagePath), files, blockSize); sidecar = sidecarClass.BlockTape(Path.GetFileName(settings.ImagePath),
files,
settings.BlockSize);
}); });
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
@@ -369,9 +352,10 @@ sealed class CreateSidecarCommand : Command
ctx.AddTask(Localization.Core.Writing_metadata_sidecar).IsIndeterminate(); ctx.AddTask(Localization.Core.Writing_metadata_sidecar).IsIndeterminate();
var jsonFs = var jsonFs =
new FileStream(Path.Combine(Path.GetDirectoryName(imagePath) ?? new FileStream(Path.Combine(Path.GetDirectoryName(settings.ImagePath) ??
throw new InvalidOperationException(), throw new InvalidOperationException(),
Path.GetFileNameWithoutExtension(imagePath) + ".metadata.json"), Path.GetFileNameWithoutExtension(settings.ImagePath) +
".metadata.json"),
FileMode.Create); FileMode.Create);
JsonSerializer.Serialize(jsonFs, JsonSerializer.Serialize(jsonFs,
@@ -390,4 +374,27 @@ sealed class CreateSidecarCommand : Command
return (int)ErrorNumber.NoError; 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 // Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System; using System.ComponentModel;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.Globalization; using System.Globalization;
using Aaru.CommonTypes; using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
@@ -44,41 +42,19 @@ using Aaru.Decoders.CD;
using Aaru.Decoders.SCSI; using Aaru.Decoders.SCSI;
using Aaru.Localization; using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image; namespace Aaru.Commands.Image;
sealed class DecodeCommand : Command sealed class DecodeCommand : Command<DecodeCommand.Settings>
{ {
const string MODULE_NAME = "Decode command"; const string MODULE_NAME = "Decode command";
public DecodeCommand() : base("decode", UI.Image_Decode_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -96,7 +72,7 @@ sealed class DecodeCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -109,20 +85,20 @@ sealed class DecodeCommand : Command
Statistics.AddCommand("decode"); Statistics.AddCommand("decode");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--disk-tags={0}", diskTags); AaruConsole.DebugWriteLine(MODULE_NAME, "--disk-tags={0}", settings.DiskTags);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--length={0}", Markup.Escape(length ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--length={0}", Markup.Escape(settings.Length ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--sector-tags={0}", sectorTags); AaruConsole.DebugWriteLine(MODULE_NAME, "--sector-tags={0}", settings.SectorTags);
AaruConsole.DebugWriteLine(MODULE_NAME, "--start={0}", startSector); AaruConsole.DebugWriteLine(MODULE_NAME, "--start={0}", settings.StartSector);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
IFilter inputFilter = null; IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath); inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
}); });
if(inputFilter == null) if(inputFilter == null)
@@ -176,7 +152,7 @@ sealed class DecodeCommand : Command
Statistics.AddMedia(inputFormat.Info.MediaType, false); Statistics.AddMedia(inputFormat.Info.MediaType, false);
Statistics.AddFilter(inputFilter.Name); Statistics.AddFilter(inputFilter.Name);
if(diskTags) if(settings.DiskTags)
{ {
if(inputFormat.Info.ReadableMediaTags.Count == 0) if(inputFormat.Info.ReadableMediaTags.Count == 0)
AaruConsole.WriteLine(UI.There_are_no_media_tags_in_chosen_disc_image); 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 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); AaruConsole.WriteLine(UI.Not_decoding_sectors_tags);
return 3; return 3;
@@ -429,4 +405,31 @@ sealed class DecodeCommand : Command
return (int)ErrorNumber.NoError; 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 // Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System; using System.ComponentModel;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using Aaru.CommonTypes; using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Interfaces;
@@ -40,43 +38,21 @@ using Aaru.Console;
using Aaru.Core; using Aaru.Core;
using Aaru.Localization; using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image; namespace Aaru.Commands.Image;
sealed class EntropyCommand : Command sealed class EntropyCommand : Command<EntropyCommand.Settings>
{ {
const string MODULE_NAME = "Entropy command"; const string MODULE_NAME = "Entropy command";
static ProgressTask _progressTask1; static ProgressTask _progressTask1;
static ProgressTask _progressTask2; static ProgressTask _progressTask2;
public EntropyCommand() : base("entropy", UI.Image_Entropy_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -94,7 +70,7 @@ sealed class EntropyCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -107,19 +83,19 @@ sealed class EntropyCommand : Command
Statistics.AddCommand("entropy"); Statistics.AddCommand("entropy");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--duplicated-sectors={0}", duplicatedSectors); AaruConsole.DebugWriteLine(MODULE_NAME, "--duplicated-sectors={0}", settings.DuplicatedSectors);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--separated-tracks={0}", separatedTracks); AaruConsole.DebugWriteLine(MODULE_NAME, "--separated-tracks={0}", settings.SeparatedTracks);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--whole-disc={0}", wholeDisc); AaruConsole.DebugWriteLine(MODULE_NAME, "--whole-disc={0}", settings.WholeDisc);
IFilter inputFilter = null; IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath); inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
}); });
if(inputFilter == null) if(inputFilter == null)
@@ -164,7 +140,10 @@ sealed class EntropyCommand : Command
Statistics.AddMedia(inputFormat.Info.MediaType, false); Statistics.AddMedia(inputFormat.Info.MediaType, false);
Statistics.AddFilter(inputFilter.Name); 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() AnsiConsole.Progress()
.AutoClear(true) .AutoClear(true)
@@ -204,7 +183,7 @@ sealed class EntropyCommand : Command
_progressTask2 = null; _progressTask2 = null;
}; };
if(wholeDisc && inputFormat is IOpticalMediaImage opticalFormat) if(settings.WholeDisc && inputFormat is IOpticalMediaImage opticalFormat)
{ {
if(opticalFormat.Sessions?.Count > 1) if(opticalFormat.Sessions?.Count > 1)
{ {
@@ -221,7 +200,7 @@ sealed class EntropyCommand : Command
if(separatedTracks) if(separatedTracks)
{ {
EntropyResults[] tracksEntropy = EntropyResults[] tracksEntropy =
entropyCalculator.CalculateTracksEntropy(duplicatedSectors); entropyCalculator.CalculateTracksEntropy(settings.DuplicatedSectors);
foreach(EntropyResults trackEntropy in tracksEntropy) foreach(EntropyResults trackEntropy in tracksEntropy)
{ {
@@ -243,7 +222,8 @@ sealed class EntropyCommand : Command
EntropyResults entropy = inputFormat.Info.MetadataMediaType == MetadataMediaType.LinearMedia EntropyResults entropy = inputFormat.Info.MetadataMediaType == MetadataMediaType.LinearMedia
? entropyCalculator.CalculateLinearMediaEntropy() ? entropyCalculator.CalculateLinearMediaEntropy()
: entropyCalculator.CalculateMediaEntropy(duplicatedSectors); : entropyCalculator.CalculateMediaEntropy(settings
.DuplicatedSectors);
AaruConsole.WriteLine(UI.Entropy_for_disk_is_0, entropy.Entropy); AaruConsole.WriteLine(UI.Entropy_for_disk_is_0, entropy.Entropy);
@@ -257,4 +237,27 @@ sealed class EntropyCommand : Command
return (int)ErrorNumber.NoError; 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 // Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System.CommandLine;
using Aaru.Localization;
namespace Aaru.Commands.Image; namespace Aaru.Commands.Image;
sealed class ImageFamily : Command class ImageFamily : BaseSettings {}
{
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());
}
}

View File

@@ -31,8 +31,7 @@
// ****************************************************************************/ // ****************************************************************************/
using System; using System;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using Aaru.CommonTypes; using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Interfaces;
@@ -40,30 +39,19 @@ using Aaru.Console;
using Aaru.Core; using Aaru.Core;
using Aaru.Localization; using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image; namespace Aaru.Commands.Image;
sealed class ImageInfoCommand : Command sealed class ImageInfoCommand : Command<ImageInfoCommand.Settings>
{ {
const string MODULE_NAME = "Image-info command"; const string MODULE_NAME = "Image-info command";
public ImageInfoCommand() : base("info", UI.Image_Info_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -81,7 +69,7 @@ sealed class ImageInfoCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -94,16 +82,16 @@ sealed class ImageInfoCommand : Command
Statistics.AddCommand("image-info"); Statistics.AddCommand("image-info");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
IFilter inputFilter = null; IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath); inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
}); });
if(inputFilter == null) if(inputFilter == null)
@@ -176,4 +164,15 @@ sealed class ImageInfoCommand : Command
return (int)ErrorNumber.NoError; 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;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.Linq; using System.Linq;
using Aaru.CommonTypes; using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
@@ -42,21 +40,20 @@ using Aaru.Core;
using Aaru.Localization; using Aaru.Localization;
using JetBrains.Annotations; using JetBrains.Annotations;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image; namespace Aaru.Commands.Image;
sealed class ListOptionsCommand : Command sealed class ListOptionsCommand : Command<ListOptionsCommand.Settings>
{ {
const string MODULE_NAME = "List-Options command"; const string MODULE_NAME = "List-Options command";
public ListOptionsCommand() : base("options", UI.Image_Options_Command_Description) => 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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -74,7 +71,7 @@ sealed class ListOptionsCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -85,8 +82,8 @@ sealed class ListOptionsCommand : Command
}; };
} }
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
Statistics.AddCommand("list-options"); Statistics.AddCommand("list-options");
PluginRegister plugins = PluginRegister.Singleton; PluginRegister plugins = PluginRegister.Singleton;
@@ -144,4 +141,10 @@ sealed class ListOptionsCommand : Command
return type == typeof(string) ? UI.TypeToString_string : type.ToString(); 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;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using Aaru.CommonTypes; using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Interfaces;
@@ -41,39 +40,20 @@ using Aaru.Core;
using Aaru.Helpers; using Aaru.Helpers;
using Aaru.Localization; using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image; namespace Aaru.Commands.Image;
sealed class PrintHexCommand : Command sealed class PrintHexCommand : Command<PrintHexCommand.Settings>
{ {
const string MODULE_NAME = "PrintHex command"; const string MODULE_NAME = "PrintHex command";
public PrintHexCommand() : base("print", UI.Image_Print_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
Add(new Option<ulong>(["--length", "-l"], () => 1, UI.How_many_sectors_to_print));
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(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -91,7 +71,7 @@ sealed class PrintHexCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -104,20 +84,20 @@ sealed class PrintHexCommand : Command
Statistics.AddCommand("print-hex"); Statistics.AddCommand("print-hex");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--length={0}", length); AaruConsole.DebugWriteLine(MODULE_NAME, "--length={0}", settings.Length);
AaruConsole.DebugWriteLine(MODULE_NAME, "--long-sectors={0}", longSectors); AaruConsole.DebugWriteLine(MODULE_NAME, "--long-sectors={0}", settings.LongSectors);
AaruConsole.DebugWriteLine(MODULE_NAME, "--start={0}", start); AaruConsole.DebugWriteLine(MODULE_NAME, "--start={0}", settings.Start);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--width={0}", width); AaruConsole.DebugWriteLine(MODULE_NAME, "--width={0}", settings.Width);
IFilter inputFilter = null; IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath); inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
}); });
if(inputFilter == null) if(inputFilter == null)
@@ -129,6 +109,8 @@ sealed class PrintHexCommand : Command
IBaseImage inputFormat = null; IBaseImage inputFormat = null;
bool longSectors = settings.LongSectors;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_image_format).IsIndeterminate(); ctx.AddTask(UI.Identifying_image_format).IsIndeterminate();
@@ -162,36 +144,40 @@ sealed class PrintHexCommand : Command
{ {
var byteAddressableImage = inputFormat as IByteAddressableImage; 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; ErrorNumber errno = ErrorNumber.NoError;
var bytesRead = 0; int bytesRead = 0;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Reading_data).IsIndeterminate(); 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; ErrorNumber.InvalidArgument;
}); });
// TODO: Span // 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); Array.Copy(data, 0, tmp, 0, bytesRead);
data = tmp; data = tmp;
} }
if(errno == ErrorNumber.NoError) if(errno == ErrorNumber.NoError)
AaruConsole.WriteLine(Markup.Escape(PrintHex.ByteArrayToHexArrayString(data, width, true))); AaruConsole.WriteLine(Markup.Escape(PrintHex.ByteArrayToHexArrayString(data, settings.Width, true)));
else 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 else
{ {
for(ulong i = 0; i < length; i++) for(ulong i = 0; i < settings.Length; i++)
{ {
if(inputFormat is not IMediaImage blockImage) if(inputFormat is not IMediaImage blockImage)
{ {
@@ -200,8 +186,9 @@ sealed class PrintHexCommand : Command
break; break;
} }
AaruConsole.WriteLine($"[bold][italic]{string.Format(UI.Sector_0_as_in_sector_number, start)}[/][/]" + AaruConsole
i); .WriteLine($"[bold][italic]{string.Format(UI.Sector_0_as_in_sector_number, settings.Start)}[/][/]" +
i);
if(blockImage.Info.ReadableSectorTags == null) if(blockImage.Info.ReadableSectorTags == null)
{ {
@@ -227,17 +214,46 @@ sealed class PrintHexCommand : Command
ctx.AddTask(UI.Reading_sector).IsIndeterminate(); ctx.AddTask(UI.Reading_sector).IsIndeterminate();
errno = longSectors errno = longSectors
? blockImage.ReadSectorLong(start + i, out sector) ? blockImage.ReadSectorLong(settings.Start + i, out sector)
: blockImage.ReadSector(start + i, out sector); : blockImage.ReadSector(settings.Start + i, out sector);
}); });
if(errno == ErrorNumber.NoError) if(errno == ErrorNumber.NoError)
AaruConsole.WriteLine(Markup.Escape(PrintHex.ByteArrayToHexArrayString(sector, width, true))); AaruConsole.WriteLine(Markup.Escape(PrintHex.ByteArrayToHexArrayString(sector,
settings.Width,
true)));
else 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; 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 // Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using Aaru.CommonTypes; using Aaru.CommonTypes;
@@ -47,39 +45,19 @@ using Aaru.Localization;
using Humanizer; using Humanizer;
using Humanizer.Localisation; using Humanizer.Localisation;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Image; namespace Aaru.Commands.Image;
sealed class VerifyCommand : Command sealed class VerifyCommand : Command<VerifyCommand.Settings>
{ {
const string MODULE_NAME = "Verify command"; const string MODULE_NAME = "Verify command";
public VerifyCommand() : base("verify", UI.Image_Verify_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -97,7 +75,7 @@ sealed class VerifyCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -110,20 +88,20 @@ sealed class VerifyCommand : Command
Statistics.AddCommand("verify"); Statistics.AddCommand("verify");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(imagePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--input={0}", Markup.Escape(settings.ImagePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verify-disc={0}", verifyDisc); AaruConsole.DebugWriteLine(MODULE_NAME, "--verify-disc={0}", settings.VerifyDisc);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verify-sectors={0}", verifySectors); AaruConsole.DebugWriteLine(MODULE_NAME, "--verify-sectors={0}", settings.VerifySectors);
AaruConsole.DebugWriteLine(MODULE_NAME, "--create-graph={0}", createGraph); AaruConsole.DebugWriteLine(MODULE_NAME, "--create-graph={0}", settings.CreateGraph);
AaruConsole.DebugWriteLine(MODULE_NAME, "--dimensions={0}", dimensions); AaruConsole.DebugWriteLine(MODULE_NAME, "--dimensions={0}", settings.Dimensions);
IFilter inputFilter = null; IFilter inputFilter = null;
Core.Spectre.ProgressSingleSpinner(ctx => Core.Spectre.ProgressSingleSpinner(ctx =>
{ {
ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate(); ctx.AddTask(UI.Identifying_file_filter).IsIndeterminate();
inputFilter = PluginRegister.Singleton.GetFilter(imagePath); inputFilter = PluginRegister.Singleton.GetFilter(settings.ImagePath);
}); });
if(inputFilter == null) if(inputFilter == null)
@@ -183,7 +161,7 @@ sealed class VerifyCommand : Command
var chkWatch = new Stopwatch(); var chkWatch = new Stopwatch();
if(verifyDisc && verifiableImage != null) if(settings.VerifyDisc && verifiableImage != null)
{ {
bool? discCheckStatus = null; bool? discCheckStatus = null;
@@ -218,7 +196,7 @@ sealed class VerifyCommand : Command
chkWatch.Elapsed.Humanize(minUnit: TimeUnit.Second)); chkWatch.Elapsed.Humanize(minUnit: TimeUnit.Second));
} }
if(!verifySectors) if(!settings.VerifySectors)
{ {
return correctImage switch return correctImage switch
{ {
@@ -237,17 +215,20 @@ sealed class VerifyCommand : Command
{ {
Spiral.DiscParameters spiralParameters = null; 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) if(spiralParameters is not null)
{ {
mediaGraph = new Spiral((int)dimensions, mediaGraph = new Spiral((int)settings.Dimensions,
(int)dimensions, (int)settings.Dimensions,
spiralParameters, spiralParameters,
opticalMediaImage.Info.Sectors); opticalMediaImage.Info.Sectors);
} }
else if(createGraph) else if(settings.CreateGraph)
mediaGraph = new BlockMap((int)dimensions, (int)dimensions, opticalMediaImage.Info.Sectors); mediaGraph = new BlockMap((int)settings.Dimensions,
(int)settings.Dimensions,
opticalMediaImage.Info.Sectors);
List<Track> inputTracks = opticalMediaImage.Tracks; List<Track> inputTracks = opticalMediaImage.Tracks;
ulong currentSectorAll = 0; ulong currentSectorAll = 0;
@@ -439,23 +420,25 @@ sealed class VerifyCommand : Command
AaruConsole.VerboseWriteLine(UI.Checking_sector_checksums_took_0, AaruConsole.VerboseWriteLine(UI.Checking_sector_checksums_took_0,
stopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)); stopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second));
if(verbose) if(settings.Verbose)
{ {
AaruConsole.VerboseWriteLine($"[red]{UI.LBAs_with_error}[/]"); AaruConsole.VerboseWriteLine($"[red]{UI.LBAs_with_error}[/]");
if(failingLbas.Count == (int)inputFormat.Info.Sectors) if(failingLbas.Count == (int)inputFormat.Info.Sectors)
AaruConsole.VerboseWriteLine($"\t[red]{UI.all_sectors}[/]"); AaruConsole.VerboseWriteLine($"\t[red]{UI.all_sectors}[/]");
else 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}[/]"); AaruConsole.WriteLine($"[yellow3_1]{UI.LBAs_without_checksum}[/]");
if(unknownLbas.Count == (int)inputFormat.Info.Sectors) if(unknownLbas.Count == (int)inputFormat.Info.Sectors)
AaruConsole.VerboseWriteLine($"\t[yellow3_1]{UI.all_sectors}[/]"); AaruConsole.VerboseWriteLine($"\t[yellow3_1]{UI.all_sectors}[/]");
else 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 // TODO: Convert to table
@@ -483,4 +466,31 @@ sealed class VerifyCommand : Command
true => (int)ErrorNumber.NoError 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 // Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
@@ -40,21 +37,20 @@ using Aaru.Console;
using Aaru.Core; using Aaru.Core;
using Aaru.Localization; using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands; namespace Aaru.Commands;
sealed class ListEncodingsCommand : Command sealed class ListEncodingsCommand : Command<ListEncodingsCommand.Settings>
{ {
const string MODULE_NAME = "List-Encodings command"; const string MODULE_NAME = "List-Encodings command";
public ListEncodingsCommand() : base("list-encodings", UI.List_Encodings_Command_Description) => 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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -72,7 +68,7 @@ sealed class ListEncodingsCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -85,8 +81,8 @@ sealed class ListEncodingsCommand : Command
Statistics.AddCommand("list-encodings"); Statistics.AddCommand("list-encodings");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
var encodings = Encoding.GetEncodings() var encodings = Encoding.GetEncodings()
.Select(info => new CommonEncodingInfo .Select(info => new CommonEncodingInfo
@@ -123,5 +119,11 @@ sealed class ListEncodingsCommand : Command
public string DisplayName; public string DisplayName;
} }
#endregion
#region Nested type: Settings
public class Settings : BaseSettings {}
#endregion #endregion
} }

View File

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

View File

@@ -33,8 +33,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -56,121 +55,24 @@ using Aaru.Core.Logging;
using Aaru.Localization; using Aaru.Localization;
using Schemas; using Schemas;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
using Dump = Aaru.Core.Devices.Dumping.Dump; using Dump = Aaru.Core.Devices.Dumping.Dump;
using File = System.IO.File; using File = System.IO.File;
namespace Aaru.Commands.Media; namespace Aaru.Commands.Media;
// TODO: Add raw dumping // TODO: Add raw dumping
sealed class DumpMediaCommand : Command sealed class DumpMediaCommand : Command<DumpMediaCommand.Settings>
{ {
const string MODULE_NAME = "Dump-Media command"; const string MODULE_NAME = "Dump-Media command";
static ProgressTask _progressTask1; static ProgressTask _progressTask1;
static ProgressTask _progressTask2; static ProgressTask _progressTask2;
public DumpMediaCommand() : base("dump", UI.Media_Dump_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -188,7 +90,7 @@ sealed class DumpMediaCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => 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; fixSubchannel |= fixSubchannelCrc;
fixSubchannelPosition |= retrySubchannel || fixSubchannel; fixSubchannelPosition |= settings.RetrySubchannel || fixSubchannel;
if(maxBlocks == 0) maxBlocks = 64; if(maxBlocks == 0) maxBlocks = 64;
Statistics.AddCommand("dump-media"); Statistics.AddCommand("dump-media");
AaruConsole.DebugWriteLine(MODULE_NAME, "--cicm-xml={0}", Markup.Escape(cicmXml ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--cicm-xml={0}", Markup.Escape(settings.CicmXml ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(devicePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(settings.DevicePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(encoding ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--encoding={0}", Markup.Escape(settings.Encoding ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--first-pregap={0}", firstPregap); AaruConsole.DebugWriteLine(MODULE_NAME, "--first-pregap={0}", settings.FirstPregap);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-offset={0}", fixOffset); AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-offset={0}", settings.FixOffset);
AaruConsole.DebugWriteLine(MODULE_NAME, "--force={0}", force); AaruConsole.DebugWriteLine(MODULE_NAME, "--force={0}", settings.Force);
AaruConsole.DebugWriteLine(MODULE_NAME, "--format={0}", Markup.Escape(format ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--format={0}", Markup.Escape(settings.Format ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--metadata={0}", metadata); AaruConsole.DebugWriteLine(MODULE_NAME, "--metadata={0}", settings.Metadata);
AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(options ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--options={0}", Markup.Escape(settings.Options ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(outputPath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--output={0}", Markup.Escape(settings.OutputPath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--persistent={0}", persistent); AaruConsole.DebugWriteLine(MODULE_NAME, "--persistent={0}", settings.Persistent);
AaruConsole.DebugWriteLine(MODULE_NAME, "--resume={0}", resume); AaruConsole.DebugWriteLine(MODULE_NAME, "--resume={0}", settings.Resume);
AaruConsole.DebugWriteLine(MODULE_NAME, "--retry-passes={0}", retryPasses); AaruConsole.DebugWriteLine(MODULE_NAME, "--retry-passes={0}", settings.RetryPasses);
AaruConsole.DebugWriteLine(MODULE_NAME, "--skip={0}", skip); AaruConsole.DebugWriteLine(MODULE_NAME, "--skip={0}", settings.Skip);
AaruConsole.DebugWriteLine(MODULE_NAME, "--stop-on-error={0}", stopOnError); AaruConsole.DebugWriteLine(MODULE_NAME, "--stop-on-error={0}", settings.StopOnError);
AaruConsole.DebugWriteLine(MODULE_NAME, "--trim={0}", trim); AaruConsole.DebugWriteLine(MODULE_NAME, "--trim={0}", settings.Trim);
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--subchannel={0}", Markup.Escape(subchannel ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--subchannel={0}", Markup.Escape(settings.Subchannel ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "----private={0}", @private); AaruConsole.DebugWriteLine(MODULE_NAME, "----private={0}", settings.Private);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel-position={0}", fixSubchannelPosition); AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel-position={0}", settings.FixSubchannelPosition);
AaruConsole.DebugWriteLine(MODULE_NAME, "--retry-subchannel={0}", retrySubchannel); AaruConsole.DebugWriteLine(MODULE_NAME, "--retry-subchannel={0}", settings.RetrySubchannel);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel={0}", fixSubchannel); AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel={0}", fixSubchannel);
AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel-crc={0}", fixSubchannelCrc); AaruConsole.DebugWriteLine(MODULE_NAME, "--fix-subchannel-crc={0}", fixSubchannelCrc);
AaruConsole.DebugWriteLine(MODULE_NAME, "--generate-subchannels={0}", generateSubchannels); AaruConsole.DebugWriteLine(MODULE_NAME, "--generate-subchannels={0}", settings.GenerateSubchannels);
AaruConsole.DebugWriteLine(MODULE_NAME, "--skip-cdiready-hole={0}", skipCdiReadyHole); AaruConsole.DebugWriteLine(MODULE_NAME, "--skip-cdiready-hole={0}", settings.SkipCdiReadyHole);
AaruConsole.DebugWriteLine(MODULE_NAME, "--eject={0}", eject); AaruConsole.DebugWriteLine(MODULE_NAME, "--eject={0}", eject);
AaruConsole.DebugWriteLine(MODULE_NAME, "--max-blocks={0}", maxBlocks); AaruConsole.DebugWriteLine(MODULE_NAME, "--max-blocks={0}", maxBlocks);
AaruConsole.DebugWriteLine(MODULE_NAME, "--use-buffered-reads={0}", useBufferedReads); AaruConsole.DebugWriteLine(MODULE_NAME, "--use-buffered-reads={0}", settings.UseBufferedReads);
AaruConsole.DebugWriteLine(MODULE_NAME, "--store-encrypted={0}", storeEncrypted); AaruConsole.DebugWriteLine(MODULE_NAME, "--store-encrypted={0}", settings.StoreEncrypted);
AaruConsole.DebugWriteLine(MODULE_NAME, "--title-keys={0}", titleKeys); AaruConsole.DebugWriteLine(MODULE_NAME, "--title-keys={0}", settings.TitleKeys);
AaruConsole.DebugWriteLine(MODULE_NAME, "--ignore-cdr-runouts={0}", ignoreCdrRunOuts); AaruConsole.DebugWriteLine(MODULE_NAME, "--ignore-cdr-runouts={0}", settings.IgnoreCdrRunOuts);
AaruConsole.DebugWriteLine(MODULE_NAME, "--create-graph={0}", createGraph); AaruConsole.DebugWriteLine(MODULE_NAME, "--create-graph={0}", settings.CreateGraph);
AaruConsole.DebugWriteLine(MODULE_NAME, "--dimensions={0}", dimensions); AaruConsole.DebugWriteLine(MODULE_NAME, "--dimensions={0}", settings.Dimensions);
AaruConsole.DebugWriteLine(MODULE_NAME, "--aaru-metadata={0}", Markup.Escape(aaruMetadata ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--aaru-metadata={0}", Markup.Escape(settings.AaruMetadata ?? ""));
// TODO: Disabled temporarily // TODO: Disabled temporarily
//AaruConsole.DebugWriteLine(MODULE_NAME, "--raw={0}", Markup.Escape(raw ?? "")); //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); AaruConsole.DebugWriteLine(MODULE_NAME, UI.Parsed_options);
foreach(KeyValuePair<string, string> parsedOption in parsedOptions) foreach(KeyValuePair<string, string> parsedOption in parsedOptions)
@@ -253,13 +162,13 @@ sealed class DumpMediaCommand : Command
Encoding encodingClass = null; Encoding encodingClass = null;
if(encoding != null) if(settings.Encoding != null)
{ {
try 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) catch(ArgumentException)
{ {
@@ -271,43 +180,44 @@ sealed class DumpMediaCommand : Command
DumpSubchannel wantedSubchannel = DumpSubchannel.Any; 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; wantedSubchannel = DumpSubchannel.Any;
else if(subchannel?.ToLowerInvariant() == UI.Subchannel_name_rw) else if(settings.Subchannel?.ToLowerInvariant() == UI.Subchannel_name_rw)
wantedSubchannel = DumpSubchannel.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; wantedSubchannel = DumpSubchannel.RwOrPq;
else if(subchannel?.ToLowerInvariant() == UI.Subchannel_name_pq) else if(settings.Subchannel?.ToLowerInvariant() == UI.Subchannel_name_pq)
wantedSubchannel = DumpSubchannel.Pq; wantedSubchannel = DumpSubchannel.Pq;
else if(subchannel?.ToLowerInvariant() == UI.Subchannel_name_none) else if(settings.Subchannel?.ToLowerInvariant() == UI.Subchannel_name_none)
wantedSubchannel = DumpSubchannel.None; wantedSubchannel = DumpSubchannel.None;
else 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) && bool isResponse = filename.StartsWith("#", StringComparison.OrdinalIgnoreCase) &&
File.Exists(Path.Combine(Path.GetDirectoryName(outputPath), File.Exists(Path.Combine(Path.GetDirectoryName(settings.OutputPath),
Path.GetFileNameWithoutExtension(outputPath))); Path.GetFileNameWithoutExtension(settings.OutputPath)));
TextReader resReader; TextReader resReader;
if(isResponse) if(isResponse)
{ {
resReader = new StreamReader(Path.Combine(Path.GetDirectoryName(outputPath), resReader = new StreamReader(Path.Combine(Path.GetDirectoryName(settings.OutputPath),
Path.GetFileNameWithoutExtension(outputPath))); Path.GetFileNameWithoutExtension(settings.OutputPath)));
} }
else else
resReader = new StringReader(Path.GetFileNameWithoutExtension(outputPath)); resReader = new StringReader(Path.GetFileNameWithoutExtension(settings.OutputPath));
if(isResponse) eject = true; if(isResponse) eject = true;
PluginRegister plugins = PluginRegister.Singleton; PluginRegister plugins = PluginRegister.Singleton;
List<IBaseWritableImage> candidates = []; List<IBaseWritableImage> candidates = [];
string extension = Path.GetExtension(outputPath); string extension = Path.GetExtension(settings.OutputPath);
// Try extension // Try extension
if(string.IsNullOrEmpty(format)) if(string.IsNullOrEmpty(settings.Format))
{ {
candidates.AddRange(from plugin in plugins.WritableImages.Values candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null where plugin is not null
@@ -316,7 +226,7 @@ sealed class DumpMediaCommand : Command
} }
// Try Id // 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 candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null where plugin is not null
@@ -329,7 +239,7 @@ sealed class DumpMediaCommand : Command
{ {
candidates.AddRange(from plugin in plugins.WritableImages.Values candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null where plugin is not null
where plugin.Name.Equals(format, StringComparison.InvariantCultureIgnoreCase) where plugin.Name.Equals(settings.Format, StringComparison.InvariantCultureIgnoreCase)
select plugin); select plugin);
} }
@@ -381,6 +291,8 @@ sealed class DumpMediaCommand : Command
.Replace('*', ''); .Replace('*', '');
} }
string devicePath = settings.DevicePath;
if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0])) if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0]))
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':'; devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
@@ -424,11 +336,11 @@ sealed class DumpMediaCommand : Command
Statistics.AddDevice(dev); Statistics.AddDevice(dev);
string outputPrefix = Path.Combine(Path.GetDirectoryName(outputPath), responseLine); string outputPrefix = Path.Combine(Path.GetDirectoryName(settings.OutputPath), responseLine);
Resume resumeClass = null; Resume resumeClass = null;
if(resume) if(settings.Resume)
{ {
try try
{ {
@@ -445,7 +357,7 @@ sealed class DumpMediaCommand : Command
} }
// DEPRECATED: To be removed in Aaru 7 // 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 // Should be covered by virtue of being the same exact class as the JSON above
#pragma warning disable IL2026 #pragma warning disable IL2026
@@ -488,13 +400,13 @@ sealed class DumpMediaCommand : Command
Metadata sidecar = null; Metadata sidecar = null;
if(aaruMetadata != null) if(settings.AaruMetadata != null)
{ {
if(File.Exists(aaruMetadata)) if(File.Exists(settings.AaruMetadata))
{ {
try try
{ {
var fs = new FileStream(aaruMetadata, FileMode.Open); var fs = new FileStream(settings.AaruMetadata, FileMode.Open);
sidecar = sidecar =
(JsonSerializer.Deserialize(fs, typeof(MetadataJson), MetadataJsonContext.Default) as (JsonSerializer.Deserialize(fs, typeof(MetadataJson), MetadataJsonContext.Default) as
@@ -520,13 +432,13 @@ sealed class DumpMediaCommand : Command
return (int)ErrorNumber.NoSuchFile; return (int)ErrorNumber.NoSuchFile;
} }
} }
else if(cicmXml != null) else if(settings.CicmXml != null)
{ {
if(File.Exists(cicmXml)) if(File.Exists(settings.CicmXml))
{ {
try try
{ {
var sr = new StreamReader(cicmXml); var sr = new StreamReader(settings.CicmXml);
// Bypassed by JSON source generator used above // Bypassed by JSON source generator used above
#pragma warning disable IL2026 #pragma warning disable IL2026
@@ -563,16 +475,16 @@ sealed class DumpMediaCommand : Command
candidates = []; candidates = [];
// Try extension // Try extension
if(string.IsNullOrEmpty(format)) if(string.IsNullOrEmpty(settings.Format))
{ {
candidates.AddRange(from plugin in plugins.WritableImages.Values candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null where plugin is not null
where plugin.KnownExtensions.Contains(Path.GetExtension(outputPath)) where plugin.KnownExtensions.Contains(Path.GetExtension(settings.OutputPath))
select plugin); select plugin);
} }
// Try Id // 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 candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null where plugin is not null
@@ -585,15 +497,16 @@ sealed class DumpMediaCommand : Command
{ {
candidates.AddRange(from plugin in plugins.WritableImages.Values candidates.AddRange(from plugin in plugins.WritableImages.Values
where plugin is not null where plugin is not null
where plugin.Name.Equals(format, StringComparison.InvariantCultureIgnoreCase) where plugin.Name.Equals(settings.Format,
StringComparison.InvariantCultureIgnoreCase)
select plugin); select plugin);
} }
IBaseWritableImage outputFormat = candidates[0]; 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); dumpLog.WriteLine(UI.Output_image_format_0_1, outputFormat.Name, outputFormat.Id);
AaruConsole.VerboseWriteLine(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 errorLog = new ErrorLog(outputPrefix + ".error.log");
var dumper = new Dump(resume, var dumper = new Dump(settings.Resume,
dev, dev,
devicePath, devicePath,
outputFormat, outputFormat,
retryPasses, settings.RetryPasses,
force, settings.Force,
false, false,
persistent, settings.Persistent,
stopOnError, settings.StopOnError,
resumeClass, resumeClass,
dumpLog, dumpLog,
encodingClass, encodingClass,
@@ -622,29 +535,29 @@ sealed class DumpMediaCommand : Command
outputPrefix + extension, outputPrefix + extension,
parsedOptions, parsedOptions,
sidecar, sidecar,
skip, settings.Skip,
metadata, settings.Metadata,
trim, settings.Trim,
firstPregap, settings.FirstPregap,
fixOffset, settings.FixOffset,
debug, settings.Debug,
wantedSubchannel, wantedSubchannel,
speed, settings.Speed,
@private, settings.Private,
fixSubchannelPosition, fixSubchannelPosition,
retrySubchannel, settings.RetrySubchannel,
fixSubchannel, fixSubchannel,
fixSubchannelCrc, fixSubchannelCrc,
skipCdiReadyHole, settings.SkipCdiReadyHole,
errorLog, errorLog,
generateSubchannels, settings.GenerateSubchannels,
maxBlocks, maxBlocks,
useBufferedReads, settings.UseBufferedReads,
storeEncrypted, settings.StoreEncrypted,
titleKeys, settings.TitleKeys,
ignoreCdrRunOuts, settings.IgnoreCdrRunOuts,
createGraph, settings.CreateGraph,
dimensions); settings.Dimensions);
AnsiConsole.Progress() AnsiConsole.Progress()
.AutoClear(true) .AutoClear(true)
@@ -765,4 +678,146 @@ sealed class DumpMediaCommand : Command
return (int)ErrorNumber.NoError; 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using System.Linq; using System.Linq;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Structs; using Aaru.CommonTypes.Structs;
@@ -53,9 +52,9 @@ using Aaru.Decoders.Xbox;
using Aaru.Localization; using Aaru.Localization;
using Humanizer.Bytes; using Humanizer.Bytes;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
using BCA = Aaru.Decoders.Bluray.BCA; using BCA = Aaru.Decoders.Bluray.BCA;
using Cartridge = Aaru.Decoders.DVD.Cartridge; using Cartridge = Aaru.Decoders.DVD.Cartridge;
using Command = System.CommandLine.Command;
using DDS = Aaru.Decoders.DVD.DDS; using DDS = Aaru.Decoders.DVD.DDS;
using DMI = Aaru.Decoders.Xbox.DMI; using DMI = Aaru.Decoders.Xbox.DMI;
using Session = Aaru.Decoders.CD.Session; using Session = Aaru.Decoders.CD.Session;
@@ -63,29 +62,16 @@ using Spare = Aaru.Decoders.DVD.Spare;
namespace Aaru.Commands.Media; namespace Aaru.Commands.Media;
sealed class MediaInfoCommand : Command sealed class MediaInfoCommand : Command<MediaInfoCommand.Settings>
{ {
const string MODULE_NAME = "Media-Info command"; const string MODULE_NAME = "Media-Info command";
public MediaInfoCommand() : base("info", UI.Media_Info_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -103,7 +89,7 @@ sealed class MediaInfoCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -116,10 +102,12 @@ sealed class MediaInfoCommand : Command
Statistics.AddCommand("media-info"); Statistics.AddCommand("media-info");
AaruConsole.DebugWriteLine(MODULE_NAME, "debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "device={0}", Markup.Escape(devicePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "device={0}", Markup.Escape(settings.DevicePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "output-prefix={0}", Markup.Escape(outputPrefix ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "output-prefix={0}", Markup.Escape(settings.OutputPrefix ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "verbose={0}", settings.Verbose);
string devicePath = settings.DevicePath;
if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0])) if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0]))
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':'; devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
@@ -170,12 +158,12 @@ sealed class MediaInfoCommand : Command
break; break;
case DeviceType.NVMe: case DeviceType.NVMe:
DoNvmeMediaInfo(outputPrefix, dev); DoNvmeMediaInfo(settings.OutputPrefix, dev);
break; break;
case DeviceType.ATAPI: case DeviceType.ATAPI:
case DeviceType.SCSI: case DeviceType.SCSI:
DoScsiMediaInfo(debug, outputPrefix, dev); DoScsiMediaInfo(settings.Debug, settings.OutputPrefix, dev);
break; break;
default: default:
@@ -827,7 +815,7 @@ sealed class MediaInfoCommand : Command
AaruConsole.Write($"[bold]{Localization.Core.Media_Serial_Number}:[/] "); 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.Write("{0:X2}", scsiInfo.MediaSerialNumber[i]);
AaruConsole.WriteLine(); AaruConsole.WriteLine();
@@ -843,13 +831,13 @@ sealed class MediaInfoCommand : Command
if(tracks != null) 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 supportsPqSubchannel = Dump.SupportsPqSubchannel(dev, null, null, firstLba);
bool supportsRwSubchannel = Dump.SupportsRwSubchannel(dev, null, null, firstLba); bool supportsRwSubchannel = Dump.SupportsRwSubchannel(dev, null, null, firstLba);
// Open main database // Open main database
var ctx = AaruContext.Create(Settings.Settings.MainDbPath); var ctx = AaruContext.Create(Aaru.Settings.Settings.MainDbPath);
// Search for device in main database // Search for device in main database
Aaru.Database.Models.Device dbDev = Aaru.Database.Models.Device dbDev =
@@ -867,7 +855,7 @@ sealed class MediaInfoCommand : Command
out bool inexactPositioning, out bool inexactPositioning,
false); 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; tracks[^1].EndSector = (ulong)lastSector;
@@ -969,4 +957,19 @@ sealed class MediaInfoCommand : Command
dev.Close(); 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 // Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System.CommandLine;
using Aaru.Localization;
namespace Aaru.Commands.Media; namespace Aaru.Commands.Media;
sealed class MediaFamily : Command class MediaFamily : BaseSettings {}
{
public MediaFamily() : base("media", UI.Media_Command_Family_Description)
{
AddAlias("m");
AddCommand(new DumpMediaCommand());
AddCommand(new MediaInfoCommand());
AddCommand(new MediaScanCommand());
}
}

View File

@@ -30,9 +30,7 @@
// Copyright © 2011-2025 Natalia Portillo // Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System; using System.ComponentModel;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
using Aaru.Console; using Aaru.Console;
using Aaru.Core; using Aaru.Core;
@@ -42,42 +40,20 @@ using Humanizer;
using Humanizer.Bytes; using Humanizer.Bytes;
using Humanizer.Localisation; using Humanizer.Localisation;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
namespace Aaru.Commands.Media; namespace Aaru.Commands.Media;
sealed class MediaScanCommand : Command sealed class MediaScanCommand : Command<MediaScanCommand.Settings>
{ {
const string MODULE_NAME = "Media-Scan command"; const string MODULE_NAME = "Media-Scan command";
static ProgressTask _progressTask1; static ProgressTask _progressTask1;
public MediaScanCommand() : base("scan", UI.Media_Scan_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -95,7 +71,7 @@ sealed class MediaScanCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -108,12 +84,14 @@ sealed class MediaScanCommand : Command
Statistics.AddCommand("media-scan"); Statistics.AddCommand("media-scan");
AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "--debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(devicePath ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--device={0}", Markup.Escape(settings.DevicePath ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--ibg-log={0}", Markup.Escape(ibgLog ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--ibg-log={0}", Markup.Escape(settings.IbgLog ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--mhdd-log={0}", Markup.Escape(mhddLog ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "--mhdd-log={0}", Markup.Escape(settings.MhddLog ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "--verbose={0}", settings.Verbose);
AaruConsole.DebugWriteLine(MODULE_NAME, "--use-buffered-reads={0}", useBufferedReads); 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])) if(devicePath.Length == 2 && devicePath[1] == ':' && devicePath[0] != '/' && char.IsLetter(devicePath[0]))
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':'; devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
@@ -152,7 +130,7 @@ sealed class MediaScanCommand : Command
Statistics.AddDevice(dev); 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(); ScanResults results = new();
AnsiConsole.Progress() AnsiConsole.Progress()
@@ -254,4 +232,27 @@ sealed class MediaScanCommand : Command
return (int)ErrorNumber.NoError; 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 // TODO: Fix errors returned
using System; using System;
using System.CommandLine; using System.ComponentModel;
using System.CommandLine.NamingConventionBinder;
using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Enums;
using Aaru.Console; using Aaru.Console;
using Aaru.Core; using Aaru.Core;
using Aaru.Localization;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
using Remote = Aaru.Devices.Remote.Remote; using Remote = Aaru.Devices.Remote.Remote;
namespace Aaru.Commands; namespace Aaru.Commands;
sealed class RemoteCommand : Command sealed class RemoteCommand : Command<RemoteCommand.Settings>
{ {
const string MODULE_NAME = "Remote command"; const string MODULE_NAME = "Remote command";
public RemoteCommand() : base("remote", UI.Remote_Command_Description) public override int Execute(CommandContext context, Settings settings)
{
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)
{ {
MainClass.PrintCopyright(); MainClass.PrintCopyright();
if(debug) if(settings.Debug)
{ {
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{ {
@@ -82,7 +69,7 @@ sealed class RemoteCommand : Command
AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); }; AaruConsole.WriteExceptionEvent += ex => { stderrConsole.WriteException(ex); };
} }
if(verbose) if(settings.Verbose)
{ {
AaruConsole.WriteEvent += (format, objects) => AaruConsole.WriteEvent += (format, objects) =>
{ {
@@ -95,13 +82,13 @@ sealed class RemoteCommand : Command
Statistics.AddCommand("remote"); Statistics.AddCommand("remote");
AaruConsole.DebugWriteLine(MODULE_NAME, "debug={0}", debug); AaruConsole.DebugWriteLine(MODULE_NAME, "debug={0}", settings.Debug);
AaruConsole.DebugWriteLine(MODULE_NAME, "host={0}", Markup.Escape(host ?? "")); AaruConsole.DebugWriteLine(MODULE_NAME, "host={0}", Markup.Escape(settings.Host ?? ""));
AaruConsole.DebugWriteLine(MODULE_NAME, "verbose={0}", verbose); AaruConsole.DebugWriteLine(MODULE_NAME, "verbose={0}", settings.Verbose);
try try
{ {
var remote = new Remote(new Uri(host)); var remote = new Remote(new Uri(settings.Host));
Statistics.AddRemote(remote.ServerApplication, Statistics.AddRemote(remote.ServerApplication,
remote.ServerVersion, remote.ServerVersion,
@@ -138,4 +125,15 @@ sealed class RemoteCommand : Command
return (int)ErrorNumber.NoError; 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;
using System.CommandLine;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@@ -54,6 +53,8 @@ using Aaru.Settings;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli;
using ListOptionsCommand = Aaru.Commands.Filesystem.ListOptionsCommand;
namespace Aaru; namespace Aaru;
@@ -176,7 +177,7 @@ class MainClass
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
var mainDbUpdate = false; bool mainDbUpdate = false;
if(!File.Exists(Settings.Settings.MainDbPath)) 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. // There are too many places that depend on this being inited to be sure all are covered, so init it here.
PluginBase.Init(); 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"], config.AddBranch<ArchiveFamily>("archive",
() => false, archive =>
UI.Shows_debug_output_from_plugins)); {
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)); config.AddBranch<DeviceFamily>("device",
rootCommand.AddCommand(new DeviceFamily()); device =>
rootCommand.AddCommand(new FilesystemFamily()); {
rootCommand.AddCommand(new ImageFamily()); device.SetDescription(UI.Device_Command_Family_Description);
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());
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(); await Statistics.SaveStatsAsync();
if(!rootCommand.Parse(args).RootCommandResult.GetValueForOption(pauseOption)) return ret; // TODO: Return pause functionality
// AaruConsole.WriteLine(UI.Press_any_key_to_exit);
AaruConsole.WriteLine(UI.Press_any_key_to_exit); // System.Console.ReadKey();
System.Console.ReadKey();
return ret; return ret;
} }

View File

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