Ejemplo n.º 1
0
        /// <summary>Reads all CD user data</summary>
        /// <param name="audioExtents">Extents with audio sectors</param>
        /// <param name="blocks">Total number of positive sectors</param>
        /// <param name="blockSize">Size of the read sector in bytes</param>
        /// <param name="currentSpeed">Current read speed</param>
        /// <param name="currentTry">Current dump hardware try</param>
        /// <param name="extents">Extents</param>
        /// <param name="ibgLog">IMGBurn log</param>
        /// <param name="imageWriteDuration">Duration of image write</param>
        /// <param name="lastSector">Last sector number</param>
        /// <param name="leadOutExtents">Lead-out extents</param>
        /// <param name="maxSpeed">Maximum speed</param>
        /// <param name="mhddLog">MHDD log</param>
        /// <param name="minSpeed">Minimum speed</param>
        /// <param name="newTrim">Is trim a new one?</param>
        /// <param name="nextData">Next cluster of sectors is all data</param>
        /// <param name="offsetBytes">Read offset</param>
        /// <param name="read6">Device supports READ(6)</param>
        /// <param name="read10">Device supports READ(10)</param>
        /// <param name="read12">Device supports READ(12)</param>
        /// <param name="read16">Device supports READ(16)</param>
        /// <param name="readcd">Device supports READ CD</param>
        /// <param name="sectorsForOffset">Sectors needed to fix offset</param>
        /// <param name="subSize">Subchannel size in bytes</param>
        /// <param name="supportedSubchannel">Drive's maximum supported subchannel</param>
        /// <param name="supportsLongSectors">Supports reading EDC and ECC</param>
        /// <param name="totalDuration">Total commands duration</param>
        void ReadCdData(ExtentsULong audioExtents, ulong blocks, uint blockSize, ref double currentSpeed,
                        DumpHardwareType currentTry, ExtentsULong extents, IbgLog ibgLog, ref double imageWriteDuration,
                        long lastSector, ExtentsULong leadOutExtents, ref double maxSpeed, MhddLog mhddLog,
                        ref double minSpeed, out bool newTrim, bool nextData, int offsetBytes, bool read6, bool read10,
                        bool read12, bool read16, bool readcd, int sectorsForOffset, uint subSize,
                        MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration,
                        Track[] tracks)
        {
            ulong    sectorSpeedStart = 0;                 // Used to calculate correct speed
            DateTime timeSpeedStart   = DateTime.UtcNow;   // Time of start for speed calculation
            uint     blocksToRead     = 0;                 // How many sectors to read at once
            bool     sense            = true;              // Sense indicator

            byte[]     cmdBuf      = null;                 // Data buffer
            byte[]     senseBuf    = null;                 // Sense buffer
            double     cmdDuration = 0;                    // Command execution time
            const uint sectorSize  = 2352;                 // Full sector size

            byte[] tmpBuf;                                 // Temporary buffer
            newTrim = false;

            InitProgress?.Invoke();

            bool crossingLeadOut       = false;
            bool failedCrossingLeadOut = false;

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

                    break;
                }

                while (leadOutExtents.Contains(i))
                {
                    i++;
                }

                if ((long)i > lastSector)
                {
                    break;
                }

                uint firstSectorToRead = (uint)i;

                Track track = tracks.OrderBy(t => t.TrackStartSector).LastOrDefault(t => i >= t.TrackStartSector);

                blocksToRead = 0;
                bool inData = nextData;

                for (ulong j = i; j < i + _maximumReadable; j++)
                {
                    if (j > (ulong)lastSector)
                    {
                        if (!failedCrossingLeadOut)
                        {
                            blocksToRead += (uint)sectorsForOffset;
                        }

                        if (sectorsForOffset > 0)
                        {
                            crossingLeadOut = true;
                        }

                        break;
                    }

                    if (nextData)
                    {
                        if (audioExtents.Contains(j))
                        {
                            nextData = false;

                            break;
                        }

                        blocksToRead++;
                    }
                    else
                    {
                        if (!audioExtents.Contains(j))
                        {
                            nextData = true;

                            break;
                        }

                        blocksToRead++;
                    }
                }

                if (track.TrackSequence != 0 &&
                    (i + blocksToRead) - (ulong)sectorsForOffset > track.TrackEndSector + 1)
                {
                    blocksToRead = (uint)(((track.TrackEndSector + 1) - i) + (ulong)sectorsForOffset);
                }

                if (blocksToRead == 1)
                {
                    blocksToRead += (uint)sectorsForOffset;
                }

                if (_fixOffset && !inData)
                {
                    // TODO: FreeBSD bug
                    if (offsetBytes < 0)
                    {
                        if (i == 0)
                        {
                            firstSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1
                        }
                        else
                        {
                            firstSectorToRead -= (uint)sectorsForOffset;
                        }
                    }
                }

                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator

                // ReSharper disable CompareOfFloatsByEqualityOperator
                if (currentSpeed > maxSpeed &&
                    currentSpeed != 0)
                {
                    maxSpeed = currentSpeed;
                }

                if (currentSpeed < minSpeed &&
                    currentSpeed != 0)
                {
                    minSpeed = currentSpeed;
                }

                // ReSharper restore CompareOfFloatsByEqualityOperator

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

                if (readcd)
                {
                    sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead,
                                        MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
                                        true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);

                    totalDuration += cmdDuration;
                }
                else if (read16)
                {
                    sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, firstSectorToRead, blockSize,
                                        0, blocksToRead, false, _dev.Timeout, out cmdDuration);
                }
                else if (read12)
                {
                    sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead,
                                        blockSize, 0, blocksToRead, false, _dev.Timeout, out cmdDuration);
                }
                else if (read10)
                {
                    sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead,
                                        blockSize, 0, (ushort)blocksToRead, _dev.Timeout, out cmdDuration);
                }
                else if (read6)
                {
                    sense = _dev.Read6(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, (byte)blocksToRead,
                                       _dev.Timeout, out cmdDuration);
                }

                if (!sense &&
                    !_dev.Error)
                {
                    // Because one block has been partially used to fix the offset
                    if (_fixOffset &&
                        !inData &&
                        offsetBytes != 0)
                    {
                        int offsetFix = offsetBytes < 0 ? (int)(sectorSize - (offsetBytes * -1)) : offsetBytes;

                        if (supportedSubchannel != MmcSubchannel.None)
                        {
                            // De-interleave subchannel
                            byte[] data = new byte[sectorSize * blocksToRead];
                            byte[] sub  = new byte[subSize * blocksToRead];

                            for (int b = 0; b < blocksToRead; b++)
                            {
                                Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b,
                                           sectorSize);

                                Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
                            }

                            if (failedCrossingLeadOut)
                            {
                                blocksToRead += (uint)sectorsForOffset;

                                tmpBuf = new byte[sectorSize * blocksToRead];
                                Array.Copy(data, 0, tmpBuf, 0, data.Length);
                                data   = tmpBuf;
                                tmpBuf = new byte[subSize * blocksToRead];
                                Array.Copy(sub, 0, tmpBuf, 0, sub.Length);
                                sub = tmpBuf;
                            }

                            tmpBuf = new byte[sectorSize * (blocksToRead - sectorsForOffset)];
                            Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length);
                            data = tmpBuf;

                            blocksToRead -= (uint)sectorsForOffset;

                            // Re-interleave subchannel
                            cmdBuf = new byte[blockSize * blocksToRead];

                            for (int b = 0; b < blocksToRead; b++)
                            {
                                Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)),
                                           sectorSize);

                                Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize);
                            }
                        }
                        else
                        {
                            if (failedCrossingLeadOut)
                            {
                                blocksToRead += (uint)sectorsForOffset;

                                tmpBuf = new byte[blockSize * blocksToRead];
                                Array.Copy(cmdBuf, 0, tmpBuf, 0, cmdBuf.Length);
                                cmdBuf = tmpBuf;
                            }

                            tmpBuf = new byte[blockSize * (blocksToRead - sectorsForOffset)];
                            Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length);
                            cmdBuf        = tmpBuf;
                            blocksToRead -= (uint)sectorsForOffset;
                        }
                    }

                    mhddLog.Write(i, cmdDuration);
                    ibgLog.Write(i, currentSpeed * 1024);
                    extents.Add(i, blocksToRead, true);
                    DateTime writeStart = DateTime.Now;

                    if (supportedSubchannel != MmcSubchannel.None)
                    {
                        byte[] data = new byte[sectorSize * blocksToRead];
                        byte[] sub  = new byte[subSize * blocksToRead];

                        for (int b = 0; b < blocksToRead; b++)
                        {
                            Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);

                            Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
                        }

                        _outputPlugin.WriteSectorsLong(data, i, blocksToRead);
                        _outputPlugin.WriteSectorsTag(sub, i, blocksToRead, SectorTagType.CdSectorSubchannel);
                    }
                    else
                    {
                        if (supportsLongSectors)
                        {
                            _outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead);
                        }
                        else
                        {
                            if (cmdBuf.Length % sectorSize == 0)
                            {
                                byte[] data = new byte[2048 * blocksToRead];

                                for (int b = 0; b < blocksToRead; b++)
                                {
                                    Array.Copy(cmdBuf, (int)(16 + (b * blockSize)), data, 2048 * b, 2048);
                                }

                                _outputPlugin.WriteSectors(data, i, blocksToRead);
                            }
                            else
                            {
                                _outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead);
                            }
                        }
                    }

                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                }
                else
                {
                    if (crossingLeadOut && Sense.DecodeFixed(senseBuf)?.ASC == 0x21)
                    {
                        failedCrossingLeadOut = true;
                        blocksToRead          = 0;

                        continue;
                    }

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

                    if (supportedSubchannel != MmcSubchannel.None)
                    {
                        _outputPlugin.WriteSectorsLong(new byte[sectorSize * _skip], i, _skip);

                        _outputPlugin.WriteSectorsTag(new byte[subSize * _skip], i, _skip,
                                                      SectorTagType.CdSectorSubchannel);
                    }
                    else
                    {
                        if (supportsLongSectors)
                        {
                            _outputPlugin.WriteSectorsLong(new byte[blockSize * _skip], i, _skip);
                        }
                        else
                        {
                            if (cmdBuf.Length % sectorSize == 0)
                            {
                                _outputPlugin.WriteSectors(new byte[2048 * _skip], i, _skip);
                            }
                            else
                            {
                                _outputPlugin.WriteSectorsLong(new byte[blockSize * _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;
                    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;
            }

            EndProgress?.Invoke();

            if (!failedCrossingLeadOut)
            {
                return;
            }

            _dumpLog.WriteLine("Failed crossing into Lead-Out, dump may not be correct.");
            UpdateStatus?.Invoke("Failed crossing into Lead-Out, dump may not be correct.");
        }
Ejemplo n.º 2
0
        /// <summary>Dumps an ATA device</summary>
        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 ataProfile         = 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 AtaErrorRegistersChs errorChs);

            if (sense)
            {
                _errorLog?.WriteLine("ATA IDENTIFY DEVICE", _dev.Error, _dev.LastError, errorChs);
            }
            else if (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;

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

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

                    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", ataProfile);

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

                            if (currentSpeed > maxSpeed &&
                                currentSpeed > 0)
                            {
                                maxSpeed = currentSpeed;
                            }

                            if (currentSpeed < minSpeed &&
                                currentSpeed > 0)
                            {
                                minSpeed = currentSpeed;
                            }

                            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($"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;

                            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", ataProfile);

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

                                    if (currentSpeed > maxSpeed &&
                                        currentSpeed > 0)
                                    {
                                        maxSpeed = currentSpeed;
                                    }

                                    if (currentSpeed < minSpeed &&
                                        currentSpeed > 0)
                                    {
                                        minSpeed = currentSpeed;
                                    }

                                    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 manufacturerId =
                                            CIS.DecodeManufacturerIdentificationTuple(tuple);

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

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

                                        break;

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

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

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

                                            sidecar.BlockMedia[0].PCMCIA.AdditionalInformation =
                                                version.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.");
            }
        }
    }
}
Ejemplo n.º 3
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();

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

            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);
        }
Ejemplo n.º 4
0
        void RetryTitleKeys(DVDDecryption dvdDecrypt, byte[] discKey, ref double totalDuration)
        {
            int  pass    = 1;
            bool forward = true;
            bool sense;

            byte[] buffer;

            InitProgress?.Invoke();

repeatRetry:
            ulong[] tmpArray = _resume.MissingTitleKeys.ToArray();

            foreach (ulong missingKey in tmpArray)
            {
                if (_aborted)
                {
                    UpdateStatus?.Invoke("Aborted!");
                    _dumpLog.WriteLine("Aborted!");

                    break;
                }

                PulseProgress?.Invoke(string.Format("Retrying title key {0}, pass {1}, {2}", missingKey, pass,
                                                    forward ? "forward" : "reverse"));

                sense = dvdDecrypt.ReadTitleKey(out buffer, out _, DvdCssKeyClass.DvdCssCppmOrCprm, missingKey,
                                                _dev.Timeout, out double cmdDuration);

                totalDuration += cmdDuration;

                if (!sense &&
                    !_dev.Error)
                {
                    CSS_CPRM.TitleKey?titleKey = CSS.DecodeTitleKey(buffer, dvdDecrypt.BusKey);

                    if (titleKey.HasValue)
                    {
                        _outputPlugin.WriteSectorTag(new[]
                        {
                            titleKey.Value.CMI
                        }, missingKey, SectorTagType.DvdCmi);

                        // If the CMI bit is 1, the sector is using copy protection, else it is not
                        // If the decoded title key is zeroed, there should be no copy protection
                        if ((titleKey.Value.CMI & 0x80) >> 7 == 0 ||
                            titleKey.Value.Key.All(k => k == 0))
                        {
                            _outputPlugin.WriteSectorTag(new byte[]
                            {
                                0, 0, 0, 0, 0
                            }, missingKey, SectorTagType.DvdTitleKey);

                            _outputPlugin.WriteSectorTag(new byte[]
                            {
                                0, 0, 0, 0, 0
                            }, missingKey, SectorTagType.DvdTitleKeyDecrypted);

                            _resume.MissingTitleKeys.Remove(missingKey);
                            UpdateStatus?.Invoke($"Correctly retried title key {missingKey} in pass {pass}.");
                            _dumpLog.WriteLine("Correctly retried title key {0} in pass {1}.", missingKey, pass);
                        }
                        else
                        {
                            _outputPlugin.WriteSectorTag(titleKey.Value.Key, missingKey, SectorTagType.DvdTitleKey);
                            _resume.MissingTitleKeys.Remove(missingKey);

                            if (discKey != null)
                            {
                                CSS.DecryptTitleKey(0, discKey, titleKey.Value.Key, out buffer);
                                _outputPlugin.WriteSectorTag(buffer, missingKey, SectorTagType.DvdTitleKeyDecrypted);
                            }

                            UpdateStatus?.Invoke($"Correctly retried title key {missingKey} in pass {pass}.");
                            _dumpLog.WriteLine("Correctly retried title key {0} in pass {1}.", missingKey, pass);
                        }
                    }
                }
            }

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

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

                goto repeatRetry;
            }

            EndProgress?.Invoke();
        }
Ejemplo n.º 5
0
        /// <summary>Scans the media from an ATA device</summary>
        /// <returns>Scanning results</returns>
        ScanResults Ata()
        {
            var  results = new ScanResults();
            bool sense;

            results.Blocks = 0;
            const ushort ATA_PROFILE = 0x0001;
            const uint   TIMEOUT     = 5;

            sense = dev.AtaIdentify(out byte[] cmdBuf, out _);

            if (!sense &&
                Identify.Decode(cmdBuf).HasValue)
            {
                // Initializate reader
                var ataReader = new Reader(dev, TIMEOUT, cmdBuf);

                // Fill reader blocks
                results.Blocks = ataReader.GetDeviceBlocks();

                if (ataReader.FindReadCommand())
                {
                    StoppingErrorMessage?.Invoke(ataReader.ErrorMessage);

                    return(results);
                }

                // Check block sizes
                if (ataReader.GetBlockSize())
                {
                    StoppingErrorMessage?.Invoke(ataReader.ErrorMessage);

                    return(results);
                }

                uint blockSize = ataReader.LogicalBlockSize;

                // Check how many blocks to read, if error show and return
                if (ataReader.GetBlocksToRead())
                {
                    StoppingErrorMessage?.Invoke(ataReader.ErrorMessage);

                    return(results);
                }

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

                results.A       = 0; // <3ms
                results.B       = 0; // >=3ms, <10ms
                results.C       = 0; // >=10ms, <50ms
                results.D       = 0; // >=50ms, <150ms
                results.E       = 0; // >=150ms, <500ms
                results.F       = 0; // >=500ms
                results.Errored = 0;
                DateTime start;
                DateTime end;
                results.ProcessingTime = 0;
                double currentSpeed = 0;
                results.MaxSpeed          = double.MinValue;
                results.MinSpeed          = double.MaxValue;
                results.UnreadableSectors = new List <ulong>();
                results.SeekMax           = double.MinValue;
                results.SeekMin           = double.MaxValue;
                results.SeekTotal         = 0;
                const int SEEK_TIMES = 1000;

                double seekCur;

                var rnd = new Random();

                MhddLog mhddLog;
                IbgLog  ibgLog;
                double  duration;

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

                    InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, ATA_PROFILE);
                    mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                    ibgLog  = new IbgLog(ibgLogPath, ATA_PROFILE);

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

                    for (ulong i = 0; i < results.Blocks; i += blocksToRead)
                    {
                        if (aborted)
                        {
                            break;
                        }

                        if (results.Blocks - i < blocksToRead)
                        {
                            blocksToRead = (byte)(results.Blocks - i);
                        }

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

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

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

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

                        if (!error)
                        {
                            if (duration >= 500)
                            {
                                results.F += blocksToRead;
                            }
                            else if (duration >= 150)
                            {
                                results.E += blocksToRead;
                            }
                            else if (duration >= 50)
                            {
                                results.D += blocksToRead;
                            }
                            else if (duration >= 10)
                            {
                                results.C += blocksToRead;
                            }
                            else if (duration >= 3)
                            {
                                results.B += blocksToRead;
                            }
                            else
                            {
                                results.A += blocksToRead;
                            }

                            ScanTime?.Invoke(i, duration);
                            mhddLog.Write(i, duration);
                            ibgLog.Write(i, currentSpeed * 1024);
                        }
                        else
                        {
                            ScanUnreadable?.Invoke(i);
                            results.Errored += blocksToRead;

                            for (ulong b = i; b < i + blocksToRead; b++)
                            {
                                results.UnreadableSectors.Add(b);
                            }

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

                            ibgLog.Write(i, 0);
                        }

                        sectorSpeedStart += blocksToRead;

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

                        if (elapsed < 1)
                        {
                            continue;
                        }

                        currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                        ScanSpeed?.Invoke(i, currentSpeed * 1024);
                        sectorSpeedStart = 0;
                        timeSpeedStart   = DateTime.UtcNow;
                    }

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

                    ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                 (blockSize * (double)(results.Blocks + 1)) / 1024 /
                                 (results.ProcessingTime / 1000),
                                 devicePath);

                    InitProgress?.Invoke();

                    if (ataReader.CanSeekLba)
                    {
                        for (int i = 0; i < SEEK_TIMES; i++)
                        {
                            if (aborted)
                            {
                                break;
                            }

                            uint seekPos = (uint)rnd.Next((int)results.Blocks);

                            PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t");

                            ataReader.Seek(seekPos, out seekCur);

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

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

                            results.SeekTotal += seekCur;
                            GC.Collect();
                        }
                    }

                    EndProgress?.Invoke();
                }
                else
                {
                    InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, ATA_PROFILE);
                    mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                    ibgLog  = new IbgLog(ibgLogPath, ATA_PROFILE);

                    ulong currentBlock = 0;
                    results.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)
                                {
                                    break;
                                }

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

                                if (currentSpeed < results.MinSpeed &&
                                    currentSpeed != 0)
                                {
                                    results.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);

                                if (!error)
                                {
                                    if (duration >= 500)
                                    {
                                        results.F += blocksToRead;
                                    }
                                    else if (duration >= 150)
                                    {
                                        results.E += blocksToRead;
                                    }
                                    else if (duration >= 50)
                                    {
                                        results.D += blocksToRead;
                                    }
                                    else if (duration >= 10)
                                    {
                                        results.C += blocksToRead;
                                    }
                                    else if (duration >= 3)
                                    {
                                        results.B += blocksToRead;
                                    }
                                    else
                                    {
                                        results.A += blocksToRead;
                                    }

                                    ScanTime?.Invoke(currentBlock, duration);
                                    mhddLog.Write(currentBlock, duration);
                                    ibgLog.Write(currentBlock, currentSpeed * 1024);
                                }
                                else
                                {
                                    ScanUnreadable?.Invoke(currentBlock);
                                    results.Errored += blocksToRead;
                                    results.UnreadableSectors.Add(currentBlock);
                                    mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration);

                                    ibgLog.Write(currentBlock, 0);
                                }

                                sectorSpeedStart++;
                                currentBlock++;

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

                                if (elapsed < 1)
                                {
                                    continue;
                                }

                                currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                                ScanSpeed?.Invoke(currentBlock, currentSpeed * 1024);
                                sectorSpeedStart = 0;
                                timeSpeedStart   = DateTime.UtcNow;
                            }
                        }
                    }

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

                    ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                 (blockSize * (double)(results.Blocks + 1)) / 1024 /
                                 (results.ProcessingTime / 1000),
                                 devicePath);

                    InitProgress?.Invoke();

                    if (ataReader.CanSeek)
                    {
                        for (int i = 0; i < SEEK_TIMES; i++)
                        {
                            if (aborted)
                            {
                                break;
                            }

                            ushort seekCy = (ushort)rnd.Next(cylinders);
                            byte   seekHd = (byte)rnd.Next(heads);
                            byte   seekSc = (byte)rnd.Next(sectors);

                            PulseProgress?.
                            Invoke($"\rSeeking to cylinder {seekCy}, head {seekHd}, sector {seekSc}...\t\t");

                            ataReader.SeekChs(seekCy, seekHd, seekSc, out seekCur);

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

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

                            results.SeekTotal += seekCur;
                            GC.Collect();
                        }
                    }

                    EndProgress?.Invoke();
                }

                results.ProcessingTime /= 1000;
                results.TotalTime       = (end - start).TotalSeconds;
                results.AvgSpeed        = (blockSize * (double)(results.Blocks + 1)) / 1048576 / results.ProcessingTime;
                results.SeekTimes       = SEEK_TIMES;

                return(results);
            }

            StoppingErrorMessage?.Invoke("Unable to communicate with ATA device.");

            return(results);
        }
Ejemplo n.º 6
0
        ScanResults Scsi()
        {
            var     results = new ScanResults();
            MhddLog mhddLog;
            IbgLog  ibgLog;

            byte[] senseBuf;
            bool   sense = false;

            results.Blocks = 0;
            uint   blockSize      = 0;
            ushort currentProfile = 0x0001;

            if (_dev.IsRemovable)
            {
                sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _);

                if (sense)
                {
                    InitProgress?.Invoke();
                    FixedSense?decSense = Sense.DecodeFixed(senseBuf);

                    if (decSense.HasValue)
                    {
                        if (decSense.Value.ASC == 0x3A)
                        {
                            int leftRetries = 5;

                            while (leftRetries > 0)
                            {
                                PulseProgress?.Invoke("Waiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _);

                                if (!sense)
                                {
                                    break;
                                }

                                leftRetries--;
                            }

                            if (sense)
                            {
                                StoppingErrorMessage?.Invoke("Please insert media in drive");

                                return(results);
                            }
                        }
                        else if (decSense.Value.ASC == 0x04 &&
                                 decSense.Value.ASCQ == 0x01)
                        {
                            int leftRetries = 10;

                            while (leftRetries > 0)
                            {
                                PulseProgress?.Invoke("Waiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _);

                                if (!sense)
                                {
                                    break;
                                }

                                leftRetries--;
                            }

                            if (sense)
                            {
                                StoppingErrorMessage?.
                                Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");

                                return(results);
                            }
                        }

                        // 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)
                            {
                                PulseProgress?.Invoke("Waiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _);

                                if (!sense)
                                {
                                    break;
                                }

                                leftRetries--;
                            }

                            if (sense)
                            {
                                StoppingErrorMessage?.
                                Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");

                                return(results);
                            }
                        }
                        else
                        {
                            StoppingErrorMessage?.
                            Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");

                            return(results);
                        }
                    }
                    else
                    {
                        StoppingErrorMessage?.Invoke("Unknown testing unit was ready.");

                        return(results);
                    }

                    EndProgress?.Invoke();
                }
            }

            Reader scsiReader = null;

            switch (_dev.ScsiType)
            {
            case PeripheralDeviceTypes.DirectAccess:
            case PeripheralDeviceTypes.MultiMediaDevice:
            case PeripheralDeviceTypes.OCRWDevice:
            case PeripheralDeviceTypes.OpticalDevice:
            case PeripheralDeviceTypes.SimplifiedDevice:
            case PeripheralDeviceTypes.WriteOnceDevice:
                scsiReader     = new Reader(_dev, _dev.Timeout, null, null);
                results.Blocks = scsiReader.GetDeviceBlocks();

                if (scsiReader.FindReadCommand())
                {
                    StoppingErrorMessage?.Invoke("Unable to read medium.");

                    return(results);
                }

                blockSize = scsiReader.LogicalBlockSize;

                if (results.Blocks != 0 &&
                    blockSize != 0)
                {
                    results.Blocks++;

                    ulong totalSize = results.Blocks * blockSize;

                    if (totalSize > 1099511627776)
                    {
                        UpdateStatus?.
                        Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)");
                    }
                    else if (totalSize > 1073741824)
                    {
                        UpdateStatus?.
                        Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)");
                    }
                    else if (totalSize > 1048576)
                    {
                        UpdateStatus?.
                        Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)");
                    }
                    else if (totalSize > 1024)
                    {
                        UpdateStatus?.
                        Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)");
                    }
                    else
                    {
                        UpdateStatus?.
                        Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)");
                    }
                }

                break;

            case PeripheralDeviceTypes.SequentialAccess:
                StoppingErrorMessage?.Invoke("Scanning will never be supported on SCSI Streaming Devices." +
                                             Environment.NewLine +
                                             "It has no sense to do it, and it will put too much strain on the tape.");

                return(results);
            }

            if (results.Blocks == 0)
            {
                StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present...");

                return(results);
            }

            bool compactDisc = true;

            FullTOC.CDFullTOC?toc = null;

            if (_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice)
            {
                sense = _dev.GetConfiguration(out byte[] cmdBuf, out senseBuf, 0, MmcGetConfigurationRt.Current,
                                              _dev.Timeout, out _);

                if (!sense)
                {
                    Features.SeparatedFeatures ftr = Features.Separate(cmdBuf);

                    currentProfile = ftr.CurrentProfile;

                    switch (ftr.CurrentProfile)
                    {
                    case 0x0005:
                    case 0x0008:
                    case 0x0009:
                    case 0x000A:
                    case 0x0020:
                    case 0x0021:
                    case 0x0022: break;

                    default:
                        compactDisc = false;

                        break;
                    }
                }

                if (compactDisc)
                {
                    currentProfile = 0x0008;

                    // We discarded all discs that falsify a TOC before requesting a real TOC
                    // No TOC, no CD (or an empty one)
                    bool tocSense = _dev.ReadRawToc(out cmdBuf, out senseBuf, 1, _dev.Timeout, out _);

                    if (!tocSense)
                    {
                        toc = FullTOC.Decode(cmdBuf);
                    }
                }
            }
            else
            {
                compactDisc = false;
            }

            uint blocksToRead = 64;

            results.A       = 0; // <3ms
            results.B       = 0; // >=3ms, <10ms
            results.C       = 0; // >=10ms, <50ms
            results.D       = 0; // >=50ms, <150ms
            results.E       = 0; // >=150ms, <500ms
            results.F       = 0; // >=500ms
            results.Errored = 0;
            DateTime start;
            DateTime end;

            results.ProcessingTime = 0;
            results.TotalTime      = 0;
            double currentSpeed = 0;

            results.MaxSpeed          = double.MinValue;
            results.MinSpeed          = double.MaxValue;
            results.UnreadableSectors = new List <ulong>();

            if (compactDisc)
            {
                if (toc == null)
                {
                    StoppingErrorMessage?.Invoke("Error trying to decode TOC...");

                    return(results);
                }

                bool readcd = !_dev.ReadCd(out _, out senseBuf, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true,
                                           MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
                                           MmcSubchannel.None, _dev.Timeout, out _);

                if (readcd)
                {
                    UpdateStatus?.Invoke("Using MMC READ CD command.");
                }

                start = DateTime.UtcNow;

                while (true)
                {
                    if (readcd)
                    {
                        sense = _dev.ReadCd(out _, out senseBuf, 0, 2352, blocksToRead, MmcSectorTypes.AllTypes, false,
                                            false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
                                            MmcSubchannel.None, _dev.Timeout, out _);

                        if (_dev.Error)
                        {
                            blocksToRead /= 2;
                        }
                    }

                    if (!_dev.Error ||
                        blocksToRead == 1)
                    {
                        break;
                    }
                }

                if (_dev.Error)
                {
                    StoppingErrorMessage?.
                    Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length.");

                    return(results);
                }

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

                InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, currentProfile);
                mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false);
                ibgLog  = new IbgLog(_ibgLogPath, currentProfile);
                DateTime timeSpeedStart   = DateTime.UtcNow;
                ulong    sectorSpeedStart = 0;

                InitProgress?.Invoke();

                for (ulong i = 0; i < results.Blocks; i += blocksToRead)
                {
                    if (_aborted)
                    {
                        break;
                    }

                    double cmdDuration = 0;

                    if (results.Blocks - i < blocksToRead)
                    {
                        blocksToRead = (uint)(results.Blocks - i);
                    }

                    if (currentSpeed > results.MaxSpeed &&
                        currentSpeed > 0)
                    {
                        results.MaxSpeed = currentSpeed;
                    }

                    if (currentSpeed < results.MinSpeed &&
                        currentSpeed > 0)
                    {
                        results.MinSpeed = currentSpeed;
                    }

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

                    if (readcd)
                    {
                        sense = _dev.ReadCd(out _, out senseBuf, (uint)i, 2352, blocksToRead, MmcSectorTypes.AllTypes,
                                            false, false, true, MmcHeaderCodes.AllHeaders, true, true,
                                            MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, out cmdDuration);

                        results.ProcessingTime += cmdDuration;
                    }

                    if (!sense)
                    {
                        if (cmdDuration >= 500)
                        {
                            results.F += blocksToRead;
                        }
                        else if (cmdDuration >= 150)
                        {
                            results.E += blocksToRead;
                        }
                        else if (cmdDuration >= 50)
                        {
                            results.D += blocksToRead;
                        }
                        else if (cmdDuration >= 10)
                        {
                            results.C += blocksToRead;
                        }
                        else if (cmdDuration >= 3)
                        {
                            results.B += blocksToRead;
                        }
                        else
                        {
                            results.A += blocksToRead;
                        }

                        ScanTime?.Invoke(i, cmdDuration);
                        mhddLog.Write(i, cmdDuration);
                        ibgLog.Write(i, currentSpeed * 1024);
                    }
                    else
                    {
                        AaruConsole.DebugWriteLine("Media-Scan", "READ CD error:\n{0}", Sense.PrettifySense(senseBuf));

                        FixedSense?senseDecoded = Sense.DecodeFixed(senseBuf);

                        if (senseDecoded.HasValue)
                        {
                            // TODO: This error happens when changing from track type afaik. Need to solve that more cleanly
                            // LOGICAL BLOCK ADDRESS OUT OF RANGE
                            if ((senseDecoded.Value.ASC != 0x21 || senseDecoded.Value.ASCQ != 0x00) &&

                                // ILLEGAL MODE FOR THIS TRACK (requesting sectors as-is, this is a firmware misconception when audio sectors
                                // are in a track where subchannel indicates data)
                                (senseDecoded.Value.ASC != 0x64 || senseDecoded.Value.ASCQ != 0x00))
                            {
                                results.Errored += blocksToRead;

                                for (ulong b = i; b < i + blocksToRead; b++)
                                {
                                    results.UnreadableSectors.Add(b);
                                }

                                ScanUnreadable?.Invoke(i);
                                mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);

                                ibgLog.Write(i, 0);
                            }
                        }
                        else
                        {
                            ScanUnreadable?.Invoke(i);
                            results.Errored += blocksToRead;

                            for (ulong b = i; b < i + blocksToRead; b++)
                            {
                                results.UnreadableSectors.Add(b);
                            }

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

                            ibgLog.Write(i, 0);
                        }
                    }

                    sectorSpeedStart += blocksToRead;

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

                    if (elapsed < 1)
                    {
                        continue;
                    }

                    currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                    ScanSpeed?.Invoke(i, currentSpeed * 1024);
                    sectorSpeedStart = 0;
                    timeSpeedStart   = DateTime.UtcNow;
                }

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

                ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                             (blockSize * (double)(results.Blocks + 1)) / 1024 / (results.ProcessingTime / 1000),
                             _devicePath);
            }
            else
            {
                start = DateTime.UtcNow;

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

                InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, currentProfile);
                mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false);
                ibgLog  = new IbgLog(_ibgLogPath, currentProfile);
                DateTime timeSpeedStart   = DateTime.UtcNow;
                ulong    sectorSpeedStart = 0;

                InitProgress?.Invoke();

                for (ulong i = 0; i < results.Blocks; i += blocksToRead)
                {
                    if (_aborted)
                    {
                        break;
                    }

                    if (results.Blocks - i < blocksToRead)
                    {
                        blocksToRead = (uint)(results.Blocks - i);
                    }

                    if (currentSpeed > results.MaxSpeed &&
                        currentSpeed > 0)
                    {
                        results.MaxSpeed = currentSpeed;
                    }

                    if (currentSpeed < results.MinSpeed &&
                        currentSpeed > 0)
                    {
                        results.MinSpeed = currentSpeed;
                    }

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

                    sense = scsiReader.ReadBlocks(out _, i, blocksToRead, out double cmdDuration);
                    results.ProcessingTime += cmdDuration;

                    if (!sense &&
                        !_dev.Error)
                    {
                        if (cmdDuration >= 500)
                        {
                            results.F += blocksToRead;
                        }
                        else if (cmdDuration >= 150)
                        {
                            results.E += blocksToRead;
                        }
                        else if (cmdDuration >= 50)
                        {
                            results.D += blocksToRead;
                        }
                        else if (cmdDuration >= 10)
                        {
                            results.C += blocksToRead;
                        }
                        else if (cmdDuration >= 3)
                        {
                            results.B += blocksToRead;
                        }
                        else
                        {
                            results.A += blocksToRead;
                        }

                        ScanTime?.Invoke(i, cmdDuration);
                        mhddLog.Write(i, cmdDuration);
                        ibgLog.Write(i, currentSpeed * 1024);
                    }

                    // TODO: Separate errors on kind of errors.
                    else
                    {
                        ScanUnreadable?.Invoke(i);
                        results.Errored += blocksToRead;

                        for (ulong b = i; b < i + blocksToRead; b++)
                        {
                            results.UnreadableSectors.Add(b);
                        }

                        mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
                        ibgLog.Write(i, 0);
                    }

                    sectorSpeedStart += blocksToRead;

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

                    if (elapsed < 1)
                    {
                        continue;
                    }

                    currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                    ScanSpeed?.Invoke(i, currentSpeed * 1024);
                    sectorSpeedStart = 0;
                    timeSpeedStart   = DateTime.UtcNow;
                }

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

                ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                             (blockSize * (double)(results.Blocks + 1)) / 1024 / (results.ProcessingTime / 1000),
                             _devicePath);
            }

            results.SeekMax   = double.MinValue;
            results.SeekMin   = double.MaxValue;
            results.SeekTotal = 0;
            const int seekTimes = 1000;

            var rnd = new Random();

            InitProgress?.Invoke();

            for (int i = 0; i < seekTimes; i++)
            {
                if (_aborted || !_seekTest)
                {
                    break;
                }

                uint seekPos = (uint)rnd.Next((int)results.Blocks);

                PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t");

                double seekCur;

                if (scsiReader.CanSeek)
                {
                    scsiReader.Seek(seekPos, out seekCur);
                }
                else
                {
                    scsiReader.ReadBlock(out _, seekPos, out seekCur);
                }

                if (seekCur > results.SeekMax &&
                    seekCur > 0)
                {
                    results.SeekMax = seekCur;
                }

                if (seekCur < results.SeekMin &&
                    seekCur > 0)
                {
                    results.SeekMin = seekCur;
                }

                results.SeekTotal += seekCur;
                GC.Collect();
            }

            EndProgress?.Invoke();

            results.ProcessingTime /= 1000;
            results.TotalTime       = (end - start).TotalSeconds;
            results.AvgSpeed        = (blockSize * (double)(results.Blocks + 1)) / 1048576 / results.ProcessingTime;
            results.SeekTimes       = seekTimes;

            return(results);
        }
Ejemplo n.º 7
0
Archivo: SCSI.cs Proyecto: paulyc/Aaru
        // TODO: Get cartridge serial number from Certance vendor EVPD
        /// <summary>Dumps a SCSI Block Commands device or a Reduced Block Commands devices</summary>
        void Scsi()
        {
            int resets = 0;

            if(_dev.IsRemovable)
            {
                InitProgress?.Invoke();
                deviceGotReset:
                bool sense = _dev.ScsiTestUnitReady(out byte[] senseBuf, _dev.Timeout, out _);

                if(sense)
                {
                    FixedSense? decSense = Sense.DecodeFixed(senseBuf);

                    if(decSense.HasValue)
                    {
                        ErrorMessage?.
                            Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");

                        _dumpLog.WriteLine("Device not ready. Sense {0} 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)
                            {
                                PulseProgress?.Invoke("Waiting 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)
                                {
                                    ErrorMessage?.
                                        Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");

                                    _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h",
                                                       decSense.Value.SenseKey, decSense.Value.ASC,
                                                       decSense.Value.ASCQ);
                                }

                                leftRetries--;
                            }

                            if(sense)
                            {
                                StoppingErrorMessage?.Invoke("Please insert media in drive");

                                return;
                            }
                        }
                        else if(decSense.Value.ASC  == 0x04 &&
                                decSense.Value.ASCQ == 0x01)
                        {
                            int leftRetries = 50;

                            while(leftRetries > 0)
                            {
                                PulseProgress?.Invoke("Waiting 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)
                                {
                                    ErrorMessage?.
                                        Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");

                                    _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)
                            {
                                StoppingErrorMessage?.
                                    Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");

                                return;
                            }
                        }
                        /*else if (decSense.Value.ASC == 0x29 && decSense.Value.ASCQ == 0x00)
                        {
                            if (!deviceReset)
                            {
                                deviceReset = true;
                                ErrorMessage?.Invoke("Device did reset, retrying...");
                                goto retryTestReady;
                            }

                            StoppingErrorMessage?.Invoke(string.Format("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)
                            {
                                PulseProgress?.Invoke("Waiting 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)
                                {
                                    ErrorMessage?.
                                        Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");

                                    _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)
                            {
                                StoppingErrorMessage?.
                                    Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");

                                return;
                            }
                        }
                        else
                        {
                            StoppingErrorMessage?.
                                Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");

                            return;
                        }
                    }
                    else
                    {
                        StoppingErrorMessage?.Invoke("Unknown testing unit was ready.");

                        return;
                    }
                }

                EndProgress?.Invoke();
            }

            switch(_dev.ScsiType)
            {
                case PeripheralDeviceTypes.SequentialAccess:
                    if(_dumpRaw)
                    {
                        StoppingErrorMessage?.Invoke("Tapes cannot be dumped raw.");

                        return;
                    }

                    if(_outputPlugin is IWritableTapeImage)
                        Ssc();
                    else
                        StoppingErrorMessage?.
                            Invoke("The specified plugin does not support storing streaming tape images.");

                    return;
                case PeripheralDeviceTypes.MultiMediaDevice:
                    if(_outputPlugin is IWritableOpticalImage)
                        Mmc();
                    else
                        StoppingErrorMessage?.
                            Invoke("The specified plugin does not support storing optical disc images.");

                    return;
                case PeripheralDeviceTypes.BridgingExpander
                    when _dev.Model.StartsWith("MDM", StringComparison.InvariantCulture) ||
                         _dev.Model.StartsWith("MDH", StringComparison.InvariantCulture):
                    MiniDisc();

                    break;
                default:
                    Sbc(null, MediaType.Unknown, false);

                    break;
            }
        }
Ejemplo n.º 8
0
        /// <summary>Dumps inter-session lead-outs</summary>
        /// <param name="blockSize">Size of the read sector in bytes</param>
        /// <param name="currentSpeed">Current read speed</param>
        /// <param name="currentTry">Current dump hardware try</param>
        /// <param name="extents">Extents</param>
        /// <param name="ibgLog">IMGBurn log</param>
        /// <param name="imageWriteDuration">Duration of image write</param>
        /// <param name="leadOutExtents">Lead-out extents</param>
        /// <param name="maxSpeed">Maximum speed</param>
        /// <param name="mhddLog">MHDD log</param>
        /// <param name="minSpeed">Minimum speed</param>
        /// <param name="read6">Device supports READ(6)</param>
        /// <param name="read10">Device supports READ(10)</param>
        /// <param name="read12">Device supports READ(12)</param>
        /// <param name="read16">Device supports READ(16)</param>
        /// <param name="readcd">Device supports READ CD</param>
        /// <param name="supportedSubchannel">Drive's maximum supported subchannel</param>
        /// <param name="subSize">Subchannel size in bytes</param>
        /// <param name="totalDuration">Total commands duration</param>
        /// <param name="tracks">Disc tracks</param>
        /// <param name="subLog">Subchannel log</param>
        /// <param name="desiredSubchannel">Subchannel desired to save</param>
        /// <param name="isrcs">List of disc ISRCs</param>
        /// <param name="mcn">Disc media catalogue number</param>
        /// <param name="subchannelExtents">List of subchannels not yet dumped correctly</param>
        /// <param name="smallestPregapLbaPerTrack">List of smallest pregap relative address per track</param>
        void DumpCdLeadOuts(uint blockSize, ref double currentSpeed, DumpHardwareType currentTry, ExtentsULong extents,
                            IbgLog ibgLog, ref double imageWriteDuration, ExtentsULong leadOutExtents,
                            ref double maxSpeed, MhddLog mhddLog, ref double minSpeed, bool read6, bool read10,
                            bool read12, bool read16, bool readcd, MmcSubchannel supportedSubchannel, uint subSize,
                            ref double totalDuration, SubchannelLog subLog, MmcSubchannel desiredSubchannel,
                            Dictionary <byte, string> isrcs, ref string mcn, Track[] tracks,
                            HashSet <int> subchannelExtents, Dictionary <byte, int> smallestPregapLbaPerTrack)
        {
            byte[]     cmdBuf     = null; // Data buffer
            const uint sectorSize = 2352; // Full sector size
            bool       sense      = true; // Sense indicator

            byte[] senseBuf = null;

            UpdateStatus?.Invoke("Reading lead-outs");
            _dumpLog.WriteLine("Reading lead-outs");

            InitProgress?.Invoke();

            foreach ((ulong item1, ulong item2) in leadOutExtents.ToArray())
            {
                for (ulong i = item1; i <= item2; i++)
                {
                    if (_aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        _dumpLog.WriteLine("Aborted!");

                        break;
                    }

                    double cmdDuration = 0;

                    if (currentSpeed > maxSpeed &&
                        currentSpeed > 0)
                    {
                        maxSpeed = currentSpeed;
                    }

                    if (currentSpeed < minSpeed &&
                        currentSpeed > 0)
                    {
                        minSpeed = currentSpeed;
                    }

                    PulseProgress?.Invoke($"Reading sector {i} at lead-out ({currentSpeed:F3} MiB/sec.)");

                    if (readcd)
                    {
                        sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, MmcSectorTypes.AllTypes,
                                            false, false, true, MmcHeaderCodes.AllHeaders, true, true,
                                            MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);

                        totalDuration += cmdDuration;
                    }
                    else if (read16)
                    {
                        sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, i, blockSize, 0, 1, false,
                                            _dev.Timeout, out cmdDuration);
                    }
                    else if (read12)
                    {
                        sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize,
                                            0, 1, false, _dev.Timeout, out cmdDuration);
                    }
                    else if (read10)
                    {
                        sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize,
                                            0, 1, _dev.Timeout, out cmdDuration);
                    }
                    else if (read6)
                    {
                        sense = _dev.Read6(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, _dev.Timeout,
                                           out cmdDuration);
                    }

                    if (!sense &&
                        !_dev.Error)
                    {
                        mhddLog.Write(i, cmdDuration);
                        ibgLog.Write(i, currentSpeed * 1024);
                        extents.Add(i, _maximumReadable, true);
                        leadOutExtents.Remove(i);
                        DateTime writeStart = DateTime.Now;

                        if (supportedSubchannel != MmcSubchannel.None)
                        {
                            byte[] data = new byte[sectorSize * _maximumReadable];
                            byte[] sub  = new byte[subSize * _maximumReadable];

                            for (int b = 0; b < _maximumReadable; b++)
                            {
                                Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);

                                Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
                            }

                            _outputPlugin.WriteSectorsLong(data, i, _maximumReadable);

                            bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
                                                                                           desiredSubchannel, sub, i, _maximumReadable, subLog, isrcs, 0xAA, ref mcn, tracks,
                                                                                           subchannelExtents, _fixSubchannelPosition, _outputPlugin, _fixSubchannel,
                                                                                           _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true);

                            // Set tracks and go back
                            if (indexesChanged)
                            {
                                (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList());
                                i--;

                                continue;
                            }
                        }
                        else
                        {
                            _outputPlugin.WriteSectors(cmdBuf, i, _maximumReadable);
                        }

                        imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    }
                    else
                    {
                        _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf);

                        // TODO: Reset device after X errors
                        if (_stopOnError)
                        {
                            return; // TODO: Return more cleanly
                        }
                        // Write empty data
                        DateTime writeStart = DateTime.Now;

                        if (supportedSubchannel != MmcSubchannel.None)
                        {
                            _outputPlugin.WriteSectorsLong(new byte[sectorSize * _skip], i, 1);

                            _outputPlugin.WriteSectorsTag(new byte[subSize * _skip], i, 1,
                                                          SectorTagType.CdSectorSubchannel);
                        }
                        else
                        {
                            _outputPlugin.WriteSectors(new byte[blockSize * _skip], i, 1);
                        }

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

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

                        ibgLog.Write(i, 0);
                    }

                    double newSpeed = ((double)blockSize * _maximumReadable) / 1048576 / (cmdDuration / 1000);

                    if (!double.IsInfinity(newSpeed))
                    {
                        currentSpeed = newSpeed;
                    }

                    _resume.NextBlock = i + 1;
                }
            }

            EndProgress?.Invoke();
        }
Ejemplo n.º 9
0
        void RetrySubchannel(bool readcd, uint subSize, MmcSubchannel supportedSubchannel, ref double totalDuration,
                             SubchannelLog subLog, MmcSubchannel desiredSubchannel, Track[] tracks,
                             Dictionary <byte, string> isrcs, ref string mcn, HashSet <int> subchannelExtents)
        {
            bool sense = true;                   // Sense indicator

            byte[]     cmdBuf = null;            // Data buffer
            double     cmdDuration;              // Command execution time
            const uint sectorSize = 2352;        // Full sector size

            byte[]            senseBuf = null;   // Sense buffer
            PlextorSubchannel supportedPlextorSubchannel;

            if (supportedSubchannel == MmcSubchannel.None ||
                desiredSubchannel == MmcSubchannel.None)
            {
                return;
            }

            switch (supportedSubchannel)
            {
            case MmcSubchannel.None:
                supportedPlextorSubchannel = PlextorSubchannel.None;

                break;

            case MmcSubchannel.Raw:
                supportedPlextorSubchannel = PlextorSubchannel.All;

                break;

            case MmcSubchannel.Q16:
                supportedPlextorSubchannel = PlextorSubchannel.Q16;

                break;

            case MmcSubchannel.Rw:
                supportedPlextorSubchannel = PlextorSubchannel.Pack;

                break;

            default:
                supportedPlextorSubchannel = PlextorSubchannel.None;

                break;
            }

            if (_aborted)
            {
                return;
            }

            int  pass    = 1;
            bool forward = true;

            InitProgress?.Invoke();

cdRepeatRetry:

            _resume.BadSubchannels = new List <int>();
            _resume.BadSubchannels.AddRange(subchannelExtents);
            _resume.BadSubchannels.Sort();

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

            int[] tmpArray = _resume.BadSubchannels.ToArray();

            for (int i = 0; i < tmpArray.Length; i++)
            {
                uint badSector = (uint)tmpArray[i];

                Track track = tracks.OrderBy(t => t.TrackStartSector).
                              LastOrDefault(t => badSector >= t.TrackStartSector);

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

                    break;
                }

                PulseProgress?.
                Invoke($"Retrying sector {badSector} subchannel, pass {pass}, {(forward ? "forward" : "reverse")}");

                uint startSector = badSector - 2;

                if (_supportsPlextorD8)
                {
                    sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, startSector, subSize, 5,
                                                 supportedPlextorSubchannel, 0, out cmdDuration);

                    totalDuration += cmdDuration;
                }
                else if (readcd)
                {
                    sense = _dev.ReadCd(out cmdBuf, out senseBuf, startSector, subSize, 5, MmcSectorTypes.AllTypes,
                                        false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None,
                                        supportedSubchannel, _dev.Timeout, out cmdDuration);

                    totalDuration += cmdDuration;
                }

                if (sense || _dev.Error)
                {
                    continue;
                }

                WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, cmdBuf, badSector, 5, subLog, isrcs,
                                       (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents);

                if (subchannelExtents.Contains(tmpArray[i]))
                {
                    continue;
                }

                UpdateStatus?.Invoke($"Correctly retried sector {badSector} subchannel in pass {pass}.");
                _dumpLog.WriteLine("Correctly retried sector {0} subchannel in pass {1}.", badSector, pass);
            }

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

                goto cdRepeatRetry;
            }

            EndProgress?.Invoke();
        }
Ejemplo n.º 10
0
        /// <summary>Dumps inter-session lead-outs</summary>
        /// <param name="blocks">Total number of positive sectors</param>
        /// <param name="blockSize">Size of the read sector in bytes</param>
        /// <param name="currentSpeed">Current read speed</param>
        /// <param name="currentTry">Current dump hardware try</param>
        /// <param name="extents">Extents</param>
        /// <param name="ibgLog">IMGBurn log</param>
        /// <param name="imageWriteDuration">Duration of image write</param>
        /// <param name="leadOutExtents">Lead-out extents</param>
        /// <param name="maxSpeed">Maximum speed</param>
        /// <param name="mhddLog">MHDD log</param>
        /// <param name="minSpeed">Minimum speed</param>
        /// <param name="read6">Device supports READ(6)</param>
        /// <param name="read10">Device supports READ(10)</param>
        /// <param name="read12">Device supports READ(12)</param>
        /// <param name="read16">Device supports READ(16)</param>
        /// <param name="readcd">Device supports READ CD</param>
        /// <param name="supportedSubchannel">Drive's maximum supported subchannel</param>
        /// <param name="subSize">Subchannel size in bytes</param>
        /// <param name="totalDuration">Total commands duration</param>
        void DumpCdLeadOuts(ulong blocks, uint blockSize, ref double currentSpeed, DumpHardwareType currentTry,
                            ExtentsULong extents, IbgLog ibgLog, ref double imageWriteDuration,
                            ExtentsULong leadOutExtents, ref double maxSpeed, MhddLog mhddLog, ref double minSpeed,
                            bool read6, bool read10, bool read12, bool read16, bool readcd,
                            MmcSubchannel supportedSubchannel, uint subSize, ref double totalDuration)
        {
            byte[]     cmdBuf     = null; // Data buffer
            const uint sectorSize = 2352; // Full sector size
            bool       sense      = true; // Sense indicator

            UpdateStatus?.Invoke("Reading lead-outs");
            _dumpLog.WriteLine("Reading lead-outs");

            InitProgress?.Invoke();

            foreach ((ulong item1, ulong item2) in leadOutExtents.ToArray())
            {
                for (ulong i = item1; i <= item2; i++)
                {
                    if (_aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        _dumpLog.WriteLine("Aborted!");

                        break;
                    }

                    double cmdDuration = 0;

                    #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator

                    // ReSharper disable CompareOfFloatsByEqualityOperator
                    if (currentSpeed > maxSpeed &&
                        currentSpeed != 0)
                    {
                        maxSpeed = currentSpeed;
                    }

                    if (currentSpeed < minSpeed &&
                        currentSpeed != 0)
                    {
                        minSpeed = currentSpeed;
                    }

                    // ReSharper restore CompareOfFloatsByEqualityOperator
                    #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                    PulseProgress?.Invoke($"Reading sector {i} at lead-out ({currentSpeed:F3} MiB/sec.)");

                    if (readcd)
                    {
                        sense = _dev.ReadCd(out cmdBuf, out _, (uint)i, blockSize, 1, MmcSectorTypes.AllTypes, false,
                                            false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
                                            supportedSubchannel, _dev.Timeout, out cmdDuration);

                        totalDuration += cmdDuration;
                    }
                    else if (read16)
                    {
                        sense = _dev.Read16(out cmdBuf, out _, 0, false, true, false, i, blockSize, 0, 1, false,
                                            _dev.Timeout, out cmdDuration);
                    }
                    else if (read12)
                    {
                        sense = _dev.Read12(out cmdBuf, out _, 0, false, true, false, false, (uint)i, blockSize, 0, 1,
                                            false, _dev.Timeout, out cmdDuration);
                    }
                    else if (read10)
                    {
                        sense = _dev.Read10(out cmdBuf, out _, 0, false, true, false, false, (uint)i, blockSize, 0, 1,
                                            _dev.Timeout, out cmdDuration);
                    }
                    else if (read6)
                    {
                        sense = _dev.Read6(out cmdBuf, out _, (uint)i, blockSize, 1, _dev.Timeout, out cmdDuration);
                    }

                    if (!sense &&
                        !_dev.Error)
                    {
                        mhddLog.Write(i, cmdDuration);
                        ibgLog.Write(i, currentSpeed * 1024);
                        extents.Add(i, _maximumReadable, true);
                        leadOutExtents.Remove(i);
                        DateTime writeStart = DateTime.Now;

                        if (supportedSubchannel != MmcSubchannel.None)
                        {
                            byte[] data = new byte[sectorSize * _maximumReadable];
                            byte[] sub  = new byte[subSize * _maximumReadable];

                            for (int b = 0; b < _maximumReadable; b++)
                            {
                                Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);

                                Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
                            }

                            _outputPlugin.WriteSectorsLong(data, i, _maximumReadable);
                            _outputPlugin.WriteSectorsTag(sub, i, _maximumReadable, SectorTagType.CdSectorSubchannel);
                        }
                        else
                        {
                            _outputPlugin.WriteSectors(cmdBuf, i, _maximumReadable);
                        }

                        imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    }
                    else
                    {
                        // TODO: Reset device after X errors
                        if (_stopOnError)
                        {
                            return; // TODO: Return more cleanly
                        }
                        // Write empty data
                        DateTime writeStart = DateTime.Now;

                        if (supportedSubchannel != MmcSubchannel.None)
                        {
                            _outputPlugin.WriteSectorsLong(new byte[sectorSize * _skip], i, 1);

                            _outputPlugin.WriteSectorsTag(new byte[subSize * _skip], i, 1,
                                                          SectorTagType.CdSectorSubchannel);
                        }
                        else
                        {
                            _outputPlugin.WriteSectors(new byte[blockSize * _skip], i, 1);
                        }

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

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

                        ibgLog.Write(i, 0);
                    }

                    double newSpeed = ((double)blockSize * _maximumReadable) / 1048576 / (cmdDuration / 1000);

                    if (!double.IsInfinity(newSpeed))
                    {
                        currentSpeed = newSpeed;
                    }

                    _resume.NextBlock = i + 1;
                }
            }

            EndProgress?.Invoke();
        }
Ejemplo n.º 11
0
        void RetryCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry,
                             ExtentsULong extents, int offsetBytes, bool readcd, int sectorsForOffset, uint subSize,
                             MmcSubchannel supportedSubchannel, ref double totalDuration)
        {
            bool sense = true;            // Sense indicator

            byte[]     cmdBuf = null;     // Data buffer
            double     cmdDuration;       // Command execution time
            const uint sectorSize = 2352; // Full sector size

            byte[] tmpBuf;                // Temporary buffer
            byte[] senseBuf = null;       // Sense buffer

            if (_resume.BadBlocks.Count <= 0 ||
                _aborted ||
                _retryPasses <= 0)
            {
                return;
            }

            int  pass              = 1;
            bool forward           = true;
            bool runningPersistent = false;

            Modes.ModePage?currentModePage = null;
            byte[]         md6;
            byte[]         md10;

            if (_persistent)
            {
                Modes.ModePage_01_MMC pgMmc;

                sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout,
                                        out _);

                if (sense)
                {
                    sense = _dev.ModeSense10(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01,
                                             _dev.Timeout, out _);

                    if (!sense)
                    {
                        Modes.DecodedMode?dcMode10 =
                            Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);

                        if (dcMode10?.Pages != null)
                        {
                            foreach (Modes.ModePage modePage in dcMode10.Value.Pages)
                            {
                                if (modePage.Page == 0x01 &&
                                    modePage.Subpage == 0x00)
                                {
                                    currentModePage = modePage;
                                }
                            }
                        }
                    }
                }
                else
                {
                    Modes.DecodedMode?dcMode6 = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);

                    if (dcMode6?.Pages != null)
                    {
                        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 = 32, 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
                };

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

                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 senseBuf, true, false, _dev.Timeout, out _);

                if (sense)
                {
                    sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _);
                }

                if (sense)
                {
                    UpdateStatus?.
                    Invoke("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;
                }
            }

            InitProgress?.Invoke();
cdRepeatRetry:
            ulong[]      tmpArray = _resume.BadBlocks.ToArray();
            List <ulong> sectorsNotEvenPartial = new List <ulong>();

            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, " : ""));

                byte sectorsToReRead   = 1;
                uint badSectorToReRead = (uint)badSector;

                if (_fixOffset &&
                    audioExtents.Contains(badSector) &&
                    offsetBytes != 0)
                {
                    if (offsetBytes > 0)
                    {
                        badSectorToReRead -= (uint)sectorsForOffset;
                    }

                    sectorsToReRead = (byte)(sectorsForOffset + 1);
                }

                if (readcd)
                {
                    sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, sectorsToReRead,
                                        MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
                                        true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);

                    totalDuration += cmdDuration;
                }

                if (sense || _dev.Error)
                {
                    if (!runningPersistent)
                    {
                        continue;
                    }

                    FixedSense?decSense = Sense.DecodeFixed(senseBuf);

                    // MEDIUM ERROR, retry with ignore error below
                    if (decSense.HasValue &&
                        decSense.Value.ASC == 0x11)
                    {
                        if (!sectorsNotEvenPartial.Contains(badSector))
                        {
                            sectorsNotEvenPartial.Add(badSector);
                        }
                    }
                }

                // Because one block has been partially used to fix the offset
                if (_fixOffset &&
                    audioExtents.Contains(badSector) &&
                    offsetBytes != 0)
                {
                    int offsetFix = offsetBytes > 0 ? (int)(sectorSize - (offsetBytes * -1)) : offsetBytes;

                    if (supportedSubchannel != MmcSubchannel.None)
                    {
                        // De-interleave subchannel
                        byte[] data = new byte[sectorSize * sectorsToReRead];
                        byte[] sub  = new byte[subSize * sectorsToReRead];

                        for (int b = 0; b < sectorsToReRead; b++)
                        {
                            Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);

                            Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
                        }

                        tmpBuf = new byte[sectorSize * (sectorsToReRead - sectorsForOffset)];
                        Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length);
                        data = tmpBuf;

                        // Re-interleave subchannel
                        cmdBuf = new byte[blockSize * sectorsToReRead];

                        for (int b = 0; b < sectorsToReRead; b++)
                        {
                            Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize);

                            Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize);
                        }
                    }
                    else
                    {
                        tmpBuf = new byte[blockSize * (sectorsToReRead - sectorsForOffset)];
                        Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length);
                        cmdBuf = tmpBuf;
                    }
                }

                if (!sense &&
                    !_dev.Error)
                {
                    _resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                    UpdateStatus?.Invoke($"Correctly retried sector {badSector} in pass {pass}.");
                    _dumpLog.WriteLine("Correctly retried sector {0} in pass {1}.", badSector, pass);
                    sectorsNotEvenPartial.Remove(badSector);
                }

                if (supportedSubchannel != MmcSubchannel.None)
                {
                    byte[] data = new byte[sectorSize];
                    byte[] sub  = new byte[subSize];
                    Array.Copy(cmdBuf, 0, data, 0, sectorSize);
                    Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
                    _outputPlugin.WriteSectorLong(data, badSector);
                    _outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel);
                }
                else
                {
                    _outputPlugin.WriteSectorLong(cmdBuf, badSector);
                }
            }

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

                goto cdRepeatRetry;
            }

            EndProgress?.Invoke();

            // TODO: Enable when underlying images support lead-outs

            /*
             *  RetryCdLeadOuts(blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration,
             *         leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, read6, read10, read12, read16, readcd,
             *         supportedSubchannel, subSize, ref totalDuration);
             */

            // Try to ignore read errors, on some drives this allows to recover partial even if damaged data
            if (_persistent && sectorsNotEvenPartial.Count > 0)
            {
                var pgMmc = new Modes.ModePage_01_MMC
                {
                    PS = false, ReadRetryCount = 255, Parameter = 0x01
                };

                var 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 (ignore error correction).");
                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)
                {
                    runningPersistent = true;

                    InitProgress?.Invoke();

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

                            break;
                        }

                        PulseProgress?.Invoke($"Trying to get partial data for sector {badSector}");

                        if (readcd)
                        {
                            sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)badSector, blockSize, 1,
                                                MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
                                                true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout,
                                                out cmdDuration);

                            totalDuration += cmdDuration;
                        }

                        if (sense || _dev.Error)
                        {
                            continue;
                        }

                        _dumpLog.WriteLine("Got partial data for sector {0} in pass {1}.", badSector, pass);

                        if (supportedSubchannel != MmcSubchannel.None)
                        {
                            byte[] data = new byte[sectorSize];
                            byte[] sub  = new byte[subSize];
                            Array.Copy(cmdBuf, 0, data, 0, sectorSize);
                            Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
                            _outputPlugin.WriteSectorLong(data, badSector);
                            _outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel);
                        }
                        else
                        {
                            _outputPlugin.WriteSectorLong(cmdBuf, badSector);
                        }
                    }

                    EndProgress?.Invoke();
                }
            }

            if (runningPersistent && currentModePage.HasValue)
            {
                var 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 _);
                }
            }

            EndProgress?.Invoke();
        }
Ejemplo n.º 12
0
        void TrimCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry,
                            ExtentsULong extents, bool newTrim, int offsetBytes, bool read6, bool read10, bool read12,
                            bool read16, bool readcd, int sectorsForOffset, uint subSize,
                            MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration)
        {
            DateTime start;
            DateTime end;
            bool     sense = true;         // Sense indicator

            byte[]     cmdBuf      = null; // Data buffer
            double     cmdDuration = 0;    // Command execution time
            const uint sectorSize  = 2352; // Full sector size

            byte[] tmpBuf;                 // Temporary buffer

            if (_resume.BadBlocks.Count <= 0 ||
                _aborted ||
                !_trim ||
                !newTrim)
            {
                return;
            }

            start = DateTime.UtcNow;
            UpdateStatus?.Invoke("Trimming bad sectors");
            _dumpLog.WriteLine("Trimming bad 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}");

                byte sectorsToTrim   = 1;
                uint badSectorToRead = (uint)badSector;

                if (_fixOffset &&
                    audioExtents.Contains(badSector) &&
                    offsetBytes != 0)
                {
                    if (offsetBytes > 0)
                    {
                        badSectorToRead -= (uint)sectorsForOffset;
                    }

                    sectorsToTrim = (byte)(sectorsForOffset + 1);
                }

                if (readcd)
                {
                    sense = _dev.ReadCd(out cmdBuf, out _, badSectorToRead, blockSize, sectorsToTrim,
                                        MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
                                        true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
                }
                else if (read16)
                {
                    sense = _dev.Read16(out cmdBuf, out _, 0, false, true, false, badSectorToRead, blockSize, 0,
                                        sectorsToTrim, false, _dev.Timeout, out cmdDuration);
                }
                else if (read12)
                {
                    sense = _dev.Read12(out cmdBuf, out _, 0, false, true, false, false, badSectorToRead, blockSize, 0,
                                        sectorsToTrim, false, _dev.Timeout, out cmdDuration);
                }
                else if (read10)
                {
                    sense = _dev.Read10(out cmdBuf, out _, 0, false, true, false, false, badSectorToRead, blockSize, 0,
                                        sectorsToTrim, _dev.Timeout, out cmdDuration);
                }
                else if (read6)
                {
                    sense = _dev.Read6(out cmdBuf, out _, badSectorToRead, blockSize, sectorsToTrim, _dev.Timeout,
                                       out cmdDuration);
                }

                totalDuration += cmdDuration;

                if (sense || _dev.Error)
                {
                    continue;
                }

                if (!sense &&
                    !_dev.Error)
                {
                    _resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                }

                // Because one block has been partially used to fix the offset
                if (_fixOffset &&
                    audioExtents.Contains(badSector) &&
                    offsetBytes != 0)
                {
                    int offsetFix = offsetBytes < 0 ? (int)(sectorSize - (offsetBytes * -1)) : offsetBytes;

                    if (supportedSubchannel != MmcSubchannel.None)
                    {
                        // De-interleave subchannel
                        byte[] data = new byte[sectorSize * sectorsToTrim];
                        byte[] sub  = new byte[subSize * sectorsToTrim];

                        for (int b = 0; b < sectorsToTrim; b++)
                        {
                            Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);

                            Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
                        }

                        tmpBuf = new byte[sectorSize * (sectorsToTrim - sectorsForOffset)];
                        Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length);
                        data = tmpBuf;

                        // Re-interleave subchannel
                        cmdBuf = new byte[blockSize * sectorsToTrim];

                        for (int b = 0; b < sectorsToTrim; b++)
                        {
                            Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize);

                            Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize);
                        }
                    }
                    else
                    {
                        tmpBuf = new byte[blockSize * (sectorsToTrim - sectorsForOffset)];
                        Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length);
                        cmdBuf = tmpBuf;
                    }
                }

                if (supportedSubchannel != MmcSubchannel.None)
                {
                    byte[] data = new byte[sectorSize];
                    byte[] sub  = new byte[subSize];
                    Array.Copy(cmdBuf, 0, data, 0, sectorSize);
                    Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
                    _outputPlugin.WriteSectorLong(data, badSector);
                    _outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel);
                }
                else
                {
                    if (supportsLongSectors)
                    {
                        _outputPlugin.WriteSectorLong(cmdBuf, badSector);
                    }
                    else
                    {
                        if (cmdBuf.Length % sectorSize == 0)
                        {
                            byte[] data = new byte[2048];
                            Array.Copy(cmdBuf, 16, data, 0, 2048);

                            _outputPlugin.WriteSector(data, badSector);
                        }
                        else
                        {
                            _outputPlugin.WriteSectorLong(cmdBuf, 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);
        }
Ejemplo n.º 13
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);
        }
Ejemplo n.º 14
0
        /// <summary>Retries errored data when dumping from a SCSI Block Commands compliant device</summary>
        /// <param name="currentTry">Resume information</param>
        /// <param name="extents">Correctly dump extents</param>
        /// <param name="totalDuration">Total time spent in commands</param>
        /// <param name="scsiReader">SCSI reader</param>
        /// <param name="blankExtents">Blank extents</param>
        void RetrySbcData(Reader scsiReader, DumpHardwareType currentTry, ExtentsULong extents,
                          ref double totalDuration, ExtentsULong blankExtents)
        {
            int  pass              = 1;
            bool forward           = true;
            bool runningPersistent = false;
            bool sense;

            byte[] buffer;
            bool   recoveredError;

            Modes.ModePage?currentModePage = null;
            byte[]         md6;
            byte[]         md10;
            bool           blankCheck;
            bool           newBlank = false;

            if (_persistent)
            {
                Modes.ModePage_01_MMC pgMmc;
                Modes.ModePage_01     pg;

                sense = _dev.ModeSense6(out buffer, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout,
                                        out _);

                if (sense)
                {
                    sense = _dev.ModeSense10(out buffer, out _, false, ScsiModeSensePageControl.Current, 0x01,
                                             _dev.Timeout, out _);

                    if (!sense)
                    {
                        Modes.DecodedMode?dcMode10 = Modes.DecodeMode10(buffer, _dev.ScsiType);

                        if (dcMode10?.Pages != null)
                        {
                            foreach (Modes.ModePage modePage in dcMode10.Value.Pages.Where(modePage =>
                                                                                           modePage.Page == 0x01 && modePage.Subpage == 0x00))
                            {
                                currentModePage = modePage;
                            }
                        }
                    }
                }
                else
                {
                    Modes.DecodedMode?dcMode6 = Modes.DecodeMode6(buffer, _dev.ScsiType);

                    if (dcMode6?.Pages != null)
                    {
                        foreach (Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage =>
                                                                                      modePage.Page == 0x01 && modePage.Subpage == 0x00))
                        {
                            currentModePage = modePage;
                        }
                    }
                }

                if (currentModePage == null)
                {
                    if (_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice)
                    {
                        pgMmc = new Modes.ModePage_01_MMC
                        {
                            PS             = false,
                            ReadRetryCount = 32,
                            Parameter      = 0x00
                        };

                        currentModePage = new Modes.ModePage
                        {
                            Page         = 0x01,
                            Subpage      = 0x00,
                            PageResponse = Modes.EncodeModePage_01_MMC(pgMmc)
                        };
                    }
                    else
                    {
                        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)
                        };
                    }
                }

                if (_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice)
                {
                    pgMmc = new Modes.ModePage_01_MMC
                    {
                        PS             = false,
                        ReadRetryCount = 255,
                        Parameter      = 0x20
                    };

                    var 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);
                }
                else
                {
                    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);
                    md10 = Modes.EncodeMode10(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)
                {
                    sense = _dev.ModeSelect10(md10, out 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 = scsiReader.ReadBlock(out buffer, badSector, out double cmdDuration, out recoveredError,
                                             out blankCheck);

                totalDuration += cmdDuration;

                if (blankCheck)
                {
                    _resume.BadBlocks.Remove(badSector);
                    blankExtents.Add(badSector, badSector);
                    newBlank = true;

                    UpdateStatus?.Invoke($"Found blank block {badSector} in pass {pass}.");
                    _dumpLog.WriteLine("Found blank block {0} in pass {1}.", badSector, pass);

                    continue;
                }

                if ((!sense && !_dev.Error) || recoveredError)
                {
                    _resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                    _outputPlugin.WriteSector(buffer, 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(buffer, 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);
                md10 = Modes.EncodeMode10(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 _);

                if (sense)
                {
                    _dev.ModeSelect10(md10, out _, true, false, _dev.Timeout, out _);
                }
            }

            if (newBlank)
            {
                _resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents);
            }

            EndProgress?.Invoke();
        }
Ejemplo n.º 15
0
        void DumpUmd()
        {
            const uint      BLOCK_SIZE    = 2048;
            const MediaType DSK_TYPE      = MediaType.UMD;
            uint            blocksToRead  = 16;
            double          totalDuration = 0;
            double          currentSpeed  = 0;
            double          maxSpeed      = double.MinValue;
            double          minSpeed      = double.MaxValue;
            DateTime        start;
            DateTime        end;

            bool sense = _dev.Read12(out byte[] readBuffer, out _, 0, false, true, false, false, 0, 512, 0, 1, false,
                                     _dev.Timeout, out _);

            if (sense)
            {
                _dumpLog.WriteLine("Could not read...");
                StoppingErrorMessage?.Invoke("Could not read...");

                return;
            }

            ushort fatStart      = (ushort)((readBuffer[0x0F] << 8) + readBuffer[0x0E]);
            ushort sectorsPerFat = (ushort)((readBuffer[0x17] << 8) + readBuffer[0x16]);
            ushort rootStart     = (ushort)((sectorsPerFat * 2) + fatStart);
            ushort rootSize      = (ushort)((((readBuffer[0x12] << 8) + readBuffer[0x11]) * 32) / 512);
            ushort umdStart      = (ushort)(rootStart + rootSize);

            UpdateStatus?.Invoke($"Reading root directory in sector {rootStart}...");
            _dumpLog.WriteLine("Reading root directory in sector {0}...", rootStart);

            sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, rootStart, 512, 0, 1, false,
                                _dev.Timeout, out _);

            if (sense)
            {
                _dumpLog.WriteLine("Could not read...");
                StoppingErrorMessage?.Invoke("Could not read...");

                return;
            }

            uint   umdSizeInBytes  = BitConverter.ToUInt32(readBuffer, 0x3C);
            ulong  blocks          = umdSizeInBytes / BLOCK_SIZE;
            string mediaPartNumber = Encoding.ASCII.GetString(readBuffer, 0, 11).Trim();

            UpdateStatus?.
            Invoke($"Media has {blocks} blocks of {BLOCK_SIZE} bytes/each. (for a total of {blocks * (ulong)BLOCK_SIZE} bytes)");

            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($"Device reports {2048} bytes per physical block.");
            UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}.");
            UpdateStatus?.Invoke($"Media identified as {DSK_TYPE}.");
            UpdateStatus?.Invoke($"Media part number is {mediaPartNumber}.");
            _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * BLOCK_SIZE);
            _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
            _dumpLog.WriteLine("Device reports {0} bytes per logical block.", BLOCK_SIZE);
            _dumpLog.WriteLine("Device reports {0} bytes per physical block.", 2048);
            _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType);
            _dumpLog.WriteLine("Media identified as {0}.", DSK_TYPE);
            _dumpLog.WriteLine("Media part number is {0}.", mediaPartNumber);

            bool ret;

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

            ret = _outputPlugin.Create(_outputPath, DSK_TYPE, _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;

            (_outputPlugin as IWritableOpticalImage).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
                }
            });

            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,
                                       (long)blocks);

                sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, (uint)(umdStart + (i * 4)),
                                    512, 0, blocksToRead * 4, 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;
                _dumpLog.WriteLine("Trimming skipped sectors");

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

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

                        break;
                    }

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

                    sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false,
                                        (uint)(umdStart + (badSector * 4)), 512, 0, 4, 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)
                    {
                        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);

                    _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($"Retrying sector {badSector}, pass {pass}, {(runningPersistent ? "recovering partial data, " : "")}{(forward ? "forward" : "reverse")}");

                    sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false,
                                        (uint)(umdStart + (badSector * 4)), 512, 0, 4, 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(string.Format("Correctly retried block {0} in pass {1}.", badSector,
                                                           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);

                    _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status).");
                    sense = _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _);
                }

                EndProgress?.Invoke();
                AaruConsole.WriteLine();
            }
            #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(), MediaPartNumber = mediaPartNumber
            };

            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;
            _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);

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

                return;
            }

            double totalChkDuration = 0;

            if (_metadata)
            {
                WriteOpticalSidecar(BLOCK_SIZE, blocks, DSK_TYPE, null, null, 1, out totalChkDuration, null);
            }

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

            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(DSK_TYPE, true);
        }
Ejemplo n.º 16
0
        void RetryCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry,
                             ExtentsULong extents, int offsetBytes, bool readcd, int sectorsForOffset, uint subSize,
                             MmcSubchannel supportedSubchannel, ref double totalDuration, SubchannelLog subLog,
                             MmcSubchannel desiredSubchannel, Track[] tracks, Dictionary <byte, string> isrcs,
                             ref string mcn, HashSet <int> subchannelExtents)
        {
            bool sense = true;                   // Sense indicator

            byte[]     cmdBuf = null;            // Data buffer
            double     cmdDuration;              // Command execution time
            const uint sectorSize = 2352;        // Full sector size

            byte[]            senseBuf = null;   // Sense buffer
            PlextorSubchannel supportedPlextorSubchannel;

            switch (supportedSubchannel)
            {
            case MmcSubchannel.None:
                supportedPlextorSubchannel = PlextorSubchannel.None;

                break;

            case MmcSubchannel.Raw:
                supportedPlextorSubchannel = PlextorSubchannel.All;

                break;

            case MmcSubchannel.Q16:
                supportedPlextorSubchannel = PlextorSubchannel.Q16;

                break;

            case MmcSubchannel.Rw:
                supportedPlextorSubchannel = PlextorSubchannel.Pack;

                break;

            default:
                supportedPlextorSubchannel = PlextorSubchannel.None;

                break;
            }

            if (_resume.BadBlocks.Count <= 0 ||
                _aborted ||
                _retryPasses <= 0)
            {
                return;
            }

            int  pass              = 1;
            bool forward           = true;
            bool runningPersistent = false;

            Modes.ModePage?currentModePage = null;
            byte[]         md6;
            byte[]         md10;

            if (_persistent)
            {
                Modes.ModePage_01_MMC pgMmc;

                sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout,
                                        out _);

                if (sense)
                {
                    sense = _dev.ModeSense10(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01,
                                             _dev.Timeout, out _);

                    if (!sense)
                    {
                        Modes.DecodedMode?dcMode10 =
                            Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);

                        if (dcMode10?.Pages != null)
                        {
                            foreach (Modes.ModePage modePage in dcMode10.Value.Pages)
                            {
                                if (modePage.Page == 0x01 &&
                                    modePage.Subpage == 0x00)
                                {
                                    currentModePage = modePage;
                                }
                            }
                        }
                    }
                }
                else
                {
                    Modes.DecodedMode?dcMode6 = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);

                    if (dcMode6?.Pages != null)
                    {
                        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 = 32, 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
                };

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

                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 senseBuf, true, false, _dev.Timeout, out _);

                if (sense)
                {
                    sense = _dev.ModeSelect10(md10, out 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();
cdRepeatRetry:
            ulong[]      tmpArray = _resume.BadBlocks.ToArray();
            List <ulong> sectorsNotEvenPartial = new List <ulong>();

            for (int i = 0; i < tmpArray.Length; i++)
            {
                ulong badSector = tmpArray[i];

                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, " : ""));

                Track track = tracks.OrderBy(t => t.TrackStartSector).
                              LastOrDefault(t => badSector >= t.TrackStartSector);

                byte sectorsToReRead   = 1;
                uint badSectorToReRead = (uint)badSector;

                if (_fixOffset &&
                    audioExtents.Contains(badSector) &&
                    offsetBytes != 0)
                {
                    if (offsetBytes > 0)
                    {
                        badSectorToReRead -= (uint)sectorsForOffset;
                    }

                    sectorsToReRead = (byte)(sectorsForOffset + 1);
                }

                if (_supportsPlextorD8 && audioExtents.Contains(badSector))
                {
                    sense = ReadPlextorWithSubchannel(out cmdBuf, out senseBuf, badSectorToReRead, blockSize,
                                                      sectorsToReRead, supportedPlextorSubchannel, out cmdDuration);

                    totalDuration += cmdDuration;
                }
                else if (readcd)
                {
                    sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, sectorsToReRead,
                                        MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
                                        true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);

                    totalDuration += cmdDuration;
                }

                if (sense || _dev.Error)
                {
                    if (!runningPersistent)
                    {
                        continue;
                    }

                    FixedSense?decSense = Sense.DecodeFixed(senseBuf);

                    // MEDIUM ERROR, retry with ignore error below
                    if (decSense.HasValue &&
                        decSense.Value.ASC == 0x11)
                    {
                        if (!sectorsNotEvenPartial.Contains(badSector))
                        {
                            sectorsNotEvenPartial.Add(badSector);
                        }
                    }
                }

                // Because one block has been partially used to fix the offset
                if (_fixOffset &&
                    audioExtents.Contains(badSector) &&
                    offsetBytes != 0)
                {
                    uint blocksToRead = sectorsToReRead;

                    FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead,
                                  subSize, ref cmdBuf, blockSize, false);
                }

                if (!sense &&
                    !_dev.Error)
                {
                    _resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                    UpdateStatus?.Invoke($"Correctly retried sector {badSector} in pass {pass}.");
                    _dumpLog.WriteLine("Correctly retried sector {0} in pass {1}.", badSector, pass);
                    sectorsNotEvenPartial.Remove(badSector);
                }

                if (supportedSubchannel != MmcSubchannel.None)
                {
                    byte[] data = new byte[sectorSize];
                    byte[] sub  = new byte[subSize];
                    Array.Copy(cmdBuf, 0, data, 0, sectorSize);
                    Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
                    _outputPlugin.WriteSectorLong(data, badSector);

                    bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, badSector,
                                                                 1, subLog, isrcs, (byte)track.TrackSequence, ref mcn,
                                                                 tracks, subchannelExtents);

                    // Set tracks and go back
                    if (indexesChanged)
                    {
                        (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList());
                        i--;
                    }
                }
                else
                {
                    _outputPlugin.WriteSectorLong(cmdBuf, badSector);
                }
            }

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

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

                goto cdRepeatRetry;
            }

            EndProgress?.Invoke();

            // TODO: Enable when underlying images support lead-outs

            /*
             *  RetryCdLeadOuts(blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration,
             *         leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, read6, read10, read12, read16, readcd,
             *         supportedSubchannel, subSize, ref totalDuration);
             */

            // Try to ignore read errors, on some drives this allows to recover partial even if damaged data
            if (_persistent && sectorsNotEvenPartial.Count > 0)
            {
                var pgMmc = new Modes.ModePage_01_MMC
                {
                    PS = false, ReadRetryCount = 255, Parameter = 0x01
                };

                var 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 (ignore error correction).");
                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)
                {
                    runningPersistent = true;

                    InitProgress?.Invoke();

                    for (int i = 0; i < sectorsNotEvenPartial.Count; i++)
                    {
                        ulong badSector = sectorsNotEvenPartial[i];

                        if (_aborted)
                        {
                            currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                            _dumpLog.WriteLine("Aborted!");

                            break;
                        }

                        PulseProgress?.Invoke($"Trying to get partial data for sector {badSector}");

                        Track track = tracks.OrderBy(t => t.TrackStartSector).
                                      LastOrDefault(t => badSector >= t.TrackStartSector);

                        if (readcd)
                        {
                            sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)badSector, blockSize, 1,
                                                MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
                                                true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout,
                                                out cmdDuration);

                            totalDuration += cmdDuration;
                        }

                        if (sense || _dev.Error)
                        {
                            continue;
                        }

                        _dumpLog.WriteLine("Got partial data for sector {0} in pass {1}.", badSector, pass);

                        if (supportedSubchannel != MmcSubchannel.None)
                        {
                            byte[] data = new byte[sectorSize];
                            byte[] sub  = new byte[subSize];
                            Array.Copy(cmdBuf, 0, data, 0, sectorSize);
                            Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
                            _outputPlugin.WriteSectorLong(data, badSector);

                            bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub,
                                                                         badSector, 1, subLog, isrcs,
                                                                         (byte)track.TrackSequence, ref mcn, tracks,
                                                                         subchannelExtents);

                            // Set tracks and go back
                            if (indexesChanged)
                            {
                                (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList());
                                i--;
                            }
                        }
                        else
                        {
                            _outputPlugin.WriteSectorLong(cmdBuf, badSector);
                        }
                    }

                    EndProgress?.Invoke();
                }
            }

            if (runningPersistent && currentModePage.HasValue)
            {
                var 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 _);
                }
            }

            EndProgress?.Invoke();
        }
Ejemplo n.º 17
0
        /// <summary>Dumps a MultiMediaCard or SecureDigital flash card</summary>
        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 sdProfile = 0x0001;
            const uint   timeout   = 5;
            double       duration;
            uint         blocksToRead = 1;
            uint         blockSize    = 512;
            ulong        blocks       = 0;

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

            uint[] response;

            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 response, 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
                {
                    _errorLog?.WriteLine("Read eCSD", _dev.Error, _dev.LastError, response);
                    ecsd = null;
                }

                UpdateStatus?.Invoke("Reading CSD");
                _dumpLog.WriteLine("Reading CSD");
                sense = _dev.ReadCsd(out csd, out response, 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
                {
                    _errorLog?.WriteLine("Read CSD", _dev.Error, _dev.LastError, response);
                    csd = null;
                }

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

                if (sense)
                {
                    _errorLog?.WriteLine("Read OCR", _dev.Error, _dev.LastError, response);
                    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 response, 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
                {
                    _errorLog?.WriteLine("Read CSD", _dev.Error, _dev.LastError, response);
                    csd = null;
                }

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

                if (sense)
                {
                    _errorLog?.WriteLine("Read OCR", _dev.Error, _dev.LastError, response);
                    ocr = null;
                }
                else
                {
                    mediaTags.Add(MediaTagType.SD_OCR, null);
                }

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

                if (sense)
                {
                    _errorLog?.WriteLine("Read SCR", _dev.Error, _dev.LastError, response);
                    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 response, timeout, out duration);

            if (sense)
            {
                _errorLog?.WriteLine("Read CID", _dev.Error, _dev.LastError, response);
                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.Where(tag => !_outputPlugin.SupportedMediaTags.Contains(tag)))
            {
                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", sdProfile);

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

                if (currentSpeed > maxSpeed &&
                    currentSpeed > 0)
                {
                    maxSpeed = currentSpeed;
                }

                if (currentSpeed < minSpeed &&
                    currentSpeed > 0)
                {
                    minSpeed = currentSpeed;
                }

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

                error = _dev.Read(out cmdBuf, out response, (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
                {
                    _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, byteAddressed, response);

                    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 response, (uint)badSector, blockSize, 1, byteAddressed, timeout,
                                      out duration);

                    totalDuration += duration;

                    if (error)
                    {
                        _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, byteAddressed, response);

                        continue;
                    }

                    _resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                    _outputPlugin.WriteSector(cmdBuf, 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;

                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 response, (uint)badSector, blockSize, 1, byteAddressed, timeout,
                                      out duration);

                    totalDuration += duration;

                    if (error)
                    {
                        _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, byteAddressed, response);
                    }

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

                    if (!forward)
                    {
                        _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.");

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

            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;
            }
        }
Ejemplo n.º 18
0
        void TrimCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry,
                            ExtentsULong extents, bool newTrim, int offsetBytes, bool read6, bool read10, bool read12,
                            bool read16, bool readcd, int sectorsForOffset, uint subSize,
                            MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration,
                            SubchannelLog subLog, MmcSubchannel desiredSubchannel, Track[] tracks,
                            Dictionary <byte, string> isrcs, ref string mcn, HashSet <int> subchannelExtents)
        {
            DateTime start;
            DateTime end;
            bool     sense = true;                // Sense indicator

            byte[]            cmdBuf      = null; // Data buffer
            double            cmdDuration = 0;    // Command execution time
            const uint        sectorSize  = 2352; // Full sector size
            PlextorSubchannel supportedPlextorSubchannel;

            switch (supportedSubchannel)
            {
            case MmcSubchannel.None:
                supportedPlextorSubchannel = PlextorSubchannel.None;

                break;

            case MmcSubchannel.Raw:
                supportedPlextorSubchannel = PlextorSubchannel.All;

                break;

            case MmcSubchannel.Q16:
                supportedPlextorSubchannel = PlextorSubchannel.Q16;

                break;

            case MmcSubchannel.Rw:
                supportedPlextorSubchannel = PlextorSubchannel.Pack;

                break;

            default:
                supportedPlextorSubchannel = PlextorSubchannel.None;

                break;
            }

            if (_resume.BadBlocks.Count <= 0 ||
                _aborted ||
                !_trim ||
                !newTrim)
            {
                return;
            }

            start = DateTime.UtcNow;
            UpdateStatus?.Invoke("Trimming skipped sectors");
            _dumpLog.WriteLine("Trimming skipped sectors");

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

            for (int b = 0; b < tmpArray.Length; b++)
            {
                ulong badSector = tmpArray[b];

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

                    break;
                }

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

                Track track = tracks.OrderBy(t => t.TrackStartSector).
                              LastOrDefault(t => badSector >= t.TrackStartSector);

                byte sectorsToTrim   = 1;
                uint badSectorToRead = (uint)badSector;

                if (_fixOffset &&
                    audioExtents.Contains(badSector) &&
                    offsetBytes != 0)
                {
                    if (offsetBytes > 0)
                    {
                        badSectorToRead -= (uint)sectorsForOffset;
                    }

                    sectorsToTrim = (byte)(sectorsForOffset + 1);
                }

                if (_supportsPlextorD8 && audioExtents.Contains(badSector))
                {
                    sense = ReadPlextorWithSubchannel(out cmdBuf, out _, badSectorToRead, blockSize, sectorsToTrim,
                                                      supportedPlextorSubchannel, out cmdDuration);

                    totalDuration += cmdDuration;
                }
                else if (readcd)
                {
                    sense = _dev.ReadCd(out cmdBuf, out _, badSectorToRead, blockSize, sectorsToTrim,
                                        MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
                                        true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
                }
                else if (read16)
                {
                    sense = _dev.Read16(out cmdBuf, out _, 0, false, true, false, badSectorToRead, blockSize, 0,
                                        sectorsToTrim, false, _dev.Timeout, out cmdDuration);
                }
                else if (read12)
                {
                    sense = _dev.Read12(out cmdBuf, out _, 0, false, true, false, false, badSectorToRead, blockSize, 0,
                                        sectorsToTrim, false, _dev.Timeout, out cmdDuration);
                }
                else if (read10)
                {
                    sense = _dev.Read10(out cmdBuf, out _, 0, false, true, false, false, badSectorToRead, blockSize, 0,
                                        sectorsToTrim, _dev.Timeout, out cmdDuration);
                }
                else if (read6)
                {
                    sense = _dev.Read6(out cmdBuf, out _, badSectorToRead, blockSize, sectorsToTrim, _dev.Timeout,
                                       out cmdDuration);
                }

                totalDuration += cmdDuration;

                if (sense || _dev.Error)
                {
                    continue;
                }

                if (!sense &&
                    !_dev.Error)
                {
                    _resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                }

                // Because one block has been partially used to fix the offset
                if (_fixOffset &&
                    audioExtents.Contains(badSector) &&
                    offsetBytes != 0)
                {
                    uint blocksToRead = sectorsToTrim;

                    FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead,
                                  subSize, ref cmdBuf, blockSize, false);
                }

                if (supportedSubchannel != MmcSubchannel.None)
                {
                    byte[] data = new byte[sectorSize];
                    byte[] sub  = new byte[subSize];
                    Array.Copy(cmdBuf, 0, data, 0, sectorSize);
                    Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
                    _outputPlugin.WriteSectorLong(data, badSector);

                    bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, badSector,
                                                                 1, subLog, isrcs, (byte)track.TrackSequence, ref mcn,
                                                                 tracks, subchannelExtents);

                    // Set tracks and go back
                    if (!indexesChanged)
                    {
                        continue;
                    }

                    (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList());
                    b--;

                    continue;
                }

                if (supportsLongSectors)
                {
                    _outputPlugin.WriteSectorLong(cmdBuf, badSector);
                }
                else
                {
                    if (cmdBuf.Length % sectorSize == 0)
                    {
                        byte[] data = new byte[2048];
                        Array.Copy(cmdBuf, 16, data, 0, 2048);

                        _outputPlugin.WriteSector(data, badSector);
                    }
                    else
                    {
                        _outputPlugin.WriteSectorLong(cmdBuf, 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);
        }
Ejemplo n.º 19
0
        // TODO: Set pregap for Track 1
        // TODO: Detect errors in sectors
        /// <summary>Reads all CD user data</summary>
        /// <param name="blocks">Total number of positive sectors</param>
        /// <param name="blockSize">Size of the read sector in bytes</param>
        /// <param name="currentSpeed">Current read speed</param>
        /// <param name="currentTry">Current dump hardware try</param>
        /// <param name="extents">Extents</param>
        /// <param name="ibgLog">IMGBurn log</param>
        /// <param name="imageWriteDuration">Duration of image write</param>
        /// <param name="leadOutExtents">Lead-out extents</param>
        /// <param name="maxSpeed">Maximum speed</param>
        /// <param name="mhddLog">MHDD log</param>
        /// <param name="minSpeed">Minimum speed</param>
        /// <param name="offsetBytes">Read offset</param>
        /// <param name="sectorsForOffset">Sectors needed to fix offset</param>
        /// <param name="subSize">Subchannel size in bytes</param>
        /// <param name="supportedSubchannel">Drive's maximum supported subchannel</param>
        /// <param name="totalDuration">Total commands duration</param>
        /// <param name="cdiReadyReadAsAudio">Is the drive returning CD-i Ready hidden track as audio?</param>
        /// <param name="tracks">Disc tracks</param>
        /// <param name="subLog">Subchannel log</param>
        /// <param name="desiredSubchannel">Subchannel desired to save</param>
        /// <param name="isrcs">List of disc ISRCs</param>
        /// <param name="mcn">Disc media catalogue number</param>
        /// <param name="subchannelExtents">List of subchannels not yet dumped correctly</param>
        /// <param name="smallestPregapLbaPerTrack">List of smallest pregap relative address per track</param>
        void ReadCdiReady(uint blockSize, ref double currentSpeed, DumpHardwareType currentTry, ExtentsULong extents,
                          IbgLog ibgLog, ref double imageWriteDuration, ExtentsULong leadOutExtents,
                          ref double maxSpeed, MhddLog mhddLog, ref double minSpeed, uint subSize,
                          MmcSubchannel supportedSubchannel, ref double totalDuration, Track[] tracks,
                          SubchannelLog subLog, MmcSubchannel desiredSubchannel, Dictionary <byte, string> isrcs,
                          ref string mcn, HashSet <int> subchannelExtents, ulong blocks, bool cdiReadyReadAsAudio,
                          int offsetBytes, int sectorsForOffset, Dictionary <byte, int> smallestPregapLbaPerTrack)
        {
            ulong    sectorSpeedStart = 0;               // Used to calculate correct speed
            DateTime timeSpeedStart   = DateTime.UtcNow; // Time of start for speed calculation
            bool     sense;                              // Sense indicator

            byte[]     cmdBuf;                           // Data buffer
            byte[]     senseBuf;                         // Sense buffer
            double     cmdDuration;                      // Command execution time
            const uint sectorSize = 2352;                // Full sector size
            Track      firstTrack = tracks.FirstOrDefault(t => t.TrackSequence == 1);
            uint       blocksToRead;                     // How many sectors to read at once

            if (firstTrack is null)
            {
                return;
            }

            if (cdiReadyReadAsAudio)
            {
                _dumpLog.WriteLine("Setting speed to 8x for CD-i Ready reading as audio.");
                UpdateStatus?.Invoke("Setting speed to 8x for CD-i Ready reading as audio.");

                _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, 1416, 0, _dev.Timeout, out _);
            }

            InitProgress?.Invoke();

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

                    break;
                }

                uint firstSectorToRead = (uint)i;

                blocksToRead = _maximumReadable;

                if (blocksToRead == 1 && cdiReadyReadAsAudio)
                {
                    blocksToRead += (uint)sectorsForOffset;
                }

                if (cdiReadyReadAsAudio)
                {
                    // TODO: FreeBSD bug
                    if (offsetBytes < 0)
                    {
                        if (i == 0)
                        {
                            firstSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1
                        }
                        else
                        {
                            firstSectorToRead -= (uint)sectorsForOffset;
                        }
                    }
                }

                if (currentSpeed > maxSpeed &&
                    currentSpeed > 0)
                {
                    maxSpeed = currentSpeed;
                }

                if (currentSpeed < minSpeed &&
                    currentSpeed > 0)
                {
                    minSpeed = currentSpeed;
                }

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

                sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead,
                                    MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
                                    MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);

                totalDuration += cmdDuration;

                double elapsed;

                // Overcome the track mode change drive error
                if (sense)
                {
                    for (uint r = 0; r < _maximumReadable; r++)
                    {
                        UpdateProgress?.Invoke($"Reading sector {i + r} of {blocks} ({currentSpeed:F3} MiB/sec.)",
                                               (long)i + r, (long)blocks);

                        sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)(i + r), blockSize,
                                            (uint)sectorsForOffset + 1, MmcSectorTypes.AllTypes, false, false, true,
                                            MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
                                            supportedSubchannel, _dev.Timeout, out cmdDuration);

                        totalDuration += cmdDuration;

                        if (!sense &&
                            !_dev.Error)
                        {
                            mhddLog.Write(i + r, cmdDuration);
                            ibgLog.Write(i + r, currentSpeed * 1024);
                            extents.Add(i + r, 1, true);
                            DateTime writeStart = DateTime.Now;

                            if (cdiReadyReadAsAudio)
                            {
                                FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel,
                                              ref blocksToRead, subSize, ref cmdBuf, blockSize, false);
                            }

                            if (supportedSubchannel != MmcSubchannel.None)
                            {
                                byte[] data = new byte[sectorSize];
                                byte[] sub  = new byte[subSize];

                                Array.Copy(cmdBuf, 0, data, 0, sectorSize);

                                Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);

                                if (cdiReadyReadAsAudio)
                                {
                                    data = Sector.Scramble(data);
                                }

                                _outputPlugin.WriteSectorsLong(data, i + r, 1);

                                bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
                                                                                               desiredSubchannel, sub,
                                                                                               i + r, 1, subLog, isrcs,
                                                                                               1, ref mcn, tracks,
                                                                                               subchannelExtents,
                                                                                               _fixSubchannelPosition,
                                                                                               _outputPlugin,
                                                                                               _fixSubchannel,
                                                                                               _fixSubchannelCrc,
                                                                                               _dumpLog, UpdateStatus,
                                                                                               smallestPregapLbaPerTrack);

                                // Set tracks and go back
                                if (indexesChanged)
                                {
                                    (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList());
                                    i -= _maximumReadable;

                                    continue;
                                }
                            }
                            else
                            {
                                _outputPlugin.WriteSectorsLong(cmdBuf, i + r, 1);
                            }

                            imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                        }
                        else
                        {
                            _errorLog?.WriteLine(i + r, _dev.Error, _dev.LastError, senseBuf);

                            leadOutExtents.Add(i + r, firstTrack.TrackStartSector - 1);

                            UpdateStatus?.
                            Invoke($"Adding CD-i Ready hole from LBA {i + r} to {firstTrack.TrackStartSector - 1} inclusive.");

                            _dumpLog.WriteLine("Adding CD-i Ready hole from LBA {0} to {1} inclusive.", i + r,
                                               firstTrack.TrackStartSector - 1);

                            break;
                        }

                        sectorSpeedStart += r;

                        _resume.NextBlock = i + r;

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

                        if (elapsed < 1)
                        {
                            continue;
                        }

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

                if (!sense &&
                    !_dev.Error)
                {
                    if (cdiReadyReadAsAudio)
                    {
                        FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead,
                                      subSize, ref cmdBuf, blockSize, false);
                    }

                    mhddLog.Write(i, cmdDuration);
                    ibgLog.Write(i, currentSpeed * 1024);
                    extents.Add(i, blocksToRead, true);
                    DateTime writeStart = DateTime.Now;

                    if (supportedSubchannel != MmcSubchannel.None)
                    {
                        byte[] data    = new byte[sectorSize * blocksToRead];
                        byte[] sub     = new byte[subSize * blocksToRead];
                        byte[] tmpData = new byte[sectorSize];

                        for (int b = 0; b < blocksToRead; b++)
                        {
                            if (cdiReadyReadAsAudio)
                            {
                                Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), tmpData, 0, sectorSize);
                                tmpData = Sector.Scramble(tmpData);
                                Array.Copy(tmpData, 0, data, sectorSize * b, sectorSize);
                            }
                            else
                            {
                                Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);
                            }

                            Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
                        }

                        _outputPlugin.WriteSectorsLong(data, i, blocksToRead);

                        bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
                                                                                       desiredSubchannel, sub, i,
                                                                                       blocksToRead, subLog, isrcs, 1,
                                                                                       ref mcn, tracks,
                                                                                       subchannelExtents,
                                                                                       _fixSubchannelPosition,
                                                                                       _outputPlugin, _fixSubchannel,
                                                                                       _fixSubchannelCrc, _dumpLog,
                                                                                       UpdateStatus,
                                                                                       smallestPregapLbaPerTrack);

                        // Set tracks and go back
                        if (indexesChanged)
                        {
                            (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList());
                            i -= blocksToRead;

                            continue;
                        }
                    }
                    else
                    {
                        if (cdiReadyReadAsAudio)
                        {
                            byte[] tmpData = new byte[sectorSize];
                            byte[] data    = new byte[sectorSize * blocksToRead];

                            for (int b = 0; b < blocksToRead; b++)
                            {
                                Array.Copy(cmdBuf, (int)(b * sectorSize), tmpData, 0, sectorSize);
                                tmpData = Sector.Scramble(tmpData);
                                Array.Copy(tmpData, 0, data, sectorSize * b, sectorSize);
                            }

                            _outputPlugin.WriteSectorsLong(data, i, blocksToRead);
                        }
                        else
                        {
                            _outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead);
                        }
                    }

                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                }
                else
                {
                    _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf);

                    _resume.NextBlock = firstTrack.TrackStartSector;

                    break;
                }

                sectorSpeedStart += blocksToRead;

                _resume.NextBlock = i + blocksToRead;

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

                if (elapsed < 1)
                {
                    continue;
                }

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

            EndProgress?.Invoke();
        }
Ejemplo n.º 20
0
        ScanResults SecureDigital()
        {
            var results = new ScanResults();

            byte[] cmdBuf;
            bool   sense;

            results.Blocks = 0;
            const uint   TIMEOUT = 5;
            double       duration;
            const ushort SD_PROFILE    = 0x0001;
            uint         blocksToRead  = 128;
            uint         blockSize     = 512;
            bool         byteAddressed = true;

            switch (_dev.Type)
            {
            case DeviceType.MMC:
            {
                sense = _dev.ReadExtendedCsd(out cmdBuf, out _, TIMEOUT, out _);

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

                    // Supposing it's high-capacity MMC if it has Extended CSD...
                    byteAddressed = false;
                }

                if (sense || results.Blocks == 0)
                {
                    sense = _dev.ReadCsd(out cmdBuf, out _, TIMEOUT, out _);

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

                break;
            }

            case DeviceType.SecureDigital:
            {
                sense = _dev.ReadCsd(out cmdBuf, out _, TIMEOUT, out _);

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

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

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

                    // Structure >=1 for SDHC/SDXC, so that's block addressed
                    byteAddressed = csd.Structure == 0;
                }

                break;
            }
            }

            if (results.Blocks == 0)
            {
                StoppingErrorMessage?.Invoke("Unable to get device size.");

                return(results);
            }

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

                if (sense)
                {
                    blocksToRead /= 2;
                }

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

            if (sense)
            {
                StoppingErrorMessage?.Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length.");

                return(results);
            }

            results.A       = 0; // <3ms
            results.B       = 0; // >=3ms, <10ms
            results.C       = 0; // >=10ms, <50ms
            results.D       = 0; // >=50ms, <150ms
            results.E       = 0; // >=150ms, <500ms
            results.F       = 0; // >=500ms
            results.Errored = 0;
            DateTime start;
            DateTime end;

            results.ProcessingTime = 0;
            double currentSpeed = 0;

            results.MaxSpeed          = double.MinValue;
            results.MinSpeed          = double.MaxValue;
            results.UnreadableSectors = new List <ulong>();
            results.SeekMax           = double.MinValue;
            results.SeekMin           = double.MaxValue;
            results.SeekTotal         = 0;
            const int SEEK_TIMES = 1000;

            var rnd = new Random();

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

            InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, SD_PROFILE);
            var mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false);
            var ibgLog  = new IbgLog(_ibgLogPath, SD_PROFILE);

            start = DateTime.UtcNow;
            DateTime timeSpeedStart   = DateTime.UtcNow;
            ulong    sectorSpeedStart = 0;

            InitProgress?.Invoke();

            for (ulong i = 0; i < results.Blocks; i += blocksToRead)
            {
                if (_aborted)
                {
                    break;
                }

                if (results.Blocks - i < blocksToRead)
                {
                    blocksToRead = (byte)(results.Blocks - i);
                }

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

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

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

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

                if (!error)
                {
                    if (duration >= 500)
                    {
                        results.F += blocksToRead;
                    }
                    else if (duration >= 150)
                    {
                        results.E += blocksToRead;
                    }
                    else if (duration >= 50)
                    {
                        results.D += blocksToRead;
                    }
                    else if (duration >= 10)
                    {
                        results.C += blocksToRead;
                    }
                    else if (duration >= 3)
                    {
                        results.B += blocksToRead;
                    }
                    else
                    {
                        results.A += blocksToRead;
                    }

                    ScanTime?.Invoke(i, duration);
                    mhddLog.Write(i, duration);
                    ibgLog.Write(i, currentSpeed * 1024);
                }
                else
                {
                    ScanUnreadable?.Invoke(i);
                    results.Errored += blocksToRead;

                    for (ulong b = i; b < i + blocksToRead; b++)
                    {
                        results.UnreadableSectors.Add(b);
                    }

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

                    ibgLog.Write(i, 0);
                }

                sectorSpeedStart += blocksToRead;

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

                if (elapsed < 1)
                {
                    continue;
                }

                currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                ScanSpeed?.Invoke(i, currentSpeed * 1024);
                sectorSpeedStart = 0;
                timeSpeedStart   = DateTime.UtcNow;
            }

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

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

            InitProgress?.Invoke();

            for (int i = 0; i < SEEK_TIMES; i++)
            {
                if (_aborted || !_seekTest)
                {
                    break;
                }

                uint seekPos = (uint)rnd.Next((int)results.Blocks);

                PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t");

                _dev.Read(out cmdBuf, out _, seekPos, blockSize, blocksToRead, byteAddressed, TIMEOUT,
                          out double seekCur);

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

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

                results.SeekTotal += seekCur;
                GC.Collect();
            }

            EndProgress?.Invoke();

            results.ProcessingTime /= 1000;
            results.TotalTime       = (end - start).TotalSeconds;
            results.AvgSpeed        = (blockSize * (double)(results.Blocks + 1)) / 1048576 / results.ProcessingTime;
            results.SeekTimes       = SEEK_TIMES;

            return(results);
        }
Ejemplo n.º 21
0
        // TODO: Fix offset
        void ReadCdFirstTrackPregap(uint blockSize, ref double currentSpeed, Dictionary<MediaTagType, byte[]> mediaTags,
                                    MmcSubchannel supportedSubchannel, ref double totalDuration)
        {
            bool     sense;                           // Sense indicator
            byte[]   cmdBuf;                          // Data buffer
            double   cmdDuration;                     // Command execution time
            DateTime timeSpeedStart;                  // Time of start for speed calculation
            ulong    sectorSpeedStart            = 0; // Used to calculate correct speed
            bool     gotFirstTrackPregap         = false;
            int      firstTrackPregapSectorsGood = 0;
            var      firstTrackPregapMs          = new MemoryStream();

            _dumpLog.WriteLine("Reading first track pregap");
            UpdateStatus?.Invoke("Reading first track pregap");
            InitProgress?.Invoke();
            timeSpeedStart = DateTime.UtcNow;

            for(int firstTrackPregapBlock = -150; firstTrackPregapBlock < 0 && _resume.NextBlock == 0;
                firstTrackPregapBlock++)
            {
                if(_aborted)
                {
                    _dumpLog.WriteLine("Aborted!");
                    UpdateStatus?.Invoke("Aborted!");

                    break;
                }

                PulseProgress?.
                    Invoke($"Trying to read first track pregap sector {firstTrackPregapBlock} ({currentSpeed:F3} MiB/sec.)");

                sense = _dev.ReadCd(out cmdBuf, out _, (uint)firstTrackPregapBlock, blockSize, 1,
                                    MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
                                    MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);

                if(!sense &&
                   !_dev.Error)
                {
                    firstTrackPregapMs.Write(cmdBuf, 0, (int)blockSize);
                    gotFirstTrackPregap = true;
                    firstTrackPregapSectorsGood++;
                    totalDuration += cmdDuration;
                }
                else
                {
                    // Write empty data
                    if(gotFirstTrackPregap)
                        firstTrackPregapMs.Write(new byte[blockSize], 0, (int)blockSize);
                }

                sectorSpeedStart++;

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

                if(elapsed < 1)
                    continue;

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

            if(firstTrackPregapSectorsGood > 0)
                mediaTags.Add(MediaTagType.CD_FirstTrackPregap, firstTrackPregapMs.ToArray());

            EndProgress?.Invoke();
            UpdateStatus?.Invoke($"Got {firstTrackPregapSectorsGood} first track pregap sectors.");
            _dumpLog.WriteLine("Got {0} first track pregap sectors.", firstTrackPregapSectorsGood);

            firstTrackPregapMs.Close();
        }
Ejemplo n.º 22
0
        ScanResults SecureDigital()
        {
            var results = new ScanResults();

            byte[] cmdBuf;
            bool   sense;

            results.Blocks = 0;
            const uint   timeout = 5;
            double       duration;
            const ushort sdProfile     = 0x0001;
            ushort       blocksToRead  = 128;
            uint         blockSize     = 512;
            bool         byteAddressed = true;
            bool         supportsCmd23 = false;

            switch (_dev.Type)
            {
            case DeviceType.MMC:
            {
                sense = _dev.ReadCsd(out cmdBuf, out _, timeout, out _);

                if (!sense)
                {
                    CSD csd = Decoders.MMC.Decoders.DecodeCSD(cmdBuf);
                    results.Blocks = (ulong)((csd.Size + 1) * Math.Pow(2, csd.SizeMultiplier + 2));
                    blockSize      = (uint)Math.Pow(2, csd.ReadBlockLength);

                    // Found at least since MMC System Specification 3.31
                    supportsCmd23 = csd.Version >= 3;

                    if (csd.Size == 0xFFF)
                    {
                        sense = _dev.ReadExtendedCsd(out cmdBuf, out _, timeout, out _);

                        if (!sense)
                        {
                            ExtendedCSD ecsd = Decoders.MMC.Decoders.DecodeExtendedCSD(cmdBuf);
                            results.Blocks = ecsd.SectorCount;
                            blockSize      = (uint)(ecsd.SectorSize == 1 ? 4096 : 512);

                            blocksToRead = (ushort)(ecsd.OptimalReadSize * 4096 / blockSize);

                            if (blocksToRead == 0)
                            {
                                blocksToRead = 128;
                            }

                            // Supposing it's high-capacity MMC if it has Extended CSD...
                            byteAddressed = false;
                        }
                    }
                }

                break;
            }

            case DeviceType.SecureDigital:
            {
                sense = _dev.ReadCsd(out cmdBuf, out _, timeout, out _);

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

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

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

                    // Structure >=1 for SDHC/SDXC, so that's block addressed
                    byteAddressed = csd.Structure == 0;

                    if (blockSize != 512)
                    {
                        uint ratio = blockSize / 512;
                        results.Blocks *= ratio;
                        blockSize       = 512;
                    }

                    sense = _dev.ReadScr(out cmdBuf, out _, timeout, out _);

                    if (!sense)
                    {
                        supportsCmd23 = Decoders.SecureDigital.Decoders.DecodeSCR(cmdBuf)?.CommandSupport.
                                        HasFlag(CommandSupport.SetBlockCount) ?? false;
                    }
                }

                break;
            }
            }

            if (results.Blocks == 0)
            {
                StoppingErrorMessage?.Invoke("Unable to get device size.");

                return(results);
            }

            if (supportsCmd23)
            {
                sense = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, 1, byteAddressed, timeout,
                                                out duration);

                if (sense || _dev.Error)
                {
                    UpdateStatus?.
                    Invoke("Environment does not support setting block count, downgrading to OS reading.");

                    supportsCmd23 = false;
                }

                // Need to restart device, otherwise is it just busy streaming data with no one listening
                sense = _dev.ReOpen();

                if (sense)
                {
                    StoppingErrorMessage?.Invoke($"Error {_dev.LastError} reopening device.");

                    return(results);
                }
            }

            if (supportsCmd23)
            {
                while (true)
                {
                    sense = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed,
                                                    timeout, out duration);

                    if (sense)
                    {
                        blocksToRead /= 2;
                    }

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

                if (sense)
                {
                    StoppingErrorMessage?.
                    Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length.");

                    return(results);
                }
            }

            results.A       = 0; // <3ms
            results.B       = 0; // >=3ms, <10ms
            results.C       = 0; // >=10ms, <50ms
            results.D       = 0; // >=50ms, <150ms
            results.E       = 0; // >=150ms, <500ms
            results.F       = 0; // >=500ms
            results.Errored = 0;
            DateTime start;
            DateTime end;

            results.ProcessingTime = 0;
            double currentSpeed = 0;

            results.MaxSpeed          = double.MinValue;
            results.MinSpeed          = double.MaxValue;
            results.UnreadableSectors = new List <ulong>();
            results.SeekMax           = double.MinValue;
            results.SeekMin           = double.MaxValue;
            results.SeekTotal         = 0;
            const int seekTimes = 100;

            var rnd = new Random();

            if (supportsCmd23 || blocksToRead == 1)
            {
                UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time.");
            }
            else if (_useBufferedReads)
            {
                UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time using OS buffered reads.");
            }
            else
            {
                UpdateStatus?.Invoke($"Reading {blocksToRead} sectors using sequential single commands.");
            }

            InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, sdProfile);
            var mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false);
            var ibgLog  = new IbgLog(_ibgLogPath, sdProfile);

            start = DateTime.UtcNow;
            DateTime timeSpeedStart   = DateTime.UtcNow;
            ulong    sectorSpeedStart = 0;

            InitProgress?.Invoke();

            for (ulong i = 0; i < results.Blocks; i += blocksToRead)
            {
                if (_aborted)
                {
                    break;
                }

                if (results.Blocks - i < blocksToRead)
                {
                    blocksToRead = (byte)(results.Blocks - i);
                }

                if (currentSpeed > results.MaxSpeed &&
                    currentSpeed > 0)
                {
                    results.MaxSpeed = currentSpeed;
                }

                if (currentSpeed < results.MinSpeed &&
                    currentSpeed > 0)
                {
                    results.MinSpeed = currentSpeed;
                }

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

                bool error;

                if (blocksToRead == 1)
                {
                    error = _dev.ReadSingleBlock(out cmdBuf, out _, (uint)i, blockSize, byteAddressed, timeout,
                                                 out duration);
                }
                else if (supportsCmd23)
                {
                    error = _dev.ReadWithBlockCount(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed,
                                                    timeout, out duration);
                }
                else if (_useBufferedReads)
                {
                    error = _dev.BufferedOsRead(out cmdBuf, (long)(i * blockSize), blockSize * blocksToRead,
                                                out duration);
                }
                else
                {
                    error = _dev.ReadMultipleUsingSingle(out cmdBuf, out _, (uint)i, blockSize, blocksToRead,
                                                         byteAddressed, timeout, out duration);
                }

                if (!error)
                {
                    if (duration >= 500)
                    {
                        results.F += blocksToRead;
                    }
                    else if (duration >= 150)
                    {
                        results.E += blocksToRead;
                    }
                    else if (duration >= 50)
                    {
                        results.D += blocksToRead;
                    }
                    else if (duration >= 10)
                    {
                        results.C += blocksToRead;
                    }
                    else if (duration >= 3)
                    {
                        results.B += blocksToRead;
                    }
                    else
                    {
                        results.A += blocksToRead;
                    }

                    ScanTime?.Invoke(i, duration);
                    mhddLog.Write(i, duration);
                    ibgLog.Write(i, currentSpeed * 1024);
                }
                else
                {
                    ScanUnreadable?.Invoke(i);
                    results.Errored += blocksToRead;

                    for (ulong b = i; b < i + blocksToRead; b++)
                    {
                        results.UnreadableSectors.Add(b);
                    }

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

                    ibgLog.Write(i, 0);
                }

                sectorSpeedStart += blocksToRead;

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

                if (elapsed < 1)
                {
                    continue;
                }

                currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed);
                ScanSpeed?.Invoke(i, currentSpeed * 1024);
                sectorSpeedStart = 0;
                timeSpeedStart   = DateTime.UtcNow;
            }

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

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

            InitProgress?.Invoke();

            for (int i = 0; i < seekTimes; i++)
            {
                if (_aborted || !_seekTest)
                {
                    break;
                }

                uint seekPos = (uint)rnd.Next((int)results.Blocks);

                PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t");

                _dev.ReadSingleBlock(out cmdBuf, out _, seekPos, blockSize, byteAddressed, timeout, out double seekCur);

                if (seekCur > results.SeekMax &&
                    seekCur > 0)
                {
                    results.SeekMax = seekCur;
                }

                if (seekCur < results.SeekMin &&
                    seekCur > 0)
                {
                    results.SeekMin = seekCur;
                }

                results.SeekTotal += seekCur;
                GC.Collect();
            }

            EndProgress?.Invoke();

            results.ProcessingTime /= 1000;
            results.TotalTime       = (end - start).TotalSeconds;
            results.AvgSpeed        = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime;
            results.SeekTimes       = seekTimes;

            return(results);
        }