Beispiel #1
0
        /// <summary>Dumps an optical disc</summary>
        void Mmc()
        {
            MediaType dskType = MediaType.Unknown;
            bool      sense;

            byte[]        tmpBuf;
            bool          compactDisc      = true;
            bool          gotConfiguration = false;
            bool          isXbox           = false;
            DVDDecryption dvdDecrypt       = null;

            _speedMultiplier = 1;

            // TODO: Log not only what is it reading, but if it was read correctly or not.
            sense = _dev.GetConfiguration(out byte[] cmdBuf, out _, 0, MmcGetConfigurationRt.Current, _dev.Timeout,
                                          out _);

            if (!sense)
            {
                gotConfiguration = true;
                Features.SeparatedFeatures ftr = Features.Separate(cmdBuf);
                _dumpLog.WriteLine("Device reports current profile is 0x{0:X4}", ftr.CurrentProfile);

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

                case 0x0002:
                    dskType          = MediaType.PD650;
                    _speedMultiplier = -1;
                    goto default;

                case 0x0005:
                    dskType = MediaType.CDMO;

                    break;

                case 0x0008:
                    dskType = MediaType.CD;

                    break;

                case 0x0009:
                    dskType = MediaType.CDR;

                    break;

                case 0x000A:
                    dskType = MediaType.CDRW;

                    break;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                default:
                    compactDisc = false;

                    break;
                }
            }

            Modes.DecodedMode?decMode = null;

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

            if (sense || _dev.Error)
            {
                sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x00, _dev.Timeout,
                                        out _);

                if (!sense &&
                    !_dev.Error)
                {
                    decMode = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);
                }
            }
            else
            {
                decMode = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);
            }

            if (decMode is null)
            {
                sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00,
                                         _dev.Timeout, out _);

                if (sense || _dev.Error)
                {
                    sense = _dev.ModeSense10(out cmdBuf, out _, false, false, ScsiModeSensePageControl.Current, 0x3F,
                                             0x00, _dev.Timeout, out _);

                    if (sense || _dev.Error)
                    {
                        sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x00,
                                                 0x00, _dev.Timeout, out _);

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

                            if (!sense &&
                                !_dev.Error)
                            {
                                decMode = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);
                            }
                        }
                        else
                        {
                            decMode = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);
                        }
                    }
                    else
                    {
                        decMode = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);
                    }
                }
                else
                {
                    decMode = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);
                }
            }

            if (decMode.HasValue &&
                _dev.IsUsb &&
                !gotConfiguration &&
                (decMode.Value.Header.MediumType == MediumTypes.UnknownBlockDevice ||
                 decMode.Value.Header.MediumType == MediumTypes.ReadOnlyBlockDevice ||
                 decMode.Value.Header.MediumType == MediumTypes.ReadWriteBlockDevice))
            {
                _speedMultiplier = -1;
                Sbc(null, MediaType.Unknown, false);

                return;
            }

            if (compactDisc)
            {
                _speedMultiplier *= 177;
                CompactDisc();

                return;
            }

            _speedMultiplier *= 150;

            var   scsiReader = new Reader(_dev, _dev.Timeout, null, _errorLog, _dumpRaw);
            ulong blocks     = scsiReader.GetDeviceBlocks();

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

            if (dskType == MediaType.PD650)
            {
                switch (blocks + 1)
                {
                case 1281856:
                    dskType = MediaType.PD650_WORM;

                    break;

                case 58620544:
                    dskType = MediaType.REV120;

                    break;

                case 17090880:
                    dskType = MediaType.REV35;

                    break;

                case 34185728:
                    dskType = MediaType.REV70;

                    break;
                }
            }

            #region Nintendo
            switch (dskType)
            {
            case MediaType.Unknown when blocks > 0:
                _dumpLog.WriteLine("Reading Physical Format Information");

                sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                               MmcDiscStructureFormat.PhysicalInformation, 0, _dev.Timeout, out _);

                if (!sense)
                {
                    PFI.PhysicalFormatInformation?nintendoPfi = PFI.Decode(cmdBuf);

                    if (nintendoPfi?.DiskCategory == DiskCategory.Nintendo &&
                        nintendoPfi.Value.PartVersion == 15)
                    {
                        _dumpLog.WriteLine("Dumping Nintendo GameCube or Wii discs is not yet implemented.");

                        StoppingErrorMessage?.
                        Invoke("Dumping Nintendo GameCube or Wii discs is not yet implemented.");

                        return;
                    }
                }

                break;

            case MediaType.DVDDownload:
            case MediaType.DVDPR:
            case MediaType.DVDPRDL:
            case MediaType.DVDPRW:
            case MediaType.DVDPRWDL:
            case MediaType.DVDR:
            case MediaType.DVDRAM:
            case MediaType.DVDRDL:
            case MediaType.DVDROM:
            case MediaType.DVDRW:
            case MediaType.DVDRWDL:
            case MediaType.HDDVDR:
            case MediaType.HDDVDRAM:
            case MediaType.HDDVDRDL:
            case MediaType.HDDVDROM:
            case MediaType.HDDVDRW:
            case MediaType.HDDVDRWDL:
                _dumpLog.WriteLine("Reading Physical Format Information");

                sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                               MmcDiscStructureFormat.PhysicalInformation, 0, _dev.Timeout, out _);

                if (!sense)
                {
                    if (PFI.Decode(cmdBuf).HasValue)
                    {
                        tmpBuf = new byte[cmdBuf.Length - 4];
                        Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                        mediaTags.Add(MediaTagType.DVD_PFI, tmpBuf);

                        PFI.PhysicalFormatInformation decPfi = PFI.Decode(cmdBuf).Value;
                        UpdateStatus?.Invoke($"PFI:\n{PFI.Prettify(decPfi)}");

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

                            break;

                        case DiskCategory.DVDPRDL:
                            dskType = MediaType.DVDPRDL;

                            break;

                        case DiskCategory.DVDPRW:
                            dskType = MediaType.DVDPRW;

                            break;

                        case DiskCategory.DVDPRWDL:
                            dskType = MediaType.DVDPRWDL;

                            break;

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

                            break;

                        case DiskCategory.DVDRAM:
                            dskType = MediaType.DVDRAM;

                            break;

                        default:
                            dskType = MediaType.DVDROM;

                            break;

                        case DiskCategory.DVDRW:
                            dskType = decPfi.PartVersion >= 15 ? MediaType.DVDRWDL : MediaType.DVDRW;

                            break;

                        case DiskCategory.HDDVDR:
                            dskType = MediaType.HDDVDR;

                            break;

                        case DiskCategory.HDDVDRAM:
                            dskType = MediaType.HDDVDRAM;

                            break;

                        case DiskCategory.HDDVDROM:
                            dskType = MediaType.HDDVDROM;

                            break;

                        case DiskCategory.HDDVDRW:
                            dskType = MediaType.HDDVDRW;

                            break;

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

                            break;

                        case DiskCategory.UMD:
                            dskType = MediaType.UMD;

                            break;
                        }
                    }
                }

                _dumpLog.WriteLine("Reading Disc Manufacturing Information");

                sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                               MmcDiscStructureFormat.DiscManufacturingInformation, 0, _dev.Timeout,
                                               out _);

                if (!sense)
                {
                    if (DMI.IsXbox(cmdBuf) ||
                        DMI.IsXbox360(cmdBuf))
                    {
                        if (DMI.IsXbox(cmdBuf))
                        {
                            dskType = MediaType.XGD;
                        }
                        else if (DMI.IsXbox360(cmdBuf))
                        {
                            dskType = MediaType.XGD2;

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

                        isXbox = true;

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

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

                            StoppingErrorMessage?.
                            Invoke("Dumping Xbox Game Discs requires a drive with Kreon firmware.");

                            if (!_force)
                            {
                                return;
                            }

                            isXbox = false;
                        }

                        if (_dumpRaw && !_force)
                        {
                            StoppingErrorMessage?.
                            Invoke("Not continuing. If you want to continue reading cooked data when raw is not available use the force option.");

                            // TODO: Exit more gracefully
                            return;
                        }
                    }

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

                break;
            }
            #endregion Nintendo

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

            #region DVD-ROM
            if (dskType == MediaType.DVDDownload ||
                dskType == MediaType.DVDROM)
            {
                _dumpLog.WriteLine("Reading Lead-in Copyright Information.");

                sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
                                               MmcDiscStructureFormat.CopyrightInformation, 0, _dev.Timeout, out _);

                if (!sense)
                {
                    if (CSS_CPRM.DecodeLeadInCopyright(cmdBuf).HasValue)
                    {
                        tmpBuf = new byte[cmdBuf.Length - 4];
                        Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
                        mediaTags.Add(MediaTagType.DVD_CMI, tmpBuf);

                        CSS_CPRM.LeadInCopyright?cmi = CSS_CPRM.DecodeLeadInCopyright(cmdBuf);

                        if (cmi !.Value.CopyrightType == CopyrightType.NoProtection)
                        {
                            UpdateStatus?.Invoke("Drive reports no copy protection on disc.");
                        }
                        else
                        {
                            if (!Settings.Settings.Current.EnableDecryption)
                            {
                                UpdateStatus?.Invoke("Drive reports the disc uses copy protection. " +
                                                     "The dump will be incorrect unless decryption is enabled.");
                            }
                            else
                            {
                                if (cmi !.Value.CopyrightType == CopyrightType.CSS)
                                {
                                    UpdateStatus?.Invoke("Drive reports disc uses CSS copy protection.");

                                    dvdDecrypt = new DVDDecryption(_dev);

                                    sense = dvdDecrypt.ReadBusKey(out cmdBuf, out _,
                                                                  CSS_CPRM.DecodeLeadInCopyright(cmdBuf) !.Value.
                                                                  CopyrightType, _dev.Timeout, out _);

                                    if (!sense)
                                    {
                                        byte[] busKey = cmdBuf;

                                        UpdateStatus?.Invoke("Reading disc key.");
                                        sense = dvdDecrypt.ReadDiscKey(out cmdBuf, out _, _dev.Timeout, out _);

                                        if (!sense)
                                        {
                                            CSS_CPRM.DiscKey?decodedDiscKey = CSS.DecodeDiscKey(cmdBuf, busKey);

                                            sense = dvdDecrypt.ReadAsf(out cmdBuf, out _,
                                                                       DvdCssKeyClass.DvdCssCppmOrCprm, _dev.Timeout,
                                                                       out _);

                                            if (!sense)
                                            {
                                                if (cmdBuf[7] == 1)
                                                {
                                                    UpdateStatus?.Invoke("Disc and drive authentication succeeded.");

                                                    sense = dvdDecrypt.ReadRpc(out cmdBuf, out _,
                                                                               DvdCssKeyClass.DvdCssCppmOrCprm,
                                                                               _dev.Timeout, out _);

                                                    if (!sense)
                                                    {
                                                        CSS_CPRM.RegionalPlaybackControlState?rpc =
                                                            CSS_CPRM.DecodeRegionalPlaybackControlState(cmdBuf);

                                                        if (rpc.HasValue)
                                                        {
                                                            UpdateStatus?.Invoke(CSS.CheckRegion(rpc.Value, cmi.Value)
                                                                ? "Disc and drive regions match."
                                                                : "Disc and drive regions do not match. The dump will be incorrect");
                                                        }
                                                    }

                                                    if (decodedDiscKey.HasValue)
                                                    {
                                                        mediaTags.Add(MediaTagType.DVD_DiscKey,
                                                                      decodedDiscKey.Value.Key);

                                                        UpdateStatus?.Invoke("Decrypting disc key.");

                                                        CSS.DecryptDiscKey(decodedDiscKey.Value.Key,
                                                                           out byte[] discKey);

                                                        if (discKey != null)
                                                        {
                                                            UpdateStatus?.Invoke("Decryption of disc key succeeded.");
                                                            mediaTags.Add(MediaTagType.DVD_DiscKey_Decrypted, discKey);
                                                        }
                                                        else
                                                        {
                                                            UpdateStatus?.Invoke("Decryption of disc key failed.");
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    UpdateStatus?.
                                    Invoke($"Drive reports disc uses {CSS_CPRM.DecodeLeadInCopyright(cmdBuf)!.Value.CopyrightType.ToString()} copy protection. " +
                                           "This is not yet supported and the dump will be incorrect.");
                                }
                            }
                        }
Beispiel #2
0
        public DeviceInfo(Device dev)
        {
            Type                  = dev.Type;
            Manufacturer          = dev.Manufacturer;
            Model                 = dev.Model;
            FirmwareRevision      = dev.FirmwareRevision;
            Serial                = dev.Serial;
            ScsiType              = dev.ScsiType;
            IsRemovable           = dev.IsRemovable;
            IsUsb                 = dev.IsUsb;
            UsbVendorId           = dev.UsbVendorId;
            UsbProductId          = dev.UsbProductId;
            UsbDescriptors        = dev.UsbDescriptors;
            UsbManufacturerString = dev.UsbManufacturerString;
            UsbProductString      = dev.UsbProductString;
            UsbSerialString       = dev.UsbSerialString;
            IsFireWire            = dev.IsFireWire;
            FireWireGuid          = dev.FireWireGuid;
            FireWireModel         = dev.FireWireModel;
            FireWireModelName     = dev.FireWireModelName;
            FireWireVendor        = dev.FireWireVendor;
            FireWireVendorName    = dev.FireWireVendorName;
            IsCompactFlash        = dev.IsCompactFlash;
            IsPcmcia              = dev.IsPcmcia;
            Cis = dev.Cis;

            switch (dev.Type)
            {
            case DeviceType.ATA:
            {
                bool sense = dev.AtaIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters);

                if (sense)
                {
                    AaruConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status);
                    AaruConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error);

                    AaruConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}",
                                               errorRegisters.SectorCount);

                    AaruConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector);

                    AaruConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}",
                                               errorRegisters.CylinderHigh);

                    AaruConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}",
                                               errorRegisters.CylinderLow);

                    AaruConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}",
                                               errorRegisters.DeviceHead);

                    AaruConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError);

                    break;
                }

                if (dev.Error)
                {
                    AaruConsole.ErrorWriteLine("Error {0} querying ATA IDENTIFY", dev.LastError);

                    break;
                }

                AtaIdentify = ataBuf;

                dev.EnableMediaCardPassThrough(out errorRegisters, dev.Timeout, out _);

                if (errorRegisters.Sector == 0xAA &&
                    errorRegisters.SectorCount == 0x55)
                {
                    AtaMcptError = errorRegisters;
                }

                break;
            }

            case DeviceType.ATAPI:
            {
                bool sense = dev.AtapiIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters);

                if (sense)
                {
                    AaruConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status);
                    AaruConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error);

                    AaruConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}",
                                               errorRegisters.SectorCount);

                    AaruConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector);

                    AaruConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}",
                                               errorRegisters.CylinderHigh);

                    AaruConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}",
                                               errorRegisters.CylinderLow);

                    AaruConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}",
                                               errorRegisters.DeviceHead);

                    AaruConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError);

                    break;
                }

                if (!dev.Error)
                {
                    AtapiIdentify = ataBuf;
                }
                else
                {
                    AaruConsole.ErrorWriteLine("Error {0} querying ATA PACKET IDENTIFY", dev.LastError);
                }

                // ATAPI devices are also SCSI devices
                goto case DeviceType.SCSI;
            }

            case DeviceType.SCSI:
            {
                bool sense = dev.ScsiInquiry(out byte[] inqBuf, out byte[] senseBuf);

                if (sense)
                {
                    AaruConsole.ErrorWriteLine("SCSI error:\n{0}", Sense.PrettifySense(senseBuf));

                    break;
                }

                ScsiInquiryData = inqBuf;
                ScsiInquiry     = Inquiry.Decode(inqBuf);

                sense = dev.ScsiInquiry(out inqBuf, out senseBuf, 0x00);

                if (!sense)
                {
                    ScsiEvpdPages = new Dictionary <byte, byte[]>();

                    byte[] pages = EVPD.DecodePage00(inqBuf);

                    if (pages != null)
                    {
                        foreach (byte page in pages)
                        {
                            sense = dev.ScsiInquiry(out inqBuf, out senseBuf, page);

                            if (sense)
                            {
                                continue;
                            }

                            ScsiEvpdPages.Add(page, inqBuf);
                        }
                    }
                }

                var devType = (PeripheralDeviceTypes)ScsiInquiry.Value.PeripheralDeviceType;

                sense = dev.ModeSense10(out byte[] modeBuf, out senseBuf, false, true,
                                        ScsiModeSensePageControl.Current, 0x3F, 0xFF, 5, out _);

                if (!sense &&
                    !dev.Error)
                {
                    ScsiModeSense10 = modeBuf;
                }

                if (sense || dev.Error)
                {
                    sense = dev.ModeSense10(out modeBuf, out senseBuf, false, true,
                                            ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _);

                    if (!sense &&
                        !dev.Error)
                    {
                        ScsiModeSense10 = modeBuf;
                    }
                }

                if (!sense &&
                    !dev.Error)
                {
                    ScsiMode = Modes.DecodeMode10(modeBuf, devType);
                }

                bool useMode10 = !(sense || dev.Error || !ScsiMode.HasValue);

                sense = dev.ModeSense6(out modeBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F,
                                       0xFF, 5, out _);

                if (!sense &&
                    !dev.Error)
                {
                    ScsiModeSense6 = modeBuf;
                }

                if (sense || dev.Error)
                {
                    sense = dev.ModeSense6(out modeBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F,
                                           0x00, 5, out _);

                    if (!sense &&
                        !dev.Error)
                    {
                        ScsiModeSense6 = modeBuf;
                    }
                }

                if (sense || dev.Error)
                {
                    sense = dev.ModeSense(out modeBuf, out senseBuf, 5, out _);

                    if (!sense &&
                        !dev.Error)
                    {
                        ScsiModeSense6 = modeBuf;
                    }
                }

                if (!sense &&
                    !dev.Error &&
                    !useMode10)
                {
                    ScsiMode = Modes.DecodeMode6(modeBuf, devType);
                }

                switch (devType)
                {
                case PeripheralDeviceTypes.MultiMediaDevice:
                {
                    sense = dev.GetConfiguration(out byte[] confBuf, out senseBuf, dev.Timeout, out _);

                    if (!sense)
                    {
                        MmcConfiguration = confBuf;
                    }

                    var dvdDecrypt = new DVDDecryption(dev);

                    sense = dvdDecrypt.ReadRpc(out byte[] cmdBuf, out _, DvdCssKeyClass.DvdCssCppmOrCprm,
                                               dev.Timeout, out _);

                    if (!sense)
                    {
                        CSS_CPRM.RegionalPlaybackControlState?rpc =
                            CSS_CPRM.DecodeRegionalPlaybackControlState(cmdBuf);

                        if (rpc.HasValue)
                        {
                            RPC = rpc;
                        }
                    }

                    // TODO: DVD drives respond correctly to BD status.
                    // While specification says if no medium is present
                    // it should inform all possible capabilities,
                    // testing drives show only supported media capabilities.

                    /*
                     * byte[] strBuf;
                     * sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _);
                     *
                     * if (!sense)
                     * {
                     *  Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf);
                     *  if (caps != null)
                     *  {
                     *      foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps)
                     *      {
                     *          if (cap.SDS && cap.RDS)
                     *              AaruConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *          else if (cap.SDS)
                     *              AaruConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *          else if (cap.RDS)
                     *              AaruConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *      }
                     *  }
                     * }
                     *
                     * sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.BD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _);
                     *
                     * if (!sense)
                     * {
                     *  Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf);
                     *  if (caps != null)
                     *  {
                     *      foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps)
                     *      {
                     *          if (cap.SDS && cap.RDS)
                     *              AaruConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *          else if (cap.SDS)
                     *              AaruConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *          else if (cap.RDS)
                     *              AaruConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *      }
                     *  }
                     * }
                     */

                    #region Plextor
                    if (dev.Manufacturer == "PLEXTOR")
                    {
                        bool   plxtSense = true;
                        bool   plxtDvd   = false;
                        byte[] plxtBuf   = null;

                        switch (dev.Model)
                        {
                        case "DVDR   PX-708A":
                        case "DVDR   PX-708A2":
                        case "DVDR   PX-712A":
                            plxtDvd = true;

                            plxtSense = dev.PlextorReadEeprom(out plxtBuf, out senseBuf, dev.Timeout,
                                                              out _);

                            break;

                        case "DVDR   PX-714A":
                        case "DVDR   PX-716A":
                        case "DVDR   PX-716AL":
                        case "DVDR   PX-755A":
                        case "DVDR   PX-760A":
                        {
                            plxtBuf = new byte[256 * 4];

                            for (byte i = 0; i < 4; i++)
                            {
                                plxtSense = dev.PlextorReadEepromBlock(out byte[] plxtBufSmall,
                                                                       out senseBuf, i, 256, dev.Timeout, out _);

                                if (plxtSense)
                                {
                                    break;
                                }

                                Array.Copy(plxtBufSmall, 0, plxtBuf, i * 256, 256);
                            }

                            plxtDvd = true;

                            break;
                        }

                        default:
                        {
                            if (dev.Model.StartsWith("CD-R   ", StringComparison.Ordinal))
                            {
                                plxtSense = dev.PlextorReadEepromCdr(out plxtBuf, out senseBuf, dev.Timeout,
                                                                     out _);
                            }

                            break;
                        }
                        }

                        PlextorFeatures = new Plextor
                        {
                            IsDvd = plxtDvd
                        };

                        if (!plxtSense)
                        {
                            PlextorFeatures.Eeprom = plxtBuf;

                            if (plxtDvd)
                            {
                                PlextorFeatures.Discs        = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0120);
                                PlextorFeatures.CdReadTime   = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0122);
                                PlextorFeatures.CdWriteTime  = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0126);
                                PlextorFeatures.DvdReadTime  = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012A);
                                PlextorFeatures.DvdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012E);
                            }
                            else
                            {
                                PlextorFeatures.Discs       = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0078);
                                PlextorFeatures.CdReadTime  = BigEndianBitConverter.ToUInt32(plxtBuf, 0x006C);
                                PlextorFeatures.CdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x007A);
                            }
                        }

                        plxtSense = dev.PlextorGetPoweRec(out senseBuf, out bool plxtPwrRecEnabled,
                                                          out ushort plxtPwrRecSpeed, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.PoweRec = true;

                            if (plxtPwrRecEnabled)
                            {
                                PlextorFeatures.PoweRecEnabled          = true;
                                PlextorFeatures.PoweRecRecommendedSpeed = plxtPwrRecSpeed;

                                plxtSense = dev.PlextorGetSpeeds(out senseBuf, out ushort plxtPwrRecSelected,
                                                                 out ushort plxtPwrRecMax,
                                                                 out ushort plxtPwrRecLast, dev.Timeout, out _);

                                if (!plxtSense)
                                {
                                    PlextorFeatures.PoweRecSelected = plxtPwrRecSelected;
                                    PlextorFeatures.PoweRecMax      = plxtPwrRecMax;
                                    PlextorFeatures.PoweRecLast     = plxtPwrRecLast;
                                }
                            }
                        }

                        // TODO: Check it with a drive
                        plxtSense = dev.PlextorGetSilentMode(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            if (plxtBuf[0] == 1)
                            {
                                PlextorFeatures.SilentModeEnabled = true;
                                PlextorFeatures.AccessTimeLimit   = plxtBuf[1];

                                PlextorFeatures.CdReadSpeedLimit  = plxtBuf[2];
                                PlextorFeatures.DvdReadSpeedLimit = plxtBuf[3];
                                PlextorFeatures.CdWriteSpeedLimit = plxtBuf[4];

                                // TODO: Check which one is each one

                                /*
                                 *  if(plxtBuf[6] > 0)
                                 *      AaruConsole.WriteLine("\tTray eject speed limited to {0}",
                                 *                           -(plxtBuf[6] + 48));
                                 *  if(plxtBuf[7] > 0)
                                 *      AaruConsole.WriteLine("\tTray eject speed limited to {0}",
                                 *                           plxtBuf[7] - 47);
                                 */
                            }
                        }

                        plxtSense = dev.PlextorGetGigaRec(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.GigaRec = true;
                        }

                        plxtSense = dev.PlextorGetSecuRec(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.SecuRec = true;
                        }

                        plxtSense = dev.PlextorGetSpeedRead(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.SpeedRead = true;

                            if ((plxtBuf[2] & 0x01) == 0x01)
                            {
                                PlextorFeatures.SpeedReadEnabled = true;
                            }
                        }

                        plxtSense = dev.PlextorGetHiding(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.Hiding = true;

                            if ((plxtBuf[2] & 0x02) == 0x02)
                            {
                                PlextorFeatures.HidesRecordables = true;
                            }

                            if ((plxtBuf[2] & 0x01) == 0x01)
                            {
                                PlextorFeatures.HidesSessions = true;
                            }
                        }

                        plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, false, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.VariRec = true;
                        }

                        if (plxtDvd)
                        {
                            plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, true, dev.Timeout,
                                                              out _);

                            if (!plxtSense)
                            {
                                PlextorFeatures.VariRecDvd = true;
                            }

                            plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, false, dev.Timeout,
                                                                 out _);

                            if (!plxtSense)
                            {
                                PlextorFeatures.BitSetting = true;
                            }

                            plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, true, dev.Timeout,
                                                                 out _);

                            if (!plxtSense)
                            {
                                PlextorFeatures.BitSettingDl = true;
                            }

                            plxtSense = dev.PlextorGetTestWriteDvdPlus(out plxtBuf, out senseBuf, dev.Timeout,
                                                                       out _);

                            if (!plxtSense)
                            {
                                PlextorFeatures.DvdPlusWriteTest = true;
                            }
                        }
                    }
                    #endregion Plextor

                    if (ScsiInquiry.Value.KreonPresent)
                    {
                        if (!dev.KreonGetFeatureList(out senseBuf, out KreonFeatures krFeatures, dev.Timeout,
                                                     out _))
                        {
                            KreonFeatures = krFeatures;
                        }
                    }

                    break;
                }

                case PeripheralDeviceTypes.SequentialAccess:
                {
                    sense = dev.ReadBlockLimits(out byte[] seqBuf, out senseBuf, dev.Timeout, out _);

                    if (sense)
                    {
                        AaruConsole.ErrorWriteLine("READ BLOCK LIMITS:\n{0}", Sense.PrettifySense(senseBuf));
                    }
                    else
                    {
                        BlockLimits = seqBuf;
                    }

                    sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, dev.Timeout, out _);

                    if (sense)
                    {
                        AaruConsole.ErrorWriteLine("REPORT DENSITY SUPPORT:\n{0}",
                                                   Sense.PrettifySense(senseBuf));
                    }
                    else
                    {
                        DensitySupport       = seqBuf;
                        DensitySupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeDensity(seqBuf);
                    }

                    sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, true, false, dev.Timeout, out _);

                    if (sense)
                    {
                        AaruConsole.ErrorWriteLine("REPORT DENSITY SUPPORT (MEDIUM):\n{0}",
                                                   Sense.PrettifySense(senseBuf));
                    }
                    else
                    {
                        MediumDensitySupport   = seqBuf;
                        MediaTypeSupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeMediumType(seqBuf);
                    }

                    break;
                }
                }

                break;
            }

            case DeviceType.MMC:
            {
                bool sense = dev.ReadCid(out byte[] mmcBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    CID = mmcBuf;
                }

                sense = dev.ReadCsd(out mmcBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    CSD = mmcBuf;
                }

                sense = dev.ReadOcr(out mmcBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    OCR = mmcBuf;
                }

                sense = dev.ReadExtendedCsd(out mmcBuf, out _, dev.Timeout, out _);

                if (!sense &&
                    !ArrayHelpers.ArrayIsNullOrEmpty(mmcBuf))
                {
                    ExtendedCSD = mmcBuf;
                }
            }

            break;

            case DeviceType.SecureDigital:
            {
                bool sense = dev.ReadCid(out byte[] sdBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    CID = sdBuf;
                }

                sense = dev.ReadCsd(out sdBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    CSD = sdBuf;
                }

                sense = dev.ReadSdocr(out sdBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    OCR = sdBuf;
                }

                sense = dev.ReadScr(out sdBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    SCR = sdBuf;
                }
            }

            break;

            default:
                AaruConsole.ErrorWriteLine("Unknown device type {0}, cannot get information.", dev.Type);

                break;
            }
        }