Beispiel #1
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);
        }
Beispiel #2
0
        // 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;
            }
        }
Beispiel #3
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();
        }
Beispiel #4
0
        /// <summary>Scan the medium for a contiguous set of written or blank logical blocks</summary>
        /// <param name="senseBuffer">Sense buffer.</param>
        /// <param name="relAddr">Set to <c>true</c> if <paramref name="lba" /> is relative</param>
        /// <param name="lba">Logical block address where to start the search.</param>
        /// <param name="scanLength">Number of blocks to scan</param>
        /// <param name="timeout">Timeout.</param>
        /// <param name="duration">Duration.</param>
        /// <param name="written">
        ///     If set to <c>true</c> drive will search for written blocks, otherwise it will search for blank
        ///     blocks
        /// </param>
        /// <param name="advancedScan">If set to <c>true</c> drive will consider the search area has contiguous blocks</param>
        /// <param name="reverse">If set to <c>true</c> drive will search in reverse</param>
        /// <param name="partial">
        ///     If set to <c>true</c> return even if the total number of blocks requested is not found but the
        ///     other parameters are met
        /// </param>
        /// <param name="requested">Number of contiguous blocks to find</param>
        public bool MediumScan(out byte[] senseBuffer, bool written, bool advancedScan, bool reverse, bool partial,
                               bool relAddr, uint lba, uint requested, uint scanLength, out uint foundLba,
                               out uint foundBlocks, uint timeout, out double duration)
        {
            senseBuffer = new byte[32];
            byte[] cdb    = new byte[10];
            byte[] buffer = new byte[0];
            foundLba    = 0;
            foundBlocks = 0;

            cdb[0] = (byte)ScsiCommands.MediumScan;

            if (written)
            {
                cdb[1] &= 0x10;
            }

            if (advancedScan)
            {
                cdb[1] &= 0x08;
            }

            if (reverse)
            {
                cdb[1] &= 0x04;
            }

            if (partial)
            {
                cdb[1] &= 0x02;
            }

            if (relAddr)
            {
                cdb[1] &= 0x01;
            }

            cdb[2] = (byte)((lba & 0xFF000000) >> 24);
            cdb[3] = (byte)((lba & 0xFF0000) >> 16);
            cdb[4] = (byte)((lba & 0xFF00) >> 8);
            cdb[5] = (byte)(lba & 0xFF);

            if (requested > 0 ||
                scanLength > 1)
            {
                buffer    = new byte[8];
                buffer[0] = (byte)((requested & 0xFF000000) >> 24);
                buffer[1] = (byte)((requested & 0xFF0000) >> 16);
                buffer[2] = (byte)((requested & 0xFF00) >> 8);
                buffer[3] = (byte)(requested & 0xFF);
                buffer[4] = (byte)((scanLength & 0xFF000000) >> 24);
                buffer[5] = (byte)((scanLength & 0xFF0000) >> 16);
                buffer[6] = (byte)((scanLength & 0xFF00) >> 8);
                buffer[7] = (byte)(scanLength & 0xFF);
                cdb[8]    = 8;
            }

            LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout,
                                        buffer.Length == 0 ? ScsiDirection.None : ScsiDirection.Out, out duration,
                                        out bool sense);

            Error = LastError != 0;

            AaruConsole.DebugWriteLine("SCSI Device", "MEDIUM SCAN took {0} ms.", duration);

            if (Error)
            {
                return(sense);
            }

            FixedSense?decodedSense = Sense.DecodeFixed(senseBuffer);

            switch (decodedSense?.SenseKey)
            {
            case SenseKeys.NoSense: return(false);

            case SenseKeys.Equal when decodedSense.Value.InformationValid:
                foundBlocks = decodedSense.Value.CommandSpecific;
                foundLba    = decodedSense.Value.Information;

                return(false);

            default: return(true);
            }
        }
Beispiel #5
0
        public TestedMedia ReportScsi()
        {
            var capabilities = new TestedMedia
            {
                MediaIsRecognized = true
            };

            AaruConsole.WriteLine("Querying SCSI READ CAPACITY...");
            bool sense = _dev.ReadCapacity(out byte[] buffer, out byte[] senseBuffer, _dev.Timeout, out _);

            if (!sense &&
                !_dev.Error)
            {
                capabilities.SupportsReadCapacity = true;

                capabilities.Blocks = (ulong)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]) + 1;

                capabilities.BlockSize = (uint)((buffer[4] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]);
            }

            AaruConsole.WriteLine("Querying SCSI READ CAPACITY (16)...");
            sense = _dev.ReadCapacity16(out buffer, out buffer, _dev.Timeout, out _);

            if (!sense &&
                !_dev.Error)
            {
                capabilities.SupportsReadCapacity16 = true;
                byte[] temp = new byte[8];
                Array.Copy(buffer, 0, temp, 0, 8);
                Array.Reverse(temp);
                capabilities.Blocks    = BitConverter.ToUInt64(temp, 0) + 1;
                capabilities.BlockSize = (uint)((buffer[8] << 24) + (buffer[9] << 16) + (buffer[10] << 8) + buffer[11]);
            }

            Modes.DecodedMode?decMode = null;

            AaruConsole.WriteLine("Querying SCSI MODE SENSE (10)...");

            sense = _dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current, 0x3F,
                                     0x00, _dev.Timeout, out _);

            if (!sense &&
                !_dev.Error)
            {
                decMode = Modes.DecodeMode10(buffer, _dev.ScsiType);
                capabilities.ModeSense10Data = buffer;
            }

            AaruConsole.WriteLine("Querying SCSI MODE SENSE...");
            sense = _dev.ModeSense(out buffer, out senseBuffer, _dev.Timeout, out _);

            if (!sense &&
                !_dev.Error)
            {
                decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType);

                capabilities.ModeSense6Data = buffer;
            }

            if (decMode.HasValue)
            {
                capabilities.MediumType = (byte)decMode.Value.Header.MediumType;

                if (decMode.Value.Header.BlockDescriptors?.Length > 0)
                {
                    capabilities.Density = (byte)decMode.Value.Header.BlockDescriptors[0].Density;
                }
            }

            AaruConsole.WriteLine("Trying SCSI READ (6)...");

            capabilities.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 0, capabilities.BlockSize ?? 512,
                                                     _dev.Timeout, out _);

            AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead6);
            capabilities.Read6Data = buffer;

            AaruConsole.WriteLine("Trying SCSI READ (10)...");

            capabilities.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, false, false, false, 0,
                                                       capabilities.BlockSize ?? 512, 0, 1, _dev.Timeout, out _);

            AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead10);
            capabilities.Read10Data = buffer;

            AaruConsole.WriteLine("Trying SCSI READ (12)...");

            capabilities.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, false, false, false, 0,
                                                       capabilities.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _);

            AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead12);
            capabilities.Read12Data = buffer;

            AaruConsole.WriteLine("Trying SCSI READ (16)...");

            capabilities.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, false, false, 0,
                                                       capabilities.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _);

            AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead16);
            capabilities.Read16Data = buffer;

            capabilities.LongBlockSize = capabilities.BlockSize;
            AaruConsole.WriteLine("Trying SCSI READ LONG (10)...");
            sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 0xFFFF, _dev.Timeout, out _);

            if (sense && !_dev.Error)
            {
                FixedSense?decSense = Sense.DecodeFixed(senseBuffer);

                if (decSense?.SenseKey == SenseKeys.IllegalRequest &&
                    decSense.Value.ASC == 0x24 &&
                    decSense.Value.ASCQ == 0x00)
                {
                    capabilities.SupportsReadLong = true;

                    if (decSense.Value.InformationValid &&
                        decSense.Value.ILI)
                    {
                        capabilities.LongBlockSize = 0xFFFF - (decSense.Value.Information & 0xFFFF);
                    }
                }
            }

            if (capabilities.SupportsReadLong != true ||
                capabilities.LongBlockSize != capabilities.BlockSize)
            {
                return(capabilities);
            }

            if (capabilities.BlockSize == 512)
            {
                foreach (ushort testSize in new ushort[]
                {
                    // Long sector sizes for floppies
                    514,

                    // Long sector sizes for SuperDisk
                    536, 558,

                    // Long sector sizes for 512-byte magneto-opticals
                    600, 610, 630
                })
                {
                    sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, testSize, _dev.Timeout,
                                            out _);

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

                    capabilities.SupportsReadLong = true;
                    capabilities.LongBlockSize    = testSize;

                    break;
                }
            }
            else if (capabilities.BlockSize == 1024)
            {
                foreach (ushort testSize in new ushort[]
                {
                    // Long sector sizes for floppies
                    1026,

                    // Long sector sizes for 1024-byte magneto-opticals
                    1200
                })
                {
                    sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, testSize, _dev.Timeout,
                                            out _);

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

                    capabilities.SupportsReadLong = true;
                    capabilities.LongBlockSize    = testSize;

                    break;
                }
            }
            else if (capabilities.BlockSize == 2048)
            {
                sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 2380, _dev.Timeout, out _);

                if (sense || _dev.Error)
                {
                    return(capabilities);
                }

                capabilities.SupportsReadLong = true;
                capabilities.LongBlockSize    = 2380;
            }
            else if (capabilities.BlockSize == 4096)
            {
                sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 4760, _dev.Timeout, out _);

                if (sense || _dev.Error)
                {
                    return(capabilities);
                }

                capabilities.SupportsReadLong = true;
                capabilities.LongBlockSize    = 4760;
            }
            else if (capabilities.BlockSize == 8192)
            {
                sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 9424, _dev.Timeout, out _);

                if (sense || _dev.Error)
                {
                    return(capabilities);
                }

                capabilities.SupportsReadLong = true;
                capabilities.LongBlockSize    = 9424;
            }

            return(capabilities);
        }
Beispiel #6
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();
        }
Beispiel #7
0
        static void ReadLeadOutUsingTrapDisc(string devPath, Device dev)
        {
            string strDev;
            int    item;
            bool   tocIsNotBcd = false;
            bool   sense;

            byte[] buffer;
            byte[] senseBuffer;
            int    retries;

start:
            System.Console.Clear();

            AaruConsole.WriteLine("Ejecting disc...");

            dev.AllowMediumRemoval(out _, dev.Timeout, out _);
            dev.EjectTray(out _, dev.Timeout, out _);

            AaruConsole.WriteLine("Please insert a data only disc inside...");
            AaruConsole.WriteLine("Press any key to continue...");
            System.Console.ReadLine();

            AaruConsole.WriteLine("Sending READ FULL TOC to the device...");

            retries = 0;

            do
            {
                retries++;
                sense = dev.ScsiTestUnitReady(out senseBuffer, dev.Timeout, out _);

                if (!sense)
                {
                    break;
                }

                FixedSense?decodedSense = Sense.DecodeFixed(senseBuffer);

                if (decodedSense.Value.ASC != 0x04)
                {
                    break;
                }

                if (decodedSense.Value.ASCQ != 0x01)
                {
                    break;
                }

                Thread.Sleep(2000);
            } while(retries < 25);

            sense = dev.ReadRawToc(out buffer, out senseBuffer, 1, dev.Timeout, out _);

            if (sense)
            {
                AaruConsole.WriteLine("READ FULL TOC failed...");
                AaruConsole.WriteLine("{0}", Sense.PrettifySense(senseBuffer));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            FullTOC.CDFullTOC?decodedToc = FullTOC.Decode(buffer);

            if (decodedToc is null)
            {
                AaruConsole.WriteLine("Could not decode TOC...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            FullTOC.CDFullTOC toc = decodedToc.Value;

            FullTOC.TrackDataDescriptor leadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2);

            if (leadOutTrack.POINT != 0xA2)
            {
                AaruConsole.WriteLine("Cannot find lead-out...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            int min   = ((leadOutTrack.PMIN >> 4) * 10) + (leadOutTrack.PMIN & 0x0F);
            int sec   = ((leadOutTrack.PSEC >> 4) * 10) + (leadOutTrack.PSEC & 0x0F);
            int frame = ((leadOutTrack.PFRAME >> 4) * 10) + (leadOutTrack.PFRAME & 0x0F);

            int sectors = ((min * 60 * 75) + (sec * 75) + frame) - 150;

            AaruConsole.WriteLine("Data disc shows {0} sectors...", sectors);

            AaruConsole.WriteLine("Ejecting disc...");

            dev.AllowMediumRemoval(out _, dev.Timeout, out _);
            dev.EjectTray(out _, dev.Timeout, out _);

            AaruConsole.WriteLine("Please insert the trap disc inside...");
            AaruConsole.WriteLine("Press any key to continue...");
            System.Console.ReadLine();

            AaruConsole.WriteLine("Sending READ FULL TOC to the device...");

            retries = 0;

            do
            {
                retries++;
                sense = dev.ScsiTestUnitReady(out senseBuffer, dev.Timeout, out _);

                if (!sense)
                {
                    break;
                }

                FixedSense?decodedSense = Sense.DecodeFixed(senseBuffer);

                if (decodedSense.Value.ASC != 0x04)
                {
                    break;
                }

                if (decodedSense.Value.ASCQ != 0x01)
                {
                    break;
                }

                Thread.Sleep(2000);
            } while(retries < 25);

            sense = dev.ReadRawToc(out buffer, out senseBuffer, 1, dev.Timeout, out _);

            if (sense)
            {
                AaruConsole.WriteLine("READ FULL TOC failed...");
                AaruConsole.WriteLine("{0}", Sense.PrettifySense(senseBuffer));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            decodedToc = FullTOC.Decode(buffer);

            if (decodedToc is null)
            {
                AaruConsole.WriteLine("Could not decode TOC...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            toc = decodedToc.Value;

            leadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2);

            if (leadOutTrack.POINT != 0xA2)
            {
                AaruConsole.WriteLine("Cannot find lead-out...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            min = 0;

            if (leadOutTrack.PMIN == 122)
            {
                tocIsNotBcd = true;
            }

            if (leadOutTrack.PMIN >= 0xA0 &&
                !tocIsNotBcd)
            {
                min += 90;
                leadOutTrack.PMIN -= 0x90;
            }

            if (tocIsNotBcd)
            {
                min   = leadOutTrack.PMIN;
                sec   = leadOutTrack.PSEC;
                frame = leadOutTrack.PFRAME;
            }
            else
            {
                min  += ((leadOutTrack.PMIN >> 4) * 10) + (leadOutTrack.PMIN & 0x0F);
                sec   = ((leadOutTrack.PSEC >> 4) * 10) + (leadOutTrack.PSEC & 0x0F);
                frame = ((leadOutTrack.PFRAME >> 4) * 10) + (leadOutTrack.PFRAME & 0x0F);
            }

            int trapSectors = ((min * 60 * 75) + (sec * 75) + frame) - 150;

            AaruConsole.WriteLine("Trap disc shows {0} sectors...", trapSectors);

            if (trapSectors < sectors + 100)
            {
                AaruConsole.WriteLine("Trap disc doesn't have enough sectors...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            AaruConsole.WriteLine("Stopping motor...");

            dev.StopUnit(out _, dev.Timeout, out _);

            AaruConsole.WriteLine("Please MANUALLY get the trap disc out and put the data disc back inside...");
            AaruConsole.WriteLine("Press any key to continue...");
            System.Console.ReadLine();

            AaruConsole.WriteLine("Waiting 5 seconds...");
            Thread.Sleep(5000);

            AaruConsole.WriteLine("Sending READ FULL TOC to the device...");

            retries = 0;

            do
            {
                retries++;
                sense = dev.ReadRawToc(out buffer, out senseBuffer, 1, dev.Timeout, out _);

                if (!sense)
                {
                    break;
                }

                FixedSense?decodedSense = Sense.DecodeFixed(senseBuffer);

                if (decodedSense.Value.ASC != 0x04)
                {
                    break;
                }

                if (decodedSense.Value.ASCQ != 0x01)
                {
                    break;
                }
            } while(retries < 25);

            if (sense)
            {
                AaruConsole.WriteLine("READ FULL TOC failed...");
                AaruConsole.WriteLine("{0}", Sense.PrettifySense(senseBuffer));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            decodedToc = FullTOC.Decode(buffer);

            if (decodedToc is null)
            {
                AaruConsole.WriteLine("Could not decode TOC...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            toc = decodedToc.Value;

            FullTOC.TrackDataDescriptor newLeadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2);

            if (newLeadOutTrack.POINT != 0xA2)
            {
                AaruConsole.WriteLine("Cannot find lead-out...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            if (newLeadOutTrack.PMIN >= 0xA0 &&
                !tocIsNotBcd)
            {
                newLeadOutTrack.PMIN -= 0x90;
            }

            if (newLeadOutTrack.PMIN != leadOutTrack.PMIN ||
                newLeadOutTrack.PSEC != leadOutTrack.PSEC ||
                newLeadOutTrack.PFRAME != leadOutTrack.PFRAME)
            {
                AaruConsole.WriteLine("Lead-out has changed, this drive does not support hot swapping discs...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            AaruConsole.Write("Reading LBA {0}... ", sectors + 5);

            bool dataResult = dev.ReadCd(out byte[] dataBuffer, out byte[] dataSense, (uint)(sectors + 5), 2352, 1,
                                         MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
                                         true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);

            AaruConsole.WriteLine(dataResult ? "FAIL!" : "Success!");

            AaruConsole.Write("Reading LBA {0} as audio (scrambled)... ", sectors + 5);

            bool scrambledResult = dev.ReadCd(out byte[] scrambledBuffer, out byte[] scrambledSense,
                                              (uint)(sectors + 5), 2352, 1, MmcSectorTypes.Cdda, false, false, false,
                                              MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None,
                                              dev.Timeout, out _);

            AaruConsole.WriteLine(scrambledResult ? "FAIL!" : "Success!");

            AaruConsole.Write("Reading LBA {0}'s PQ subchannel... ", sectors + 5);

            bool pqResult = dev.ReadCd(out byte[] pqBuffer, out byte[] pqSense, (uint)(sectors + 5), 16, 1,
                                       MmcSectorTypes.AllTypes, false, false, false, MmcHeaderCodes.None, false, false,
                                       MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, out _);

            if (pqResult)
            {
                pqResult = dev.ReadCd(out pqBuffer, out pqSense, (uint)(sectors + 5), 16, 1, MmcSectorTypes.AllTypes,
                                      false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None,
                                      MmcSubchannel.Q16, dev.Timeout, out _);
            }

            AaruConsole.WriteLine(pqResult ? "FAIL!" : "Success!");

            AaruConsole.Write("Reading LBA {0}'s PQ subchannel... ", sectors + 5);

            bool rwResult = dev.ReadCd(out byte[] rwBuffer, out byte[] rwSense, (uint)(sectors + 5), 16, 1,
                                       MmcSectorTypes.AllTypes, false, false, false, MmcHeaderCodes.None, false, false,
                                       MmcErrorField.None, MmcSubchannel.Rw, dev.Timeout, out _);

            if (rwResult)
            {
                rwResult = dev.ReadCd(out rwBuffer, out rwSense, (uint)(sectors + 5), 16, 1, MmcSectorTypes.Cdda, false,
                                      false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None,
                                      MmcSubchannel.Rw, dev.Timeout, out _);
            }

            AaruConsole.WriteLine(pqResult ? "FAIL!" : "Success!");

menu:
            System.Console.Clear();
            AaruConsole.WriteLine("Device: {0}", devPath);
            AaruConsole.WriteLine("Device {0} read Lead-Out.", dataResult && scrambledResult ? "cannot" : "can");

            AaruConsole.WriteLine("LBA {0} sense is {1}, buffer is {2}, sense buffer is {3}.", sectors + 5, dataResult,
                                  dataBuffer is null
                                      ? "null"
                                      : ArrayHelpers.ArrayIsNullOrEmpty(dataBuffer)
                                          ? "empty"
                                          : $"{dataBuffer.Length} bytes", dataSense is null
                                                                              ? "null"
                                                                              : ArrayHelpers.
                                  ArrayIsNullOrEmpty(dataSense)
                                                                                  ? "empty"
                                                                                  : $"{dataSense.Length}");

            AaruConsole.WriteLine("LBA {0} (scrambled) sense is {1}, buffer is {2}, sense buffer is {3}.", sectors + 5,
                                  scrambledResult, scrambledBuffer is null
                                                       ? "null"
                                                       : ArrayHelpers.ArrayIsNullOrEmpty(scrambledBuffer)
                                                           ? "empty"
                                                           : $"{scrambledBuffer.Length} bytes", scrambledSense is null
                                                                                                    ? "null"
                                                                                                    : ArrayHelpers.
                                  ArrayIsNullOrEmpty(scrambledSense)
                                                                                                        ? "empty"
                                                                                                        : $"{scrambledSense.Length}");

            AaruConsole.WriteLine("LBA {0}'s PQ sense is {1}, buffer is {2}, sense buffer is {3}.", sectors + 5,
                                  pqResult, pqBuffer is null
                                                ? "null"
                                                : ArrayHelpers.ArrayIsNullOrEmpty(pqBuffer)
                                                    ? "empty"
                                                    : $"{pqBuffer.Length} bytes", pqSense is null
                                                                                      ? "null"
                                                                                      : ArrayHelpers.
                                  ArrayIsNullOrEmpty(pqSense)
                                                                                          ? "empty"
                                                                                          : $"{pqSense.Length}");

            AaruConsole.WriteLine("LBA {0}'s RW sense is {1}, buffer is {2}, sense buffer is {3}.", sectors + 5,
                                  rwResult, rwBuffer is null
                                                ? "null"
                                                : ArrayHelpers.ArrayIsNullOrEmpty(rwBuffer)
                                                    ? "empty"
                                                    : $"{rwBuffer.Length} bytes", rwSense is null
                                                                                      ? "null"
                                                                                      : ArrayHelpers.
                                  ArrayIsNullOrEmpty(rwSense)
                                                                                          ? "empty"
                                                                                          : $"{rwSense.Length}");

            AaruConsole.WriteLine();
            AaruConsole.WriteLine("Choose what to do:");
            AaruConsole.WriteLine("1.- Print LBA {0} buffer.", sectors + 5);
            AaruConsole.WriteLine("2.- Print LBA {0} sense buffer.", sectors + 5);
            AaruConsole.WriteLine("3.- Decode LBA {0} sense buffer.", sectors + 5);
            AaruConsole.WriteLine("4.- Print LBA {0} (scrambled) buffer.", sectors + 5);
            AaruConsole.WriteLine("5.- Print LBA {0} (scrambled) sense buffer.", sectors + 5);
            AaruConsole.WriteLine("6.- Decode LBA {0} (scrambled) sense buffer.", sectors + 5);
            AaruConsole.WriteLine("7.- Print LBA {0}'s PQ buffer.", sectors + 5);
            AaruConsole.WriteLine("8.- Print LBA {0}'s PQ sense buffer.", sectors + 5);
            AaruConsole.WriteLine("9.- Decode LBA {0}'s PQ sense buffer.", sectors + 5);
            AaruConsole.WriteLine("10.- Print LBA {0}'s RW buffer.", sectors + 5);
            AaruConsole.WriteLine("11.- Print LBA {0}'s RW sense buffer.", sectors + 5);
            AaruConsole.WriteLine("12.- Decode LBA {0}'s RW sense buffer.", sectors + 5);
            AaruConsole.WriteLine("13.- Send command again.");
            AaruConsole.WriteLine("0.- Return to special SCSI MultiMedia Commands menu.");
            AaruConsole.Write("Choose: ");

            strDev = System.Console.ReadLine();

            if (!int.TryParse(strDev, out item))
            {
                AaruConsole.WriteLine("Not a number. Press any key to continue...");
                System.Console.ReadKey();
                System.Console.Clear();

                goto menu;
            }

            switch (item)
            {
            case 0:
                AaruConsole.WriteLine("Returning to special SCSI MultiMedia Commands menu...");

                return;

            case 1:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA {0} response:", sectors + 5);

                if (buffer != null)
                {
                    PrintHex.PrintHexArray(dataBuffer, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 2:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA {0} sense:", sectors + 5);

                if (senseBuffer != null)
                {
                    PrintHex.PrintHexArray(dataSense, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 3:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA {0} decoded sense:", sectors + 5);
                AaruConsole.Write("{0}", Sense.PrettifySense(dataSense));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 4:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA {0} (scrambled) response:", sectors + 5);

                if (buffer != null)
                {
                    PrintHex.PrintHexArray(scrambledBuffer, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 5:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA {0} (scrambled) sense:", sectors + 5);

                if (senseBuffer != null)
                {
                    PrintHex.PrintHexArray(scrambledSense, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 6:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA {0} (scrambled) decoded sense:", sectors + 5);
                AaruConsole.Write("{0}", Sense.PrettifySense(scrambledSense));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 7:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA's PQ {0} response:", sectors + 5);

                if (buffer != null)
                {
                    PrintHex.PrintHexArray(pqBuffer, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 8:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA's PQ {0} sense:", sectors + 5);

                if (senseBuffer != null)
                {
                    PrintHex.PrintHexArray(pqSense, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 9:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA's PQ {0} decoded sense:", sectors + 5);
                AaruConsole.Write("{0}", Sense.PrettifySense(pqSense));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 10:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA's RW {0} response:", sectors + 5);

                if (buffer != null)
                {
                    PrintHex.PrintHexArray(rwBuffer, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 11:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA's RW {0} sense:", sectors + 5);

                if (senseBuffer != null)
                {
                    PrintHex.PrintHexArray(rwSense, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 12:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA's RW {0} decoded sense:", sectors + 5);
                AaruConsole.Write("{0}", Sense.PrettifySense(rwSense));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 13: goto start;

            default:
                AaruConsole.WriteLine("Incorrect option. Press any key to continue...");
                System.Console.ReadKey();
                System.Console.Clear();

                goto menu;
            }
        }
Beispiel #8
0
        /// <summary>
        ///     Fills a SCSI device report with parameters and media tests specific to a Streaming device
        /// </summary>
        /// <param name="dev">Device</param>
        /// <param name="report">Device report</param>
        /// <param name="debug">If debug is enabled</param>
        internal static void Report(Device dev, ref DeviceReport report, bool debug)
        {
            if (report == null)
            {
                return;
            }

            bool           sense;
            const uint     TIMEOUT = 5;
            ConsoleKeyInfo pressedKey;

            report.SCSI.SequentialDevice = new sscType();
            DicConsole.WriteLine("Querying SCSI READ BLOCK LIMITS...");
            sense = dev.ReadBlockLimits(out byte[] buffer, out byte[] senseBuffer, TIMEOUT, out _);
            if (!sense)
            {
                BlockLimits.BlockLimitsData?decBl = BlockLimits.Decode(buffer);
                if (decBl.HasValue)
                {
                    if (decBl.Value.granularity > 0)
                    {
                        report.SCSI.SequentialDevice.BlockSizeGranularitySpecified = true;
                        report.SCSI.SequentialDevice.BlockSizeGranularity          = decBl.Value.granularity;
                    }

                    if (decBl.Value.maxBlockLen > 0)
                    {
                        report.SCSI.SequentialDevice.MaxBlockLengthSpecified = true;
                        report.SCSI.SequentialDevice.MaxBlockLength          = decBl.Value.maxBlockLen;
                    }

                    if (decBl.Value.minBlockLen > 0)
                    {
                        report.SCSI.SequentialDevice.MinBlockLengthSpecified = true;
                        report.SCSI.SequentialDevice.MinBlockLength          = decBl.Value.minBlockLen;
                    }
                }
            }

            DicConsole.WriteLine("Querying SCSI REPORT DENSITY SUPPORT...");
            sense = dev.ReportDensitySupport(out buffer, out senseBuffer, false, false, TIMEOUT, out _);
            if (!sense)
            {
                DensitySupport.DensitySupportHeader?dsh = DensitySupport.DecodeDensity(buffer);
                if (dsh.HasValue)
                {
                    report.SCSI.SequentialDevice.SupportedDensities =
                        new SupportedDensity[dsh.Value.descriptors.Length];
                    for (int i = 0; i < dsh.Value.descriptors.Length; i++)
                    {
                        report.SCSI.SequentialDevice.SupportedDensities[i].BitsPerMm = dsh.Value.descriptors[i].bpmm;
                        report.SCSI.SequentialDevice.SupportedDensities[i].Capacity  =
                            dsh.Value.descriptors[i].capacity;
                        report.SCSI.SequentialDevice.SupportedDensities[i].DefaultDensity =
                            dsh.Value.descriptors[i].defaultDensity;
                        report.SCSI.SequentialDevice.SupportedDensities[i].Description =
                            dsh.Value.descriptors[i].description;
                        report.SCSI.SequentialDevice.SupportedDensities[i].Duplicate =
                            dsh.Value.descriptors[i].duplicate;
                        report.SCSI.SequentialDevice.SupportedDensities[i].Name         = dsh.Value.descriptors[i].name;
                        report.SCSI.SequentialDevice.SupportedDensities[i].Organization =
                            dsh.Value.descriptors[i].organization;
                        report.SCSI.SequentialDevice.SupportedDensities[i].PrimaryCode =
                            dsh.Value.descriptors[i].primaryCode;
                        report.SCSI.SequentialDevice.SupportedDensities[i].SecondaryCode =
                            dsh.Value.descriptors[i].secondaryCode;
                        report.SCSI.SequentialDevice.SupportedDensities[i].Tracks   = dsh.Value.descriptors[i].tracks;
                        report.SCSI.SequentialDevice.SupportedDensities[i].Width    = dsh.Value.descriptors[i].width;
                        report.SCSI.SequentialDevice.SupportedDensities[i].Writable = dsh.Value.descriptors[i].writable;
                    }
                }
            }

            DicConsole.WriteLine("Querying SCSI REPORT DENSITY SUPPORT for medium types...");
            sense = dev.ReportDensitySupport(out buffer, out senseBuffer, true, false, TIMEOUT, out _);
            if (!sense)
            {
                DensitySupport.MediaTypeSupportHeader?mtsh = DensitySupport.DecodeMediumType(buffer);
                if (mtsh.HasValue)
                {
                    report.SCSI.SequentialDevice.SupportedMediaTypes =
                        new SupportedMedia[mtsh.Value.descriptors.Length];
                    for (int i = 0; i < mtsh.Value.descriptors.Length; i++)
                    {
                        report.SCSI.SequentialDevice.SupportedMediaTypes[i].Description =
                            mtsh.Value.descriptors[i].description;
                        report.SCSI.SequentialDevice.SupportedMediaTypes[i].Length     = mtsh.Value.descriptors[i].length;
                        report.SCSI.SequentialDevice.SupportedMediaTypes[i].MediumType =
                            mtsh.Value.descriptors[i].mediumType;
                        report.SCSI.SequentialDevice.SupportedMediaTypes[i].Name         = mtsh.Value.descriptors[i].name;
                        report.SCSI.SequentialDevice.SupportedMediaTypes[i].Organization =
                            mtsh.Value.descriptors[i].organization;
                        report.SCSI.SequentialDevice.SupportedMediaTypes[i].Width = mtsh.Value.descriptors[i].width;
                        if (mtsh.Value.descriptors[i].densityCodes == null)
                        {
                            continue;
                        }

                        report.SCSI.SequentialDevice.SupportedMediaTypes[i].DensityCodes =
                            new int[mtsh.Value.descriptors[i].densityCodes.Length];
                        for (int j = 0; j < mtsh.Value.descriptors.Length; j++)
                        {
                            report.SCSI.SequentialDevice.SupportedMediaTypes[i].DensityCodes[j] =
                                mtsh.Value.descriptors[i].densityCodes[j];
                        }
                    }
                }
            }

            List <SequentialMedia> seqTests = new List <SequentialMedia>();

            pressedKey = new ConsoleKeyInfo();
            while (pressedKey.Key != ConsoleKey.N)
            {
                pressedKey = new ConsoleKeyInfo();
                while (pressedKey.Key != ConsoleKey.Y && pressedKey.Key != ConsoleKey.N)
                {
                    DicConsole.Write("Do you have media that you can insert in the drive? (Y/N): ");
                    pressedKey = System.Console.ReadKey();
                    DicConsole.WriteLine();
                }

                if (pressedKey.Key != ConsoleKey.Y)
                {
                    continue;
                }

                DicConsole.WriteLine("Please insert it in the drive and press any key when it is ready.");
                System.Console.ReadKey(true);

                SequentialMedia seqTest = new SequentialMedia();
                DicConsole.Write("Please write a description of the media type and press enter: ");
                seqTest.MediumTypeName = System.Console.ReadLine();
                DicConsole.Write("Please write the media manufacturer and press enter: ");
                seqTest.Manufacturer = System.Console.ReadLine();
                DicConsole.Write("Please write the media model and press enter: ");
                seqTest.Model = System.Console.ReadLine();

                seqTest.MediaIsRecognized = true;

                dev.Load(out senseBuffer, TIMEOUT, out _);
                sense = dev.ScsiTestUnitReady(out senseBuffer, TIMEOUT, out _);
                if (sense)
                {
                    FixedSense?decSense = Sense.DecodeFixed(senseBuffer);
                    if (decSense.HasValue)
                    {
                        if (decSense.Value.ASC == 0x3A)
                        {
                            int leftRetries = 20;
                            while (leftRetries > 0)
                            {
                                DicConsole.Write("\rWaiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuffer, TIMEOUT, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                leftRetries--;
                            }

                            seqTest.MediaIsRecognized &= !sense;
                        }
                        else if (decSense.Value.ASC == 0x04 && decSense.Value.ASCQ == 0x01)
                        {
                            int leftRetries = 20;
                            while (leftRetries > 0)
                            {
                                DicConsole.Write("\rWaiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuffer, TIMEOUT, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                leftRetries--;
                            }

                            seqTest.MediaIsRecognized &= !sense;
                        }
                        else
                        {
                            seqTest.MediaIsRecognized = false;
                        }
                    }
                    else
                    {
                        seqTest.MediaIsRecognized = false;
                    }
                }

                if (seqTest.MediaIsRecognized)
                {
                    Modes.DecodedMode?decMode = null;

                    DicConsole.WriteLine("Querying SCSI MODE SENSE (10)...");
                    sense = dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current,
                                            0x3F, 0x00, TIMEOUT, out _);
                    if (!sense && !dev.Error)
                    {
                        report.SCSI.SupportsModeSense10 = true;
                        decMode = Modes.DecodeMode10(buffer, dev.ScsiType);
                        if (debug)
                        {
                            seqTest.ModeSense10Data = buffer;
                        }
                    }

                    DicConsole.WriteLine("Querying SCSI MODE SENSE...");
                    sense = dev.ModeSense(out buffer, out senseBuffer, TIMEOUT, out _);
                    if (!sense && !dev.Error)
                    {
                        report.SCSI.SupportsModeSense6 = true;
                        if (!decMode.HasValue)
                        {
                            decMode = Modes.DecodeMode6(buffer, dev.ScsiType);
                        }
                        if (debug)
                        {
                            seqTest.ModeSense6Data = buffer;
                        }
                    }

                    if (decMode.HasValue)
                    {
                        seqTest.MediumType          = (byte)decMode.Value.Header.MediumType;
                        seqTest.MediumTypeSpecified = true;
                        if (decMode.Value.Header.BlockDescriptors != null &&
                            decMode.Value.Header.BlockDescriptors.Length > 0)
                        {
                            seqTest.Density          = (byte)decMode.Value.Header.BlockDescriptors[0].Density;
                            seqTest.DensitySpecified = true;
                        }
                    }
                }

                DicConsole.WriteLine("Querying SCSI REPORT DENSITY SUPPORT for current media...");
                sense = dev.ReportDensitySupport(out buffer, out senseBuffer, false, true, TIMEOUT, out _);
                if (!sense)
                {
                    DensitySupport.DensitySupportHeader?dsh = DensitySupport.DecodeDensity(buffer);
                    if (dsh.HasValue)
                    {
                        seqTest.SupportedDensities = new SupportedDensity[dsh.Value.descriptors.Length];
                        for (int i = 0; i < dsh.Value.descriptors.Length; i++)
                        {
                            seqTest.SupportedDensities[i].BitsPerMm      = dsh.Value.descriptors[i].bpmm;
                            seqTest.SupportedDensities[i].Capacity       = dsh.Value.descriptors[i].capacity;
                            seqTest.SupportedDensities[i].DefaultDensity = dsh.Value.descriptors[i].defaultDensity;
                            seqTest.SupportedDensities[i].Description    = dsh.Value.descriptors[i].description;
                            seqTest.SupportedDensities[i].Duplicate      = dsh.Value.descriptors[i].duplicate;
                            seqTest.SupportedDensities[i].Name           = dsh.Value.descriptors[i].name;
                            seqTest.SupportedDensities[i].Organization   = dsh.Value.descriptors[i].organization;
                            seqTest.SupportedDensities[i].PrimaryCode    = dsh.Value.descriptors[i].primaryCode;
                            seqTest.SupportedDensities[i].SecondaryCode  = dsh.Value.descriptors[i].secondaryCode;
                            seqTest.SupportedDensities[i].Tracks         = dsh.Value.descriptors[i].tracks;
                            seqTest.SupportedDensities[i].Width          = dsh.Value.descriptors[i].width;
                            seqTest.SupportedDensities[i].Writable       = dsh.Value.descriptors[i].writable;
                        }
                    }
                }

                DicConsole.WriteLine("Querying SCSI REPORT DENSITY SUPPORT for medium types for current media...");
                sense = dev.ReportDensitySupport(out buffer, out senseBuffer, true, true, TIMEOUT, out _);
                if (!sense)
                {
                    DensitySupport.MediaTypeSupportHeader?mtsh = DensitySupport.DecodeMediumType(buffer);
                    if (mtsh.HasValue)
                    {
                        seqTest.SupportedMediaTypes = new SupportedMedia[mtsh.Value.descriptors.Length];
                        for (int i = 0; i < mtsh.Value.descriptors.Length; i++)
                        {
                            seqTest.SupportedMediaTypes[i].Description  = mtsh.Value.descriptors[i].description;
                            seqTest.SupportedMediaTypes[i].Length       = mtsh.Value.descriptors[i].length;
                            seqTest.SupportedMediaTypes[i].MediumType   = mtsh.Value.descriptors[i].mediumType;
                            seqTest.SupportedMediaTypes[i].Name         = mtsh.Value.descriptors[i].name;
                            seqTest.SupportedMediaTypes[i].Organization = mtsh.Value.descriptors[i].organization;
                            seqTest.SupportedMediaTypes[i].Width        = mtsh.Value.descriptors[i].width;
                            if (mtsh.Value.descriptors[i].densityCodes == null)
                            {
                                continue;
                            }

                            seqTest.SupportedMediaTypes[i].DensityCodes =
                                new int[mtsh.Value.descriptors[i].densityCodes.Length];
                            for (int j = 0; j < mtsh.Value.descriptors.Length; j++)
                            {
                                seqTest.SupportedMediaTypes[i].DensityCodes[j] =
                                    mtsh.Value.descriptors[i].densityCodes[j];
                            }
                        }
                    }
                }

                seqTest.CanReadMediaSerialSpecified = true;
                DicConsole.WriteLine("Trying SCSI READ MEDIA SERIAL NUMBER...");
                seqTest.CanReadMediaSerial = !dev.ReadMediaSerialNumber(out buffer, out senseBuffer, TIMEOUT, out _);
                seqTests.Add(seqTest);
            }

            report.SCSI.SequentialDevice.TestedMedia = seqTests.ToArray();
        }
Beispiel #9
0
        public TestedMedia ReportScsiMedia()
        {
            TestedMedia mediaTest = new TestedMedia();

            DicConsole.WriteLine("Querying SCSI READ CAPACITY...");
            bool sense = dev.ReadCapacity(out byte[] buffer, out byte[] senseBuffer, dev.Timeout, out _);

            if (!sense && !dev.Error)
            {
                mediaTest.SupportsReadCapacity = true;
                mediaTest.Blocks =
                    (ulong)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]) + 1;
                mediaTest.BlockSize =
                    (uint)((buffer[4] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]);
            }

            DicConsole.WriteLine("Querying SCSI READ CAPACITY (16)...");
            sense = dev.ReadCapacity16(out buffer, out buffer, dev.Timeout, out _);
            if (!sense && !dev.Error)
            {
                mediaTest.SupportsReadCapacity16 = true;
                byte[] temp = new byte[8];
                Array.Copy(buffer, 0, temp, 0, 8);
                Array.Reverse(temp);
                mediaTest.Blocks    = BitConverter.ToUInt64(temp, 0) + 1;
                mediaTest.BlockSize = (uint)((buffer[8] << 24) + (buffer[9] << 16) + (buffer[10] << 8) + buffer[11]);
            }

            Modes.DecodedMode?decMode = null;

            DicConsole.WriteLine("Querying SCSI MODE SENSE (10)...");
            sense = dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current, 0x3F,
                                    0x00, dev.Timeout, out _);
            if (!sense && !dev.Error)
            {
                decMode = Modes.DecodeMode10(buffer, dev.ScsiType);
                if (debug)
                {
                    mediaTest.ModeSense10Data = buffer;
                }
            }

            DicConsole.WriteLine("Querying SCSI MODE SENSE...");
            sense = dev.ModeSense(out buffer, out senseBuffer, dev.Timeout, out _);
            if (!sense && !dev.Error)
            {
                if (!decMode.HasValue)
                {
                    decMode = Modes.DecodeMode6(buffer, dev.ScsiType);
                }
                if (debug)
                {
                    mediaTest.ModeSense6Data = buffer;
                }
            }

            if (decMode.HasValue)
            {
                mediaTest.MediumType = (byte)decMode.Value.Header.MediumType;
                if (decMode.Value.Header.BlockDescriptors != null && decMode.Value.Header.BlockDescriptors.Length > 0)
                {
                    mediaTest.Density = (byte)decMode.Value.Header.BlockDescriptors[0].Density;
                }
            }

            DicConsole.WriteLine("Trying SCSI READ (6)...");
            mediaTest.SupportsRead6 = !dev.Read6(out buffer, out senseBuffer, 0,
                                                 mediaTest.BlockSize ?? 512, dev.Timeout, out _);
            DicConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead6);
            if (debug)
            {
                mediaTest.Read6Data = buffer;
            }

            DicConsole.WriteLine("Trying SCSI READ (10)...");
            mediaTest.SupportsRead10 = !dev.Read10(out buffer, out senseBuffer, 0, false, true, false, false, 0,
                                                   mediaTest.BlockSize ?? 512, 0, 1, dev.Timeout, out _);
            DicConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead10);
            if (debug)
            {
                mediaTest.Read10Data = buffer;
            }

            DicConsole.WriteLine("Trying SCSI READ (12)...");
            mediaTest.SupportsRead12 = !dev.Read12(out buffer, out senseBuffer, 0, false, true, false, false, 0,
                                                   mediaTest.BlockSize ?? 512, 0, 1, false, dev.Timeout, out _);
            DicConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead12);
            if (debug)
            {
                mediaTest.Read12Data = buffer;
            }

            DicConsole.WriteLine("Trying SCSI READ (16)...");
            mediaTest.SupportsRead16 = !dev.Read16(out buffer, out senseBuffer, 0, false, true, false, 0,
                                                   mediaTest.BlockSize ?? 512, 0, 1, false, dev.Timeout, out _);
            DicConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead16);
            if (debug)
            {
                mediaTest.Read16Data = buffer;
            }

            mediaTest.LongBlockSize = mediaTest.BlockSize;
            DicConsole.WriteLine("Trying SCSI READ LONG (10)...");
            sense = dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 0xFFFF, dev.Timeout, out _);
            if (sense && !dev.Error)
            {
                FixedSense?decSense = Sense.DecodeFixed(senseBuffer);
                if (decSense.HasValue)
                {
                    if (decSense.Value.SenseKey == SenseKeys.IllegalRequest && decSense.Value.ASC == 0x24 &&
                        decSense.Value.ASCQ == 0x00)
                    {
                        mediaTest.SupportsReadLong = true;
                        if (decSense.Value.InformationValid && decSense.Value.ILI)
                        {
                            mediaTest.LongBlockSize = 0xFFFF - (decSense.Value.Information & 0xFFFF);
                        }
                    }
                }
            }

            if (mediaTest.SupportsReadLong == true && mediaTest.LongBlockSize == mediaTest.BlockSize)
            {
                if (mediaTest.BlockSize == 512)
                {
                    foreach (int i in new[]
                    {
                        // Long sector sizes for floppies
                        514,
                        // Long sector sizes for SuperDisk
                        536, 558,
                        // Long sector sizes for 512-byte magneto-opticals
                        600, 610, 630
                    })
                    {
                        ushort testSize = (ushort)i;
                        sense = dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, testSize, dev.Timeout,
                                               out _);
                        if (sense || dev.Error)
                        {
                            continue;
                        }

                        mediaTest.SupportsReadLong = true;
                        mediaTest.LongBlockSize    = testSize;
                        break;
                    }
                }
                else if (mediaTest.BlockSize == 1024)
                {
                    foreach (int i in new[]
                    {
                        // Long sector sizes for floppies
                        1026,
                        // Long sector sizes for 1024-byte magneto-opticals
                        1200
                    })
                    {
                        ushort testSize = (ushort)i;
                        sense = dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, (ushort)i, dev.Timeout,
                                               out _);
                        if (sense || dev.Error)
                        {
                            continue;
                        }

                        mediaTest.SupportsReadLong = true;
                        mediaTest.LongBlockSize    = testSize;
                        break;
                    }
                }
                else if (mediaTest.BlockSize == 2048)
                {
                    sense = dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 2380, dev.Timeout, out _);
                    if (!sense && !dev.Error)
                    {
                        mediaTest.SupportsReadLong = true;
                        mediaTest.LongBlockSize    = 2380;
                    }
                }
                else if (mediaTest.BlockSize == 4096)
                {
                    sense = dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 4760, dev.Timeout, out _);
                    if (!sense && !dev.Error)
                    {
                        mediaTest.SupportsReadLong = true;
                        mediaTest.LongBlockSize    = 4760;
                    }
                }
                else if (mediaTest.BlockSize == 8192)
                {
                    sense = dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 9424, dev.Timeout, out _);
                    if (!sense && !dev.Error)
                    {
                        mediaTest.SupportsReadLong = true;
                        mediaTest.LongBlockSize    = 9424;
                    }
                }
            }

            DicConsole.WriteLine("Trying SCSI READ MEDIA SERIAL NUMBER...");
            mediaTest.CanReadMediaSerial = !dev.ReadMediaSerialNumber(out buffer, out senseBuffer, dev.Timeout, out _);

            return(mediaTest);
        }
Beispiel #10
0
        // TODO: Get cartridge serial number from Certance vendor EVPD
        /// <summary>
        ///     Dumps a SCSI Block Commands device or a Reduced Block Commands devices
        /// </summary>
        /// <param name="dev">Device</param>
        /// <param name="devicePath">Path to the device</param>
        /// <param name="outputPrefix">Prefix for output data files</param>
        /// <param name="outputPlugin">Plugin for output file</param>
        /// <param name="retryPasses">How many times to retry</param>
        /// <param name="force">Force to continue dump whenever possible</param>
        /// <param name="dumpRaw">Dump long or scrambled sectors</param>
        /// <param name="persistent">Store whatever data the drive returned on error</param>
        /// <param name="stopOnError">Stop dump on first error</param>
        /// <param name="resume">Information for dump resuming</param>
        /// <param name="dumpLog">Dump logger</param>
        /// <param name="encoding">Encoding to use when analyzing dump</param>
        /// <param name="dumpLeadIn">Try to read and dump as much Lead-in as possible</param>
        /// <param name="outputPath">Path to output file</param>
        /// <param name="formatOptions">Formats to pass to output file plugin</param>
        /// <exception cref="ArgumentException">If you asked to dump long sectors from a SCSI Streaming device</exception>
        public static void Dump(Device dev, string devicePath,
                                IWritableImage outputPlugin, ushort retryPasses,
                                bool force, bool dumpRaw,
                                bool persistent, bool stopOnError, ref Resume resume,
                                ref DumpLog dumpLog, bool dumpLeadIn,
                                Encoding encoding, string outputPrefix,
                                string outputPath, Dictionary <string, string> formatOptions,
                                CICMMetadataType preSidecar, uint skip, bool nometadata,
                                bool notrim)
        {
            MediaType dskType = MediaType.Unknown;
            int       resets  = 0;

            if (dev.IsRemovable)
            {
deviceGotReset:
                bool sense = dev.ScsiTestUnitReady(out byte[] senseBuf, dev.Timeout, out _);
                if (sense)
                {
                    FixedSense?decSense = Sense.DecodeFixed(senseBuf);
                    if (decSense.HasValue)
                    {
                        dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                          decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);

                        // Just retry, for 5 times
                        if (decSense.Value.ASC == 0x29)
                        {
                            resets++;
                            if (resets < 5)
                            {
                                goto deviceGotReset;
                            }
                        }

                        if (decSense.Value.ASC == 0x3A)
                        {
                            int leftRetries = 5;
                            while (leftRetries > 0)
                            {
                                DicConsole.WriteLine("\rWaiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                decSense = Sense.DecodeFixed(senseBuf);
                                if (decSense.HasValue)
                                {
                                    dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                                      decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
                                }
                                leftRetries--;
                            }

                            if (sense)
                            {
                                DicConsole.ErrorWriteLine("Please insert media in drive");
                                return;
                            }
                        }
                        else if (decSense.Value.ASC == 0x04 && decSense.Value.ASCQ == 0x01)
                        {
                            int leftRetries = 10;
                            while (leftRetries > 0)
                            {
                                DicConsole.WriteLine("\rWaiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                decSense = Sense.DecodeFixed(senseBuf);
                                if (decSense.HasValue)
                                {
                                    dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                                      decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
                                }
                                leftRetries--;
                            }

                            if (sense)
                            {
                                DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}",
                                                          Sense.PrettifySense(senseBuf));
                                return;
                            }
                        }

                        /*else if (decSense.Value.ASC == 0x29 && decSense.Value.ASCQ == 0x00)
                         * {
                         *  if (!deviceReset)
                         *  {
                         *      deviceReset = true;
                         *      DicConsole.ErrorWriteLine("Device did reset, retrying...");
                         *      goto retryTestReady;
                         *  }
                         *
                         *  DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf));
                         *  return;
                         * }*/
                        // These should be trapped by the OS but seems in some cases they're not
                        else if (decSense.Value.ASC == 0x28)
                        {
                            int leftRetries = 10;
                            while (leftRetries > 0)
                            {
                                DicConsole.WriteLine("\rWaiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                decSense = Sense.DecodeFixed(senseBuf);
                                if (decSense.HasValue)
                                {
                                    dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
                                                      decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
                                }
                                leftRetries--;
                            }

                            if (sense)
                            {
                                DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}",
                                                          Sense.PrettifySense(senseBuf));
                                return;
                            }
                        }
                        else
                        {
                            DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}",
                                                      Sense.PrettifySense(senseBuf));
                            return;
                        }
                    }
                    else
                    {
                        DicConsole.ErrorWriteLine("Unknown testing unit was ready.");
                        return;
                    }
                }
            }

            switch (dev.ScsiType)
            {
            case PeripheralDeviceTypes.SequentialAccess:
                if (dumpRaw)
                {
                    throw new ArgumentException("Tapes cannot be dumped raw.");
                }

                Ssc.Dump(dev, outputPrefix, devicePath, ref resume, ref dumpLog, preSidecar);
                return;

            case PeripheralDeviceTypes.MultiMediaDevice:
                Mmc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError,
                         ref dskType, ref resume, ref dumpLog, dumpLeadIn, encoding, outputPrefix, outputPath,
                         formatOptions, preSidecar, skip, nometadata, notrim);
                return;

            default:
                Sbc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError, null,
                         ref dskType, false, ref resume, ref dumpLog, encoding, outputPrefix, outputPath,
                         formatOptions, preSidecar, skip, nometadata, notrim);
                break;
            }
        }
Beispiel #11
0
        public static ScanResults Scan(string mhddLogPath, string ibgLogPath, string devicePath, Device dev)
        {
            ScanResults results = new ScanResults();
            bool        aborted;
            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)
                {
                    FixedSense?decSense = Sense.DecodeFixed(senseBuf);
                    if (decSense.HasValue)
                    {
                        if (decSense.Value.ASC == 0x3A)
                        {
                            int leftRetries = 5;
                            while (leftRetries > 0)
                            {
                                DicConsole.WriteLine("\rWaiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                leftRetries--;
                            }

                            if (sense)
                            {
                                DicConsole.ErrorWriteLine("Please insert media in drive");
                                return(results);
                            }
                        }
                        else if (decSense.Value.ASC == 0x04 && decSense.Value.ASCQ == 0x01)
                        {
                            int leftRetries = 10;
                            while (leftRetries > 0)
                            {
                                DicConsole.WriteLine("\rWaiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                leftRetries--;
                            }

                            if (sense)
                            {
                                DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}",
                                                          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)
                            {
                                DicConsole.WriteLine("\rWaiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                leftRetries--;
                            }

                            if (sense)
                            {
                                DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}",
                                                          Sense.PrettifySense(senseBuf));
                                return(results);
                            }
                        }
                        else
                        {
                            DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}",
                                                      Sense.PrettifySense(senseBuf));
                            return(results);
                        }
                    }
                    else
                    {
                        DicConsole.ErrorWriteLine("Unknown testing unit was ready.");
                        return(results);
                    }
                }
            }

            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);
                results.Blocks = scsiReader.GetDeviceBlocks();
                if (scsiReader.FindReadCommand())
                {
                    DicConsole.ErrorWriteLine("Unable to read medium.");
                    return(results);
                }

                blockSize = scsiReader.LogicalBlockSize;

                if (results.Blocks != 0 && blockSize != 0)
                {
                    results.Blocks++;
                    DicConsole.WriteLine("Media has {0} blocks of {1} bytes/each. (for a total of {2} bytes)",
                                         results.Blocks, blockSize, results.Blocks * (ulong)blockSize);
                }

                break;

            case PeripheralDeviceTypes.SequentialAccess:
                DicConsole.WriteLine("Scanning will never be supported on SCSI Streaming Devices.");
                DicConsole.WriteLine("It has no sense to do it, and it will put too much strain on the tape.");
                return(results);
            }

            if (results.Blocks == 0)
            {
                DicConsole.ErrorWriteLine("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>();

            aborted = false;
            System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;

            if (compactDisc)
            {
                if (toc == null)
                {
                    DicConsole.ErrorWriteLine("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)
                {
                    DicConsole.WriteLine("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)
                {
                    DicConsole.ErrorWriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError);
                    return(results);
                }

                DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead);

                mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                ibgLog  = new IbgLog(ibgLogPath, currentProfile);

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

                    #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

                    DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, results.Blocks, currentSpeed);

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

                        mhddLog.Write(i, cmdDuration);
                        ibgLog.Write(i, currentSpeed * 1024);
                    }
                    else
                    {
                        DicConsole.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);
                                }

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

                                ibgLog.Write(i, 0);
                            }
                        }
                        else
                        {
                            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);
                        }
                    }

                    double newSpeed =
                        (double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000);
                    if (!double.IsInfinity(newSpeed))
                    {
                        currentSpeed = newSpeed;
                    }
                }

                end = DateTime.UtcNow;
                DicConsole.WriteLine();
                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;

                DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead);

                mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                ibgLog  = new IbgLog(ibgLogPath, currentProfile);

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

                    if (results.Blocks - i < blocksToRead)
                    {
                        blocksToRead = (uint)(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

                    DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, results.Blocks, currentSpeed);

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

                        mhddLog.Write(i, cmdDuration);
                        ibgLog.Write(i, currentSpeed * 1024);
                    }
                    // TODO: Separate errors on kind of errors.
                    else
                    {
                        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);
                    }

                    double newSpeed =
                        (double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000);
                    if (!double.IsInfinity(newSpeed))
                    {
                        currentSpeed = newSpeed;
                    }
                }

                end = DateTime.UtcNow;
                DicConsole.WriteLine();
                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 SEEK_TIMES = 1000;

            Random rnd = new Random();

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

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

                DicConsole.Write("\rSeeking to sector {0}...\t\t", seekPos);

                double seekCur;
                if (scsiReader.CanSeek)
                {
                    scsiReader.Seek(seekPos, out seekCur);
                }
                else
                {
                    scsiReader.ReadBlock(out _, 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();
            }

            DicConsole.WriteLine();

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

            return(results);
        }
Beispiel #12
0
        /// <summary>Register an SCSI error after trying to read</summary>
        /// <param name="block">Starting block</param>
        /// <param name="osError"><c>true</c> if operating system returned an error status instead of the device</param>
        /// <param name="errno">Operating system error number</param>
        /// <param name="senseBuffer">REQUEST SENSE response buffer</param>
        public void WriteLine(ulong block, bool osError, int errno, byte[] senseBuffer)
        {
            if (osError)
            {
                _logSw.WriteLine("SCSI reading LBA {0} operating system error: {1}.", block, errno);
                _logSw.Flush();

                if (senseBuffer is null ||
                    senseBuffer.Length == 0 ||
                    senseBuffer.All(s => s == 0))
                {
                    return;
                }
            }

            FixedSense?     decodedFixedSense      = Sense.DecodeFixed(senseBuffer);
            DescriptorSense?decodedDescriptorSense = Sense.DecodeDescriptor(senseBuffer);
            string          prettySense            = Sense.PrettifySense(senseBuffer);
            string          hexSense = string.Join(' ', senseBuffer.Select(b => $"{b:X2}"));

            if (decodedFixedSense.HasValue)
            {
                if (prettySense != null)
                {
                    if (prettySense.StartsWith("SCSI SENSE: ", StringComparison.Ordinal))
                    {
                        prettySense = prettySense.Substring(12);
                    }

                    if (prettySense.EndsWith('\n'))
                    {
                        prettySense = prettySense.Substring(0, prettySense.Length - 1);
                    }

                    prettySense = prettySense.Replace("\n", " - ");

                    _logSw.WriteLine("SCSI reading LBA {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}, {5}.", block,
                                     decodedFixedSense?.SenseKey, decodedFixedSense?.ASC, decodedFixedSense?.ASCQ,
                                     hexSense, prettySense);
                }
                else
                {
                    _logSw.WriteLine("SCSI reading LBA {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}.", block,
                                     decodedFixedSense?.SenseKey, decodedFixedSense?.ASC, decodedFixedSense?.ASCQ,
                                     hexSense);
                }
            }
            else if (decodedDescriptorSense.HasValue)
            {
                if (prettySense != null)
                {
                    if (prettySense.StartsWith("SCSI SENSE: ", StringComparison.Ordinal))
                    {
                        prettySense = prettySense.Substring(12);
                    }

                    if (prettySense.EndsWith('\n'))
                    {
                        prettySense = prettySense.Substring(0, prettySense.Length - 1);
                    }

                    prettySense = prettySense.Replace("\n", " - ");

                    _logSw.WriteLine("SCSI reading LBA {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}, {5}.", block,
                                     decodedDescriptorSense?.SenseKey, decodedDescriptorSense?.ASC,
                                     decodedDescriptorSense?.ASCQ, hexSense, prettySense);
                }
                else
                {
                    _logSw.WriteLine("SCSI reading LBA {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}.", block,
                                     decodedDescriptorSense?.SenseKey, decodedDescriptorSense?.ASC,
                                     decodedDescriptorSense?.ASCQ, hexSense);
                }
            }
            else
            {
                if (prettySense != null)
                {
                    if (prettySense.StartsWith("SCSI SENSE: ", StringComparison.Ordinal))
                    {
                        prettySense = prettySense.Substring(12);
                    }

                    if (prettySense.EndsWith('\n'))
                    {
                        prettySense = prettySense.Substring(0, prettySense.Length - 1);
                    }

                    prettySense = prettySense.Replace("\n", " - ");

                    _logSw.WriteLine("SCSI reading LBA {0} error: {1}, {2}.", block, hexSense, prettySense);
                }
                else
                {
                    _logSw.WriteLine("SCSI reading LBA {0} error: {1}", block, hexSense);
                }
            }

            _logSw.Flush();
        }
Beispiel #13
0
        static void CheckGdromReadability(string devPath, Device dev)
        {
            string strDev;
            int    item;
            bool   tocIsNotBcd = false;
            bool   sense;

            byte[] buffer;
            byte[] senseBuffer;
            int    retries;

start:
            System.Console.Clear();

            AaruConsole.WriteLine("Ejecting disc...");

            dev.AllowMediumRemoval(out _, dev.Timeout, out _);
            dev.EjectTray(out _, dev.Timeout, out _);

            AaruConsole.WriteLine("Please insert trap disc inside...");
            AaruConsole.WriteLine("Press any key to continue...");
            System.Console.ReadLine();

            AaruConsole.WriteLine("Sending READ FULL TOC to the device...");

            retries = 0;

            do
            {
                retries++;
                sense = dev.ScsiTestUnitReady(out senseBuffer, dev.Timeout, out _);

                if (!sense)
                {
                    break;
                }

                FixedSense?decodedSense = Sense.DecodeFixed(senseBuffer);

                if (decodedSense.Value.ASC != 0x04)
                {
                    break;
                }

                if (decodedSense.Value.ASCQ != 0x01)
                {
                    break;
                }

                Thread.Sleep(2000);
            } while(retries < 25);

            sense = dev.ReadRawToc(out buffer, out senseBuffer, 1, dev.Timeout, out _);

            if (sense)
            {
                AaruConsole.WriteLine("READ FULL TOC failed...");
                AaruConsole.WriteLine("{0}", Sense.PrettifySense(senseBuffer));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            FullTOC.CDFullTOC?decodedToc = FullTOC.Decode(buffer);

            if (decodedToc is null)
            {
                AaruConsole.WriteLine("Could not decode TOC...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            FullTOC.CDFullTOC toc = decodedToc.Value;

            FullTOC.TrackDataDescriptor leadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2);

            if (leadOutTrack.POINT != 0xA2)
            {
                AaruConsole.WriteLine("Cannot find lead-out...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            int min = 0, sec, frame;

            if (leadOutTrack.PMIN == 122)
            {
                tocIsNotBcd = true;
            }

            if (leadOutTrack.PMIN >= 0xA0 &&
                !tocIsNotBcd)
            {
                min += 90;
                leadOutTrack.PMIN -= 0x90;
            }

            if (tocIsNotBcd)
            {
                min   = leadOutTrack.PMIN;
                sec   = leadOutTrack.PSEC;
                frame = leadOutTrack.PFRAME;
            }
            else
            {
                min  += ((leadOutTrack.PMIN >> 4) * 10) + (leadOutTrack.PMIN & 0x0F);
                sec   = ((leadOutTrack.PSEC >> 4) * 10) + (leadOutTrack.PSEC & 0x0F);
                frame = ((leadOutTrack.PFRAME >> 4) * 10) + (leadOutTrack.PFRAME & 0x0F);
            }

            int sectors = ((min * 60 * 75) + (sec * 75) + frame) - 150;

            AaruConsole.WriteLine("Trap disc shows {0} sectors...", sectors);

            if (sectors < 450000)
            {
                AaruConsole.WriteLine("Trap disc doesn't have enough sectors...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            AaruConsole.WriteLine("Stopping motor...");

            dev.StopUnit(out _, dev.Timeout, out _);

            AaruConsole.WriteLine("Please MANUALLY get the trap disc out and put the GD-ROM disc inside...");
            AaruConsole.WriteLine("Press any key to continue...");
            System.Console.ReadLine();

            AaruConsole.WriteLine("Waiting 5 seconds...");
            Thread.Sleep(5000);

            AaruConsole.WriteLine("Sending READ FULL TOC to the device...");

            retries = 0;

            do
            {
                retries++;
                sense = dev.ReadRawToc(out buffer, out senseBuffer, 1, dev.Timeout, out _);

                if (!sense)
                {
                    break;
                }

                FixedSense?decodedSense = Sense.DecodeFixed(senseBuffer);

                if (decodedSense.Value.ASC != 0x04)
                {
                    break;
                }

                if (decodedSense.Value.ASCQ != 0x01)
                {
                    break;
                }
            } while(retries < 25);

            if (sense)
            {
                AaruConsole.WriteLine("READ FULL TOC failed...");
                AaruConsole.WriteLine("{0}", Sense.PrettifySense(senseBuffer));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            decodedToc = FullTOC.Decode(buffer);

            if (decodedToc is null)
            {
                AaruConsole.WriteLine("Could not decode TOC...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            toc = decodedToc.Value;

            FullTOC.TrackDataDescriptor newLeadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2);

            if (newLeadOutTrack.POINT != 0xA2)
            {
                AaruConsole.WriteLine("Cannot find lead-out...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            if (newLeadOutTrack.PMIN >= 0xA0 &&
                !tocIsNotBcd)
            {
                newLeadOutTrack.PMIN -= 0x90;
            }

            if (newLeadOutTrack.PMIN != leadOutTrack.PMIN ||
                newLeadOutTrack.PSEC != leadOutTrack.PSEC ||
                newLeadOutTrack.PFRAME != leadOutTrack.PFRAME)
            {
                AaruConsole.WriteLine("Lead-out has changed, this drive does not support hot swapping discs...");
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadLine();

                return;
            }

            dev.SetCdSpeed(out _, RotationalControl.PureCav, 170, 0, dev.Timeout, out _);

            AaruConsole.Write("Reading LBA 0... ");

            bool lba0Result = dev.ReadCd(out byte[] lba0Buffer, out byte[] lba0Sense, 0, 2352, 1,
                                         MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
                                         true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);

            AaruConsole.WriteLine(lba0Result ? "FAIL!" : "Success!");

            AaruConsole.Write("Reading LBA 0 as audio (scrambled)... ");

            bool lba0ScrambledResult = dev.ReadCd(out byte[] lba0ScrambledBuffer, out byte[] lba0ScrambledSense, 0,
                                                  2352, 1, MmcSectorTypes.Cdda, false, false, false,
                                                  MmcHeaderCodes.None, true, false, MmcErrorField.None,
                                                  MmcSubchannel.None, dev.Timeout, out _);

            AaruConsole.WriteLine(lba0ScrambledResult ? "FAIL!" : "Success!");

            AaruConsole.Write("Reading LBA 100000... ");

            bool lba100000Result = dev.ReadCd(out byte[] lba100000Buffer, out byte[] lba100000Sense, 100000, 2352, 1,
                                              MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
                                              true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);

            AaruConsole.WriteLine(lba100000Result ? "FAIL!" : "Success!");

            AaruConsole.Write("Reading LBA 50000... ");

            bool lba50000Result = dev.ReadCd(out byte[] lba50000Buffer, out byte[] lba50000Sense, 50000, 2352, 1,
                                             MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
                                             true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);

            AaruConsole.WriteLine(lba50000Result ? "FAIL!" : "Success!");

            AaruConsole.Write("Reading LBA 450000... ");

            bool lba450000Result = dev.ReadCd(out byte[] lba450000Buffer, out byte[] lba450000Sense, 450000, 2352, 1,
                                              MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
                                              true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);

            AaruConsole.WriteLine(lba450000Result ? "FAIL!" : "Success!");

            AaruConsole.Write("Reading LBA 400000... ");

            bool lba400000Result = dev.ReadCd(out byte[] lba400000Buffer, out byte[] lba400000Sense, 400000, 2352, 1,
                                              MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
                                              true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);

            AaruConsole.WriteLine(lba400000Result ? "FAIL!" : "Success!");

            AaruConsole.Write("Reading LBA 45000... ");

            bool lba45000Result = dev.ReadCd(out byte[] lba45000Buffer, out byte[] lba45000Sense, 45000, 2352, 1,
                                             MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
                                             true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);

            AaruConsole.WriteLine(lba45000Result ? "FAIL!" : "Success!");

            AaruConsole.Write("Reading LBA 44990... ");

            bool lba44990Result = dev.ReadCd(out byte[] lba44990Buffer, out byte[] lba44990Sense, 44990, 2352, 1,
                                             MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
                                             true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);

            AaruConsole.WriteLine(lba44990Result ? "FAIL!" : "Success!");

menu:
            System.Console.Clear();
            AaruConsole.WriteLine("Device: {0}", devPath);
            AaruConsole.WriteLine("Device {0} read HD area.", lba450000Result ? "cannot" : "can");

            AaruConsole.WriteLine("LBA 0 sense is {0}, buffer is {1}, sense buffer is {2}.", lba0Result,
                                  lba0Buffer is null
                                      ? "null"
                                      : ArrayHelpers.ArrayIsNullOrEmpty(lba0Buffer)
                                          ? "empty"
                                          : $"{lba0Buffer.Length} bytes", lba0Sense is null
                                                                              ? "null"
                                                                              : ArrayHelpers.
                                  ArrayIsNullOrEmpty(lba0Sense)
                                                                                  ? "empty"
                                                                                  : $"{lba0Sense.Length}");

            AaruConsole.WriteLine("LBA 0 (scrambled) sense is {0}, buffer is {1}, sense buffer is {2}.",
                                  lba0ScrambledResult, lba0ScrambledBuffer is null
                                                           ? "null"
                                                           : ArrayHelpers.ArrayIsNullOrEmpty(lba0ScrambledBuffer)
                                                               ? "empty"
                                                               : $"{lba0ScrambledBuffer.Length} bytes",
                                  lba0ScrambledSense is null
                                      ? "null"
                                      : ArrayHelpers.ArrayIsNullOrEmpty(lba0ScrambledSense)
                                          ? "empty"
                                          : $"{lba0ScrambledSense.Length}");

            AaruConsole.WriteLine("LBA 44990 sense is {0}, buffer is {1}, sense buffer is {2}.", lba44990Result,
                                  lba44990Buffer is null
                                      ? "null"
                                      : ArrayHelpers.ArrayIsNullOrEmpty(lba44990Buffer)
                                          ? "empty"
                                          : $"{lba44990Buffer.Length} bytes", lba44990Sense is null
                                                                                  ? "null"
                                                                                  : ArrayHelpers.
                                  ArrayIsNullOrEmpty(lba44990Sense)
                                                                                      ? "empty"
                                                                                      : $"{lba44990Sense.Length}");

            AaruConsole.WriteLine("LBA 45000 sense is {0}, buffer is {1}, sense buffer is {2}.", lba45000Result,
                                  lba45000Buffer is null
                                      ? "null"
                                      : ArrayHelpers.ArrayIsNullOrEmpty(lba45000Buffer)
                                          ? "empty"
                                          : $"{lba45000Buffer.Length} bytes", lba45000Sense is null
                                                                                  ? "null"
                                                                                  : ArrayHelpers.
                                  ArrayIsNullOrEmpty(lba45000Sense)
                                                                                      ? "empty"
                                                                                      : $"{lba45000Sense.Length}");

            AaruConsole.WriteLine("LBA 50000 sense is {0}, buffer is {1}, sense buffer is {2}.", lba50000Result,
                                  lba50000Buffer is null
                                      ? "null"
                                      : ArrayHelpers.ArrayIsNullOrEmpty(lba50000Buffer)
                                          ? "empty"
                                          : $"{lba50000Buffer.Length} bytes", lba50000Sense is null
                                                                                  ? "null"
                                                                                  : ArrayHelpers.
                                  ArrayIsNullOrEmpty(lba50000Sense)
                                                                                      ? "empty"
                                                                                      : $"{lba50000Sense.Length}");

            AaruConsole.WriteLine("LBA 100000 sense is {0}, buffer is {1}, sense buffer is {2}.", lba100000Result,
                                  lba100000Buffer is null
                                      ? "null"
                                      : ArrayHelpers.ArrayIsNullOrEmpty(lba100000Buffer)
                                          ? "empty"
                                          : $"{lba100000Buffer.Length} bytes", lba100000Sense is null
                                                                                   ? "null"
                                                                                   : ArrayHelpers.
                                  ArrayIsNullOrEmpty(lba100000Sense)
                                                                                       ? "empty"
                                                                                       : $"{lba100000Sense.Length}");

            AaruConsole.WriteLine("LBA 400000 sense is {0}, buffer is {1}, sense buffer is {2}.", lba400000Result,
                                  lba400000Buffer is null
                                      ? "null"
                                      : ArrayHelpers.ArrayIsNullOrEmpty(lba400000Buffer)
                                          ? "empty"
                                          : $"{lba400000Buffer.Length} bytes", lba400000Sense is null
                                                                                   ? "null"
                                                                                   : ArrayHelpers.
                                  ArrayIsNullOrEmpty(lba400000Sense)
                                                                                       ? "empty"
                                                                                       : $"{lba400000Sense.Length}");

            AaruConsole.WriteLine("LBA 450000 sense is {0}, buffer is {1}, sense buffer is {2}.", lba450000Result,
                                  lba450000Buffer is null
                                      ? "null"
                                      : ArrayHelpers.ArrayIsNullOrEmpty(lba450000Buffer)
                                          ? "empty"
                                          : $"{lba450000Buffer.Length} bytes", lba450000Sense is null
                                                                                   ? "null"
                                                                                   : ArrayHelpers.
                                  ArrayIsNullOrEmpty(lba450000Sense)
                                                                                       ? "empty"
                                                                                       : $"{lba450000Sense.Length}");

            AaruConsole.WriteLine();
            AaruConsole.WriteLine("Choose what to do:");
            AaruConsole.WriteLine("1.- Print LBA 0 buffer.");
            AaruConsole.WriteLine("2.- Print LBA 0 sense buffer.");
            AaruConsole.WriteLine("3.- Decode LBA 0 sense buffer.");
            AaruConsole.WriteLine("4.- Print LBA 0 (scrambled) buffer.");
            AaruConsole.WriteLine("5.- Print LBA 0 (scrambled) sense buffer.");
            AaruConsole.WriteLine("6.- Decode LBA 0 (scrambled) sense buffer.");
            AaruConsole.WriteLine("7.- Print LBA 44990 buffer.");
            AaruConsole.WriteLine("8.- Print LBA 44990 sense buffer.");
            AaruConsole.WriteLine("9.- Decode LBA 44990 sense buffer.");
            AaruConsole.WriteLine("10.- Print LBA 45000 buffer.");
            AaruConsole.WriteLine("11.- Print LBA 45000 sense buffer.");
            AaruConsole.WriteLine("12.- Decode LBA 45000 sense buffer.");
            AaruConsole.WriteLine("13.- Print LBA 50000 buffer.");
            AaruConsole.WriteLine("14.- Print LBA 50000 sense buffer.");
            AaruConsole.WriteLine("15.- Decode LBA 50000 sense buffer.");
            AaruConsole.WriteLine("16.- Print LBA 100000 buffer.");
            AaruConsole.WriteLine("17.- Print LBA 100000 sense buffer.");
            AaruConsole.WriteLine("18.- Decode LBA 100000 sense buffer.");
            AaruConsole.WriteLine("19.- Print LBA 400000 buffer.");
            AaruConsole.WriteLine("20.- Print LBA 400000 sense buffer.");
            AaruConsole.WriteLine("21.- Decode LBA 400000 sense buffer.");
            AaruConsole.WriteLine("22.- Print LBA 450000 buffer.");
            AaruConsole.WriteLine("23.- Print LBA 450000 sense buffer.");
            AaruConsole.WriteLine("24.- Decode LBA 450000 sense buffer.");
            AaruConsole.WriteLine("25.- Send command again.");
            AaruConsole.WriteLine("0.- Return to special SCSI MultiMedia Commands menu.");
            AaruConsole.Write("Choose: ");

            strDev = System.Console.ReadLine();

            if (!int.TryParse(strDev, out item))
            {
                AaruConsole.WriteLine("Not a number. Press any key to continue...");
                System.Console.ReadKey();
                System.Console.Clear();

                goto menu;
            }

            switch (item)
            {
            case 0:
                AaruConsole.WriteLine("Returning to special SCSI MultiMedia Commands menu...");

                return;

            case 1:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 0 response:");

                if (buffer != null)
                {
                    PrintHex.PrintHexArray(lba0Buffer, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 2:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 0 sense:");

                if (senseBuffer != null)
                {
                    PrintHex.PrintHexArray(lba0Sense, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 3:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 0 decoded sense:");
                AaruConsole.Write("{0}", Sense.PrettifySense(lba0Sense));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 4:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 0 (scrambled) response:");

                if (buffer != null)
                {
                    PrintHex.PrintHexArray(lba0ScrambledBuffer, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 5:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 0 (scrambled) sense:");

                if (senseBuffer != null)
                {
                    PrintHex.PrintHexArray(lba0ScrambledSense, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 6:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 0 (scrambled) decoded sense:");
                AaruConsole.Write("{0}", Sense.PrettifySense(lba0ScrambledSense));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 7:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 44990 response:");

                if (buffer != null)
                {
                    PrintHex.PrintHexArray(lba44990Buffer, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 8:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 44990 sense:");

                if (senseBuffer != null)
                {
                    PrintHex.PrintHexArray(lba44990Sense, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 9:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 44990 decoded sense:");
                AaruConsole.Write("{0}", Sense.PrettifySense(lba44990Sense));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 10:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 45000 response:");

                if (buffer != null)
                {
                    PrintHex.PrintHexArray(lba45000Buffer, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 11:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 45000 sense:");

                if (senseBuffer != null)
                {
                    PrintHex.PrintHexArray(lba45000Sense, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 12:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 45000 decoded sense:");
                AaruConsole.Write("{0}", Sense.PrettifySense(lba45000Sense));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 13:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 50000 response:");

                if (buffer != null)
                {
                    PrintHex.PrintHexArray(lba50000Buffer, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 14:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 50000 sense:");

                if (senseBuffer != null)
                {
                    PrintHex.PrintHexArray(lba50000Sense, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 15:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 50000 decoded sense:");
                AaruConsole.Write("{0}", Sense.PrettifySense(lba50000Sense));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 16:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 100000 response:");

                if (buffer != null)
                {
                    PrintHex.PrintHexArray(lba100000Buffer, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 17:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 100000 sense:");

                if (senseBuffer != null)
                {
                    PrintHex.PrintHexArray(lba100000Sense, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 18:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 100000 decoded sense:");
                AaruConsole.Write("{0}", Sense.PrettifySense(lba100000Sense));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 19:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 400000 response:");

                if (buffer != null)
                {
                    PrintHex.PrintHexArray(lba400000Buffer, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 20:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 400000 sense:");

                if (senseBuffer != null)
                {
                    PrintHex.PrintHexArray(lba400000Sense, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 21:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 400000 decoded sense:");
                AaruConsole.Write("{0}", Sense.PrettifySense(lba400000Sense));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 22:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 450000 response:");

                if (buffer != null)
                {
                    PrintHex.PrintHexArray(lba450000Buffer, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 23:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 450000 sense:");

                if (senseBuffer != null)
                {
                    PrintHex.PrintHexArray(lba450000Sense, 64);
                }

                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 24:
                System.Console.Clear();
                AaruConsole.WriteLine("Device: {0}", devPath);
                AaruConsole.WriteLine("LBA 450000 decoded sense:");
                AaruConsole.Write("{0}", Sense.PrettifySense(lba450000Sense));
                AaruConsole.WriteLine("Press any key to continue...");
                System.Console.ReadKey();

                goto menu;

            case 25: goto start;

            default:
                AaruConsole.WriteLine("Incorrect option. Press any key to continue...");
                System.Console.ReadKey();
                System.Console.Clear();

                goto menu;
            }
        }