Use Spectre for image compare command.

This commit is contained in:
2021-09-14 02:13:44 +01:00
parent 740a3c32ee
commit 5c670d8b1c

View File

@@ -42,6 +42,7 @@ using Aaru.CommonTypes.Interfaces;
using Aaru.Console;
using Aaru.Core;
using Aaru.Helpers;
using Spectre.Console;
using ImageInfo = Aaru.CommonTypes.Structs.ImageInfo;
namespace Aaru.Commands.Image
@@ -74,10 +75,29 @@ namespace Aaru.Commands.Image
MainClass.PrintCopyright();
if(debug)
AaruConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
{
IAnsiConsole stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
Out = new AnsiConsoleOutput(System.Console.Error)
});
AaruConsole.DebugWriteLineEvent += (format, objects) =>
{
if(objects is null)
stderrConsole.MarkupLine(format);
else
stderrConsole.MarkupLine(format, objects);
};
}
if(verbose)
AaruConsole.VerboseWriteLineEvent += System.Console.WriteLine;
AaruConsole.WriteEvent += (format, objects) =>
{
if(objects is null)
AnsiConsole.Markup(format);
else
AnsiConsole.Markup(format, objects);
};
Statistics.AddCommand("compare");
@@ -87,9 +107,22 @@ namespace Aaru.Commands.Image
AaruConsole.DebugWriteLine("Compare command", "--verbose={0}", verbose);
var filtersList = new FiltersList();
IFilter inputFilter1 = filtersList.GetFilter(imagePath1);
IFilter inputFilter1 = null;
IFilter inputFilter2 = null;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Identifying file 1 filter...").IsIndeterminate();
inputFilter1 = filtersList.GetFilter(imagePath1);
});
filtersList = new FiltersList();
IFilter inputFilter2 = filtersList.GetFilter(imagePath2);
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Identifying file 2 filter...").IsIndeterminate();
inputFilter2 = filtersList.GetFilter(imagePath2);
});
if(inputFilter1 == null)
{
@@ -105,8 +138,20 @@ namespace Aaru.Commands.Image
return (int)ErrorNumber.CannotOpenFile;
}
IMediaImage input1Format = ImageFormat.Detect(inputFilter1);
IMediaImage input2Format = ImageFormat.Detect(inputFilter2);
IMediaImage input1Format = null;
IMediaImage input2Format = null;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Identifying image 1 format...").IsIndeterminate();
input1Format = ImageFormat.Detect(inputFilter1);
});
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Identifying image 1 format...").IsIndeterminate();
input2Format = ImageFormat.Detect(inputFilter2);
});
if(input1Format == null)
{
@@ -134,8 +179,36 @@ namespace Aaru.Commands.Image
else
AaruConsole.WriteLine("Input file 2 format identified by {0}.", input2Format.Name);
input1Format.Open(inputFilter1);
input2Format.Open(inputFilter2);
bool opened1 = false;
bool opened2 = false;
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Opening image 1 file...").IsIndeterminate();
opened1 = input1Format.Open(inputFilter1);
});
if(!opened1)
{
AaruConsole.WriteLine("Unable to open image 1 format");
AaruConsole.WriteLine("No error given");
return (int)ErrorNumber.CannotOpenFormat;
}
Core.Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Opening image 2 file...").IsIndeterminate();
opened2 = input2Format.Open(inputFilter2);
});
if(!opened2)
{
AaruConsole.WriteLine("Unable to open image 1 format");
AaruConsole.WriteLine("No error given");
return (int)ErrorNumber.CannotOpenFormat;
}
Statistics.AddMediaFormat(input1Format.Format);
Statistics.AddMediaFormat(input2Format.Format);
@@ -144,32 +217,30 @@ namespace Aaru.Commands.Image
Statistics.AddFilter(inputFilter1.Name);
Statistics.AddFilter(inputFilter2.Name);
var sb = new StringBuilder();
var sb = new StringBuilder();
Table table = new();
table.AddColumn("");
table.AddColumn("Disc image 1");
table.AddColumn("Disc image 2");
table.Columns[0].RightAligned();
if(verbose)
{
sb.AppendFormat("{0,50}{1,20}", "Disc image 1", "Disc image 2").AppendLine();
sb.AppendLine("================================================================================================");
sb.AppendFormat("{0,-38}{1}", "File", imagePath1).AppendLine();
sb.AppendFormat(" {0}", imagePath2).
AppendLine();
sb.AppendFormat("{0,-38}{1,-20}{2}", "Disc image format", input1Format.Name, input2Format.Name).
AppendLine();
table.AddRow("File", Markup.Escape(imagePath1), Markup.Escape(imagePath2));
table.AddRow("Disc image format", input1Format.Name, input2Format.Name);
}
else
{
sb.AppendFormat("Disc image 1: {0}", imagePath1).AppendLine();
sb.AppendFormat("Disc image 2: {0}", imagePath2).AppendLine();
sb.AppendFormat("[bold]Disc image 1:[/] {0}", imagePath1).AppendLine();
sb.AppendFormat("[bold]Disc image 2:[/] {0}", imagePath2).AppendLine();
}
bool imagesDiffer = false;
ImageInfo image1Info = input1Format.Info;
ImageInfo image2Info = input2Format.Info;
Dictionary<MediaTagType, byte[]> image1DiskTags = new Dictionary<MediaTagType, byte[]>();
Dictionary<MediaTagType, byte[]> image2DiskTags = new Dictionary<MediaTagType, byte[]>();
Dictionary<MediaTagType, byte[]> image1DiskTags = new();
Dictionary<MediaTagType, byte[]> image2DiskTags = new();
foreach(MediaTagType diskTag in Enum.GetValues(typeof(MediaTagType)))
{
@@ -203,191 +274,189 @@ namespace Aaru.Commands.Image
if(verbose)
{
sb.AppendFormat("{0,-38}{1,-20}{2}", "Has partitions?", image1Info.HasPartitions,
image2Info.HasPartitions).AppendLine();
table.AddRow("Has partitions?", image1Info.HasPartitions.ToString(),
image2Info.HasPartitions.ToString());
sb.AppendFormat("{0,-38}{1,-20}{2}", "Has sessions?", image1Info.HasSessions, image2Info.HasSessions).
AppendLine();
table.AddRow("Has sessions?", image1Info.HasSessions.ToString(), image2Info.HasSessions.ToString());
sb.AppendFormat("{0,-38}{1,-20}{2}", "Image size", image1Info.ImageSize, image2Info.ImageSize).
AppendLine();
table.AddRow("Image size", image1Info.ImageSize.ToString(), image2Info.ImageSize.ToString());
sb.AppendFormat("{0,-38}{1,-20}{2}", "Sectors", image1Info.Sectors, image2Info.Sectors).AppendLine();
table.AddRow("Sectors", image1Info.Sectors.ToString(), image2Info.Sectors.ToString());
sb.AppendFormat("{0,-38}{1,-20}{2}", "Sector size", image1Info.SectorSize, image2Info.SectorSize).
AppendLine();
table.AddRow("Sector size", image1Info.SectorSize.ToString(), image2Info.SectorSize.ToString());
sb.AppendFormat("{0,-38}{1,-20}{2}", "Creation time", image1Info.CreationTime, image2Info.CreationTime).
AppendLine();
table.AddRow("Creation time", image1Info.CreationTime.ToString(), image2Info.CreationTime.ToString());
sb.AppendFormat("{0,-38}{1,-20}{2}", "Last modification time", image1Info.LastModificationTime,
image2Info.LastModificationTime).AppendLine();
table.AddRow("Last modification time", image1Info.LastModificationTime.ToString(),
image2Info.LastModificationTime.ToString());
sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk type", image1Info.MediaType, image2Info.MediaType).
AppendLine();
table.AddRow("Disk type", image1Info.MediaType.ToString(), image2Info.MediaType.ToString());
sb.AppendFormat("{0,-38}{1,-20}{2}", "Image version", image1Info.Version, image2Info.Version).
AppendLine();
table.AddRow("Image version", image1Info.Version ?? "", image2Info.Version ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Image application", image1Info.Application,
image2Info.Application).AppendLine();
table.AddRow("Image application", image1Info.Application ?? "", image2Info.Application ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Image application version", image1Info.ApplicationVersion,
image2Info.ApplicationVersion).AppendLine();
table.AddRow("Image application version", image1Info.ApplicationVersion ?? "",
image2Info.ApplicationVersion ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Image creator", image1Info.Creator, image2Info.Creator).
AppendLine();
table.AddRow("Image creator", image1Info.Creator ?? "", image2Info.Creator ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Image name", image1Info.MediaTitle, image2Info.MediaTitle).
AppendLine();
table.AddRow("Image name", image1Info.MediaTitle ?? "", image2Info.MediaTitle ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Image comments", image1Info.Comments, image2Info.Comments).
AppendLine();
table.AddRow("Image comments", image1Info.Comments ?? "", image2Info.Comments ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Image comments", image1Info.Comments, image2Info.Comments).
AppendLine();
table.AddRow("Disk manufacturer", image1Info.MediaManufacturer ?? "",
image2Info.MediaManufacturer ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk manufacturer", image1Info.MediaManufacturer,
image2Info.MediaManufacturer).AppendLine();
table.AddRow("Disk model", image1Info.MediaModel ?? "", image2Info.MediaModel ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk model", image1Info.MediaModel, image2Info.MediaModel).
AppendLine();
table.AddRow("Disk serial number", image1Info.MediaSerialNumber ?? "",
image2Info.MediaSerialNumber ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk serial number", image1Info.MediaSerialNumber,
image2Info.MediaSerialNumber).AppendLine();
table.AddRow("Disk barcode", image1Info.MediaBarcode ?? "", image2Info.MediaBarcode ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk barcode", image1Info.MediaBarcode, image2Info.MediaBarcode).
AppendLine();
table.AddRow("Disk part no.", image1Info.MediaPartNumber ?? "", image2Info.MediaPartNumber ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk part no.", image1Info.MediaPartNumber,
image2Info.MediaPartNumber).AppendLine();
table.AddRow("Disk sequence", image1Info.MediaSequence.ToString(), image2Info.MediaSequence.ToString());
sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk sequence", image1Info.MediaSequence,
image2Info.MediaSequence).AppendLine();
table.AddRow("Last disk on sequence", image1Info.LastMediaSequence.ToString(),
image2Info.LastMediaSequence.ToString());
sb.AppendFormat("{0,-38}{1,-20}{2}", "Last disk on sequence", image1Info.LastMediaSequence,
image2Info.LastMediaSequence).AppendLine();
table.AddRow("Drive manufacturer", image1Info.DriveManufacturer ?? "",
image2Info.DriveManufacturer ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Drive manufacturer", image1Info.DriveManufacturer,
image2Info.DriveManufacturer).AppendLine();
table.AddRow("Drive firmware revision", image1Info.DriveFirmwareRevision ?? "",
image2Info.DriveFirmwareRevision ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Drive firmware revision", image1Info.DriveFirmwareRevision,
image2Info.DriveFirmwareRevision).AppendLine();
table.AddRow("Drive model", image1Info.DriveModel ?? "", image2Info.DriveModel ?? "");
sb.AppendFormat("{0,-38}{1,-20}{2}", "Drive model", image1Info.DriveModel, image2Info.DriveModel).
AppendLine();
sb.AppendFormat("{0,-38}{1,-20}{2}", "Drive serial number", image1Info.DriveSerialNumber,
image2Info.DriveSerialNumber).AppendLine();
table.AddRow("Drive serial number", image1Info.DriveSerialNumber ?? "",
image2Info.DriveSerialNumber ?? "");
foreach(MediaTagType diskTag in
(Enum.GetValues(typeof(MediaTagType)) as MediaTagType[]).OrderBy(e => e.ToString()))
sb.AppendFormat("{0,-38}{1,-20}{2}", $"Has {diskTag}?", image1DiskTags.ContainsKey(diskTag),
image2DiskTags.ContainsKey(diskTag)).AppendLine();
table.AddRow($"Has {diskTag}?", image1DiskTags.ContainsKey(diskTag).ToString(),
image2DiskTags.ContainsKey(diskTag).ToString());
}
AaruConsole.WriteLine("Comparing disk image characteristics");
ulong leastSectors = 0;
if(image1Info.HasPartitions != image2Info.HasPartitions)
Core.Spectre.ProgressSingleSpinner(ctx =>
{
imagesDiffer = true;
ctx.AddTask("Comparing disk image characteristics").IsIndeterminate();
if(!verbose)
sb.AppendLine("Image partitioned status differ");
}
if(image1Info.HasSessions != image2Info.HasSessions)
{
imagesDiffer = true;
if(!verbose)
sb.AppendLine("Image session status differ");
}
if(image1Info.Sectors != image2Info.Sectors)
{
imagesDiffer = true;
if(!verbose)
sb.AppendLine("Image sectors differ");
}
if(image1Info.SectorSize != image2Info.SectorSize)
{
imagesDiffer = true;
if(!verbose)
sb.AppendLine("Image sector size differ");
}
if(image1Info.MediaType != image2Info.MediaType)
{
imagesDiffer = true;
if(!verbose)
sb.AppendLine("Disk type differ");
}
ulong leastSectors;
if(image1Info.Sectors < image2Info.Sectors)
{
imagesDiffer = true;
leastSectors = image1Info.Sectors;
if(!verbose)
sb.AppendLine("Image 2 has more sectors");
}
else if(image1Info.Sectors > image2Info.Sectors)
{
imagesDiffer = true;
leastSectors = image2Info.Sectors;
if(!verbose)
sb.AppendLine("Image 1 has more sectors");
}
else
leastSectors = image1Info.Sectors;
AaruConsole.WriteLine("Comparing sectors...");
for(ulong sector = 0; sector < leastSectors; sector++)
{
AaruConsole.Write("\rComparing sector {0} of {1}...", sector + 1, leastSectors);
try
if(image1Info.HasPartitions != image2Info.HasPartitions)
{
byte[] image1Sector = input1Format.ReadSector(sector);
byte[] image2Sector = input2Format.ReadSector(sector);
ArrayHelpers.CompareBytes(out bool different, out bool sameSize, image1Sector, image2Sector);
imagesDiffer = true;
if(different)
{
imagesDiffer = true;
sb.AppendFormat("Sector {0} is different", sector).AppendLine();
}
else if(!sameSize)
{
imagesDiffer = true;
sb.
AppendFormat("Sector {0} has different sizes ({1} bytes in image 1, {2} in image 2) but are otherwise identical",
sector, image1Sector.LongLength, image2Sector.LongLength).AppendLine();
}
if(!verbose)
sb.AppendLine("Image partitioned status differ");
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
if(image1Info.HasSessions != image2Info.HasSessions)
{
// ignored
imagesDiffer = true;
if(!verbose)
sb.AppendLine("Image session status differ");
}
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
}
if(image1Info.Sectors != image2Info.Sectors)
{
imagesDiffer = true;
if(!verbose)
sb.AppendLine("Image sectors differ");
}
if(image1Info.SectorSize != image2Info.SectorSize)
{
imagesDiffer = true;
if(!verbose)
sb.AppendLine("Image sector size differ");
}
if(image1Info.MediaType != image2Info.MediaType)
{
imagesDiffer = true;
if(!verbose)
sb.AppendLine("Disk type differ");
}
if(image1Info.Sectors < image2Info.Sectors)
{
imagesDiffer = true;
leastSectors = image1Info.Sectors;
if(!verbose)
sb.AppendLine("Image 2 has more sectors");
}
else if(image1Info.Sectors > image2Info.Sectors)
{
imagesDiffer = true;
leastSectors = image2Info.Sectors;
if(!verbose)
sb.AppendLine("Image 1 has more sectors");
}
else
leastSectors = image1Info.Sectors;
});
AnsiConsole.Progress().AutoClear(true).HideCompleted(true).
Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()).
Start(ctx =>
{
ProgressTask task = ctx.AddTask("Comparing sectors...");
task.MaxValue = leastSectors;
for(ulong sector = 0; sector < leastSectors; sector++)
{
task.Value = sector;
task.Description = $"Comparing sector {sector + 1} of {leastSectors}...";
try
{
byte[] image1Sector = input1Format.ReadSector(sector);
byte[] image2Sector = input2Format.ReadSector(sector);
ArrayHelpers.CompareBytes(out bool different, out bool sameSize, image1Sector,
image2Sector);
if(different)
{
imagesDiffer = true;
// sb.AppendFormat("Sector {0} is different", sector).AppendLine();
}
else if(!sameSize)
{
imagesDiffer = true;
/* sb.
AppendFormat("Sector {0} has different sizes ({1} bytes in image 1, {2} in image 2) but are otherwise identical",
sector, image1Sector.LongLength, image2Sector.LongLength).AppendLine();*/
}
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
{
// ignored
}
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
}
});
AaruConsole.WriteLine();
sb.AppendLine(imagesDiffer ? "Images differ" : "Images do not differ");
AaruConsole.WriteLine(sb.ToString());
if(verbose)
AnsiConsole.Render(table);
else
AaruConsole.WriteLine(sb.ToString());
return (int)ErrorNumber.NoError;
}