Exemple #1
0
        internal static void DoCompare(CompareOptions options)
        {
            DicConsole.DebugWriteLine("Compare command", "--debug={0}", options.Debug);
            DicConsole.DebugWriteLine("Compare command", "--verbose={0}", options.Verbose);
            DicConsole.DebugWriteLine("Compare command", "--input1={0}", options.InputFile1);
            DicConsole.DebugWriteLine("Compare command", "--input2={0}", options.InputFile2);

            FiltersList filtersList  = new FiltersList();
            IFilter     inputFilter1 = filtersList.GetFilter(options.InputFile1);

            filtersList = new FiltersList();
            IFilter inputFilter2 = filtersList.GetFilter(options.InputFile2);

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

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

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

            if (options.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;
            }

            if (options.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);

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

            StringBuilder sb = new StringBuilder();

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

            bool imagesDiffer = false;

            DiscImages.ImageInfo image1Info     = new DiscImages.ImageInfo();
            DiscImages.ImageInfo image2Info     = new DiscImages.ImageInfo();
            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[]>();

            image1Info.HasPartitions = input1Format.Info.HasPartitions;
            #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
            try { image1Sessions = input1Format.Sessions; }
            catch
            {
                // ignored
            }
            #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
            image1Info.HasSessions          |= image1Sessions?.Count > 0;
            image1Info.ImageSize             = input1Format.Info.ImageSize;
            image1Info.Sectors               = input1Format.Info.Sectors;
            image1Info.SectorSize            = input1Format.Info.SectorSize;
            image1Info.CreationTime          = input1Format.Info.CreationTime;
            image1Info.LastModificationTime  = input1Format.Info.LastModificationTime;
            image1Info.MediaType             = input1Format.Info.MediaType;
            image1Info.Version               = input1Format.Info.Version;
            image1Info.Application           = input1Format.Info.Application;
            image1Info.ApplicationVersion    = input1Format.Info.ApplicationVersion;
            image1Info.Creator               = input1Format.Info.Creator;
            image1Info.MediaTitle            = input1Format.Info.MediaTitle;
            image1Info.Comments              = input1Format.Info.Comments;
            image1Info.MediaManufacturer     = input1Format.Info.MediaManufacturer;
            image1Info.MediaModel            = input1Format.Info.MediaModel;
            image1Info.MediaSerialNumber     = input1Format.Info.MediaSerialNumber;
            image1Info.MediaBarcode          = input1Format.Info.MediaBarcode;
            image1Info.MediaPartNumber       = input1Format.Info.MediaPartNumber;
            image1Info.MediaSequence         = input1Format.Info.MediaSequence;
            image1Info.LastMediaSequence     = input1Format.Info.LastMediaSequence;
            image1Info.DriveManufacturer     = input1Format.Info.DriveManufacturer;
            image1Info.DriveModel            = input1Format.Info.DriveModel;
            image1Info.DriveSerialNumber     = input1Format.Info.DriveSerialNumber;
            image1Info.DriveFirmwareRevision = input1Format.Info.DriveFirmwareRevision;
            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
            }

            image2Info.HasPartitions = input2Format.Info.HasPartitions;
            #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
            try { image2Sessions = input2Format.Sessions; }
            catch
            {
                // ignored
            }
            #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
            image2Info.HasSessions          |= image2Sessions?.Count > 0;
            image2Info.ImageSize             = input2Format.Info.ImageSize;
            image2Info.Sectors               = input2Format.Info.Sectors;
            image2Info.SectorSize            = input2Format.Info.SectorSize;
            image2Info.CreationTime          = input2Format.Info.CreationTime;
            image2Info.LastModificationTime  = input2Format.Info.LastModificationTime;
            image2Info.MediaType             = input2Format.Info.MediaType;
            image2Info.Version               = input2Format.Info.Version;
            image2Info.Application           = input2Format.Info.Application;
            image2Info.ApplicationVersion    = input2Format.Info.ApplicationVersion;
            image2Info.Creator               = input2Format.Info.Creator;
            image2Info.MediaTitle            = input2Format.Info.MediaTitle;
            image2Info.Comments              = input2Format.Info.Comments;
            image2Info.MediaManufacturer     = input2Format.Info.MediaManufacturer;
            image2Info.MediaModel            = input2Format.Info.MediaModel;
            image2Info.MediaSerialNumber     = input2Format.Info.MediaSerialNumber;
            image2Info.MediaBarcode          = input2Format.Info.MediaBarcode;
            image2Info.MediaPartNumber       = input2Format.Info.MediaPartNumber;
            image2Info.MediaSequence         = input2Format.Info.MediaSequence;
            image2Info.LastMediaSequence     = input2Format.Info.LastMediaSequence;
            image2Info.DriveManufacturer     = input2Format.Info.DriveManufacturer;
            image2Info.DriveModel            = input2Format.Info.DriveModel;
            image2Info.DriveSerialNumber     = input2Format.Info.DriveSerialNumber;
            image2Info.DriveFirmwareRevision = input2Format.Info.DriveFirmwareRevision;
            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 (options.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 (!options.Verbose)
                {
                    sb.AppendLine("Image partitioned status differ");
                }
            }

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

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

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

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

            if (image1Info.CreationTime != image2Info.CreationTime)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Image creation time differ");
                }
            }

            if (image1Info.LastModificationTime != image2Info.LastModificationTime)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Image last modification time differ");
                }
            }

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

            if (image1Info.Version != image2Info.Version)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Image version differ");
                }
            }

            if (image1Info.Application != image2Info.Application)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Image application differ");
                }
            }

            if (image1Info.ApplicationVersion != image2Info.ApplicationVersion)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Image application version differ");
                }
            }

            if (image1Info.Creator != image2Info.Creator)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Image creator differ");
                }
            }

            if (image1Info.MediaTitle != image2Info.MediaTitle)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Image name differ");
                }
            }

            if (image1Info.Comments != image2Info.Comments)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Image comments differ");
                }
            }

            if (image1Info.MediaManufacturer != image2Info.MediaManufacturer)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Disk manufacturer differ");
                }
            }

            if (image1Info.MediaModel != image2Info.MediaModel)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Disk model differ");
                }
            }

            if (image1Info.MediaSerialNumber != image2Info.MediaSerialNumber)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Disk serial number differ");
                }
            }

            if (image1Info.MediaBarcode != image2Info.MediaBarcode)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Disk barcode differ");
                }
            }

            if (image1Info.MediaPartNumber != image2Info.MediaPartNumber)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Disk part number differ");
                }
            }

            if (image1Info.MediaSequence != image2Info.MediaSequence)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Disk sequence differ");
                }
            }

            if (image1Info.LastMediaSequence != image2Info.LastMediaSequence)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Last disk in sequence differ");
                }
            }

            if (image1Info.DriveManufacturer != image2Info.DriveManufacturer)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Drive manufacturer differ");
                }
            }

            if (image1Info.DriveModel != image2Info.DriveModel)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Drive model differ");
                }
            }

            if (image1Info.DriveSerialNumber != image2Info.DriveSerialNumber)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Drive serial number differ");
                }
            }

            if (image1Info.DriveFirmwareRevision != image2Info.DriveFirmwareRevision)
            {
                imagesDiffer = true;
                if (!options.Verbose)
                {
                    sb.AppendLine("Drive firmware revision differ");
                }
            }

            ulong leastSectors;
            if (image1Info.Sectors < image2Info.Sectors)
            {
                imagesDiffer = true;
                leastSectors = image1Info.Sectors;
                if (!options.Verbose)
                {
                    sb.AppendLine("Image 2 has more sectors");
                }
            }
            else if (image1Info.Sectors > image2Info.Sectors)
            {
                imagesDiffer = true;
                leastSectors = image2Info.Sectors;
                if (!options.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());

            Core.Statistics.AddCommand("compare");
        }
Exemple #2
0
        public static void DoConvert(ConvertImageOptions options)
        {
            DicConsole.DebugWriteLine("Analyze command", "--debug={0}", options.Debug);
            DicConsole.DebugWriteLine("Analyze command", "--verbose={0}", options.Verbose);
            DicConsole.DebugWriteLine("Analyze command", "--input={0}", options.InputFile);
            DicConsole.DebugWriteLine("Analyze command", "--output={0}", options.OutputFile);
            DicConsole.DebugWriteLine("Analyze command", "--format={0}", options.OutputFormat);
            DicConsole.DebugWriteLine("Analyze command", "--count={0}", options.Count);
            DicConsole.DebugWriteLine("Analyze command", "--force={0}", options.Force);
            DicConsole.DebugWriteLine("Analyze command", "--creator={0}", options.Creator);
            DicConsole.DebugWriteLine("Analyze command", "--media-title={0}", options.MediaTitle);
            DicConsole.DebugWriteLine("Analyze command", "--comments={0}", options.Comments);
            DicConsole.DebugWriteLine("Analyze command", "--media-manufacturer={0}", options.MediaManufacturer);
            DicConsole.DebugWriteLine("Analyze command", "--media-model={0}", options.MediaModel);
            DicConsole.DebugWriteLine("Analyze command", "--media-serial={0}", options.MediaSerialNumber);
            DicConsole.DebugWriteLine("Analyze command", "--media-barcode={0}", options.MediaBarcode);
            DicConsole.DebugWriteLine("Analyze command", "--media-partnumber={0}", options.MediaPartNumber);
            DicConsole.DebugWriteLine("Analyze command", "--media-sequence={0}", options.MediaSequence);
            DicConsole.DebugWriteLine("Analyze command", "--media-lastsequence={0}", options.LastMediaSequence);
            DicConsole.DebugWriteLine("Analyze command", "--drive-manufacturer={0}", options.DriveManufacturer);
            DicConsole.DebugWriteLine("Analyze command", "--drive-model={0}", options.DriveModel);
            DicConsole.DebugWriteLine("Analyze command", "--drive-serial={0}", options.DriveSerialNumber);
            DicConsole.DebugWriteLine("Analyze command", "--drive-revision={0}", options.DriveFirmwareRevision);
            DicConsole.DebugWriteLine("Analyze command", "--cicm-xml={0}", options.CicmXml);
            DicConsole.DebugWriteLine("Analyze command", "--resume-file={0}", options.ResumeFile);
            DicConsole.DebugWriteLine("Analyze command", "--options={0}", options.Options);

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

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

            if (options.Count == 0)
            {
                DicConsole.ErrorWriteLine("Need to specify more than 0 sectors to copy at once");
                return;
            }

            Resume           resume  = null;
            CICMMetadataType sidecar = null;

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

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

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

            FiltersList filtersList = new FiltersList();
            IFilter     inputFilter = filtersList.GetFilter(options.InputFile);

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

            if (File.Exists(options.OutputFile))
            {
                DicConsole.ErrorWriteLine("Output file already exists, not continuing.");
                return;
            }

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

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

            if (options.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;
                }

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

                Core.Statistics.AddMediaFormat(inputFormat.Format);
                Core.Statistics.AddMedia(inputFormat.Info.MediaType, false);
                Core.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;
            }

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

            // Try extension
            if (string.IsNullOrEmpty(options.OutputFormat))
            {
                candidates.AddRange(plugins.WritableImages.Values.Where(t =>
                                                                        t.KnownExtensions
                                                                        .Contains(Path.GetExtension(options
                                                                                                    .OutputFile))));
            }
            // Try Id
            else if (Guid.TryParse(options.OutputFormat, 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, options.OutputFormat,
                                                                                           StringComparison
                                                                                           .InvariantCultureIgnoreCase)));
            }

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

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

            IWritableImage outputFormat = candidates[0];

            if (options.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;
            }

            foreach (MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags)
            {
                if (outputFormat.SupportedMediaTags.Contains(mediaTag) || options.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;
            }

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

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

                if (options.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;
            }

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

            DiscImages.ImageInfo metadata = new DiscImages.ImageInfo
            {
                Application           = "DiscImageChef",
                ApplicationVersion    = Version.GetVersion(),
                Comments              = options.Comments ?? inputFormat.Info.Comments,
                Creator               = options.Creator ?? inputFormat.Info.Creator,
                DriveFirmwareRevision = options.DriveFirmwareRevision ?? inputFormat.Info.DriveFirmwareRevision,
                DriveManufacturer     = options.DriveManufacturer ?? inputFormat.Info.DriveManufacturer,
                DriveModel            = options.DriveModel ?? inputFormat.Info.DriveModel,
                DriveSerialNumber     = options.DriveSerialNumber ?? inputFormat.Info.DriveSerialNumber,
                LastMediaSequence     =
                    options.LastMediaSequence != 0 ? options.LastMediaSequence : inputFormat.Info.LastMediaSequence,
                MediaBarcode      = options.MediaBarcode ?? inputFormat.Info.MediaBarcode,
                MediaManufacturer = options.MediaManufacturer ?? inputFormat.Info.MediaManufacturer,
                MediaModel        = options.MediaModel ?? inputFormat.Info.MediaModel,
                MediaPartNumber   = options.MediaPartNumber ?? inputFormat.Info.MediaPartNumber,
                MediaSequence     = options.MediaSequence != 0 ? options.MediaSequence : inputFormat.Info.MediaSequence,
                MediaSerialNumber = options.MediaSerialNumber ?? inputFormat.Info.MediaSerialNumber,
                MediaTitle        = options.MediaTitle ?? inputFormat.Info.MediaTitle
            };

            if (!outputFormat.SetMetadata(metadata))
            {
                DicConsole.ErrorWrite("Error {0} setting metadata, ", outputFormat.ErrorMessage);
                if (!options.Force)
                {
                    DicConsole.ErrorWriteLine("not continuing...");
                    return;
                }

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

            List <Track> tracks;

            try { tracks = inputFormat.Tracks; }
            catch (Exception) { tracks = null; }

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

            if (tracks != null)
            {
                if (!outputFormat.SetTracks(tracks))
                {
                    DicConsole.ErrorWriteLine("Error {0} sending tracks list to output image.",
                                              outputFormat.ErrorMessage);
                    return;
                }
            }

            foreach (MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags)
            {
                if (options.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 (options.Force)
                {
                    DicConsole.ErrorWriteLine("Error {0} writing media tag, continuing...", outputFormat.ErrorMessage);
                }
                else
                {
                    DicConsole.ErrorWriteLine("Error {0} writing media tag, not continuing...",
                                              outputFormat.ErrorMessage);
                    return;
                }
            }

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

            if (tracks == null)
            {
                DicConsole.WriteLine("Setting geometry to {0} cylinders, {1} heads and {2} sectors per track",
                                     inputFormat.Info.Cylinders, inputFormat.Info.Heads,
                                     inputFormat.Info.SectorsPerTrack);
                if (!outputFormat.SetGeometry(inputFormat.Info.Cylinders, inputFormat.Info.Heads,
                                              inputFormat.Info.SectorsPerTrack))
                {
                    DicConsole.ErrorWriteLine("Error {0} setting geometry, image may be incorrect, continuing...",
                                              outputFormat.ErrorMessage);
                }

                while (doneSectors < inputFormat.Info.Sectors)
                {
                    byte[] sector;

                    uint sectorsToDo;
                    if (inputFormat.Info.Sectors - doneSectors >= (ulong)options.Count)
                    {
                        sectorsToDo = (uint)options.Count;
                    }
                    else
                    {
                        sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors);
                    }

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

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

                    if (!result)
                    {
                        if (options.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;
                        }
                    }

                    doneSectors += sectorsToDo;
                }

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

                foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags)
                {
                    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 (options.Force && !outputFormat.SupportedSectorTags.Contains(tag))
                    {
                        continue;
                    }

                    doneSectors = 0;
                    while (doneSectors < inputFormat.Info.Sectors)
                    {
                        byte[] sector;

                        uint sectorsToDo;
                        if (inputFormat.Info.Sectors - doneSectors >= (ulong)options.Count)
                        {
                            sectorsToDo = (uint)options.Count;
                        }
                        else
                        {
                            sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors);
                        }

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

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

                        if (!result)
                        {
                            if (options.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;
                            }
                        }

                        doneSectors += sectorsToDo;
                    }

                    DicConsole.Write("\rConverting tag {2} for sectors {0} to {1} ({2:P2} done)",
                                     inputFormat.Info.Sectors, inputFormat.Info.Sectors, 1.0, tag);
                    DicConsole.WriteLine();
                }
            }
            else
            {
                foreach (Track track in tracks)
                {
                    doneSectors = 0;
                    ulong trackSectors = track.TrackEndSector - track.TrackStartSector + 1;

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

                        uint sectorsToDo;
                        if (trackSectors - doneSectors >= (ulong)options.Count)
                        {
                            sectorsToDo = (uint)options.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 (options.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;
                            }
                        }

                        doneSectors += sectorsToDo;
                    }
                }

                DicConsole.Write("\rConverting sectors {0} to {1} in track {3} ({2:P2} done)", inputFormat.Info.Sectors,
                                 inputFormat.Info.Sectors, 1.0, 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 (options.Force && !outputFormat.SupportedSectorTags.Contains(tag))
                    {
                        continue;
                    }

                    foreach (Track track in 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)tracks.Count);
                            sector = inputFormat.ReadSectorTag(track.TrackStartSector, tag);
                            result = outputFormat.WriteSectorTag(sector, track.TrackStartSector, tag);
                            if (!result)
                            {
                                if (options.Force)
                                {
                                    DicConsole.ErrorWriteLine("Error {0} writing tag, continuing...",
                                                              outputFormat.ErrorMessage);
                                }
                                else
                                {
                                    DicConsole.ErrorWriteLine("Error {0} writing tag, not continuing...",
                                                              outputFormat.ErrorMessage);
                                    return;
                                }
                            }

                            continue;
                        }

                        while (doneSectors < trackSectors)
                        {
                            uint sectorsToDo;
                            if (trackSectors - doneSectors >= (ulong)options.Count)
                            {
                                sectorsToDo = (uint)options.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 (options.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;
                                }
                            }

                            doneSectors += sectorsToDo;
                        }
                    }

                    switch (tag)
                    {
                    case SectorTagType.CdTrackFlags:
                    case SectorTagType.CdTrackIsrc:
                        DicConsole.Write("\rConverting tag {0} in track {1} ({2:P2} done).", tag, 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, tracks.Count,
                                         tag);
                        break;
                    }

                    DicConsole.WriteLine();
                }
            }

            bool ret = false;

            if (resume != null || dumpHardware != null)
            {
                if (resume != null)
                {
                    ret = outputFormat.SetDumpHardware(resume.Tries);
                }
                else if (dumpHardware != null)
                {
                    ret = outputFormat.SetDumpHardware(dumpHardware);
                }
                if (ret)
                {
                    DicConsole.WriteLine("Written dump hardware list to output image.");
                }
            }

            ret = false;
            if (sidecar != null || cicmMetadata != null)
            {
                if (sidecar != null)
                {
                    ret = outputFormat.SetCicmMetadata(sidecar);
                }
                else if (cicmMetadata != null)
                {
                    ret = outputFormat.SetCicmMetadata(cicmMetadata);
                }
                if (ret)
                {
                    DicConsole.WriteLine("Written CICM XML metadata to output image.");
                }
            }

            DicConsole.WriteLine("Closing output image.");

            if (!outputFormat.Close())
            {
                DicConsole.ErrorWriteLine("Error {0} closing output image... Contents are not correct.",
                                          outputFormat.ErrorMessage);
            }

            DicConsole.WriteLine();
            DicConsole.WriteLine("Conversion done.");

            Core.Statistics.AddCommand("convert-image");
        }