Example #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("convert-image");

            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);
            }

            inputFile  = extra[0];
            outputFile = extra[1];

            DicConsole.DebugWriteLine("Analyze command", "--cicm-xml={0}", cicmXml);
            DicConsole.DebugWriteLine("Analyze command", "--comments={0}", comments);
            DicConsole.DebugWriteLine("Analyze command", "--count={0}", count);
            DicConsole.DebugWriteLine("Analyze command", "--creator={0}", creator);
            DicConsole.DebugWriteLine("Analyze command", "--debug={0}", MainClass.Debug);
            DicConsole.DebugWriteLine("Analyze command", "--drive-manufacturer={0}", driveManufacturer);
            DicConsole.DebugWriteLine("Analyze command", "--drive-model={0}", driveModel);
            DicConsole.DebugWriteLine("Analyze command", "--drive-revision={0}", driveFirmwareRevision);
            DicConsole.DebugWriteLine("Analyze command", "--drive-serial={0}", driveSerialNumber);
            DicConsole.DebugWriteLine("Analyze command", "--force={0}", force);
            DicConsole.DebugWriteLine("Analyze command", "--format={0}", wantedOutputFormat);
            DicConsole.DebugWriteLine("Analyze command", "--input={0}", inputFile);
            DicConsole.DebugWriteLine("Analyze command", "--media-barcode={0}", mediaBarcode);
            DicConsole.DebugWriteLine("Analyze command", "--media-lastsequence={0}", lastMediaSequence);
            DicConsole.DebugWriteLine("Analyze command", "--media-manufacturer={0}", mediaManufacturer);
            DicConsole.DebugWriteLine("Analyze command", "--media-model={0}", mediaModel);
            DicConsole.DebugWriteLine("Analyze command", "--media-partnumber={0}", mediaPartNumber);
            DicConsole.DebugWriteLine("Analyze command", "--media-sequence={0}", mediaSequence);
            DicConsole.DebugWriteLine("Analyze command", "--media-serial={0}", mediaSerialNumber);
            DicConsole.DebugWriteLine("Analyze command", "--media-title={0}", mediaTitle);
            DicConsole.DebugWriteLine("Analyze command", "--options={0}", outputOptions);
            DicConsole.DebugWriteLine("Analyze command", "--output={0}", outputFile);
            DicConsole.DebugWriteLine("Analyze command", "--resume-file={0}", resumeFile);
            DicConsole.DebugWriteLine("Analyze command", "--verbose={0}", MainClass.Verbose);

            Dictionary <string, string> parsedOptions = Core.Options.Parse(outputOptions);

            DicConsole.DebugWriteLine("Analyze command", "Parsed options:");
            foreach (KeyValuePair <string, string> parsedOption in parsedOptions)
            {
                DicConsole.DebugWriteLine("Analyze command", "{0} = {1}", parsedOption.Key, parsedOption.Value);
            }

            if (count == 0)
            {
                DicConsole.ErrorWriteLine("Need to specify more than 0 sectors to copy at once");
                return((int)ErrorNumber.InvalidArgument);
            }

            Resume           resume  = null;
            CICMMetadataType sidecar = null;

            XmlSerializer xs = new XmlSerializer(typeof(CICMMetadataType));

            if (cicmXml != null)
            {
                if (File.Exists(cicmXml))
                {
                    try
                    {
                        StreamReader sr = new StreamReader(cicmXml);
                        sidecar = (CICMMetadataType)xs.Deserialize(sr);
                        sr.Close();
                    }
                    catch
                    {
                        DicConsole.ErrorWriteLine("Incorrect metadata sidecar file, not continuing...");
                        return((int)ErrorNumber.InvalidSidecar);
                    }
                }
                else
                {
                    DicConsole.ErrorWriteLine("Could not find metadata sidecar, not continuing...");
                    return((int)ErrorNumber.FileNotFound);
                }
            }

            xs = new XmlSerializer(typeof(Resume));
            if (resumeFile != null)
            {
                if (File.Exists(resumeFile))
                {
                    try
                    {
                        StreamReader sr = new StreamReader(resumeFile);
                        resume = (Resume)xs.Deserialize(sr);
                        sr.Close();
                    }
                    catch
                    {
                        DicConsole.ErrorWriteLine("Incorrect resume file, not continuing...");
                        return((int)ErrorNumber.InvalidResume);
                    }
                }
                else
                {
                    DicConsole.ErrorWriteLine("Could not find resume file, not continuing...");
                    return((int)ErrorNumber.FileNotFound);
                }
            }

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

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

            if (File.Exists(outputFile))
            {
                DicConsole.ErrorWriteLine("Output file already exists, not continuing.");
                return((int)ErrorNumber.DestinationExists);
            }

            PluginBase  plugins     = GetPluginBase.Instance;
            IMediaImage inputFormat = ImageFormat.Detect(inputFilter);

            if (inputFormat == null)
            {
                DicConsole.WriteLine("Input image format not identified, not proceeding with conversion.");
                return((int)ErrorNumber.UnrecognizedFormat);
            }

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

            try
            {
                if (!inputFormat.Open(inputFilter))
                {
                    DicConsole.WriteLine("Unable to open image format");
                    DicConsole.WriteLine("No error given");
                    return((int)ErrorNumber.CannotOpenFormat);
                }

                DicConsole.DebugWriteLine("Convert-image command", "Correctly opened image file.");
                DicConsole.DebugWriteLine("Convert-image command", "Image without headers is {0} bytes.",
                                          inputFormat.Info.ImageSize);
                DicConsole.DebugWriteLine("Convert-image command", "Image has {0} sectors.", inputFormat.Info.Sectors);
                DicConsole.DebugWriteLine("Convert-image command", "Image identifies media type as {0}.",
                                          inputFormat.Info.MediaType);

                Statistics.AddMediaFormat(inputFormat.Format);
                Statistics.AddMedia(inputFormat.Info.MediaType, false);
                Statistics.AddFilter(inputFilter.Name);
            }
            catch (Exception ex)
            {
                DicConsole.ErrorWriteLine("Unable to open image format");
                DicConsole.ErrorWriteLine("Error: {0}", ex.Message);
                DicConsole.DebugWriteLine("Convert-image command", "Stack trace: {0}", ex.StackTrace);
                return((int)ErrorNumber.CannotOpenFormat);
            }

            List <IWritableImage> candidates = new List <IWritableImage>();

            // Try extension
            if (string.IsNullOrEmpty(wantedOutputFormat))
            {
                candidates.AddRange(plugins.WritableImages.Values.Where(t =>
                                                                        t.KnownExtensions
                                                                        .Contains(Path.GetExtension(outputFile))));
            }
            // Try Id
            else if (Guid.TryParse(wantedOutputFormat, out Guid outId))
            {
                candidates.AddRange(plugins.WritableImages.Values.Where(t => t.Id.Equals(outId)));
            }
            // Try name
            else
            {
                candidates.AddRange(plugins.WritableImages.Values.Where(t => string.Equals(t.Name, wantedOutputFormat,
                                                                                           StringComparison
                                                                                           .InvariantCultureIgnoreCase)));
            }

            if (candidates.Count == 0)
            {
                DicConsole.WriteLine("No plugin supports requested extension.");
                return((int)ErrorNumber.FormatNotFound);
            }

            if (candidates.Count > 1)
            {
                DicConsole.WriteLine("More than one plugin supports requested extension.");
                return((int)ErrorNumber.TooManyFormats);
            }

            IWritableImage outputFormat = candidates[0];

            if (MainClass.Verbose)
            {
                DicConsole.VerboseWriteLine("Output image format: {0} ({1}).", outputFormat.Name, outputFormat.Id);
            }
            else
            {
                DicConsole.WriteLine("Output image format: {0}.", outputFormat.Name);
            }

            if (!outputFormat.SupportedMediaTypes.Contains(inputFormat.Info.MediaType))
            {
                DicConsole.ErrorWriteLine("Output format does not support media type, cannot continue...");
                return((int)ErrorNumber.UnsupportedMedia);
            }

            foreach (MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags)
            {
                if (outputFormat.SupportedMediaTags.Contains(mediaTag) || force)
                {
                    continue;
                }

                DicConsole.ErrorWriteLine("Converting image will lose media tag {0}, not continuing...", mediaTag);
                DicConsole.ErrorWriteLine("If you don't care, use force option.");
                return((int)ErrorNumber.DataWillBeLost);
            }

            bool useLong = inputFormat.Info.ReadableSectorTags.Count != 0;

            foreach (SectorTagType sectorTag in inputFormat.Info.ReadableSectorTags)
            {
                if (outputFormat.SupportedSectorTags.Contains(sectorTag))
                {
                    continue;
                }

                if (force)
                {
                    if (sectorTag != SectorTagType.CdTrackFlags && sectorTag != SectorTagType.CdTrackIsrc &&
                        sectorTag != SectorTagType.CdSectorSubchannel)
                    {
                        useLong = false;
                    }
                    continue;
                }

                DicConsole.ErrorWriteLine("Converting image will lose sector tag {0}, not continuing...", sectorTag);
                DicConsole
                .ErrorWriteLine("If you don't care, use force option. This will skip all sector tags converting only user data.");
                return((int)ErrorNumber.DataWillBeLost);
            }

            if (!outputFormat.Create(outputFile, inputFormat.Info.MediaType, parsedOptions, inputFormat.Info.Sectors,
                                     inputFormat.Info.SectorSize))
            {
                DicConsole.ErrorWriteLine("Error {0} creating output image.", outputFormat.ErrorMessage);
                return((int)ErrorNumber.CannotCreateFormat);
            }

            ImageInfo metadata = new ImageInfo
            {
                Application           = "DiscImageChef",
                ApplicationVersion    = Version.GetVersion(),
                Comments              = comments ?? inputFormat.Info.Comments,
                Creator               = creator ?? inputFormat.Info.Creator,
                DriveFirmwareRevision = driveFirmwareRevision ?? inputFormat.Info.DriveFirmwareRevision,
                DriveManufacturer     = driveManufacturer ?? inputFormat.Info.DriveManufacturer,
                DriveModel            = driveModel ?? inputFormat.Info.DriveModel,
                DriveSerialNumber     = driveSerialNumber ?? inputFormat.Info.DriveSerialNumber,
                LastMediaSequence     = lastMediaSequence != 0 ? lastMediaSequence : inputFormat.Info.LastMediaSequence,
                MediaBarcode          = mediaBarcode ?? inputFormat.Info.MediaBarcode,
                MediaManufacturer     = mediaManufacturer ?? inputFormat.Info.MediaManufacturer,
                MediaModel            = mediaModel ?? inputFormat.Info.MediaModel,
                MediaPartNumber       = mediaPartNumber ?? inputFormat.Info.MediaPartNumber,
                MediaSequence         = mediaSequence != 0 ? mediaSequence : inputFormat.Info.MediaSequence,
                MediaSerialNumber     = mediaSerialNumber ?? inputFormat.Info.MediaSerialNumber,
                MediaTitle            = mediaTitle ?? inputFormat.Info.MediaTitle
            };

            if (!outputFormat.SetMetadata(metadata))
            {
                DicConsole.ErrorWrite("Error {0} setting metadata, ", outputFormat.ErrorMessage);
                if (!force)
                {
                    DicConsole.ErrorWriteLine("not continuing...");
                    return((int)ErrorNumber.WriteError);
                }

                DicConsole.ErrorWriteLine("continuing...");
            }

            CICMMetadataType        cicmMetadata = inputFormat.CicmMetadata;
            List <DumpHardwareType> dumpHardware = inputFormat.DumpHardware;

            foreach (MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags)
            {
                if (force && !outputFormat.SupportedMediaTags.Contains(mediaTag))
                {
                    continue;
                }

                DicConsole.WriteLine("Converting media tag {0}", mediaTag);
                byte[] tag = inputFormat.ReadDiskTag(mediaTag);
                if (outputFormat.WriteMediaTag(tag, mediaTag))
                {
                    continue;
                }

                if (force)
                {
                    DicConsole.ErrorWriteLine("Error {0} writing media tag, continuing...", outputFormat.ErrorMessage);
                }
                else
                {
                    DicConsole.ErrorWriteLine("Error {0} writing media tag, not continuing...",
                                              outputFormat.ErrorMessage);
                    return((int)ErrorNumber.WriteError);
                }
            }

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

            if (inputFormat is IOpticalMediaImage inputOptical && outputFormat is IWritableOpticalImage outputOptical &&
                inputOptical.Tracks != null)
            {
                if (!outputOptical.SetTracks(inputOptical.Tracks))
                {
                    DicConsole.ErrorWriteLine("Error {0} sending tracks list to output image.",
                                              outputFormat.ErrorMessage);
                    return((int)ErrorNumber.WriteError);
                }

                foreach (Track track in inputOptical.Tracks)
                {
                    doneSectors = 0;
                    ulong trackSectors = track.TrackEndSector - track.TrackStartSector + 1;

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

                        uint sectorsToDo;
                        if (trackSectors - doneSectors >= (ulong)count)
                        {
                            sectorsToDo = (uint)count;
                        }
                        else
                        {
                            sectorsToDo = (uint)(trackSectors - doneSectors);
                        }

                        DicConsole.Write("\rConverting sectors {0} to {1} in track {3} ({2:P2} done)",
                                         doneSectors + track.TrackStartSector,
                                         doneSectors + sectorsToDo + track.TrackStartSector,
                                         (doneSectors + track.TrackStartSector) / (double)inputFormat.Info.Sectors,
                                         track.TrackSequence);

                        bool result;
                        if (useLong)
                        {
                            if (sectorsToDo == 1)
                            {
                                sector = inputFormat.ReadSectorLong(doneSectors + track.TrackStartSector);
                                result = outputFormat.WriteSectorLong(sector, doneSectors + track.TrackStartSector);
                            }
                            else
                            {
                                sector = inputFormat.ReadSectorsLong(doneSectors + track.TrackStartSector, sectorsToDo);
                                result = outputFormat.WriteSectorsLong(sector, doneSectors + track.TrackStartSector,
                                                                       sectorsToDo);
                            }
                        }
                        else
                        {
                            if (sectorsToDo == 1)
                            {
                                sector = inputFormat.ReadSector(doneSectors + track.TrackStartSector);
                                result = outputFormat.WriteSector(sector, doneSectors + track.TrackStartSector);
                            }
                            else
                            {
                                sector = inputFormat.ReadSectors(doneSectors + track.TrackStartSector, sectorsToDo);
                                result = outputFormat.WriteSectors(sector, doneSectors + track.TrackStartSector,
                                                                   sectorsToDo);
                            }
                        }

                        if (!result)
                        {
                            if (force)
                            {
                                DicConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...",
                                                          outputFormat.ErrorMessage, doneSectors);
                            }
                            else
                            {
                                DicConsole.ErrorWriteLine("Error {0} writing sector {1}, not continuing...",
                                                          outputFormat.ErrorMessage, doneSectors);
                                return((int)ErrorNumber.WriteError);
                            }
                        }

                        doneSectors += sectorsToDo;
                    }
                }

                DicConsole.Write("\rConverting sectors {0} to {1} in track {3} ({2:P2} done)", inputFormat.Info.Sectors,
                                 inputFormat.Info.Sectors, 1.0, inputOptical.Tracks.Count);
                DicConsole.WriteLine();

                foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags.OrderBy(t => t))
                {
                    if (!useLong)
                    {
                        break;
                    }

                    switch (tag)
                    {
                    case SectorTagType.AppleSectorTag:
                    case SectorTagType.CdSectorSync:
                    case SectorTagType.CdSectorHeader:
                    case SectorTagType.CdSectorSubHeader:
                    case SectorTagType.CdSectorEdc:
                    case SectorTagType.CdSectorEccP:
                    case SectorTagType.CdSectorEccQ:
                    case SectorTagType.CdSectorEcc:
                        // This tags are inline in long sector
                        continue;
                    }

                    if (force && !outputFormat.SupportedSectorTags.Contains(tag))
                    {
                        continue;
                    }

                    foreach (Track track in inputOptical.Tracks)
                    {
                        doneSectors = 0;
                        ulong  trackSectors = track.TrackEndSector - track.TrackStartSector + 1;
                        byte[] sector;
                        bool   result;

                        switch (tag)
                        {
                        case SectorTagType.CdTrackFlags:
                        case SectorTagType.CdTrackIsrc:
                            DicConsole.Write("\rConverting tag {0} in track {1} ({2:P2} done).", tag,
                                             track.TrackSequence,
                                             track.TrackSequence / (double)inputOptical.Tracks.Count);
                            sector = inputFormat.ReadSectorTag(track.TrackStartSector, tag);
                            result = outputFormat.WriteSectorTag(sector, track.TrackStartSector, tag);
                            if (!result)
                            {
                                if (force)
                                {
                                    DicConsole.ErrorWriteLine("Error {0} writing tag, continuing...",
                                                              outputFormat.ErrorMessage);
                                }
                                else
                                {
                                    DicConsole.ErrorWriteLine("Error {0} writing tag, not continuing...",
                                                              outputFormat.ErrorMessage);
                                    return((int)ErrorNumber.WriteError);
                                }
                            }

                            continue;
                        }

                        while (doneSectors < trackSectors)
                        {
                            uint sectorsToDo;
                            if (trackSectors - doneSectors >= (ulong)count)
                            {
                                sectorsToDo = (uint)count;
                            }
                            else
                            {
                                sectorsToDo =
                                    (uint)(trackSectors - doneSectors);
                            }

                            DicConsole.Write("\rConverting tag {4} for sectors {0} to {1} in track {3} ({2:P2} done)",
                                             doneSectors + track.TrackStartSector,
                                             doneSectors + sectorsToDo + track.TrackStartSector,
                                             (doneSectors + track.TrackStartSector) / (double)inputFormat.Info.Sectors,
                                             track.TrackSequence, tag);

                            if (sectorsToDo == 1)
                            {
                                sector = inputFormat.ReadSectorTag(doneSectors + track.TrackStartSector, tag);
                                result = outputFormat.WriteSectorTag(sector, doneSectors + track.TrackStartSector, tag);
                            }
                            else
                            {
                                sector = inputFormat.ReadSectorsTag(doneSectors + track.TrackStartSector, sectorsToDo,
                                                                    tag);
                                result = outputFormat.WriteSectorsTag(sector, doneSectors + track.TrackStartSector,
                                                                      sectorsToDo, tag);
                            }

                            if (!result)
                            {
                                if (force)
                                {
                                    DicConsole.ErrorWriteLine("Error {0} writing tag for sector {1}, continuing...",
                                                              outputFormat.ErrorMessage, doneSectors);
                                }
                                else
                                {
                                    DicConsole.ErrorWriteLine("Error {0} writing tag for sector {1}, not continuing...",
                                                              outputFormat.ErrorMessage, doneSectors);
                                    return((int)ErrorNumber.WriteError);
                                }
                            }

                            doneSectors += sectorsToDo;
                        }
                    }

                    switch (tag)
                    {
                    case SectorTagType.CdTrackFlags:
                    case SectorTagType.CdTrackIsrc:
                        DicConsole.Write("\rConverting tag {0} in track {1} ({2:P2} done).", tag,
                                         inputOptical.Tracks.Count, 1.0);
                        break;

                    default:
                        DicConsole.Write("\rConverting tag {4} for sectors {0} to {1} in track {3} ({2:P2} done)",
                                         inputFormat.Info.Sectors, inputFormat.Info.Sectors, 1.0,
                                         inputOptical.Tracks.Count, tag);
                        break;
                    }

                    DicConsole.WriteLine();
                }
            }
Example #2
0
        public static int Invoke(bool debug, bool verbose, string imagePath1, string imagePath2)
        {
            MainClass.PrintCopyright();

            if (debug)
            {
                DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
            }

            if (verbose)
            {
                DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
            }

            Statistics.AddCommand("compare");

            DicConsole.DebugWriteLine("Compare command", "--debug={0}", debug);
            DicConsole.DebugWriteLine("Compare command", "--input1={0}", imagePath1);
            DicConsole.DebugWriteLine("Compare command", "--input2={0}", imagePath2);
            DicConsole.DebugWriteLine("Compare command", "--verbose={0}", verbose);

            var     filtersList  = new FiltersList();
            IFilter inputFilter1 = filtersList.GetFilter(imagePath1);

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

            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 (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 (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);

            var sb = new StringBuilder();

            if (verbose)
            {
                sb.AppendLine("\tDisc image 1\tDisc image 2");
                sb.AppendLine("================================");
                sb.AppendFormat("File\t{0}\t{1}", imagePath1, imagePath2).AppendLine();
                sb.AppendFormat("Disc image format\t{0}\t{1}", input1Format.Name, input2Format.Name).AppendLine();
            }
            else
            {
                sb.AppendFormat("Disc image 1: {0}", imagePath1).AppendLine();
                sb.AppendFormat("Disc image 2: {0}", imagePath2).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 (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 (!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;
            }

            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);
        }