예제 #1
0
        public override int Invoke(IEnumerable <string> arguments)
        {
            List <string> extra = Options.Parse(arguments);

            if (showHelp)
            {
                Options.WriteOptionDescriptions(CommandSet.Out);
                return((int)ErrorNumber.HelpRequested);
            }

            MainClass.PrintCopyright();
            if (MainClass.Debug)
            {
                DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
            }
            if (MainClass.Verbose)
            {
                DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
            }
            Statistics.AddCommand("checksum");

            if (extra.Count > 1)
            {
                DicConsole.ErrorWriteLine("Too many arguments.");
                return((int)ErrorNumber.UnexpectedArgumentCount);
            }

            if (extra.Count == 0)
            {
                DicConsole.ErrorWriteLine("Missing input image.");
                return((int)ErrorNumber.MissingArgument);
            }

            inputFile = extra[0];

            DicConsole.DebugWriteLine("Checksum command", "--adler32={0}", doAdler32);
            DicConsole.DebugWriteLine("Checksum command", "--crc16={0}", doCrc16);
            DicConsole.DebugWriteLine("Checksum command", "--crc32={0}", doCrc32);
            DicConsole.DebugWriteLine("Checksum command", "--crc64={0}", doCrc64);
            DicConsole.DebugWriteLine("Checksum command", "--debug={0}", MainClass.Debug);
            DicConsole.DebugWriteLine("Checksum command", "--fletcher16={0}", doFletcher16);
            DicConsole.DebugWriteLine("Checksum command", "--fletcher32={0}", doFletcher32);
            DicConsole.DebugWriteLine("Checksum command", "--input={0}", inputFile);
            DicConsole.DebugWriteLine("Checksum command", "--md5={0}", doMd5);
            DicConsole.DebugWriteLine("Checksum command", "--ripemd160={0}", doRipemd160);
            DicConsole.DebugWriteLine("Checksum command", "--separated-tracks={0}", separatedTracks);
            DicConsole.DebugWriteLine("Checksum command", "--sha1={0}", doSha1);
            DicConsole.DebugWriteLine("Checksum command", "--sha256={0}", doSha256);
            DicConsole.DebugWriteLine("Checksum command", "--sha384={0}", doSha384);
            DicConsole.DebugWriteLine("Checksum command", "--sha512={0}", doSha512);
            DicConsole.DebugWriteLine("Checksum command", "--spamsum={0}", doSpamSum);
            DicConsole.DebugWriteLine("Checksum command", "--verbose={0}", MainClass.Verbose);
            DicConsole.DebugWriteLine("Checksum command", "--whole-disc={0}", wholeDisc);

            FiltersList filtersList = new FiltersList();
            IFilter     inputFilter = filtersList.GetFilter(inputFile);

            if (inputFilter == null)
            {
                DicConsole.ErrorWriteLine("Cannot open specified file.");
                return((int)ErrorNumber.CannotOpenFile);
            }

            IMediaImage inputFormat = ImageFormat.Detect(inputFilter);

            if (inputFormat == null)
            {
                DicConsole.ErrorWriteLine("Unable to recognize image format, not checksumming");
                return((int)ErrorNumber.UnrecognizedFormat);
            }

            inputFormat.Open(inputFilter);
            Statistics.AddMediaFormat(inputFormat.Format);
            Statistics.AddMedia(inputFormat.Info.MediaType, false);
            Statistics.AddFilter(inputFilter.Name);
            EnableChecksum enabledChecksums = new EnableChecksum();

            if (doAdler32)
            {
                enabledChecksums |= EnableChecksum.Adler32;
            }
            if (doCrc16)
            {
                enabledChecksums |= EnableChecksum.Crc16;
            }
            if (doCrc32)
            {
                enabledChecksums |= EnableChecksum.Crc32;
            }
            if (doCrc64)
            {
                enabledChecksums |= EnableChecksum.Crc64;
            }
            if (doMd5)
            {
                enabledChecksums |= EnableChecksum.Md5;
            }
            if (doRipemd160)
            {
                enabledChecksums |= EnableChecksum.Ripemd160;
            }
            if (doSha1)
            {
                enabledChecksums |= EnableChecksum.Sha1;
            }
            if (doSha256)
            {
                enabledChecksums |= EnableChecksum.Sha256;
            }
            if (doSha384)
            {
                enabledChecksums |= EnableChecksum.Sha384;
            }
            if (doSha512)
            {
                enabledChecksums |= EnableChecksum.Sha512;
            }
            if (doSpamSum)
            {
                enabledChecksums |= EnableChecksum.SpamSum;
            }
            if (doFletcher16)
            {
                enabledChecksums |= EnableChecksum.Fletcher16;
            }
            if (doFletcher32)
            {
                enabledChecksums |= EnableChecksum.Fletcher32;
            }

            Checksum mediaChecksum = null;

            switch (inputFormat)
            {
            case IOpticalMediaImage opticalInput when opticalInput.Tracks != null:
                try
                {
                    Checksum trackChecksum = null;

                    if (wholeDisc)
                    {
                        mediaChecksum = new Checksum(enabledChecksums);
                    }

                    ulong previousTrackEnd = 0;

                    List <Track> inputTracks = opticalInput.Tracks;
                    foreach (Track currentTrack in inputTracks)
                    {
                        if (currentTrack.TrackStartSector - previousTrackEnd != 0 && wholeDisc)
                        {
                            for (ulong i = previousTrackEnd + 1; i < currentTrack.TrackStartSector; i++)
                            {
                                DicConsole.Write("\rHashing track-less sector {0}", i);

                                byte[] hiddenSector = inputFormat.ReadSector(i);

                                mediaChecksum?.Update(hiddenSector);
                            }
                        }

                        DicConsole.DebugWriteLine("Checksum command",
                                                  "Track {0} starts at sector {1} and ends at sector {2}",
                                                  currentTrack.TrackSequence, currentTrack.TrackStartSector,
                                                  currentTrack.TrackEndSector);

                        if (separatedTracks)
                        {
                            trackChecksum = new Checksum(enabledChecksums);
                        }

                        ulong sectors     = currentTrack.TrackEndSector - currentTrack.TrackStartSector + 1;
                        ulong doneSectors = 0;
                        DicConsole.WriteLine("Track {0} has {1} sectors", currentTrack.TrackSequence, sectors);

                        while (doneSectors < sectors)
                        {
                            byte[] sector;

                            if (sectors - doneSectors >= SECTORS_TO_READ)
                            {
                                sector = opticalInput.ReadSectors(doneSectors, SECTORS_TO_READ,
                                                                  currentTrack.TrackSequence);
                                DicConsole.Write("\rHashings sectors {0} to {2} of track {1}", doneSectors,
                                                 currentTrack.TrackSequence, doneSectors + SECTORS_TO_READ);
                                doneSectors += SECTORS_TO_READ;
                            }
                            else
                            {
                                sector = opticalInput.ReadSectors(doneSectors, (uint)(sectors - doneSectors),
                                                                  currentTrack.TrackSequence);
                                DicConsole.Write("\rHashings sectors {0} to {2} of track {1}", doneSectors,
                                                 currentTrack.TrackSequence, doneSectors + (sectors - doneSectors));
                                doneSectors += sectors - doneSectors;
                            }

                            if (wholeDisc)
                            {
                                mediaChecksum?.Update(sector);
                            }

                            if (separatedTracks)
                            {
                                trackChecksum?.Update(sector);
                            }
                        }

                        DicConsole.WriteLine();

                        if (separatedTracks)
                        {
                            if (trackChecksum != null)
                            {
                                foreach (ChecksumType chk in trackChecksum.End())
                                {
                                    DicConsole.WriteLine("Track {0}'s {1}: {2}", currentTrack.TrackSequence,
                                                         chk.type, chk.Value);
                                }
                            }
                        }

                        previousTrackEnd = currentTrack.TrackEndSector;
                    }

                    if (opticalInput.Info.Sectors - previousTrackEnd != 0 && wholeDisc)
                    {
                        for (ulong i = previousTrackEnd + 1; i < opticalInput.Info.Sectors; i++)
                        {
                            DicConsole.Write("\rHashing track-less sector {0}", i);

                            byte[] hiddenSector = inputFormat.ReadSector(i);
                            mediaChecksum?.Update(hiddenSector);
                        }
                    }

                    if (wholeDisc)
                    {
                        if (mediaChecksum != null)
                        {
                            foreach (ChecksumType chk in mediaChecksum.End())
                            {
                                DicConsole.WriteLine("Disk's {0}: {1}", chk.type, chk.Value);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    if (MainClass.Debug)
                    {
                        DicConsole.DebugWriteLine("Could not get tracks because {0}", ex.Message);
                    }
                    else
                    {
                        DicConsole.WriteLine("Unable to get separate tracks, not checksumming them");
                    }
                }

                break;

            case ITapeImage tapeImage when tapeImage.IsTape && tapeImage.Files?.Count > 0:
            {
                Checksum trackChecksum = null;

                if (wholeDisc)
                {
                    mediaChecksum = new Checksum(enabledChecksums);
                }

                ulong previousTrackEnd = 0;

                foreach (TapeFile currentFile in tapeImage.Files)
                {
                    if (currentFile.FirstBlock - previousTrackEnd != 0 && wholeDisc)
                    {
                        for (ulong i = previousTrackEnd + 1; i < currentFile.FirstBlock; i++)
                        {
                            DicConsole.Write("\rHashing file-less block {0}", i);

                            byte[] hiddenSector = inputFormat.ReadSector(i);

                            mediaChecksum?.Update(hiddenSector);
                        }
                    }

                    DicConsole.DebugWriteLine("Checksum command",
                                              "Track {0} starts at sector {1} and ends at block {2}",
                                              currentFile.File, currentFile.FirstBlock, currentFile.LastBlock);

                    if (separatedTracks)
                    {
                        trackChecksum = new Checksum(enabledChecksums);
                    }

                    ulong sectors     = currentFile.LastBlock - currentFile.FirstBlock + 1;
                    ulong doneSectors = 0;
                    DicConsole.WriteLine("File {0} has {1} sectors", currentFile.File, sectors);

                    while (doneSectors < sectors)
                    {
                        byte[] sector;

                        if (sectors - doneSectors >= SECTORS_TO_READ)
                        {
                            sector = tapeImage.ReadSectors(doneSectors + currentFile.FirstBlock, SECTORS_TO_READ);
                            DicConsole.Write("\rHashings blocks {0} to {2} of file {1}", doneSectors,
                                             currentFile.File, doneSectors + SECTORS_TO_READ);
                            doneSectors += SECTORS_TO_READ;
                        }
                        else
                        {
                            sector = tapeImage.ReadSectors(doneSectors + currentFile.FirstBlock,
                                                           (uint)(sectors - doneSectors));
                            DicConsole.Write("\rHashings blocks {0} to {2} of file {1}", doneSectors,
                                             currentFile.File, doneSectors + (sectors - doneSectors));
                            doneSectors += sectors - doneSectors;
                        }

                        if (wholeDisc)
                        {
                            mediaChecksum?.Update(sector);
                        }

                        if (separatedTracks)
                        {
                            trackChecksum?.Update(sector);
                        }
                    }

                    DicConsole.WriteLine();

                    if (separatedTracks)
                    {
                        if (trackChecksum != null)
                        {
                            foreach (ChecksumType chk in trackChecksum.End())
                            {
                                DicConsole.WriteLine("File {0}'s {1}: {2}", currentFile.File, chk.type, chk.Value);
                            }
                        }
                    }

                    previousTrackEnd = currentFile.LastBlock;
                }

                if (tapeImage.Info.Sectors - previousTrackEnd != 0 && wholeDisc)
                {
                    for (ulong i = previousTrackEnd + 1; i < tapeImage.Info.Sectors; i++)
                    {
                        DicConsole.Write("\rHashing file-less sector {0}", i);

                        byte[] hiddenSector = inputFormat.ReadSector(i);
                        mediaChecksum?.Update(hiddenSector);
                    }
                }

                if (wholeDisc)
                {
                    if (mediaChecksum != null)
                    {
                        foreach (ChecksumType chk in mediaChecksum.End())
                        {
                            DicConsole.WriteLine("Tape's {0}: {1}", chk.type, chk.Value);
                        }
                    }
                }
                break;
            }

            default:
            {
                mediaChecksum = new Checksum(enabledChecksums);

                ulong sectors = inputFormat.Info.Sectors;
                DicConsole.WriteLine("Sectors {0}", sectors);
                ulong doneSectors = 0;

                while (doneSectors < sectors)
                {
                    byte[] sector;

                    if (sectors - doneSectors >= SECTORS_TO_READ)
                    {
                        sector = inputFormat.ReadSectors(doneSectors, SECTORS_TO_READ);
                        DicConsole.Write("\rHashings sectors {0} to {1}", doneSectors,
                                         doneSectors + SECTORS_TO_READ);
                        doneSectors += SECTORS_TO_READ;
                    }
                    else
                    {
                        sector = inputFormat.ReadSectors(doneSectors, (uint)(sectors - doneSectors));
                        DicConsole.Write("\rHashings sectors {0} to {1}", doneSectors,
                                         doneSectors + (sectors - doneSectors));
                        doneSectors += sectors - doneSectors;
                    }

                    mediaChecksum.Update(sector);
                }

                DicConsole.WriteLine();

                foreach (ChecksumType chk in mediaChecksum.End())
                {
                    DicConsole.WriteLine("Disk's {0}: {1}", chk.type, chk.Value);
                }
                break;
            }
            }

            return((int)ErrorNumber.NoError);
        }
예제 #2
0
        public override int Invoke(IEnumerable <string> arguments)
        {
            List <string> extra = Options.Parse(arguments);

            if (showHelp)
            {
                Options.WriteOptionDescriptions(CommandSet.Out);
                return((int)ErrorNumber.HelpRequested);
            }

            MainClass.PrintCopyright();
            if (MainClass.Debug)
            {
                DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
            }
            if (MainClass.Verbose)
            {
                DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
            }
            Statistics.AddCommand("compare");

            if (extra.Count > 2)
            {
                DicConsole.ErrorWriteLine("Too many arguments.");
                return((int)ErrorNumber.UnexpectedArgumentCount);
            }

            if (extra.Count <= 1)
            {
                DicConsole.ErrorWriteLine("Missing input image.");
                return((int)ErrorNumber.MissingArgument);
            }

            inputFile1 = extra[0];
            inputFile2 = extra[1];

            DicConsole.DebugWriteLine("Compare command", "--debug={0}", MainClass.Debug);
            DicConsole.DebugWriteLine("Compare command", "--input1={0}", inputFile1);
            DicConsole.DebugWriteLine("Compare command", "--input2={0}", inputFile2);
            DicConsole.DebugWriteLine("Compare command", "--verbose={0}", MainClass.Verbose);

            FiltersList filtersList  = new FiltersList();
            IFilter     inputFilter1 = filtersList.GetFilter(inputFile1);

            filtersList = new FiltersList();
            IFilter inputFilter2 = filtersList.GetFilter(inputFile2);

            if (inputFilter1 == null)
            {
                DicConsole.ErrorWriteLine("Cannot open input file 1");
                return((int)ErrorNumber.CannotOpenFile);
            }

            if (inputFilter2 == null)
            {
                DicConsole.ErrorWriteLine("Cannot open input file 2");
                return((int)ErrorNumber.CannotOpenFile);
            }

            IMediaImage input1Format = ImageFormat.Detect(inputFilter1);
            IMediaImage input2Format = ImageFormat.Detect(inputFilter2);

            if (input1Format == null)
            {
                DicConsole.ErrorWriteLine("Input file 1 format not identified, not proceeding with comparison.");
                return((int)ErrorNumber.UnrecognizedFormat);
            }

            if (MainClass.Verbose)
            {
                DicConsole.VerboseWriteLine("Input file 1 format identified by {0} ({1}).", input1Format.Name,
                                            input1Format.Id);
            }
            else
            {
                DicConsole.WriteLine("Input file 1 format identified by {0}.", input1Format.Name);
            }

            if (input2Format == null)
            {
                DicConsole.ErrorWriteLine("Input file 2 format not identified, not proceeding with comparison.");
                return((int)ErrorNumber.UnrecognizedFormat);
            }

            if (MainClass.Verbose)
            {
                DicConsole.VerboseWriteLine("Input file 2 format identified by {0} ({1}).", input2Format.Name,
                                            input2Format.Id);
            }
            else
            {
                DicConsole.WriteLine("Input file 2 format identified by {0}.", input2Format.Name);
            }

            input1Format.Open(inputFilter1);
            input2Format.Open(inputFilter2);

            Statistics.AddMediaFormat(input1Format.Format);
            Statistics.AddMediaFormat(input2Format.Format);
            Statistics.AddMedia(input1Format.Info.MediaType, false);
            Statistics.AddMedia(input2Format.Info.MediaType, false);
            Statistics.AddFilter(inputFilter1.Name);
            Statistics.AddFilter(inputFilter2.Name);

            StringBuilder sb = new StringBuilder();

            if (MainClass.Verbose)
            {
                sb.AppendLine("\tDisc image 1\tDisc image 2");
                sb.AppendLine("================================");
                sb.AppendFormat("File\t{0}\t{1}", inputFile1, inputFile2).AppendLine();
                sb.AppendFormat("Disc image format\t{0}\t{1}", input1Format.Name, input2Format.Name).AppendLine();
            }
            else
            {
                sb.AppendFormat("Disc image 1: {0}", inputFile1).AppendLine();
                sb.AppendFormat("Disc image 2: {0}", inputFile2).AppendLine();
            }

            bool imagesDiffer = false;

            ImageInfo      image1Info     = input1Format.Info;
            ImageInfo      image2Info     = input2Format.Info;
            List <Session> image1Sessions = new List <Session>();
            List <Session> image2Sessions = new List <Session>();
            Dictionary <MediaTagType, byte[]> image1DiskTags = new Dictionary <MediaTagType, byte[]>();
            Dictionary <MediaTagType, byte[]> image2DiskTags = new Dictionary <MediaTagType, byte[]>();

            foreach (MediaTagType disktag in Enum.GetValues(typeof(MediaTagType)))
            {
                try
                {
                    byte[] temparray = input1Format.ReadDiskTag(disktag);
                    image1DiskTags.Add(disktag, temparray);
                }
                #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
            }

            foreach (MediaTagType disktag in Enum.GetValues(typeof(MediaTagType)))
            {
                try
                {
                    byte[] temparray = input2Format.ReadDiskTag(disktag);
                    image2DiskTags.Add(disktag, temparray);
                }
                #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
            }

            if (MainClass.Verbose)
            {
                sb.AppendFormat("Has partitions?\t{0}\t{1}", image1Info.HasPartitions, image2Info.HasPartitions)
                .AppendLine();
                sb.AppendFormat("Has sessions?\t{0}\t{1}", image1Info.HasSessions, image2Info.HasSessions).AppendLine();
                sb.AppendFormat("Image size\t{0}\t{1}", image1Info.ImageSize, image2Info.ImageSize).AppendLine();
                sb.AppendFormat("Sectors\t{0}\t{1}", image1Info.Sectors, image2Info.Sectors).AppendLine();
                sb.AppendFormat("Sector size\t{0}\t{1}", image1Info.SectorSize, image2Info.SectorSize).AppendLine();
                sb.AppendFormat("Creation time\t{0}\t{1}", image1Info.CreationTime, image2Info.CreationTime)
                .AppendLine();
                sb.AppendFormat("Last modification time\t{0}\t{1}", image1Info.LastModificationTime,
                                image2Info.LastModificationTime).AppendLine();
                sb.AppendFormat("Disk type\t{0}\t{1}", image1Info.MediaType, image2Info.MediaType).AppendLine();
                sb.AppendFormat("Image version\t{0}\t{1}", image1Info.Version, image2Info.Version).AppendLine();
                sb.AppendFormat("Image application\t{0}\t{1}", image1Info.Application, image2Info.Application)
                .AppendLine();
                sb.AppendFormat("Image application version\t{0}\t{1}", image1Info.ApplicationVersion,
                                image2Info.ApplicationVersion).AppendLine();
                sb.AppendFormat("Image creator\t{0}\t{1}", image1Info.Creator, image2Info.Creator).AppendLine();
                sb.AppendFormat("Image name\t{0}\t{1}", image1Info.MediaTitle, image2Info.MediaTitle).AppendLine();
                sb.AppendFormat("Image comments\t{0}\t{1}", image1Info.Comments, image2Info.Comments).AppendLine();
                sb.AppendFormat("Disk manufacturer\t{0}\t{1}", image1Info.MediaManufacturer,
                                image2Info.MediaManufacturer).AppendLine();
                sb.AppendFormat("Disk model\t{0}\t{1}", image1Info.MediaModel, image2Info.MediaModel).AppendLine();
                sb.AppendFormat("Disk serial number\t{0}\t{1}", image1Info.MediaSerialNumber,
                                image2Info.MediaSerialNumber).AppendLine();
                sb.AppendFormat("Disk barcode\t{0}\t{1}", image1Info.MediaBarcode, image2Info.MediaBarcode)
                .AppendLine();
                sb.AppendFormat("Disk part no.\t{0}\t{1}", image1Info.MediaPartNumber, image2Info.MediaPartNumber)
                .AppendLine();
                sb.AppendFormat("Disk sequence\t{0}\t{1}", image1Info.MediaSequence, image2Info.MediaSequence)
                .AppendLine();
                sb.AppendFormat("Last disk on sequence\t{0}\t{1}", image1Info.LastMediaSequence,
                                image2Info.LastMediaSequence).AppendLine();
                sb.AppendFormat("Drive manufacturer\t{0}\t{1}", image1Info.DriveManufacturer,
                                image2Info.DriveManufacturer).AppendLine();
                sb.AppendFormat("Drive firmware revision\t{0}\t{1}", image1Info.DriveFirmwareRevision,
                                image2Info.DriveFirmwareRevision).AppendLine();
                sb.AppendFormat("Drive model\t{0}\t{1}", image1Info.DriveModel, image2Info.DriveModel).AppendLine();
                sb.AppendFormat("Drive serial number\t{0}\t{1}", image1Info.DriveSerialNumber,
                                image2Info.DriveSerialNumber).AppendLine();
                foreach (MediaTagType disktag in Enum.GetValues(typeof(MediaTagType)))
                {
                    sb.AppendFormat("Has {0}?\t{1}\t{2}", disktag, image1DiskTags.ContainsKey(disktag),
                                    image2DiskTags.ContainsKey(disktag)).AppendLine();
                }
            }

            DicConsole.WriteLine("Comparing disk image characteristics");

            if (image1Info.HasPartitions != image2Info.HasPartitions)
            {
                imagesDiffer = true;
                if (!MainClass.Verbose)
                {
                    sb.AppendLine("Image partitioned status differ");
                }
            }

            if (image1Info.HasSessions != image2Info.HasSessions)
            {
                imagesDiffer = true;
                if (!MainClass.Verbose)
                {
                    sb.AppendLine("Image session status differ");
                }
            }

            if (image1Info.Sectors != image2Info.Sectors)
            {
                imagesDiffer = true;
                if (!MainClass.Verbose)
                {
                    sb.AppendLine("Image sectors differ");
                }
            }

            if (image1Info.SectorSize != image2Info.SectorSize)
            {
                imagesDiffer = true;
                if (!MainClass.Verbose)
                {
                    sb.AppendLine("Image sector size differ");
                }
            }

            if (image1Info.MediaType != image2Info.MediaType)
            {
                imagesDiffer = true;
                if (!MainClass.Verbose)
                {
                    sb.AppendLine("Disk type differ");
                }
            }

            ulong leastSectors;
            if (image1Info.Sectors < image2Info.Sectors)
            {
                imagesDiffer = true;
                leastSectors = image1Info.Sectors;
                if (!MainClass.Verbose)
                {
                    sb.AppendLine("Image 2 has more sectors");
                }
            }
            else if (image1Info.Sectors > image2Info.Sectors)
            {
                imagesDiffer = true;
                leastSectors = image2Info.Sectors;
                if (!MainClass.Verbose)
                {
                    sb.AppendLine("Image 1 has more sectors");
                }
            }
            else
            {
                leastSectors = image1Info.Sectors;
            }

            DicConsole.WriteLine("Comparing sectors...");

            for (ulong sector = 0; sector < leastSectors; sector++)
            {
                DicConsole.Write("\rComparing sector {0} of {1}...", sector + 1, 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
            }

            DicConsole.WriteLine();

            sb.AppendLine(imagesDiffer ? "Images differ" : "Images do not differ");

            DicConsole.WriteLine(sb.ToString());

            return((int)ErrorNumber.NoError);
        }