Beispiel #1
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     = GetPluginBase.Instance;
            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;
            }

            CommonTypes.Structs.ImageInfo metadata = new CommonTypes.Structs.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");
        }
Beispiel #2
0
        public static int Invoke(bool verbose, bool debug, string cicmXml, string comments, int count, string creator,
                                 string driveFirmwareRevision, string driveManufacturer, string driveModel,
                                 string driveSerialNumber, bool force, string inputPath, int lastMediaSequence,
                                 string mediaBarcode, string mediaManufacturer, string mediaModel,
                                 string mediaPartNumber, int mediaSequence, string mediaSerialNumber, string mediaTitle,
                                 string outputPath, string outputOptions, string resumeFile, string format)
        {
            MainClass.PrintCopyright();

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

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

            Statistics.AddCommand("convert-image");

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

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

            AaruConsole.DebugWriteLine("Analyze command", "Parsed options:");

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

            if (count == 0)
            {
                AaruConsole.ErrorWriteLine("Need to specify more than 0 sectors to copy at once");

                return((int)ErrorNumber.InvalidArgument);
            }

            Resume           resume  = null;
            CICMMetadataType sidecar = null;
            MediaType        mediaType;

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

            if (cicmXml != null)
            {
                if (File.Exists(cicmXml))
                {
                    try
                    {
                        var sr = new StreamReader(cicmXml);
                        sidecar = (CICMMetadataType)xs.Deserialize(sr);
                        sr.Close();
                    }
                    catch (Exception ex)
                    {
                        AaruConsole.ErrorWriteLine("Incorrect metadata sidecar file, not continuing...");
                        AaruConsole.DebugWriteLine("Image conversion", $"{ex}");

                        return((int)ErrorNumber.InvalidSidecar);
                    }
                }
                else
                {
                    AaruConsole.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
                    {
                        var sr = new StreamReader(resumeFile);
                        resume = (Resume)xs.Deserialize(sr);
                        sr.Close();
                    }
                    catch (Exception ex)
                    {
                        AaruConsole.ErrorWriteLine("Incorrect resume file, not continuing...");
                        AaruConsole.DebugWriteLine("Image conversion", $"{ex}");

                        return((int)ErrorNumber.InvalidResume);
                    }
                }
                else
                {
                    AaruConsole.ErrorWriteLine("Could not find resume file, not continuing...");

                    return((int)ErrorNumber.FileNotFound);
                }
            }

            var     filtersList = new FiltersList();
            IFilter inputFilter = filtersList.GetFilter(inputPath);

            if (inputFilter == null)
            {
                AaruConsole.ErrorWriteLine("Cannot open specified file.");

                return((int)ErrorNumber.CannotOpenFile);
            }

            if (File.Exists(outputPath))
            {
                AaruConsole.ErrorWriteLine("Output file already exists, not continuing.");

                return((int)ErrorNumber.DestinationExists);
            }

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

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

                return((int)ErrorNumber.UnrecognizedFormat);
            }

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

            try
            {
                if (!inputFormat.Open(inputFilter))
                {
                    AaruConsole.WriteLine("Unable to open image format");
                    AaruConsole.WriteLine("No error given");

                    return((int)ErrorNumber.CannotOpenFormat);
                }

                mediaType = inputFormat.Info.MediaType;

                // Obsolete types
                #pragma warning disable 612
                switch (mediaType)
                {
                case MediaType.SQ1500:
                    mediaType = MediaType.SyJet;

                    break;

                case MediaType.Bernoulli:
                    mediaType = MediaType.Bernoulli10;

                    break;

                case MediaType.Bernoulli2:
                    mediaType = MediaType.BernoulliBox2_20;

                    break;
                }
                #pragma warning restore 612

                AaruConsole.DebugWriteLine("Convert-image command", "Correctly opened image file.");

                AaruConsole.DebugWriteLine("Convert-image command", "Image without headers is {0} bytes.",
                                           inputFormat.Info.ImageSize);

                AaruConsole.DebugWriteLine("Convert-image command", "Image has {0} sectors.", inputFormat.Info.Sectors);

                AaruConsole.DebugWriteLine("Convert-image command", "Image identifies media type as {0}.", mediaType);

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

                return((int)ErrorNumber.CannotOpenFormat);
            }

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

            // Try extension
            if (string.IsNullOrEmpty(format))
            {
                candidates.AddRange(plugins.WritableImages.Values.Where(t =>
                                                                        t.KnownExtensions.
                                                                        Contains(Path.GetExtension(outputPath))));
            }

            // Try Id
            else if (Guid.TryParse(format, 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, format,
                                                                                           StringComparison.
                                                                                           InvariantCultureIgnoreCase)));
            }

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

                return((int)ErrorNumber.FormatNotFound);
            }

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

                return((int)ErrorNumber.TooManyFormats);
            }

            IWritableImage outputFormat = candidates[0];

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

            if (!outputFormat.SupportedMediaTypes.Contains(mediaType))
            {
                AaruConsole.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;
                }

                AaruConsole.ErrorWriteLine("Converting image will lose media tag {0}, not continuing...", mediaTag);
                AaruConsole.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;
                }

                AaruConsole.ErrorWriteLine("Converting image will lose sector tag {0}, not continuing...", sectorTag);

                AaruConsole.
                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(outputPath, mediaType, parsedOptions, inputFormat.Info.Sectors,
                                     inputFormat.Info.SectorSize))
            {
                AaruConsole.ErrorWriteLine("Error {0} creating output image.", outputFormat.ErrorMessage);

                return((int)ErrorNumber.CannotCreateFormat);
            }

            var metadata = new ImageInfo
            {
                Application           = "Aaru",
                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))
            {
                AaruConsole.ErrorWrite("Error {0} setting metadata, ", outputFormat.ErrorMessage);

                if (!force)
                {
                    AaruConsole.ErrorWriteLine("not continuing...");

                    return((int)ErrorNumber.WriteError);
                }

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

                AaruConsole.WriteLine("Converting media tag {0}", mediaTag);
                byte[] tag = inputFormat.ReadDiskTag(mediaTag);

                if (outputFormat.WriteMediaTag(tag, mediaTag))
                {
                    continue;
                }

                if (force)
                {
                    AaruConsole.ErrorWriteLine("Error {0} writing media tag, continuing...", outputFormat.ErrorMessage);
                }
                else
                {
                    AaruConsole.ErrorWriteLine("Error {0} writing media tag, not continuing...",
                                               outputFormat.ErrorMessage);

                    return((int)ErrorNumber.WriteError);
                }
            }

            AaruConsole.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))
                {
                    AaruConsole.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);
                        }

                        AaruConsole.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 useNotLong = false;
                        bool result     = false;

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

                            if (!result &&
                                sector.Length % 2352 != 0)
                            {
                                if (!force)
                                {
                                    AaruConsole.ErrorWriteLine("Input image is not returning raw sectors, use force if you want to continue...");

                                    return((int)Errno.InOutError);
                                }

                                useNotLong = true;
                            }
                        }

                        if (!useLong || useNotLong)
                        {
                            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)
                            {
                                AaruConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...",
                                                           outputFormat.ErrorMessage,
                                                           doneSectors + track.TrackStartSector);
                            }
                            else
                            {
                                AaruConsole.ErrorWriteLine("Error {0} writing sector {1}, not continuing...",
                                                           outputFormat.ErrorMessage,
                                                           doneSectors + track.TrackStartSector);

                                return((int)ErrorNumber.WriteError);
                            }
                        }

                        doneSectors += sectorsToDo;
                    }
                }

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

                AaruConsole.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:
                            AaruConsole.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)
                                {
                                    AaruConsole.ErrorWriteLine("Error {0} writing tag, continuing...",
                                                               outputFormat.ErrorMessage);
                                }
                                else
                                {
                                    AaruConsole.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);
                            }

                            AaruConsole.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)
                                {
                                    AaruConsole.ErrorWriteLine("Error {0} writing tag for sector {1}, continuing...",
                                                               outputFormat.ErrorMessage,
                                                               doneSectors + track.TrackStartSector);
                                }
                                else
                                {
                                    AaruConsole.
                                    ErrorWriteLine("Error {0} writing tag for sector {1}, not continuing...",
                                                   outputFormat.ErrorMessage, doneSectors + track.TrackStartSector);

                                    return((int)ErrorNumber.WriteError);
                                }
                            }

                            doneSectors += sectorsToDo;
                        }
                    }

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

                        break;

                    default:
                        AaruConsole.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;
                    }

                    AaruConsole.WriteLine();
                }
            }