Ejemplo n.º 1
0
 /// <summary>Initializes dumpers</summary>
 /// <param name="doResume">Should resume?</param>
 /// <param name="dev">Device</param>
 /// <param name="devicePath">Path to the device</param>
 /// <param name="outputPrefix">Prefix for output log files</param>
 /// <param name="outputPlugin">Plugin for output file</param>
 /// <param name="retryPasses">How many times to retry</param>
 /// <param name="force">Force to continue dump whenever possible</param>
 /// <param name="dumpRaw">Dump long sectors</param>
 /// <param name="persistent">Store whatever data the drive returned on error</param>
 /// <param name="stopOnError">Stop dump on first error</param>
 /// <param name="resume">Information for dump resuming</param>
 /// <param name="dumpLog">Dump logger</param>
 /// <param name="encoding">Encoding to use when analyzing dump</param>
 /// <param name="outputPath">Path to output file</param>
 /// <param name="formatOptions">Formats to pass to output file plugin</param>
 /// <param name="trim">Trim errors from skipped sectors</param>
 /// <param name="dumpFirstTrackPregap">Try to read and dump as much first track pregap as possible</param>
 /// <param name="preSidecar">Sidecar to store in dumped image</param>
 /// <param name="skip">How many sectors to skip reading on error</param>
 /// <param name="metadata">Create metadata sidecar after dump?</param>
 public Dump(bool doResume, Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses,
             bool force, bool dumpRaw, bool persistent, bool stopOnError, Resume resume, DumpLog dumpLog,
             Encoding encoding, string outputPrefix, string outputPath, Dictionary <string, string> formatOptions,
             CICMMetadataType preSidecar, uint skip, bool metadata, bool trim, bool dumpFirstTrackPregap,
             bool fixOffset, bool debug, DumpSubchannel subchannel, int speed)
 {
     _doResume             = doResume;
     _dev                  = dev;
     _devicePath           = devicePath;
     _outputPlugin         = outputPlugin;
     _retryPasses          = retryPasses;
     _force                = force;
     _dumpRaw              = dumpRaw;
     _persistent           = persistent;
     _stopOnError          = stopOnError;
     _resume               = resume;
     _dumpLog              = dumpLog;
     _encoding             = encoding;
     _outputPrefix         = outputPrefix;
     _outputPath           = outputPath;
     _formatOptions        = formatOptions;
     _preSidecar           = preSidecar;
     _skip                 = skip;
     _metadata             = metadata;
     _trim                 = trim;
     _dumpFirstTrackPregap = dumpFirstTrackPregap;
     _aborted              = false;
     _fixOffset            = fixOffset;
     _debug                = debug;
     _maximumReadable      = 64;
     _subchannel           = subchannel;
     _speedMultiplier      = -1;
     _speed                = speed;
 }
Ejemplo n.º 2
0
Archivo: Dump.cs Proyecto: paulyc/Aaru
 /// <summary>Initializes dumpers</summary>
 /// <param name="doResume">Should resume?</param>
 /// <param name="dev">Device</param>
 /// <param name="devicePath">Path to the device</param>
 /// <param name="outputPrefix">Prefix for output log files</param>
 /// <param name="outputPlugin">Plugin for output file</param>
 /// <param name="retryPasses">How many times to retry</param>
 /// <param name="force">Force to continue dump whenever possible</param>
 /// <param name="dumpRaw">Dump long sectors</param>
 /// <param name="persistent">Store whatever data the drive returned on error</param>
 /// <param name="stopOnError">Stop dump on first error</param>
 /// <param name="resume">Information for dump resuming</param>
 /// <param name="dumpLog">Dump logger</param>
 /// <param name="encoding">Encoding to use when analyzing dump</param>
 /// <param name="outputPath">Path to output file</param>
 /// <param name="formatOptions">Formats to pass to output file plugin</param>
 /// <param name="trim">Trim errors from skipped sectors</param>
 /// <param name="dumpFirstTrackPregap">Try to read and dump as much first track pregap as possible</param>
 /// <param name="preSidecar">Sidecar to store in dumped image</param>
 /// <param name="skip">How many sectors to skip reading on error</param>
 /// <param name="metadata">Create metadata sidecar after dump?</param>
 /// <param name="fixOffset">Fix audio offset</param>
 /// <param name="debug">Debug mode</param>
 /// <param name="subchannel">Desired subchannel to save to image</param>
 /// <param name="speed">Desired drive speed</param>
 /// <param name="private">Disable saving paths or serial numbers in images and logs</param>
 /// <param name="fixSubchannelPosition">Fix subchannel position (save where it says it belongs)</param>
 /// <param name="retrySubchannel">Retry reading incorrect or missing subchannels</param>
 /// <param name="fixSubchannel">Try to fix subchannel errors (but not Q CRC)</param>
 /// <param name="fixSubchannelCrc">Try to fix subchannel Q CRC errors</param>
 /// <param name="skipCdireadyHole">Skip gap between CD-i Ready hidden track and track 1 audio</param>
 /// <param name="errorLog">Error log</param>
 /// <param name="generateSubchannels">Generate missing subchannels</param>
 /// <param name="maximumReadable">Number of maximum blocks to be read at once (can be overriden by database)</param>
 /// <param name="useBufferedReads">
 ///     If MMC/SD does not support CMD23, use OS buffered reads instead of multiple single block
 ///     commands
 /// </param>
 /// <param name="storeEncrypted">Store encrypted data as is</param>
 /// <param name="titleKeys">Dump DVD CSS title keys</param>
 public Dump(bool doResume, Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses,
             bool force, bool dumpRaw, bool persistent, bool stopOnError, Resume resume, DumpLog dumpLog,
             Encoding encoding, string outputPrefix, string outputPath, Dictionary <string, string> formatOptions,
             CICMMetadataType preSidecar, uint skip, bool metadata, bool trim, bool dumpFirstTrackPregap,
             bool fixOffset, bool debug, DumpSubchannel subchannel, int speed, bool @private,
             bool fixSubchannelPosition, bool retrySubchannel, bool fixSubchannel, bool fixSubchannelCrc,
             bool skipCdireadyHole, ErrorLog errorLog, bool generateSubchannels, uint maximumReadable,
             bool useBufferedReads, bool storeEncrypted, bool titleKeys)
 {
     _doResume              = doResume;
     _dev                   = dev;
     _devicePath            = devicePath;
     _outputPlugin          = outputPlugin;
     _retryPasses           = retryPasses;
     _force                 = force;
     _dumpRaw               = dumpRaw;
     _persistent            = persistent;
     _stopOnError           = stopOnError;
     _resume                = resume;
     _dumpLog               = dumpLog;
     _encoding              = encoding;
     _outputPrefix          = outputPrefix;
     _outputPath            = outputPath;
     _formatOptions         = formatOptions;
     _preSidecar            = preSidecar;
     _skip                  = skip;
     _metadata              = metadata;
     _trim                  = trim;
     _dumpFirstTrackPregap  = dumpFirstTrackPregap;
     _aborted               = false;
     _fixOffset             = fixOffset;
     _debug                 = debug;
     _maximumReadable       = maximumReadable;
     _subchannel            = subchannel;
     _speedMultiplier       = -1;
     _speed                 = speed;
     _private               = @private;
     _fixSubchannelPosition = fixSubchannelPosition;
     _retrySubchannel       = retrySubchannel;
     _fixSubchannel         = fixSubchannel;
     _fixSubchannelCrc      = fixSubchannelCrc;
     _skipCdireadyHole      = skipCdireadyHole;
     _errorLog              = errorLog;
     _generateSubchannels   = generateSubchannels;
     _useBufferedReads      = useBufferedReads;
     _storeEncrypted        = storeEncrypted;
     _titleKeys             = titleKeys;
 }
Ejemplo n.º 3
0
        public static int Invoke(bool debug, bool verbose, string cicmXml, string devicePath, bool resume,
                                 string encoding, bool firstPregap, bool fixOffset, bool force, bool metadata,
                                 bool trim, string outputPath, string options, bool persistent, ushort retryPasses,
                                 uint skip, byte speed, bool stopOnError, string format, string subchannel,
                                 bool @private, bool fixSubchannelPosition, bool retrySubchannel, bool fixSubchannel,
                                 bool fixSubchannelCrc, bool generateSubchannels, bool skipCdiReadyHole, bool eject)
        {
            MainClass.PrintCopyright();

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

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

            if (fixSubchannelCrc)
            {
                fixSubchannel = true;
            }

            if (retrySubchannel || fixSubchannel)
            {
                fixSubchannelPosition = true;
            }

            Statistics.AddCommand("dump-media");

            AaruConsole.DebugWriteLine("Dump-Media command", "--cicm-xml={0}", cicmXml);
            AaruConsole.DebugWriteLine("Dump-Media command", "--debug={0}", debug);
            AaruConsole.DebugWriteLine("Dump-Media command", "--device={0}", devicePath);
            AaruConsole.DebugWriteLine("Dump-Media command", "--encoding={0}", encoding);
            AaruConsole.DebugWriteLine("Dump-Media command", "--first-pregap={0}", firstPregap);
            AaruConsole.DebugWriteLine("Dump-Media command", "--fix-offset={0}", fixOffset);
            AaruConsole.DebugWriteLine("Dump-Media command", "--force={0}", force);
            AaruConsole.DebugWriteLine("Dump-Media command", "--format={0}", format);
            AaruConsole.DebugWriteLine("Dump-Media command", "--metadata={0}", metadata);
            AaruConsole.DebugWriteLine("Dump-Media command", "--options={0}", options);
            AaruConsole.DebugWriteLine("Dump-Media command", "--output={0}", outputPath);
            AaruConsole.DebugWriteLine("Dump-Media command", "--persistent={0}", persistent);
            AaruConsole.DebugWriteLine("Dump-Media command", "--resume={0}", resume);
            AaruConsole.DebugWriteLine("Dump-Media command", "--retry-passes={0}", retryPasses);
            AaruConsole.DebugWriteLine("Dump-Media command", "--skip={0}", skip);
            AaruConsole.DebugWriteLine("Dump-Media command", "--stop-on-error={0}", stopOnError);
            AaruConsole.DebugWriteLine("Dump-Media command", "--trim={0}", trim);
            AaruConsole.DebugWriteLine("Dump-Media command", "--verbose={0}", verbose);
            AaruConsole.DebugWriteLine("Dump-Media command", "--subchannel={0}", subchannel);
            AaruConsole.DebugWriteLine("Dump-Media command", "--private={0}", @private);
            AaruConsole.DebugWriteLine("Dump-Media command", "--fix-subchannel-position={0}", fixSubchannelPosition);
            AaruConsole.DebugWriteLine("Dump-Media command", "--retry-subchannel={0}", retrySubchannel);
            AaruConsole.DebugWriteLine("Dump-Media command", "--fix-subchannel={0}", fixSubchannel);
            AaruConsole.DebugWriteLine("Dump-Media command", "--fix-subchannel-crc={0}", fixSubchannelCrc);
            AaruConsole.DebugWriteLine("Dump-Media command", "--generate-subchannels={0}", generateSubchannels);
            AaruConsole.DebugWriteLine("Dump-Media command", "--skip-cdiready-hole={0}", skipCdiReadyHole);
            AaruConsole.DebugWriteLine("Dump-Media command", "--eject={0}", eject);

            // TODO: Disabled temporarily
            //AaruConsole.DebugWriteLine("Dump-Media command", "--raw={0}",           raw);

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

            AaruConsole.DebugWriteLine("Dump-Media command", "Parsed options:");

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

            Encoding encodingClass = null;

            if (encoding != null)
            {
                try
                {
                    encodingClass = Claunia.Encoding.Encoding.GetEncoding(encoding);

                    if (verbose)
                    {
                        AaruConsole.VerboseWriteLine("Using encoding for {0}.", encodingClass.EncodingName);
                    }
                }
                catch (ArgumentException)
                {
                    AaruConsole.ErrorWriteLine("Specified encoding is not supported.");

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

            DumpSubchannel wantedSubchannel = DumpSubchannel.Any;

            switch (subchannel?.ToLowerInvariant())
            {
            case "any":
            case null:
                wantedSubchannel = DumpSubchannel.Any;

                break;

            case "rw":
                wantedSubchannel = DumpSubchannel.Rw;

                break;

            case "rw-or-pq":
                wantedSubchannel = DumpSubchannel.RwOrPq;

                break;

            case "pq":
                wantedSubchannel = DumpSubchannel.Pq;

                break;

            case "none":
                wantedSubchannel = DumpSubchannel.None;

                break;

            default:
                AaruConsole.WriteLine("Incorrect subchannel type \"{0}\" requested.", subchannel);

                break;
            }

            string filename = Path.GetFileNameWithoutExtension(outputPath);

            bool isResponse = filename.StartsWith("#", StringComparison.OrdinalIgnoreCase) &&
                              File.Exists(Path.Combine(Path.GetDirectoryName(outputPath),
                                                       Path.GetFileNameWithoutExtension(outputPath)));

            TextReader resReader;

            if (isResponse)
            {
                resReader = new StreamReader(Path.Combine(Path.GetDirectoryName(outputPath),
                                                          Path.GetFileNameWithoutExtension(outputPath)));
            }
            else
            {
                resReader = new StringReader(Path.GetFileNameWithoutExtension(outputPath));
            }

            if (isResponse)
            {
                eject = true;
            }

            PluginBase            plugins    = GetPluginBase.Instance;
            List <IWritableImage> candidates = new List <IWritableImage>();
            string extension = Path.GetExtension(outputPath);

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

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

            while (true)
            {
                string responseLine = resReader.ReadLine();

                if (responseLine is null)
                {
                    break;
                }

                if (responseLine.Any(c => c < 0x20))
                {
                    AaruConsole.ErrorWriteLine("Invalid characters found in list of files, exiting...");

                    return((int)ErrorNumber.InvalidArgument);
                }

                if (isResponse)
                {
                    AaruConsole.WriteLine("Please insert media with title {0} and press any key to continue...",
                                          responseLine);

                    System.Console.ReadKey();
                    Thread.Sleep(1000);
                }

                responseLine = responseLine.Replace('/', '/');

                // Replace Windows forbidden filename characters with Japanese equivalents that are visually the same, but bigger.
                if (DetectOS.IsWindows)
                {
                    responseLine = responseLine.Replace('<', '\uFF1C').Replace('>', '\uFF1E').Replace(':', '\uFF1A').
                                   Replace('"', '\u2033').Replace('\\', '\').Replace('|', '|').
                                   Replace('?', '?').Replace('*', '*');
                }

                if (devicePath.Length == 2 &&
                    devicePath[1] == ':' &&
                    devicePath[0] != '/' &&
                    char.IsLetter(devicePath[0]))
                {
                    devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
                }

                Devices.Device dev;

                try
                {
                    dev = new Devices.Device(devicePath);

                    if (dev.IsRemote)
                    {
                        Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem,
                                             dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture);
                    }

                    if (dev.Error)
                    {
                        AaruConsole.ErrorWriteLine(Error.Print(dev.LastError));

                        if (isResponse)
                        {
                            continue;
                        }

                        return((int)ErrorNumber.CannotOpenDevice);
                    }
                }
                catch (DeviceException e)
                {
                    AaruConsole.ErrorWriteLine(e.Message ?? Error.Print(e.LastError));

                    if (isResponse)
                    {
                        continue;
                    }

                    return((int)ErrorNumber.CannotOpenDevice);
                }

                Statistics.AddDevice(dev);

                string outputPrefix = Path.Combine(Path.GetDirectoryName(outputPath), responseLine);

                Resume resumeClass = null;
                var    xs          = new XmlSerializer(typeof(Resume));

                if (File.Exists(outputPrefix + ".resume.xml") && resume)
                {
                    try
                    {
                        var sr = new StreamReader(outputPrefix + ".resume.xml");
                        resumeClass = (Resume)xs.Deserialize(sr);
                        sr.Close();
                    }
                    catch
                    {
                        AaruConsole.ErrorWriteLine("Incorrect resume file, not continuing...");

                        if (isResponse)
                        {
                            continue;
                        }

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

                if (resumeClass != null &&
                    resumeClass.NextBlock > resumeClass.LastBlock &&
                    resumeClass.BadBlocks.Count == 0 &&
                    !resumeClass.Tape &&
                    (resumeClass.BadSubchannels is null || resumeClass.BadSubchannels.Count == 0))
                {
                    AaruConsole.WriteLine("Media already dumped correctly, not continuing...");

                    if (isResponse)
                    {
                        continue;
                    }

                    return((int)ErrorNumber.AlreadyDumped);
                }

                CICMMetadataType sidecar = null;
                var sidecarXs            = new XmlSerializer(typeof(CICMMetadataType));

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

                            if (isResponse)
                            {
                                continue;
                            }

                            return((int)ErrorNumber.InvalidSidecar);
                        }
                    }
                    else
                    {
                        AaruConsole.ErrorWriteLine("Could not find metadata sidecar, not continuing...");

                        if (isResponse)
                        {
                            continue;
                        }

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

                plugins    = GetPluginBase.Instance;
                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)));
                }

                IWritableImage outputFormat = candidates[0];

                var dumpLog = new DumpLog(outputPrefix + ".log", dev, @private);

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

                var errorLog = new ErrorLog(outputPrefix + ".error.log");

                var dumper = new Dump(resume, dev, devicePath, outputFormat, retryPasses, force, false, persistent,
                                      stopOnError, resumeClass, dumpLog, encodingClass, outputPrefix,
                                      outputPrefix + extension, parsedOptions, sidecar, skip, metadata, trim,
                                      firstPregap, fixOffset, debug, wantedSubchannel, speed, @private,
                                      fixSubchannelPosition, retrySubchannel, fixSubchannel, fixSubchannelCrc,
                                      skipCdiReadyHole, errorLog, generateSubchannels);

                dumper.UpdateStatus         += Progress.UpdateStatus;
                dumper.ErrorMessage         += Progress.ErrorMessage;
                dumper.StoppingErrorMessage += Progress.ErrorMessage;
                dumper.UpdateProgress       += Progress.UpdateProgress;
                dumper.PulseProgress        += Progress.PulseProgress;
                dumper.InitProgress         += Progress.InitProgress;
                dumper.EndProgress          += Progress.EndProgress;
                dumper.InitProgress2        += Progress.InitProgress2;
                dumper.EndProgress2         += Progress.EndProgress2;
                dumper.UpdateProgress2      += Progress.UpdateProgress2;

                System.Console.CancelKeyPress += (sender, e) =>
                {
                    e.Cancel = true;
                    dumper.Abort();
                };

                dumper.Start();

                if (eject && dev.IsRemovable)
                {
                    switch (dev.Type)
                    {
                    case DeviceType.ATA:
                        dev.DoorUnlock(out _, dev.Timeout, out _);
                        dev.MediaEject(out _, dev.Timeout, out _);

                        break;

                    case DeviceType.ATAPI:
                    case DeviceType.SCSI:
                        switch (dev.ScsiType)
                        {
                        case PeripheralDeviceTypes.DirectAccess:
                        case PeripheralDeviceTypes.SimplifiedDevice:
                        case PeripheralDeviceTypes.SCSIZonedBlockDevice:
                        case PeripheralDeviceTypes.WriteOnceDevice:
                        case PeripheralDeviceTypes.OpticalDevice:
                        case PeripheralDeviceTypes.OCRWDevice:
                            dev.SpcAllowMediumRemoval(out _, dev.Timeout, out _);
                            dev.EjectTray(out _, dev.Timeout, out _);

                            break;

                        case PeripheralDeviceTypes.MultiMediaDevice:
                            dev.AllowMediumRemoval(out _, dev.Timeout, out _);
                            dev.EjectTray(out _, dev.Timeout, out _);

                            break;

                        case PeripheralDeviceTypes.SequentialAccess:
                            dev.SpcAllowMediumRemoval(out _, dev.Timeout, out _);
                            dev.LoadUnload(out _, true, false, false, false, false, dev.Timeout, out _);

                            break;
                        }

                        break;
                    }
                }

                dev.Close();
            }

            return((int)ErrorNumber.NoError);
        }
Ejemplo n.º 4
0
        public static int Invoke(bool debug, bool verbose, string cicmXml, string devicePath, bool resume,
                                 string encoding, bool firstPregap, bool fixOffset, bool force, bool metadata,
                                 bool trim, string outputPath, string options, bool persistent, ushort retryPasses,
                                 uint skip, byte speed, bool stopOnError, string format, string subchannel,
                                 bool @private)
        {
            MainClass.PrintCopyright();

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

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

            Statistics.AddCommand("dump-media");

            AaruConsole.DebugWriteLine("Dump-Media command", "--cicm-xml={0}", cicmXml);
            AaruConsole.DebugWriteLine("Dump-Media command", "--debug={0}", debug);
            AaruConsole.DebugWriteLine("Dump-Media command", "--device={0}", devicePath);
            AaruConsole.DebugWriteLine("Dump-Media command", "--encoding={0}", encoding);
            AaruConsole.DebugWriteLine("Dump-Media command", "--first-pregap={0}", firstPregap);
            AaruConsole.DebugWriteLine("Dump-Media command", "--fix-offset={0}", fixOffset);
            AaruConsole.DebugWriteLine("Dump-Media command", "--force={0}", force);
            AaruConsole.DebugWriteLine("Dump-Media command", "--format={0}", format);
            AaruConsole.DebugWriteLine("Dump-Media command", "--metadata={0}", metadata);
            AaruConsole.DebugWriteLine("Dump-Media command", "--options={0}", options);
            AaruConsole.DebugWriteLine("Dump-Media command", "--output={0}", outputPath);
            AaruConsole.DebugWriteLine("Dump-Media command", "--persistent={0}", persistent);
            AaruConsole.DebugWriteLine("Dump-Media command", "--resume={0}", resume);
            AaruConsole.DebugWriteLine("Dump-Media command", "--retry-passes={0}", retryPasses);
            AaruConsole.DebugWriteLine("Dump-Media command", "--skip={0}", skip);
            AaruConsole.DebugWriteLine("Dump-Media command", "--stop-on-error={0}", stopOnError);
            AaruConsole.DebugWriteLine("Dump-Media command", "--trim={0}", trim);
            AaruConsole.DebugWriteLine("Dump-Media command", "--verbose={0}", verbose);
            AaruConsole.DebugWriteLine("Dump-Media command", "--subchannel={0}", subchannel);
            AaruConsole.DebugWriteLine("Dump-Media command", "--private={0}", @private);

            // TODO: Disabled temporarily
            //AaruConsole.DebugWriteLine("Dump-Media command", "--raw={0}",           raw);

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

            AaruConsole.DebugWriteLine("Dump-Media command", "Parsed options:");

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

            Encoding encodingClass = null;

            if (encoding != null)
            {
                try
                {
                    encodingClass = Claunia.Encoding.Encoding.GetEncoding(encoding);

                    if (verbose)
                    {
                        AaruConsole.VerboseWriteLine("Using encoding for {0}.", encodingClass.EncodingName);
                    }
                }
                catch (ArgumentException)
                {
                    AaruConsole.ErrorWriteLine("Specified encoding is not supported.");

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

            DumpSubchannel wantedSubchannel = DumpSubchannel.Any;

            switch (subchannel?.ToLowerInvariant())
            {
            case "any":
            case null:
                wantedSubchannel = DumpSubchannel.Any;

                break;

            case "rw":
                wantedSubchannel = DumpSubchannel.Rw;

                break;

            case "rw-or-pq":
                wantedSubchannel = DumpSubchannel.RwOrPq;

                break;

            case "pq":
                wantedSubchannel = DumpSubchannel.Pq;

                break;

            case "none":
                wantedSubchannel = DumpSubchannel.None;

                break;

            default:
                AaruConsole.WriteLine("Incorrect subchannel type \"{0}\" requested.", subchannel);

                break;
            }

            if (devicePath.Length == 2 &&
                devicePath[1] == ':' &&
                devicePath[0] != '/' &&
                char.IsLetter(devicePath[0]))
            {
                devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
            }

            Devices.Device dev;

            try
            {
                dev = new Devices.Device(devicePath);

                if (dev.IsRemote)
                {
                    Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem,
                                         dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture);
                }

                if (dev.Error)
                {
                    AaruConsole.ErrorWriteLine(Error.Print(dev.LastError));

                    return((int)ErrorNumber.CannotOpenDevice);
                }
            }
            catch (DeviceException e)
            {
                AaruConsole.ErrorWriteLine(e.Message ?? Error.Print(e.LastError));

                return((int)ErrorNumber.CannotOpenDevice);
            }

            Statistics.AddDevice(dev);

            string outputPrefix = Path.Combine(Path.GetDirectoryName(outputPath),
                                               Path.GetFileNameWithoutExtension(outputPath));

            Resume resumeClass = null;
            var    xs          = new XmlSerializer(typeof(Resume));

            if (File.Exists(outputPrefix + ".resume.xml") && resume)
            {
                try
                {
                    var sr = new StreamReader(outputPrefix + ".resume.xml");
                    resumeClass = (Resume)xs.Deserialize(sr);
                    sr.Close();
                }
                catch
                {
                    AaruConsole.ErrorWriteLine("Incorrect resume file, not continuing...");

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

            if (resumeClass != null &&
                resumeClass.NextBlock > resumeClass.LastBlock &&
                resumeClass.BadBlocks.Count == 0 &&
                !resumeClass.Tape)
            {
                AaruConsole.WriteLine("Media already dumped correctly, not continuing...");

                return((int)ErrorNumber.AlreadyDumped);
            }

            CICMMetadataType sidecar = null;
            var sidecarXs            = new XmlSerializer(typeof(CICMMetadataType));

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

                        return((int)ErrorNumber.InvalidSidecar);
                    }
                }
                else
                {
                    AaruConsole.ErrorWriteLine("Could not find metadata sidecar, not continuing...");

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

            PluginBase            plugins    = GetPluginBase.Instance;
            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];

            var dumpLog = new DumpLog(outputPrefix + ".log", dev, @private);

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

            var dumper = new Dump(resume, dev, devicePath, outputFormat, retryPasses, force, false, persistent,
                                  stopOnError, resumeClass, dumpLog, encodingClass, outputPrefix, outputPath,
                                  parsedOptions, sidecar, skip, metadata, trim, firstPregap, fixOffset, debug,
                                  wantedSubchannel, speed, @private);

            dumper.UpdateStatus         += Progress.UpdateStatus;
            dumper.ErrorMessage         += Progress.ErrorMessage;
            dumper.StoppingErrorMessage += Progress.ErrorMessage;
            dumper.UpdateProgress       += Progress.UpdateProgress;
            dumper.PulseProgress        += Progress.PulseProgress;
            dumper.InitProgress         += Progress.InitProgress;
            dumper.EndProgress          += Progress.EndProgress;
            dumper.InitProgress2        += Progress.InitProgress2;
            dumper.EndProgress2         += Progress.EndProgress2;
            dumper.UpdateProgress2      += Progress.UpdateProgress2;

            System.Console.CancelKeyPress += (sender, e) =>
            {
                e.Cancel = true;
                dumper.Abort();
            };

            dumper.Start();

            dev.Close();

            return((int)ErrorNumber.NoError);
        }