Exemplo n.º 1
0
        public override int Invoke(IEnumerable <string> arguments)
        {
            List <string> extra = Options.Parse(arguments);

            if (showHelp)
            {
                Options.WriteOptionDescriptions(CommandSet.Out);

                return((int)ErrorNumber.HelpRequested);
            }

            MainClass.PrintCopyright();

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

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

            Statistics.AddCommand("dump-media");

            if (extra.Count > 2)
            {
                DicConsole.ErrorWriteLine("Too many arguments.");

                return((int)ErrorNumber.UnexpectedArgumentCount);
            }

            if (extra.Count <= 1)
            {
                DicConsole.ErrorWriteLine("Missing paths.");

                return((int)ErrorNumber.MissingArgument);
            }

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

            DicConsole.DebugWriteLine("Dump-Media command", "--cicm-xml={0}", cicmXml);
            DicConsole.DebugWriteLine("Dump-Media command", "--debug={0}", MainClass.Debug);
            DicConsole.DebugWriteLine("Dump-Media command", "--device={0}", devicePath);
            DicConsole.DebugWriteLine("Dump-Media command", "--encoding={0}", encodingName);
            DicConsole.DebugWriteLine("Dump-Media command", "--first-pregap={0}", firstTrackPregap);
            DicConsole.DebugWriteLine("Dump-Media command", "--force={0}", force);
            DicConsole.DebugWriteLine("Dump-Media command", "--force={0}", force);
            DicConsole.DebugWriteLine("Dump-Media command", "--format={0}", wantedOutputFormat);
            DicConsole.DebugWriteLine("Dump-Media command", "--no-metadata={0}", noMetadata);
            DicConsole.DebugWriteLine("Dump-Media command", "--options={0}", Options);
            DicConsole.DebugWriteLine("Dump-Media command", "--output={0}", outputFile);
            DicConsole.DebugWriteLine("Dump-Media command", "--persistent={0}", persistent);

            // TODO: Disabled temporarily
            //DicConsole.DebugWriteLine("Dump-Media command", "--raw={0}",           raw);
            DicConsole.DebugWriteLine("Dump-Media command", "--resume={0}", doResume);
            DicConsole.DebugWriteLine("Dump-Media command", "--retry-passes={0}", retryPasses);
            DicConsole.DebugWriteLine("Dump-Media command", "--skip={0}", skip);
            DicConsole.DebugWriteLine("Dump-Media command", "--stop-on-error={0}", stopOnError);
            DicConsole.DebugWriteLine("Dump-Media command", "--verbose={0}", MainClass.Verbose);

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

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

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

            Encoding encoding = null;

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

                    if (MainClass.Verbose)
                    {
                        DicConsole.VerboseWriteLine("Using encoding for {0}.", encoding.EncodingName);
                    }
                }
                catch (ArgumentException)
                {
                    DicConsole.ErrorWriteLine("Specified encoding is not supported.");

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

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

            Device dev;

            try
            {
                dev = new Device(devicePath);

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

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

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

                return((int)ErrorNumber.CannotOpenDevice);
            }

            Statistics.AddDevice(dev);

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

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

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

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

            if (resume != null &&
                resume.NextBlock > resume.LastBlock &&
                resume.BadBlocks.Count == 0 &&
                !resume.Tape)
            {
                DicConsole.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
                    {
                        DicConsole.ErrorWriteLine("Incorrect metadata sidecar file, not continuing...");

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

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

            PluginBase            plugins    = GetPluginBase.Instance;
            List <IWritableImage> candidates = new List <IWritableImage>();

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

            // Try Id
            else if (Guid.TryParse(wantedOutputFormat, out Guid outId))
            {
                candidates.AddRange(plugins.WritableImages.Values.Where(t => t.Id.Equals(outId)));
            }

            // Try name
            else
            {
                candidates.AddRange(plugins.WritableImages.Values.Where(t => string.Equals(t.Name, wantedOutputFormat,
                                                                                           StringComparison.
                                                                                           InvariantCultureIgnoreCase)));
            }

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

                return((int)ErrorNumber.FormatNotFound);
            }

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

                return((int)ErrorNumber.TooManyFormats);
            }

            IWritableImage outputFormat = candidates[0];

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

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

            var dumper = new Dump(doResume, dev, devicePath, outputFormat, retryPasses, force, false, persistent,
                                  stopOnError, resume, dumpLog, encoding, outputPrefix, outputFile, parsedOptions,
                                  sidecar, (uint)skip, noMetadata, noTrim, firstTrackPregap);

            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);
        }
Exemplo n.º 2
0
        /// <summary>Dumps an ATA device</summary>
        public void Ata()
        {
            if (_dumpRaw)
            {
                if (_force)
                {
                    ErrorMessage?.Invoke("Raw dumping not yet supported in ATA devices, continuing...");
                }
                else
                {
                    StoppingErrorMessage?.Invoke("Raw dumping not yet supported in ATA devices, aborting...");

                    return;
                }
            }

            const ushort ATA_PROFILE        = 0x0001;
            const uint   TIMEOUT            = 5;
            double       imageWriteDuration = 0;
            MediaType    mediaType          = MediaType.Unknown;

            UpdateStatus?.Invoke("Requesting ATA IDENTIFY DEVICE.");
            _dumpLog.WriteLine("Requesting ATA IDENTIFY DEVICE.");
            bool sense = _dev.AtaIdentify(out byte[] cmdBuf, out _);

            if (!sense &&
                Identify.Decode(cmdBuf).HasValue)
            {
                Identify.IdentifyDevice?ataIdNullable = Identify.Decode(cmdBuf);

                if (ataIdNullable != null)
                {
                    Identify.IdentifyDevice ataId = ataIdNullable.Value;
                    byte[] ataIdentify            = cmdBuf;
                    cmdBuf = new byte[0];

                    DateTime start;
                    DateTime end;
                    double   totalDuration = 0;
                    double   currentSpeed  = 0;
                    double   maxSpeed      = double.MinValue;
                    double   minSpeed      = double.MaxValue;

                    // Initializate reader
                    UpdateStatus?.Invoke("Initializing reader.");
                    _dumpLog.WriteLine("Initializing reader.");
                    var ataReader = new Reader(_dev, TIMEOUT, ataIdentify);

                    // Fill reader blocks
                    ulong blocks = ataReader.GetDeviceBlocks();

                    // Check block sizes
                    if (ataReader.GetBlockSize())
                    {
                        _dumpLog.WriteLine("ERROR: Cannot get block size: {0}.", ataReader.ErrorMessage);
                        ErrorMessage(ataReader.ErrorMessage);

                        return;
                    }

                    uint blockSize          = ataReader.LogicalBlockSize;
                    uint physicalsectorsize = ataReader.PhysicalBlockSize;

                    if (ataReader.FindReadCommand())
                    {
                        _dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", ataReader.ErrorMessage);
                        ErrorMessage(ataReader.ErrorMessage);

                        return;
                    }

                    // Check how many blocks to read, if error show and return
                    if (ataReader.GetBlocksToRead(_maximumReadable))
                    {
                        _dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", ataReader.ErrorMessage);
                        ErrorMessage(ataReader.ErrorMessage);

                        return;
                    }

                    uint   blocksToRead = ataReader.BlocksToRead;
                    ushort cylinders    = ataReader.Cylinders;
                    byte   heads        = ataReader.Heads;
                    byte   sectors      = ataReader.Sectors;

                    UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes).");

                    UpdateStatus?.
                    Invoke($"Device reports {cylinders} cylinders {heads} heads {sectors} sectors per track.");

                    UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time.");
                    UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block.");
                    UpdateStatus?.Invoke($"Device reports {physicalsectorsize} bytes per physical block.");
                    _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);

                    _dumpLog.WriteLine("Device reports {0} cylinders {1} heads {2} sectors per track.", cylinders,
                                       heads, sectors);

                    _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
                    _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize);
                    _dumpLog.WriteLine("Device reports {0} bytes per physical block.", physicalsectorsize);

                    bool removable = !_dev.IsCompactFlash &&
                                     ataId.GeneralConfiguration.HasFlag(Identify.GeneralConfigurationBit.Removable);

                    DumpHardwareType currentTry = null;
                    ExtentsULong     extents    = null;

                    ResumeSupport.Process(ataReader.IsLba, removable, blocks, _dev.Manufacturer, _dev.Model,
                                          _dev.Serial, _dev.PlatformId, ref _resume, ref currentTry, ref extents,
                                          _dev.FirmwareRevision, _private);

                    if (currentTry == null ||
                        extents == null)
                    {
                        StoppingErrorMessage?.Invoke("Could not process resume file, not continuing...");

                        return;
                    }

                    MhddLog mhddLog;
                    IbgLog  ibgLog;
                    double  duration;

                    bool ret = true;

                    if (_dev.IsUsb &&
                        _dev.UsbDescriptors != null &&
                        !_outputPlugin.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors))
                    {
                        ret = false;
                        _dumpLog.WriteLine("Output format does not support USB descriptors.");
                        ErrorMessage("Output format does not support USB descriptors.");
                    }

                    if (_dev.IsPcmcia &&
                        _dev.Cis != null &&
                        !_outputPlugin.SupportedMediaTags.Contains(MediaTagType.PCMCIA_CIS))
                    {
                        ret = false;
                        _dumpLog.WriteLine("Output format does not support PCMCIA CIS descriptors.");
                        ErrorMessage("Output format does not support PCMCIA CIS descriptors.");
                    }

                    if (!_outputPlugin.SupportedMediaTags.Contains(MediaTagType.ATA_IDENTIFY))
                    {
                        ret = false;
                        _dumpLog.WriteLine("Output format does not support ATA IDENTIFY.");
                        ErrorMessage("Output format does not support ATA IDENTIFY.");
                    }

                    if (!ret)
                    {
                        _dumpLog.WriteLine("Several media tags not supported, {0}continuing...", _force ? "" : "not ");

                        if (_force)
                        {
                            ErrorMessage("Several media tags not supported, continuing...");
                        }
                        else
                        {
                            StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing...");

                            return;
                        }
                    }

                    mediaType = MediaTypeFromDevice.GetFromAta(_dev.Manufacturer, _dev.Model, _dev.IsRemovable,
                                                               _dev.IsCompactFlash, _dev.IsPcmcia, blocks, blockSize);

                    ret = _outputPlugin.Create(_outputPath, mediaType, _formatOptions, blocks, blockSize);

                    // Cannot create image
                    if (!ret)
                    {
                        _dumpLog.WriteLine("Error creating output image, not continuing.");
                        _dumpLog.WriteLine(_outputPlugin.ErrorMessage);

                        StoppingErrorMessage?.Invoke("Error creating output image, not continuing." +
                                                     Environment.NewLine + _outputPlugin.ErrorMessage);

                        return;
                    }

                    // Setting geometry
                    _outputPlugin.SetGeometry(cylinders, heads, sectors);

                    if (ataReader.IsLba)
                    {
                        UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time.");

                        if (_skip < blocksToRead)
                        {
                            _skip = blocksToRead;
                        }

                        mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead,
                                              _private);

                        ibgLog = new IbgLog(_outputPrefix + ".ibg", ATA_PROFILE);

                        if (_resume.NextBlock > 0)
                        {
                            UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}.");
                            _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
                        }

                        bool newTrim = false;

                        start = DateTime.UtcNow;
                        DateTime timeSpeedStart   = DateTime.UtcNow;
                        ulong    sectorSpeedStart = 0;
                        InitProgress?.Invoke();

                        for (ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
                        {
                            if (_aborted)
                            {
                                currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                                UpdateStatus?.Invoke("Aborted!");
                                _dumpLog.WriteLine("Aborted!");

                                break;
                            }

                            if (blocks - i < blocksToRead)
                            {
                                blocksToRead = (byte)(blocks - i);
                            }

                            #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                            if (currentSpeed > maxSpeed &&
                                currentSpeed != 0)
                            {
                                maxSpeed = currentSpeed;
                            }

                            if (currentSpeed < minSpeed &&
                                currentSpeed != 0)
                            {
                                minSpeed = currentSpeed;
                            }
                            #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                            UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)",
                                                   (long)i, (long)blocks);

                            bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration);

                            if (!error)
                            {
                                mhddLog.Write(i, duration);
                                ibgLog.Write(i, currentSpeed * 1024);
                                DateTime writeStart = DateTime.Now;
                                _outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
                                imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                                extents.Add(i, blocksToRead, true);
                            }
                            else
                            {
                                if (i + _skip > blocks)
                                {
                                    _skip = (uint)(blocks - i);
                                }

                                for (ulong b = i; b < i + _skip; b++)
                                {
                                    _resume.BadBlocks.Add(b);
                                }

                                mhddLog.Write(i, duration < 500 ? 65535 : duration);

                                ibgLog.Write(i, 0);
                                DateTime writeStart = DateTime.Now;
                                _outputPlugin.WriteSectors(new byte[blockSize * _skip], i, _skip);
                                imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                                _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
                                i      += _skip - blocksToRead;
                                newTrim = true;
                            }

                            sectorSpeedStart += blocksToRead;
                            _resume.NextBlock = i + blocksToRead;

                            double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;

                            if (elapsed < 1)
                            {
                                continue;
                            }

                            currentSpeed     = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                            sectorSpeedStart = 0;
                            timeSpeedStart   = DateTime.UtcNow;
                        }

                        end = DateTime.Now;
                        EndProgress?.Invoke();
                        mhddLog.Close();

                        ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                     (blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000), _devicePath);

                        UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");

                        UpdateStatus?.
                        Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");

                        UpdateStatus?.
                        Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec.");

                        _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);

                        _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
                                           ((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000));

                        _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
                                           ((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration);

                        #region Trimming
                        if (_resume.BadBlocks.Count > 0 &&
                            !_aborted &&
                            _trim &&
                            newTrim)
                        {
                            start = DateTime.UtcNow;
                            UpdateStatus?.Invoke("Trimming skipped sectors");
                            _dumpLog.WriteLine("Trimming skipped sectors");

                            ulong[] tmpArray = _resume.BadBlocks.ToArray();
                            InitProgress?.Invoke();

                            foreach (ulong badSector in tmpArray)
                            {
                                if (_aborted)
                                {
                                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                                    UpdateStatus?.Invoke("Aborted!");
                                    _dumpLog.WriteLine("Aborted!");

                                    break;
                                }

                                PulseProgress?.Invoke($"Trimming sector {badSector}");

                                bool error = ataReader.ReadBlock(out cmdBuf, badSector, out duration);

                                totalDuration += duration;

                                if (error)
                                {
                                    continue;
                                }

                                _resume.BadBlocks.Remove(badSector);
                                extents.Add(badSector);
                                _outputPlugin.WriteSector(cmdBuf, badSector);
                            }

                            EndProgress?.Invoke();
                            end = DateTime.UtcNow;
                            UpdateStatus?.Invoke($"Trimmming finished in {(end - start).TotalSeconds} seconds.");
                            _dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds);
                        }
                        #endregion Trimming

                        #region Error handling
                        if (_resume.BadBlocks.Count > 0 &&
                            !_aborted &&
                            _retryPasses > 0)
                        {
                            int  pass    = 1;
                            bool forward = true;

                            InitProgress?.Invoke();
repeatRetryLba:
                            ulong[] tmpArray = _resume.BadBlocks.ToArray();

                            foreach (ulong badSector in tmpArray)
                            {
                                if (_aborted)
                                {
                                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                                    UpdateStatus?.Invoke("Aborted!");
                                    _dumpLog.WriteLine("Aborted!");

                                    break;
                                }

                                PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector,
                                                                    pass, forward ? "forward" : "reverse",
                                                                    _persistent ? "recovering partial data, " : ""));

                                bool error = ataReader.ReadBlock(out cmdBuf, badSector, out duration);

                                totalDuration += duration;

                                if (!error)
                                {
                                    _resume.BadBlocks.Remove(badSector);
                                    extents.Add(badSector);
                                    _outputPlugin.WriteSector(cmdBuf, badSector);
                                    UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}.");
                                    _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
                                }
                                else if (_persistent)
                                {
                                    _outputPlugin.WriteSector(cmdBuf, badSector);
                                }
                            }

                            if (pass < _retryPasses &&
                                !_aborted &&
                                _resume.BadBlocks.Count > 0)
                            {
                                pass++;
                                forward = !forward;
                                _resume.BadBlocks.Sort();

                                if (!forward)
                                {
                                    _resume.BadBlocks.Reverse();
                                }

                                goto repeatRetryLba;
                            }

                            EndProgress?.Invoke();
                        }
                        #endregion Error handling LBA

                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                    }
                    else
                    {
                        mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead,
                                              _private);

                        ibgLog = new IbgLog(_outputPrefix + ".ibg", ATA_PROFILE);

                        ulong currentBlock = 0;
                        blocks = (ulong)(cylinders * heads * sectors);
                        start  = DateTime.UtcNow;
                        DateTime timeSpeedStart   = DateTime.UtcNow;
                        ulong    sectorSpeedStart = 0;
                        InitProgress?.Invoke();

                        for (ushort cy = 0; cy < cylinders; cy++)
                        {
                            for (byte hd = 0; hd < heads; hd++)
                            {
                                for (byte sc = 1; sc < sectors; sc++)
                                {
                                    if (_aborted)
                                    {
                                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                                        UpdateStatus?.Invoke("Aborted!");
                                        _dumpLog.WriteLine("Aborted!");

                                        break;
                                    }

                                    #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                                    if (currentSpeed > maxSpeed &&
                                        currentSpeed != 0)
                                    {
                                        maxSpeed = currentSpeed;
                                    }

                                    if (currentSpeed < minSpeed &&
                                        currentSpeed != 0)
                                    {
                                        minSpeed = currentSpeed;
                                    }
                                    #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                                    PulseProgress?.
                                    Invoke($"Reading cylinder {cy} head {hd} sector {sc} ({currentSpeed:F3} MiB/sec.)");

                                    bool error = ataReader.ReadChs(out cmdBuf, cy, hd, sc, out duration);

                                    totalDuration += duration;

                                    if (!error)
                                    {
                                        mhddLog.Write(currentBlock, duration);
                                        ibgLog.Write(currentBlock, currentSpeed * 1024);
                                        DateTime writeStart = DateTime.Now;

                                        _outputPlugin.WriteSector(cmdBuf,
                                                                  (ulong)((((cy * heads) + hd) * sectors) + (sc - 1)));

                                        imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                                        extents.Add(currentBlock);

                                        _dumpLog.WriteLine("Error reading cylinder {0} head {1} sector {2}.", cy, hd,
                                                           sc);
                                    }
                                    else
                                    {
                                        _resume.BadBlocks.Add(currentBlock);
                                        mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration);

                                        ibgLog.Write(currentBlock, 0);
                                        DateTime writeStart = DateTime.Now;

                                        _outputPlugin.WriteSector(new byte[blockSize],
                                                                  (ulong)((((cy * heads) + hd) * sectors) + (sc - 1)));

                                        imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                                    }

                                    sectorSpeedStart++;
                                    currentBlock++;

                                    double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;

                                    if (elapsed < 1)
                                    {
                                        continue;
                                    }

                                    currentSpeed     = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                                    sectorSpeedStart = 0;
                                    timeSpeedStart   = DateTime.UtcNow;
                                }
                            }
                        }

                        end = DateTime.Now;
                        EndProgress?.Invoke();
                        mhddLog.Close();

                        ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                     (blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000), _devicePath);

                        UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");

                        UpdateStatus?.
                        Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");

                        UpdateStatus?.
                        Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (imageWriteDuration / 1000):F3} KiB/sec.");

                        _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);

                        _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
                                           ((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000));

                        _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
                                           ((double)blockSize * (double)(blocks + 1)) / 1024 /
                                           (imageWriteDuration / 1000));
                    }

                    foreach (ulong bad in _resume.BadBlocks)
                    {
                        _dumpLog.WriteLine("Sector {0} could not be read.", bad);
                    }

                    _outputPlugin.SetDumpHardware(_resume.Tries);

                    // TODO: Non-removable
                    var metadata = new CommonTypes.Structs.ImageInfo
                    {
                        Application = "Aaru", ApplicationVersion = Version.GetVersion()
                    };

                    if (!_outputPlugin.SetMetadata(metadata))
                    {
                        ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine +
                                             _outputPlugin.ErrorMessage);
                    }

                    if (_preSidecar != null)
                    {
                        _outputPlugin.SetCicmMetadata(_preSidecar);
                    }

                    _dumpLog.WriteLine("Closing output file.");
                    UpdateStatus?.Invoke("Closing output file.");
                    DateTime closeStart = DateTime.Now;
                    _outputPlugin.Close();
                    DateTime closeEnd = DateTime.Now;
                    UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds.");
                    _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);

                    if (_aborted)
                    {
                        _dumpLog.WriteLine("Aborted!");
                        UpdateStatus?.Invoke("Aborted!");

                        return;
                    }

                    double totalChkDuration = 0;

                    if (_metadata)
                    {
                        _dumpLog.WriteLine("Creating sidecar.");
                        UpdateStatus?.Invoke("Creating sidecar.");
                        var         filters     = new FiltersList();
                        IFilter     filter      = filters.GetFilter(_outputPath);
                        IMediaImage inputPlugin = ImageFormat.Detect(filter);

                        if (!inputPlugin.Open(filter))
                        {
                            StoppingErrorMessage?.Invoke("Could not open created image.");

                            return;
                        }

                        DateTime chkStart = DateTime.UtcNow;

                        _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding);

                        _sidecarClass.InitProgressEvent    += InitProgress;
                        _sidecarClass.UpdateProgressEvent  += UpdateProgress;
                        _sidecarClass.EndProgressEvent     += EndProgress;
                        _sidecarClass.InitProgressEvent2   += InitProgress2;
                        _sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
                        _sidecarClass.EndProgressEvent2    += EndProgress2;
                        _sidecarClass.UpdateStatusEvent    += UpdateStatus;
                        CICMMetadataType sidecar = _sidecarClass.Create();

                        if (_preSidecar != null)
                        {
                            _preSidecar.BlockMedia = sidecar.BlockMedia;
                            sidecar = _preSidecar;
                        }

                        if (_dev.IsUsb &&
                            _dev.UsbDescriptors != null)
                        {
                            _dumpLog.WriteLine("Reading USB descriptors.");
                            UpdateStatus?.Invoke("Reading USB descriptors.");
                            ret = _outputPlugin.WriteMediaTag(_dev.UsbDescriptors, MediaTagType.USB_Descriptors);

                            if (ret)
                            {
                                sidecar.BlockMedia[0].USB = new USBType
                                {
                                    ProductID = _dev.UsbProductId, VendorID = _dev.UsbVendorId, Descriptors =
                                        new DumpType
                                    {
                                        Image     = _outputPath, Size = (ulong)_dev.UsbDescriptors.Length,
                                        Checksums = Checksum.GetChecksums(_dev.UsbDescriptors).ToArray()
                                    }
                                }
                            }
                            ;
                        }

                        if (_dev.IsPcmcia &&
                            _dev.Cis != null)
                        {
                            _dumpLog.WriteLine("Reading PCMCIA CIS.");
                            UpdateStatus?.Invoke("Reading PCMCIA CIS.");
                            ret = _outputPlugin.WriteMediaTag(_dev.Cis, MediaTagType.PCMCIA_CIS);

                            if (ret)
                            {
                                sidecar.BlockMedia[0].PCMCIA = new PCMCIAType
                                {
                                    CIS = new DumpType
                                    {
                                        Image     = _outputPath, Size = (ulong)_dev.Cis.Length,
                                        Checksums = Checksum.GetChecksums(_dev.Cis).ToArray()
                                    }
                                }
                            }
                            ;

                            _dumpLog.WriteLine("Decoding PCMCIA CIS.");
                            UpdateStatus?.Invoke("Decoding PCMCIA CIS.");
                            Tuple[] tuples = CIS.GetTuples(_dev.Cis);

                            if (tuples != null)
                            {
                                foreach (Tuple tuple in tuples)
                                {
                                    switch (tuple.Code)
                                    {
                                    case TupleCodes.CISTPL_MANFID:
                                        ManufacturerIdentificationTuple manfid =
                                            CIS.DecodeManufacturerIdentificationTuple(tuple);

                                        if (manfid != null)
                                        {
                                            sidecar.BlockMedia[0].PCMCIA.ManufacturerCode = manfid.ManufacturerID;

                                            sidecar.BlockMedia[0].PCMCIA.CardCode = manfid.CardID;
                                            sidecar.BlockMedia[0].PCMCIA.ManufacturerCodeSpecified = true;
                                            sidecar.BlockMedia[0].PCMCIA.CardCodeSpecified         = true;
                                        }

                                        break;

                                    case TupleCodes.CISTPL_VERS_1:
                                        Level1VersionTuple vers = CIS.DecodeLevel1VersionTuple(tuple);

                                        if (vers != null)
                                        {
                                            sidecar.BlockMedia[0].PCMCIA.Manufacturer = vers.Manufacturer;
                                            sidecar.BlockMedia[0].PCMCIA.ProductName  = vers.Product;

                                            sidecar.BlockMedia[0].PCMCIA.Compliance =
                                                $"{vers.MajorVersion}.{vers.MinorVersion}";

                                            sidecar.BlockMedia[0].PCMCIA.AdditionalInformation =
                                                vers.AdditionalInformation;
                                        }

                                        break;
                                    }
                                }
                            }
                        }

                        if (!_private)
                        {
                            DeviceReport.ClearIdentify(ataIdentify);
                        }

                        ret = _outputPlugin.WriteMediaTag(ataIdentify, MediaTagType.ATA_IDENTIFY);

                        if (ret)
                        {
                            sidecar.BlockMedia[0].ATA = new ATAType
                            {
                                Identify = new DumpType
                                {
                                    Image     = _outputPath, Size = (ulong)cmdBuf.Length,
                                    Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
                                }
                            }
                        }
                        ;

                        DateTime chkEnd = DateTime.UtcNow;

                        totalChkDuration = (chkEnd - chkStart).TotalMilliseconds;
                        UpdateStatus?.Invoke($"Sidecar created in {(chkEnd - chkStart).TotalSeconds} seconds.");

                        UpdateStatus?.
                        Invoke($"Average checksum speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000):F3} KiB/sec.");

                        _dumpLog.WriteLine("Sidecar created in {0} seconds.", (chkEnd - chkStart).TotalSeconds);

                        _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
                                           ((double)blockSize * (double)(blocks + 1)) / 1024 /
                                           (totalChkDuration / 1000));

                        List <(ulong start, string type)> filesystems = new List <(ulong start, string type)>();

                        if (sidecar.BlockMedia[0].FileSystemInformation != null)
                        {
                            filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation
                                                 where partition.FileSystems != null
                                                 from fileSystem in partition.FileSystems
                                                 select(partition.StartSector, fileSystem.Type));
                        }

                        if (filesystems.Count > 0)
                        {
                            foreach (var filesystem in filesystems.Select(o => new
                            {
                                o.start, o.type
                            }).Distinct())
                            {
                                UpdateStatus?.
                                Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}");

                                _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type,
                                                   filesystem.start);
                            }
                        }

                        (string type, string subType) = CommonTypes.Metadata.MediaType.MediaTypeToString(mediaType);

                        sidecar.BlockMedia[0].DiskType          = type;
                        sidecar.BlockMedia[0].DiskSubType       = subType;
                        sidecar.BlockMedia[0].Interface         = "ATA";
                        sidecar.BlockMedia[0].LogicalBlocks     = blocks;
                        sidecar.BlockMedia[0].PhysicalBlockSize = physicalsectorsize;
                        sidecar.BlockMedia[0].LogicalBlockSize  = blockSize;
                        sidecar.BlockMedia[0].Manufacturer      = _dev.Manufacturer;
                        sidecar.BlockMedia[0].Model             = _dev.Model;

                        if (!_private)
                        {
                            sidecar.BlockMedia[0].Serial = _dev.Serial;
                        }

                        sidecar.BlockMedia[0].Size = blocks * blockSize;

                        if (cylinders > 0 &&
                            heads > 0 &&
                            sectors > 0)
                        {
                            sidecar.BlockMedia[0].Cylinders          = cylinders;
                            sidecar.BlockMedia[0].CylindersSpecified = true;
                            sidecar.BlockMedia[0].Heads                    = heads;
                            sidecar.BlockMedia[0].HeadsSpecified           = true;
                            sidecar.BlockMedia[0].SectorsPerTrack          = sectors;
                            sidecar.BlockMedia[0].SectorsPerTrackSpecified = true;
                        }

                        UpdateStatus?.Invoke("Writing metadata sidecar");

                        var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);

                        var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                        xmlSer.Serialize(xmlFs, sidecar);
                        xmlFs.Close();
                    }

                    UpdateStatus?.Invoke("");

                    UpdateStatus?.
                    Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");

                    UpdateStatus?.
                    Invoke($"Average speed: {((double)blockSize * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");

                    if (maxSpeed > 0)
                    {
                        UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
                    }

                    if (minSpeed > 0 &&
                        minSpeed < double.MaxValue)
                    {
                        UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
                    }

                    UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");

                    if (_resume.BadBlocks.Count > 0)
                    {
                        _resume.BadBlocks.Sort();
                    }

                    UpdateStatus?.Invoke("");
                }

                Statistics.AddMedia(mediaType, true);
            }
            else
            {
                StoppingErrorMessage?.Invoke("Unable to communicate with ATA device.");
            }
        }
    }
}
Exemplo n.º 3
0
        internal static void DoDumpMedia(DumpMediaOptions options)
        {
            // TODO: Be able to cancel hashing
            Sidecar.InitProgressEvent    += Progress.InitProgress;
            Sidecar.UpdateProgressEvent  += Progress.UpdateProgress;
            Sidecar.EndProgressEvent     += Progress.EndProgress;
            Sidecar.InitProgressEvent2   += Progress.InitProgress2;
            Sidecar.UpdateProgressEvent2 += Progress.UpdateProgress2;
            Sidecar.EndProgressEvent2    += Progress.EndProgress2;
            Sidecar.UpdateStatusEvent    += Progress.UpdateStatus;

            DicConsole.DebugWriteLine("Dump-Media command", "--debug={0}", options.Debug);
            DicConsole.DebugWriteLine("Dump-Media command", "--verbose={0}", options.Verbose);
            DicConsole.DebugWriteLine("Dump-Media command", "--device={0}", options.DevicePath);
            DicConsole.DebugWriteLine("Dump-Media command", "--raw={0}", options.Raw);
            DicConsole.DebugWriteLine("Dump-Media command", "--stop-on-error={0}", options.StopOnError);
            DicConsole.DebugWriteLine("Dump-Media command", "--force={0}", options.Force);
            DicConsole.DebugWriteLine("Dump-Media command", "--retry-passes={0}", options.RetryPasses);
            DicConsole.DebugWriteLine("Dump-Media command", "--persistent={0}", options.Persistent);
            DicConsole.DebugWriteLine("Dump-Media command", "--resume={0}", options.Resume);
            DicConsole.DebugWriteLine("Dump-Media command", "--lead-in={0}", options.LeadIn);
            DicConsole.DebugWriteLine("Dump-Media command", "--encoding={0}", options.EncodingName);
            DicConsole.DebugWriteLine("Dump-Media command", "--output={0}", options.OutputFile);
            DicConsole.DebugWriteLine("Dump-Media command", "--format={0}", options.OutputFormat);
            DicConsole.DebugWriteLine("Dump-Media command", "--force={0}", options.Force);
            DicConsole.DebugWriteLine("Dump-Media command", "--options={0}", options.Options);
            DicConsole.DebugWriteLine("Dump-Media command", "--cicm-xml={0}", options.CicmXml);
            DicConsole.DebugWriteLine("Dump-Media command", "--skip={0}", options.Skip);
            DicConsole.DebugWriteLine("Dump-Media command", "--no-metadata={0}", options.NoMetadata);

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

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

            Encoding encoding = null;

            if (options.EncodingName != null)
            {
                try
                {
                    encoding = Claunia.Encoding.Encoding.GetEncoding(options.EncodingName);
                    if (options.Verbose)
                    {
                        DicConsole.VerboseWriteLine("Using encoding for {0}.", encoding.EncodingName);
                    }
                }
                catch (ArgumentException)
                {
                    DicConsole.ErrorWriteLine("Specified encoding is not supported.");
                    return;
                }
            }

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

            Device dev = new Device(options.DevicePath);

            if (dev.Error)
            {
                DicConsole.ErrorWriteLine("Error {0} opening device.", dev.LastError);
                return;
            }

            Core.Statistics.AddDevice(dev);

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

            Resume        resume = null;
            XmlSerializer xs     = new XmlSerializer(typeof(Resume));

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

            if (resume != null && resume.NextBlock > resume.LastBlock && resume.BadBlocks.Count == 0)
            {
                DicConsole.WriteLine("Media already dumped correctly, not continuing...");
                return;
            }

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

            if (options.CicmXml != null)
            {
                if (File.Exists(options.CicmXml))
                {
                    try
                    {
                        StreamReader sr = new StreamReader(options.CicmXml);
                        sidecar = (CICMMetadataType)sidecarXs.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;
                }
            }

            PluginBase            plugins    = new PluginBase();
            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];

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

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

            switch (dev.Type)
            {
            case DeviceType.ATA:
                Ata.Dump(dev, options.DevicePath, outputFormat, options.RetryPasses, options.Force, options.Raw,
                         options.Persistent, options.StopOnError, ref resume, ref dumpLog, encoding, outputPrefix,
                         options.OutputFile, parsedOptions, sidecar, (uint)options.Skip, options.NoMetadata,
                         options.NoTrim);
                break;

            case DeviceType.MMC:
            case DeviceType.SecureDigital:
                SecureDigital.Dump(dev, options.DevicePath, outputFormat, options.RetryPasses, options.Force,
                                   options.Raw, options.Persistent, options.StopOnError, ref resume, ref dumpLog,
                                   encoding, outputPrefix, options.OutputFile, parsedOptions, sidecar,
                                   (uint)options.Skip, options.NoMetadata, options.NoTrim);
                break;

            case DeviceType.NVMe:
                NvMe.Dump(dev, options.DevicePath, outputFormat, options.RetryPasses, options.Force, options.Raw,
                          options.Persistent, options.StopOnError, ref resume, ref dumpLog, encoding, outputPrefix,
                          options.OutputFile, parsedOptions, sidecar, (uint)options.Skip, options.NoMetadata,
                          options.NoTrim);
                break;

            case DeviceType.ATAPI:
            case DeviceType.SCSI:
                Scsi.Dump(dev, options.DevicePath, outputFormat, options.RetryPasses, options.Force, options.Raw,
                          options.Persistent, options.StopOnError, ref resume, ref dumpLog, options.LeadIn,
                          encoding, outputPrefix, options.OutputFile, parsedOptions, sidecar, (uint)options.Skip,
                          options.NoMetadata, options.NoTrim);
                break;

            default:
                dumpLog.WriteLine("Unknown device type.");
                dumpLog.Close();
                throw new NotSupportedException("Unknown device type.");
            }

            if (resume != null && options.Resume)
            {
                resume.LastWriteDate = DateTime.UtcNow;
                resume.BadBlocks.Sort();

                if (File.Exists(outputPrefix + ".resume.xml"))
                {
                    File.Delete(outputPrefix + ".resume.xml");
                }

                FileStream fs = new FileStream(outputPrefix + ".resume.xml", FileMode.Create, FileAccess.ReadWrite);
                xs = new XmlSerializer(resume.GetType());
                xs.Serialize(fs, resume);
                fs.Close();
            }

            dumpLog.Close();

            Core.Statistics.AddCommand("dump-media");
        }
Exemplo n.º 4
0
        /// <summary>Creates a metadata sidecar for an optical disc (e.g. CD, DVD, GD, BD, XGD, GOD)</summary>
        /// <param name="image">Image</param>
        /// <param name="filterId">Filter uuid</param>
        /// <param name="imagePath">Image path</param>
        /// <param name="fi">Image file information</param>
        /// <param name="plugins">Image plugins</param>
        /// <param name="imgChecksums">List of image checksums</param>
        /// <param name="sidecar">Metadata sidecar</param>
        void OpticalDisc(IOpticalMediaImage image, Guid filterId, string imagePath, FileInfo fi, PluginBase plugins,
            List<ChecksumType> imgChecksums, ref CICMMetadataType sidecar, Encoding encoding)
        {
            if(aborted)
                return;

            sidecar.OpticalDisc = new[]
            {
                new OpticalDiscType
                {
                    Checksums = imgChecksums.ToArray(), Image = new ImageType
                    {
                        format = image.Format, offset = 0, offsetSpecified = true, Value = Path.GetFileName(imagePath)
                    },
                    Size = (ulong)fi.Length, Sequence = new SequenceType
                    {
                        MediaTitle = image.Info.MediaTitle
                    }
                }
            };

            if(image.Info.MediaSequence     != 0 &&
               image.Info.LastMediaSequence != 0)
            {
                sidecar.OpticalDisc[0].Sequence.MediaSequence = (uint)image.Info.MediaSequence;
                sidecar.OpticalDisc[0].Sequence.TotalMedia    = (uint)image.Info.LastMediaSequence;
            }
            else
            {
                sidecar.OpticalDisc[0].Sequence.MediaSequence = 1;
                sidecar.OpticalDisc[0].Sequence.TotalMedia    = 1;
            }

            MediaType dskType = image.Info.MediaType;

            UpdateStatus("Hashing media tags...");

            foreach(MediaTagType tagType in image.Info.ReadableMediaTags)
            {
                if(aborted)
                    return;

                switch(tagType)
                {
                    case MediaTagType.CD_ATIP:
                        sidecar.OpticalDisc[0].ATIP = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_ATIP)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.CD_ATIP).Length
                        };

                        ATIP.CDATIP? atip = ATIP.Decode(image.ReadDiskTag(MediaTagType.CD_ATIP));

                        if(atip.HasValue)
                            if(atip.Value.DDCD)
                                dskType = atip.Value.DiscType ? MediaType.DDCDRW : MediaType.DDCDR;
                            else
                                dskType = atip.Value.DiscType ? MediaType.CDRW : MediaType.CDR;

                        break;
                    case MediaTagType.DVD_BCA:
                        sidecar.OpticalDisc[0].BCA = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_BCA)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.DVD_BCA).Length
                        };

                        break;
                    case MediaTagType.BD_BCA:
                        sidecar.OpticalDisc[0].BCA = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.BD_BCA)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.BD_BCA).Length
                        };

                        break;
                    case MediaTagType.DVD_CMI:
                        sidecar.OpticalDisc[0].CMI = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_CMI)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.DVD_CMI).Length
                        };

                        CSS_CPRM.LeadInCopyright? cmi =
                            CSS_CPRM.DecodeLeadInCopyright(image.ReadDiskTag(MediaTagType.DVD_CMI));

                        if(cmi.HasValue)
                            switch(cmi.Value.CopyrightType)
                            {
                                case CopyrightType.AACS:
                                    sidecar.OpticalDisc[0].CopyProtection = "AACS";

                                    break;
                                case CopyrightType.CSS:
                                    sidecar.OpticalDisc[0].CopyProtection = "CSS";

                                    break;
                                case CopyrightType.CPRM:
                                    sidecar.OpticalDisc[0].CopyProtection = "CPRM";

                                    break;
                            }

                        break;
                    case MediaTagType.DVD_DMI:
                        sidecar.OpticalDisc[0].DMI = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_DMI)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.DVD_DMI).Length
                        };

                        if(DMI.IsXbox(image.ReadDiskTag(MediaTagType.DVD_DMI)))
                        {
                            dskType = MediaType.XGD;

                            sidecar.OpticalDisc[0].Dimensions = new DimensionsType
                            {
                                Diameter = 120, Thickness = 1.2
                            };
                        }
                        else if(DMI.IsXbox360(image.ReadDiskTag(MediaTagType.DVD_DMI)))
                        {
                            dskType = MediaType.XGD2;

                            sidecar.OpticalDisc[0].Dimensions = new DimensionsType
                            {
                                Diameter = 120, Thickness = 1.2
                            };
                        }

                        break;
                    case MediaTagType.DVD_PFI:
                        sidecar.OpticalDisc[0].PFI = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_PFI)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.DVD_PFI).Length
                        };

                        PFI.PhysicalFormatInformation? pfi = PFI.Decode(image.ReadDiskTag(MediaTagType.DVD_PFI));

                        if(pfi.HasValue)
                            if(dskType != MediaType.XGD    &&
                               dskType != MediaType.XGD2   &&
                               dskType != MediaType.XGD3   &&
                               dskType != MediaType.PS2DVD &&
                               dskType != MediaType.PS3DVD &&
                               dskType != MediaType.Nuon)
                            {
                                switch(pfi.Value.DiskCategory)
                                {
                                    case DiskCategory.DVDPR:
                                        dskType = MediaType.DVDPR;

                                        break;
                                    case DiskCategory.DVDPRDL:
                                        dskType = MediaType.DVDPRDL;

                                        break;
                                    case DiskCategory.DVDPRW:
                                        dskType = MediaType.DVDPRW;

                                        break;
                                    case DiskCategory.DVDPRWDL:
                                        dskType = MediaType.DVDPRWDL;

                                        break;
                                    case DiskCategory.DVDR:
                                        dskType = MediaType.DVDR;

                                        break;
                                    case DiskCategory.DVDRAM:
                                        dskType = MediaType.DVDRAM;

                                        break;
                                    case DiskCategory.DVDROM:
                                        dskType = MediaType.DVDROM;

                                        break;
                                    case DiskCategory.DVDRW:
                                        dskType = MediaType.DVDRW;

                                        break;
                                    case DiskCategory.HDDVDR:
                                        dskType = MediaType.HDDVDR;

                                        break;
                                    case DiskCategory.HDDVDRAM:
                                        dskType = MediaType.HDDVDRAM;

                                        break;
                                    case DiskCategory.HDDVDROM:
                                        dskType = MediaType.HDDVDROM;

                                        break;
                                    case DiskCategory.HDDVDRW:
                                        dskType = MediaType.HDDVDRW;

                                        break;
                                    case DiskCategory.Nintendo:
                                        dskType = MediaType.GOD;

                                        break;
                                    case DiskCategory.UMD:
                                        dskType = MediaType.UMD;

                                        break;
                                }

                                if(dskType               == MediaType.DVDR &&
                                   pfi.Value.PartVersion == 6)
                                    dskType = MediaType.DVDRDL;

                                if(dskType               == MediaType.DVDRW &&
                                   pfi.Value.PartVersion == 3)
                                    dskType = MediaType.DVDRWDL;

                                if(dskType            == MediaType.GOD &&
                                   pfi.Value.DiscSize == DVDSize.OneTwenty)
                                    dskType = MediaType.WOD;

                                sidecar.OpticalDisc[0].Dimensions = new DimensionsType();

                                if(dskType == MediaType.UMD)
                                {
                                    sidecar.OpticalDisc[0].Dimensions.Height          = 64;
                                    sidecar.OpticalDisc[0].Dimensions.HeightSpecified = true;
                                    sidecar.OpticalDisc[0].Dimensions.Width           = 63;
                                    sidecar.OpticalDisc[0].Dimensions.WidthSpecified  = true;
                                    sidecar.OpticalDisc[0].Dimensions.Thickness       = 4;
                                }
                                else
                                    switch(pfi.Value.DiscSize)
                                    {
                                        case DVDSize.Eighty:
                                            sidecar.OpticalDisc[0].Dimensions.Diameter          = 80;
                                            sidecar.OpticalDisc[0].Dimensions.DiameterSpecified = true;
                                            sidecar.OpticalDisc[0].Dimensions.Thickness         = 1.2;

                                            break;
                                        case DVDSize.OneTwenty:
                                            sidecar.OpticalDisc[0].Dimensions.Diameter          = 120;
                                            sidecar.OpticalDisc[0].Dimensions.DiameterSpecified = true;
                                            sidecar.OpticalDisc[0].Dimensions.Thickness         = 1.2;

                                            break;
                                    }
                            }

                        break;
                    case MediaTagType.CD_PMA:
                        sidecar.OpticalDisc[0].PMA = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_PMA)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.CD_PMA).Length
                        };

                        break;
                    case MediaTagType.CD_FullTOC:
                        sidecar.OpticalDisc[0].TOC = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_FullTOC)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.CD_FullTOC).Length
                        };

                        break;
                    case MediaTagType.CD_FirstTrackPregap:
                        sidecar.OpticalDisc[0].FirstTrackPregrap = new[]
                        {
                            new BorderType
                            {
                                Image = Path.GetFileName(imagePath),
                                Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_FirstTrackPregap)).
                                                     ToArray(),
                                Size = (ulong)image.ReadDiskTag(MediaTagType.CD_FirstTrackPregap).Length
                            }
                        };

                        break;
                    case MediaTagType.CD_LeadIn:
                        sidecar.OpticalDisc[0].LeadIn = new[]
                        {
                            new BorderType
                            {
                                Image     = Path.GetFileName(imagePath),
                                Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_LeadIn)).ToArray(),
                                Size      = (ulong)image.ReadDiskTag(MediaTagType.CD_LeadIn).Length
                            }
                        };

                        break;
                    case MediaTagType.Xbox_SecuritySector:
                        if(sidecar.OpticalDisc[0].Xbox == null)
                            sidecar.OpticalDisc[0].Xbox = new XboxType();

                        sidecar.OpticalDisc[0].Xbox.SecuritySectors = new[]
                        {
                            new XboxSecuritySectorsType
                            {
                                RequestNumber = 0, RequestVersion = 1, SecuritySectors = new DumpType
                                {
                                    Image = Path.GetFileName(imagePath),
                                    Checksums = Checksum.
                                                GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_SecuritySector)).
                                                ToArray(),
                                    Size = (ulong)image.ReadDiskTag(MediaTagType.Xbox_SecuritySector).Length
                                }
                            }
                        };

                        break;
                    case MediaTagType.Xbox_PFI:
                        if(sidecar.OpticalDisc[0].Xbox == null)
                            sidecar.OpticalDisc[0].Xbox = new XboxType();

                        sidecar.OpticalDisc[0].Xbox.PFI = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_PFI)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.Xbox_PFI).Length
                        };

                        break;
                    case MediaTagType.Xbox_DMI:
                        if(sidecar.OpticalDisc[0].Xbox == null)
                            sidecar.OpticalDisc[0].Xbox = new XboxType();

                        sidecar.OpticalDisc[0].Xbox.DMI = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_DMI)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.Xbox_DMI).Length
                        };

                        break;
                }
            }

            try
            {
                List<Session> sessions = image.Sessions;
                sidecar.OpticalDisc[0].Sessions = (uint)(sessions?.Count ?? 1);
            }
            catch
            {
                sidecar.OpticalDisc[0].Sessions = 1;
            }

            List<Track>     tracks  = image.Tracks;
            List<TrackType> trksLst = null;

            if(tracks != null)
            {
                sidecar.OpticalDisc[0].Tracks    = new uint[1];
                sidecar.OpticalDisc[0].Tracks[0] = (uint)tracks.Count;
                trksLst                          = new List<TrackType>();
            }

            if(sidecar.OpticalDisc[0].Dimensions == null &&
               image.Info.MediaType              != MediaType.Unknown)
                sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(image.Info.MediaType);

            if(aborted)
                return;

            InitProgress();

            UpdateStatus("Checking filesystems");
            List<Partition> partitions = Partitions.GetAll(image);
            Partitions.AddSchemesToStats(partitions);

            UpdateStatus("Hashing tracks...");

            foreach(Track trk in tracks)
            {
                if(aborted)
                {
                    EndProgress();

                    return;
                }

                var xmlTrk = new TrackType();

                switch(trk.TrackType)
                {
                    case CommonTypes.Enums.TrackType.Audio:
                        xmlTrk.TrackType1 = TrackTypeTrackType.audio;

                        break;
                    case CommonTypes.Enums.TrackType.CdMode2Form2:
                        xmlTrk.TrackType1 = TrackTypeTrackType.m2f2;

                        break;
                    case CommonTypes.Enums.TrackType.CdMode2Formless:
                        xmlTrk.TrackType1 = TrackTypeTrackType.mode2;

                        break;
                    case CommonTypes.Enums.TrackType.CdMode2Form1:
                        xmlTrk.TrackType1 = TrackTypeTrackType.m2f1;

                        break;
                    case CommonTypes.Enums.TrackType.CdMode1:
                        xmlTrk.TrackType1 = TrackTypeTrackType.mode1;

                        break;
                    case CommonTypes.Enums.TrackType.Data:
                        switch(sidecar.OpticalDisc[0].DiscType)
                        {
                            case"BD":
                                xmlTrk.TrackType1 = TrackTypeTrackType.bluray;

                                break;
                            case"DDCD":
                                xmlTrk.TrackType1 = TrackTypeTrackType.ddcd;

                                break;
                            case"DVD":
                                xmlTrk.TrackType1 = TrackTypeTrackType.dvd;

                                break;
                            case"HD DVD":
                                xmlTrk.TrackType1 = TrackTypeTrackType.hddvd;

                                break;
                            default:
                                xmlTrk.TrackType1 = TrackTypeTrackType.mode1;

                                break;
                        }

                        break;
                }

                xmlTrk.Sequence = new TrackSequenceType
                {
                    Session = trk.TrackSession, TrackNumber = trk.TrackSequence
                };

                xmlTrk.StartSector = trk.TrackStartSector;
                xmlTrk.EndSector   = trk.TrackEndSector;

                if(trk.Indexes != null &&
                   trk.Indexes.ContainsKey(0))
                    if(trk.Indexes.TryGetValue(0, out ulong idx0))
                        xmlTrk.StartSector = idx0;

                switch(sidecar.OpticalDisc[0].DiscType)
                {
                    case"CD":
                    case"GD":
                        xmlTrk.StartMSF = LbaToMsf((long)xmlTrk.StartSector);
                        xmlTrk.EndMSF   = LbaToMsf((long)xmlTrk.EndSector);

                        break;
                    case"DDCD":
                        xmlTrk.StartMSF = DdcdLbaToMsf((long)xmlTrk.StartSector);
                        xmlTrk.EndMSF   = DdcdLbaToMsf((long)xmlTrk.EndSector);

                        break;
                }

                xmlTrk.Image = new ImageType
                {
                    Value = Path.GetFileName(trk.TrackFile), format = trk.TrackFileType
                };

                if(trk.TrackFileOffset > 0)
                {
                    xmlTrk.Image.offset          = trk.TrackFileOffset;
                    xmlTrk.Image.offsetSpecified = true;
                }

                xmlTrk.Size           = (xmlTrk.EndSector - xmlTrk.StartSector + 1) * (ulong)trk.TrackRawBytesPerSector;
                xmlTrk.BytesPerSector = (uint)trk.TrackBytesPerSector;

                uint  sectorsToRead = 512;
                ulong sectors       = xmlTrk.EndSector - xmlTrk.StartSector + 1;
                ulong doneSectors   = 0;

                // If there is only one track, and it's the same as the image file (e.g. ".iso" files), don't re-checksum.
                if(image.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") &&

                   // Only if filter is none...
                   (filterId == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") ||

                    // ...or AppleDouble
                    filterId == new Guid("1b2165ee-c9df-4b21-bbbb-9e5892b2df4d")))
                    xmlTrk.Checksums = sidecar.OpticalDisc[0].Checksums;
                else
                {
                    UpdateProgress("Track {0} of {1}", trk.TrackSequence, tracks.Count);

                    // For fast debugging, skip checksum
                    //goto skipChecksum;

                    var trkChkWorker = new Checksum();

                    InitProgress2();

                    while(doneSectors < sectors)
                    {
                        if(aborted)
                        {
                            EndProgress();
                            EndProgress2();

                            return;
                        }

                        byte[] sector;

                        if(sectors - doneSectors >= sectorsToRead)
                        {
                            sector = image.ReadSectorsLong(doneSectors, sectorsToRead, xmlTrk.Sequence.TrackNumber);

                            UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));

                            doneSectors += sectorsToRead;
                        }
                        else
                        {
                            sector = image.ReadSectorsLong(doneSectors, (uint)(sectors - doneSectors),
                                                           xmlTrk.Sequence.TrackNumber);

                            UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));

                            doneSectors += sectors - doneSectors;
                        }

                        trkChkWorker.Update(sector);
                    }

                    List<ChecksumType> trkChecksums = trkChkWorker.End();

                    xmlTrk.Checksums = trkChecksums.ToArray();

                    EndProgress2();
                }

                if(trk.TrackSubchannelType != TrackSubchannelType.None)
                {
                    xmlTrk.SubChannel = new SubChannelType
                    {
                        Image = new ImageType
                        {
                            Value = trk.TrackSubchannelFile
                        },

                        // TODO: Packed subchannel has different size?
                        Size = (xmlTrk.EndSector - xmlTrk.StartSector + 1) * 96
                    };

                    switch(trk.TrackSubchannelType)
                    {
                        case TrackSubchannelType.Packed:
                        case TrackSubchannelType.PackedInterleaved:
                            xmlTrk.SubChannel.Image.format = "rw";

                            break;
                        case TrackSubchannelType.Raw:
                        case TrackSubchannelType.RawInterleaved:
                            xmlTrk.SubChannel.Image.format = "rw_raw";

                            break;
                        case TrackSubchannelType.Q16:
                        case TrackSubchannelType.Q16Interleaved:
                            xmlTrk.SubChannel.Image.format = "q16";

                            break;
                    }

                    if(trk.TrackFileOffset > 0)
                    {
                        xmlTrk.SubChannel.Image.offset          = trk.TrackSubchannelOffset;
                        xmlTrk.SubChannel.Image.offsetSpecified = true;
                    }

                    var subChkWorker = new Checksum();

                    sectors     = xmlTrk.EndSector - xmlTrk.StartSector + 1;
                    doneSectors = 0;

                    InitProgress2();

                    while(doneSectors < sectors)
                    {
                        if(aborted)
                        {
                            EndProgress();
                            EndProgress2();

                            return;
                        }

                        byte[] sector;

                        if(sectors - doneSectors >= sectorsToRead)
                        {
                            sector = image.ReadSectorsTag(doneSectors, sectorsToRead, xmlTrk.Sequence.TrackNumber,
                                                          SectorTagType.CdSectorSubchannel);

                            UpdateProgress2("Hashings subchannel sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));

                            doneSectors += sectorsToRead;
                        }
                        else
                        {
                            sector = image.ReadSectorsTag(doneSectors, (uint)(sectors - doneSectors),
                                                          xmlTrk.Sequence.TrackNumber,
                                                          SectorTagType.CdSectorSubchannel);

                            UpdateProgress2("Hashings subchannel sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));

                            doneSectors += sectors - doneSectors;
                        }

                        subChkWorker.Update(sector);
                    }

                    List<ChecksumType> subChecksums = subChkWorker.End();

                    xmlTrk.SubChannel.Checksums = subChecksums.ToArray();

                    EndProgress2();
                }

                // For fast debugging, skip checksum
                //skipChecksum:

                List<Partition> trkPartitions = partitions.
                                                Where(p => p.Start >= trk.TrackStartSector &&
                                                           p.End   <= trk.TrackEndSector).ToList();

                xmlTrk.FileSystemInformation = new PartitionType[1];

                if(trkPartitions.Count > 0)
                {
                    xmlTrk.FileSystemInformation = new PartitionType[trkPartitions.Count];

                    for(int i = 0; i < trkPartitions.Count; i++)
                    {
                        xmlTrk.FileSystemInformation[i] = new PartitionType
                        {
                            Description = trkPartitions[i].Description, EndSector = trkPartitions[i].End,
                            Name        = trkPartitions[i].Name, Sequence         = (uint)trkPartitions[i].Sequence,
                            StartSector = trkPartitions[i].Start, Type            = trkPartitions[i].Type
                        };

                        List<FileSystemType> lstFs = new List<FileSystemType>();

                        foreach(IFilesystem plugin in plugins.PluginsList.Values)
                            try
                            {
                                if(aborted)
                                {
                                    EndProgress();

                                    return;
                                }

                                if(!plugin.Identify(image, trkPartitions[i]))
                                    continue;

                                plugin.GetInformation(image, trkPartitions[i], out _, encoding);
                                lstFs.Add(plugin.XmlFsType);
                                Statistics.AddFilesystem(plugin.XmlFsType.Type);

                                switch(plugin.XmlFsType.Type)
                                {
                                    case"Opera":
                                        dskType = MediaType.ThreeDO;

                                        break;
                                    case"PC Engine filesystem":
                                        dskType = MediaType.SuperCDROM2;

                                        break;
                                    case"Nintendo Wii filesystem":
                                        dskType = MediaType.WOD;

                                        break;
                                    case"Nintendo Gamecube filesystem":
                                        dskType = MediaType.GOD;

                                        break;
                                }
                            }
                            #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
                            catch
                                #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
                            {
                                //DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name);
                            }

                        if(lstFs.Count > 0)
                            xmlTrk.FileSystemInformation[i].FileSystems = lstFs.ToArray();
                    }
                }
                else
                {
                    xmlTrk.FileSystemInformation[0] = new PartitionType
                    {
                        EndSector = xmlTrk.EndSector, StartSector = xmlTrk.StartSector
                    };

                    List<FileSystemType> lstFs = new List<FileSystemType>();

                    var xmlPart = new Partition
                    {
                        Start = xmlTrk.StartSector, Length         = xmlTrk.EndSector - xmlTrk.StartSector + 1,
                        Type  = xmlTrk.TrackType1.ToString(), Size = xmlTrk.Size, Sequence = xmlTrk.Sequence.TrackNumber
                    };

                    foreach(IFilesystem plugin in plugins.PluginsList.Values)
                        try
                        {
                            if(aborted)
                            {
                                EndProgress();

                                return;
                            }

                            if(!plugin.Identify(image, xmlPart))
                                continue;

                            plugin.GetInformation(image, xmlPart, out _, encoding);
                            lstFs.Add(plugin.XmlFsType);
                            Statistics.AddFilesystem(plugin.XmlFsType.Type);

                            switch(plugin.XmlFsType.Type)
                            {
                                case"Opera":
                                    dskType = MediaType.ThreeDO;

                                    break;
                                case"PC Engine filesystem":
                                    dskType = MediaType.SuperCDROM2;

                                    break;
                                case"Nintendo Wii filesystem":
                                    dskType = MediaType.WOD;

                                    break;
                                case"Nintendo Gamecube filesystem":
                                    dskType = MediaType.GOD;

                                    break;
                            }
                        }
                        #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
                        catch
                            #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
                        {
                            //DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name);
                        }

                    if(lstFs.Count > 0)
                        xmlTrk.FileSystemInformation[0].FileSystems = lstFs.ToArray();
                }

                trksLst.Add(xmlTrk);
            }

            EndProgress();

            if(trksLst != null)
                sidecar.OpticalDisc[0].Track = trksLst.ToArray();

            // All XGD3 all have the same number of blocks
            if(dskType                             == MediaType.XGD2 &&
               sidecar.OpticalDisc[0].Track.Length == 1)
            {
                ulong blocks = sidecar.OpticalDisc[0].Track[0].EndSector - sidecar.OpticalDisc[0].Track[0].StartSector +
                               1;

                if(blocks == 25063   || // Locked (or non compatible drive)
                   blocks == 4229664 || // Xtreme unlock
                   blocks == 4246304)   // Wxripper unlock
                    dskType = MediaType.XGD3;
            }

            (string type, string subType) discType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType);
            sidecar.OpticalDisc[0].DiscType    = discType.type;
            sidecar.OpticalDisc[0].DiscSubType = discType.subType;
            Statistics.AddMedia(dskType, false);

            if(image.DumpHardware != null)
                sidecar.OpticalDisc[0].DumpHardwareArray = image.DumpHardware.ToArray();
            else if(!string.IsNullOrEmpty(image.Info.DriveManufacturer)     ||
                    !string.IsNullOrEmpty(image.Info.DriveModel)            ||
                    !string.IsNullOrEmpty(image.Info.DriveFirmwareRevision) ||
                    !string.IsNullOrEmpty(image.Info.DriveSerialNumber))
                sidecar.OpticalDisc[0].DumpHardwareArray = new[]
                {
                    new DumpHardwareType
                    {
                        Extents = new[]
                        {
                            new ExtentType
                            {
                                Start = 0, End = image.Info.Sectors
                            }
                        },
                        Manufacturer = image.Info.DriveManufacturer, Model      = image.Info.DriveModel,
                        Firmware     = image.Info.DriveFirmwareRevision, Serial = image.Info.DriveSerialNumber,
                        Software =
                            new SoftwareType
                            {
                                Name = image.Info.Application, Version = image.Info.ApplicationVersion
                            }
                    }
                };
        }
Exemplo n.º 5
0
        /// <summary>
        ///     Creates a metadata sidecar for a block tape (e.g. scsi streaming)
        /// </summary>
        /// <param name="files">List of files</param>
        /// <param name="folderName">Dump path</param>
        /// <param name="blockSize">Expected block size in bytes</param>
        public static CICMMetadataType Create(string folderName, List <string> files, int blockSize)
        {
            CICMMetadataType sidecar = new CICMMetadataType
            {
                BlockMedia = new[]
                {
                    new BlockMediaType
                    {
                        Image = new ImageType {
                            format = "Directory", offsetSpecified = false, Value = folderName
                        },
                        Sequence = new SequenceType {
                            MediaTitle = folderName, MediaSequence = 1, TotalMedia = 1
                        },
                        PhysicalBlockSize = blockSize,
                        LogicalBlockSize  = blockSize,
                        TapeInformation   = new[]
                        {
                            new TapePartitionType
                            {
                                Image = new ImageType
                                {
                                    format          = "Directory",
                                    offsetSpecified = false,
                                    Value           = folderName
                                }
                            }
                        }
                    }
                }
            };

            long                currentBlock = 0;
            long                totalSize    = 0;
            Checksum            tapeWorker   = new Checksum();
            List <TapeFileType> tapeFiles    = new List <TapeFileType>();

            for (int i = 0; i < files.Count; i++)
            {
                FileStream   fs         = new FileStream(files[i], FileMode.Open, FileAccess.Read);
                Checksum     fileWorker = new Checksum();
                TapeFileType tapeFile   = new TapeFileType
                {
                    Image = new ImageType
                    {
                        format = "Raw disk image (sector by sector copy)",
                        offset = 0,
                        Value  = Path.GetFileName(files[i])
                    },
                    Size       = fs.Length,
                    BlockSize  = blockSize,
                    StartBlock = currentBlock,
                    Sequence   = i
                };

                const uint SECTORS_TO_READ = 512;
                long       sectors         = fs.Length / blockSize;
                long       doneSectors     = 0;

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

                    if (sectors - doneSectors >= SECTORS_TO_READ)
                    {
                        sector = new byte[SECTORS_TO_READ * blockSize];
                        fs.Read(sector, 0, sector.Length);
                        UpdateProgress2($"Hashing block {doneSectors} of {sectors} on file {i + 1} of {files.Count}",
                                        doneSectors, sectors);
                        doneSectors += SECTORS_TO_READ;
                    }
                    else
                    {
                        sector = new byte[(uint)(sectors - doneSectors) * blockSize];
                        fs.Read(sector, 0, sector.Length);
                        UpdateProgress2($"Hashing block {doneSectors} of {sectors} on file {i + 1} of {files.Count}",
                                        doneSectors, sectors);
                        doneSectors += sectors - doneSectors;
                    }

                    fileWorker.Update(sector);
                    tapeWorker.Update(sector);
                }

                tapeFile.EndBlock  = tapeFile.StartBlock + sectors - 1;
                currentBlock      += sectors;
                totalSize         += fs.Length;
                tapeFile.Checksums = fileWorker.End().ToArray();
                tapeFiles.Add(tapeFile);

                EndProgress2();
            }

            sidecar.BlockMedia[0].Checksums                    = tapeWorker.End().ToArray();
            sidecar.BlockMedia[0].ContentChecksums             = sidecar.BlockMedia[0].Checksums;
            sidecar.BlockMedia[0].Size                         = totalSize;
            sidecar.BlockMedia[0].LogicalBlocks                = currentBlock;
            sidecar.BlockMedia[0].TapeInformation[0].EndBlock  = currentBlock - 1;
            sidecar.BlockMedia[0].TapeInformation[0].Size      = totalSize;
            sidecar.BlockMedia[0].TapeInformation[0].Checksums = sidecar.BlockMedia[0].Checksums;
            sidecar.BlockMedia[0].TapeInformation[0].File      = tapeFiles.ToArray();

            // This is purely for convenience, as typically these kind of data represents QIC tapes
            if (blockSize == 512)
            {
                sidecar.BlockMedia[0].DiskType = "Quarter-inch cartridge";

                if (totalSize <= 20 * 1048576)
                {
                    sidecar.BlockMedia[0].DiskSubType = "QIC-11";
                }
                else if (totalSize <= 40 * 1048576)
                {
                    sidecar.BlockMedia[0].DiskSubType = "QIC-40";
                }
                else if (totalSize <= 60 * 1048576)
                {
                    sidecar.BlockMedia[0].DiskSubType = "QIC-24";
                }
                else if (totalSize <= 80 * 1048576)
                {
                    sidecar.BlockMedia[0].DiskSubType = "QIC-80";
                }
                else if (totalSize <= 120 * 1048576)
                {
                    sidecar.BlockMedia[0].DiskSubType = "QIC-120";
                }
                else if (totalSize <= 150 * 1048576)
                {
                    sidecar.BlockMedia[0].DiskSubType = "QIC-150";
                }
                else if (totalSize <= 320 * 1048576)
                {
                    sidecar.BlockMedia[0].DiskSubType = "QIC-320";
                }
                else if (totalSize <= 340 * 1048576)
                {
                    sidecar.BlockMedia[0].DiskSubType = "QIC-3010";
                }
                else if (totalSize <= 525 * 1048576)
                {
                    sidecar.BlockMedia[0].DiskSubType = "QIC-525";
                }
                else if (totalSize <= 670 * 1048576)
                {
                    sidecar.BlockMedia[0].DiskSubType = "QIC-3020";
                }
                else if (totalSize <= 1200 * 1048576)
                {
                    sidecar.BlockMedia[0].DiskSubType = "QIC-3080";
                }
                else if (totalSize <= 1350 * 1048576)
                {
                    sidecar.BlockMedia[0].DiskSubType = "QIC-1350";
                }
                else if (totalSize <= (long)4000 * 1048576)
                {
                    sidecar.BlockMedia[0].DiskSubType = "QIC-3095";
                }
                else
                {
                    sidecar.BlockMedia[0].DiskType    = "Unknown tape";
                    sidecar.BlockMedia[0].DiskSubType = "Unknown tape";
                }
            }
            else
            {
                sidecar.BlockMedia[0].DiskType    = "Unknown tape";
                sidecar.BlockMedia[0].DiskSubType = "Unknown tape";
            }

            return(sidecar);
        }
Exemplo n.º 6
0
        /// <summary>
        ///     Creates a metadata sidecar for an optical disc (e.g. CD, DVD, GD, BD, XGD, GOD)
        /// </summary>
        /// <param name="image">Image</param>
        /// <param name="filterId">Filter uuid</param>
        /// <param name="imagePath">Image path</param>
        /// <param name="fi">Image file information</param>
        /// <param name="plugins">Image plugins</param>
        /// <param name="imgChecksums">List of image checksums</param>
        /// <param name="sidecar">Metadata sidecar</param>
        static void OpticalDisc(IMediaImage image, Guid filterId, string imagePath,
                                FileInfo fi, PluginBase plugins,
                                List <ChecksumType> imgChecksums, ref CICMMetadataType sidecar, Encoding encoding)
        {
            sidecar.OpticalDisc = new[]
            {
                new OpticalDiscType
                {
                    Checksums = imgChecksums.ToArray(),
                    Image     = new ImageType
                    {
                        format          = image.Format,
                        offset          = 0,
                        offsetSpecified = true,
                        Value           = Path.GetFileName(imagePath)
                    },
                    Size     = fi.Length,
                    Sequence = new SequenceType {
                        MediaTitle = image.Info.MediaTitle
                    }
                }
            };

            if (image.Info.MediaSequence != 0 && image.Info.LastMediaSequence != 0)
            {
                sidecar.OpticalDisc[0].Sequence.MediaSequence = image.Info.MediaSequence;
                sidecar.OpticalDisc[0].Sequence.TotalMedia    = image.Info.LastMediaSequence;
            }
            else
            {
                sidecar.OpticalDisc[0].Sequence.MediaSequence = 1;
                sidecar.OpticalDisc[0].Sequence.TotalMedia    = 1;
            }

            MediaType dskType = image.Info.MediaType;

            foreach (MediaTagType tagType in image.Info.ReadableMediaTags)
            {
                switch (tagType)
                {
                case MediaTagType.CD_ATIP:
                    sidecar.OpticalDisc[0].ATIP = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_ATIP)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.CD_ATIP).Length
                    };
                    ATIP.CDATIP?atip = ATIP.Decode(image.ReadDiskTag(MediaTagType.CD_ATIP));
                    if (atip.HasValue)
                    {
                        if (atip.Value.DDCD)
                        {
                            dskType = atip.Value.DiscType ? MediaType.DDCDRW : MediaType.DDCDR;
                        }
                        else
                        {
                            dskType = atip.Value.DiscType ? MediaType.CDRW : MediaType.CDR;
                        }
                    }
                    break;

                case MediaTagType.DVD_BCA:
                    sidecar.OpticalDisc[0].BCA = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_BCA)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.DVD_BCA).Length
                    };
                    break;

                case MediaTagType.BD_BCA:
                    sidecar.OpticalDisc[0].BCA = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.BD_BCA)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.BD_BCA).Length
                    };
                    break;

                case MediaTagType.DVD_CMI:
                    sidecar.OpticalDisc[0].CMI = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_CMI)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.DVD_CMI).Length
                    };
                    CSS_CPRM.LeadInCopyright?cmi =
                        CSS_CPRM.DecodeLeadInCopyright(image.ReadDiskTag(MediaTagType.DVD_CMI));
                    if (cmi.HasValue)
                    {
                        switch (cmi.Value.CopyrightType)
                        {
                        case CopyrightType.AACS:
                            sidecar.OpticalDisc[0].CopyProtection = "AACS";
                            break;

                        case CopyrightType.CSS:
                            sidecar.OpticalDisc[0].CopyProtection = "CSS";
                            break;

                        case CopyrightType.CPRM:
                            sidecar.OpticalDisc[0].CopyProtection = "CPRM";
                            break;
                        }
                    }

                    break;

                case MediaTagType.DVD_DMI:
                    sidecar.OpticalDisc[0].DMI = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_DMI)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.DVD_DMI).Length
                    };
                    if (DMI.IsXbox(image.ReadDiskTag(MediaTagType.DVD_DMI)))
                    {
                        dskType = MediaType.XGD;
                        sidecar.OpticalDisc[0].Dimensions = new DimensionsType {
                            Diameter = 120, Thickness = 1.2
                        };
                    }
                    else if (DMI.IsXbox360(image.ReadDiskTag(MediaTagType.DVD_DMI)))
                    {
                        dskType = MediaType.XGD2;
                        sidecar.OpticalDisc[0].Dimensions = new DimensionsType {
                            Diameter = 120, Thickness = 1.2
                        };
                    }

                    break;

                case MediaTagType.DVD_PFI:
                    sidecar.OpticalDisc[0].PFI = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_PFI)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.DVD_PFI).Length
                    };
                    PFI.PhysicalFormatInformation?pfi = PFI.Decode(image.ReadDiskTag(MediaTagType.DVD_PFI));
                    if (pfi.HasValue)
                    {
                        if (dskType != MediaType.XGD && dskType != MediaType.XGD2 && dskType != MediaType.XGD3)
                        {
                            switch (pfi.Value.DiskCategory)
                            {
                            case DiskCategory.DVDPR:
                                dskType = MediaType.DVDPR;
                                break;

                            case DiskCategory.DVDPRDL:
                                dskType = MediaType.DVDPRDL;
                                break;

                            case DiskCategory.DVDPRW:
                                dskType = MediaType.DVDPRW;
                                break;

                            case DiskCategory.DVDPRWDL:
                                dskType = MediaType.DVDPRWDL;
                                break;

                            case DiskCategory.DVDR:
                                dskType = MediaType.DVDR;
                                break;

                            case DiskCategory.DVDRAM:
                                dskType = MediaType.DVDRAM;
                                break;

                            case DiskCategory.DVDROM:
                                dskType = MediaType.DVDROM;
                                break;

                            case DiskCategory.DVDRW:
                                dskType = MediaType.DVDRW;
                                break;

                            case DiskCategory.HDDVDR:
                                dskType = MediaType.HDDVDR;
                                break;

                            case DiskCategory.HDDVDRAM:
                                dskType = MediaType.HDDVDRAM;
                                break;

                            case DiskCategory.HDDVDROM:
                                dskType = MediaType.HDDVDROM;
                                break;

                            case DiskCategory.HDDVDRW:
                                dskType = MediaType.HDDVDRW;
                                break;

                            case DiskCategory.Nintendo:
                                dskType = MediaType.GOD;
                                break;

                            case DiskCategory.UMD:
                                dskType = MediaType.UMD;
                                break;
                            }

                            if (dskType == MediaType.DVDR && pfi.Value.PartVersion == 6)
                            {
                                dskType = MediaType.DVDRDL;
                            }
                            if (dskType == MediaType.DVDRW && pfi.Value.PartVersion == 3)
                            {
                                dskType = MediaType.DVDRWDL;
                            }
                            if (dskType == MediaType.GOD && pfi.Value.DiscSize == DVDSize.OneTwenty)
                            {
                                dskType = MediaType.WOD;
                            }

                            sidecar.OpticalDisc[0].Dimensions = new DimensionsType();
                            if (dskType == MediaType.UMD)
                            {
                                sidecar.OpticalDisc[0].Dimensions.Height          = 64;
                                sidecar.OpticalDisc[0].Dimensions.HeightSpecified = true;
                                sidecar.OpticalDisc[0].Dimensions.Width           = 63;
                                sidecar.OpticalDisc[0].Dimensions.WidthSpecified  = true;
                                sidecar.OpticalDisc[0].Dimensions.Thickness       = 4;
                            }
                            else
                            {
                                switch (pfi.Value.DiscSize)
                                {
                                case DVDSize.Eighty:
                                    sidecar.OpticalDisc[0].Dimensions.Diameter          = 80;
                                    sidecar.OpticalDisc[0].Dimensions.DiameterSpecified = true;
                                    sidecar.OpticalDisc[0].Dimensions.Thickness         = 1.2;
                                    break;

                                case DVDSize.OneTwenty:
                                    sidecar.OpticalDisc[0].Dimensions.Diameter          = 120;
                                    sidecar.OpticalDisc[0].Dimensions.DiameterSpecified = true;
                                    sidecar.OpticalDisc[0].Dimensions.Thickness         = 1.2;
                                    break;
                                }
                            }
                        }
                    }

                    break;

                case MediaTagType.CD_PMA:
                    sidecar.OpticalDisc[0].PMA = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_PMA)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.CD_PMA).Length
                    };
                    break;

                case MediaTagType.CD_FullTOC:
                    sidecar.OpticalDisc[0].TOC = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_FullTOC)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.CD_FullTOC).Length
                    };
                    break;

                case MediaTagType.CD_LeadIn:
                    sidecar.OpticalDisc[0].LeadIn = new[]
                    {
                        new BorderType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_LeadIn)).ToArray(),
                            Size      = image.ReadDiskTag(MediaTagType.CD_LeadIn).Length
                        }
                    };
                    break;

                case MediaTagType.Xbox_SecuritySector:
                    if (sidecar.OpticalDisc[0].Xbox == null)
                    {
                        sidecar.OpticalDisc[0].Xbox = new XboxType();
                    }

                    sidecar.OpticalDisc[0].Xbox.SecuritySectors = new[]
                    {
                        new XboxSecuritySectorsType
                        {
                            RequestNumber   = 0,
                            RequestVersion  = 1,
                            SecuritySectors = new DumpType
                            {
                                Image     = Path.GetFileName(imagePath),
                                Checksums =
                                    Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_SecuritySector))
                                    .ToArray(),
                                Size = image.ReadDiskTag(MediaTagType.Xbox_SecuritySector).Length
                            }
                        }
                    };

                    break;

                case MediaTagType.Xbox_PFI:
                    if (sidecar.OpticalDisc[0].Xbox == null)
                    {
                        sidecar.OpticalDisc[0].Xbox = new XboxType();
                    }

                    sidecar.OpticalDisc[0].Xbox.PFI = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_PFI)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.Xbox_PFI).Length
                    };
                    break;

                case MediaTagType.Xbox_DMI:
                    if (sidecar.OpticalDisc[0].Xbox == null)
                    {
                        sidecar.OpticalDisc[0].Xbox = new XboxType();
                    }

                    sidecar.OpticalDisc[0].Xbox.DMI = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_DMI)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.Xbox_DMI).Length
                    };
                    break;
                }
            }

            try
            {
                List <Session> sessions = image.Sessions;
                sidecar.OpticalDisc[0].Sessions = sessions?.Count ?? 1;
            }
            catch { sidecar.OpticalDisc[0].Sessions = 1; }

            List <Track>     tracks  = image.Tracks;
            List <TrackType> trksLst = null;

            if (tracks != null)
            {
                sidecar.OpticalDisc[0].Tracks    = new int[1];
                sidecar.OpticalDisc[0].Tracks[0] = tracks.Count;
                trksLst = new List <TrackType>();
            }

            if (sidecar.OpticalDisc[0].Dimensions == null && image.Info.MediaType != MediaType.Unknown)
            {
                sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(image.Info.MediaType);
            }

            InitProgress();

            UpdateStatus("Checking filesystems");
            List <Partition> partitions = Partitions.GetAll(image);

            Partitions.AddSchemesToStats(partitions);

            foreach (Track trk in tracks)
            {
                TrackType xmlTrk = new TrackType();
                switch (trk.TrackType)
                {
                case DiscImages.TrackType.Audio:
                    xmlTrk.TrackType1 = TrackTypeTrackType.audio;
                    break;

                case DiscImages.TrackType.CdMode2Form2:
                    xmlTrk.TrackType1 = TrackTypeTrackType.m2f2;
                    break;

                case DiscImages.TrackType.CdMode2Formless:
                    xmlTrk.TrackType1 = TrackTypeTrackType.mode2;
                    break;

                case DiscImages.TrackType.CdMode2Form1:
                    xmlTrk.TrackType1 = TrackTypeTrackType.m2f1;
                    break;

                case DiscImages.TrackType.CdMode1:
                    xmlTrk.TrackType1 = TrackTypeTrackType.mode1;
                    break;

                case DiscImages.TrackType.Data:
                    switch (sidecar.OpticalDisc[0].DiscType)
                    {
                    case "BD":
                        xmlTrk.TrackType1 = TrackTypeTrackType.bluray;
                        break;

                    case "DDCD":
                        xmlTrk.TrackType1 = TrackTypeTrackType.ddcd;
                        break;

                    case "DVD":
                        xmlTrk.TrackType1 = TrackTypeTrackType.dvd;
                        break;

                    case "HD DVD":
                        xmlTrk.TrackType1 = TrackTypeTrackType.hddvd;
                        break;

                    default:
                        xmlTrk.TrackType1 = TrackTypeTrackType.mode1;
                        break;
                    }

                    break;
                }

                xmlTrk.Sequence =
                    new TrackSequenceType {
                    Session = trk.TrackSession, TrackNumber = (int)trk.TrackSequence
                };
                xmlTrk.StartSector = (long)trk.TrackStartSector;
                xmlTrk.EndSector   = (long)trk.TrackEndSector;

                if (trk.Indexes != null && trk.Indexes.ContainsKey(0))
                {
                    if (trk.Indexes.TryGetValue(0, out ulong idx0))
                    {
                        xmlTrk.StartSector = (long)idx0;
                    }
                }

                switch (sidecar.OpticalDisc[0].DiscType)
                {
                case "CD":
                case "GD":
                    xmlTrk.StartMSF = LbaToMsf(xmlTrk.StartSector);
                    xmlTrk.EndMSF   = LbaToMsf(xmlTrk.EndSector);
                    break;

                case "DDCD":
                    xmlTrk.StartMSF = DdcdLbaToMsf(xmlTrk.StartSector);
                    xmlTrk.EndMSF   = DdcdLbaToMsf(xmlTrk.EndSector);
                    break;
                }

                xmlTrk.Image = new ImageType {
                    Value = Path.GetFileName(trk.TrackFile), format = trk.TrackFileType
                };

                if (trk.TrackFileOffset > 0)
                {
                    xmlTrk.Image.offset          = (long)trk.TrackFileOffset;
                    xmlTrk.Image.offsetSpecified = true;
                }

                xmlTrk.Size           = (xmlTrk.EndSector - xmlTrk.StartSector + 1) * trk.TrackRawBytesPerSector;
                xmlTrk.BytesPerSector = trk.TrackBytesPerSector;

                uint  sectorsToRead = 512;
                ulong sectors       = (ulong)(xmlTrk.EndSector - xmlTrk.StartSector + 1);
                ulong doneSectors   = 0;

                // If there is only one track, and it's the same as the image file (e.g. ".iso" files), don't re-checksum.
                if (image.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") &&
                    // Only if filter is none...
                    (filterId == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") ||
                     // ...or AppleDouble
                     filterId == new Guid("1b2165ee-c9df-4b21-bbbb-9e5892b2df4d")))
                {
                    xmlTrk.Checksums = sidecar.OpticalDisc[0].Checksums;
                }
                else
                {
                    UpdateProgress("Track {0} of {1}", trk.TrackSequence, tracks.Count);

                    // For fast debugging, skip checksum
                    //goto skipChecksum;

                    Checksum trkChkWorker = new Checksum();

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

                        if (sectors - doneSectors >= sectorsToRead)
                        {
                            sector = image.ReadSectorsLong(doneSectors, sectorsToRead,
                                                           (uint)xmlTrk.Sequence.TrackNumber);
                            UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));
                            doneSectors += sectorsToRead;
                        }
                        else
                        {
                            sector = image.ReadSectorsLong(doneSectors, (uint)(sectors - doneSectors),
                                                           (uint)xmlTrk.Sequence.TrackNumber);
                            UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));
                            doneSectors += sectors - doneSectors;
                        }

                        trkChkWorker.Update(sector);
                    }

                    List <ChecksumType> trkChecksums = trkChkWorker.End();

                    xmlTrk.Checksums = trkChecksums.ToArray();

                    EndProgress2();
                }

                if (trk.TrackSubchannelType != TrackSubchannelType.None)
                {
                    xmlTrk.SubChannel = new SubChannelType
                    {
                        Image = new ImageType {
                            Value = trk.TrackSubchannelFile
                        },
                        // TODO: Packed subchannel has different size?
                        Size = (xmlTrk.EndSector - xmlTrk.StartSector + 1) * 96
                    };

                    switch (trk.TrackSubchannelType)
                    {
                    case TrackSubchannelType.Packed:
                    case TrackSubchannelType.PackedInterleaved:
                        xmlTrk.SubChannel.Image.format = "rw";
                        break;

                    case TrackSubchannelType.Raw:
                    case TrackSubchannelType.RawInterleaved:
                        xmlTrk.SubChannel.Image.format = "rw_raw";
                        break;

                    case TrackSubchannelType.Q16:
                    case TrackSubchannelType.Q16Interleaved:
                        xmlTrk.SubChannel.Image.format = "q16";
                        break;
                    }

                    if (trk.TrackFileOffset > 0)
                    {
                        xmlTrk.SubChannel.Image.offset          = (long)trk.TrackSubchannelOffset;
                        xmlTrk.SubChannel.Image.offsetSpecified = true;
                    }

                    Checksum subChkWorker = new Checksum();

                    sectors     = (ulong)(xmlTrk.EndSector - xmlTrk.StartSector + 1);
                    doneSectors = 0;

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

                        if (sectors - doneSectors >= sectorsToRead)
                        {
                            sector = image.ReadSectorsTag(doneSectors, sectorsToRead, (uint)xmlTrk.Sequence.TrackNumber,
                                                          SectorTagType.CdSectorSubchannel);
                            UpdateProgress2("Hashings subchannel sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));
                            doneSectors += sectorsToRead;
                        }
                        else
                        {
                            sector = image.ReadSectorsTag(doneSectors, (uint)(sectors - doneSectors),
                                                          (uint)xmlTrk.Sequence.TrackNumber,
                                                          SectorTagType.CdSectorSubchannel);
                            UpdateProgress2("Hashings subchannel sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));
                            doneSectors += sectors - doneSectors;
                        }

                        subChkWorker.Update(sector);
                    }

                    List <ChecksumType> subChecksums = subChkWorker.End();

                    xmlTrk.SubChannel.Checksums = subChecksums.ToArray();

                    EndProgress2();
                }

                // For fast debugging, skip checksum
                //skipChecksum:

                List <Partition> trkPartitions =
                    partitions.Where(p => p.Start >= trk.TrackStartSector && p.End <= trk.TrackEndSector).ToList();

                xmlTrk.FileSystemInformation = new PartitionType[1];
                if (trkPartitions.Count > 0)
                {
                    xmlTrk.FileSystemInformation = new PartitionType[trkPartitions.Count];
                    for (int i = 0; i < trkPartitions.Count; i++)
                    {
                        xmlTrk.FileSystemInformation[i] = new PartitionType
                        {
                            Description = trkPartitions[i].Description,
                            EndSector   = (int)trkPartitions[i].End,
                            Name        = trkPartitions[i].Name,
                            Sequence    = (int)trkPartitions[i].Sequence,
                            StartSector = (int)trkPartitions[i].Start,
                            Type        = trkPartitions[i].Type
                        };
                        List <FileSystemType> lstFs = new List <FileSystemType>();

                        foreach (IFilesystem plugin in plugins.PluginsList.Values)
                        {
                            try
                            {
                                if (!plugin.Identify(image, trkPartitions[i]))
                                {
                                    continue;
                                }

                                plugin.GetInformation(image, trkPartitions[i], out _, encoding);
                                lstFs.Add(plugin.XmlFsType);
                                Statistics.AddFilesystem(plugin.XmlFsType.Type);

                                switch (plugin.XmlFsType.Type)
                                {
                                case "Opera":
                                    dskType = MediaType.ThreeDO;
                                    break;

                                case "PC Engine filesystem":
                                    dskType = MediaType.SuperCDROM2;
                                    break;

                                case "Nintendo Wii filesystem":
                                    dskType = MediaType.WOD;
                                    break;

                                case "Nintendo Gamecube filesystem":
                                    dskType = MediaType.GOD;
                                    break;
                                }
                            }
                        }
Exemplo n.º 7
0
        /// <summary>Creates optical metadata sidecar</summary>
        /// <param name="blockSize">Size of the read sector in bytes</param>
        /// <param name="blocks">Total number of positive sectors</param>
        /// <param name="mediaType">Disc type</param>
        /// <param name="layers">Disc layers</param>
        /// <param name="mediaTags">Media tags</param>
        /// <param name="sessions">Disc sessions</param>
        /// <param name="totalChkDuration">Total time spent doing checksums</param>
        /// <param name="discOffset">Disc write offset</param>
        void WriteOpticalSidecar(uint blockSize, ulong blocks, MediaType mediaType, LayersType layers,
                                 Dictionary <MediaTagType, byte[]> mediaTags, int sessions, out double totalChkDuration,
                                 int?discOffset)
        {
            _dumpLog.WriteLine("Creating sidecar.");
            var         filters     = new FiltersList();
            IFilter     filter      = filters.GetFilter(_outputPath);
            IMediaImage inputPlugin = ImageFormat.Detect(filter);

            totalChkDuration = 0;

            if (!inputPlugin.Open(filter))
            {
                StoppingErrorMessage?.Invoke("Could not open created image.");

                return;
            }

            DateTime chkStart = DateTime.UtcNow;

            // ReSharper disable once UseObjectOrCollectionInitializer
            _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding);
            _sidecarClass.InitProgressEvent    += InitProgress;
            _sidecarClass.UpdateProgressEvent  += UpdateProgress;
            _sidecarClass.EndProgressEvent     += EndProgress;
            _sidecarClass.InitProgressEvent2   += InitProgress2;
            _sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
            _sidecarClass.EndProgressEvent2    += EndProgress2;
            _sidecarClass.UpdateStatusEvent    += UpdateStatus;
            CICMMetadataType sidecar = _sidecarClass.Create();
            DateTime         end     = DateTime.UtcNow;

            if (_aborted)
            {
                return;
            }

            totalChkDuration = (end - chkStart).TotalMilliseconds;
            _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);

            _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
                               ((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000));

            if (_preSidecar != null)
            {
                _preSidecar.OpticalDisc = sidecar.OpticalDisc;
                sidecar = _preSidecar;
            }

            List <(ulong start, string type)> filesystems = new List <(ulong start, string type)>();

            if (sidecar.OpticalDisc[0].Track != null)
            {
                filesystems.AddRange(from xmlTrack in sidecar.OpticalDisc[0].Track
                                     where xmlTrack.FileSystemInformation != null
                                     from partition in xmlTrack.FileSystemInformation
                                     where partition.FileSystems != null from fileSystem in partition.FileSystems
                                     select(partition.StartSector, fileSystem.Type));
            }

            if (filesystems.Count > 0)
            {
                foreach (var filesystem in filesystems.Select(o => new
                {
                    o.start, o.type
                }).Distinct())
                {
                    _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start);
                }
            }

            sidecar.OpticalDisc[0].Dimensions        = Dimensions.DimensionsFromMediaType(mediaType);
            (string type, string subType)discType    = CommonTypes.Metadata.MediaType.MediaTypeToString(mediaType);
            sidecar.OpticalDisc[0].DiscType          = discType.type;
            sidecar.OpticalDisc[0].DiscSubType       = discType.subType;
            sidecar.OpticalDisc[0].DumpHardwareArray = _resume.Tries.ToArray();
            sidecar.OpticalDisc[0].Sessions          = (uint)sessions;
            sidecar.OpticalDisc[0].Layers            = layers;

            if (discOffset.HasValue)
            {
                sidecar.OpticalDisc[0].Offset          = (int)(discOffset / 4);
                sidecar.OpticalDisc[0].OffsetSpecified = true;
            }

            if (mediaTags != null)
            {
                foreach (KeyValuePair <MediaTagType, byte[]> tag in mediaTags.Where(tag => _outputPlugin.
                                                                                    SupportedMediaTags.
                                                                                    Contains(tag.Key)))
                {
                    AddMediaTagToSidecar(_outputPath, tag, ref sidecar);
                }
            }

            UpdateStatus?.Invoke("Writing metadata sidecar");

            var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);

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

            xmlSer.Serialize(xmlFs, sidecar);
            xmlFs.Close();
        }
Exemplo n.º 8
0
        /// <summary>Dumps a MultiMediaCard or SecureDigital flash card</summary>
        public void SecureDigital()
        {
            if (_dumpRaw)
            {
                if (_force)
                {
                    ErrorMessage?.
                    Invoke("Raw dumping is not supported in MultiMediaCard or SecureDigital devices. Continuing...");
                }
                else
                {
                    StoppingErrorMessage?.
                    Invoke("Raw dumping is not supported in MultiMediaCard or SecureDigital devices. Aborting...");

                    return;
                }
            }

            bool         sense;
            const ushort SD_PROFILE = 0x0001;
            const uint   TIMEOUT    = 5;
            double       duration;

            uint  blocksToRead = 128;
            uint  blockSize    = 512;
            ulong blocks       = 0;

            byte[] csd  = null;
            byte[] ocr  = null;
            byte[] ecsd = null;
            byte[] scr  = null;
            uint   physicalBlockSize = 0;
            bool   byteAddressed     = true;

            Dictionary <MediaTagType, byte[]> mediaTags = new Dictionary <MediaTagType, byte[]>();

            switch (_dev.Type)
            {
            case DeviceType.MMC:
            {
                UpdateStatus?.Invoke("Reading Extended CSD");
                _dumpLog.WriteLine("Reading Extended CSD");
                sense = _dev.ReadExtendedCsd(out ecsd, out _, TIMEOUT, out duration);

                if (!sense)
                {
                    ExtendedCSD ecsdDecoded = Decoders.MMC.Decoders.DecodeExtendedCSD(ecsd);
                    blocksToRead = ecsdDecoded.OptimalReadSize;
                    blocks       = ecsdDecoded.SectorCount;
                    blockSize    = (uint)(ecsdDecoded.SectorSize == 1 ? 4096 : 512);

                    if (ecsdDecoded.NativeSectorSize == 0)
                    {
                        physicalBlockSize = 512;
                    }
                    else if (ecsdDecoded.NativeSectorSize == 1)
                    {
                        physicalBlockSize = 4096;
                    }

                    // Supposing it's high-capacity MMC if it has Extended CSD...
                    byteAddressed = false;
                    mediaTags.Add(MediaTagType.MMC_ExtendedCSD, null);
                }
                else
                {
                    ecsd = null;
                }

                UpdateStatus?.Invoke("Reading CSD");
                _dumpLog.WriteLine("Reading CSD");
                sense = _dev.ReadCsd(out csd, out _, TIMEOUT, out duration);

                if (!sense)
                {
                    if (blocks == 0)
                    {
                        CSD csdDecoded = Decoders.MMC.Decoders.DecodeCSD(csd);
                        blocks    = (ulong)((csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2));
                        blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength);
                    }

                    mediaTags.Add(MediaTagType.MMC_CSD, null);
                }
                else
                {
                    csd = null;
                }

                UpdateStatus?.Invoke("Reading OCR");
                _dumpLog.WriteLine("Reading OCR");
                sense = _dev.ReadOcr(out ocr, out _, TIMEOUT, out duration);

                if (sense)
                {
                    ocr = null;
                }
                else
                {
                    mediaTags.Add(MediaTagType.MMC_OCR, null);
                }

                break;
            }

            case DeviceType.SecureDigital:
            {
                UpdateStatus?.Invoke("Reading CSD");
                _dumpLog.WriteLine("Reading CSD");
                sense = _dev.ReadCsd(out csd, out _, TIMEOUT, out duration);

                if (!sense)
                {
                    Decoders.SecureDigital.CSD csdDecoded = Decoders.SecureDigital.Decoders.DecodeCSD(csd);

                    blocks = (ulong)(csdDecoded.Structure == 0
                                             ? (csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2)
                                             : (csdDecoded.Size + 1) * 1024);

                    blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength);

                    // Structure >=1 for SDHC/SDXC, so that's block addressed
                    byteAddressed = csdDecoded.Structure == 0;
                    mediaTags.Add(MediaTagType.SD_CSD, null);
                }
                else
                {
                    csd = null;
                }

                UpdateStatus?.Invoke("Reading OCR");
                _dumpLog.WriteLine("Reading OCR");
                sense = _dev.ReadSdocr(out ocr, out _, TIMEOUT, out duration);

                if (sense)
                {
                    ocr = null;
                }
                else
                {
                    mediaTags.Add(MediaTagType.SD_OCR, null);
                }

                UpdateStatus?.Invoke("Reading SCR");
                _dumpLog.WriteLine("Reading SCR");
                sense = _dev.ReadScr(out scr, out _, TIMEOUT, out duration);

                if (sense)
                {
                    scr = null;
                }
                else
                {
                    mediaTags.Add(MediaTagType.SD_SCR, null);
                }

                break;
            }
            }

            UpdateStatus?.Invoke("Reading CID");
            _dumpLog.WriteLine("Reading CID");
            sense = _dev.ReadCid(out byte[] cid, out _, TIMEOUT, out duration);

            if (sense)
            {
                cid = null;
            }
            else
            {
                mediaTags.Add(_dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID : MediaTagType.MMC_CID, null);
            }

            DateTime start;
            DateTime end;
            double   totalDuration = 0;
            double   currentSpeed  = 0;
            double   maxSpeed      = double.MinValue;
            double   minSpeed      = double.MaxValue;

            if (blocks == 0)
            {
                _dumpLog.WriteLine("Unable to get device size.");
                StoppingErrorMessage?.Invoke("Unable to get device size.");

                return;
            }

            UpdateStatus?.Invoke($"Device reports {blocks} blocks.");
            _dumpLog.WriteLine("Device reports {0} blocks.", blocks);

            byte[] cmdBuf;
            bool   error;

            while (true)
            {
                error = _dev.Read(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, TIMEOUT, out duration);

                if (error)
                {
                    blocksToRead /= 2;
                }

                if (!error ||
                    blocksToRead == 1)
                {
                    break;
                }
            }

            if (error)
            {
                _dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", _dev.LastError);
                StoppingErrorMessage?.Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length.");

                return;
            }

            UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time.");
            _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);

            if (_skip < blocksToRead)
            {
                _skip = blocksToRead;
            }

            DumpHardwareType currentTry = null;
            ExtentsULong     extents    = null;

            ResumeSupport.Process(true, false, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId,
                                  ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, _private);

            if (currentTry == null ||
                extents == null)
            {
                StoppingErrorMessage?.Invoke("Could not process resume file, not continuing...");

                return;
            }

            bool ret = true;

            foreach (MediaTagType tag in mediaTags.Keys)
            {
                if (_outputPlugin.SupportedMediaTags.Contains(tag))
                {
                    continue;
                }

                ret = false;
                _dumpLog.WriteLine($"Output format does not support {tag}.");
                ErrorMessage?.Invoke($"Output format does not support {tag}.");
            }

            if (!ret)
            {
                if (_force)
                {
                    _dumpLog.WriteLine("Several media tags not supported, continuing...");
                    ErrorMessage?.Invoke("Several media tags not supported, continuing...");
                }
                else
                {
                    _dumpLog.WriteLine("Several media tags not supported, not continuing...");
                    StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing...");

                    return;
                }
            }

            var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private);
            var ibgLog  = new IbgLog(_outputPrefix + ".ibg", SD_PROFILE);

            ret = _outputPlugin.Create(_outputPath,
                                       _dev.Type == DeviceType.SecureDigital ? MediaType.SecureDigital : MediaType.MMC,
                                       _formatOptions, blocks, blockSize);

            // Cannot create image
            if (!ret)
            {
                _dumpLog.WriteLine("Error creating output image, not continuing.");
                _dumpLog.WriteLine(_outputPlugin.ErrorMessage);

                StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine +
                                             _outputPlugin.ErrorMessage);

                return;
            }

            if (_resume.NextBlock > 0)
            {
                UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}.");
                _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
            }

            start = DateTime.UtcNow;
            double   imageWriteDuration = 0;
            bool     newTrim            = false;
            DateTime timeSpeedStart     = DateTime.UtcNow;
            ulong    sectorSpeedStart   = 0;

            InitProgress?.Invoke();

            for (ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
            {
                if (_aborted)
                {
                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                    UpdateStatus?.Invoke("Aborted!");
                    _dumpLog.WriteLine("Aborted!");

                    break;
                }

                if (blocks - i < blocksToRead)
                {
                    blocksToRead = (byte)(blocks - i);
                }

                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                if (currentSpeed > maxSpeed &&
                    currentSpeed != 0)
                {
                    maxSpeed = currentSpeed;
                }

                if (currentSpeed < minSpeed &&
                    currentSpeed != 0)
                {
                    minSpeed = currentSpeed;
                }
                #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i,
                                       (long)blocks);

                error = _dev.Read(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed, TIMEOUT,
                                  out duration);

                if (!error)
                {
                    mhddLog.Write(i, duration);
                    ibgLog.Write(i, currentSpeed * 1024);
                    DateTime writeStart = DateTime.Now;
                    _outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    extents.Add(i, blocksToRead, true);
                }
                else
                {
                    if (i + _skip > blocks)
                    {
                        _skip = (uint)(blocks - i);
                    }

                    for (ulong b = i; b < i + _skip; b++)
                    {
                        _resume.BadBlocks.Add(b);
                    }

                    mhddLog.Write(i, duration < 500 ? 65535 : duration);

                    ibgLog.Write(i, 0);
                    DateTime writeStart = DateTime.Now;
                    _outputPlugin.WriteSectors(new byte[blockSize * _skip], i, _skip);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
                    i      += _skip - blocksToRead;
                    newTrim = true;
                }

                sectorSpeedStart += blocksToRead;
                _resume.NextBlock = i + blocksToRead;

                double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;

                if (elapsed < 1)
                {
                    continue;
                }

                currentSpeed     = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                sectorSpeedStart = 0;
                timeSpeedStart   = DateTime.UtcNow;
            }

            end = DateTime.Now;
            EndProgress?.Invoke();
            mhddLog.Close();

            ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                         (blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000),
                         _devicePath);

            UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");

            UpdateStatus?.
            Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");

            UpdateStatus?.
            Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec.");

            _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);

            _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
                               ((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000));

            _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
                               ((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration);

            #region Trimming
            if (_resume.BadBlocks.Count > 0 &&
                !_aborted &&
                _trim &&
                newTrim)
            {
                start = DateTime.UtcNow;
                UpdateStatus?.Invoke("Trimming skipped sectors");
                _dumpLog.WriteLine("Trimming skipped sectors");

                ulong[] tmpArray = _resume.BadBlocks.ToArray();
                InitProgress?.Invoke();

                foreach (ulong badSector in tmpArray)
                {
                    if (_aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        UpdateStatus?.Invoke("Aborted!");
                        _dumpLog.WriteLine("Aborted!");

                        break;
                    }

                    PulseProgress?.Invoke($"Trimming sector {badSector}");

                    error = _dev.Read(out cmdBuf, out _, (uint)badSector, blockSize, 1, byteAddressed, TIMEOUT,
                                      out duration);

                    totalDuration += duration;

                    if (error)
                    {
                        continue;
                    }

                    _resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                    _outputPlugin.WriteSector(cmdBuf, badSector);
                }

                EndProgress?.Invoke();
                end = DateTime.UtcNow;
                UpdateStatus?.Invoke($"Trimmming finished in {(end - start).TotalSeconds} seconds.");
                _dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds);
            }
            #endregion Trimming

            #region Error handling
            if (_resume.BadBlocks.Count > 0 &&
                !_aborted &&
                _retryPasses > 0)
            {
                int  pass              = 1;
                bool forward           = true;
                bool runningPersistent = false;

                InitProgress?.Invoke();
repeatRetryLba:
                ulong[] tmpArray = _resume.BadBlocks.ToArray();

                foreach (ulong badSector in tmpArray)
                {
                    if (_aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        UpdateStatus?.Invoke("Aborted!");
                        _dumpLog.WriteLine("Aborted!");

                        break;
                    }

                    PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass,
                                                        forward ? "forward" : "reverse",
                                                        runningPersistent ? "recovering partial data, " : ""));

                    error = _dev.Read(out cmdBuf, out _, (uint)badSector, blockSize, 1, byteAddressed, TIMEOUT,
                                      out duration);

                    totalDuration += duration;

                    if (!error)
                    {
                        _resume.BadBlocks.Remove(badSector);
                        extents.Add(badSector);
                        _outputPlugin.WriteSector(cmdBuf, badSector);
                        UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}.");
                        _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
                    }
                    else if (runningPersistent)
                    {
                        _outputPlugin.WriteSector(cmdBuf, badSector);
                    }
                }

                if (pass < _retryPasses &&
                    !_aborted &&
                    _resume.BadBlocks.Count > 0)
                {
                    pass++;
                    forward = !forward;
                    _resume.BadBlocks.Sort();
                    _resume.BadBlocks.Reverse();

                    goto repeatRetryLba;
                }

                EndProgress?.Invoke();
            }
            #endregion Error handling

            currentTry.Extents = ExtentsConverter.ToMetadata(extents);

            _outputPlugin.SetDumpHardware(_resume.Tries);

            // TODO: Drive info
            var metadata = new CommonTypes.Structs.ImageInfo
            {
                Application = "Aaru", ApplicationVersion = Version.GetVersion()
            };

            if (!_outputPlugin.SetMetadata(metadata))
            {
                ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine +
                                     _outputPlugin.ErrorMessage);
            }

            if (_preSidecar != null)
            {
                _outputPlugin.SetCicmMetadata(_preSidecar);
            }

            _dumpLog.WriteLine("Closing output file.");
            UpdateStatus?.Invoke("Closing output file.");
            DateTime closeStart = DateTime.Now;
            _outputPlugin.Close();
            DateTime closeEnd = DateTime.Now;
            UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds.");
            _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);

            if (_aborted)
            {
                UpdateStatus?.Invoke("Aborted!");
                _dumpLog.WriteLine("Aborted!");

                return;
            }

            double totalChkDuration = 0;

            if (_metadata)
            {
                UpdateStatus?.Invoke("Creating sidecar.");
                _dumpLog.WriteLine("Creating sidecar.");
                var         filters     = new FiltersList();
                IFilter     filter      = filters.GetFilter(_outputPath);
                IMediaImage inputPlugin = ImageFormat.Detect(filter);

                if (!inputPlugin.Open(filter))
                {
                    StoppingErrorMessage?.Invoke("Could not open created image.");
                }

                DateTime chkStart = DateTime.UtcNow;
                _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding);
                _sidecarClass.InitProgressEvent    += InitProgress;
                _sidecarClass.UpdateProgressEvent  += UpdateProgress;
                _sidecarClass.EndProgressEvent     += EndProgress;
                _sidecarClass.InitProgressEvent2   += InitProgress2;
                _sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
                _sidecarClass.EndProgressEvent2    += EndProgress2;
                _sidecarClass.UpdateStatusEvent    += UpdateStatus;
                CICMMetadataType sidecar = _sidecarClass.Create();

                if (_preSidecar != null)
                {
                    _preSidecar.BlockMedia = sidecar.BlockMedia;
                    sidecar = _preSidecar;
                }

                switch (_dev.Type)
                {
                case DeviceType.MMC:
                    sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();

                    break;

                case DeviceType.SecureDigital:
                    sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType();

                    break;
                }

                DumpType cidDump = null;
                DumpType csdDump = null;
                DumpType ocrDump = null;

                if (cid != null)
                {
                    if (_dev.Type == DeviceType.SecureDigital && _private)
                    {
                        // Clear serial number and manufacturing date
                        cid[9]  = 0;
                        cid[10] = 0;
                        cid[11] = 0;
                        cid[12] = 0;
                        cid[13] = 0;
                        cid[14] = 0;
                    }
                    else if (_dev.Type == DeviceType.MMC && _private)
                    {
                        // Clear serial number and manufacturing date
                        cid[10] = 0;
                        cid[11] = 0;
                        cid[12] = 0;
                        cid[13] = 0;
                        cid[14] = 0;
                    }

                    cidDump = new DumpType
                    {
                        Image = _outputPath, Size = (ulong)cid.Length, Checksums = Checksum.GetChecksums(cid).ToArray()
                    };

                    ret =
                        _outputPlugin.WriteMediaTag(cid,
                                                    _dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID
                                                        : MediaTagType.MMC_CID);

                    // Cannot write CID to image
                    if (!ret &&
                        !_force)
                    {
                        _dumpLog.WriteLine("Cannot write CID to output image.");

                        StoppingErrorMessage?.Invoke("Cannot write CID to output image." + Environment.NewLine +
                                                     _outputPlugin.ErrorMessage);

                        return;
                    }
                }

                if (csd != null)
                {
                    csdDump = new DumpType
                    {
                        Image = _outputPath, Size = (ulong)csd.Length, Checksums = Checksum.GetChecksums(csd).ToArray()
                    };

                    ret =
                        _outputPlugin.WriteMediaTag(csd,
                                                    _dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CSD
                                                        : MediaTagType.MMC_CSD);

                    // Cannot write CSD to image
                    if (!ret &&
                        !_force)
                    {
                        _dumpLog.WriteLine("Cannot write CSD to output image.");

                        StoppingErrorMessage?.Invoke("Cannot write CSD to output image." + Environment.NewLine +
                                                     _outputPlugin.ErrorMessage);

                        return;
                    }
                }

                if (ecsd != null)
                {
                    sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD = new DumpType
                    {
                        Image     = _outputPath, Size = (ulong)ecsd.Length,
                        Checksums = Checksum.GetChecksums(ecsd).ToArray()
                    };

                    ret = _outputPlugin.WriteMediaTag(ecsd, MediaTagType.MMC_ExtendedCSD);

                    // Cannot write Extended CSD to image
                    if (!ret &&
                        !_force)
                    {
                        _dumpLog.WriteLine("Cannot write Extended CSD to output image.");

                        StoppingErrorMessage?.Invoke("Cannot write Extended CSD to output image." +
                                                     Environment.NewLine +
                                                     _outputPlugin.ErrorMessage);

                        return;
                    }
                }

                if (ocr != null)
                {
                    ocrDump = new DumpType
                    {
                        Image = _outputPath, Size = (ulong)ocr.Length, Checksums = Checksum.GetChecksums(ocr).ToArray()
                    };

                    ret =
                        _outputPlugin.WriteMediaTag(ocr,
                                                    _dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_OCR
                                                        : MediaTagType.MMC_OCR);

                    // Cannot write OCR to image
                    if (!ret &&
                        !_force)
                    {
                        _dumpLog.WriteLine("Cannot write OCR to output image.");

                        StoppingErrorMessage?.Invoke("Cannot write OCR to output image." + Environment.NewLine +
                                                     _outputPlugin.ErrorMessage);

                        return;
                    }
                }

                if (scr != null)
                {
                    sidecar.BlockMedia[0].SecureDigital.SCR = new DumpType
                    {
                        Image = _outputPath, Size = (ulong)scr.Length, Checksums = Checksum.GetChecksums(scr).ToArray()
                    };

                    ret = _outputPlugin.WriteMediaTag(scr, MediaTagType.SD_SCR);

                    // Cannot write SCR to image
                    if (!ret &&
                        !_force)
                    {
                        _dumpLog.WriteLine("Cannot write SCR to output image.");

                        StoppingErrorMessage?.Invoke("Cannot write SCR to output image." + Environment.NewLine +
                                                     _outputPlugin.ErrorMessage);

                        return;
                    }
                }

                switch (_dev.Type)
                {
                case DeviceType.MMC:
                    sidecar.BlockMedia[0].MultiMediaCard.CID = cidDump;
                    sidecar.BlockMedia[0].MultiMediaCard.CSD = csdDump;
                    sidecar.BlockMedia[0].MultiMediaCard.OCR = ocrDump;

                    break;

                case DeviceType.SecureDigital:
                    sidecar.BlockMedia[0].SecureDigital.CID = cidDump;
                    sidecar.BlockMedia[0].SecureDigital.CSD = csdDump;
                    sidecar.BlockMedia[0].SecureDigital.OCR = ocrDump;

                    break;
                }

                end = DateTime.UtcNow;

                totalChkDuration = (end - chkStart).TotalMilliseconds;
                UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds.");

                UpdateStatus?.
                Invoke($"Average checksum speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000):F3} KiB/sec.");

                _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);

                _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
                                   ((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000));

                (string type, string subType)xmlType = (null, null);

                switch (_dev.Type)
                {
                case DeviceType.MMC:
                    xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.MMC);

                    sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.MMC);

                    break;

                case DeviceType.SecureDigital:
                    CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.SecureDigital);
                    sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.SecureDigital);

                    break;
                }

                sidecar.BlockMedia[0].DiskType    = xmlType.type;
                sidecar.BlockMedia[0].DiskSubType = xmlType.subType;

                // TODO: Implement device firmware revision
                sidecar.BlockMedia[0].LogicalBlocks     = blocks;
                sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize > 0 ? physicalBlockSize : blockSize;
                sidecar.BlockMedia[0].LogicalBlockSize  = blockSize;
                sidecar.BlockMedia[0].Manufacturer      = _dev.Manufacturer;
                sidecar.BlockMedia[0].Model             = _dev.Model;

                if (!_private)
                {
                    sidecar.BlockMedia[0].Serial = _dev.Serial;
                }

                sidecar.BlockMedia[0].Size = blocks * blockSize;

                UpdateStatus?.Invoke("Writing metadata sidecar");

                var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);

                var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                xmlSer.Serialize(xmlFs, sidecar);
                xmlFs.Close();
            }

            UpdateStatus?.Invoke("");

            UpdateStatus?.
            Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");

            UpdateStatus?.
            Invoke($"Average speed: {((double)blockSize * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");

            UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
            UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
            UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");
            UpdateStatus?.Invoke("");

            if (_resume.BadBlocks.Count > 0)
            {
                _resume.BadBlocks.Sort();
            }

            switch (_dev.Type)
            {
            case DeviceType.MMC:
                Statistics.AddMedia(MediaType.MMC, true);

                break;

            case DeviceType.SecureDigital:
                Statistics.AddMedia(MediaType.SecureDigital, true);

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

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

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

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

            Resume           resume  = null;
            CICMMetadataType sidecar = null;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            IWritableImage outputFormat = candidates[0];

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

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

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

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

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

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

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

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

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

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

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

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

            List <Track> tracks;

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

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

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

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

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

                if (options.Force)
                {
                    DicConsole.ErrorWriteLine("Error {0} writing media tag, continuing...", outputFormat.ErrorMessage);
                }
                else
                {
                    DicConsole.ErrorWriteLine("Error {0} writing media tag, not continuing...",
                                              outputFormat.ErrorMessage);
                    return;
                }
            }

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

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

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

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

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

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

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

                    doneSectors += sectorsToDo;
                }

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

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

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

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

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

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

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

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

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

                        doneSectors += sectorsToDo;
                    }

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

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

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

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

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

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

                        doneSectors += sectorsToDo;
                    }
                }

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

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

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

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

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

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

                            continue;
                        }

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

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

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

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

                            doneSectors += sectorsToDo;
                        }
                    }

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

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

                    DicConsole.WriteLine();
                }
            }

            bool ret = false;

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

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

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

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

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

            Core.Statistics.AddCommand("convert-image");
        }
        public void Convert()
        {
            Environment.CurrentDirectory = DataFolder;

            Resume           resume  = null;
            CICMMetadataType sidecar = null;

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

            Assert.IsNotNull(inputFilter, "Cannot open specified file.");

            string outputPath = Path.Combine(Path.GetTempPath(), SuggestedOutputFilename);

            Assert.IsFalse(File.Exists(outputPath), "Output file already exists, not continuing.");

            IMediaImage inputFormat = ImageFormat.Detect(inputFilter);

            Assert.IsNotNull(inputFormat, "Input image format not identified, not proceeding with conversion.");

            Assert.IsTrue(inputFormat.Open(inputFilter), "Unable to open image format");

            Assert.IsTrue(OutputFormat.SupportedMediaTypes.Contains(inputFormat.Info.MediaType),
                          "Output format does not support media type, cannot continue...");

            if (inputFormat.Info.ReadableSectorTags.Count == 0)
            {
                Assert.IsFalse(UseLong, "Input image does not support long sectors.");
            }

            var inputOptical  = inputFormat as IOpticalMediaImage;
            var outputOptical = OutputFormat as IWritableOpticalImage;

            Assert.IsNotNull(inputOptical, "Could not treat existing image as optical disc.");
            Assert.IsNotNull(outputOptical, "Could not treat new image as optical disc.");
            Assert.IsNotNull(inputOptical.Tracks, "Existing image contains no tracks.");

            Assert.IsTrue(outputOptical.Create(outputPath, inputFormat.Info.MediaType, ParsedOptions, inputFormat.Info.Sectors, inputFormat.Info.SectorSize),
                          $"Error {outputOptical.ErrorMessage} creating output image.");

            var metadata = new ImageInfo
            {
                Application           = "Aaru",
                ApplicationVersion    = Version.GetVersion(),
                Comments              = inputFormat.Info.Comments,
                Creator               = inputFormat.Info.Creator,
                DriveFirmwareRevision = inputFormat.Info.DriveFirmwareRevision,
                DriveManufacturer     = inputFormat.Info.DriveManufacturer,
                DriveModel            = inputFormat.Info.DriveModel,
                DriveSerialNumber     = inputFormat.Info.DriveSerialNumber,
                LastMediaSequence     = inputFormat.Info.LastMediaSequence,
                MediaBarcode          = inputFormat.Info.MediaBarcode,
                MediaManufacturer     = inputFormat.Info.MediaManufacturer,
                MediaModel            = inputFormat.Info.MediaModel,
                MediaPartNumber       = inputFormat.Info.MediaPartNumber,
                MediaSequence         = inputFormat.Info.MediaSequence,
                MediaSerialNumber     = inputFormat.Info.MediaSerialNumber,
                MediaTitle            = inputFormat.Info.MediaTitle
            };

            Assert.IsTrue(outputOptical.SetMetadata(metadata),
                          $"Error {outputOptical.ErrorMessage} setting metadata, ");

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

            foreach (MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags.Where(mediaTag =>
                                                                                       outputOptical.SupportedMediaTags.Contains(mediaTag)))
            {
                AaruConsole.WriteLine("Converting media tag {0}", mediaTag);
                byte[] tag = inputFormat.ReadDiskTag(mediaTag);

                Assert.IsTrue(outputOptical.WriteMediaTag(tag, mediaTag));
            }

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

            Assert.IsTrue(outputOptical.SetTracks(inputOptical.Tracks),
                          $"Error {outputOptical.ErrorMessage} sending tracks list to output image.");

            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 >= SECTORS_TO_READ)
                    {
                        sectorsToDo = SECTORS_TO_READ;
                    }
                    else
                    {
                        sectorsToDo = (uint)(trackSectors - doneSectors);
                    }

                    bool useNotLong = false;
                    bool result     = false;

                    if (UseLong)
                    {
                        if (sectorsToDo == 1)
                        {
                            sector = inputFormat.ReadSectorLong(doneSectors + track.TrackStartSector);
                            result = outputOptical.WriteSectorLong(sector, doneSectors + track.TrackStartSector);
                        }
                        else
                        {
                            sector = inputFormat.ReadSectorsLong(doneSectors + track.TrackStartSector, sectorsToDo);

                            result = outputOptical.WriteSectorsLong(sector, doneSectors + track.TrackStartSector,
                                                                    sectorsToDo);
                        }

                        if (!result &&
                            sector.Length % 2352 != 0)
                        {
                            useNotLong = true;
                        }
                    }

                    if (!UseLong || useNotLong)
                    {
                        if (sectorsToDo == 1)
                        {
                            sector = inputFormat.ReadSector(doneSectors + track.TrackStartSector);
                            result = outputOptical.WriteSector(sector, doneSectors + track.TrackStartSector);
                        }
                        else
                        {
                            sector = inputFormat.ReadSectors(doneSectors + track.TrackStartSector, sectorsToDo);

                            result = outputOptical.WriteSectors(sector, doneSectors + track.TrackStartSector,
                                                                sectorsToDo);
                        }
                    }

                    Assert.IsTrue(result,
                                  $"Error {outputOptical.ErrorMessage} writing sector {doneSectors + track.TrackStartSector}, not continuing...");

                    doneSectors += sectorsToDo;
                }
            }

            Dictionary <byte, string> isrcs                  = new Dictionary <byte, string>();
            Dictionary <byte, byte>   trackFlags             = new Dictionary <byte, byte>();
            string                 mcn                       = null;
            HashSet <int>          subchannelExtents         = new HashSet <int>();
            Dictionary <byte, int> smallestPregapLbaPerTrack = new Dictionary <byte, int>();

            Track[] tracks = new Track[inputOptical.Tracks.Count];

            for (int i = 0; i < tracks.Length; i++)
            {
                tracks[i] = new Track
                {
                    Indexes                = new Dictionary <ushort, int>(),
                    TrackDescription       = inputOptical.Tracks[i].TrackDescription,
                    TrackEndSector         = inputOptical.Tracks[i].TrackEndSector,
                    TrackStartSector       = inputOptical.Tracks[i].TrackStartSector,
                    TrackPregap            = inputOptical.Tracks[i].TrackPregap,
                    TrackSequence          = inputOptical.Tracks[i].TrackSequence,
                    TrackSession           = inputOptical.Tracks[i].TrackSession,
                    TrackBytesPerSector    = inputOptical.Tracks[i].TrackBytesPerSector,
                    TrackRawBytesPerSector = inputOptical.Tracks[i].TrackRawBytesPerSector,
                    TrackType              = inputOptical.Tracks[i].TrackType,
                    TrackSubchannelType    = inputOptical.Tracks[i].TrackSubchannelType
                };

                foreach (KeyValuePair <ushort, int> idx in inputOptical.Tracks[i].Indexes)
                {
                    tracks[i].Indexes[idx.Key] = idx.Value;
                }
            }

            foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags.Where(t => t == SectorTagType.CdTrackIsrc).
                     OrderBy(t => t))
            {
                foreach (Track track in tracks)
                {
                    byte[] isrc = inputFormat.ReadSectorTag(track.TrackSequence, tag);

                    if (isrc is null)
                    {
                        continue;
                    }

                    isrcs[(byte)track.TrackSequence] = Encoding.UTF8.GetString(isrc);
                }
            }

            foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags.
                     Where(t => t == SectorTagType.CdTrackFlags).OrderBy(t => t))
            {
                foreach (Track track in tracks)
                {
                    byte[] flags = inputFormat.ReadSectorTag(track.TrackSequence, tag);

                    if (flags is null)
                    {
                        continue;
                    }

                    trackFlags[(byte)track.TrackSequence] = flags[0];
                }
            }

            for (ulong s = 0; s < inputFormat.Info.Sectors; s++)
            {
                if (s > int.MaxValue)
                {
                    break;
                }

                subchannelExtents.Add((int)s);
            }

            foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags.OrderBy(t => t).TakeWhile(tag => UseLong))
            {
                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 (!outputOptical.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:
                        sector = inputFormat.ReadSectorTag(track.TrackSequence, tag);
                        result = outputOptical.WriteSectorTag(sector, track.TrackSequence, tag);

                        Assert.IsTrue(result, $"Error {outputOptical.ErrorMessage} writing tag, not continuing...");

                        continue;
                    }

                    while (doneSectors < trackSectors)
                    {
                        uint sectorsToDo;

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

                        if (sectorsToDo == 1)
                        {
                            sector = inputFormat.ReadSectorTag(doneSectors + track.TrackStartSector, tag);

                            if (tag == SectorTagType.CdSectorSubchannel)
                            {
                                bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw,
                                                                                         MmcSubchannel.Raw, sector, doneSectors + track.TrackStartSector, 1, null,
                                                                                         isrcs, (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents, true,
                                                                                         outputOptical, true, true, null, null, smallestPregapLbaPerTrack, false);

                                if (indexesChanged)
                                {
                                    outputOptical.SetTracks(tracks.ToList());
                                }

                                result = true;
                            }
                            else
                            {
                                result = outputOptical.WriteSectorTag(sector, doneSectors + track.TrackStartSector,
                                                                      tag);
                            }
                        }
                        else
                        {
                            sector = inputFormat.ReadSectorsTag(doneSectors + track.TrackStartSector, sectorsToDo, tag);

                            if (tag == SectorTagType.CdSectorSubchannel)
                            {
                                bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw,
                                                                                         MmcSubchannel.Raw, sector, doneSectors + track.TrackStartSector, sectorsToDo,
                                                                                         null, isrcs, (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents,
                                                                                         true, outputOptical, true, true, null, null, smallestPregapLbaPerTrack, false);

                                if (indexesChanged)
                                {
                                    outputOptical.SetTracks(tracks.ToList());
                                }

                                result = true;
                            }
                            else
                            {
                                result = outputOptical.WriteSectorsTag(sector, doneSectors + track.TrackStartSector,
                                                                       sectorsToDo, tag);
                            }
                        }

                        Assert.IsTrue(result,
                                      $"Error {outputOptical.ErrorMessage} writing tag for sector {doneSectors + track.TrackStartSector}, not continuing...");

                        doneSectors += sectorsToDo;
                    }
                }
            }

            if (isrcs.Count > 0)
            {
                foreach (KeyValuePair <byte, string> isrc in isrcs)
                {
                    outputOptical.WriteSectorTag(Encoding.UTF8.GetBytes(isrc.Value), isrc.Key,
                                                 SectorTagType.CdTrackIsrc);
                }
            }

            if (trackFlags.Count > 0)
            {
                foreach ((byte track, byte flags) in trackFlags)
                {
                    outputOptical.WriteSectorTag(new[]
                    {
                        flags
                    }, track, SectorTagType.CdTrackFlags);
                }
            }

            if (mcn != null)
            {
                outputOptical.WriteMediaTag(Encoding.UTF8.GetBytes(mcn), MediaTagType.CD_MCN);
            }

            if (resume != null ||
                dumpHardware != null)
            {
                if (resume != null)
                {
                    outputOptical.SetDumpHardware(resume.Tries);
                }
                else if (dumpHardware != null)
                {
                    outputOptical.SetDumpHardware(dumpHardware);
                }
            }

            if (sidecar != null ||
                cicmMetadata != null)
            {
                if (sidecar != null)
                {
                    outputOptical.SetCicmMetadata(sidecar);
                }
                else if (cicmMetadata != null)
                {
                    outputOptical.SetCicmMetadata(cicmMetadata);
                }
            }

            Assert.True(outputOptical.Close(),
                        $"Error {outputOptical.ErrorMessage} closing output image... Contents are not correct.");

            // Some images will never generate the same
            if (Md5 != null)
            {
                string md5 = Md5Context.File(outputPath, out _);

                Assert.AreEqual(Md5, md5, "Hashes are different");
            }

            File.Delete(outputPath);
        }
Exemplo n.º 11
0
Arquivo: Dump.cs Projeto: paulyc/Aaru
        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,
                                 uint maxBlocks, bool useBufferedReads, bool storeEncrypted,
                                 bool titleKeys)
        {
            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;
            }

            if (maxBlocks == 0)
            {
                maxBlocks = 64;
            }

            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);
            AaruConsole.DebugWriteLine("Dump-Media command", "--max-blocks={0}", maxBlocks);
            AaruConsole.DebugWriteLine("Dump-Media command", "--use-buffered-reads={0}", useBufferedReads);
            AaruConsole.DebugWriteLine("Dump-Media command", "--store-encrypted={0}", storeEncrypted);
            AaruConsole.DebugWriteLine("Dump-Media command", "--title-keys={0}", titleKeys);

            // 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) &&
                    (resumeClass.MissingTitleKeys is null || resumeClass.MissingTitleKeys.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, maxBlocks, useBufferedReads,
                                      storeEncrypted, titleKeys);

                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);
        }
Exemplo n.º 12
0
        /// <summary>
        ///     Dumps an optical disc
        /// </summary>
        /// <param name="dev">Device</param>
        /// <param name="devicePath">Path to the device</param>
        /// <param name="outputPrefix">Prefix for output data 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 raw/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="dskType">Disc type as detected in MMC layer</param>
        /// <param name="dumpLeadIn">Try to read and dump as much Lead-in as possible</param>
        /// <param name="outputPath">Path to output file</param>
        /// <param name="formatOptions">Formats to pass to output file plugin</param>
        /// <exception cref="NotImplementedException">If trying to dump GOD or WOD, or XGDs without a Kreon drive</exception>
        internal static void Dump(Device dev, string devicePath,
                                  IWritableImage outputPlugin, ushort retryPasses,
                                  bool force, bool dumpRaw,
                                  bool persistent, bool stopOnError, ref MediaType dskType,
                                  ref Resume resume, ref DumpLog dumpLog,
                                  bool dumpLeadIn, Encoding encoding,
                                  string outputPrefix, string outputPath,
                                  Dictionary <string, string> formatOptions,
                                  CICMMetadataType preSidecar, uint skip,
                                  bool nometadata, bool notrim)
        {
            bool sense;

            byte[] tmpBuf;
            bool   compactDisc = true;
            bool   isXbox      = false;

            // TODO: Log not only what is it reading, but if it was read correctly or not.
            sense = dev.GetConfiguration(out byte[] cmdBuf, out _, 0, MmcGetConfigurationRt.Current, dev.Timeout,
                                         out _);
            if (!sense)
            {
                Features.SeparatedFeatures ftr = Features.Separate(cmdBuf);
                dumpLog.WriteLine("Device reports current profile is 0x{0:X4}", ftr.CurrentProfile);

                switch (ftr.CurrentProfile)
                {
                case 0x0001:
                    dskType = MediaType.GENERIC_HDD;
                    goto default;

                case 0x0005:
                    dskType = MediaType.CDMO;
                    break;

                case 0x0008:
                    dskType = MediaType.CD;
                    break;

                case 0x0009:
                    dskType = MediaType.CDR;
                    break;

                case 0x000A:
                    dskType = MediaType.CDRW;
                    break;

                case 0x0010:
                    dskType = MediaType.DVDROM;
                    goto default;

                case 0x0011:
                    dskType = MediaType.DVDR;
                    goto default;

                case 0x0012:
                    dskType = MediaType.DVDRAM;
                    goto default;

                case 0x0013:
                case 0x0014:
                    dskType = MediaType.DVDRW;
                    goto default;

                case 0x0015:
                case 0x0016:
                    dskType = MediaType.DVDRDL;
                    goto default;

                case 0x0017:
                    dskType = MediaType.DVDRWDL;
                    goto default;

                case 0x0018:
                    dskType = MediaType.DVDDownload;
                    goto default;

                case 0x001A:
                    dskType = MediaType.DVDPRW;
                    goto default;

                case 0x001B:
                    dskType = MediaType.DVDPR;
                    goto default;

                case 0x0020:
                    dskType = MediaType.DDCD;
                    goto default;

                case 0x0021:
                    dskType = MediaType.DDCDR;
                    goto default;

                case 0x0022:
                    dskType = MediaType.DDCDRW;
                    goto default;

                case 0x002A:
                    dskType = MediaType.DVDPRWDL;
                    goto default;

                case 0x002B:
                    dskType = MediaType.DVDPRDL;
                    goto default;

                case 0x0040:
                    dskType = MediaType.BDROM;
                    goto default;

                case 0x0041:
                case 0x0042:
                    dskType = MediaType.BDR;
                    goto default;

                case 0x0043:
                    dskType = MediaType.BDRE;
                    goto default;

                case 0x0050:
                    dskType = MediaType.HDDVDROM;
                    goto default;

                case 0x0051:
                    dskType = MediaType.HDDVDR;
                    goto default;

                case 0x0052:
                    dskType = MediaType.HDDVDRAM;
                    goto default;

                case 0x0053:
                    dskType = MediaType.HDDVDRW;
                    goto default;

                case 0x0058:
                    dskType = MediaType.HDDVDRDL;
                    goto default;

                case 0x005A:
                    dskType = MediaType.HDDVDRWDL;
                    goto default;

                default:
                    compactDisc = false;
                    break;
                }
            }

            if (compactDisc)
            {
                CompactDisc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError,
                                 ref dskType, ref resume, ref dumpLog, dumpLeadIn, encoding, outputPrefix, outputPath,
                                 formatOptions, preSidecar, skip, nometadata, notrim);
                return;
            }

            Reader scsiReader = new Reader(dev, dev.Timeout, null, dumpRaw);
            ulong  blocks     = scsiReader.GetDeviceBlocks();

            dumpLog.WriteLine("Device reports disc has {0} blocks", blocks);
            Dictionary <MediaTagType, byte[]> mediaTags = new Dictionary <MediaTagType, byte[]>();

            #region Nintendo
            switch (dskType)
            {
            case MediaType.Unknown when blocks > 0:
                dumpLog.WriteLine("Reading Physical Format Information");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                              MmcDiscStructureFormat.PhysicalInformation, 0, dev.Timeout, out _);
                if (!sense)
                {
                    PFI.PhysicalFormatInformation?nintendoPfi = PFI.Decode(cmdBuf);
                    if (nintendoPfi != null)
                    {
                        if (nintendoPfi.Value.DiskCategory == DiskCategory.Nintendo &&
                            nintendoPfi.Value.PartVersion == 15)
                        {
                            dumpLog.WriteLine("Dumping Nintendo GameCube or Wii discs is not yet implemented.");
                            throw new
                                  NotImplementedException("Dumping Nintendo GameCube or Wii discs is not yet implemented.");
                        }
                    }
                }

                break;

            case MediaType.DVDDownload:
            case MediaType.DVDPR:
            case MediaType.DVDPRDL:
            case MediaType.DVDPRW:
            case MediaType.DVDPRWDL:
            case MediaType.DVDR:
            case MediaType.DVDRAM:
            case MediaType.DVDRDL:
            case MediaType.DVDROM:
            case MediaType.DVDRW:
            case MediaType.DVDRWDL:
            case MediaType.HDDVDR:
            case MediaType.HDDVDRAM:
            case MediaType.HDDVDRDL:
            case MediaType.HDDVDROM:
            case MediaType.HDDVDRW:
            case MediaType.HDDVDRWDL:
                dumpLog.WriteLine("Reading Physical Format Information");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                              MmcDiscStructureFormat.PhysicalInformation, 0, dev.Timeout, out _);
                if (!sense)
                {
                    if (PFI.Decode(cmdBuf).HasValue)
                    {
                        tmpBuf = new byte[cmdBuf.Length - 4];
                        Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                        mediaTags.Add(MediaTagType.DVD_PFI, tmpBuf);

                        PFI.PhysicalFormatInformation decPfi = PFI.Decode(cmdBuf).Value;
                        DicConsole.WriteLine("PFI:\n{0}", PFI.Prettify(decPfi));

                        // False book types
                        if (dskType == MediaType.DVDROM)
                        {
                            switch (decPfi.DiskCategory)
                            {
                            case DiskCategory.DVDPR:
                                dskType = MediaType.DVDPR;
                                break;

                            case DiskCategory.DVDPRDL:
                                dskType = MediaType.DVDPRDL;
                                break;

                            case DiskCategory.DVDPRW:
                                dskType = MediaType.DVDPRW;
                                break;

                            case DiskCategory.DVDPRWDL:
                                dskType = MediaType.DVDPRWDL;
                                break;

                            case DiskCategory.DVDR:
                                dskType = decPfi.PartVersion == 6 ? MediaType.DVDRDL : MediaType.DVDR;
                                break;

                            case DiskCategory.DVDRAM:
                                dskType = MediaType.DVDRAM;
                                break;

                            default:
                                dskType = MediaType.DVDROM;
                                break;

                            case DiskCategory.DVDRW:
                                dskType = decPfi.PartVersion == 3 ? MediaType.DVDRWDL : MediaType.DVDRW;
                                break;

                            case DiskCategory.HDDVDR:
                                dskType = MediaType.HDDVDR;
                                break;

                            case DiskCategory.HDDVDRAM:
                                dskType = MediaType.HDDVDRAM;
                                break;

                            case DiskCategory.HDDVDROM:
                                dskType = MediaType.HDDVDROM;
                                break;

                            case DiskCategory.HDDVDRW:
                                dskType = MediaType.HDDVDRW;
                                break;

                            case DiskCategory.Nintendo:
                                dskType = decPfi.DiscSize == DVDSize.Eighty ? MediaType.GOD : MediaType.WOD;
                                break;

                            case DiskCategory.UMD:
                                dskType = MediaType.UMD;
                                break;
                            }
                        }
                    }
                }

                dumpLog.WriteLine("Reading Disc Manufacturing Information");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                              MmcDiscStructureFormat.DiscManufacturingInformation, 0, dev.Timeout,
                                              out _);
                if (!sense)
                {
                    if (DMI.IsXbox(cmdBuf) || DMI.IsXbox360(cmdBuf))
                    {
                        if (DMI.IsXbox(cmdBuf))
                        {
                            dskType = MediaType.XGD;
                        }
                        else if (DMI.IsXbox360(cmdBuf))
                        {
                            dskType = MediaType.XGD2;

                            // All XGD3 all have the same number of blocks
                            if (blocks == 25063 ||      // Locked (or non compatible drive)
                                blocks == 4229664 ||    // Xtreme unlock
                                blocks == 4246304)      // Wxripper unlock
                            {
                                dskType = MediaType.XGD3;
                            }
                        }

                        sense = dev.ScsiInquiry(out byte[] inqBuf, out _);

                        if (sense || !Inquiry.Decode(inqBuf).HasValue || Inquiry.Decode(inqBuf).HasValue&&
                            !Inquiry.Decode(inqBuf).Value.KreonPresent)
                        {
                            dumpLog.WriteLine("Dumping Xbox Game Discs requires a drive with Kreon firmware.");
                            throw new
                                  NotImplementedException("Dumping Xbox Game Discs requires a drive with Kreon firmware.");
                        }

                        if (dumpRaw && !force)
                        {
                            DicConsole
                            .ErrorWriteLine("Not continuing. If you want to continue reading cooked data when raw is not available use the force option.");
                            // TODO: Exit more gracefully
                            return;
                        }

                        isXbox = true;
                    }

                    if (cmdBuf.Length == 2052)
                    {
                        tmpBuf = new byte[cmdBuf.Length - 4];
                        Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                        mediaTags.Add(MediaTagType.DVD_DMI, tmpBuf);
                    }
                }

                break;
            }
            #endregion Nintendo

            #region All DVD and HD DVD types
            #endregion All DVD and HD DVD types

            #region DVD-ROM
            if (dskType == MediaType.DVDDownload || dskType == MediaType.DVDROM)
            {
                dumpLog.WriteLine("Reading Lead-in Copyright Information.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                              MmcDiscStructureFormat.CopyrightInformation, 0, dev.Timeout, out _);
                if (!sense)
                {
                    if (CSS_CPRM.DecodeLeadInCopyright(cmdBuf).HasValue)
                    {
                        tmpBuf = new byte[cmdBuf.Length - 4];
                        Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                        mediaTags.Add(MediaTagType.DVD_CMI, tmpBuf);
                    }
                }
            }
            #endregion DVD-ROM

            switch (dskType)
            {
                #region DVD-ROM and HD DVD-ROM
            case MediaType.DVDDownload:
            case MediaType.DVDROM:
            case MediaType.HDDVDROM:
                dumpLog.WriteLine("Reading Burst Cutting Area.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                              MmcDiscStructureFormat.BurstCuttingArea, 0, dev.Timeout, out _);
                if (!sense)
                {
                    tmpBuf = new byte[cmdBuf.Length - 4];
                    Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                    mediaTags.Add(MediaTagType.DVD_BCA, tmpBuf);
                }

                break;
                #endregion DVD-ROM and HD DVD-ROM

                #region DVD-RAM and HD DVD-RAM
            case MediaType.DVDRAM:
            case MediaType.HDDVDRAM:
                dumpLog.WriteLine("Reading Disc Description Structure.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                              MmcDiscStructureFormat.DvdramDds, 0, dev.Timeout, out _);
                if (!sense)
                {
                    if (DDS.Decode(cmdBuf).HasValue)
                    {
                        tmpBuf = new byte[cmdBuf.Length - 4];
                        Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                        mediaTags.Add(MediaTagType.DVDRAM_DDS, tmpBuf);
                    }
                }

                dumpLog.WriteLine("Reading Spare Area Information.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                              MmcDiscStructureFormat.DvdramSpareAreaInformation, 0, dev.Timeout,
                                              out _);
                if (!sense)
                {
                    if (Spare.Decode(cmdBuf).HasValue)
                    {
                        tmpBuf = new byte[cmdBuf.Length - 4];
                        Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                        mediaTags.Add(MediaTagType.DVDRAM_SpareArea, tmpBuf);
                    }
                }

                break;
                #endregion DVD-RAM and HD DVD-RAM

                #region DVD-R and DVD-RW
            case MediaType.DVDR:
            case MediaType.DVDRW:
                dumpLog.WriteLine("Reading Pre-Recorded Information.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                              MmcDiscStructureFormat.PreRecordedInfo, 0, dev.Timeout, out _);
                if (!sense)
                {
                    tmpBuf = new byte[cmdBuf.Length - 4];
                    Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                    mediaTags.Add(MediaTagType.DVDR_PreRecordedInfo, tmpBuf);
                }

                break;
                #endregion DVD-R and DVD-RW
            }

            switch (dskType)
            {
                #region DVD-R, DVD-RW and HD DVD-R
            case MediaType.DVDR:
            case MediaType.DVDRW:
            case MediaType.HDDVDR:
                dumpLog.WriteLine("Reading Media Identifier.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                              MmcDiscStructureFormat.DvdrMediaIdentifier, 0, dev.Timeout, out _);
                if (!sense)
                {
                    tmpBuf = new byte[cmdBuf.Length - 4];
                    Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                    mediaTags.Add(MediaTagType.DVDR_MediaIdentifier, tmpBuf);
                }

                dumpLog.WriteLine("Reading Recordable Physical Information.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                              MmcDiscStructureFormat.DvdrPhysicalInformation, 0, dev.Timeout,
                                              out _);
                if (!sense)
                {
                    tmpBuf = new byte[cmdBuf.Length - 4];
                    Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                    mediaTags.Add(MediaTagType.DVDR_PFI, tmpBuf);
                }

                break;
                #endregion DVD-R, DVD-RW and HD DVD-R

                #region All DVD+
            case MediaType.DVDPR:
            case MediaType.DVDPRDL:
            case MediaType.DVDPRW:
            case MediaType.DVDPRWDL:
                dumpLog.WriteLine("Reading ADdress In Pregroove.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                              MmcDiscStructureFormat.Adip, 0, dev.Timeout, out _);
                if (!sense)
                {
                    tmpBuf = new byte[cmdBuf.Length - 4];
                    Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                    mediaTags.Add(MediaTagType.DVD_ADIP, tmpBuf);
                }

                dumpLog.WriteLine("Reading Disc Control Blocks.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                              MmcDiscStructureFormat.Dcb, 0, dev.Timeout, out _);
                if (!sense)
                {
                    tmpBuf = new byte[cmdBuf.Length - 4];
                    Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                    mediaTags.Add(MediaTagType.DCB, tmpBuf);
                }

                break;
                #endregion All DVD+

                #region HD DVD-ROM
            case MediaType.HDDVDROM:
                dumpLog.WriteLine("Reading Lead-in Copyright Information.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                              MmcDiscStructureFormat.HddvdCopyrightInformation, 0, dev.Timeout,
                                              out _);
                if (!sense)
                {
                    tmpBuf = new byte[cmdBuf.Length - 4];
                    Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                    mediaTags.Add(MediaTagType.HDDVD_CPI, tmpBuf);
                }

                break;
                #endregion HD DVD-ROM

                #region All Blu-ray
            case MediaType.BDR:
            case MediaType.BDRE:
            case MediaType.BDROM:
            case MediaType.BDRXL:
            case MediaType.BDREXL:
                dumpLog.WriteLine("Reading Disc Information.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0,
                                              MmcDiscStructureFormat.DiscInformation, 0, dev.Timeout, out _);
                if (!sense)
                {
                    if (DI.Decode(cmdBuf).HasValue)
                    {
                        tmpBuf = new byte[cmdBuf.Length - 4];
                        Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                        mediaTags.Add(MediaTagType.BD_DI, tmpBuf);
                    }
                }

                // TODO: PAC

                /*
                 * dumpLog.WriteLine("Reading PAC.");
                 * sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0,
                 *                            MmcDiscStructureFormat.Pac, 0, dev.Timeout, out _);
                 * if(!sense)
                 * {
                 *  tmpBuf = new byte[cmdBuf.Length - 4];
                 *  Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                 *  mediaTags.Add(MediaTagType.PAC, tmpBuf);
                 * }*/
                break;
                #endregion All Blu-ray
            }

            switch (dskType)
            {
                #region BD-ROM only
            case MediaType.BDROM:
                dumpLog.WriteLine("Reading Burst Cutting Area.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0,
                                              MmcDiscStructureFormat.BdBurstCuttingArea, 0, dev.Timeout, out _);
                if (!sense)
                {
                    tmpBuf = new byte[cmdBuf.Length - 4];
                    Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                    mediaTags.Add(MediaTagType.BD_BCA, tmpBuf);
                }

                break;
                #endregion BD-ROM only

                #region Writable Blu-ray only
            case MediaType.BDR:
            case MediaType.BDRE:
            case MediaType.BDRXL:
            case MediaType.BDREXL:
                dumpLog.WriteLine("Reading Disc Definition Structure.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0,
                                              MmcDiscStructureFormat.BdDds, 0, dev.Timeout, out _);
                if (!sense)
                {
                    tmpBuf = new byte[cmdBuf.Length - 4];
                    Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                    mediaTags.Add(MediaTagType.BD_DDS, tmpBuf);
                }

                dumpLog.WriteLine("Reading Spare Area Information.");
                sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0,
                                              MmcDiscStructureFormat.BdSpareAreaInformation, 0, dev.Timeout, out _);
                if (!sense)
                {
                    tmpBuf = new byte[cmdBuf.Length - 4];
                    Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                    mediaTags.Add(MediaTagType.BD_SpareArea, tmpBuf);
                }

                break;
                #endregion Writable Blu-ray only
            }

            if (isXbox)
            {
                Xgd.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError, mediaTags,
                         ref dskType, ref resume, ref dumpLog, encoding, outputPrefix, outputPath, formatOptions,
                         preSidecar, skip, nometadata, notrim);
                return;
            }

            Sbc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError, mediaTags,
                     ref dskType, true, ref resume, ref dumpLog, encoding, outputPrefix, outputPath, formatOptions,
                     preSidecar, skip, nometadata, notrim);
        }
Exemplo n.º 13
0
        /// <summary>
        ///     Creates a metadata sidecar for a block media (e.g. floppy, hard disk, flash card, usb stick)
        /// </summary>
        /// <param name="image">Image</param>
        /// <param name="filterId">Filter uuid</param>
        /// <param name="imagePath">Image path</param>
        /// <param name="fi">Image file information</param>
        /// <param name="plugins">Image plugins</param>
        /// <param name="imgChecksums">List of image checksums</param>
        /// <param name="sidecar">Metadata sidecar</param>
        static void BlockMedia(IMediaImage image, Guid filterId, string imagePath,
                               FileInfo fi, PluginBase plugins,
                               List <ChecksumType> imgChecksums, ref CICMMetadataType sidecar, Encoding encoding)
        {
            sidecar.BlockMedia = new[]
            {
                new BlockMediaType
                {
                    Checksums = imgChecksums.ToArray(),
                    Image     = new ImageType
                    {
                        format          = image.Format,
                        offset          = 0,
                        offsetSpecified = true,
                        Value           = Path.GetFileName(imagePath)
                    },
                    Size     = fi.Length,
                    Sequence = new SequenceType {
                        MediaTitle = image.Info.MediaTitle
                    }
                }
            };

            if (image.Info.MediaSequence != 0 && image.Info.LastMediaSequence != 0)
            {
                sidecar.BlockMedia[0].Sequence.MediaSequence = image.Info.MediaSequence;
                sidecar.BlockMedia[0].Sequence.TotalMedia    = image.Info.LastMediaSequence;
            }
            else
            {
                sidecar.BlockMedia[0].Sequence.MediaSequence = 1;
                sidecar.BlockMedia[0].Sequence.TotalMedia    = 1;
            }

            foreach (MediaTagType tagType in image.Info.ReadableMediaTags)
            {
                switch (tagType)
                {
                case MediaTagType.ATAPI_IDENTIFY:
                    sidecar.BlockMedia[0].ATA = new ATAType
                    {
                        Identify = new DumpType
                        {
                            Checksums =
                                Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.ATAPI_IDENTIFY)).ToArray(),
                            Size = image.ReadDiskTag(MediaTagType.ATAPI_IDENTIFY).Length
                        }
                    };
                    break;

                case MediaTagType.ATA_IDENTIFY:
                    sidecar.BlockMedia[0].ATA = new ATAType
                    {
                        Identify = new DumpType
                        {
                            Checksums =
                                Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.ATA_IDENTIFY)).ToArray(),
                            Size = image.ReadDiskTag(MediaTagType.ATA_IDENTIFY).Length
                        }
                    };
                    break;

                case MediaTagType.PCMCIA_CIS:
                    byte[] cis = image.ReadDiskTag(MediaTagType.PCMCIA_CIS);
                    sidecar.BlockMedia[0].PCMCIA = new PCMCIAType
                    {
                        CIS = new DumpType {
                            Checksums = Checksum.GetChecksums(cis).ToArray(), Size = cis.Length
                        }
                    };
                    Tuple[] tuples = CIS.GetTuples(cis);
                    if (tuples != null)
                    {
                        foreach (Tuple tuple in tuples)
                        {
                            switch (tuple.Code)
                            {
                            case TupleCodes.CISTPL_MANFID:
                                ManufacturerIdentificationTuple manfid =
                                    CIS.DecodeManufacturerIdentificationTuple(tuple);

                                if (manfid != null)
                                {
                                    sidecar.BlockMedia[0].PCMCIA.ManufacturerCode =
                                        manfid.ManufacturerID;
                                    sidecar.BlockMedia[0].PCMCIA.CardCode = manfid.CardID;
                                    sidecar.BlockMedia[0].PCMCIA.ManufacturerCodeSpecified = true;
                                    sidecar.BlockMedia[0].PCMCIA.CardCodeSpecified         = true;
                                }

                                break;

                            case TupleCodes.CISTPL_VERS_1:
                                Level1VersionTuple vers = CIS.DecodeLevel1VersionTuple(tuple);

                                if (vers != null)
                                {
                                    sidecar.BlockMedia[0].PCMCIA.Manufacturer = vers.Manufacturer;
                                    sidecar.BlockMedia[0].PCMCIA.ProductName  = vers.Product;
                                    sidecar.BlockMedia[0].PCMCIA.Compliance   =
                                        $"{vers.MajorVersion}.{vers.MinorVersion}";
                                    sidecar.BlockMedia[0].PCMCIA.AdditionalInformation =
                                        vers.AdditionalInformation;
                                }

                                break;
                            }
                        }
                    }

                    break;

                case MediaTagType.SCSI_INQUIRY:
                    sidecar.BlockMedia[0].SCSI = new SCSIType
                    {
                        Inquiry = new DumpType
                        {
                            Checksums =
                                Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SCSI_INQUIRY)).ToArray(),
                            Size = image.ReadDiskTag(MediaTagType.SCSI_INQUIRY).Length
                        }
                    };
                    break;

                case MediaTagType.SD_CID:
                    if (sidecar.BlockMedia[0].SecureDigital == null)
                    {
                        sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType();
                    }
                    sidecar.BlockMedia[0].SecureDigital.CID = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_CID)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_CID).Length
                    };
                    break;

                case MediaTagType.SD_CSD:
                    if (sidecar.BlockMedia[0].SecureDigital == null)
                    {
                        sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType();
                    }
                    sidecar.BlockMedia[0].SecureDigital.CSD = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_CSD)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_CSD).Length
                    };
                    break;

                case MediaTagType.SD_SCR:
                    if (sidecar.BlockMedia[0].SecureDigital == null)
                    {
                        sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType();
                    }
                    sidecar.BlockMedia[0].SecureDigital.SCR = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_SCR)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_SCR).Length
                    };
                    break;

                case MediaTagType.SD_OCR:
                    if (sidecar.BlockMedia[0].SecureDigital == null)
                    {
                        sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType();
                    }
                    sidecar.BlockMedia[0].SecureDigital.OCR = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_OCR)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_OCR).Length
                    };
                    break;

                case MediaTagType.MMC_CID:
                    if (sidecar.BlockMedia[0].MultiMediaCard == null)
                    {
                        sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();
                    }
                    sidecar.BlockMedia[0].MultiMediaCard.CID = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_CID)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_CID).Length
                    };
                    break;

                case MediaTagType.MMC_CSD:
                    if (sidecar.BlockMedia[0].MultiMediaCard == null)
                    {
                        sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();
                    }
                    sidecar.BlockMedia[0].MultiMediaCard.CSD = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_CSD)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_CSD).Length
                    };
                    break;

                case MediaTagType.MMC_OCR:
                    if (sidecar.BlockMedia[0].MultiMediaCard == null)
                    {
                        sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();
                    }
                    sidecar.BlockMedia[0].MultiMediaCard.OCR = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_OCR)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_OCR).Length
                    };
                    break;

                case MediaTagType.MMC_ExtendedCSD:
                    if (sidecar.BlockMedia[0].MultiMediaCard == null)
                    {
                        sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();
                    }
                    sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD = new DumpType
                    {
                        Checksums =
                            Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.MMC_ExtendedCSD)).ToArray(),
                        Size = image.ReadDiskTag(MediaTagType.MMC_ExtendedCSD).Length
                    };
                    break;

                case MediaTagType.USB_Descriptors:
                    if (sidecar.BlockMedia[0].USB == null)
                    {
                        sidecar.BlockMedia[0].USB = new USBType();
                    }
                    sidecar.BlockMedia[0].USB.Descriptors = new DumpType
                    {
                        Checksums =
                            Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.USB_Descriptors)).ToArray(),
                        Size = image.ReadDiskTag(MediaTagType.USB_Descriptors).Length
                    };
                    break;

                case MediaTagType.SCSI_MODESENSE_6:
                    if (sidecar.BlockMedia[0].SCSI == null)
                    {
                        sidecar.BlockMedia[0].SCSI = new SCSIType();
                    }
                    sidecar.BlockMedia[0].SCSI.ModeSense = new DumpType
                    {
                        Checksums =
                            Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_6)).ToArray(),
                        Size = image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_6).Length
                    };
                    break;

                case MediaTagType.SCSI_MODESENSE_10:
                    if (sidecar.BlockMedia[0].SCSI == null)
                    {
                        sidecar.BlockMedia[0].SCSI = new SCSIType();
                    }
                    sidecar.BlockMedia[0].SCSI.ModeSense10 = new DumpType
                    {
                        Checksums =
                            Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_10)).ToArray(),
                        Size = image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_10).Length
                    };
                    break;
                }
            }

            // If there is only one track, and it's the same as the image file (e.g. ".iso" files), don't re-checksum.
            if (image.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") &&
                filterId == new Guid("12345678-AAAA-BBBB-CCCC-123456789000"))
            {
                sidecar.BlockMedia[0].ContentChecksums = sidecar.BlockMedia[0].Checksums;
            }
            else
            {
                Checksum contentChkWorker = new Checksum();

                // For fast debugging, skip checksum
                //goto skipImageChecksum;

                uint  sectorsToRead = 512;
                ulong sectors       = image.Info.Sectors;
                ulong doneSectors   = 0;

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

                    if (sectors - doneSectors >= sectorsToRead)
                    {
                        sector = image.ReadSectors(doneSectors, sectorsToRead);
                        UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors, (long)sectors);
                        doneSectors += sectorsToRead;
                    }
                    else
                    {
                        sector = image.ReadSectors(doneSectors, (uint)(sectors - doneSectors));
                        UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors, (long)sectors);
                        doneSectors += sectors - doneSectors;
                    }

                    contentChkWorker.Update(sector);
                }

                // For fast debugging, skip checksum
                //skipImageChecksum:

                List <ChecksumType> cntChecksums = contentChkWorker.End();

                sidecar.BlockMedia[0].ContentChecksums = cntChecksums.ToArray();

                EndProgress2();
            }

            MediaType.MediaTypeToString(image.Info.MediaType, out string dskType, out string dskSubType);
            sidecar.BlockMedia[0].DiskType    = dskType;
            sidecar.BlockMedia[0].DiskSubType = dskSubType;
            Statistics.AddMedia(image.Info.MediaType, false);

            sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(image.Info.MediaType);

            sidecar.BlockMedia[0].LogicalBlocks    = (long)image.Info.Sectors;
            sidecar.BlockMedia[0].LogicalBlockSize = (int)image.Info.SectorSize;
            // TODO: Detect it
            sidecar.BlockMedia[0].PhysicalBlockSize = (int)image.Info.SectorSize;

            UpdateStatus("Checking filesystems...");

            List <Partition> partitions = Partitions.GetAll(image);

            Partitions.AddSchemesToStats(partitions);

            sidecar.BlockMedia[0].FileSystemInformation = new PartitionType[1];
            if (partitions.Count > 0)
            {
                sidecar.BlockMedia[0].FileSystemInformation = new PartitionType[partitions.Count];
                for (int i = 0; i < partitions.Count; i++)
                {
                    sidecar.BlockMedia[0].FileSystemInformation[i] = new PartitionType
                    {
                        Description = partitions[i].Description,
                        EndSector   = (int)partitions[i].End,
                        Name        = partitions[i].Name,
                        Sequence    = (int)partitions[i].Sequence,
                        StartSector = (int)partitions[i].Start,
                        Type        = partitions[i].Type
                    };
                    List <FileSystemType> lstFs = new List <FileSystemType>();

                    foreach (IFilesystem plugin in plugins.PluginsList.Values)
                    {
                        try
                        {
                            if (!plugin.Identify(image, partitions[i]))
                            {
                                continue;
                            }

                            plugin.GetInformation(image, partitions[i], out _, encoding);
                            lstFs.Add(plugin.XmlFsType);
                            Statistics.AddFilesystem(plugin.XmlFsType.Type);
                        }
                    }
Exemplo n.º 14
0
        public static int Invoke(bool debug, bool verbose, uint blockSize, string encodingName, string imagePath,
                                 bool tape)
        {
            MainClass.PrintCopyright();

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

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

            Statistics.AddCommand("create-sidecar");

            AaruConsole.DebugWriteLine("Create sidecar command", "--block-size={0}", blockSize);
            AaruConsole.DebugWriteLine("Create sidecar command", "--debug={0}", debug);
            AaruConsole.DebugWriteLine("Create sidecar command", "--encoding={0}", encodingName);
            AaruConsole.DebugWriteLine("Create sidecar command", "--input={0}", imagePath);
            AaruConsole.DebugWriteLine("Create sidecar command", "--tape={0}", tape);
            AaruConsole.DebugWriteLine("Create sidecar command", "--verbose={0}", verbose);

            Encoding encodingClass = null;

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

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

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

            if (File.Exists(imagePath))
            {
                if (tape)
                {
                    AaruConsole.ErrorWriteLine("You cannot use --tape option when input is a file.");

                    return((int)ErrorNumber.ExpectedDirectory);
                }

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

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

                    return((int)ErrorNumber.CannotOpenFile);
                }

                try
                {
                    IMediaImage imageFormat = ImageFormat.Detect(inputFilter);

                    if (imageFormat == null)
                    {
                        AaruConsole.WriteLine("Image format not identified, not proceeding with analysis.");

                        return((int)ErrorNumber.UnrecognizedFormat);
                    }

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

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

                            return((int)ErrorNumber.CannotOpenFormat);
                        }

                        AaruConsole.DebugWriteLine("Create sidecar command", "Correctly opened image file.");
                    }
                    catch (Exception ex)
                    {
                        AaruConsole.ErrorWriteLine("Unable to open image format");
                        AaruConsole.ErrorWriteLine("Error: {0}", ex.Message);

                        return((int)ErrorNumber.CannotOpenFormat);
                    }

                    Statistics.AddMediaFormat(imageFormat.Format);
                    Statistics.AddFilter(inputFilter.Name);

                    var sidecarClass = new Sidecar(imageFormat, imagePath, inputFilter.Id, encodingClass);
                    sidecarClass.InitProgressEvent    += Progress.InitProgress;
                    sidecarClass.UpdateProgressEvent  += Progress.UpdateProgress;
                    sidecarClass.EndProgressEvent     += Progress.EndProgress;
                    sidecarClass.InitProgressEvent2   += Progress.InitProgress2;
                    sidecarClass.UpdateProgressEvent2 += Progress.UpdateProgress2;
                    sidecarClass.EndProgressEvent2    += Progress.EndProgress2;
                    sidecarClass.UpdateStatusEvent    += Progress.UpdateStatus;

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

                    CICMMetadataType sidecar = sidecarClass.Create();

                    AaruConsole.WriteLine("Writing metadata sidecar");

                    var xmlFs =
                        new
                        FileStream(Path.Combine(Path.GetDirectoryName(imagePath) ?? throw new InvalidOperationException(), Path.GetFileNameWithoutExtension(imagePath) + ".cicm.xml"),
                                   FileMode.CreateNew);

                    var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                    xmlSer.Serialize(xmlFs, sidecar);
                    xmlFs.Close();
                }
                catch (Exception ex)
                {
                    AaruConsole.ErrorWriteLine($"Error reading file: {ex.Message}");
                    AaruConsole.DebugWriteLine("Create sidecar command", ex.StackTrace);

                    return((int)ErrorNumber.UnexpectedException);
                }
            }
            else if (Directory.Exists(imagePath))
            {
                if (!tape)
                {
                    AaruConsole.ErrorWriteLine("Cannot create a sidecar from a directory.");

                    return((int)ErrorNumber.ExpectedFile);
                }

                string[]      contents = Directory.GetFiles(imagePath, "*", SearchOption.TopDirectoryOnly);
                List <string> files    = contents.Where(file => new FileInfo(file).Length % blockSize == 0).ToList();

                files.Sort(StringComparer.CurrentCultureIgnoreCase);

                var sidecarClass = new Sidecar();
                sidecarClass.InitProgressEvent    += Progress.InitProgress;
                sidecarClass.UpdateProgressEvent  += Progress.UpdateProgress;
                sidecarClass.EndProgressEvent     += Progress.EndProgress;
                sidecarClass.InitProgressEvent2   += Progress.InitProgress2;
                sidecarClass.UpdateProgressEvent2 += Progress.UpdateProgress2;
                sidecarClass.EndProgressEvent2    += Progress.EndProgress2;
                sidecarClass.UpdateStatusEvent    += Progress.UpdateStatus;
                CICMMetadataType sidecar = sidecarClass.BlockTape(Path.GetFileName(imagePath), files, blockSize);

                AaruConsole.WriteLine("Writing metadata sidecar");

                var xmlFs =
                    new
                    FileStream(Path.Combine(Path.GetDirectoryName(imagePath) ?? throw new InvalidOperationException(), Path.GetFileNameWithoutExtension(imagePath) + ".cicm.xml"),
                               FileMode.CreateNew);

                var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                xmlSer.Serialize(xmlFs, sidecar);
                xmlFs.Close();
            }
            else
            {
                AaruConsole.ErrorWriteLine("The specified input file cannot be found.");
            }

            return((int)ErrorNumber.NoError);
        }
Exemplo n.º 15
0
        // TODO: Get cartridge serial number from Certance vendor EVPD
        /// <summary>
        ///     Dumps a SCSI Block Commands device or a Reduced Block Commands devices
        /// </summary>
        /// <param name="dev">Device</param>
        /// <param name="devicePath">Path to the device</param>
        /// <param name="outputPrefix">Prefix for output data 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 or scrambled 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="dumpLeadIn">Try to read and dump as much Lead-in as possible</param>
        /// <param name="outputPath">Path to output file</param>
        /// <param name="formatOptions">Formats to pass to output file plugin</param>
        /// <exception cref="ArgumentException">If you asked to dump long sectors from a SCSI Streaming device</exception>
        public static void Dump(Device dev, string devicePath,
                                IWritableImage outputPlugin, ushort retryPasses,
                                bool force, bool dumpRaw,
                                bool persistent, bool stopOnError, ref Resume resume,
                                ref DumpLog dumpLog, bool dumpLeadIn,
                                Encoding encoding, string outputPrefix,
                                string outputPath, Dictionary <string, string> formatOptions,
                                CICMMetadataType preSidecar, uint skip, bool nometadata,
                                bool notrim)
        {
            MediaType dskType = MediaType.Unknown;
            int       resets  = 0;

            if (dev.IsRemovable)
            {
deviceGotReset:
                bool sense = dev.ScsiTestUnitReady(out byte[] senseBuf, dev.Timeout, out _);
                if (sense)
                {
                    FixedSense?decSense = Sense.DecodeFixed(senseBuf);
                    if (decSense.HasValue)
                    {
                        dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                          decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);

                        // Just retry, for 5 times
                        if (decSense.Value.ASC == 0x29)
                        {
                            resets++;
                            if (resets < 5)
                            {
                                goto deviceGotReset;
                            }
                        }

                        if (decSense.Value.ASC == 0x3A)
                        {
                            int leftRetries = 5;
                            while (leftRetries > 0)
                            {
                                DicConsole.WriteLine("\rWaiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                decSense = Sense.DecodeFixed(senseBuf);
                                if (decSense.HasValue)
                                {
                                    dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                                      decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
                                }
                                leftRetries--;
                            }

                            if (sense)
                            {
                                DicConsole.ErrorWriteLine("Please insert media in drive");
                                return;
                            }
                        }
                        else if (decSense.Value.ASC == 0x04 && decSense.Value.ASCQ == 0x01)
                        {
                            int leftRetries = 10;
                            while (leftRetries > 0)
                            {
                                DicConsole.WriteLine("\rWaiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                decSense = Sense.DecodeFixed(senseBuf);
                                if (decSense.HasValue)
                                {
                                    dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                                      decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
                                }
                                leftRetries--;
                            }

                            if (sense)
                            {
                                DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}",
                                                          Sense.PrettifySense(senseBuf));
                                return;
                            }
                        }

                        /*else if (decSense.Value.ASC == 0x29 && decSense.Value.ASCQ == 0x00)
                         * {
                         *  if (!deviceReset)
                         *  {
                         *      deviceReset = true;
                         *      DicConsole.ErrorWriteLine("Device did reset, retrying...");
                         *      goto retryTestReady;
                         *  }
                         *
                         *  DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf));
                         *  return;
                         * }*/
                        // These should be trapped by the OS but seems in some cases they're not
                        else if (decSense.Value.ASC == 0x28)
                        {
                            int leftRetries = 10;
                            while (leftRetries > 0)
                            {
                                DicConsole.WriteLine("\rWaiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                decSense = Sense.DecodeFixed(senseBuf);
                                if (decSense.HasValue)
                                {
                                    dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                                      decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
                                }
                                leftRetries--;
                            }

                            if (sense)
                            {
                                DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}",
                                                          Sense.PrettifySense(senseBuf));
                                return;
                            }
                        }
                        else
                        {
                            DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}",
                                                      Sense.PrettifySense(senseBuf));
                            return;
                        }
                    }
                    else
                    {
                        DicConsole.ErrorWriteLine("Unknown testing unit was ready.");
                        return;
                    }
                }
            }

            switch (dev.ScsiType)
            {
            case PeripheralDeviceTypes.SequentialAccess:
                if (dumpRaw)
                {
                    throw new ArgumentException("Tapes cannot be dumped raw.");
                }

                Ssc.Dump(dev, outputPrefix, devicePath, ref resume, ref dumpLog, preSidecar);
                return;

            case PeripheralDeviceTypes.MultiMediaDevice:
                Mmc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError,
                         ref dskType, ref resume, ref dumpLog, dumpLeadIn, encoding, outputPrefix, outputPath,
                         formatOptions, preSidecar, skip, nometadata, notrim);
                return;

            default:
                Sbc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError, null,
                         ref dskType, false, ref resume, ref dumpLog, encoding, outputPrefix, outputPath,
                         formatOptions, preSidecar, skip, nometadata, notrim);
                break;
            }
        }
Exemplo n.º 16
0
        /// <summary>
        ///     Implements creating a metadata sidecar
        /// </summary>
        /// <param name="image">Image</param>
        /// <param name="imagePath">Path to image</param>
        /// <param name="filterId">Filter uuid</param>
        /// <param name="encoding">Encoding for analysis</param>
        /// <returns>The metadata sidecar</returns>
        public static CICMMetadataType Create(IMediaImage image, string imagePath, Guid filterId, Encoding encoding)
        {
            CICMMetadataType sidecar = image.CicmMetadata ?? new CICMMetadataType();
            PluginBase       plugins = GetPluginBase.Instance;

            FileInfo   fi = new FileInfo(imagePath);
            FileStream fs = new FileStream(imagePath, FileMode.Open, FileAccess.Read);

            Checksum imgChkWorker = new Checksum();

            // For fast debugging, skip checksum
            //goto skipImageChecksum;

            byte[] data;
            long   position = 0;

            InitProgress();
            while (position < fi.Length - 1048576)
            {
                data = new byte[1048576];
                fs.Read(data, 0, 1048576);

                UpdateProgress("Hashing image file byte {0} of {1}", position, fi.Length);

                imgChkWorker.Update(data);

                position += 1048576;
            }

            data = new byte[fi.Length - position];
            fs.Read(data, 0, (int)(fi.Length - position));

            UpdateProgress("Hashing image file byte {0} of {1}", position, fi.Length);

            imgChkWorker.Update(data);

            // For fast debugging, skip checksum
            //skipImageChecksum:

            EndProgress();
            fs.Close();

            List <ChecksumType> imgChecksums = imgChkWorker.End();

            sidecar.OpticalDisc = null;
            sidecar.BlockMedia  = null;
            sidecar.AudioMedia  = null;
            sidecar.LinearMedia = null;

            switch (image.Info.XmlMediaType)
            {
            case XmlMediaType.OpticalDisc:
                OpticalDisc(image, filterId, imagePath, fi, plugins, imgChecksums, ref sidecar, encoding);
                break;

            case XmlMediaType.BlockMedia:
                BlockMedia(image, filterId, imagePath, fi, plugins, imgChecksums, ref sidecar, encoding);
                break;

            case XmlMediaType.LinearMedia:
                LinearMedia(image, filterId, imagePath, fi, plugins, imgChecksums, ref sidecar, encoding);
                break;

            case XmlMediaType.AudioMedia:
                AudioMedia(image, filterId, imagePath, fi, plugins, imgChecksums, ref sidecar, encoding);
                break;
            }

            return(sidecar);
        }
Exemplo n.º 17
0
        /// <summary>
        ///     Dumps the tape from a SCSI Streaming device
        /// </summary>
        /// <param name="dev">Device</param>
        /// <param name="devicePath">Path to the device</param>
        /// <param name="outputPrefix">Prefix for output data files</param>
        /// <param name="resume">Information for dump resuming</param>
        /// <param name="dumpLog">Dump logger</param>
        internal static void Dump(Device dev, string outputPrefix, string devicePath, ref Resume resume,
                                  ref DumpLog dumpLog,
                                  CICMMetadataType preSidecar)
        {
            FixedSense?      fxSense;
            bool             aborted;
            bool             sense;
            ulong            blocks = 0;
            uint             blockSize;
            MediaType        dskType = MediaType.Unknown;
            DateTime         start;
            DateTime         end;
            double           totalDuration    = 0;
            double           totalChkDuration = 0;
            double           currentSpeed     = 0;
            double           maxSpeed         = double.MinValue;
            double           minSpeed         = double.MaxValue;
            CICMMetadataType sidecar          = preSidecar ?? new CICMMetadataType();

            dev.RequestSense(out byte[] senseBuf, dev.Timeout, out double duration);
            fxSense = Sense.DecodeFixed(senseBuf, out string strSense);

            if (fxSense.HasValue && fxSense.Value.SenseKey != SenseKeys.NoSense)
            {
                dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey,
                                  fxSense.Value.ASC, fxSense.Value.ASCQ);
                DicConsole.ErrorWriteLine("Drive has status error, please correct. Sense follows...");
                DicConsole.ErrorWriteLine("{0}", strSense);
                return;
            }

            // Not in BOM/P
            if (fxSense.HasValue && fxSense.Value.ASC == 0x00 && fxSense.Value.ASCQ != 0x00 &&
                fxSense.Value.ASCQ != 0x04 && fxSense.Value.SenseKey != SenseKeys.IllegalRequest)
            {
                dumpLog.WriteLine("Rewinding, please wait...");
                DicConsole.Write("Rewinding, please wait...");
                // Rewind, let timeout apply
                dev.Rewind(out senseBuf, dev.Timeout, out duration);

                // Still rewinding?
                // TODO: Pause?
                do
                {
                    DicConsole.Write("\rRewinding, please wait...");
                    dev.RequestSense(out senseBuf, dev.Timeout, out duration);
                    fxSense = Sense.DecodeFixed(senseBuf, out strSense);
                }while(fxSense.HasValue && fxSense.Value.ASC == 0x00 &&
                       (fxSense.Value.ASCQ == 0x1A || fxSense.Value.ASCQ != 0x04));

                dev.RequestSense(out senseBuf, dev.Timeout, out duration);
                fxSense = Sense.DecodeFixed(senseBuf, out strSense);

                // And yet, did not rewind!
                if (fxSense.HasValue &&
                    (fxSense.Value.ASC == 0x00 && fxSense.Value.ASCQ != 0x04 || fxSense.Value.ASC != 0x00))
                {
                    DicConsole.WriteLine();
                    DicConsole.ErrorWriteLine("Drive could not rewind, please correct. Sense follows...");
                    DicConsole.ErrorWriteLine("{0}", strSense);
                    dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows...");
                    dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey,
                                      fxSense.Value.ASC, fxSense.Value.ASCQ);
                    return;
                }

                DicConsole.WriteLine();
            }

            // Check position
            sense = dev.ReadPosition(out byte[] cmdBuf, out senseBuf, SscPositionForms.Short, dev.Timeout,
                                     out duration);

            if (sense)
            {
                // READ POSITION is mandatory starting SCSI-2, so do not cry if the drive does not recognize the command (SCSI-1 or earlier)
                // Anyway, <=SCSI-1 tapes do not support partitions
                fxSense = Sense.DecodeFixed(senseBuf, out strSense);

                if (fxSense.HasValue && (fxSense.Value.ASC == 0x20 && fxSense.Value.ASCQ != 0x00 ||
                                         fxSense.Value.ASC != 0x20 &&
                                         fxSense.Value.SenseKey != SenseKeys.IllegalRequest))
                {
                    DicConsole.ErrorWriteLine("Could not get position. Sense follows...");
                    DicConsole.ErrorWriteLine("{0}", strSense);
                    dumpLog.WriteLine("Could not get position. Sense follows...");
                    dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey,
                                      fxSense.Value.ASC, fxSense.Value.ASCQ);
                    return;
                }
            }
            else
            {
                // Not in partition 0
                if (cmdBuf[1] != 0)
                {
                    DicConsole.Write("Drive not in partition 0. Rewinding, please wait...");
                    dumpLog.WriteLine("Drive not in partition 0. Rewinding, please wait...");
                    // Rewind, let timeout apply
                    sense = dev.Locate(out senseBuf, false, 0, 0, dev.Timeout, out duration);
                    if (sense)
                    {
                        DicConsole.WriteLine();
                        DicConsole.ErrorWriteLine("Drive could not rewind, please correct. Sense follows...");
                        DicConsole.ErrorWriteLine("{0}", strSense);
                        dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows...");
                        dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                          fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ);
                        return;
                    }

                    // Still rewinding?
                    // TODO: Pause?
                    do
                    {
                        Thread.Sleep(1000);
                        DicConsole.Write("\rRewinding, please wait...");
                        dev.RequestSense(out senseBuf, dev.Timeout, out duration);
                        fxSense = Sense.DecodeFixed(senseBuf, out strSense);
                    }while(fxSense.HasValue && fxSense.Value.ASC == 0x00 &&
                           (fxSense.Value.ASCQ == 0x1A || fxSense.Value.ASCQ == 0x19));

                    // And yet, did not rewind!
                    if (fxSense.HasValue && (fxSense.Value.ASC == 0x00 && fxSense.Value.ASCQ != 0x04 ||
                                             fxSense.Value.ASC != 0x00))
                    {
                        DicConsole.WriteLine();
                        DicConsole.ErrorWriteLine("Drive could not rewind, please correct. Sense follows...");
                        DicConsole.ErrorWriteLine("{0}", strSense);
                        dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows...");
                        dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                          fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ);
                        return;
                    }

                    sense = dev.ReadPosition(out cmdBuf, out senseBuf, SscPositionForms.Short, dev.Timeout,
                                             out duration);
                    if (sense)
                    {
                        fxSense = Sense.DecodeFixed(senseBuf, out strSense);
                        DicConsole.ErrorWriteLine("Drive could not rewind, please correct. Sense follows...");
                        DicConsole.ErrorWriteLine("{0}", strSense);
                        dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows...");
                        dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                          fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ);
                        return;
                    }

                    // Still not in partition 0!!!?
                    if (cmdBuf[1] != 0)
                    {
                        DicConsole.ErrorWriteLine("Drive could not rewind to partition 0 but no error occurred...");
                        dumpLog.WriteLine("Drive could not rewind to partition 0 but no error occurred...");
                        return;
                    }

                    DicConsole.WriteLine();
                }
            }

            sidecar.BlockMedia    = new BlockMediaType[1];
            sidecar.BlockMedia[0] = new BlockMediaType {
                SCSI = new SCSIType()
            };
            byte scsiMediumTypeTape  = 0;
            byte scsiDensityCodeTape = 0;

            dumpLog.WriteLine("Requesting MODE SENSE (10).");
            sense = dev.ModeSense10(out cmdBuf, out senseBuf, false, true, ScsiModeSensePageControl.Current, 0x3F, 0xFF,
                                    5, out duration);
            if (!sense || dev.Error)
            {
                sense = dev.ModeSense10(out cmdBuf, out senseBuf, false, true, ScsiModeSensePageControl.Current, 0x3F,
                                        0x00, 5, out duration);
            }

            Modes.DecodedMode?decMode = null;

            if (!sense && !dev.Error)
            {
                if (Modes.DecodeMode10(cmdBuf, dev.ScsiType).HasValue)
                {
                    decMode = Modes.DecodeMode10(cmdBuf, dev.ScsiType);
                    sidecar.BlockMedia[0].SCSI.ModeSense10 = new DumpType
                    {
                        Image     = outputPrefix + ".modesense10.bin",
                        Size      = cmdBuf.Length,
                        Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
                    };
                    DataFile.WriteTo("SCSI Dump", sidecar.BlockMedia[0].SCSI.ModeSense10.Image, cmdBuf);
                }
            }

            dumpLog.WriteLine("Requesting MODE SENSE (6).");
            sense = dev.ModeSense6(out cmdBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5,
                                   out duration);
            if (sense || dev.Error)
            {
                sense = dev.ModeSense6(out cmdBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5,
                                       out duration);
            }
            if (sense || dev.Error)
            {
                sense = dev.ModeSense(out cmdBuf, out senseBuf, 5, out duration);
            }

            if (!sense && !dev.Error)
            {
                if (Modes.DecodeMode6(cmdBuf, dev.ScsiType).HasValue)
                {
                    decMode = Modes.DecodeMode6(cmdBuf, dev.ScsiType);
                    sidecar.BlockMedia[0].SCSI.ModeSense = new DumpType
                    {
                        Image     = outputPrefix + ".modesense.bin",
                        Size      = cmdBuf.Length,
                        Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
                    };
                    DataFile.WriteTo("SCSI Dump", sidecar.BlockMedia[0].SCSI.ModeSense.Image, cmdBuf);
                }
            }

            // TODO: Check partitions page
            if (decMode.HasValue)
            {
                scsiMediumTypeTape = (byte)decMode.Value.Header.MediumType;
                if (decMode.Value.Header.BlockDescriptors != null && decMode.Value.Header.BlockDescriptors.Length >= 1)
                {
                    scsiDensityCodeTape = (byte)decMode.Value.Header.BlockDescriptors[0].Density;
                }
                blockSize = decMode.Value.Header.BlockDescriptors[0].BlockLength;
                dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);
            }
            else
            {
                blockSize = 1;
            }

            if (dskType == MediaType.Unknown)
            {
                dskType = MediaTypeFromScsi.Get((byte)dev.ScsiType, dev.Manufacturer, dev.Model, scsiMediumTypeTape,
                                                scsiDensityCodeTape, blocks, blockSize);
            }

            DicConsole.WriteLine("Media identified as {0}", dskType);

            dumpLog.WriteLine("SCSI device type: {0}.", dev.ScsiType);
            dumpLog.WriteLine("SCSI medium type: {0}.", scsiMediumTypeTape);
            dumpLog.WriteLine("SCSI density type: {0}.", scsiDensityCodeTape);
            dumpLog.WriteLine("Media identified as {0}.", dskType);

            bool  endOfMedia           = false;
            ulong currentBlock         = 0;
            ulong currentFile          = 0;
            byte  currentPartition     = 0;
            byte  totalPartitions      = 1; // TODO: Handle partitions.
            ulong currentSize          = 0;
            ulong currentPartitionSize = 0;
            ulong currentFileSize      = 0;

            bool fixedLen    = false;
            uint transferLen = blockSize;

            sense = dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, dev.Timeout,
                              out duration);
            if (sense)
            {
                fxSense = Sense.DecodeFixed(senseBuf, out strSense);
                if (fxSense.HasValue)
                {
                    if (fxSense.Value.SenseKey == SenseKeys.IllegalRequest)
                    {
                        sense = dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, dev.Timeout, out duration);
                        if (sense)
                        {
                            fxSense = Sense.DecodeFixed(senseBuf, out strSense);
                            if (!fxSense.HasValue || !fxSense.Value.EOM)
                            {
                                DicConsole.WriteLine();
                                DicConsole.ErrorWriteLine("Drive could not return back. Sense follows...");
                                DicConsole.ErrorWriteLine("{0}", strSense);
                                dumpLog.WriteLine("Drive could not return back. Sense follows...");
                                dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                                  fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ);
                                return;
                            }
                        }

                        fixedLen    = true;
                        transferLen = 1;
                        sense       = dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize,
                                                dev.Timeout, out duration);
                        if (sense)
                        {
                            DicConsole.WriteLine();
                            DicConsole.ErrorWriteLine("Drive could not read. Sense follows...");
                            DicConsole.ErrorWriteLine("{0}", strSense);
                            dumpLog.WriteLine("Drive could not read. Sense follows...");
                            dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                              fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ);
                            return;
                        }
                    }
                    else
                    {
                        DicConsole.WriteLine();
                        DicConsole.ErrorWriteLine("Drive could not read. Sense follows...");
                        DicConsole.ErrorWriteLine("{0}", strSense);
                        dumpLog.WriteLine("Drive could not read. Sense follows...");
                        dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                          fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ);
                        return;
                    }
                }
                else
                {
                    DicConsole.WriteLine();
                    DicConsole.ErrorWriteLine("Cannot read device, don't know why, exiting...");
                    dumpLog.WriteLine("Cannot read device, don't know why, exiting...");
                    return;
                }
            }

            sense = dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, dev.Timeout, out duration);
            if (sense)
            {
                fxSense = Sense.DecodeFixed(senseBuf, out strSense);
                if (!fxSense.HasValue || !fxSense.Value.EOM)
                {
                    DicConsole.WriteLine();
                    DicConsole.ErrorWriteLine("Drive could not return back. Sense follows...");
                    DicConsole.ErrorWriteLine("{0}", strSense);
                    dumpLog.WriteLine("Drive could not return back. Sense follows...");
                    dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey,
                                      fxSense.Value.ASC, fxSense.Value.ASCQ);
                    return;
                }
            }

            List <TapePartitionType> partitions = new List <TapePartitionType>();
            List <TapeFileType>      files      = new List <TapeFileType>();

            DicConsole.WriteLine();
            DataFile dumpFile = new DataFile(outputPrefix + ".bin");
            Checksum dataChk  = new Checksum();

            start = DateTime.UtcNow;
            MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, 1);
            IbgLog  ibgLog  = new IbgLog(outputPrefix + ".ibg", 0x0008);

            TapeFileType currentTapeFile = new TapeFileType
            {
                Image = new ImageType
                {
                    format          = "BINARY",
                    offset          = (long)currentSize,
                    offsetSpecified = true,
                    Value           = outputPrefix + ".bin"
                },
                Sequence   = (long)currentFile,
                StartBlock = (long)currentBlock,
                BlockSize  = blockSize
            };
            Checksum          fileChk = new Checksum();
            TapePartitionType currentTapePartition = new TapePartitionType
            {
                Image = new ImageType
                {
                    format          = "BINARY",
                    offset          = (long)currentSize,
                    offsetSpecified = true,
                    Value           = outputPrefix + ".bin"
                },
                Sequence   = currentPartition,
                StartBlock = (long)currentBlock
            };
            Checksum partitionChk = new Checksum();

            aborted = false;
            System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;

            while (currentPartition < totalPartitions)
            {
                if (aborted)
                {
                    dumpLog.WriteLine("Aborted!");
                    break;
                }

                if (endOfMedia)
                {
                    DicConsole.WriteLine();
                    DicConsole.WriteLine("Finished partition {0}", currentPartition);
                    dumpLog.WriteLine("Finished partition {0}", currentPartition);
                    currentTapePartition.File      = files.ToArray();
                    currentTapePartition.Checksums = partitionChk.End().ToArray();
                    currentTapePartition.EndBlock  = (long)(currentBlock - 1);
                    currentTapePartition.Size      = (long)currentPartitionSize;
                    partitions.Add(currentTapePartition);

                    currentPartition++;

                    if (currentPartition < totalPartitions)
                    {
                        currentFile++;
                        currentTapeFile = new TapeFileType
                        {
                            Image = new ImageType
                            {
                                format          = "BINARY",
                                offset          = (long)currentSize,
                                offsetSpecified = true,
                                Value           = outputPrefix + ".bin"
                            },
                            Sequence   = (long)currentFile,
                            StartBlock = (long)currentBlock,
                            BlockSize  = blockSize
                        };
                        currentFileSize      = 0;
                        fileChk              = new Checksum();
                        files                = new List <TapeFileType>();
                        currentTapePartition = new TapePartitionType
                        {
                            Image = new ImageType
                            {
                                format          = "BINARY",
                                offset          = (long)currentSize,
                                offsetSpecified = true,
                                Value           = outputPrefix + ".bin"
                            },
                            Sequence   = currentPartition,
                            StartBlock = (long)currentBlock
                        };
                        currentPartitionSize = 0;
                        partitionChk         = new Checksum();
                        DicConsole.WriteLine("Seeking to partition {0}", currentPartition);
                        dev.Locate(out senseBuf, false, currentPartition, 0, dev.Timeout, out duration);
                        totalDuration += duration;
                    }

                    continue;
                }

                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                if (currentSpeed > maxSpeed && currentSpeed != 0)
                {
                    maxSpeed = currentSpeed;
                }
                if (currentSpeed < minSpeed && currentSpeed != 0)
                {
                    minSpeed = currentSpeed;
                }
                #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                DicConsole.Write("\rReading block {0} ({1:F3} MiB/sec.)", currentBlock, currentSpeed);

                sense = dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, dev.Timeout,
                                  out duration);
                totalDuration += duration;

                if (sense)
                {
                    fxSense = Sense.DecodeFixed(senseBuf, out strSense);
                    if (fxSense.Value.ASC == 0x00 && fxSense.Value.ASCQ == 0x00 && fxSense.Value.ILI &&
                        fxSense.Value.InformationValid)
                    {
                        blockSize = (uint)((int)blockSize -
                                           BitConverter.ToInt32(BitConverter.GetBytes(fxSense.Value.Information), 0));
                        currentTapeFile.BlockSize = blockSize;

                        DicConsole.WriteLine();
                        DicConsole.WriteLine("Blocksize changed to {0} bytes at block {1}", blockSize, currentBlock);
                        dumpLog.WriteLine("Blocksize changed to {0} bytes at block {1}", blockSize, currentBlock);

                        sense = dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, dev.Timeout,
                                          out duration);
                        totalDuration += duration;

                        if (sense)
                        {
                            fxSense = Sense.DecodeFixed(senseBuf, out strSense);
                            DicConsole.WriteLine();
                            DicConsole.ErrorWriteLine("Drive could not go back one block. Sense follows...");
                            DicConsole.ErrorWriteLine("{0}", strSense);
                            dumpFile.Close();
                            dumpLog.WriteLine("Drive could not go back one block. Sense follows...");
                            dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                              fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ);
                            return;
                        }

                        continue;
                    }

                    if (fxSense.Value.SenseKey == SenseKeys.BlankCheck)
                    {
                        if (currentBlock == 0)
                        {
                            DicConsole.WriteLine();
                            DicConsole.ErrorWriteLine("Cannot dump a blank tape...");
                            dumpFile.Close();
                            dumpLog.WriteLine("Cannot dump a blank tape...");
                            return;
                        }

                        // For sure this is an end-of-tape/partition
                        if (fxSense.Value.ASC == 0x00 &&
                            (fxSense.Value.ASCQ == 0x02 || fxSense.Value.ASCQ == 0x05 || fxSense.Value.EOM))
                        {
                            // TODO: Detect end of partition
                            endOfMedia = true;
                            dumpLog.WriteLine("Found end-of-tape/partition...");
                            continue;
                        }

                        DicConsole.WriteLine();
                        DicConsole.WriteLine("Blank block found, end of tape?");
                        endOfMedia = true;
                        dumpLog.WriteLine("Blank block found, end of tape?...");
                        continue;
                    }

                    if ((fxSense.Value.SenseKey == SenseKeys.NoSense ||
                         fxSense.Value.SenseKey == SenseKeys.RecoveredError) &&
                        (fxSense.Value.ASCQ == 0x02 || fxSense.Value.ASCQ == 0x05 || fxSense.Value.EOM))
                    {
                        // TODO: Detect end of partition
                        endOfMedia = true;
                        dumpLog.WriteLine("Found end-of-tape/partition...");
                        continue;
                    }

                    if ((fxSense.Value.SenseKey == SenseKeys.NoSense ||
                         fxSense.Value.SenseKey == SenseKeys.RecoveredError) &&
                        (fxSense.Value.ASCQ == 0x01 || fxSense.Value.Filemark))
                    {
                        currentTapeFile.Checksums = fileChk.End().ToArray();
                        currentTapeFile.EndBlock  = (long)(currentBlock - 1);
                        currentTapeFile.Size      = (long)currentFileSize;
                        files.Add(currentTapeFile);

                        currentFile++;
                        currentTapeFile = new TapeFileType
                        {
                            Image = new ImageType
                            {
                                format          = "BINARY",
                                offset          = (long)currentSize,
                                offsetSpecified = true,
                                Value           = outputPrefix + ".bin"
                            },
                            Sequence   = (long)currentFile,
                            StartBlock = (long)currentBlock,
                            BlockSize  = blockSize
                        };
                        currentFileSize = 0;
                        fileChk         = new Checksum();

                        DicConsole.WriteLine();
                        DicConsole.WriteLine("Changed to file {0} at block {1}", currentFile, currentBlock);
                        dumpLog.WriteLine("Changed to file {0} at block {1}", currentFile, currentBlock);
                        continue;
                    }

                    // TODO: Add error recovering for tapes
                    fxSense = Sense.DecodeFixed(senseBuf, out strSense);
                    DicConsole.ErrorWriteLine("Drive could not read block. Sense follows...");
                    DicConsole.ErrorWriteLine("{0} {1}", fxSense.Value.SenseKey, strSense);
                    dumpLog.WriteLine("Drive could not read block. Sense follows...");
                    dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey,
                                      fxSense.Value.ASC, fxSense.Value.ASCQ);
                    return;
                }

                mhddLog.Write(currentBlock, duration);
                ibgLog.Write(currentBlock, currentSpeed * 1024);
                dumpFile.Write(cmdBuf);

                DateTime chkStart = DateTime.UtcNow;
                dataChk.Update(cmdBuf);
                fileChk.Update(cmdBuf);
                partitionChk.Update(cmdBuf);
                DateTime chkEnd      = DateTime.UtcNow;
                double   chkDuration = (chkEnd - chkStart).TotalMilliseconds;
                totalChkDuration += chkDuration;

                if (currentBlock % 10 == 0)
                {
                    double newSpeed = blockSize / (double)1048576 / (duration / 1000);
                    if (!double.IsInfinity(newSpeed))
                    {
                        currentSpeed = newSpeed;
                    }
                }

                currentBlock++;
                currentSize          += blockSize;
                currentFileSize      += blockSize;
                currentPartitionSize += blockSize;
            }

            blocks = currentBlock + 1;
            DicConsole.WriteLine();
            end = DateTime.UtcNow;
            mhddLog.Close();
            ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                         blockSize * (double)(blocks + 1) /
                         1024 / (totalDuration / 1000), devicePath);
            dumpLog.WriteLine("Dump finished in {0} seconds.",
                              (end - start).TotalSeconds);
            dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
                              (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
            dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
                              (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));

            DicConsole.WriteLine("Took a total of {0:F3} seconds ({1:F3} processing commands, {2:F3} checksumming).",
                                 (end - start).TotalSeconds, totalDuration / 1000, totalChkDuration / 1000);
            DicConsole.WriteLine("Avegare speed: {0:F3} MiB/sec.",
                                 (double)blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000));
            DicConsole.WriteLine("Fastest speed burst: {0:F3} MiB/sec.", maxSpeed);
            DicConsole.WriteLine("Slowest speed burst: {0:F3} MiB/sec.", minSpeed);

            sidecar.BlockMedia[0].Checksums  = dataChk.End().ToArray();
            sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType);
            Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp, out string xmlDskSubTyp);
            sidecar.BlockMedia[0].DiskType    = xmlDskTyp;
            sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp;
            // TODO: Implement device firmware revision
            sidecar.BlockMedia[0].Image = new ImageType
            {
                format = "Raw disk image (sector by sector copy)",
                Value  = outputPrefix + ".bin"
            };
            sidecar.BlockMedia[0].LogicalBlocks        = (long)blocks;
            sidecar.BlockMedia[0].Size                 = (long)currentSize;
            sidecar.BlockMedia[0].DumpHardwareArray    = new DumpHardwareType[1];
            sidecar.BlockMedia[0].DumpHardwareArray[0] =
                new DumpHardwareType {
                Extents = new ExtentType[1]
            };
            sidecar.BlockMedia[0].DumpHardwareArray[0].Extents[0] =
                new ExtentType {
                Start = 0, End = blocks - 1
            };
            sidecar.BlockMedia[0].DumpHardwareArray[0].Manufacturer = dev.Manufacturer;
            sidecar.BlockMedia[0].DumpHardwareArray[0].Model        = dev.Model;
            sidecar.BlockMedia[0].DumpHardwareArray[0].Revision     = dev.Revision;
            sidecar.BlockMedia[0].DumpHardwareArray[0].Serial       = dev.Serial;
            sidecar.BlockMedia[0].DumpHardwareArray[0].Software     = Version.GetSoftwareType();
            sidecar.BlockMedia[0].TapeInformation = partitions.ToArray();

            if (!aborted)
            {
                DicConsole.WriteLine("Writing metadata sidecar");

                FileStream xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create);

                XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                xmlSer.Serialize(xmlFs, sidecar);
                xmlFs.Close();
            }

            Statistics.AddMedia(dskType, true);
        }
Exemplo n.º 18
0
 public bool SetCicmMetadata(CICMMetadataType metadata)
 {
     // Not supported
     return(false);
 }
Exemplo n.º 19
0
        /// <summary>Dumps a MiniDisc Data device</summary>
        internal void MiniDisc()
        {
            bool         sense;
            byte         scsiMediumType = 0;
            const ushort sbcProfile     = 0x0001;
            DateTime     start;
            DateTime     end;
            double       totalDuration = 0;
            double       currentSpeed  = 0;
            double       maxSpeed      = double.MinValue;
            double       minSpeed      = double.MaxValue;

            byte[]            readBuffer;
            Modes.DecodedMode?decMode = null;
            Dictionary <MediaTagType, byte[]> mediaTags = new Dictionary <MediaTagType, byte[]>();

            byte[] cmdBuf;
            bool   ret;

            _dumpLog.WriteLine("Initializing reader.");
            var   scsiReader = new Reader(_dev, _dev.Timeout, null);
            ulong blocks     = scsiReader.GetDeviceBlocks();
            uint  blockSize  = scsiReader.LogicalBlockSize;

            _dumpLog.WriteLine("Requesting MODE SENSE (6).");
            UpdateStatus?.Invoke("Requesting MODE SENSE (6).");

            sense = _dev.ModeSense6(out cmdBuf, out _, true, ScsiModeSensePageControl.Current, 0x3F, 5, out _);

            if (!sense &&
                !_dev.Error &&
                Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue)
            {
                decMode = Modes.DecodeMode6(cmdBuf, _dev.ScsiType);
            }

            if (decMode.HasValue)
            {
                scsiMediumType = (byte)decMode.Value.Header.MediumType;
            }

            if (blockSize != 2048)
            {
                _dumpLog.WriteLine("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped.");

                StoppingErrorMessage?.
                Invoke("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped.");

                return;
            }

            MediaType dskType = MediaType.MDData;

            if (scsiReader.FindReadCommand())
            {
                _dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", scsiReader.ErrorMessage);
                StoppingErrorMessage?.Invoke("Unable to read medium.");

                return;
            }

            if (blocks != 0 &&
                blockSize != 0)
            {
                blocks++;

                UpdateStatus?.
                Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {blocks * (ulong)blockSize} bytes)");
            }

            // Check how many blocks to read, if error show and return
            // 64 works, gets maximum speed (150KiB/s), slow I know...
            if (scsiReader.GetBlocksToRead())
            {
                _dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", scsiReader.ErrorMessage);
                StoppingErrorMessage?.Invoke(scsiReader.ErrorMessage);

                return;
            }

            uint blocksToRead      = scsiReader.BlocksToRead;
            uint logicalBlockSize  = blockSize;
            uint physicalBlockSize = scsiReader.PhysicalBlockSize;

            if (blocks == 0)
            {
                _dumpLog.WriteLine("ERROR: Unable to read medium or empty medium present...");
                StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present...");

                return;
            }

            UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes).");
            UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time.");
            UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block.");
            UpdateStatus?.Invoke($"Device reports {scsiReader.LongBlockSize} bytes per physical block.");
            UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}.");
            UpdateStatus?.Invoke($"SCSI medium type: {scsiMediumType}.");
            UpdateStatus?.Invoke($"Media identified as {dskType}");

            _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);
            _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
            _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize);
            _dumpLog.WriteLine("Device reports {0} bytes per physical block.", scsiReader.LongBlockSize);
            _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType);
            _dumpLog.WriteLine("SCSI medium type: {0}.", scsiMediumType);
            _dumpLog.WriteLine("Media identified as {0}.", dskType);

            sense = _dev.MiniDiscGetType(out cmdBuf, out _, _dev.Timeout, out _);

            if (!sense &&
                !_dev.Error)
            {
                mediaTags.Add(MediaTagType.MiniDiscType, cmdBuf);
            }

            sense = _dev.MiniDiscD5(out cmdBuf, out _, _dev.Timeout, out _);

            if (!sense &&
                !_dev.Error)
            {
                mediaTags.Add(MediaTagType.MiniDiscD5, cmdBuf);
            }

            sense = _dev.MiniDiscReadDataTOC(out cmdBuf, out _, _dev.Timeout, out _);

            if (!sense &&
                !_dev.Error)
            {
                mediaTags.Add(MediaTagType.MiniDiscDTOC, cmdBuf);
            }

            var utocMs = new MemoryStream();

            for (uint i = 0; i < 3; i++)
            {
                sense = _dev.MiniDiscReadUserTOC(out cmdBuf, out _, i, _dev.Timeout, out _);

                if (sense || _dev.Error)
                {
                    break;
                }

                utocMs.Write(cmdBuf, 0, 2336);
            }

            if (utocMs.Length > 0)
            {
                mediaTags.Add(MediaTagType.MiniDiscUTOC, utocMs.ToArray());
            }

            ret = true;

            foreach (MediaTagType tag in mediaTags.Keys)
            {
                if (_outputPlugin.SupportedMediaTags.Contains(tag))
                {
                    continue;
                }

                ret = false;
                _dumpLog.WriteLine($"Output format does not support {tag}.");
                ErrorMessage?.Invoke($"Output format does not support {tag}.");
            }

            if (!ret)
            {
                if (_force)
                {
                    _dumpLog.WriteLine("Several media tags not supported, continuing...");
                    ErrorMessage?.Invoke("Several media tags not supported, continuing...");
                }
                else
                {
                    _dumpLog.WriteLine("Several media tags not supported, not continuing...");
                    StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing...");

                    return;
                }
            }

            UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time.");
            _dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead);

            var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private);
            var ibgLog  = new IbgLog(_outputPrefix + ".ibg", sbcProfile);

            ret = _outputPlugin.Create(_outputPath, dskType, _formatOptions, blocks, blockSize);

            // Cannot create image
            if (!ret)
            {
                _dumpLog.WriteLine("Error creating output image, not continuing.");
                _dumpLog.WriteLine(_outputPlugin.ErrorMessage);

                StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine +
                                             _outputPlugin.ErrorMessage);

                return;
            }

            start = DateTime.UtcNow;
            double imageWriteDuration = 0;

            if (decMode?.Pages != null)
            {
                bool setGeometry = false;

                foreach (Modes.ModePage page in decMode.Value.Pages)
                {
                    if (page.Page == 0x04 &&
                        page.Subpage == 0x00)
                    {
                        Modes.ModePage_04?rigidPage = Modes.DecodeModePage_04(page.PageResponse);

                        if (!rigidPage.HasValue || setGeometry)
                        {
                            continue;
                        }

                        _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track",
                                           rigidPage.Value.Cylinders, rigidPage.Value.Heads,
                                           (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads)));

                        UpdateStatus?.
                        Invoke($"Setting geometry to {rigidPage.Value.Cylinders} cylinders, {rigidPage.Value.Heads} heads, {(uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))} sectors per track");

                        _outputPlugin.SetGeometry(rigidPage.Value.Cylinders, rigidPage.Value.Heads,
                                                  (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads)));

                        setGeometry = true;
                    }
                    else if (page.Page == 0x05 &&
                             page.Subpage == 0x00)
                    {
                        Modes.ModePage_05?flexiblePage = Modes.DecodeModePage_05(page.PageResponse);

                        if (!flexiblePage.HasValue)
                        {
                            continue;
                        }

                        _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track",
                                           flexiblePage.Value.Cylinders, flexiblePage.Value.Heads,
                                           flexiblePage.Value.SectorsPerTrack);

                        UpdateStatus?.
                        Invoke($"Setting geometry to {flexiblePage.Value.Cylinders} cylinders, {flexiblePage.Value.Heads} heads, {flexiblePage.Value.SectorsPerTrack} sectors per track");

                        _outputPlugin.SetGeometry(flexiblePage.Value.Cylinders, flexiblePage.Value.Heads,
                                                  flexiblePage.Value.SectorsPerTrack);

                        setGeometry = true;
                    }
                }
            }

            DumpHardwareType currentTry = null;
            ExtentsULong     extents    = null;

            ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial,
                                  _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision,
                                  _private);

            if (currentTry == null ||
                extents == null)
            {
                StoppingErrorMessage?.Invoke("Could not process resume file, not continuing...");

                return;
            }

            if (_resume.NextBlock > 0)
            {
                UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}.");
                _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
            }

            bool     newTrim          = false;
            DateTime timeSpeedStart   = DateTime.UtcNow;
            ulong    sectorSpeedStart = 0;

            InitProgress?.Invoke();

            for (ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
            {
                if (_aborted)
                {
                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                    UpdateStatus?.Invoke("Aborted!");
                    _dumpLog.WriteLine("Aborted!");

                    break;
                }

                if (blocks - i < blocksToRead)
                {
                    blocksToRead = (uint)(blocks - i);
                }

                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                if (currentSpeed > maxSpeed &&
                    currentSpeed != 0)
                {
                    maxSpeed = currentSpeed;
                }

                if (currentSpeed < minSpeed &&
                    currentSpeed != 0)
                {
                    minSpeed = currentSpeed;
                }
                #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i,
                                       (long)blocks);

                sense = _dev.Read6(out readBuffer, out _, (uint)i, blockSize, (byte)blocksToRead, _dev.Timeout,
                                   out double cmdDuration);

                totalDuration += cmdDuration;

                if (!sense &&
                    !_dev.Error)
                {
                    mhddLog.Write(i, cmdDuration);
                    ibgLog.Write(i, currentSpeed * 1024);
                    DateTime writeStart = DateTime.Now;
                    _outputPlugin.WriteSectors(readBuffer, i, blocksToRead);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    extents.Add(i, blocksToRead, true);
                }
                else
                {
                    // TODO: Reset device after X errors
                    if (_stopOnError)
                    {
                        return; // TODO: Return more cleanly
                    }
                    if (i + _skip > blocks)
                    {
                        _skip = (uint)(blocks - i);
                    }

                    // Write empty data
                    DateTime writeStart = DateTime.Now;
                    _outputPlugin.WriteSectors(new byte[blockSize * _skip], i, _skip);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;

                    for (ulong b = i; b < i + _skip; b++)
                    {
                        _resume.BadBlocks.Add(b);
                    }

                    mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);

                    ibgLog.Write(i, 0);
                    _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
                    i      += _skip - blocksToRead;
                    newTrim = true;
                }

                sectorSpeedStart += blocksToRead;
                _resume.NextBlock = i + blocksToRead;

                double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;

                if (elapsed < 1)
                {
                    continue;
                }

                currentSpeed     = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                sectorSpeedStart = 0;
                timeSpeedStart   = DateTime.UtcNow;
            }

            end = DateTime.UtcNow;
            EndProgress?.Invoke();
            mhddLog.Close();

            ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                         (blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000), _devicePath);

            UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");

            UpdateStatus?.
            Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");

            UpdateStatus?.
            Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec.");

            _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);

            _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
                               ((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000));

            _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
                               ((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration);

            #region Trimming
            if (_resume.BadBlocks.Count > 0 &&
                !_aborted &&
                _trim &&
                newTrim)
            {
                start = DateTime.UtcNow;
                UpdateStatus?.Invoke("Trimming skipped sectors");
                _dumpLog.WriteLine("Trimming skipped sectors");

                ulong[] tmpArray = _resume.BadBlocks.ToArray();
                InitProgress?.Invoke();

                foreach (ulong badSector in tmpArray)
                {
                    if (_aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        UpdateStatus?.Invoke("Aborted!");
                        _dumpLog.WriteLine("Aborted!");

                        break;
                    }

                    PulseProgress?.Invoke($"Trimming sector {badSector}");

                    sense = _dev.Read6(out readBuffer, out _, (uint)badSector, blockSize, 1, _dev.Timeout,
                                       out double cmdDuration);

                    if (sense || _dev.Error)
                    {
                        continue;
                    }

                    _resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                    _outputPlugin.WriteSector(readBuffer, badSector);
                }

                EndProgress?.Invoke();
                end = DateTime.UtcNow;
                UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds.");
                _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds);
            }
            #endregion Trimming

            #region Error handling
            if (_resume.BadBlocks.Count > 0 &&
                !_aborted &&
                _retryPasses > 0)
            {
                int  pass              = 1;
                bool forward           = true;
                bool runningPersistent = false;

                Modes.ModePage?currentModePage = null;
                byte[]         md6;
                byte[]         md10;

                if (_persistent)
                {
                    Modes.ModePage_01_MMC pgMmc;
                    Modes.ModePage_01     pg;

                    sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01,
                                            _dev.Timeout, out _);

                    if (!sense)
                    {
                        Modes.DecodedMode?dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType);

                        if (dcMode6.HasValue &&
                            dcMode6.Value.Pages != null)
                        {
                            foreach (Modes.ModePage modePage in dcMode6.Value.Pages)
                            {
                                if (modePage.Page == 0x01 &&
                                    modePage.Subpage == 0x00)
                                {
                                    currentModePage = modePage;
                                }
                            }
                        }
                    }

                    if (currentModePage == null)
                    {
                        pg = new Modes.ModePage_01
                        {
                            PS  = false, AWRE = true, ARRE = true, TB = false,
                            RC  = false, EER = true, PER = false, DTE = true,
                            DCR = false, ReadRetryCount = 32
                        };

                        currentModePage = new Modes.ModePage
                        {
                            Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg)
                        };
                    }

                    pg = new Modes.ModePage_01
                    {
                        PS  = false, AWRE = false, ARRE = false, TB = true,
                        RC  = false, EER = true, PER = false, DTE = false,
                        DCR = false, ReadRetryCount = 255
                    };

                    var md = new Modes.DecodedMode
                    {
                        Header = new Modes.ModeHeader(), Pages = new[]
                        {
                            new Modes.ModePage
                            {
                                Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg)
                            }
                        }
                    };

                    md6 = Modes.EncodeMode6(md, _dev.ScsiType);

                    UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks).");
                    _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks).");
                    sense = _dev.ModeSelect(md6, out byte[] senseBuf, true, false, _dev.Timeout, out _);

                    if (sense)
                    {
                        UpdateStatus?.
                        Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");

                        AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf));

                        _dumpLog.
                        WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");
                    }
                    else
                    {
                        runningPersistent = true;
                    }
                }

                InitProgress?.Invoke();
repeatRetry:
                ulong[] tmpArray = _resume.BadBlocks.ToArray();

                foreach (ulong badSector in tmpArray)
                {
                    if (_aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        UpdateStatus?.Invoke("Aborted!");
                        _dumpLog.WriteLine("Aborted!");

                        break;
                    }

                    PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass,
                                                        forward ? "forward" : "reverse",
                                                        runningPersistent ? "recovering partial data, " : ""));

                    sense = _dev.Read6(out readBuffer, out _, (uint)badSector, blockSize, 1, _dev.Timeout,
                                       out double cmdDuration);

                    totalDuration += cmdDuration;

                    if (!sense &&
                        !_dev.Error)
                    {
                        _resume.BadBlocks.Remove(badSector);
                        extents.Add(badSector);
                        _outputPlugin.WriteSector(readBuffer, badSector);
                        UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}.");
                        _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
                    }
                    else if (runningPersistent)
                    {
                        _outputPlugin.WriteSector(readBuffer, badSector);
                    }
                }

                if (pass < _retryPasses &&
                    !_aborted &&
                    _resume.BadBlocks.Count > 0)
                {
                    pass++;
                    forward = !forward;
                    _resume.BadBlocks.Sort();

                    if (!forward)
                    {
                        _resume.BadBlocks.Reverse();
                    }

                    goto repeatRetry;
                }

                if (runningPersistent && currentModePage.HasValue)
                {
                    var md = new Modes.DecodedMode
                    {
                        Header = new Modes.ModeHeader(), Pages = new[]
                        {
                            currentModePage.Value
                        }
                    };

                    md6 = Modes.EncodeMode6(md, _dev.ScsiType);

                    UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status).");
                    _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status).");
                    _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _);
                }

                EndProgress?.Invoke();
            }
            #endregion Error handling

            _resume.BadBlocks.Sort();

            foreach (ulong bad in _resume.BadBlocks)
            {
                _dumpLog.WriteLine("Sector {0} could not be read.", bad);
            }

            currentTry.Extents = ExtentsConverter.ToMetadata(extents);

            _outputPlugin.SetDumpHardware(_resume.Tries);

            var metadata = new CommonTypes.Structs.ImageInfo
            {
                Application = "Aaru", ApplicationVersion = Version.GetVersion()
            };

            if (!_outputPlugin.SetMetadata(metadata))
            {
                ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine +
                                     _outputPlugin.ErrorMessage);
            }

            if (_preSidecar != null)
            {
                _outputPlugin.SetCicmMetadata(_preSidecar);
            }

            _dumpLog.WriteLine("Closing output file.");
            UpdateStatus?.Invoke("Closing output file.");
            DateTime closeStart = DateTime.Now;
            _outputPlugin.Close();
            DateTime closeEnd = DateTime.Now;
            UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds.");
            _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);

            if (_aborted)
            {
                UpdateStatus?.Invoke("Aborted!");
                _dumpLog.WriteLine("Aborted!");

                return;
            }

            double totalChkDuration = 0;

            if (_metadata)
            {
                UpdateStatus?.Invoke("Creating sidecar.");
                _dumpLog.WriteLine("Creating sidecar.");
                var         filters     = new FiltersList();
                IFilter     filter      = filters.GetFilter(_outputPath);
                IMediaImage inputPlugin = ImageFormat.Detect(filter);

                if (!inputPlugin.Open(filter))
                {
                    StoppingErrorMessage?.Invoke("Could not open created image.");

                    return;
                }

                DateTime chkStart = DateTime.UtcNow;
                _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding);
                _sidecarClass.InitProgressEvent    += InitProgress;
                _sidecarClass.UpdateProgressEvent  += UpdateProgress;
                _sidecarClass.EndProgressEvent     += EndProgress;
                _sidecarClass.InitProgressEvent2   += InitProgress2;
                _sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
                _sidecarClass.EndProgressEvent2    += EndProgress2;
                _sidecarClass.UpdateStatusEvent    += UpdateStatus;
                CICMMetadataType sidecar = _sidecarClass.Create();
                end = DateTime.UtcNow;

                totalChkDuration = (end - chkStart).TotalMilliseconds;
                UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds.");

                UpdateStatus?.
                Invoke($"Average checksum speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000):F3} KiB/sec.");

                _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);

                _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
                                   ((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000));

                if (_preSidecar != null)
                {
                    _preSidecar.BlockMedia = sidecar.BlockMedia;
                    sidecar = _preSidecar;
                }

                List <(ulong start, string type)> filesystems = new List <(ulong start, string type)>();

                if (sidecar.BlockMedia[0].FileSystemInformation != null)
                {
                    filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation
                                         where partition.FileSystems != null from fileSystem in partition.FileSystems
                                         select(partition.StartSector, fileSystem.Type));
                }

                if (filesystems.Count > 0)
                {
                    foreach (var filesystem in filesystems.Select(o => new
                    {
                        o.start, o.type
                    }).Distinct())
                    {
                        UpdateStatus?.Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}");
                        _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start);
                    }
                }

                sidecar.BlockMedia[0].Dimensions     = Dimensions.DimensionsFromMediaType(dskType);
                (string type, string subType)xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType);
                sidecar.BlockMedia[0].DiskType       = xmlType.type;
                sidecar.BlockMedia[0].DiskSubType    = xmlType.subType;

                if (!_dev.IsRemovable ||
                    _dev.IsUsb)
                {
                    if (_dev.Type == DeviceType.ATAPI)
                    {
                        sidecar.BlockMedia[0].Interface = "ATAPI";
                    }
                    else if (_dev.IsUsb)
                    {
                        sidecar.BlockMedia[0].Interface = "USB";
                    }
                    else if (_dev.IsFireWire)
                    {
                        sidecar.BlockMedia[0].Interface = "FireWire";
                    }
                    else
                    {
                        sidecar.BlockMedia[0].Interface = "SCSI";
                    }
                }

                sidecar.BlockMedia[0].LogicalBlocks     = blocks;
                sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize;
                sidecar.BlockMedia[0].LogicalBlockSize  = logicalBlockSize;
                sidecar.BlockMedia[0].Manufacturer      = _dev.Manufacturer;
                sidecar.BlockMedia[0].Model             = _dev.Model;

                if (!_private)
                {
                    sidecar.BlockMedia[0].Serial = _dev.Serial;
                }

                sidecar.BlockMedia[0].Size = blocks * blockSize;

                if (_dev.IsRemovable)
                {
                    sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray();
                }

                UpdateStatus?.Invoke("Writing metadata sidecar");

                var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);

                var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                xmlSer.Serialize(xmlFs, sidecar);
                xmlFs.Close();
            }

            UpdateStatus?.Invoke("");

            UpdateStatus?.
            Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");

            UpdateStatus?.
            Invoke($"Average speed: {((double)blockSize * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");

            if (maxSpeed > 0)
            {
                UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
            }

            if (minSpeed > 0 &&
                minSpeed < double.MaxValue)
            {
                UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
            }

            UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");
            UpdateStatus?.Invoke("");

            Statistics.AddMedia(dskType, true);
        }
Exemplo n.º 20
0
        /// <summary>
        ///     Dumps a MultiMediaCard or SecureDigital flash card
        /// </summary>
        /// <param name="dev">Device</param>
        /// <param name="devicePath">Path to the device</param>
        /// <param name="outputPrefix">Prefix for output data 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 or scrambled 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>
        /// <exception cref="ArgumentException">If you asked to dump long sectors from a SCSI Streaming device</exception>
        public static void Dump(Device dev, string devicePath,
                                IWritableImage outputPlugin, ushort retryPasses,
                                bool force, bool dumpRaw,
                                bool persistent, bool stopOnError, ref Resume resume,
                                ref DumpLog dumpLog, Encoding encoding,
                                string outputPrefix, string outputPath,
                                Dictionary <string, string> formatOptions, CICMMetadataType preSidecar,
                                uint skip,
                                bool nometadata, bool notrim)
        {
            bool aborted;

            if (dumpRaw)
            {
                DicConsole.ErrorWriteLine("Raw dumping is not supported in MultiMediaCard or SecureDigital devices.");

                if (force)
                {
                    DicConsole.ErrorWriteLine("Continuing...");
                }
                else
                {
                    DicConsole.ErrorWriteLine("Aborting...");
                    return;
                }
            }

            bool         sense;
            const ushort SD_PROFILE = 0x0001;
            const uint   TIMEOUT    = 5;
            double       duration;

            uint  blocksToRead = 128;
            uint  blockSize    = 512;
            ulong blocks       = 0;

            byte[] csd  = null;
            byte[] ocr  = null;
            byte[] ecsd = null;
            byte[] scr  = null;
            int    physicalBlockSize = 0;
            bool   byteAddressed     = true;

            Dictionary <MediaTagType, byte[]> mediaTags = new Dictionary <MediaTagType, byte[]>();

            switch (dev.Type)
            {
            case DeviceType.MMC:
            {
                dumpLog.WriteLine("Reading Extended CSD");
                sense = dev.ReadExtendedCsd(out ecsd, out _, TIMEOUT, out duration);
                if (!sense)
                {
                    ExtendedCSD ecsdDecoded = Decoders.MMC.Decoders.DecodeExtendedCSD(ecsd);
                    blocksToRead = ecsdDecoded.OptimalReadSize;
                    blocks       = ecsdDecoded.SectorCount;
                    blockSize    = (uint)(ecsdDecoded.SectorSize == 1 ? 4096 : 512);
                    if (ecsdDecoded.NativeSectorSize == 0)
                    {
                        physicalBlockSize = 512;
                    }
                    else if (ecsdDecoded.NativeSectorSize == 1)
                    {
                        physicalBlockSize = 4096;
                    }
                    // Supposing it's high-capacity MMC if it has Extended CSD...
                    byteAddressed = false;
                    mediaTags.Add(MediaTagType.MMC_ExtendedCSD, null);
                }
                else
                {
                    ecsd = null;
                }

                dumpLog.WriteLine("Reading CSD");
                sense = dev.ReadCsd(out csd, out _, TIMEOUT, out duration);
                if (!sense)
                {
                    if (blocks == 0)
                    {
                        CSD csdDecoded = Decoders.MMC.Decoders.DecodeCSD(csd);
                        blocks    = (ulong)((csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2));
                        blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength);
                    }

                    mediaTags.Add(MediaTagType.MMC_CSD, null);
                }
                else
                {
                    csd = null;
                }

                dumpLog.WriteLine("Reading OCR");
                sense = dev.ReadOcr(out ocr, out _, TIMEOUT, out duration);
                if (sense)
                {
                    ocr = null;
                }
                else
                {
                    mediaTags.Add(MediaTagType.MMC_OCR, null);
                }

                break;
            }

            case DeviceType.SecureDigital:
            {
                dumpLog.WriteLine("Reading CSD");
                sense = dev.ReadCsd(out csd, out _, TIMEOUT, out duration);
                if (!sense)
                {
                    Decoders.SecureDigital.CSD csdDecoded = Decoders.SecureDigital.Decoders.DecodeCSD(csd);
                    blocks = (ulong)(csdDecoded.Structure == 0
                                             ? (csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2)
                                             : (csdDecoded.Size + 1) * 1024);
                    blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength);
                    // Structure >=1 for SDHC/SDXC, so that's block addressed
                    byteAddressed = csdDecoded.Structure == 0;
                    mediaTags.Add(MediaTagType.SD_CSD, null);
                }
                else
                {
                    csd = null;
                }

                dumpLog.WriteLine("Reading OCR");
                sense = dev.ReadSdocr(out ocr, out _, TIMEOUT, out duration);
                if (sense)
                {
                    ocr = null;
                }
                else
                {
                    mediaTags.Add(MediaTagType.SD_OCR, null);
                }

                dumpLog.WriteLine("Reading SCR");
                sense = dev.ReadScr(out scr, out _, TIMEOUT, out duration);
                if (sense)
                {
                    scr = null;
                }
                else
                {
                    mediaTags.Add(MediaTagType.SD_SCR, null);
                }

                break;
            }
            }

            dumpLog.WriteLine("Reading CID");
            sense = dev.ReadCid(out byte[] cid, out _, TIMEOUT, out duration);
            if (sense)
            {
                cid = null;
            }
            else
            {
                mediaTags.Add(dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID : MediaTagType.MMC_CID, null);
            }

            DateTime start;
            DateTime end;
            double   totalDuration = 0;
            double   currentSpeed  = 0;
            double   maxSpeed      = double.MinValue;
            double   minSpeed      = double.MaxValue;

            aborted = false;
            System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;

            if (blocks == 0)
            {
                dumpLog.WriteLine("Cannot get device size.");
                DicConsole.ErrorWriteLine("Unable to get device size.");
                return;
            }

            dumpLog.WriteLine("Device reports {0} blocks.", blocks);

            byte[] cmdBuf;
            bool   error;

            while (true)
            {
                error = dev.Read(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, TIMEOUT, out duration);

                if (error)
                {
                    blocksToRead /= 2;
                }

                if (!error || blocksToRead == 1)
                {
                    break;
                }
            }

            if (error)
            {
                dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", dev.LastError);
                DicConsole.ErrorWriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError);
                return;
            }

            dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);

            if (skip < blocksToRead)
            {
                skip = blocksToRead;
            }

            DumpHardwareType currentTry = null;
            ExtentsULong     extents    = null;

            ResumeSupport.Process(true, false, blocks, dev.Manufacturer, dev.Model, dev.Serial, dev.PlatformId,
                                  ref resume, ref currentTry, ref extents);
            if (currentTry == null || extents == null)
            {
                throw new InvalidOperationException("Could not process resume file, not continuing...");
            }

            bool ret = true;

            foreach (MediaTagType tag in mediaTags.Keys)
            {
                if (outputPlugin.SupportedMediaTags.Contains(tag))
                {
                    continue;
                }

                ret = false;
                dumpLog.WriteLine($"Output format does not support {tag}.");
                DicConsole.ErrorWriteLine($"Output format does not support {tag}.");
            }

            if (!ret)
            {
                dumpLog.WriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
                DicConsole.ErrorWriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
                if (!force)
                {
                    return;
                }
            }

            DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead);

            MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
            IbgLog  ibgLog  = new IbgLog(outputPrefix + ".ibg", SD_PROFILE);

            ret = outputPlugin.Create(outputPath,
                                      dev.Type == DeviceType.SecureDigital ? MediaType.SecureDigital : MediaType.MMC,
                                      formatOptions, blocks, blockSize);

            // Cannot create image
            if (!ret)
            {
                dumpLog.WriteLine("Error creating output image, not continuing.");
                dumpLog.WriteLine(outputPlugin.ErrorMessage);
                DicConsole.ErrorWriteLine("Error creating output image, not continuing.");
                DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage);
                return;
            }

            if (resume.NextBlock > 0)
            {
                dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock);
            }

            start = DateTime.UtcNow;
            double imageWriteDuration = 0;
            bool   newTrim            = false;

            for (ulong i = resume.NextBlock; i < blocks; i += blocksToRead)
            {
                if (aborted)
                {
                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                    dumpLog.WriteLine("Aborted!");
                    break;
                }

                if (blocks - i < blocksToRead)
                {
                    blocksToRead = (byte)(blocks - i);
                }

                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                if (currentSpeed > maxSpeed && currentSpeed != 0)
                {
                    maxSpeed = currentSpeed;
                }
                if (currentSpeed < minSpeed && currentSpeed != 0)
                {
                    minSpeed = currentSpeed;
                }
                #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed);

                error = dev.Read(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed, TIMEOUT,
                                 out duration);

                if (!error)
                {
                    mhddLog.Write(i, duration);
                    ibgLog.Write(i, currentSpeed * 1024);
                    DateTime writeStart = DateTime.Now;
                    outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    extents.Add(i, blocksToRead, true);
                }
                else
                {
                    if (i + skip > blocks)
                    {
                        skip = (uint)(blocks - i);
                    }

                    for (ulong b = i; b < i + skip; b++)
                    {
                        resume.BadBlocks.Add(b);
                    }

                    mhddLog.Write(i, duration < 500 ? 65535 : duration);

                    ibgLog.Write(i, 0);
                    DateTime writeStart = DateTime.Now;
                    outputPlugin.WriteSectors(new byte[blockSize * skip], i, skip);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", skip, i);
                    i      += skip - blocksToRead;
                    newTrim = true;
                }

                double newSpeed =
                    (double)blockSize * blocksToRead / 1048576 / (duration / 1000);
                if (!double.IsInfinity(newSpeed))
                {
                    currentSpeed = newSpeed;
                }
                resume.NextBlock = i + blocksToRead;
            }

            end = DateTime.Now;
            DicConsole.WriteLine();
            mhddLog.Close();
            ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                         blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000),
                         devicePath);
            dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
            dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
                              (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
            dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
                              (double)blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration);

            #region Trimming
            if (resume.BadBlocks.Count > 0 && !aborted && !notrim && newTrim)
            {
                start = DateTime.UtcNow;
                dumpLog.WriteLine("Trimming bad sectors");

                ulong[] tmpArray = resume.BadBlocks.ToArray();
                foreach (ulong badSector in tmpArray)
                {
                    if (aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        dumpLog.WriteLine("Aborted!");
                        break;
                    }

                    DicConsole.Write("\rTrimming sector {0}", badSector);

                    error = dev.Read(out cmdBuf, out _, (uint)badSector, blockSize, 1, byteAddressed, TIMEOUT,
                                     out duration);

                    totalDuration += duration;

                    if (error)
                    {
                        continue;
                    }

                    resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                    outputPlugin.WriteSector(cmdBuf, badSector);
                }

                DicConsole.WriteLine();
                end = DateTime.UtcNow;
                dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds);
            }
            #endregion Trimming

            #region Error handling
            if (resume.BadBlocks.Count > 0 && !aborted && retryPasses > 0)
            {
                int  pass              = 1;
                bool forward           = true;
                bool runningPersistent = false;

repeatRetryLba:
                ulong[] tmpArray = resume.BadBlocks.ToArray();
                foreach (ulong badSector in tmpArray)
                {
                    if (aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        dumpLog.WriteLine("Aborted!");
                        break;
                    }

                    DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass,
                                     forward ? "forward" : "reverse",
                                     runningPersistent ? "recovering partial data, " : "");

                    error = dev.Read(out cmdBuf, out _, (uint)badSector, blockSize, 1, byteAddressed, TIMEOUT,
                                     out duration);

                    totalDuration += duration;

                    if (!error)
                    {
                        resume.BadBlocks.Remove(badSector);
                        extents.Add(badSector);
                        outputPlugin.WriteSector(cmdBuf, badSector);
                        dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
                    }
                    else if (runningPersistent)
                    {
                        outputPlugin.WriteSector(cmdBuf, badSector);
                    }
                }

                if (pass < retryPasses && !aborted && resume.BadBlocks.Count > 0)
                {
                    pass++;
                    forward = !forward;
                    resume.BadBlocks.Sort();
                    resume.BadBlocks.Reverse();
                    goto repeatRetryLba;
                }

                DicConsole.WriteLine();
            }
            #endregion Error handling

            currentTry.Extents = ExtentsConverter.ToMetadata(extents);

            outputPlugin.SetDumpHardware(resume.Tries);
            if (preSidecar != null)
            {
                outputPlugin.SetCicmMetadata(preSidecar);
            }
            dumpLog.WriteLine("Closing output file.");
            DicConsole.WriteLine("Closing output file.");
            DateTime closeStart = DateTime.Now;
            outputPlugin.Close();
            DateTime closeEnd = DateTime.Now;
            dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);

            if (aborted)
            {
                dumpLog.WriteLine("Aborted!");
                return;
            }

            double totalChkDuration = 0;
            if (!nometadata)
            {
                dumpLog.WriteLine("Creating sidecar.");
                FiltersList filters     = new FiltersList();
                IFilter     filter      = filters.GetFilter(outputPath);
                IMediaImage inputPlugin = ImageFormat.Detect(filter);
                if (!inputPlugin.Open(filter))
                {
                    throw new ArgumentException("Could not open created image.");
                }

                DateTime         chkStart = DateTime.UtcNow;
                CICMMetadataType sidecar  = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding);

                if (preSidecar != null)
                {
                    preSidecar.BlockMedia = sidecar.BlockMedia;
                    sidecar = preSidecar;
                }

                switch (dev.Type)
                {
                case DeviceType.MMC:
                    sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();
                    break;

                case DeviceType.SecureDigital:
                    sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType();
                    break;
                }

                DumpType cidDump = null;
                DumpType csdDump = null;
                DumpType ocrDump = null;

                if (cid != null)
                {
                    cidDump = new DumpType
                    {
                        Image     = outputPath,
                        Size      = cid.Length,
                        Checksums = Checksum.GetChecksums(cid).ToArray()
                    };

                    ret =
                        outputPlugin.WriteMediaTag(cid,
                                                   dev.Type == DeviceType.SecureDigital
                                                       ? MediaTagType.SD_CID
                                                       : MediaTagType.MMC_CID);

                    // Cannot write CID to image
                    if (!ret && !force)
                    {
                        dumpLog.WriteLine("Cannot write CID to output image.");
                        throw new ArgumentException(outputPlugin.ErrorMessage);
                    }
                }

                if (csd != null)
                {
                    csdDump = new DumpType
                    {
                        Image     = outputPath,
                        Size      = csd.Length,
                        Checksums = Checksum.GetChecksums(csd).ToArray()
                    };

                    ret =
                        outputPlugin.WriteMediaTag(csd,
                                                   dev.Type == DeviceType.SecureDigital
                                                       ? MediaTagType.SD_CSD
                                                       : MediaTagType.MMC_CSD);

                    // Cannot write CSD to image
                    if (!ret && !force)
                    {
                        dumpLog.WriteLine("Cannot write CSD to output image.");
                        throw new ArgumentException(outputPlugin.ErrorMessage);
                    }
                }

                if (ecsd != null)
                {
                    sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD = new DumpType
                    {
                        Image     = outputPath,
                        Size      = ecsd.Length,
                        Checksums = Checksum.GetChecksums(ecsd).ToArray()
                    };

                    ret = outputPlugin.WriteMediaTag(ecsd, MediaTagType.MMC_ExtendedCSD);

                    // Cannot write Extended CSD to image
                    if (!ret && !force)
                    {
                        dumpLog.WriteLine("Cannot write Extended CSD to output image.");
                        throw new ArgumentException(outputPlugin.ErrorMessage);
                    }
                }

                if (ocr != null)
                {
                    ocrDump = new DumpType
                    {
                        Image     = outputPath,
                        Size      = ocr.Length,
                        Checksums = Checksum.GetChecksums(ocr).ToArray()
                    };

                    ret =
                        outputPlugin.WriteMediaTag(ocr,
                                                   dev.Type == DeviceType.SecureDigital
                                                       ? MediaTagType.SD_OCR
                                                       : MediaTagType.MMC_OCR);

                    // Cannot write OCR to image
                    if (!ret && !force)
                    {
                        dumpLog.WriteLine("Cannot write OCR to output image.");
                        throw new ArgumentException(outputPlugin.ErrorMessage);
                    }
                }

                if (scr != null)
                {
                    sidecar.BlockMedia[0].SecureDigital.SCR = new DumpType
                    {
                        Image     = outputPath,
                        Size      = scr.Length,
                        Checksums = Checksum.GetChecksums(scr).ToArray()
                    };

                    ret = outputPlugin.WriteMediaTag(scr, MediaTagType.SD_SCR);

                    // Cannot write SCR to image
                    if (!ret && !force)
                    {
                        dumpLog.WriteLine("Cannot write SCR to output image.");
                        throw new ArgumentException(outputPlugin.ErrorMessage);
                    }
                }

                switch (dev.Type)
                {
                case DeviceType.MMC:
                    sidecar.BlockMedia[0].MultiMediaCard.CID = cidDump;
                    sidecar.BlockMedia[0].MultiMediaCard.CSD = csdDump;
                    sidecar.BlockMedia[0].MultiMediaCard.OCR = ocrDump;
                    break;

                case DeviceType.SecureDigital:
                    sidecar.BlockMedia[0].SecureDigital.CID = cidDump;
                    sidecar.BlockMedia[0].SecureDigital.CSD = csdDump;
                    sidecar.BlockMedia[0].SecureDigital.OCR = ocrDump;
                    break;
                }

                end = DateTime.UtcNow;

                totalChkDuration = (end - chkStart).TotalMilliseconds;
                dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);
                dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
                                  (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));

                string xmlDskTyp = null, xmlDskSubTyp = null;
                switch (dev.Type)
                {
                case DeviceType.MMC:
                    CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.MMC, out xmlDskTyp,
                                                                     out xmlDskSubTyp);
                    sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.MMC);
                    break;

                case DeviceType.SecureDigital:
                    CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.SecureDigital, out xmlDskTyp,
                                                                     out xmlDskSubTyp);
                    sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.SecureDigital);
                    break;
                }

                sidecar.BlockMedia[0].DiskType    = xmlDskTyp;
                sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp;
                // TODO: Implement device firmware revision
                sidecar.BlockMedia[0].LogicalBlocks     = (long)blocks;
                sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize > 0 ? physicalBlockSize : (int)blockSize;
                sidecar.BlockMedia[0].LogicalBlockSize  = (int)blockSize;
                sidecar.BlockMedia[0].Manufacturer      = dev.Manufacturer;
                sidecar.BlockMedia[0].Model             = dev.Model;
                sidecar.BlockMedia[0].Serial            = dev.Serial;
                sidecar.BlockMedia[0].Size = (long)(blocks * blockSize);

                DicConsole.WriteLine("Writing metadata sidecar");

                FileStream xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create);

                XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                xmlSer.Serialize(xmlFs, sidecar);
                xmlFs.Close();
            }

            DicConsole.WriteLine();

            DicConsole.WriteLine("Took a total of {0:F3} seconds ({1:F3} processing commands, {2:F3} checksumming, {3:F3} writing, {4:F3} closing).",
                                 (end - start).TotalSeconds, totalDuration / 1000,
                                 totalChkDuration / 1000,
                                 imageWriteDuration, (closeEnd - closeStart).TotalSeconds);
            DicConsole.WriteLine("Avegare speed: {0:F3} MiB/sec.",
                                 (double)blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000));
            DicConsole.WriteLine("Fastest speed burst: {0:F3} MiB/sec.", maxSpeed);
            DicConsole.WriteLine("Slowest speed burst: {0:F3} MiB/sec.", minSpeed);
            DicConsole.WriteLine("{0} sectors could not be read.", resume.BadBlocks.Count);
            if (resume.BadBlocks.Count > 0)
            {
                resume.BadBlocks.Sort();
            }
            DicConsole.WriteLine();

            switch (dev.Type)
            {
            case DeviceType.MMC:
                Statistics.AddMedia(MediaType.MMC, true);
                break;

            case DeviceType.SecureDigital:
                Statistics.AddMedia(MediaType.SecureDigital, true);
                break;
            }
        }
Exemplo n.º 21
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 skipCdireadyHole)
        {
            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", "--skip-cdiready-hole={0}", skipCdireadyHole);

            // 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 &&
                (resumeClass.BadSubchannels is null || resumeClass.BadSubchannels.Count == 0))
            {
                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, fixSubchannelPosition, retrySubchannel,
                                  fixSubchannel, fixSubchannelCrc, skipCdireadyHole);

            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);
        }
Exemplo n.º 22
0
        internal static void AddMediaTagToSidecar(string outputPath,
                                                  KeyValuePair <MediaTagType, byte[]> tag,
                                                  ref CICMMetadataType sidecar)
        {
            switch (tag.Key)
            {
            case MediaTagType.DVD_PFI:
                sidecar.OpticalDisc[0].PFI = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.DVD_DMI:
                sidecar.OpticalDisc[0].DMI = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.DVD_CMI:
            case MediaTagType.HDDVD_CPI:
                sidecar.OpticalDisc[0].CMI = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };

                byte[] tmp = new byte[tag.Value.Length + 4];
                Array.Copy(tag.Value, 0, tmp, 4, tag.Value.Length);
                tmp[0] = (byte)((tag.Value.Length & 0xFF00) >> 8);
                tmp[1] = (byte)(tag.Value.Length & 0xFF);

                CSS_CPRM.LeadInCopyright?cpy = CSS_CPRM.DecodeLeadInCopyright(tmp);
                if (cpy.HasValue && cpy.Value.CopyrightType != CopyrightType.NoProtection)
                {
                    sidecar.OpticalDisc[0].CopyProtection = cpy.Value.CopyrightType.ToString();
                }

                break;

            case MediaTagType.DVD_BCA:
            case MediaTagType.BD_BCA:
                sidecar.OpticalDisc[0].BCA = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.BD_DDS:
            case MediaTagType.DVDRAM_DDS:
                sidecar.OpticalDisc[0].DDS = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.DVDRAM_SpareArea:
            case MediaTagType.BD_SpareArea:
                sidecar.OpticalDisc[0].SAI = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.DVDR_PreRecordedInfo:
                sidecar.OpticalDisc[0].PRI = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.DVD_MediaIdentifier:
                sidecar.OpticalDisc[0].MediaID = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.DVDR_PFI:
                sidecar.OpticalDisc[0].PFIR = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.DVD_ADIP:
                sidecar.OpticalDisc[0].ADIP = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.DCB:
                sidecar.OpticalDisc[0].DCB = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.BD_DI:
                sidecar.OpticalDisc[0].DI = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.Xbox_SecuritySector:
                if (sidecar.OpticalDisc[0].Xbox == null)
                {
                    sidecar.OpticalDisc[0].Xbox = new XboxType();
                }

                sidecar.OpticalDisc[0].Xbox.SecuritySectors = new[]
                {
                    new XboxSecuritySectorsType
                    {
                        RequestNumber   = 0,
                        RequestVersion  = 1,
                        SecuritySectors = new DumpType
                        {
                            Image     = outputPath,
                            Size      = (ulong)tag.Value.Length,
                            Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                        }
                    }
                };

                break;

            case MediaTagType.Xbox_PFI:
                if (sidecar.OpticalDisc[0].Xbox == null)
                {
                    sidecar.OpticalDisc[0].Xbox = new XboxType();
                }

                sidecar.OpticalDisc[0].Xbox.PFI = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.Xbox_DMI:
                if (sidecar.OpticalDisc[0].Xbox == null)
                {
                    sidecar.OpticalDisc[0].Xbox = new XboxType();
                }

                sidecar.OpticalDisc[0].Xbox.DMI = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.CD_FullTOC:
                sidecar.OpticalDisc[0].TOC = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.CD_ATIP:
                sidecar.OpticalDisc[0].ATIP = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.CD_PMA:
                sidecar.OpticalDisc[0].PMA = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.CD_TEXT:
                sidecar.OpticalDisc[0].LeadInCdText = new DumpType
                {
                    Image     = outputPath,
                    Size      = (ulong)tag.Value.Length,
                    Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                };
                break;

            case MediaTagType.CD_FirstTrackPregap:
                sidecar.OpticalDisc[0].FirstTrackPregrap = new[]
                {
                    new BorderType
                    {
                        Image     = outputPath,
                        Size      = (ulong)tag.Value.Length,
                        Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                    }
                };
                break;

            case MediaTagType.CD_LeadIn:
                sidecar.OpticalDisc[0].LeadIn = new[]
                {
                    new BorderType
                    {
                        Image     = outputPath,
                        Size      = (ulong)tag.Value.Length,
                        Checksums = Checksum.GetChecksums(tag.Value).ToArray()
                    }
                };
                break;
            }
        }
Exemplo n.º 23
0
        /// <summary>
        ///     Dumps an Xbox Game Disc using a Kreon drive
        /// </summary>
        /// <param name="dev">Device</param>
        /// <param name="devicePath">Path to the device</param>
        /// <param name="outputPrefix">Prefix for output data 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 raw/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="mediaTags">Media tags as retrieved in MMC layer</param>
        /// <param name="dskType">Disc type as detected in MMC layer</param>
        /// <param name="outputPath">Path to output file</param>
        /// <param name="formatOptions">Formats to pass to output file plugin</param>
        /// <exception cref="InvalidOperationException">
        ///     If the provided resume does not correspond with the current in progress
        ///     dump
        /// </exception>
        internal static void Dump(Device dev, string devicePath,
                                  IWritableImage outputPlugin, ushort retryPasses,
                                  bool force, bool dumpRaw,
                                  bool persistent, bool stopOnError,
                                  Dictionary <MediaTagType, byte[]> mediaTags, ref MediaType dskType,
                                  ref Resume resume,
                                  ref DumpLog dumpLog, Encoding encoding,
                                  string outputPrefix, string outputPath,
                                  Dictionary <string, string> formatOptions, CICMMetadataType preSidecar,
                                  uint skip,
                                  bool nometadata, bool notrim)
        {
            bool       sense;
            const uint BLOCK_SIZE   = 2048;
            uint       blocksToRead = 64;
            DateTime   start;
            DateTime   end;
            double     totalDuration = 0;
            double     currentSpeed  = 0;
            double     maxSpeed      = double.MinValue;
            double     minSpeed      = double.MaxValue;
            bool       aborted       = false;

            System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;

            if (mediaTags.ContainsKey(MediaTagType.DVD_PFI))
            {
                mediaTags.Remove(MediaTagType.DVD_PFI);
            }
            if (mediaTags.ContainsKey(MediaTagType.DVD_DMI))
            {
                mediaTags.Remove(MediaTagType.DVD_DMI);
            }

            dumpLog.WriteLine("Reading Xbox Security Sector.");
            sense = dev.KreonExtractSs(out byte[] ssBuf, out byte[] senseBuf, dev.Timeout, out _);
            if (sense)
            {
                dumpLog.WriteLine("Cannot get Xbox Security Sector, not continuing.");
                DicConsole.ErrorWriteLine("Cannot get Xbox Security Sector, not continuing.");
                return;
            }

            dumpLog.WriteLine("Decoding Xbox Security Sector.");
            SS.SecuritySector?xboxSs = SS.Decode(ssBuf);
            if (!xboxSs.HasValue)
            {
                dumpLog.WriteLine("Cannot decode Xbox Security Sector, not continuing.");
                DicConsole.ErrorWriteLine("Cannot decode Xbox Security Sector, not continuing.");
                return;
            }

            byte[] tmpBuf = new byte[ssBuf.Length - 4];
            Array.Copy(ssBuf, 4, tmpBuf, 0, ssBuf.Length - 4);
            mediaTags.Add(MediaTagType.Xbox_SecuritySector, tmpBuf);

            // Get video partition size
            DicConsole.DebugWriteLine("Dump-media command", "Getting video partition size");
            dumpLog.WriteLine("Locking drive.");
            sense = dev.KreonLock(out senseBuf, dev.Timeout, out _);
            if (sense)
            {
                dumpLog.WriteLine("Cannot lock drive, not continuing.");
                DicConsole.ErrorWriteLine("Cannot lock drive, not continuing.");
                return;
            }

            dumpLog.WriteLine("Getting video partition size.");
            sense = dev.ReadCapacity(out byte[] readBuffer, out senseBuf, dev.Timeout, out _);
            if (sense)
            {
                dumpLog.WriteLine("Cannot get disc capacity.");
                DicConsole.ErrorWriteLine("Cannot get disc capacity.");
                return;
            }

            ulong totalSize =
                (ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]);

            dumpLog.WriteLine("Reading Physical Format Information.");
            sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0,
                                          MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _);
            if (sense)
            {
                dumpLog.WriteLine("Cannot get PFI.");
                DicConsole.ErrorWriteLine("Cannot get PFI.");
                return;
            }

            tmpBuf = new byte[readBuffer.Length - 4];
            Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4);
            mediaTags.Add(MediaTagType.DVD_PFI, tmpBuf);
            DicConsole.DebugWriteLine("Dump-media command", "Video partition total size: {0} sectors", totalSize);
            ulong l0Video = PFI.Decode(readBuffer).Value.Layer0EndPSN - PFI.Decode(readBuffer).Value.DataAreaStartPSN +
                            1;
            ulong l1Video = totalSize - l0Video + 1;

            dumpLog.WriteLine("Reading Disc Manufacturing Information.");
            sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0,
                                          MmcDiscStructureFormat.DiscManufacturingInformation, 0, 0, out _);
            if (sense)
            {
                dumpLog.WriteLine("Cannot get DMI.");
                DicConsole.ErrorWriteLine("Cannot get DMI.");
                return;
            }

            tmpBuf = new byte[readBuffer.Length - 4];
            Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4);
            mediaTags.Add(MediaTagType.DVD_DMI, tmpBuf);

            // Get game partition size
            DicConsole.DebugWriteLine("Dump-media command", "Getting game partition size");
            dumpLog.WriteLine("Unlocking drive (Xtreme).");
            sense = dev.KreonUnlockXtreme(out senseBuf, dev.Timeout, out _);
            if (sense)
            {
                dumpLog.WriteLine("Cannot unlock drive, not continuing.");
                DicConsole.ErrorWriteLine("Cannot unlock drive, not continuing.");
                return;
            }

            dumpLog.WriteLine("Getting game partition size.");
            sense = dev.ReadCapacity(out readBuffer, out senseBuf, dev.Timeout, out _);
            if (sense)
            {
                dumpLog.WriteLine("Cannot get disc capacity.");
                DicConsole.ErrorWriteLine("Cannot get disc capacity.");
                return;
            }

            ulong gameSize =
                (ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]) + 1;

            DicConsole.DebugWriteLine("Dump-media command", "Game partition total size: {0} sectors", gameSize);

            // Get middle zone size
            DicConsole.DebugWriteLine("Dump-media command", "Getting middle zone size");
            dumpLog.WriteLine("Unlocking drive (Wxripper).");
            sense = dev.KreonUnlockWxripper(out senseBuf, dev.Timeout, out _);
            if (sense)
            {
                dumpLog.WriteLine("Cannot unlock drive, not continuing.");
                DicConsole.ErrorWriteLine("Cannot unlock drive, not continuing.");
                return;
            }

            dumpLog.WriteLine("Getting disc size.");
            sense = dev.ReadCapacity(out readBuffer, out senseBuf, dev.Timeout, out _);
            if (sense)
            {
                dumpLog.WriteLine("Cannot get disc capacity.");
                DicConsole.ErrorWriteLine("Cannot get disc capacity.");
                return;
            }

            totalSize = (ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]);
            dumpLog.WriteLine("Reading Physical Format Information.");
            sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0,
                                          MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _);
            if (sense)
            {
                dumpLog.WriteLine("Cannot get PFI.");
                DicConsole.ErrorWriteLine("Cannot get PFI.");
                return;
            }

            DicConsole.DebugWriteLine("Dump-media command", "Unlocked total size: {0} sectors", totalSize);
            ulong blocks     = totalSize + 1;
            ulong middleZone =
                totalSize - (PFI.Decode(readBuffer).Value.Layer0EndPSN -
                             PFI.Decode(readBuffer).Value.DataAreaStartPSN +
                             1) - gameSize + 1;

            tmpBuf = new byte[readBuffer.Length - 4];
            Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4);
            mediaTags.Add(MediaTagType.Xbox_PFI, tmpBuf);

            dumpLog.WriteLine("Reading Disc Manufacturing Information.");
            sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0,
                                          MmcDiscStructureFormat.DiscManufacturingInformation, 0, 0, out _);
            if (sense)
            {
                dumpLog.WriteLine("Cannot get DMI.");
                DicConsole.ErrorWriteLine("Cannot get DMI.");
                return;
            }

            tmpBuf = new byte[readBuffer.Length - 4];
            Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4);
            mediaTags.Add(MediaTagType.Xbox_DMI, tmpBuf);

            totalSize = l0Video + l1Video + middleZone * 2 + gameSize;
            ulong layerBreak = l0Video + middleZone + gameSize / 2;

            DicConsole.WriteLine("Video layer 0 size: {0} sectors", l0Video);
            DicConsole.WriteLine("Video layer 1 size: {0} sectors", l1Video);
            DicConsole.WriteLine("Middle zone size: {0} sectors", middleZone);
            DicConsole.WriteLine("Game data size: {0} sectors", gameSize);
            DicConsole.WriteLine("Total size: {0} sectors", totalSize);
            DicConsole.WriteLine("Real layer break: {0}", layerBreak);
            DicConsole.WriteLine();

            dumpLog.WriteLine("Video layer 0 size: {0} sectors", l0Video);
            dumpLog.WriteLine("Video layer 1 size: {0} sectors", l1Video);
            dumpLog.WriteLine("Middle zone 0 size: {0} sectors", middleZone);
            dumpLog.WriteLine("Game data 0 size: {0} sectors", gameSize);
            dumpLog.WriteLine("Total 0 size: {0} sectors", totalSize);
            dumpLog.WriteLine("Real layer break: {0}", layerBreak);

            bool read12 = !dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, 0, BLOCK_SIZE, 0, 1,
                                      false, dev.Timeout, out _);

            if (!read12)
            {
                dumpLog.WriteLine("Cannot read medium, aborting scan...");
                DicConsole.ErrorWriteLine("Cannot read medium, aborting scan...");
                return;
            }

            dumpLog.WriteLine("Using SCSI READ (12) command.");
            DicConsole.WriteLine("Using SCSI READ (12) command.");

            while (true)
            {
                if (read12)
                {
                    sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, 0, BLOCK_SIZE, 0,
                                       blocksToRead, false, dev.Timeout, out _);
                    if (sense || dev.Error)
                    {
                        blocksToRead /= 2;
                    }
                }

                if (!dev.Error || blocksToRead == 1)
                {
                    break;
                }
            }

            if (dev.Error)
            {
                dumpLog.WriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError);
                DicConsole.ErrorWriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError);
                return;
            }

            if (skip < blocksToRead)
            {
                skip = blocksToRead;
            }

            bool ret = true;

            foreach (MediaTagType tag in mediaTags.Keys)
            {
                if (outputPlugin.SupportedMediaTags.Contains(tag))
                {
                    continue;
                }

                ret = false;
                dumpLog.WriteLine($"Output format does not support {tag}.");
                DicConsole.ErrorWriteLine($"Output format does not support {tag}.");
            }

            if (!ret)
            {
                dumpLog.WriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
                DicConsole.ErrorWriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
                if (!force)
                {
                    return;
                }
            }

            dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead);
            DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead);

            MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, BLOCK_SIZE, blocksToRead);
            IbgLog  ibgLog  = new IbgLog(outputPrefix + ".ibg", 0x0010);

            ret = outputPlugin.Create(outputPath, dskType, formatOptions, blocks, BLOCK_SIZE);

            // Cannot create image
            if (!ret)
            {
                dumpLog.WriteLine("Error creating output image, not continuing.");
                dumpLog.WriteLine(outputPlugin.ErrorMessage);
                DicConsole.ErrorWriteLine("Error creating output image, not continuing.");
                DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage);
                return;
            }

            start = DateTime.UtcNow;
            double imageWriteDuration = 0;

            double           cmdDuration      = 0;
            uint             saveBlocksToRead = blocksToRead;
            DumpHardwareType currentTry       = null;
            ExtentsULong     extents          = null;

            ResumeSupport.Process(true, true, totalSize, dev.Manufacturer, dev.Model, dev.Serial, dev.PlatformId,
                                  ref resume, ref currentTry, ref extents);
            if (currentTry == null || extents == null)
            {
                throw new NotImplementedException("Could not process resume file, not continuing...");
            }

            outputPlugin.SetTracks(new List <Track>
            {
                new Track
                {
                    TrackBytesPerSector    = (int)BLOCK_SIZE,
                    TrackEndSector         = blocks - 1,
                    TrackSequence          = 1,
                    TrackRawBytesPerSector = (int)BLOCK_SIZE,
                    TrackSubchannelType    = TrackSubchannelType.None,
                    TrackSession           = 1,
                    TrackType = TrackType.Data
                }
            });

            ulong currentSector = resume.NextBlock;

            if (resume.NextBlock > 0)
            {
                dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock);
            }
            bool newTrim = false;

            dumpLog.WriteLine("Reading game partition.");
            for (int e = 0; e <= 16; e++)
            {
                if (aborted)
                {
                    resume.NextBlock   = currentSector;
                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                    dumpLog.WriteLine("Aborted!");
                    break;
                }

                if (currentSector >= blocks)
                {
                    break;
                }

                ulong extentStart, extentEnd;
                // Extents
                if (e < 16)
                {
                    if (xboxSs.Value.Extents[e].StartPSN <= xboxSs.Value.Layer0EndPSN)
                    {
                        extentStart = xboxSs.Value.Extents[e].StartPSN - 0x30000;
                    }
                    else
                    {
                        extentStart = (xboxSs.Value.Layer0EndPSN + 1) * 2 -
                                      ((xboxSs.Value.Extents[e].StartPSN ^ 0xFFFFFF) + 1) - 0x30000;
                    }
                    if (xboxSs.Value.Extents[e].EndPSN <= xboxSs.Value.Layer0EndPSN)
                    {
                        extentEnd = xboxSs.Value.Extents[e].EndPSN - 0x30000;
                    }
                    else
                    {
                        extentEnd = (xboxSs.Value.Layer0EndPSN + 1) * 2 -
                                    ((xboxSs.Value.Extents[e].EndPSN ^ 0xFFFFFF) + 1) - 0x30000;
                    }
                }
                // After last extent
                else
                {
                    extentStart = blocks;
                    extentEnd   = blocks;
                }

                if (currentSector > extentEnd)
                {
                    continue;
                }

                for (ulong i = currentSector; i < extentStart; i += blocksToRead)
                {
                    saveBlocksToRead = blocksToRead;

                    if (aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        dumpLog.WriteLine("Aborted!");
                        break;
                    }

                    if (extentStart - i < blocksToRead)
                    {
                        blocksToRead = (uint)(extentStart - i);
                    }

                    #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                    if (currentSpeed > maxSpeed && currentSpeed != 0)
                    {
                        maxSpeed = currentSpeed;
                    }
                    if (currentSpeed < minSpeed && currentSpeed != 0)
                    {
                        minSpeed = currentSpeed;
                    }
                    #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                    DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, totalSize, currentSpeed);

                    sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)i, BLOCK_SIZE,
                                       0, blocksToRead, false, dev.Timeout, out cmdDuration);
                    totalDuration += cmdDuration;

                    if (!sense && !dev.Error)
                    {
                        mhddLog.Write(i, cmdDuration);
                        ibgLog.Write(i, currentSpeed * 1024);
                        DateTime writeStart = DateTime.Now;
                        outputPlugin.WriteSectors(readBuffer, i, blocksToRead);
                        imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                        extents.Add(i, blocksToRead, true);
                    }
                    else
                    {
                        // TODO: Reset device after X errors
                        if (stopOnError)
                        {
                            return;             // TODO: Return more cleanly
                        }
                        if (i + skip > blocks)
                        {
                            skip = (uint)(blocks - i);
                        }

                        // Write empty data
                        DateTime writeStart = DateTime.Now;
                        outputPlugin.WriteSectors(new byte[BLOCK_SIZE * skip], i, skip);
                        imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;

                        for (ulong b = i; b < i + skip; b++)
                        {
                            resume.BadBlocks.Add(b);
                        }

                        DicConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf));
                        mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);

                        ibgLog.Write(i, 0);

                        dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", skip, i);
                        i += skip - blocksToRead;
                        string[] senseLines = Sense.PrettifySense(senseBuf).Split(new[] { Environment.NewLine },
                                                                                  StringSplitOptions
                                                                                  .RemoveEmptyEntries);
                        foreach (string senseLine in senseLines)
                        {
                            dumpLog.WriteLine(senseLine);
                        }
                        newTrim = true;
                    }

                    double newSpeed =
                        (double)BLOCK_SIZE * blocksToRead / 1048576 / (cmdDuration / 1000);
                    if (!double.IsInfinity(newSpeed))
                    {
                        currentSpeed = newSpeed;
                    }
                    blocksToRead     = saveBlocksToRead;
                    currentSector    = i + 1;
                    resume.NextBlock = currentSector;
                }

                for (ulong i = extentStart; i <= extentEnd; i += blocksToRead)
                {
                    saveBlocksToRead = blocksToRead;
                    if (aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        dumpLog.WriteLine("Aborted!");
                        break;
                    }

                    if (extentEnd - i < blocksToRead)
                    {
                        blocksToRead = (uint)(extentEnd - i) + 1;
                    }

                    mhddLog.Write(i, cmdDuration);
                    ibgLog.Write(i, currentSpeed * 1024);
                    // Write empty data
                    DateTime writeStart = DateTime.Now;
                    outputPlugin.WriteSectors(new byte[BLOCK_SIZE * blocksToRead], i, blocksToRead);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    blocksToRead        = saveBlocksToRead;
                    extents.Add(i, blocksToRead, true);
                    currentSector    = i + 1;
                    resume.NextBlock = currentSector;
                }

                if (!aborted)
                {
                    currentSector = extentEnd + 1;
                }
            }

            // Middle Zone D
            dumpLog.WriteLine("Writing Middle Zone D (empty).");
            for (ulong middle = currentSector - blocks - 1; middle < middleZone - 1; middle += blocksToRead)
            {
                if (aborted)
                {
                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                    dumpLog.WriteLine("Aborted!");
                    break;
                }

                if (middleZone - 1 - middle < blocksToRead)
                {
                    blocksToRead = (uint)(middleZone - 1 - middle);
                }

                DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", middle + currentSector, totalSize,
                                 currentSpeed);

                mhddLog.Write(middle + currentSector, cmdDuration);
                ibgLog.Write(middle + currentSector, currentSpeed * 1024);
                // Write empty data
                DateTime writeStart = DateTime.Now;
                outputPlugin.WriteSectors(new byte[BLOCK_SIZE * blocksToRead], middle + currentSector, blocksToRead);
                imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                extents.Add(currentSector, blocksToRead, true);

                currentSector   += blocksToRead;
                resume.NextBlock = currentSector;
            }

            blocksToRead = saveBlocksToRead;

            dumpLog.WriteLine("Locking drive.");
            sense = dev.KreonLock(out senseBuf, dev.Timeout, out _);
            if (sense)
            {
                dumpLog.WriteLine("Cannot lock drive, not continuing.");
                DicConsole.ErrorWriteLine("Cannot lock drive, not continuing.");
                return;
            }

            sense = dev.ReadCapacity(out readBuffer, out senseBuf, dev.Timeout, out _);
            if (sense)
            {
                DicConsole.ErrorWriteLine("Cannot get disc capacity.");
                return;
            }

            // Video Layer 1
            dumpLog.WriteLine("Reading Video Layer 1.");
            for (ulong l1 = currentSector - blocks - middleZone + l0Video; l1 < l0Video + l1Video; l1 += blocksToRead)
            {
                if (aborted)
                {
                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                    dumpLog.WriteLine("Aborted!");
                    break;
                }

                if (l0Video + l1Video - l1 < blocksToRead)
                {
                    blocksToRead = (uint)(l0Video + l1Video - l1);
                }

                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                if (currentSpeed > maxSpeed && currentSpeed != 0)
                {
                    maxSpeed = currentSpeed;
                }
                if (currentSpeed < minSpeed && currentSpeed != 0)
                {
                    minSpeed = currentSpeed;
                }
                #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", currentSector, totalSize,
                                 currentSpeed);

                sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)l1, BLOCK_SIZE, 0,
                                   blocksToRead, false, dev.Timeout, out cmdDuration);
                totalDuration += cmdDuration;

                if (!sense && !dev.Error)
                {
                    mhddLog.Write(currentSector, cmdDuration);
                    ibgLog.Write(currentSector, currentSpeed * 1024);
                    DateTime writeStart = DateTime.Now;
                    outputPlugin.WriteSectors(readBuffer, currentSector, blocksToRead);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    extents.Add(currentSector, blocksToRead, true);
                }
                else
                {
                    // TODO: Reset device after X errors
                    if (stopOnError)
                    {
                        return;             // TODO: Return more cleanly
                    }
                    // Write empty data
                    DateTime writeStart = DateTime.Now;
                    outputPlugin.WriteSectors(new byte[BLOCK_SIZE * skip], currentSector, skip);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;

                    // TODO: Handle errors in video partition
                    //errored += blocksToRead;
                    //resume.BadBlocks.Add(l1);
                    DicConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf));
                    mhddLog.Write(l1, cmdDuration < 500 ? 65535 : cmdDuration);

                    ibgLog.Write(l1, 0);
                    dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", skip, l1);
                    l1 += skip - blocksToRead;
                    string[] senseLines = Sense.PrettifySense(senseBuf).Split(new[] { Environment.NewLine },
                                                                              StringSplitOptions.RemoveEmptyEntries);
                    foreach (string senseLine in senseLines)
                    {
                        dumpLog.WriteLine(senseLine);
                    }
                }

                double newSpeed =
                    (double)BLOCK_SIZE * blocksToRead / 1048576 / (cmdDuration / 1000);
                if (!double.IsInfinity(newSpeed))
                {
                    currentSpeed = newSpeed;
                }
                currentSector   += blocksToRead;
                resume.NextBlock = currentSector;
            }

            dumpLog.WriteLine("Unlocking drive (Wxripper).");
            sense = dev.KreonUnlockWxripper(out senseBuf, dev.Timeout, out _);
            if (sense)
            {
                dumpLog.WriteLine("Cannot unlock drive, not continuing.");
                DicConsole.ErrorWriteLine("Cannot unlock drive, not continuing.");
                return;
            }

            sense = dev.ReadCapacity(out readBuffer, out senseBuf, dev.Timeout, out _);
            if (sense)
            {
                DicConsole.ErrorWriteLine("Cannot get disc capacity.");
                return;
            }

            end = DateTime.UtcNow;
            DicConsole.WriteLine();
            mhddLog.Close();
            ibgLog.Close(dev, blocks, BLOCK_SIZE, (end - start).TotalSeconds, currentSpeed * 1024,
                         BLOCK_SIZE * (double)(blocks + 1) / 1024 / (totalDuration / 1000),
                         devicePath);
            dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
            dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
                              (double)BLOCK_SIZE * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
            dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
                              (double)BLOCK_SIZE * (double)(blocks + 1) / 1024 / imageWriteDuration);

            #region Trimming
            if (resume.BadBlocks.Count > 0 && !aborted && !notrim && newTrim)
            {
                start = DateTime.UtcNow;
                dumpLog.WriteLine("Trimming bad sectors");

                ulong[] tmpArray = resume.BadBlocks.ToArray();
                foreach (ulong badSector in tmpArray)
                {
                    if (aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        dumpLog.WriteLine("Aborted!");
                        break;
                    }

                    DicConsole.Write("\rTrimming sector {0}", badSector);

                    sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)badSector,
                                       BLOCK_SIZE, 0, 1, false, dev.Timeout, out cmdDuration);
                    totalDuration += cmdDuration;

                    if (sense || dev.Error)
                    {
                        continue;
                    }

                    resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                    outputPlugin.WriteSector(readBuffer, badSector);
                }

                DicConsole.WriteLine();
                end = DateTime.UtcNow;
                dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds);
            }
            #endregion Trimming

            #region Error handling
            if (resume.BadBlocks.Count > 0 && !aborted && retryPasses > 0)
            {
                List <ulong> tmpList = new List <ulong>();

                foreach (ulong ur in resume.BadBlocks)
                {
                    for (ulong i = ur; i < ur + blocksToRead; i++)
                    {
                        tmpList.Add(i);
                    }
                }

                tmpList.Sort();

                int  pass              = 1;
                bool forward           = true;
                bool runningPersistent = false;

                resume.BadBlocks = tmpList;
                Modes.ModePage?currentModePage = null;
                byte[]         md6;
                byte[]         md10;

                if (persistent)
                {
                    Modes.ModePage_01_MMC pgMmc;

                    sense = dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01,
                                           dev.Timeout, out _);
                    if (sense)
                    {
                        sense = dev.ModeSense10(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01,
                                                dev.Timeout, out _);

                        if (!sense)
                        {
                            Modes.DecodedMode?dcMode10 =
                                Modes.DecodeMode10(readBuffer, PeripheralDeviceTypes.MultiMediaDevice);

                            if (dcMode10.HasValue)
                            {
                                foreach (Modes.ModePage modePage in dcMode10.Value.Pages)
                                {
                                    if (modePage.Page == 0x01 && modePage.Subpage == 0x00)
                                    {
                                        currentModePage = modePage;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        Modes.DecodedMode?dcMode6 =
                            Modes.DecodeMode6(readBuffer, PeripheralDeviceTypes.MultiMediaDevice);

                        if (dcMode6.HasValue)
                        {
                            foreach (Modes.ModePage modePage in dcMode6.Value.Pages)
                            {
                                if (modePage.Page == 0x01 && modePage.Subpage == 0x00)
                                {
                                    currentModePage = modePage;
                                }
                            }
                        }
                    }

                    if (currentModePage == null)
                    {
                        pgMmc = new Modes.ModePage_01_MMC {
                            PS = false, ReadRetryCount = 0x20, Parameter = 0x00
                        };
                        currentModePage = new Modes.ModePage
                        {
                            Page         = 0x01,
                            Subpage      = 0x00,
                            PageResponse = Modes.EncodeModePage_01_MMC(pgMmc)
                        };
                    }

                    pgMmc = new Modes.ModePage_01_MMC {
                        PS = false, ReadRetryCount = 255, Parameter = 0x20
                    };
                    Modes.DecodedMode md = new Modes.DecodedMode
                    {
                        Header = new Modes.ModeHeader(),
                        Pages  = new[]
                        {
                            new Modes.ModePage
                            {
                                Page         = 0x01,
                                Subpage      = 0x00,
                                PageResponse = Modes.EncodeModePage_01_MMC(pgMmc)
                            }
                        }
                    };
                    md6  = Modes.EncodeMode6(md, dev.ScsiType);
                    md10 = Modes.EncodeMode10(md, dev.ScsiType);

                    dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks).");
                    sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out _);
                    if (sense)
                    {
                        sense = dev.ModeSelect10(md10, out senseBuf, true, false, dev.Timeout, out _);
                    }

                    if (sense)
                    {
                        DicConsole
                        .WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");
                        DicConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf));
                        dumpLog.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");
                    }
                    else
                    {
                        runningPersistent = true;
                    }
                }

repeatRetry:
                ulong[] tmpArray = resume.BadBlocks.ToArray();
                foreach (ulong badSector in tmpArray)
                {
                    if (aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        dumpLog.WriteLine("Aborted!");
                        break;
                    }

                    DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass,
                                     forward ? "forward" : "reverse",
                                     runningPersistent ? "recovering partial data, " : "");

                    sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)badSector,
                                       BLOCK_SIZE, 0, 1, false, dev.Timeout, out cmdDuration);
                    totalDuration += cmdDuration;

                    if (!sense && !dev.Error)
                    {
                        resume.BadBlocks.Remove(badSector);
                        extents.Add(badSector);
                        outputPlugin.WriteSector(readBuffer, badSector);
                        dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
                    }
                    else if (runningPersistent)
                    {
                        outputPlugin.WriteSector(readBuffer, badSector);
                    }
                }

                if (pass < retryPasses && !aborted && resume.BadBlocks.Count > 0)
                {
                    pass++;
                    forward = !forward;
                    resume.BadBlocks.Sort();
                    resume.BadBlocks.Reverse();
                    goto repeatRetry;
                }

                if (runningPersistent && currentModePage.HasValue)
                {
                    Modes.DecodedMode md = new Modes.DecodedMode
                    {
                        Header = new Modes.ModeHeader(),
                        Pages  = new[] { currentModePage.Value }
                    };
                    md6  = Modes.EncodeMode6(md, dev.ScsiType);
                    md10 = Modes.EncodeMode10(md, dev.ScsiType);

                    dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status).");
                    sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out _);
                    if (sense)
                    {
                        dev.ModeSelect10(md10, out senseBuf, true, false, dev.Timeout, out _);
                    }
                }

                DicConsole.WriteLine();
            }
            #endregion Error handling

            resume.BadBlocks.Sort();
            currentTry.Extents = ExtentsConverter.ToMetadata(extents);

            foreach (KeyValuePair <MediaTagType, byte[]> tag in mediaTags)
            {
                ret = outputPlugin.WriteMediaTag(tag.Value, tag.Key);
                if (ret || force)
                {
                    continue;
                }

                // Cannot write tag to image
                dumpLog.WriteLine($"Cannot write tag {tag.Key}.");
                throw new ArgumentException(outputPlugin.ErrorMessage);
            }

            resume.BadBlocks.Sort();
            foreach (ulong bad in resume.BadBlocks)
            {
                dumpLog.WriteLine("Sector {0} could not be read.", bad);
            }
            currentTry.Extents = ExtentsConverter.ToMetadata(extents);

            outputPlugin.SetDumpHardware(resume.Tries);
            if (preSidecar != null)
            {
                outputPlugin.SetCicmMetadata(preSidecar);
            }
            dumpLog.WriteLine("Closing output file.");
            DicConsole.WriteLine("Closing output file.");
            DateTime closeStart = DateTime.Now;
            outputPlugin.Close();
            DateTime closeEnd = DateTime.Now;
            dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);

            if (aborted)
            {
                dumpLog.WriteLine("Aborted!");
                return;
            }

            double totalChkDuration = 0;
            if (!nometadata)
            {
                dumpLog.WriteLine("Creating sidecar.");
                FiltersList filters     = new FiltersList();
                IFilter     filter      = filters.GetFilter(outputPath);
                IMediaImage inputPlugin = ImageFormat.Detect(filter);
                if (!inputPlugin.Open(filter))
                {
                    throw new ArgumentException("Could not open created image.");
                }

                DateTime         chkStart = DateTime.UtcNow;
                CICMMetadataType sidecar  = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding);
                end = DateTime.UtcNow;

                if (preSidecar != null)
                {
                    preSidecar.OpticalDisc = sidecar.OpticalDisc;
                    sidecar = preSidecar;
                }

                totalChkDuration = (end - chkStart).TotalMilliseconds;
                dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);
                dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
                                  (double)BLOCK_SIZE * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));

                foreach (KeyValuePair <MediaTagType, byte[]> tag in mediaTags)
                {
                    Mmc.AddMediaTagToSidecar(outputPath, tag, ref sidecar);
                }

                List <(ulong start, string type)> filesystems = new List <(ulong start, string type)>();
                if (sidecar.OpticalDisc[0].Track != null)
                {
                    filesystems.AddRange(from xmlTrack in sidecar.OpticalDisc[0].Track
                                         where xmlTrack.FileSystemInformation != null
                                         from partition in xmlTrack.FileSystemInformation
                                         where partition.FileSystems != null
                                         from fileSystem in partition.FileSystems
                                         select((ulong)partition.StartSector, fileSystem.Type));
                }

                if (filesystems.Count > 0)
                {
                    foreach (var filesystem in filesystems.Select(o => new { o.start, o.type }).Distinct())
                    {
                        dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start);
                    }
                }

                sidecar.OpticalDisc[0].Layers = new LayersType
                {
                    type          = LayersTypeType.OTP,
                    typeSpecified = true,
                    Sectors       = new SectorsType[1]
                };
                sidecar.OpticalDisc[0].Layers.Sectors[0] = new SectorsType {
                    Value = (long)layerBreak
                };
                sidecar.OpticalDisc[0].Sessions   = 1;
                sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType);
                CommonTypes.Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp,
                                                                 out string xmlDskSubTyp);
                sidecar.OpticalDisc[0].DiscType    = xmlDskTyp;
                sidecar.OpticalDisc[0].DiscSubType = xmlDskSubTyp;

                foreach (KeyValuePair <MediaTagType, byte[]> tag in mediaTags)
                {
                    if (outputPlugin.SupportedMediaTags.Contains(tag.Key))
                    {
                        Mmc.AddMediaTagToSidecar(outputPath, tag, ref sidecar);
                    }
                }

                DicConsole.WriteLine("Writing metadata sidecar");

                FileStream xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create);

                XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                xmlSer.Serialize(xmlFs, sidecar);
                xmlFs.Close();
            }

            DicConsole.WriteLine();
            DicConsole.WriteLine("Took a total of {0:F3} seconds ({1:F3} processing commands, {2:F3} checksumming, {3:F3} writing, {4:F3} closing).",
                                 (end - start).TotalSeconds, totalDuration / 1000,
                                 totalChkDuration / 1000,
                                 imageWriteDuration, (closeEnd - closeStart).TotalSeconds);
            DicConsole.WriteLine("Avegare speed: {0:F3} MiB/sec.",
                                 (double)BLOCK_SIZE * (double)(blocks + 1) / 1048576 / (totalDuration / 1000));
            DicConsole.WriteLine("Fastest speed burst: {0:F3} MiB/sec.", maxSpeed);
            DicConsole.WriteLine("Slowest speed burst: {0:F3} MiB/sec.", minSpeed);
            DicConsole.WriteLine("{0} sectors could not be read.", resume.BadBlocks.Count);
            DicConsole.WriteLine();

            Statistics.AddMedia(dskType, true);
        }
Exemplo n.º 24
0
 public bool SetCicmMetadata(CICMMetadataType metadata) => false;
Exemplo n.º 25
0
        void DumpMs()
        {
            const ushort SBC_PROFILE   = 0x0001;
            const uint   BLOCK_SIZE    = 512;
            double       totalDuration = 0;
            double       currentSpeed  = 0;
            double       maxSpeed      = double.MinValue;
            double       minSpeed      = double.MaxValue;
            uint         blocksToRead  = 64;
            DateTime     start;
            DateTime     end;
            MediaType    dskType;
            bool         sense;

            sense = _dev.ReadCapacity(out byte[] readBuffer, out _, _dev.Timeout, out _);

            if (sense)
            {
                _dumpLog.WriteLine("Could not detect capacity...");
                StoppingErrorMessage?.Invoke("Could not detect capacity...");

                return;
            }

            uint blocks = (uint)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]);

            blocks++;

            UpdateStatus?.
            Invoke($"Media has {blocks} blocks of {BLOCK_SIZE} bytes/each. (for a total of {blocks * (ulong)BLOCK_SIZE} bytes)");

            if (blocks == 0)
            {
                _dumpLog.WriteLine("ERROR: Unable to read medium or empty medium present...");
                StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present...");

                return;
            }

            UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * BLOCK_SIZE} bytes).");
            UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time.");
            UpdateStatus?.Invoke($"Device reports {BLOCK_SIZE} bytes per logical block.");
            UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}.");

            if (blocks > 262144)
            {
                dskType = MediaType.MemoryStickProDuo;
                _dumpLog.WriteLine("Media detected as MemoryStick Pro Duo...");
                UpdateStatus?.Invoke("Media detected as MemoryStick Pro Duo...");
            }
            else
            {
                dskType = MediaType.MemoryStickDuo;
                _dumpLog.WriteLine("Media detected as MemoryStick Duo...");
                UpdateStatus?.Invoke("Media detected as MemoryStick Duo...");
            }

            bool ret;

            var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, BLOCK_SIZE, blocksToRead, _private);
            var ibgLog  = new IbgLog(_outputPrefix + ".ibg", SBC_PROFILE);

            ret = _outputPlugin.Create(_outputPath, dskType, _formatOptions, blocks, BLOCK_SIZE);

            // Cannot create image
            if (!ret)
            {
                _dumpLog.WriteLine("Error creating output image, not continuing.");
                _dumpLog.WriteLine(_outputPlugin.ErrorMessage);

                StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine +
                                             _outputPlugin.ErrorMessage);

                return;
            }

            start = DateTime.UtcNow;
            double imageWriteDuration = 0;

            DumpHardwareType currentTry = null;
            ExtentsULong     extents    = null;

            ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial,
                                  _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision,
                                  _private);

            if (currentTry == null ||
                extents == null)
            {
                StoppingErrorMessage?.Invoke("Could not process resume file, not continuing...");

                return;
            }

            if (_resume.NextBlock > 0)
            {
                _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
            }

            bool newTrim = false;

            DateTime timeSpeedStart   = DateTime.UtcNow;
            ulong    sectorSpeedStart = 0;

            InitProgress?.Invoke();

            for (ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
            {
                if (_aborted)
                {
                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                    UpdateStatus?.Invoke("Aborted!");
                    _dumpLog.WriteLine("Aborted!");

                    break;
                }

                if (blocks - i < blocksToRead)
                {
                    blocksToRead = (uint)(blocks - i);
                }

                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                if (currentSpeed > maxSpeed &&
                    currentSpeed != 0)
                {
                    maxSpeed = currentSpeed;
                }

                if (currentSpeed < minSpeed &&
                    currentSpeed != 0)
                {
                    minSpeed = currentSpeed;
                }
                #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, blocks);

                sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, (uint)i, BLOCK_SIZE, 0,
                                    blocksToRead, false, _dev.Timeout, out double cmdDuration);

                totalDuration += cmdDuration;

                if (!sense &&
                    !_dev.Error)
                {
                    mhddLog.Write(i, cmdDuration);
                    ibgLog.Write(i, currentSpeed * 1024);
                    DateTime writeStart = DateTime.Now;
                    _outputPlugin.WriteSectors(readBuffer, i, blocksToRead);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    extents.Add(i, blocksToRead, true);
                }
                else
                {
                    // TODO: Reset device after X errors
                    if (_stopOnError)
                    {
                        return; // TODO: Return more cleanly
                    }
                    if (i + _skip > blocks)
                    {
                        _skip = (uint)(blocks - i);
                    }

                    // Write empty data
                    DateTime writeStart = DateTime.Now;
                    _outputPlugin.WriteSectors(new byte[BLOCK_SIZE * _skip], i, _skip);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;

                    for (ulong b = i; b < i + _skip; b++)
                    {
                        _resume.BadBlocks.Add(b);
                    }

                    mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);

                    ibgLog.Write(i, 0);
                    _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
                    i      += _skip - blocksToRead;
                    newTrim = true;
                }

                sectorSpeedStart += blocksToRead;
                _resume.NextBlock = i + blocksToRead;

                double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;

                if (elapsed < 1)
                {
                    continue;
                }

                currentSpeed     = (sectorSpeedStart * BLOCK_SIZE) / (1048576 * elapsed);
                sectorSpeedStart = 0;
                timeSpeedStart   = DateTime.UtcNow;
            }

            end = DateTime.UtcNow;
            EndProgress?.Invoke();
            mhddLog.Close();

            ibgLog.Close(_dev, blocks, BLOCK_SIZE, (end - start).TotalSeconds, currentSpeed * 1024,
                         (BLOCK_SIZE * (double)(blocks + 1)) / 1024 / (totalDuration / 1000),
                         _devicePath);

            UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");

            UpdateStatus?.
            Invoke($"Average dump speed {((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");

            UpdateStatus?.
            Invoke($"Average write speed {((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec.");

            _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);

            _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
                               ((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / (totalDuration / 1000));

            _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
                               ((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / imageWriteDuration);

            #region Trimming
            if (_resume.BadBlocks.Count > 0 &&
                !_aborted &&
                _trim &&
                newTrim)
            {
                start = DateTime.UtcNow;
                UpdateStatus?.Invoke("Trimming skipped sectors");
                _dumpLog.WriteLine("Trimming skipped sectors");

                ulong[] tmpArray = _resume.BadBlocks.ToArray();
                InitProgress?.Invoke();

                foreach (ulong badSector in tmpArray)
                {
                    if (_aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        UpdateStatus?.Invoke("Aborted!");
                        _dumpLog.WriteLine("Aborted!");

                        break;
                    }

                    PulseProgress?.Invoke($"Trimming sector {badSector}");

                    sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, (uint)badSector,
                                        BLOCK_SIZE, 0, 1, false, _dev.Timeout, out double cmdDuration);

                    if (sense || _dev.Error)
                    {
                        continue;
                    }

                    _resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                    _outputPlugin.WriteSector(readBuffer, badSector);
                }

                EndProgress?.Invoke();
                end = DateTime.UtcNow;
                _dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds);
            }
            #endregion Trimming

            #region Error handling
            if (_resume.BadBlocks.Count > 0 &&
                !_aborted &&
                _retryPasses > 0)
            {
                int  pass              = 1;
                bool forward           = true;
                bool runningPersistent = false;

                Modes.ModePage?currentModePage = null;
                byte[]         md6;

                if (_persistent)
                {
                    Modes.ModePage_01 pg;

                    sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01,
                                            _dev.Timeout, out _);

                    if (sense)
                    {
                        sense = _dev.ModeSense10(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01,
                                                 _dev.Timeout, out _);

                        if (!sense)
                        {
                            Modes.DecodedMode?dcMode10 = Modes.DecodeMode10(readBuffer, _dev.ScsiType);

                            if (dcMode10.HasValue)
                            {
                                foreach (Modes.ModePage modePage in dcMode10.Value.Pages)
                                {
                                    if (modePage.Page == 0x01 &&
                                        modePage.Subpage == 0x00)
                                    {
                                        currentModePage = modePage;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        Modes.DecodedMode?dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType);

                        if (dcMode6.HasValue)
                        {
                            foreach (Modes.ModePage modePage in dcMode6.Value.Pages)
                            {
                                if (modePage.Page == 0x01 &&
                                    modePage.Subpage == 0x00)
                                {
                                    currentModePage = modePage;
                                }
                            }
                        }
                    }

                    if (currentModePage == null)
                    {
                        pg = new Modes.ModePage_01
                        {
                            PS  = false, AWRE = true, ARRE = true, TB = false,
                            RC  = false, EER = true, PER = false, DTE = true,
                            DCR = false, ReadRetryCount = 32
                        };

                        currentModePage = new Modes.ModePage
                        {
                            Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg)
                        };
                    }

                    pg = new Modes.ModePage_01
                    {
                        PS  = false, AWRE = false, ARRE = false, TB = true,
                        RC  = false, EER = true, PER = false, DTE = false,
                        DCR = false, ReadRetryCount = 255
                    };

                    var md = new Modes.DecodedMode
                    {
                        Header = new Modes.ModeHeader(), Pages = new[]
                        {
                            new Modes.ModePage
                            {
                                Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg)
                            }
                        }
                    };

                    md6 = Modes.EncodeMode6(md, _dev.ScsiType);

                    UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks).");
                    _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks).");
                    sense = _dev.ModeSelect(md6, out byte[] senseBuf, true, false, _dev.Timeout, out _);

                    if (sense)
                    {
                        UpdateStatus?.
                        Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");

                        AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf));

                        _dumpLog.
                        WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");
                    }
                    else
                    {
                        runningPersistent = true;
                    }
                }

                InitProgress?.Invoke();
repeatRetry:
                ulong[] tmpArray = _resume.BadBlocks.ToArray();

                foreach (ulong badSector in tmpArray)
                {
                    if (_aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        _dumpLog.WriteLine("Aborted!");

                        break;
                    }

                    PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass,
                                                        forward ? "forward" : "reverse",
                                                        runningPersistent ? "recovering partial data, " : ""));

                    sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, (uint)badSector,
                                        BLOCK_SIZE, 0, 1, false, _dev.Timeout, out double cmdDuration);

                    totalDuration += cmdDuration;

                    if (!sense &&
                        !_dev.Error)
                    {
                        _resume.BadBlocks.Remove(badSector);
                        extents.Add(badSector);
                        _outputPlugin.WriteSector(readBuffer, badSector);
                        UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}.");
                        _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
                    }
                    else if (runningPersistent)
                    {
                        _outputPlugin.WriteSector(readBuffer, badSector);
                    }
                }

                if (pass < _retryPasses &&
                    !_aborted &&
                    _resume.BadBlocks.Count > 0)
                {
                    pass++;
                    forward = !forward;
                    _resume.BadBlocks.Sort();
                    _resume.BadBlocks.Reverse();

                    goto repeatRetry;
                }

                if (runningPersistent && currentModePage.HasValue)
                {
                    var md = new Modes.DecodedMode
                    {
                        Header = new Modes.ModeHeader(), Pages = new[]
                        {
                            currentModePage.Value
                        }
                    };

                    md6 = Modes.EncodeMode6(md, _dev.ScsiType);

                    UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status).");
                    _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status).");
                    sense = _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _);
                }

                EndProgress?.Invoke();
            }
            #endregion Error handling

            _resume.BadBlocks.Sort();

            foreach (ulong bad in _resume.BadBlocks)
            {
                _dumpLog.WriteLine("Sector {0} could not be read.", bad);
            }

            currentTry.Extents = ExtentsConverter.ToMetadata(extents);

            var metadata = new CommonTypes.Structs.ImageInfo
            {
                Application = "Aaru", ApplicationVersion = Version.GetVersion()
            };

            if (!_outputPlugin.SetMetadata(metadata))
            {
                ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine +
                                     _outputPlugin.ErrorMessage);
            }

            _outputPlugin.SetDumpHardware(_resume.Tries);

            if (_preSidecar != null)
            {
                _outputPlugin.SetCicmMetadata(_preSidecar);
            }

            _dumpLog.WriteLine("Closing output file.");
            UpdateStatus?.Invoke("Closing output file.");
            DateTime closeStart = DateTime.Now;
            _outputPlugin.Close();
            DateTime closeEnd = DateTime.Now;
            UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds.");
            _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);

            if (_aborted)
            {
                UpdateStatus?.Invoke("Aborted!");
                _dumpLog.WriteLine("Aborted!");

                return;
            }

            double totalChkDuration = 0;

            if (_metadata)
            {
                UpdateStatus?.Invoke("Creating sidecar.");
                _dumpLog.WriteLine("Creating sidecar.");
                var         filters     = new FiltersList();
                IFilter     filter      = filters.GetFilter(_outputPath);
                IMediaImage inputPlugin = ImageFormat.Detect(filter);

                if (!inputPlugin.Open(filter))
                {
                    StoppingErrorMessage?.Invoke("Could not open created image.");

                    return;
                }

                DateTime chkStart = DateTime.UtcNow;
                _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding);
                _sidecarClass.InitProgressEvent    += InitProgress;
                _sidecarClass.UpdateProgressEvent  += UpdateProgress;
                _sidecarClass.EndProgressEvent     += EndProgress;
                _sidecarClass.InitProgressEvent2   += InitProgress2;
                _sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
                _sidecarClass.EndProgressEvent2    += EndProgress2;
                _sidecarClass.UpdateStatusEvent    += UpdateStatus;
                CICMMetadataType sidecar = _sidecarClass.Create();
                end = DateTime.UtcNow;

                totalChkDuration = (end - chkStart).TotalMilliseconds;
                UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds.");

                UpdateStatus?.
                Invoke($"Average checksum speed {((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000):F3} KiB/sec.");

                _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);

                _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
                                   ((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000));

                if (_preSidecar != null)
                {
                    _preSidecar.BlockMedia = sidecar.BlockMedia;
                    sidecar = _preSidecar;
                }

                List <(ulong start, string type)> filesystems = new List <(ulong start, string type)>();

                if (sidecar.BlockMedia[0].FileSystemInformation != null)
                {
                    filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation
                                         where partition.FileSystems != null from fileSystem in partition.FileSystems
                                         select(partition.StartSector, fileSystem.Type));
                }

                if (filesystems.Count > 0)
                {
                    foreach (var filesystem in filesystems.Select(o => new
                    {
                        o.start, o.type
                    }).Distinct())
                    {
                        UpdateStatus?.Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}");
                        _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start);
                    }
                }

                sidecar.BlockMedia[0].Dimensions        = Dimensions.DimensionsFromMediaType(dskType);
                (string type, string subType)xmlType    = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType);
                sidecar.BlockMedia[0].DiskType          = xmlType.type;
                sidecar.BlockMedia[0].DiskSubType       = xmlType.subType;
                sidecar.BlockMedia[0].Interface         = "USB";
                sidecar.BlockMedia[0].LogicalBlocks     = blocks;
                sidecar.BlockMedia[0].PhysicalBlockSize = (int)BLOCK_SIZE;
                sidecar.BlockMedia[0].LogicalBlockSize  = (int)BLOCK_SIZE;
                sidecar.BlockMedia[0].Manufacturer      = _dev.Manufacturer;
                sidecar.BlockMedia[0].Model             = _dev.Model;

                if (!_private)
                {
                    sidecar.BlockMedia[0].Serial = _dev.Serial;
                }

                sidecar.BlockMedia[0].Size = blocks * BLOCK_SIZE;

                if (_dev.IsRemovable)
                {
                    sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray();
                }

                UpdateStatus?.Invoke("Writing metadata sidecar");

                var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);

                var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                xmlSer.Serialize(xmlFs, sidecar);
                xmlFs.Close();
            }

            UpdateStatus?.Invoke("");

            UpdateStatus?.
            Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");

            UpdateStatus?.
            Invoke($"Average speed: {((double)BLOCK_SIZE * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");

            UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
            UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
            UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");
            UpdateStatus?.Invoke("");

            Statistics.AddMedia(dskType, true);
        }