Example #1
0
        public static bool SupportsPqSubchannel(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus)
        {
            dumpLog?.WriteLine("Checking if drive supports PQ subchannel reading...");
            updateStatus?.Invoke("Checking if drive supports PQ subchannel reading...");

            return(!dev.ReadCd(out _, out _, 0, 2352 + 16, 1, MmcSectorTypes.AllTypes, false, false, true,
                               MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout,
                               out _));
        }
Example #2
0
        public async void Handle_UpdateStatus_Stop()
        {
            var updateStatusHandler = new UpdateStatusHandler(StateManager.Object);

            await updateStatusHandler.Handle(
                new FORFarm.Application.Status.UpdateStatus.UpdateStatus()
            {
                Running = false
            }, CancellationToken.None);

            StateManager.Verify(s => s.Stop());
        }
Example #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
            }
        }
Example #4
0
        public static void GetOffset(CdOffset cdOffset, Device dbDev, bool debug, Aaru.Devices.Device dev,
                                     MediaType dskType, DumpLog dumpLog, Track[] tracks,
                                     UpdateStatusHandler updateStatus, out int?driveOffset, out int?combinedOffset,
                                     out bool supportsPlextorReadCdDa)
        {
            byte[] cmdBuf;
            bool   sense;
            int    minute;
            int    second;
            int    frame;

            byte[]     sectorSync;
            byte[]     tmpBuf;
            int        lba;
            int        diff;
            Track      dataTrack   = default;
            Track      audioTrack  = default;
            bool       offsetFound = false;
            const uint sectorSize  = 2352;

            driveOffset             = cdOffset?.Offset * 4;
            combinedOffset          = null;
            supportsPlextorReadCdDa = false;

            if (dskType != MediaType.VideoNowColor)
            {
                if (tracks.Any(t => t.TrackType != TrackType.Audio))
                {
                    dataTrack = tracks.FirstOrDefault(t => t.TrackType != TrackType.Audio);

                    if (dataTrack != null)
                    {
                        // Build sync
                        sectorSync = new byte[]
                        {
                            0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
                        };

                        tmpBuf = new byte[sectorSync.Length];

                        // Ensure to be out of the pregap, or multi-session discs give funny values
                        uint wantedLba = (uint)(dataTrack.TrackStartSector + 151);

                        // Plextor READ CDDA
                        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 _, wantedLba, sectorSize, 3,
                                                        PlextorSubchannel.None, dev.Timeout, out _);

                            if (!sense &&
                                !dev.Error)
                            {
                                supportsPlextorReadCdDa = true;

                                for (int i = 0; i < cmdBuf.Length - sectorSync.Length; i++)
                                {
                                    Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length);

                                    if (!tmpBuf.SequenceEqual(sectorSync))
                                    {
                                        continue;
                                    }

                                    // De-scramble M and S
                                    minute = cmdBuf[i + 12] ^ 0x01;
                                    second = cmdBuf[i + 13] ^ 0x80;
                                    frame  = cmdBuf[i + 14];

                                    // Convert to binary
                                    minute = ((minute / 16) * 10) + (minute & 0x0F);
                                    second = ((second / 16) * 10) + (second & 0x0F);
                                    frame  = ((frame / 16) * 10) + (frame & 0x0F);

                                    // Calculate the first found LBA
                                    lba = ((minute * 60 * 75) + (second * 75) + frame) - 150;

                                    // Calculate the difference between the found LBA and the requested one
                                    diff = (int)wantedLba - lba;

                                    combinedOffset = i + (2352 * diff);
                                    offsetFound    = true;

                                    break;
                                }
                            }
                        }

                        if (!offsetFound &&
                            (debug || dbDev?.ATAPI?.RemovableMedias?.Any(d => d.CanReadCdScrambled == true) == true ||
                             dbDev?.SCSI?.RemovableMedias?.Any(d => d.CanReadCdScrambled == true) == true ||
                             dbDev?.SCSI?.MultiMediaDevice?.TestedMedia?.Any(d => d.CanReadCdScrambled == true) ==
                             true || dev.Manufacturer.ToLowerInvariant() == "hl-dt-st"))
                        {
                            sense = dev.ReadCd(out cmdBuf, out _, wantedLba, sectorSize, 3, MmcSectorTypes.Cdda, false,
                                               false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None,
                                               MmcSubchannel.None, dev.Timeout, out _);

                            if (!sense &&
                                !dev.Error)
                            {
                                // Clear cache
                                for (int i = 0; i < 63; i++)
                                {
                                    sense = dev.ReadCd(out _, out _, (uint)(wantedLba + 3 + (16 * i)), sectorSize, 16,
                                                       MmcSectorTypes.AllTypes, false, false, false,
                                                       MmcHeaderCodes.None, true, false, MmcErrorField.None,
                                                       MmcSubchannel.None, dev.Timeout, out _);

                                    if (sense || dev.Error)
                                    {
                                        break;
                                    }
                                }

                                dev.ReadCd(out cmdBuf, out _, wantedLba, sectorSize, 3, MmcSectorTypes.Cdda, false,
                                           false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None,
                                           MmcSubchannel.None, dev.Timeout, out _);

                                for (int i = 0; i < cmdBuf.Length - sectorSync.Length; i++)
                                {
                                    Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length);

                                    if (!tmpBuf.SequenceEqual(sectorSync))
                                    {
                                        continue;
                                    }

                                    // De-scramble M and S
                                    minute = cmdBuf[i + 12] ^ 0x01;
                                    second = cmdBuf[i + 13] ^ 0x80;
                                    frame  = cmdBuf[i + 14];

                                    // Convert to binary
                                    minute = ((minute / 16) * 10) + (minute & 0x0F);
                                    second = ((second / 16) * 10) + (second & 0x0F);
                                    frame  = ((frame / 16) * 10) + (frame & 0x0F);

                                    // Calculate the first found LBA
                                    lba = ((minute * 60 * 75) + (second * 75) + frame) - 150;

                                    // Calculate the difference between the found LBA and the requested one
                                    diff = (int)wantedLba - lba;

                                    combinedOffset = i + (2352 * diff);
                                    offsetFound    = true;

                                    break;
                                }
                            }
                        }
                    }
                }

                if (offsetFound)
                {
                    return;
                }

                // Try to get another the offset some other way, we need an audio track just after a data track, same session

                for (int i = 1; i < tracks.Length; i++)
                {
                    if (tracks[i - 1].TrackType == TrackType.Audio ||
                        tracks[i].TrackType != TrackType.Audio)
                    {
                        continue;
                    }

                    dataTrack  = tracks[i - 1];
                    audioTrack = tracks[i];

                    break;
                }

                if (dataTrack is null ||
                    audioTrack is null)
                {
                    return;
                }

                // Found them
                sense = dev.ReadCd(out cmdBuf, out _, (uint)audioTrack.TrackStartSector, sectorSize, 3,
                                   MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false,
                                   MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);

                if (sense || dev.Error)
                {
                    return;
                }

                dataTrack.TrackEndSector += 150;

                // Calculate MSF
                minute = (int)dataTrack.TrackEndSector / 4500;
                second = ((int)dataTrack.TrackEndSector - (minute * 4500)) / 75;
                frame  = (int)dataTrack.TrackEndSector - (minute * 4500) - (second * 75);

                dataTrack.TrackEndSector -= 150;

                // Convert to BCD
                minute = ((minute / 10) << 4) + (minute % 10);
                second = ((second / 10) << 4) + (second % 10);
                frame  = ((frame / 10) << 4) + (frame % 10);

                // Scramble M and S
                minute ^= 0x01;
                second ^= 0x80;

                // Build sync
                sectorSync = new byte[]
                {
                    0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, (byte)minute, (byte)second,
                    (byte)frame
                };

                tmpBuf = new byte[sectorSync.Length];

                for (int i = 0; i < cmdBuf.Length - sectorSync.Length; i++)
                {
                    Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length);

                    if (!tmpBuf.SequenceEqual(sectorSync))
                    {
                        continue;
                    }

                    combinedOffset = i + 2352;
                    offsetFound    = true;

                    break;
                }

                if (offsetFound || audioTrack.TrackPregap <= 0)
                {
                    return;
                }

                sense = dev.ReadCd(out byte[] dataBuf, out _, (uint)dataTrack.TrackEndSector, sectorSize, 1,
                                   MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
                                   MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);

                if (sense || dev.Error)
                {
                    return;
                }

                for (int i = 0; i < dataBuf.Length; i++)
                {
                    dataBuf[i] ^= Sector.ScrambleTable[i];
                }

                for (int i = 0; i < 2352; i++)
                {
                    byte[] dataSide  = new byte[2352 - i];
                    byte[] audioSide = new byte[2352 - i];

                    Array.Copy(dataBuf, i, dataSide, 0, dataSide.Length);
                    Array.Copy(cmdBuf, 0, audioSide, 0, audioSide.Length);

                    if (!dataSide.SequenceEqual(audioSide))
                    {
                        continue;
                    }

                    combinedOffset = audioSide.Length;

                    break;
                }
            }
            else
            {
                byte[] videoNowColorFrame = new byte[9 * sectorSize];

                sense = dev.ReadCd(out cmdBuf, out _, 0, sectorSize, 9, MmcSectorTypes.AllTypes, false, false, true,
                                   MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None,
                                   dev.Timeout, out _);

                if (sense || dev.Error)
                {
                    sense = dev.ReadCd(out cmdBuf, out _, 0, sectorSize, 9, MmcSectorTypes.Cdda, false, false, true,
                                       MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None,
                                       dev.Timeout, out _);

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

                if (videoNowColorFrame is null)
                {
                    dumpLog?.WriteLine("Could not find VideoNow Color frame offset, dump may not be correct.");
                    updateStatus?.Invoke("Could not find VideoNow Color frame offset, dump may not be correct.");
                }
                else
                {
                    combinedOffset = MMC.GetVideoNowColorOffset(videoNowColorFrame);
                    dumpLog?.WriteLine($"VideoNow Color frame is offset {combinedOffset} bytes.");
                    updateStatus?.Invoke($"VideoNow Color frame is offset {combinedOffset} bytes.");
                }
            }
        }
Example #5
0
        /// <summary>Reads the TOC, processes it, returns the track list and last sector</summary>
        /// <param name="dev">Device</param>
        /// <param name="dumpLog">Dump log</param>
        /// <param name="force">Force dump enabled</param>
        /// <param name="lastSector">Last sector number</param>
        /// <param name="leadOutStarts">Lead-out starts</param>
        /// <param name="mediaTags">Media tags</param>
        /// <param name="stoppingErrorMessage">Stopping error message handler</param>
        /// <param name="toc">Full CD TOC</param>
        /// <param name="trackFlags">Track flags</param>
        /// <param name="updateStatus">Update status handler</param>
        /// <returns>List of tracks</returns>
        public static Track[] GetCdTracks(Device dev, DumpLog dumpLog, bool force, out long lastSector,
                                          Dictionary <int, long> leadOutStarts,
                                          Dictionary <MediaTagType, byte[]> mediaTags,
                                          ErrorMessageHandler stoppingErrorMessage, out FullTOC.CDFullTOC?toc,
                                          Dictionary <byte, byte> trackFlags, UpdateStatusHandler updateStatus)
        {
            byte[]       cmdBuf;                         // Data buffer
            const uint   sectorSize = 2352;              // Full sector size
            bool         sense;                          // Sense indicator
            List <Track> trackList = new List <Track>(); // Tracks in disc

            byte[] tmpBuf;                               // Temporary buffer
            toc        = null;
            lastSector = 0;
            TrackType leadoutTrackType = TrackType.Audio;

            // We discarded all discs that falsify a TOC before requesting a real TOC
            // No TOC, no CD (or an empty one)
            dumpLog?.WriteLine("Reading full TOC");
            updateStatus?.Invoke("Reading full TOC");
            sense = dev.ReadRawToc(out cmdBuf, out _, 0, dev.Timeout, out _);

            if (!sense)
            {
                toc = FullTOC.Decode(cmdBuf);

                if (toc.HasValue)
                {
                    tmpBuf = new byte[cmdBuf.Length - 2];
                    Array.Copy(cmdBuf, 2, tmpBuf, 0, cmdBuf.Length - 2);
                    mediaTags?.Add(MediaTagType.CD_FullTOC, tmpBuf);
                }
            }

            updateStatus?.Invoke("Building track map...");
            dumpLog?.WriteLine("Building track map...");

            if (toc.HasValue)
            {
                FullTOC.TrackDataDescriptor[] sortedTracks =
                    toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray();

                foreach (FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4))
                {
                    if (trk.POINT >= 0x01 &&
                        trk.POINT <= 0x63)
                    {
                        trackList.Add(new Track
                        {
                            TrackSequence = trk.POINT,
                            TrackSession  = trk.SessionNumber,
                            TrackType     = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
                                            (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental
                                            ? TrackType.Data : TrackType.Audio,
                            TrackStartSector =
                                (ulong)((trk.PHOUR * 3600 * 75) + (trk.PMIN * 60 * 75) + (trk.PSEC * 75) + trk.PFRAME -
                                        150),
                            TrackBytesPerSector    = (int)sectorSize,
                            TrackRawBytesPerSector = (int)sectorSize
                        });

                        trackFlags?.Add(trk.POINT, trk.CONTROL);
                    }
                    else if (trk.POINT == 0xA2)
                    {
                        int phour, pmin, psec, pframe;

                        if (trk.PFRAME == 0)
                        {
                            pframe = 74;

                            if (trk.PSEC == 0)
                            {
                                psec = 59;

                                if (trk.PMIN == 0)
                                {
                                    pmin  = 59;
                                    phour = trk.PHOUR - 1;
                                }
                                else
                                {
                                    pmin  = trk.PMIN - 1;
                                    phour = trk.PHOUR;
                                }
                            }
                            else
                            {
                                psec  = trk.PSEC - 1;
                                pmin  = trk.PMIN;
                                phour = trk.PHOUR;
                            }
                        }
                        else
                        {
                            pframe = trk.PFRAME - 1;
                            psec   = trk.PSEC;
                            pmin   = trk.PMIN;
                            phour  = trk.PHOUR;
                        }

                        lastSector = (phour * 3600 * 75) + (pmin * 60 * 75) + (psec * 75) + pframe - 150;
                        leadOutStarts?.Add(trk.SessionNumber, lastSector + 1);
                    }
                    else if (trk.POINT == 0xA0 &&
                             trk.ADR == 1)
                    {
                        leadoutTrackType =
                            (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
                            (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data
                                : TrackType.Audio;
                    }
                }
            }
            else
            {
                updateStatus?.Invoke("Cannot read RAW TOC, requesting processed one...");
                dumpLog?.WriteLine("Cannot read RAW TOC, requesting processed one...");
                sense = dev.ReadToc(out cmdBuf, out _, false, 0, dev.Timeout, out _);

                TOC.CDTOC?oldToc = TOC.Decode(cmdBuf);

                if ((sense || !oldToc.HasValue) &&
                    !force)
                {
                    dumpLog?.WriteLine("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000...");

                    stoppingErrorMessage?.
                    Invoke("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000...");

                    return(null);
                }

                if (oldToc.HasValue)
                {
                    foreach (TOC.CDTOCTrackDataDescriptor trk in oldToc.Value.TrackDescriptors.
                             OrderBy(t => t.TrackNumber).
                             Where(trk => trk.ADR == 1 || trk.ADR == 4))
                    {
                        if (trk.TrackNumber >= 0x01 &&
                            trk.TrackNumber <= 0x63)
                        {
                            trackList.Add(new Track
                            {
                                TrackSequence = trk.TrackNumber,
                                TrackSession  = 1,
                                TrackType     = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
                                                (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental
                                                ? TrackType.Data : TrackType.Audio,
                                TrackStartSector       = trk.TrackStartAddress,
                                TrackBytesPerSector    = (int)sectorSize,
                                TrackRawBytesPerSector = (int)sectorSize
                            });

                            trackFlags?.Add(trk.TrackNumber, trk.CONTROL);
                        }
                        else if (trk.TrackNumber == 0xAA)
                        {
                            leadoutTrackType =
                                (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
                                (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data
                                    : TrackType.Audio;

                            lastSector = trk.TrackStartAddress - 1;
                        }
                    }
                }
            }

            if (trackList.Count == 0)
            {
                updateStatus?.Invoke("No tracks found, adding a single track from 0 to Lead-Out");
                dumpLog?.WriteLine("No tracks found, adding a single track from 0 to Lead-Out");

                trackList.Add(new Track
                {
                    TrackSequence          = 1,
                    TrackSession           = 1,
                    TrackType              = leadoutTrackType,
                    TrackStartSector       = 0,
                    TrackBytesPerSector    = (int)sectorSize,
                    TrackRawBytesPerSector = (int)sectorSize
                });

                trackFlags?.Add(1, (byte)(leadoutTrackType == TrackType.Audio ? 0 : 4));
            }

            if (lastSector != 0)
            {
                return(trackList.ToArray());
            }

            sense = dev.ReadCapacity16(out cmdBuf, out _, dev.Timeout, out _);

            if (!sense)
            {
                byte[] temp = new byte[8];

                Array.Copy(cmdBuf, 0, temp, 0, 8);
                Array.Reverse(temp);
                lastSector = (long)BitConverter.ToUInt64(temp, 0);
            }
            else
            {
                sense = dev.ReadCapacity(out cmdBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    lastSector = (cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3];
                }
            }

            if (lastSector > 0)
            {
                return(trackList.ToArray());
            }

            if (!force)
            {
                stoppingErrorMessage?.
                Invoke("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors...");

                dumpLog?.WriteLine("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors...");

                return(null);
            }

            updateStatus?.
            Invoke("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before...");

            dumpLog?.WriteLine("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before...");
            lastSector = 360000;

            return(trackList.ToArray());
        }
Example #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
            }
        }