Пример #1
0
        static bool GetSectorForPregapRaw(Device dev, uint lba, Database.Models.Device dbDev, out byte[] subBuf)
        {
            byte[] cmdBuf;
            bool   sense;

            subBuf = null;

            sense = dev.ReadCd(out cmdBuf, out _, lba, 2448, 1, MmcSectorTypes.AllTypes, false, false, true,
                               MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Raw,
                               dev.Timeout, out _);

            if (sense)
            {
                sense = dev.ReadCd(out cmdBuf, out _, lba, 2448, 1, MmcSectorTypes.Cdda, false, false, false,
                                   MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout,
                                   out _);
            }

            if (!sense)
            {
                byte[] tmpBuf = new byte[96];
                Array.Copy(cmdBuf, 2352, tmpBuf, 0, 96);
                subBuf = DeinterleaveQ(tmpBuf);
            }
            else
            {
                sense = dev.ReadCd(out cmdBuf, out _, lba, 96, 1, MmcSectorTypes.AllTypes, false, false, false,
                                   MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Raw,
                                   dev.Timeout, out _);

                if (sense)
                {
                    sense = dev.ReadCd(out cmdBuf, out _, lba, 96, 1, MmcSectorTypes.Cdda, false, false, false,
                                       MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Raw,
                                       dev.Timeout, out _);
                }

                if (!sense)
                {
                    subBuf = DeinterleaveQ(cmdBuf);
                }
                else if (dbDev?.ATAPI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true ||
                         dbDev?.SCSI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true ||
                         dev.Manufacturer.ToLowerInvariant() == "plextor")
                {
                    sense = dev.PlextorReadCdDa(out cmdBuf, out _, lba, 96, 1, PlextorSubchannel.All, dev.Timeout,
                                                out _);
                }

                {
                    if (!sense)
                    {
                        subBuf = DeinterleaveQ(cmdBuf);
                    }
                }
            }

            return(sense);
        }
Пример #2
0
        static bool GetSectorForPregapQ16(Device dev, uint lba, Database.Models.Device dbDev, out byte[] subBuf,
                                          bool audioTrack)
        {
            byte[] cmdBuf;
            bool   sense;
            subBuf = null;

            if(audioTrack)
            {
                sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.Cdda, false, false, false,
                                   MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout,
                                   out _);

                if(sense)
                    sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.AllTypes, false, false, true,
                                       MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16,
                                       dev.Timeout, out _);
            }
            else
            {
                sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.AllTypes, false, false, true,
                                   MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16,
                                   dev.Timeout, out _);

                if(sense)
                    sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.Cdda, false, false, false,
                                       MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Q16,
                                       dev.Timeout, out _);
            }

            if(!sense)
            {
                subBuf = new byte[16];
                Array.Copy(cmdBuf, 2352, subBuf, 0, 16);
            }
            else
            {
                sense = dev.ReadCd(out cmdBuf, out _, lba, 16, 1, MmcSectorTypes.AllTypes, false, false, false,
                                   MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Q16,
                                   dev.Timeout, out _);

                if(sense)
                    sense = dev.ReadCd(out cmdBuf, out _, lba, 16, 1, MmcSectorTypes.Cdda, false, false, false,
                                       MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Q16,
                                       dev.Timeout, out _);

                if(!sense)
                    subBuf = cmdBuf;
            }

            return sense;
        }
Пример #3
0
        public static void SolveTrackPregaps(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus,
                                             Track[] tracks, bool supportsPqSubchannel, bool supportsRwSubchannel,
                                             Database.Models.Device dbDev, out bool inexactPositioning)
        {
            bool                  sense  = true; // Sense indicator
            byte[]                subBuf = null;
            int                   posQ;
            uint                  retries;
            bool?                 bcd = null;
            byte[]                crc;
            Dictionary<uint, int> pregaps = new Dictionary<uint, int>();
            inexactPositioning = false;

            if(!supportsPqSubchannel &&
               !supportsRwSubchannel)
                return;

            // Check if subchannel is BCD
            for(retries = 0; retries < 10; retries++)
            {
                sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, 11, dbDev, out subBuf, false)
                            : GetSectorForPregapQ16(dev, 11, dbDev, out subBuf, false);

                if(sense)
                    continue;

                bcd = (subBuf[9] & 0x10) > 0;

                break;
            }

            AaruConsole.DebugWriteLine("Pregap calculator", bcd == true
                                                                ? "Subchannel is BCD"
                                                                : bcd == false
                                                                    ? "Subchannel is not BCD"
                                                                    : "Could not detect drive subchannel BCD");

            if(bcd is null)
            {
                dumpLog?.WriteLine("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect...");

                updateStatus?.
                    Invoke("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect...");

                return;
            }

            // Initialize the dictionary
            for(int i = 0; i < tracks.Length; i++)
                pregaps[tracks[i].TrackSequence] = 0;

            for(int t = 0; t < tracks.Length; t++)
            {
                Track track        = tracks[t];
                int   trackRetries = 0;

                // First track of each session has at least 150 sectors of pregap and is not readable always
                if(tracks.Where(t => t.TrackSession == track.TrackSession).OrderBy(t => t.TrackSequence).
                          FirstOrDefault().TrackSequence == track.TrackSequence)
                {
                    AaruConsole.DebugWriteLine("Pregap calculator", "Skipping track {0}", track.TrackSequence);

                    if(track.TrackSequence > 1)
                        pregaps[track.TrackSequence] = 150;

                    continue;
                }

                if(t                       > 0 &&
                   tracks[t - 1].TrackType == tracks[t].TrackType)
                {
                    AaruConsole.DebugWriteLine("Pregap calculator", "Skipping track {0}", track.TrackSequence);

                    continue;
                }

                AaruConsole.DebugWriteLine("Pregap calculator", "Track {0}", track.TrackSequence);

                int   lba           = (int)track.TrackStartSector - 1;
                bool  pregapFound   = false;
                Track previousTrack = tracks.FirstOrDefault(t => t.TrackSequence == track.TrackSequence - 1);

                bool goneBack                      = false;
                bool goFront                       = false;
                bool forward                       = false;
                bool crcOk                         = false;
                bool previousPregapIsPreviousTrack = false;

                // Check if pregap is 0
                for(retries = 0; retries < 10 && !pregapFound; retries++)
                {
                    sense = supportsRwSubchannel
                                ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf,
                                                        track.TrackType == TrackType.Audio)
                                : GetSectorForPregapQ16(dev, (uint)lba, dbDev, out subBuf,
                                                        track.TrackType == TrackType.Audio);

                    if(sense)
                    {
                        AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}", lba,
                                                   retries + 1, sense);

                        continue;
                    }

                    if(bcd == false)
                        BinaryToBcdQ(subBuf);

                    CRC16CCITTContext.Data(subBuf, 10, out crc);

                    AaruConsole.DebugWriteLine("Pregap calculator",
                                               "LBA: {0}, Try {1}, Sense {2}, Q: {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}",
                                               lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], subBuf[3],
                                               subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], subBuf[9],
                                               subBuf[10], subBuf[11], crc[0], crc[1]);

                    crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11];

                    // Try to do a simple correction
                    if(!crcOk)
                    {
                        // Data track cannot have 11xxb in CONTROL
                        if((subBuf[0] & 0x40) > 0)
                            subBuf[0] &= 0x7F;

                        // ADR only uses two bits
                        subBuf[0] &= 0xF3;

                        // Don't care about other Q modes
                        if((subBuf[0] & 0xF) == 1)
                        {
                            // ZERO only used in DDCD
                            subBuf[6] = 0;

                            // Fix BCD numbering
                            for(int i = 1; i < 10; i++)
                            {
                                if((subBuf[i] & 0xF0) > 0xA0)
                                    subBuf[i] &= 0x7F;

                                if((subBuf[i] & 0x0F) > 0x0A)
                                    subBuf[i] &= 0xF7;
                            }
                        }

                        CRC16CCITTContext.Data(subBuf, 10, out crc);

                        crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11];

                        if(crcOk)
                        {
                            AaruConsole.DebugWriteLine("Pregap calculator",
                                                       "LBA: {0}, Try {1}, Sense {2}, Q (FIXED): {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}",
                                                       lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2],
                                                       subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8],
                                                       subBuf[9], subBuf[10], subBuf[11], crc[0], crc[1]);
                        }
                        else
                            continue;
                    }

                    BcdToBinaryQ(subBuf);

                    // Q position
                    if((subBuf[0] & 0xF) != 1)
                        continue;

                    posQ = ((subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9]) - 150;

                    if(subBuf[1] != track.TrackSequence - 1 ||
                       subBuf[2] == 0                       ||
                       posQ      != lba)
                        break;

                    pregaps[track.TrackSequence] = 0;

                    pregapFound = true;
                }

                if(pregapFound)
                    continue;

                // Calculate pregap
                lba = (int)track.TrackStartSector - 150;

                while(lba > (int)previousTrack.TrackStartSector &&
                      lba <= (int)track.TrackStartSector)
                {
                    // Some drives crash if you try to read just before the previous read, so seek away first
                    if(!forward)
                        sense = supportsRwSubchannel
                                    ? GetSectorForPregapRaw(dev, (uint)lba - 10, dbDev, out subBuf,
                                                            track.TrackType == TrackType.Audio)
                                    : GetSectorForPregapQ16(dev, (uint)lba - 10, dbDev, out subBuf,
                                                            track.TrackType == TrackType.Audio);

                    for(retries = 0; retries < 10; retries++)
                    {
                        sense = supportsRwSubchannel
                                    ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf,
                                                            track.TrackType == TrackType.Audio)
                                    : GetSectorForPregapQ16(dev, (uint)lba, dbDev, out subBuf,
                                                            track.TrackType == TrackType.Audio);

                        if(sense)
                            continue;

                        if(bcd == false)
                            BinaryToBcdQ(subBuf);

                        CRC16CCITTContext.Data(subBuf, 10, out crc);

                        AaruConsole.DebugWriteLine("Pregap calculator",
                                                   "LBA: {0}, Try {1}, Sense {2}, Q: {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}",
                                                   lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], subBuf[3],
                                                   subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], subBuf[9],
                                                   subBuf[10], subBuf[11], crc[0], crc[1]);

                        crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11];

                        // Try to do a simple correction
                        if(!crcOk)
                        {
                            // Data track cannot have 11xxb in CONTROL
                            if((subBuf[0] & 0x40) > 0)
                                subBuf[0] &= 0x7F;

                            // ADR only uses two bits
                            subBuf[0] &= 0xF3;

                            // Don't care about other Q modes
                            if((subBuf[0] & 0xF) == 1)
                            {
                                // ZERO only used in DDCD
                                subBuf[6] = 0;

                                // Fix BCD numbering
                                for(int i = 1; i < 10; i++)
                                {
                                    if((subBuf[i] & 0xF0) > 0xA0)
                                        subBuf[i] &= 0x7F;

                                    if((subBuf[i] & 0x0F) > 0x0A)
                                        subBuf[i] &= 0xF7;
                                }
                            }

                            CRC16CCITTContext.Data(subBuf, 10, out crc);

                            crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11];

                            if(crcOk)
                            {
                                AaruConsole.DebugWriteLine("Pregap calculator",
                                                           "LBA: {0}, Try {1}, Sense {2}, Q (FIXED): {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}",
                                                           lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2],
                                                           subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7],
                                                           subBuf[8], subBuf[9], subBuf[10], subBuf[11], crc[0],
                                                           crc[1]);

                                break;
                            }
                        }

                        if(crcOk)
                            break;
                    }

                    if(retries == 10)
                    {
                        if(sense)
                        {
                            trackRetries++;

                            if(trackRetries >= 10)
                            {
                                if(pregaps[track.TrackSequence] == 0)
                                {
                                    if((previousTrack.TrackType == TrackType.Audio &&
                                        track.TrackType         != TrackType.Audio) ||
                                       (previousTrack.TrackType != TrackType.Audio &&
                                        track.TrackType         == TrackType.Audio))
                                    {
                                        dumpLog?.
                                            WriteLine("Could not read subchannel for this track, supposing 150 sectors.");

                                        updateStatus?.
                                            Invoke("Could not read subchannel for this track, supposing 150 sectors.");
                                    }
                                    else
                                    {
                                        dumpLog?.
                                            WriteLine("Could not read subchannel for this track, supposing 0 sectors.");

                                        updateStatus?.
                                            Invoke("Could not read subchannel for this track, supposing 0 sectors.");
                                    }
                                }
                                else
                                {
                                    dumpLog?.
                                        WriteLine($"Could not read subchannel for this track, supposing {pregaps[track.TrackSequence]} sectors.");

                                    updateStatus?.
                                        Invoke($"Could not read subchannel for this track, supposing {pregaps[track.TrackSequence]} sectors.");
                                }

                                break;
                            }

                            dumpLog?.WriteLine($"Could not read subchannel for sector {lba}");
                            updateStatus?.Invoke($"Could not read subchannel for sector {lba}");

                            lba++;
                            forward = true;

                            continue;
                        }

                        dumpLog?.WriteLine($"Could not get correct subchannel for sector {lba}");
                        updateStatus?.Invoke($"Could not get correct subchannel for sector {lba}");
                    }

                    if(subBuf.All(b => b == 0))
                    {
                        inexactPositioning = true;

                        AaruConsole.DebugWriteLine("Pregap calculator", "All Q empty for LBA {0}", lba);

                        break;
                    }

                    BcdToBinaryQ(subBuf);

                    // If it's not Q position
                    if((subBuf[0] & 0xF) != 1)
                    {
                        // This means we already searched back, so search forward
                        if(goFront)
                        {
                            lba++;
                            forward = true;

                            if(lba == (int)previousTrack.TrackStartSector)
                                break;

                            continue;
                        }

                        // Search back
                        goneBack = true;
                        lba--;
                        forward = false;

                        continue;
                    }

                    // Previous track
                    if(subBuf[1] < track.TrackSequence)
                    {
                        lba++;
                        forward                       = true;
                        previousPregapIsPreviousTrack = true;

                        // Already gone back, so go forward
                        if(goneBack)
                            goFront = true;

                        continue;
                    }

                    // Same track, but not pregap
                    if(subBuf[1] == track.TrackSequence &&
                       subBuf[2] > 0)
                    {
                        lba--;
                        forward = false;

                        if(previousPregapIsPreviousTrack)
                            break;

                        continue;
                    }

                    previousPregapIsPreviousTrack = false;

                    // Pregap according to Q position
                    posQ = ((subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9]) - 150;
                    int diff    = posQ                               - lba;
                    int pregapQ = (int)track.TrackStartSector        - lba;

                    if(diff != 0)
                    {
                        AaruConsole.DebugWriteLine("Pregap calculator", "Invalid Q position for LBA {0}, got {1}", lba,
                                                   posQ);

                        inexactPositioning = true;
                    }

                    // Received a Q post the LBA we wanted, just go back. If we are already going forward, break
                    if(posQ > lba)
                    {
                        if(forward)
                            break;

                        lba--;

                        continue;
                    }

                    // Bigger than known change, otherwise we found it
                    if(pregapQ > pregaps[track.TrackSequence])
                    {
                        // If CRC is not OK, only accept pregaps less than 10 sectors longer than previously now
                        if(crcOk || pregapQ - pregaps[track.TrackSequence] < 10)
                        {
                            AaruConsole.DebugWriteLine("Pregap calculator", "Pregap for track {0}: {1}",
                                                       track.TrackSequence, pregapQ);

                            pregaps[track.TrackSequence] = pregapQ;
                        }

                        // We are going forward, so we have already been in the previous track, so add 1 to pregap and get out of here
                        else if(forward)
                        {
                            pregaps[track.TrackSequence]++;

                            break;
                        }
                    }
                    else if(pregapQ == pregaps[track.TrackSequence])
                        break;

                    lba--;
                    forward = false;
                }
            }

            for(int i = 0; i < tracks.Length; i++)
            {
                tracks[i].TrackPregap      =  (ulong)pregaps[tracks[i].TrackSequence];
                tracks[i].TrackStartSector -= tracks[i].TrackPregap;

            #if DEBUG
                dumpLog?.WriteLine($"Track {tracks[i].TrackSequence} pregap is {tracks[i].TrackPregap} sectors");
                updateStatus?.Invoke($"Track {tracks[i].TrackSequence} pregap is {tracks[i].TrackPregap} sectors");
            #endif
            }
        }
Пример #4
0
        static void DoScsiMediaInfo(bool debug, bool verbose, string outputPrefix, Devices.Device dev)
        {
            var scsiInfo = new ScsiInfo(dev);

            if (!scsiInfo.MediaInserted)
            {
                return;
            }

            if (scsiInfo.DeviceInfo.ScsiModeSense6 != null)
            {
                DataFile.WriteTo("Media-Info command", outputPrefix, "_scsi_modesense6.bin", "SCSI MODE SENSE (6)",
                                 scsiInfo.DeviceInfo.ScsiModeSense6);
            }

            if (scsiInfo.DeviceInfo.ScsiModeSense10 != null)
            {
                DataFile.WriteTo("Media-Info command", outputPrefix, "_scsi_modesense10.bin", "SCSI MODE SENSE (10)",
                                 scsiInfo.DeviceInfo.ScsiModeSense10);
            }

            switch (dev.ScsiType)
            {
            case PeripheralDeviceTypes.DirectAccess:
            case PeripheralDeviceTypes.MultiMediaDevice:
            case PeripheralDeviceTypes.OCRWDevice:
            case PeripheralDeviceTypes.OpticalDevice:
            case PeripheralDeviceTypes.SimplifiedDevice:
            case PeripheralDeviceTypes.WriteOnceDevice:
                if (scsiInfo.ReadCapacity != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readcapacity.bin", "SCSI READ CAPACITY",
                                     scsiInfo.ReadCapacity);
                }

                if (scsiInfo.ReadCapacity16 != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readcapacity16.bin",
                                     "SCSI READ CAPACITY(16)", scsiInfo.ReadCapacity16);
                }

                if (scsiInfo.Blocks != 0 &&
                    scsiInfo.BlockSize != 0)
                {
                    AaruConsole.WriteLine("Media has {0} blocks of {1} bytes/each. (for a total of {2} bytes)",
                                          scsiInfo.Blocks, scsiInfo.BlockSize,
                                          scsiInfo.Blocks * scsiInfo.BlockSize);
                }

                break;

            case PeripheralDeviceTypes.SequentialAccess:
                if (scsiInfo.DensitySupport != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_ssc_reportdensitysupport_media.bin",
                                     "SSC REPORT DENSITY SUPPORT (MEDIA)", scsiInfo.DensitySupport);

                    if (scsiInfo.DensitySupportHeader.HasValue)
                    {
                        AaruConsole.WriteLine("Densities supported by currently inserted media:");
                        AaruConsole.WriteLine(DensitySupport.PrettifyDensity(scsiInfo.DensitySupportHeader));
                    }
                }

                if (scsiInfo.MediaTypeSupport != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix,
                                     "_ssc_reportdensitysupport_medium_media.bin",
                                     "SSC REPORT DENSITY SUPPORT (MEDIUM & MEDIA)", scsiInfo.MediaTypeSupport);

                    if (scsiInfo.MediaTypeSupportHeader.HasValue)
                    {
                        AaruConsole.WriteLine("Medium types currently inserted in device:");
                        AaruConsole.WriteLine(DensitySupport.PrettifyMediumType(scsiInfo.MediaTypeSupportHeader));
                    }

                    AaruConsole.WriteLine(DensitySupport.PrettifyMediumType(scsiInfo.MediaTypeSupport));
                }

                break;
            }

            if (dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice)
            {
                if (scsiInfo.MmcConfiguration != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_getconfiguration_current.bin",
                                     "SCSI GET CONFIGURATION", scsiInfo.MmcConfiguration);
                }

                if (scsiInfo.RecognizedFormatLayers != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_formatlayers.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.RecognizedFormatLayers);
                }

                if (scsiInfo.WriteProtectionStatus != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_writeprotection.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.WriteProtectionStatus);
                }

                if (scsiInfo.DvdPfi != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_pfi.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdPfi);

                    if (scsiInfo.DecodedPfi.HasValue)
                    {
                        AaruConsole.WriteLine("PFI:\n{0}", PFI.Prettify(scsiInfo.DecodedPfi));
                    }
                }

                if (scsiInfo.DvdDmi != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_dmi.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdDmi);

                    if (DMI.IsXbox(scsiInfo.DvdDmi))
                    {
                        AaruConsole.WriteLine("Xbox DMI:\n{0}", DMI.PrettifyXbox(scsiInfo.DvdDmi));
                    }
                    else if (DMI.IsXbox360(scsiInfo.DvdDmi))
                    {
                        AaruConsole.WriteLine("Xbox 360 DMI:\n{0}", DMI.PrettifyXbox360(scsiInfo.DvdDmi));
                    }
                }

                if (scsiInfo.DvdCmi != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_cmi.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdCmi);

                    AaruConsole.WriteLine("Lead-In CMI:\n{0}", CSS_CPRM.PrettifyLeadInCopyright(scsiInfo.DvdCmi));
                }

                if (scsiInfo.DvdBca != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_bca.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdBca);
                }

                if (scsiInfo.DvdAacs != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_aacs.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdAacs);
                }

                if (scsiInfo.DvdRamDds != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvdram_dds.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdRamDds);

                    AaruConsole.WriteLine("Disc Definition Structure:\n{0}", DDS.Prettify(scsiInfo.DvdRamDds));
                }

                if (scsiInfo.DvdRamCartridgeStatus != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvdram_status.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdRamCartridgeStatus);

                    AaruConsole.WriteLine("Medium Status:\n{0}", Cartridge.Prettify(scsiInfo.DvdRamCartridgeStatus));
                }

                if (scsiInfo.DvdRamSpareArea != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvdram_spare.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdRamSpareArea);

                    AaruConsole.WriteLine("Spare Area Information:\n{0}", Spare.Prettify(scsiInfo.DvdRamSpareArea));
                }

                if (scsiInfo.LastBorderOutRmd != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_lastrmd.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.LastBorderOutRmd);
                }

                if (scsiInfo.DvdPreRecordedInfo != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_pri.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdPreRecordedInfo);
                }

                if (scsiInfo.DvdrMediaIdentifier != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvdr_mediaid.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdrMediaIdentifier);
                }

                if (scsiInfo.DvdrPhysicalInformation != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvdr_pfi.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdrPhysicalInformation);
                }

                if (scsiInfo.DvdPlusAdip != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd+_adip.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdPlusAdip);
                }

                if (scsiInfo.DvdPlusDcb != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd+_dcb.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdPlusDcb);
                }

                if (scsiInfo.HddvdCopyrightInformation != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_hddvd_cmi.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.HddvdCopyrightInformation);
                }

                if (scsiInfo.HddvdrMediumStatus != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_hddvdr_status.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.HddvdrMediumStatus);
                }

                if (scsiInfo.HddvdrLastRmd != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_hddvdr_lastrmd.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.HddvdrLastRmd);
                }

                if (scsiInfo.DvdrLayerCapacity != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvdr_layercap.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdrLayerCapacity);
                }

                if (scsiInfo.DvdrDlMiddleZoneStart != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_mzs.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdrDlMiddleZoneStart);
                }

                if (scsiInfo.DvdrDlJumpIntervalSize != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_jis.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdrDlJumpIntervalSize);
                }

                if (scsiInfo.DvdrDlManualLayerJumpStartLba != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_manuallj.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdrDlManualLayerJumpStartLba);
                }

                if (scsiInfo.DvdrDlRemapAnchorPoint != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_remapanchor.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.DvdrDlRemapAnchorPoint);
                }

                if (scsiInfo.BlurayDiscInformation != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_di.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.BlurayDiscInformation);

                    AaruConsole.WriteLine("Blu-ray Disc Information:\n{0}",
                                          DI.Prettify(scsiInfo.BlurayDiscInformation));
                }

                if (scsiInfo.BlurayPac != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_pac.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.BlurayPac);
                }

                if (scsiInfo.BlurayBurstCuttingArea != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_bca.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.BlurayBurstCuttingArea);

                    AaruConsole.WriteLine("Blu-ray Burst Cutting Area:\n{0}",
                                          BCA.Prettify(scsiInfo.BlurayBurstCuttingArea));
                }

                if (scsiInfo.BlurayDds != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_dds.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.BlurayDds);

                    AaruConsole.WriteLine("Blu-ray Disc Definition Structure:\n{0}",
                                          Decoders.Bluray.DDS.Prettify(scsiInfo.BlurayDds));
                }

                if (scsiInfo.BlurayCartridgeStatus != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_cartstatus.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.BlurayCartridgeStatus);

                    AaruConsole.WriteLine("Blu-ray Cartridge Status:\n{0}",
                                          Decoders.Bluray.Cartridge.Prettify(scsiInfo.BlurayCartridgeStatus));
                }

                if (scsiInfo.BluraySpareAreaInformation != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_spare.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.BluraySpareAreaInformation);

                    AaruConsole.WriteLine("Blu-ray Spare Area Information:\n{0}",
                                          Decoders.Bluray.Spare.Prettify(scsiInfo.BluraySpareAreaInformation));
                }

                if (scsiInfo.BlurayRawDfl != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_dfl.bin",
                                     "SCSI READ DISC STRUCTURE", scsiInfo.BlurayRawDfl);
                }

                if (scsiInfo.BlurayTrackResources != null)
                {
                    AaruConsole.WriteLine("Track Resources Information:\n{0}",
                                          DiscInformation.Prettify(scsiInfo.BlurayTrackResources));

                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscinformation_001b.bin",
                                     "SCSI READ DISC INFORMATION", scsiInfo.BlurayTrackResources);
                }

                if (scsiInfo.BlurayPowResources != null)
                {
                    AaruConsole.WriteLine("POW Resources Information:\n{0}",
                                          DiscInformation.Prettify(scsiInfo.BlurayPowResources));

                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscinformation_010b.bin",
                                     "SCSI READ DISC INFORMATION", scsiInfo.BlurayPowResources);
                }

                if (scsiInfo.Toc != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_toc.bin", "SCSI READ TOC/PMA/ATIP",
                                     scsiInfo.Toc);

                    if (scsiInfo.DecodedToc.HasValue)
                    {
                        AaruConsole.WriteLine("TOC:\n{0}", TOC.Prettify(scsiInfo.DecodedToc));
                    }
                }

                if (scsiInfo.Atip != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_atip.bin", "SCSI READ TOC/PMA/ATIP",
                                     scsiInfo.Atip);

                    if (scsiInfo.DecodedAtip.HasValue)
                    {
                        AaruConsole.WriteLine("ATIP:\n{0}", ATIP.Prettify(scsiInfo.DecodedAtip));
                    }
                }

                if (scsiInfo.CompactDiscInformation != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscinformation_000b.bin",
                                     "SCSI READ DISC INFORMATION", scsiInfo.CompactDiscInformation);

                    if (scsiInfo.DecodedCompactDiscInformation.HasValue)
                    {
                        AaruConsole.WriteLine("Standard Disc Information:\n{0}",
                                              DiscInformation.Prettify000b(scsiInfo.DecodedCompactDiscInformation));
                    }
                }

                if (scsiInfo.Session != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_session.bin", "SCSI READ TOC/PMA/ATIP",
                                     scsiInfo.Session);

                    if (scsiInfo.DecodedSession.HasValue)
                    {
                        AaruConsole.WriteLine("Session information:\n{0}", Session.Prettify(scsiInfo.DecodedSession));
                    }
                }

                if (scsiInfo.RawToc != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_rawtoc.bin", "SCSI READ TOC/PMA/ATIP",
                                     scsiInfo.RawToc);

                    if (scsiInfo.FullToc.HasValue)
                    {
                        AaruConsole.WriteLine("Raw TOC:\n{0}", FullTOC.Prettify(scsiInfo.RawToc));
                    }
                }

                if (scsiInfo.Pma != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_pma.bin", "SCSI READ TOC/PMA/ATIP",
                                     scsiInfo.Pma);

                    AaruConsole.WriteLine("PMA:\n{0}", PMA.Prettify(scsiInfo.Pma));
                }

                if (scsiInfo.CdTextLeadIn != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_cdtext.bin", "SCSI READ TOC/PMA/ATIP",
                                     scsiInfo.CdTextLeadIn);

                    if (scsiInfo.DecodedCdTextLeadIn.HasValue)
                    {
                        AaruConsole.WriteLine("CD-TEXT on Lead-In:\n{0}",
                                              CDTextOnLeadIn.Prettify(scsiInfo.DecodedCdTextLeadIn));
                    }
                }

                if (!string.IsNullOrEmpty(scsiInfo.Mcn))
                {
                    AaruConsole.WriteLine("MCN: {0}", scsiInfo.Mcn);
                }

                if (scsiInfo.Isrcs != null)
                {
                    foreach (KeyValuePair <byte, string> isrc in scsiInfo.Isrcs)
                    {
                        AaruConsole.WriteLine("Track's {0} ISRC: {1}", isrc.Key, isrc.Value);
                    }
                }

                if (scsiInfo.XboxSecuritySector != null)
                {
                    DataFile.WriteTo("Media-Info command", outputPrefix, "_xbox_ss.bin", "KREON EXTRACT SS",
                                     scsiInfo.XboxSecuritySector);
                }

                if (scsiInfo.DecodedXboxSecuritySector.HasValue)
                {
                    AaruConsole.WriteLine("Xbox Security Sector:\n{0}",
                                          SS.Prettify(scsiInfo.DecodedXboxSecuritySector));
                }

                if (scsiInfo.XgdInfo != null)
                {
                    AaruConsole.WriteLine("Video layer 0 size: {0} sectors", scsiInfo.XgdInfo.L0Video);
                    AaruConsole.WriteLine("Video layer 1 size: {0} sectors", scsiInfo.XgdInfo.L1Video);
                    AaruConsole.WriteLine("Middle zone size: {0} sectors", scsiInfo.XgdInfo.MiddleZone);
                    AaruConsole.WriteLine("Game data size: {0} sectors", scsiInfo.XgdInfo.GameSize);
                    AaruConsole.WriteLine("Total size: {0} sectors", scsiInfo.XgdInfo.TotalSize);
                    AaruConsole.WriteLine("Real layer break: {0}", scsiInfo.XgdInfo.LayerBreak);
                    AaruConsole.WriteLine();
                }
            }

            if (scsiInfo.MediaSerialNumber != null)
            {
                DataFile.WriteTo("Media-Info command", outputPrefix, "_mediaserialnumber.bin",
                                 "SCSI READ MEDIA SERIAL NUMBER", scsiInfo.MediaSerialNumber);

                AaruConsole.Write("Media Serial Number: ");

                for (int i = 4; i < scsiInfo.MediaSerialNumber.Length; i++)
                {
                    AaruConsole.Write("{0:X2}", scsiInfo.MediaSerialNumber[i]);
                }

                AaruConsole.WriteLine();
            }

            AaruConsole.WriteLine("Media identified as {0}", scsiInfo.MediaType);
            Statistics.AddMedia(scsiInfo.MediaType, true);

            if (scsiInfo.Toc != null ||
                scsiInfo.RawToc != null)
            {
                uint blockSize = 2352;

                Track[] tracks = Dump.GetCdTracks(ref blockSize, dev, scsiInfo.MediaType, null, false,
                                                  out long lastSector, null, null, null, TrackSubchannelType.None,
                                                  out _, null, null);

                if (tracks != null)
                {
                    bool supportsPqSubchannel = Dump.SupportsPqSubchannel(dev, null, null);
                    bool supportsRwSubchannel = Dump.SupportsRwSubchannel(dev, null, null);

                    // Open master database
                    var ctx = AaruContext.Create(Settings.Settings.MasterDbPath);

                    // Search for device in master database
                    Database.Models.Device dbDev =
                        ctx.Devices.FirstOrDefault(d => d.Manufacturer == dev.Manufacturer && d.Model == dev.Model &&
                                                   d.Revision == dev.FirmwareRevision);

                    Dump.SolveTrackPregaps(dev, null, null, tracks, supportsPqSubchannel, supportsRwSubchannel, dbDev,
                                           out bool inexactPositioning);

                    for (int t = 1; t < tracks.Length; t++)
                    {
                        tracks[t - 1].TrackEndSector = tracks[t].TrackStartSector - 1;
                    }

                    tracks[tracks.Length - 1].TrackEndSector = (ulong)lastSector;

                    AaruConsole.WriteLine();
                    AaruConsole.WriteLine("Track calculations:");

                    if (inexactPositioning)
                    {
                        AaruConsole.
                        WriteLine("WARNING: The drive has returned incorrect Q positioning when calculating pregaps. A best effort has been tried but they may be incorrect.");
                    }

                    foreach (Track track in tracks)
                    {
                        AaruConsole.
                        WriteLine("Track {0} starts at LBA {1}, ends at LBA {2}, has a pregap of {3} sectors and is of type {4}",
                                  track.TrackSequence, track.TrackStartSector, track.TrackEndSector,
                                  track.TrackPregap, track.TrackType);
                    }

                    AaruConsole.WriteLine();
                    AaruConsole.WriteLine("Offsets:");

                    CdOffset cdOffset = null;

                    // Search for read offset in master database
                    cdOffset = ctx.CdOffsets.FirstOrDefault(d => d.Manufacturer == dev.Manufacturer &&
                                                            d.Model == dev.Model);

                    CompactDisc.GetOffset(cdOffset, dbDev, debug, dev, scsiInfo.MediaType, null, tracks, null,
                                          out int?driveOffset, out int?combinedOffset, out _);

                    if (combinedOffset is null)
                    {
                        if (driveOffset is null)
                        {
                            AaruConsole.WriteLine("Drive reading offset not found in database.");
                            AaruConsole.WriteLine("Disc offset cannot be calculated.");
                        }
                        else
                        {
                            AaruConsole.
                            WriteLine($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples).");

                            AaruConsole.WriteLine("Disc write offset is unknown.");
                        }
                    }
                    else
                    {
                        int offsetBytes = combinedOffset.Value;

                        if (driveOffset is null)
                        {
                            AaruConsole.WriteLine("Drive reading offset not found in database.");

                            AaruConsole.
                            WriteLine($"Combined disc and drive offset are {offsetBytes} bytes ({offsetBytes / 4} samples).");
                        }
                        else
                        {
                            AaruConsole.
                            WriteLine($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples).");

                            AaruConsole.
                            WriteLine($"Combined offset is {offsetBytes} bytes ({offsetBytes / 4} samples)");

                            int?discOffset = offsetBytes - driveOffset;

                            AaruConsole.WriteLine($"Disc offset is {discOffset} bytes ({discOffset / 4} samples)");
                        }
                    }
                }
            }

            dev.Close();
        }
Пример #5
0
        /// <summary>Starts dumping with the stablished fields and autodetecting the device type</summary>
        public void Start()
        {
            // Open master database
            _ctx = DicContext.Create(Settings.Settings.MasterDbPath);

            // Search for device in master database
            _dbDev = _ctx.Devices.FirstOrDefault(d => d.Manufacturer == _dev.Manufacturer && d.Model == _dev.Model &&
                                                 d.Revision == _dev.Revision);

            if (_dbDev is null)
            {
                _dumpLog.WriteLine("Device not in database, please create a device report and attach it to a Github issue.");

                UpdateStatus?.
                Invoke("Device not in database, please create a device report and attach it to a Github issue.");
            }
            else
            {
                _dumpLog.WriteLine($"Device in database since {_dbDev.LastSynchronized}.");
                UpdateStatus?.Invoke($"Device in database since {_dbDev.LastSynchronized}.");

                if (_dbDev.OptimalMultipleSectorsRead > 0)
                {
                    _maximumReadable = (uint)_dbDev.OptimalMultipleSectorsRead;
                }
            }

            if (_dev.IsUsb &&
                _dev.UsbVendorId == 0x054C &&
                (_dev.UsbProductId == 0x01C8 || _dev.UsbProductId == 0x01C9 || _dev.UsbProductId == 0x02D2))
            {
                PlayStationPortable();
            }
            else
            {
                switch (_dev.Type)
                {
                case DeviceType.ATA:
                    Ata();

                    break;

                case DeviceType.MMC:
                case DeviceType.SecureDigital:
                    SecureDigital();

                    break;

                case DeviceType.NVMe:
                    NVMe();

                    break;

                case DeviceType.ATAPI:
                case DeviceType.SCSI:
                    Scsi();

                    break;

                default:
                    _dumpLog.WriteLine("Unknown device type.");
                    _dumpLog.Close();
                    StoppingErrorMessage?.Invoke("Unknown device type.");

                    return;
                }
            }

            _dumpLog.Close();

            if (_resume == null ||
                !_doResume)
            {
                return;
            }

            _resume.LastWriteDate = DateTime.UtcNow;
            _resume.BadBlocks.Sort();

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

            var fs = new FileStream(_outputPrefix + ".resume.xml", FileMode.Create, FileAccess.ReadWrite);
            var xs = new XmlSerializer(_resume.GetType());

            xs.Serialize(fs, _resume);
            fs.Close();
        }
Пример #6
0
        public static void SolveTrackPregaps(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus,
                                             Track[] tracks, bool supportsPqSubchannel, bool supportsRwSubchannel,
                                             Database.Models.Device dbDev, out bool inexactPositioning)
        {
            bool sense;                  // Sense indicator

            byte[] subBuf;
            int    posQ;
            uint   retries;
            bool?  bcd = null;

            byte[] crc;
            Dictionary <uint, int> pregaps = new Dictionary <uint, int>();

            inexactPositioning = false;

            if (!supportsPqSubchannel &&
                !supportsRwSubchannel)
            {
                return;
            }

            // Check if subchannel is BCD
            for (retries = 0; retries < 10; retries++)
            {
                sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, 11, dbDev, out subBuf)
                            : GetSectorForPregapQ16(dev, 11, dbDev, out subBuf);

                if (sense)
                {
                    continue;
                }

                bcd = (subBuf[9] & 0x10) > 0;

                break;
            }

            if (bcd is null)
            {
                dumpLog?.WriteLine("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect...");

                updateStatus?.
                Invoke("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect...");

                return;
            }

            // Initialize the dictionary
            for (int i = 0; i < tracks.Length; i++)
            {
                pregaps[tracks[i].TrackSequence] = 0;
            }

            foreach (Track track in tracks)
            {
                if (track.TrackSequence <= 1)
                {
                    continue;
                }

                int   lba           = (int)track.TrackStartSector - 1;
                bool  pregapFound   = false;
                Track previousTrack = tracks.FirstOrDefault(t => t.TrackSequence == track.TrackSequence - 1);

                bool goneBack = false;
                bool goFront  = false;

                // Check if pregap is 0
                for (retries = 0; retries < 10; retries++)
                {
                    sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf)
                                : GetSectorForPregapQ16(dev, (uint)lba, dbDev, out subBuf);

                    if (sense)
                    {
                        continue;
                    }

                    if (bcd == false)
                    {
                        BinaryToBcdQ(subBuf);
                    }

                    CRC16CCITTContext.Data(subBuf, 10, out crc);

                    if (crc[0] != subBuf[10] ||
                        crc[1] != subBuf[11])
                    {
                        continue;
                    }

                    BcdToBinaryQ(subBuf);

                    // Q position
                    if ((subBuf[0] & 0xF) != 1)
                    {
                        continue;
                    }

                    posQ = ((subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9]) - 150;

                    if (subBuf[1] != track.TrackSequence - 1 ||
                        subBuf[2] == 0 ||
                        posQ != lba)
                    {
                        break;
                    }

                    pregaps[track.TrackSequence] = 0;

                    pregapFound = true;
                }

                if (pregapFound)
                {
                    continue;
                }

                // Calculate pregap
                lba = (int)track.TrackStartSector - 150;

                while (lba > (int)previousTrack.TrackStartSector)
                {
                    // Some drives crash if you try to read just before the previous read, so seek away first
                    sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba - 10, dbDev, out subBuf)
                                : GetSectorForPregapQ16(dev, (uint)lba - 10, dbDev, out subBuf);

                    for (retries = 0; retries < 10; retries++)
                    {
                        sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf)
                                    : GetSectorForPregapQ16(dev, (uint)lba, dbDev, out subBuf);

                        if (sense)
                        {
                            continue;
                        }

                        if (bcd == false)
                        {
                            BinaryToBcdQ(subBuf);
                        }

                        CRC16CCITTContext.Data(subBuf, 10, out crc);

                        if (crc[0] == subBuf[10] &&
                            crc[1] == subBuf[11])
                        {
                            break;
                        }
                    }

                    if (retries == 10)
                    {
                        dumpLog?.WriteLine($"Could not get correct subchannel for sector {lba}");
                        updateStatus?.Invoke($"Could not get correct subchannel for sector {lba}");
                    }

                    BcdToBinaryQ(subBuf);

                    // If it's not Q position
                    if ((subBuf[0] & 0xF) != 1)
                    {
                        // This means we already searched back, so search forward
                        if (goFront)
                        {
                            lba++;

                            if (lba == (int)previousTrack.TrackStartSector)
                            {
                                break;
                            }

                            continue;
                        }

                        // Search back
                        goneBack = true;
                        lba--;

                        continue;
                    }

                    // Previous track
                    if (subBuf[1] < track.TrackSequence)
                    {
                        lba++;

                        // Already gone back, so go forward
                        if (goneBack)
                        {
                            goFront = true;
                        }

                        continue;
                    }

                    // Same track, but not pregap
                    if (subBuf[1] == track.TrackSequence &&
                        subBuf[2] > 0)
                    {
                        lba--;

                        continue;
                    }

                    // Pregap according to Q position
                    int pregapQ = (subBuf[3] * 60 * 75) + (subBuf[4] * 75) + subBuf[5] + 1;
                    posQ = ((subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9]) - 150;
                    int diff = posQ - lba;

                    if (diff != 0)
                    {
                        inexactPositioning = true;
                    }

                    // Bigger than known change, otherwise we found it
                    if (pregapQ > pregaps[track.TrackSequence])
                    {
                        pregaps[track.TrackSequence] = pregapQ;
                    }
                    else if (pregapQ == pregaps[track.TrackSequence])
                    {
                        break;
                    }

                    lba--;
                }
            }

            for (int i = 0; i < tracks.Length; i++)
            {
                tracks[i].TrackPregap       = (ulong)pregaps[tracks[i].TrackSequence];
                tracks[i].TrackStartSector -= tracks[i].TrackPregap;

            #if DEBUG
                dumpLog?.WriteLine($"Track {tracks[i].TrackSequence} pregap is {tracks[i].TrackPregap} sectors");
                updateStatus?.Invoke($"Track {tracks[i].TrackSequence} pregap is {tracks[i].TrackPregap} sectors");
            #endif
            }
        }