Example #1
0
        /// <summary>Dumps an optical disc</summary>
        void Mmc(ref MediaType dskType)
        {
            bool sense;

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

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

            if (compactDisc)
            {
                CompactDisc(out dskType);

                return;
            }

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

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

            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;

                // TODO: Unknown value
                default:
                    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 != null)
                    {
                        if (nintendoPfi.Value.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
                        if (dskType == MediaType.DVDROM)
                        {
                            switch (decPfi.DiskCategory)
                            {
                            case DiskCategory.DVDPR:
                                dskType = MediaType.DVDPR;

                                break;

                            case DiskCategory.DVDPRDL:
                                dskType = MediaType.DVDPRDL;

                                break;

                            case DiskCategory.DVDPRW:
                                dskType = MediaType.DVDPRW;

                                break;

                            case DiskCategory.DVDPRWDL:
                                dskType = MediaType.DVDPRWDL;

                                break;

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

                                break;

                            case DiskCategory.DVDRAM:
                                dskType = MediaType.DVDRAM;

                                break;

                            default:
                                dskType = MediaType.DVDROM;

                                break;

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

                                break;

                            case DiskCategory.HDDVDR:
                                dskType = MediaType.HDDVDR;

                                break;

                            case DiskCategory.HDDVDRAM:
                                dskType = MediaType.HDDVDRAM;

                                break;

                            case DiskCategory.HDDVDROM:
                                dskType = MediaType.HDDVDROM;

                                break;

                            case DiskCategory.HDDVDRW:
                                dskType = MediaType.HDDVDRW;

                                break;

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

                                break;

                            case DiskCategory.UMD:
                                dskType = MediaType.UMD;

                                break;
                            }
                        }
                    }
                }

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

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

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

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

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

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

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

                            return;
                        }

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

                        isXbox = true;
                    }

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

                break;
            }
            #endregion Nintendo

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

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

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

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

            switch (dskType)
            {
                #region DVD-ROM and HD DVD-ROM
            case MediaType.DVDDownload:
            case MediaType.DVDROM:
            case MediaType.HDDVDROM:
                _dumpLog.WriteLine("Reading Burst Cutting Area.");

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

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

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

                #region DVD-RAM and HD DVD-RAM
            case MediaType.DVDRAM:
            case MediaType.HDDVDRAM:
                _dumpLog.WriteLine("Reading Disc Description Structure.");

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

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

                _dumpLog.WriteLine("Reading Spare Area Information.");

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

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

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

                #region DVD-R and DVD-RW
            case MediaType.DVDR:
            case MediaType.DVDRW:
                _dumpLog.WriteLine("Reading Pre-Recorded Information.");

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

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

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

            switch (dskType)
            {
                #region DVD-R, DVD-RW and HD DVD-R
            case MediaType.DVDR:
            case MediaType.DVDRW:
            case MediaType.HDDVDR:
                _dumpLog.WriteLine("Reading Media Identifier.");

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

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

                _dumpLog.WriteLine("Reading Recordable Physical Information.");

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

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

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

                #region All DVD+
            case MediaType.DVDPR:
            case MediaType.DVDPRDL:
            case MediaType.DVDPRW:
            case MediaType.DVDPRWDL:
                _dumpLog.WriteLine("Reading ADdress In Pregroove.");

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

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

                _dumpLog.WriteLine("Reading Disc Control Blocks.");

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

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

                break;
                #endregion All DVD+

                #region HD DVD-ROM
            case MediaType.HDDVDROM:
                _dumpLog.WriteLine("Reading Lead-in Copyright Information.");

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

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

                break;
                #endregion HD DVD-ROM

                #region All Blu-ray
            case MediaType.BDR:
            case MediaType.BDRE:
            case MediaType.BDROM:
            case MediaType.BDRXL:
            case MediaType.BDREXL:
                _dumpLog.WriteLine("Reading Disc Information.");

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

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

                // TODO: PAC

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

            switch (dskType)
            {
                #region BD-ROM only
            case MediaType.BDROM:
                _dumpLog.WriteLine("Reading Burst Cutting Area.");

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

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

                break;
                #endregion BD-ROM only

                #region Writable Blu-ray only
            case MediaType.BDR:
            case MediaType.BDRE:
            case MediaType.BDRXL:
            case MediaType.BDREXL:
                _dumpLog.WriteLine("Reading Disc Definition Structure.");

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

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

                _dumpLog.WriteLine("Reading Spare Area Information.");

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

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

                break;
                #endregion Writable Blu-ray only
            }

            if (isXbox)
            {
                Xgd(mediaTags, ref dskType);

                return;
            }

            Sbc(mediaTags, ref dskType, true);
        }
        /// <summary>
        ///     Opens the device for sending direct commands
        /// </summary>
        /// <param name="devicePath">Device path</param>
        public Device(string devicePath)
        {
            PlatformId  = DetectOS.GetRealPlatformID();
            Timeout     = 15;
            Error       = false;
            IsRemovable = false;

            switch (PlatformId)
            {
            case PlatformID.Win32NT:
            {
                FileHandle = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite,
                                               FileShare.Read | FileShare.Write, IntPtr.Zero,
                                               FileMode.OpenExisting,
                                               FileAttributes.Normal, IntPtr.Zero);

                if (((SafeFileHandle)FileHandle).IsInvalid)
                {
                    Error     = true;
                    LastError = Marshal.GetLastWin32Error();
                }

                break;
            }

            case PlatformID.Linux:
            {
                FileHandle =
                    Linux.Extern.open(devicePath,
                                      FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew);

                if ((int)FileHandle < 0)
                {
                    LastError = Marshal.GetLastWin32Error();

                    if (LastError == 13 || LastError == 30)    // EACCES or EROFS
                    {
                        FileHandle = Linux.Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking);
                        if ((int)FileHandle < 0)
                        {
                            Error     = true;
                            LastError = Marshal.GetLastWin32Error();
                        }
                    }
                    else
                    {
                        Error = true;
                    }

                    LastError = Marshal.GetLastWin32Error();
                }

                break;
            }

            case PlatformID.FreeBSD:
            {
                FileHandle = FreeBSD.Extern.cam_open_device(devicePath, FreeBSD.FileFlags.ReadWrite);

                if (((IntPtr)FileHandle).ToInt64() == 0)
                {
                    Error     = true;
                    LastError = Marshal.GetLastWin32Error();
                }

                CamDevice camDevice = (CamDevice)Marshal.PtrToStructure((IntPtr)FileHandle, typeof(CamDevice));

                if (StringHandlers.CToString(camDevice.SimName) == "ata")
                {
                    throw new
                          InvalidOperationException("Parallel ATA devices are not supported on FreeBSD due to upstream bug #224250.");
                }

                break;
            }

            default: throw new InvalidOperationException($"Platform {PlatformId} not yet supported.");
            }

            if (Error)
            {
                throw new SystemException($"Error {LastError} opening device.");
            }

            Type     = DeviceType.Unknown;
            ScsiType = PeripheralDeviceTypes.UnknownDevice;

            byte[] ataBuf;
            byte[] inqBuf = null;

            if (Error)
            {
                throw new SystemException($"Error {LastError} trying device.");
            }

            bool scsiSense = true;

            // Windows is answering SCSI INQUIRY for all device types so it needs to be detected first
            switch (PlatformId)
            {
            case PlatformID.Win32NT:
                StoragePropertyQuery query = new StoragePropertyQuery();
                query.PropertyId           = StoragePropertyId.Device;
                query.QueryType            = StorageQueryType.Standard;
                query.AdditionalParameters = new byte[1];

                IntPtr descriptorPtr = Marshal.AllocHGlobal(1000);
                byte[] descriptorB   = new byte[1000];

                uint returned = 0;
                int  error    = 0;

                bool hasError = !Extern.DeviceIoControlStorageQuery((SafeFileHandle)FileHandle,
                                                                    WindowsIoctl.IoctlStorageQueryProperty,
                                                                    ref query, (uint)Marshal.SizeOf(query),
                                                                    descriptorPtr, 1000, ref returned, IntPtr.Zero);

                if (hasError)
                {
                    error = Marshal.GetLastWin32Error();
                }

                Marshal.Copy(descriptorPtr, descriptorB, 0, 1000);

                if (!hasError && error == 0)
                {
                    StorageDeviceDescriptor descriptor = new StorageDeviceDescriptor
                    {
                        Version               = BitConverter.ToUInt32(descriptorB, 0),
                        Size                  = BitConverter.ToUInt32(descriptorB, 4),
                        DeviceType            = descriptorB[8],
                        DeviceTypeModifier    = descriptorB[9],
                        RemovableMedia        = descriptorB[10] > 0,
                        CommandQueueing       = descriptorB[11] > 0,
                        VendorIdOffset        = BitConverter.ToInt32(descriptorB, 12),
                        ProductIdOffset       = BitConverter.ToInt32(descriptorB, 16),
                        ProductRevisionOffset = BitConverter.ToInt32(descriptorB, 20),
                        SerialNumberOffset    = BitConverter.ToInt32(descriptorB, 24),
                        BusType               = (StorageBusType)BitConverter.ToUInt32(descriptorB, 28),
                        RawPropertiesLength   = BitConverter.ToUInt32(descriptorB, 32)
                    };
                    descriptor.RawDeviceProperties = new byte[descriptor.RawPropertiesLength];
                    Array.Copy(descriptorB, 36, descriptor.RawDeviceProperties, 0, descriptor.RawPropertiesLength);

                    switch (descriptor.BusType)
                    {
                    case StorageBusType.SCSI:
                    case StorageBusType.SSA:
                    case StorageBusType.Fibre:
                    case StorageBusType.iSCSI:
                    case StorageBusType.SAS:
                        Type = DeviceType.SCSI;
                        break;

                    case StorageBusType.FireWire:
                        IsFireWire = true;
                        Type       = DeviceType.SCSI;
                        break;

                    case StorageBusType.USB:
                        IsUsb = true;
                        Type  = DeviceType.SCSI;
                        break;

                    case StorageBusType.ATAPI:
                        Type = DeviceType.ATAPI;
                        break;

                    case StorageBusType.ATA:
                    case StorageBusType.SATA:
                        Type = DeviceType.ATA;
                        break;

                    case StorageBusType.MultiMediaCard:
                        Type = DeviceType.MMC;
                        break;

                    case StorageBusType.SecureDigital:
                        Type = DeviceType.SecureDigital;
                        break;

                    case StorageBusType.NVMe:
                        Type = DeviceType.NVMe;
                        break;
                    }

                    switch (Type)
                    {
                    case DeviceType.SCSI:
                    case DeviceType.ATAPI:
                        scsiSense = ScsiInquiry(out inqBuf, out _);
                        break;

                    case DeviceType.ATA:
                        bool atapiSense = AtapiIdentify(out ataBuf, out _);

                        if (!atapiSense)
                        {
                            Type = DeviceType.ATAPI;
                            Identify.IdentifyDevice?ataid = Identify.Decode(ataBuf);

                            if (ataid.HasValue)
                            {
                                scsiSense = ScsiInquiry(out inqBuf, out _);
                            }
                        }
                        else
                        {
                            Manufacturer = "ATA";
                        }

                        break;
                    }
                }

                Marshal.FreeHGlobal(descriptorPtr);

                if (Windows.Command.IsSdhci((SafeFileHandle)FileHandle))
                {
                    byte[] sdBuffer = new byte[16];

                    LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCsd,
                                                               false, false,
                                                               MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 |
                                                               MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, out _,
                                                               out _, out bool sense);

                    if (!sense)
                    {
                        cachedCsd = new byte[16];
                        Array.Copy(sdBuffer, 0, cachedCsd, 0, 16);
                    }

                    sdBuffer = new byte[16];

                    LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCid,
                                                               false, false,
                                                               MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 |
                                                               MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, out _,
                                                               out _, out sense);

                    if (!sense)
                    {
                        cachedCid = new byte[16];
                        Array.Copy(sdBuffer, 0, cachedCid, 0, 16);
                    }

                    sdBuffer = new byte[8];

                    LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle,
                                                               (MmcCommands)SecureDigitalCommands.SendScr, false,
                                                               true,
                                                               MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 |
                                                               MmcFlags.CommandAdtc, 0, 8, 1, ref sdBuffer, out _,
                                                               out _, out sense);

                    if (!sense)
                    {
                        cachedScr = new byte[8];
                        Array.Copy(sdBuffer, 0, cachedScr, 0, 8);
                    }

                    if (cachedScr != null)
                    {
                        sdBuffer = new byte[4];

                        LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle,
                                                                   (MmcCommands)SecureDigitalCommands
                                                                   .SendOperatingCondition, false, true,
                                                                   MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 |
                                                                   MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer,
                                                                   out _, out _, out sense);

                        if (!sense)
                        {
                            cachedScr = new byte[4];
                            Array.Copy(sdBuffer, 0, cachedScr, 0, 4);
                        }
                    }
                    else
                    {
                        sdBuffer = new byte[4];

                        LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle,
                                                                   MmcCommands.SendOpCond, false, true,
                                                                   MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 |
                                                                   MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer,
                                                                   out _, out _, out sense);

                        if (!sense)
                        {
                            cachedScr = new byte[4];
                            Array.Copy(sdBuffer, 0, cachedScr, 0, 4);
                        }
                    }
                }

                break;

            case PlatformID.Linux:
                if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                {
                    scsiSense = ScsiInquiry(out inqBuf, out _);
                }
                // MultiMediaCard and SecureDigital go here
                else if (devicePath.StartsWith("/dev/mmcblk", StringComparison.Ordinal))
                {
                    string devPath = devicePath.Substring(5);
                    if (File.Exists("/sys/block/" + devPath + "/device/csd"))
                    {
                        int len =
                            ConvertFromHexAscii("/sys/block/" + devPath + "/device/csd", out cachedCsd);
                        if (len == 0)
                        {
                            cachedCsd = null;
                        }
                    }

                    if (File.Exists("/sys/block/" + devPath + "/device/cid"))
                    {
                        int len =
                            ConvertFromHexAscii("/sys/block/" + devPath + "/device/cid", out cachedCid);
                        if (len == 0)
                        {
                            cachedCid = null;
                        }
                    }

                    if (File.Exists("/sys/block/" + devPath + "/device/scr"))
                    {
                        int len =
                            ConvertFromHexAscii("/sys/block/" + devPath + "/device/scr", out cachedScr);
                        if (len == 0)
                        {
                            cachedScr = null;
                        }
                    }

                    if (File.Exists("/sys/block/" + devPath + "/device/ocr"))
                    {
                        int len =
                            ConvertFromHexAscii("/sys/block/" + devPath + "/device/ocr", out cachedOcr);
                        if (len == 0)
                        {
                            cachedOcr = null;
                        }
                    }
                }

                break;

            default:
                scsiSense = ScsiInquiry(out inqBuf, out _);
                break;
            }

            #region SecureDigital / MultiMediaCard
            if (cachedCid != null)
            {
                ScsiType    = PeripheralDeviceTypes.DirectAccess;
                IsRemovable = false;

                if (cachedScr != null)
                {
                    Type = DeviceType.SecureDigital;
                    CID decoded = Decoders.SecureDigital.Decoders.DecodeCID(cachedCid);
                    Manufacturer = VendorString.Prettify(decoded.Manufacturer);
                    Model        = decoded.ProductName;
                    Revision     = $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}";
                    Serial       = $"{decoded.ProductSerialNumber}";
                }
                else
                {
                    Type = DeviceType.MMC;
                    Decoders.MMC.CID decoded = Decoders.MMC.Decoders.DecodeCID(cachedCid);
                    Manufacturer = Decoders.MMC.VendorString.Prettify(decoded.Manufacturer);
                    Model        = decoded.ProductName;
                    Revision     = $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}";
                    Serial       = $"{decoded.ProductSerialNumber}";
                }
            }
            #endregion SecureDigital / MultiMediaCard

            #region USB
            switch (PlatformId)
            {
            case PlatformID.Linux:
                if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                {
                    string devPath = devicePath.Substring(5);
                    if (Directory.Exists("/sys/block/" + devPath))
                    {
                        string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath);
                        resolvedLink = "/sys" + resolvedLink.Substring(2);
                        if (!string.IsNullOrEmpty(resolvedLink))
                        {
                            while (resolvedLink.Contains("usb"))
                            {
                                resolvedLink = Path.GetDirectoryName(resolvedLink);
                                if (!File.Exists(resolvedLink + "/descriptors") ||
                                    !File.Exists(resolvedLink + "/idProduct") ||
                                    !File.Exists(resolvedLink + "/idVendor"))
                                {
                                    continue;
                                }

                                FileStream usbFs = new FileStream(resolvedLink + "/descriptors",
                                                                  System.IO.FileMode.Open,
                                                                  System.IO.FileAccess.Read);
                                byte[] usbBuf   = new byte[65536];
                                int    usbCount = usbFs.Read(usbBuf, 0, 65536);
                                UsbDescriptors = new byte[usbCount];
                                Array.Copy(usbBuf, 0, UsbDescriptors, 0, usbCount);
                                usbFs.Close();

                                StreamReader usbSr   = new StreamReader(resolvedLink + "/idProduct");
                                string       usbTemp = usbSr.ReadToEnd();
                                ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                                out usbProduct);
                                usbSr.Close();

                                usbSr   = new StreamReader(resolvedLink + "/idVendor");
                                usbTemp = usbSr.ReadToEnd();
                                ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                                out usbVendor);
                                usbSr.Close();

                                if (File.Exists(resolvedLink + "/manufacturer"))
                                {
                                    usbSr = new StreamReader(resolvedLink + "/manufacturer");
                                    UsbManufacturerString = usbSr.ReadToEnd().Trim();
                                    usbSr.Close();
                                }

                                if (File.Exists(resolvedLink + "/product"))
                                {
                                    usbSr            = new StreamReader(resolvedLink + "/product");
                                    UsbProductString = usbSr.ReadToEnd().Trim();
                                    usbSr.Close();
                                }

                                if (File.Exists(resolvedLink + "/serial"))
                                {
                                    usbSr           = new StreamReader(resolvedLink + "/serial");
                                    UsbSerialString = usbSr.ReadToEnd().Trim();
                                    usbSr.Close();
                                }

                                IsUsb = true;
                                break;
                            }
                        }
                    }
                }

                break;

            case PlatformID.Win32NT:
                Usb.UsbDevice usbDevice = null;

                // I have to search for USB disks, floppies and CD-ROMs as separate device types
                foreach (string devGuid in new[]
                {
                    Usb.GuidDevinterfaceFloppy, Usb.GuidDevinterfaceCdrom, Usb.GuidDevinterfaceDisk
                })
                {
                    usbDevice = Usb.FindDrivePath(devicePath, devGuid);
                    if (usbDevice != null)
                    {
                        break;
                    }
                }

                if (usbDevice != null)
                {
                    UsbDescriptors        = usbDevice.BinaryDescriptors;
                    usbVendor             = (ushort)usbDevice.DeviceDescriptor.idVendor;
                    usbProduct            = (ushort)usbDevice.DeviceDescriptor.idProduct;
                    UsbManufacturerString = usbDevice.Manufacturer;
                    UsbProductString      = usbDevice.Product;
                    UsbSerialString       =
                        usbDevice.SerialNumber;     // This is incorrect filled by Windows with SCSI/ATA serial number
                }

                break;

            default:
                IsUsb = false;
                break;
            }
            #endregion USB

            #region FireWire
            if (PlatformId == PlatformID.Linux)
            {
                if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                {
                    string devPath = devicePath.Substring(5);
                    if (Directory.Exists("/sys/block/" + devPath))
                    {
                        string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath);
                        resolvedLink = "/sys" + resolvedLink.Substring(2);
                        if (!string.IsNullOrEmpty(resolvedLink))
                        {
                            while (resolvedLink.Contains("firewire"))
                            {
                                resolvedLink = Path.GetDirectoryName(resolvedLink);
                                if (!File.Exists(resolvedLink + "/model") || !File.Exists(resolvedLink + "/vendor") ||
                                    !File.Exists(resolvedLink + "/guid"))
                                {
                                    continue;
                                }

                                StreamReader fwSr   = new StreamReader(resolvedLink + "/model");
                                string       fwTemp = fwSr.ReadToEnd();
                                uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                              out firewireModel);
                                fwSr.Close();

                                fwSr   = new StreamReader(resolvedLink + "/vendor");
                                fwTemp = fwSr.ReadToEnd();
                                uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                              out firewireVendor);
                                fwSr.Close();

                                fwSr   = new StreamReader(resolvedLink + "/guid");
                                fwTemp = fwSr.ReadToEnd();
                                ulong.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                               out firewireGuid);
                                fwSr.Close();

                                if (File.Exists(resolvedLink + "/model_name"))
                                {
                                    fwSr = new StreamReader(resolvedLink + "/model_name");
                                    FireWireModelName = fwSr.ReadToEnd().Trim();
                                    fwSr.Close();
                                }

                                if (File.Exists(resolvedLink + "/vendor_name"))
                                {
                                    fwSr = new StreamReader(resolvedLink + "/vendor_name");
                                    FireWireVendorName = fwSr.ReadToEnd().Trim();
                                    fwSr.Close();
                                }

                                IsFireWire = true;
                                break;
                            }
                        }
                    }
                }
            }
            // TODO: Implement for other operating systems
            else
            {
                IsFireWire = false;
            }
            #endregion FireWire

            #region PCMCIA
            if (PlatformId == PlatformID.Linux)
            {
                if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                {
                    string devPath = devicePath.Substring(5);
                    if (Directory.Exists("/sys/block/" + devPath))
                    {
                        string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath);
                        resolvedLink = "/sys" + resolvedLink.Substring(2);
                        if (!string.IsNullOrEmpty(resolvedLink))
                        {
                            while (resolvedLink.Contains("/sys/devices"))
                            {
                                resolvedLink = Path.GetDirectoryName(resolvedLink);
                                if (!Directory.Exists(resolvedLink + "/pcmcia_socket"))
                                {
                                    continue;
                                }

                                string[] subdirs = Directory.GetDirectories(resolvedLink + "/pcmcia_socket",
                                                                            "pcmcia_socket*",
                                                                            SearchOption.TopDirectoryOnly);

                                if (subdirs.Length <= 0)
                                {
                                    continue;
                                }

                                string possibleDir = Path.Combine(resolvedLink, "pcmcia_socket", subdirs[0]);
                                if (!File.Exists(possibleDir + "/card_type") || !File.Exists(possibleDir + "/cis"))
                                {
                                    continue;
                                }

                                FileStream cisFs = new FileStream(possibleDir + "/cis", System.IO.FileMode.Open,
                                                                  System.IO.FileAccess.Read);
                                byte[] cisBuf   = new byte[65536];
                                int    cisCount = cisFs.Read(cisBuf, 0, 65536);
                                Cis = new byte[cisCount];
                                Array.Copy(cisBuf, 0, Cis, 0, cisCount);
                                cisFs.Close();

                                IsPcmcia = true;
                                break;
                            }
                        }
                    }
                }
            }
            // TODO: Implement for other operating systems
            else
            {
                IsPcmcia = false;
            }
            #endregion PCMCIA

            if (!scsiSense)
            {
                Inquiry.SCSIInquiry?inquiry = Inquiry.Decode(inqBuf);

                Type = DeviceType.SCSI;
                bool serialSense = ScsiInquiry(out inqBuf, out _, 0x80);
                if (!serialSense)
                {
                    Serial = EVPD.DecodePage80(inqBuf);
                }

                if (inquiry.HasValue)
                {
                    string tmp = StringHandlers.CToString(inquiry.Value.ProductRevisionLevel);
                    if (tmp != null)
                    {
                        Revision = tmp.Trim();
                    }
                    tmp = StringHandlers.CToString(inquiry.Value.ProductIdentification);
                    if (tmp != null)
                    {
                        Model = tmp.Trim();
                    }
                    tmp = StringHandlers.CToString(inquiry.Value.VendorIdentification);
                    if (tmp != null)
                    {
                        Manufacturer = tmp.Trim();
                    }
                    IsRemovable = inquiry.Value.RMB;

                    ScsiType = (PeripheralDeviceTypes)inquiry.Value.PeripheralDeviceType;
                }

                bool atapiSense = AtapiIdentify(out ataBuf, out _);

                if (!atapiSense)
                {
                    Type = DeviceType.ATAPI;
                    Identify.IdentifyDevice?ataId = Identify.Decode(ataBuf);

                    if (ataId.HasValue)
                    {
                        Serial = ataId.Value.SerialNumber;
                    }
                }

                LastError = 0;
                Error     = false;
            }

            if (scsiSense && (IsUsb || IsFireWire) || Manufacturer == "ATA")
            {
                bool ataSense = AtaIdentify(out ataBuf, out _);
                if (!ataSense)
                {
                    Type = DeviceType.ATA;
                    Identify.IdentifyDevice?ataid = Identify.Decode(ataBuf);

                    if (ataid.HasValue)
                    {
                        string[] separated = ataid.Value.Model.Split(' ');

                        if (separated.Length == 1)
                        {
                            Model = separated[0];
                        }
                        else
                        {
                            Manufacturer = separated[0];
                            Model        = separated[separated.Length - 1];
                        }

                        Revision = ataid.Value.FirmwareRevision;
                        Serial   = ataid.Value.SerialNumber;

                        ScsiType = PeripheralDeviceTypes.DirectAccess;

                        if ((ushort)ataid.Value.GeneralConfiguration != 0x848A)
                        {
                            IsRemovable |=
                                (ataid.Value.GeneralConfiguration & Identify.GeneralConfigurationBit.Removable) ==
                                Identify.GeneralConfigurationBit.Removable;
                        }
                        else
                        {
                            IsCompactFlash = true;
                        }
                    }
                }
            }

            if (Type == DeviceType.Unknown)
            {
                Manufacturer = null;
                Model        = null;
                Revision     = null;
                Serial       = null;
            }

            if (IsUsb)
            {
                if (string.IsNullOrEmpty(Manufacturer))
                {
                    Manufacturer = UsbManufacturerString;
                }
                if (string.IsNullOrEmpty(Model))
                {
                    Model = UsbProductString;
                }
                if (string.IsNullOrEmpty(Serial))
                {
                    Serial = UsbSerialString;
                }
                else
                {
                    foreach (char c in Serial.Where(char.IsControl))
                    {
                        Serial = UsbSerialString;
                    }
                }
            }

            if (IsFireWire)
            {
                if (string.IsNullOrEmpty(Manufacturer))
                {
                    Manufacturer = FireWireVendorName;
                }
                if (string.IsNullOrEmpty(Model))
                {
                    Model = FireWireModelName;
                }
                if (string.IsNullOrEmpty(Serial))
                {
                    Serial = $"{firewireGuid:X16}";
                }
                else
                {
                    foreach (char c in Serial.Where(char.IsControl))
                    {
                        Serial = $"{firewireGuid:X16}";
                    }
                }
            }

            // Some optical drives are not getting the correct serial, and IDENTIFY PACKET DEVICE is blocked without
            // administrator privileges
            if (ScsiType != PeripheralDeviceTypes.MultiMediaDevice)
            {
                return;
            }

            bool featureSense = GetConfiguration(out byte[] featureBuffer, out _, 0x0108, MmcGetConfigurationRt.Single,
                                                 Timeout, out _);

            if (featureSense)
            {
                return;
            }

            Features.SeparatedFeatures features = Features.Separate(featureBuffer);
            if (features.Descriptors?.Length != 1 || features.Descriptors[0].Code != 0x0108)
            {
                return;
            }

            Feature_0108?serialFeature = Features.Decode_0108(features.Descriptors[0].Data);

            if (serialFeature is null)
            {
                return;
            }

            Serial = serialFeature.Value.Serial;
        }
Example #3
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);
        }
Example #4
0
        /// <summary>
        ///     Dumps an optical disc
        /// </summary>
        /// <param name="dev">Device</param>
        /// <param name="devicePath">Path to the device</param>
        /// <param name="outputPrefix">Prefix for output data files</param>
        /// <param name="outputPlugin">Plugin for output file</param>
        /// <param name="retryPasses">How many times to retry</param>
        /// <param name="force">Force to continue dump whenever possible</param>
        /// <param name="dumpRaw">Dump raw/long sectors</param>
        /// <param name="persistent">Store whatever data the drive returned on error</param>
        /// <param name="stopOnError">Stop dump on first error</param>
        /// <param name="resume">Information for dump resuming</param>
        /// <param name="dumpLog">Dump logger</param>
        /// <param name="encoding">Encoding to use when analyzing dump</param>
        /// <param name="dskType">Disc type as detected in MMC layer</param>
        /// <param name="dumpLeadIn">Try to read and dump as much Lead-in as possible</param>
        /// <param name="outputPath">Path to output file</param>
        /// <param name="formatOptions">Formats to pass to output file plugin</param>
        /// <exception cref="NotImplementedException">If trying to dump GOD or WOD, or XGDs without a Kreon drive</exception>
        internal static void Dump(Device dev, string devicePath,
                                  IWritableImage outputPlugin, ushort retryPasses,
                                  bool force, bool dumpRaw,
                                  bool persistent, bool stopOnError, ref MediaType dskType,
                                  ref Resume resume, ref DumpLog dumpLog,
                                  bool dumpLeadIn, Encoding encoding,
                                  string outputPrefix, string outputPath,
                                  Dictionary <string, string> formatOptions,
                                  CICMMetadataType preSidecar, uint skip,
                                  bool nometadata, bool notrim)
        {
            bool  sense;
            ulong blocks;

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

            // TODO: Log not only what is it reading, but if it was read correctly or not.

            sense = dev.GetConfiguration(out byte[] cmdBuf, out _, 0, MmcGetConfigurationRt.Current, dev.Timeout,
                                         out _);
            if (!sense)
            {
                Features.SeparatedFeatures ftr = Features.Separate(cmdBuf);
                dumpLog.WriteLine("Device reports current profile is 0x{0:X4}", ftr.CurrentProfile);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                default:
                    compactDisc = false;
                    break;
                }
            }

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

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

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

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

                break;

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

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

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

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

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

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

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

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

                            default:
                                dskType = MediaType.DVDROM;
                                break;

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

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

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

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

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

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

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

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

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

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

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

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

                        isXbox = true;
                    }

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

                break;
            }
            #endregion Nintendo

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                break;
                #endregion All DVD+

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

                break;
                #endregion HD DVD-ROM

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

                // TODO: PAC

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

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

                break;
                #endregion BD-ROM only

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

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

                break;
                #endregion Writable Blu-ray only
            }

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

            Sbc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError, mediaTags,
                     ref dskType, true, ref resume, ref dumpLog, encoding, outputPrefix, outputPath, formatOptions,
                     preSidecar, skip, nometadata, notrim);
        }
Example #5
0
        public ScsiInfoViewModel(byte[] scsiInquiryData, Inquiry?scsiInquiry, Dictionary <byte, byte[]> scsiEvpdPages,
                                 Modes.DecodedMode?scsiMode, PeripheralDeviceTypes scsiType, byte[] scsiModeSense6,
                                 byte[] scsiModeSense10, byte[] mmcConfiguration, Window view)
        {
            InquiryData              = scsiInquiryData;
            _scsiModeSense6          = scsiModeSense6;
            _scsiModeSense10         = scsiModeSense10;
            _configuration           = mmcConfiguration;
            _view                    = view;
            ModeSensePages           = new ObservableCollection <ScsiPageModel>();
            EvpdPages                = new ObservableCollection <ScsiPageModel>();
            MmcFeatures              = new ObservableCollection <ScsiPageModel>();
            SaveInquiryBinaryCommand = ReactiveCommand.Create(ExecuteSaveInquiryBinaryCommand);
            SaveInquiryTextCommand   = ReactiveCommand.Create(ExecuteSaveInquiryTextCommand);
            SaveModeSense6Command    = ReactiveCommand.Create(ExecuteSaveModeSense6Command);
            SaveModeSense10Command   = ReactiveCommand.Create(ExecuteSaveModeSense10Command);
            SaveEvpdPageCommand      = ReactiveCommand.Create(ExecuteSaveEvpdPageCommand);
            SaveMmcFeaturesCommand   = ReactiveCommand.Create(ExecuteSaveMmcFeaturesCommand);

            if (InquiryData == null ||
                !scsiInquiry.HasValue)
            {
                return;
            }

            ScsiInquiryText = Decoders.SCSI.Inquiry.Prettify(scsiInquiry);

            if (scsiMode.HasValue)
            {
                ModeSensePages.Add(new ScsiPageModel
                {
                    Page        = "Header",
                    Description = Modes.PrettifyModeHeader(scsiMode.Value.Header, scsiType)
                });

                if (scsiMode.Value.Pages != null)
                {
                    foreach (Modes.ModePage page in scsiMode.Value.Pages.OrderBy(t => t.Page).ThenBy(t => t.Subpage))
                    {
                        string pageNumberText = page.Subpage == 0 ? $"MODE {page.Page:X2}h"
                                                    : $"MODE {page.Page:X2} Subpage {page.Subpage:X2}";

                        string decodedText;

                        switch (page.Page)
                        {
                        case 0x00:
                        {
                            if (scsiType == PeripheralDeviceTypes.MultiMediaDevice &&
                                page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_00_SFF(page.PageResponse);
                            }
                            else
                            {
                                decodedText = "Undecoded";
                            }

                            break;
                        }

                        case 0x01:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = scsiType == PeripheralDeviceTypes.MultiMediaDevice
                                                      ? Modes.PrettifyModePage_01_MMC(page.PageResponse)
                                                      : Modes.PrettifyModePage_01(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x02:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_02(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x03:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_03(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x04:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_04(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x05:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_05(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x06:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_06(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x07:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = scsiType == PeripheralDeviceTypes.MultiMediaDevice
                                                      ? Modes.PrettifyModePage_07_MMC(page.PageResponse)
                                                      : Modes.PrettifyModePage_07(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x08:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_08(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0A:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0A(page.PageResponse);
                            }
                            else if (page.Subpage == 1)
                            {
                                decodedText = Modes.PrettifyModePage_0A_S01(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0B:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0B(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0D:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0D(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0E:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0E(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x0F:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_0F(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x10:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = scsiType == PeripheralDeviceTypes.SequentialAccess
                                                      ? Modes.PrettifyModePage_10_SSC(page.PageResponse)
                                                      : Modes.PrettifyModePage_10(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x11:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_11(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x12:
                        case 0x13:
                        case 0x14:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_12_13_14(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x1A:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_1A(page.PageResponse);
                            }
                            else if (page.Subpage == 1)
                            {
                                decodedText = Modes.PrettifyModePage_1A_S01(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x1B:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_1B(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x1C:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = scsiType == PeripheralDeviceTypes.MultiMediaDevice
                                                      ? Modes.PrettifyModePage_1C_SFF(page.PageResponse)
                                                      : Modes.PrettifyModePage_1C(page.PageResponse);
                            }
                            else if (page.Subpage == 1)
                            {
                                decodedText = Modes.PrettifyModePage_1C_S01(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x1D:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_1D(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x21:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() ==
                                "CERTANCE")
                            {
                                decodedText = Modes.PrettifyCertanceModePage_21(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x22:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() ==
                                "CERTANCE")
                            {
                                decodedText = Modes.PrettifyCertanceModePage_22(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x24:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "IBM")
                            {
                                decodedText = Modes.PrettifyIBMModePage_24(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x2A:
                        {
                            if (page.Subpage == 0)
                            {
                                decodedText = Modes.PrettifyModePage_2A(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x2F:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "IBM")
                            {
                                decodedText = Modes.PrettifyIBMModePage_2F(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x30:
                        {
                            if (Modes.IsAppleModePage_30(page.PageResponse))
                            {
                                decodedText = "Drive identifies as Apple OEM drive";
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x3B:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP")
                            {
                                decodedText = Modes.PrettifyHPModePage_3B(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x3C:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP")
                            {
                                decodedText = Modes.PrettifyHPModePage_3C(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x3D:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "IBM")
                            {
                                decodedText = Modes.PrettifyIBMModePage_3D(page.PageResponse);
                            }
                            else if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP")
                            {
                                decodedText = Modes.PrettifyHPModePage_3D(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        case 0x3E:
                        {
                            if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "FUJITSU")
                            {
                                decodedText = Modes.PrettifyFujitsuModePage_3E(page.PageResponse);
                            }
                            else if (StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP")
                            {
                                decodedText = Modes.PrettifyHPModePage_3E(page.PageResponse);
                            }
                            else
                            {
                                goto default;
                            }

                            break;
                        }

                        default:
                        {
                            decodedText = "Undecoded";

                            break;
                        }
                        }

                        // TODO: Automatic error reporting
                        if (decodedText == null)
                        {
                            decodedText = "Error decoding page, please open an issue.";
                        }

                        ModeSensePages.Add(new ScsiPageModel
                        {
                            Page        = pageNumberText,
                            Description = decodedText
                        });
                    }
                }
            }

            if (scsiEvpdPages != null)
            {
                foreach (KeyValuePair <byte, byte[]> page in scsiEvpdPages.OrderBy(t => t.Key))
                {
                    string evpdPageTitle = "";
                    string evpdDecodedPage;

                    if (page.Key >= 0x01 &&
                        page.Key <= 0x7F)
                    {
                        evpdPageTitle   = $"ASCII Page {page.Key:X2}h";
                        evpdDecodedPage = EVPD.DecodeASCIIPage(page.Value);
                    }
                    else if (page.Key == 0x80)
                    {
                        evpdPageTitle   = "Unit Serial Number";
                        evpdDecodedPage = EVPD.DecodePage80(page.Value);
                    }
                    else if (page.Key == 0x81)
                    {
                        evpdPageTitle   = "SCSI Implemented operating definitions";
                        evpdDecodedPage = EVPD.PrettifyPage_81(page.Value);
                    }
                    else if (page.Key == 0x82)
                    {
                        evpdPageTitle   = "ASCII implemented operating definitions";
                        evpdDecodedPage = EVPD.DecodePage82(page.Value);
                    }
                    else if (page.Key == 0x83)
                    {
                        evpdPageTitle   = "SCSI Device identification";
                        evpdDecodedPage = EVPD.PrettifyPage_83(page.Value);
                    }
                    else if (page.Key == 0x84)
                    {
                        evpdPageTitle   = "SCSI Software Interface Identifiers";
                        evpdDecodedPage = EVPD.PrettifyPage_84(page.Value);
                    }
                    else if (page.Key == 0x85)
                    {
                        evpdPageTitle   = "SCSI Management Network Addresses";
                        evpdDecodedPage = EVPD.PrettifyPage_85(page.Value);
                    }
                    else if (page.Key == 0x86)
                    {
                        evpdPageTitle   = "SCSI Extended INQUIRY Data";
                        evpdDecodedPage = EVPD.PrettifyPage_86(page.Value);
                    }
                    else if (page.Key == 0x89)
                    {
                        evpdPageTitle   = "SCSI to ATA Translation Layer Data";
                        evpdDecodedPage = EVPD.PrettifyPage_89(page.Value);
                    }
                    else if (page.Key == 0xB0)
                    {
                        evpdPageTitle   = "SCSI Sequential-access Device Capabilities";
                        evpdDecodedPage = EVPD.PrettifyPage_B0(page.Value);
                    }
                    else if (page.Key == 0xB1)
                    {
                        evpdPageTitle   = "Manufacturer-assigned Serial Number";
                        evpdDecodedPage = EVPD.DecodePageB1(page.Value);
                    }
                    else if (page.Key == 0xB2)
                    {
                        evpdPageTitle   = "TapeAlert Supported Flags Bitmap";
                        evpdDecodedPage = $"0x{EVPD.DecodePageB2(page.Value):X16}";
                    }
                    else if (page.Key == 0xB3)
                    {
                        evpdPageTitle   = "Automation Device Serial Number";
                        evpdDecodedPage = EVPD.DecodePageB3(page.Value);
                    }
                    else if (page.Key == 0xB4)
                    {
                        evpdPageTitle   = "Data Transfer Device Element Address";
                        evpdDecodedPage = EVPD.DecodePageB4(page.Value);
                    }
                    else if (page.Key == 0xC0 &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "quantum")
                    {
                        evpdPageTitle   = "Quantum Firmware Build Information page";
                        evpdDecodedPage = EVPD.PrettifyPage_C0_Quantum(page.Value);
                    }
                    else if (page.Key == 0xC0 &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "seagate")
                    {
                        evpdPageTitle   = "Seagate Firmware Numbers page";
                        evpdDecodedPage = EVPD.PrettifyPage_C0_Seagate(page.Value);
                    }
                    else if (page.Key == 0xC0 &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "ibm")
                    {
                        evpdPageTitle   = "IBM Drive Component Revision Levels page";
                        evpdDecodedPage = EVPD.PrettifyPage_C0_IBM(page.Value);
                    }
                    else if (page.Key == 0xC1 &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "ibm")
                    {
                        evpdPageTitle   = "IBM Drive Serial Numbers page";
                        evpdDecodedPage = EVPD.PrettifyPage_C1_IBM(page.Value);
                    }
                    else if ((page.Key == 0xC0 || page.Key == 0xC1) &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "certance")
                    {
                        evpdPageTitle   = "Certance Drive Component Revision Levels page";
                        evpdDecodedPage = EVPD.PrettifyPage_C0_C1_Certance(page.Value);
                    }
                    else if ((page.Key == 0xC2 || page.Key == 0xC3 || page.Key == 0xC4 || page.Key == 0xC5 ||
                              page.Key == 0xC6) &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "certance")
                    {
                        switch (page.Key)
                        {
                        case 0xC2:
                            evpdPageTitle = "Head Assembly Serial Number";

                            break;

                        case 0xC3:
                            evpdPageTitle = "Reel Motor 1 Serial Number";

                            break;

                        case 0xC4:
                            evpdPageTitle = "Reel Motor 2 Serial Number";

                            break;

                        case 0xC5:
                            evpdPageTitle = "Board Serial Number";

                            break;

                        case 0xC6:
                            evpdPageTitle = "Base Mechanical Serial Number";

                            break;
                        }

                        evpdDecodedPage = EVPD.PrettifyPage_C2_C3_C4_C5_C6_Certance(page.Value);
                    }
                    else if ((page.Key == 0xC0 || page.Key == 0xC1 || page.Key == 0xC2 || page.Key == 0xC3 ||
                              page.Key == 0xC4 || page.Key == 0xC5) &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "hp")
                    {
                        switch (page.Key)
                        {
                        case 0xC0:
                            evpdPageTitle = "HP Drive Firmware Revision Levels page:";

                            break;

                        case 0xC1:
                            evpdPageTitle = "HP Drive Hardware Revision Levels page:";

                            break;

                        case 0xC2:
                            evpdPageTitle = "HP Drive PCA Revision Levels page:";

                            break;

                        case 0xC3:
                            evpdPageTitle = "HP Drive Mechanism Revision Levels page:";

                            break;

                        case 0xC4:
                            evpdPageTitle = "HP Drive Head Assembly Revision Levels page:";

                            break;

                        case 0xC5:
                            evpdPageTitle = "HP Drive ACI Revision Levels page:";

                            break;
                        }

                        evpdDecodedPage = EVPD.PrettifyPage_C0_to_C5_HP(page.Value);
                    }
                    else if (page.Key == 0xDF &&
                             StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant().
                             Trim() == "certance")
                    {
                        evpdPageTitle   = "Certance drive status page";
                        evpdDecodedPage = EVPD.PrettifyPage_DF_Certance(page.Value);
                    }
                    else
                    {
                        if (page.Key == 0x00)
                        {
                            continue;
                        }

                        evpdPageTitle   = $"Page {page.Key:X2}h";
                        evpdDecodedPage = "Undecoded";

                        AaruConsole.DebugWriteLine("Device-Info command", "Found undecoded SCSI VPD page 0x{0:X2}",
                                                   page.Key);
                    }

                    EvpdPages.Add(new ScsiPageModel
                    {
                        Page        = evpdPageTitle,
                        Data        = page.Value,
                        Description = evpdDecodedPage
                    });
                }
            }

            if (_configuration != null)
            {
                Features.SeparatedFeatures ftr = Features.Separate(_configuration);

                AaruConsole.DebugWriteLine("Device-Info command", "GET CONFIGURATION length is {0} bytes",
                                           ftr.DataLength);

                AaruConsole.DebugWriteLine("Device-Info command", "GET CONFIGURATION current profile is {0:X4}h",
                                           ftr.CurrentProfile);

                if (ftr.Descriptors != null)
                {
                    foreach (Features.FeatureDescriptor desc in ftr.Descriptors)
                    {
                        string featureNumber = $"Feature {desc.Code:X4}h";
                        string featureDescription;
                        AaruConsole.DebugWriteLine("Device-Info command", "Feature {0:X4}h", desc.Code);

                        switch (desc.Code)
                        {
                        case 0x0000:
                            featureDescription = Features.Prettify_0000(desc.Data);

                            break;

                        case 0x0001:
                            featureDescription = Features.Prettify_0001(desc.Data);

                            break;

                        case 0x0002:
                            featureDescription = Features.Prettify_0002(desc.Data);

                            break;

                        case 0x0003:
                            featureDescription = Features.Prettify_0003(desc.Data);

                            break;

                        case 0x0004:
                            featureDescription = Features.Prettify_0004(desc.Data);

                            break;

                        case 0x0010:
                            featureDescription = Features.Prettify_0010(desc.Data);

                            break;

                        case 0x001D:
                            featureDescription = Features.Prettify_001D(desc.Data);

                            break;

                        case 0x001E:
                            featureDescription = Features.Prettify_001E(desc.Data);

                            break;

                        case 0x001F:
                            featureDescription = Features.Prettify_001F(desc.Data);

                            break;

                        case 0x0020:
                            featureDescription = Features.Prettify_0020(desc.Data);

                            break;

                        case 0x0021:
                            featureDescription = Features.Prettify_0021(desc.Data);

                            break;

                        case 0x0022:
                            featureDescription = Features.Prettify_0022(desc.Data);

                            break;

                        case 0x0023:
                            featureDescription = Features.Prettify_0023(desc.Data);

                            break;

                        case 0x0024:
                            featureDescription = Features.Prettify_0024(desc.Data);

                            break;

                        case 0x0025:
                            featureDescription = Features.Prettify_0025(desc.Data);

                            break;

                        case 0x0026:
                            featureDescription = Features.Prettify_0026(desc.Data);

                            break;

                        case 0x0027:
                            featureDescription = Features.Prettify_0027(desc.Data);

                            break;

                        case 0x0028:
                            featureDescription = Features.Prettify_0028(desc.Data);

                            break;

                        case 0x0029:
                            featureDescription = Features.Prettify_0029(desc.Data);

                            break;

                        case 0x002A:
                            featureDescription = Features.Prettify_002A(desc.Data);

                            break;

                        case 0x002B:
                            featureDescription = Features.Prettify_002B(desc.Data);

                            break;

                        case 0x002C:
                            featureDescription = Features.Prettify_002C(desc.Data);

                            break;

                        case 0x002D:
                            featureDescription = Features.Prettify_002D(desc.Data);

                            break;

                        case 0x002E:
                            featureDescription = Features.Prettify_002E(desc.Data);

                            break;

                        case 0x002F:
                            featureDescription = Features.Prettify_002F(desc.Data);

                            break;

                        case 0x0030:
                            featureDescription = Features.Prettify_0030(desc.Data);

                            break;

                        case 0x0031:
                            featureDescription = Features.Prettify_0031(desc.Data);

                            break;

                        case 0x0032:
                            featureDescription = Features.Prettify_0032(desc.Data);

                            break;

                        case 0x0033:
                            featureDescription = Features.Prettify_0033(desc.Data);

                            break;

                        case 0x0035:
                            featureDescription = Features.Prettify_0035(desc.Data);

                            break;

                        case 0x0037:
                            featureDescription = Features.Prettify_0037(desc.Data);

                            break;

                        case 0x0038:
                            featureDescription = Features.Prettify_0038(desc.Data);

                            break;

                        case 0x003A:
                            featureDescription = Features.Prettify_003A(desc.Data);

                            break;

                        case 0x003B:
                            featureDescription = Features.Prettify_003B(desc.Data);

                            break;

                        case 0x0040:
                            featureDescription = Features.Prettify_0040(desc.Data);

                            break;

                        case 0x0041:
                            featureDescription = Features.Prettify_0041(desc.Data);

                            break;

                        case 0x0042:
                            featureDescription = Features.Prettify_0042(desc.Data);

                            break;

                        case 0x0050:
                            featureDescription = Features.Prettify_0050(desc.Data);

                            break;

                        case 0x0051:
                            featureDescription = Features.Prettify_0051(desc.Data);

                            break;

                        case 0x0080:
                            featureDescription = Features.Prettify_0080(desc.Data);

                            break;

                        case 0x0100:
                            featureDescription = Features.Prettify_0100(desc.Data);

                            break;

                        case 0x0101:
                            featureDescription = Features.Prettify_0101(desc.Data);

                            break;

                        case 0x0102:
                            featureDescription = Features.Prettify_0102(desc.Data);

                            break;

                        case 0x0103:
                            featureDescription = Features.Prettify_0103(desc.Data);

                            break;

                        case 0x0104:
                            featureDescription = Features.Prettify_0104(desc.Data);

                            break;

                        case 0x0105:
                            featureDescription = Features.Prettify_0105(desc.Data);

                            break;

                        case 0x0106:
                            featureDescription = Features.Prettify_0106(desc.Data);

                            break;

                        case 0x0107:
                            featureDescription = Features.Prettify_0107(desc.Data);

                            break;

                        case 0x0108:
                            featureDescription = Features.Prettify_0108(desc.Data);

                            break;

                        case 0x0109:
                            featureDescription = Features.Prettify_0109(desc.Data);

                            break;

                        case 0x010A:
                            featureDescription = Features.Prettify_010A(desc.Data);

                            break;

                        case 0x010B:
                            featureDescription = Features.Prettify_010B(desc.Data);

                            break;

                        case 0x010C:
                            featureDescription = Features.Prettify_010C(desc.Data);

                            break;

                        case 0x010D:
                            featureDescription = Features.Prettify_010D(desc.Data);

                            break;

                        case 0x010E:
                            featureDescription = Features.Prettify_010E(desc.Data);

                            break;

                        case 0x0110:
                            featureDescription = Features.Prettify_0110(desc.Data);

                            break;

                        case 0x0113:
                            featureDescription = Features.Prettify_0113(desc.Data);

                            break;

                        case 0x0142:
                            featureDescription = Features.Prettify_0142(desc.Data);

                            break;

                        default:
                            featureDescription = "Unknown feature";

                            break;
                        }

                        MmcFeatures.Add(new ScsiPageModel
                        {
                            Page        = featureNumber,
                            Description = featureDescription
                        });
                    }
                }
                else
                {
                    AaruConsole.DebugWriteLine("Device-Info command",
                                               "GET CONFIGURATION returned no feature descriptors");
                }
            }
        }
Example #6
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.");
                                }
                            }
                        }
Example #7
0
        /// <summary>
        /// Generate a Drive object from a single device
        /// </summary>
        /// <param name="devicePath">Path to the device</param>
        /// <param name="ignoreFixedDrives">True to ignore fixed drives from population, false otherwise</param>
        /// <returns>Drive object for the device, null on error</returns>
        private static Drive GetDriveFromDevice(string devicePath, bool ignoreFixedDrives)
        {
            if (devicePath.Length == 2 &&
                devicePath[1] == ':' &&
                devicePath[0] != '/' &&
                char.IsLetter(devicePath[0]))
            {
                devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
            }

            string windowsLocalDevicePath = devicePath;

            if (windowsLocalDevicePath.StartsWith("\\\\.\\"))
            {
                windowsLocalDevicePath = windowsLocalDevicePath.Substring("\\\\.\\".Length);
            }

            var dev = Device.Create(devicePath, out _);

            if (dev == null || dev.Error)
            {
                return(null);
            }

            var devInfo = new Aaru.Core.Devices.Info.DeviceInfo(dev);

            if (devInfo.MmcConfiguration != null)
            {
                Features.SeparatedFeatures ftr = Features.Separate(devInfo.MmcConfiguration);
                if (ftr.Descriptors != null && ftr.Descriptors.Any(d => d.Code == 0x0000))
                {
                    var  desc      = ftr.Descriptors.First(d => d.Code == 0x0000);
                    bool isOptical = IsOptical(desc.Data);
                    if (isOptical)
                    {
                        return(Create(Data.InternalDriveType.Optical, windowsLocalDevicePath));
                    }
                    else if (!ignoreFixedDrives)
                    {
                        return(Create(Data.InternalDriveType.Removable, windowsLocalDevicePath));
                    }
                }
            }

            if (!ignoreFixedDrives)
            {
                switch (dev.Type)
                {
                case DeviceType.MMC:
                    return(Create(Data.InternalDriveType.Removable, windowsLocalDevicePath));

                case DeviceType.SecureDigital:
                    return(Create(Data.InternalDriveType.Removable, windowsLocalDevicePath));
                }

                if (dev.IsUsb)
                {
                    return(Create(Data.InternalDriveType.Removable, windowsLocalDevicePath));
                }

                if (dev.IsFireWire)
                {
                    return(Create(Data.InternalDriveType.Removable, windowsLocalDevicePath));
                }

                if (dev.IsPcmcia)
                {
                    return(Create(Data.InternalDriveType.Removable, windowsLocalDevicePath));
                }
            }

            dev.Close();
            return(null);
        }
Example #8
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);
        }