/// <summary>Register an SCSI error after trying to read</summary> /// <param name="block">Starting block</param> /// <param name="osError"><c>true</c> if operating system returned an error status instead of the device</param> /// <param name="errno">Operating system error number</param> /// <param name="senseBuffer">REQUEST SENSE response buffer</param> public void WriteLine(ulong block, bool osError, int errno, byte[] senseBuffer) { if (osError) { _logSw.WriteLine("SCSI reading LBA {0} operating system error: {1}.", block, errno); _logSw.Flush(); if (senseBuffer is null || senseBuffer.Length == 0 || senseBuffer.All(s => s == 0)) { return; } } DecodedSense?decodedSense = Sense.Decode(senseBuffer); string prettySense = Sense.PrettifySense(senseBuffer); string hexSense = string.Join(' ', senseBuffer.Select(b => $"{b:X2}")); if (decodedSense.HasValue) { if (prettySense != null) { if (prettySense.StartsWith("SCSI SENSE: ", StringComparison.Ordinal)) { prettySense = prettySense.Substring(12); } if (prettySense.EndsWith('\n')) { prettySense = prettySense.Substring(0, prettySense.Length - 1); } prettySense = prettySense.Replace("\n", " - "); _logSw.WriteLine("SCSI reading LBA {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}, {5}.", block, decodedSense?.SenseKey, decodedSense?.ASC, decodedSense?.ASCQ, hexSense, prettySense); } else { _logSw.WriteLine("SCSI reading LBA {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}.", block, decodedSense?.SenseKey, decodedSense?.ASC, decodedSense?.ASCQ, hexSense); } } else { if (prettySense != null) { if (prettySense.StartsWith("SCSI SENSE: ", StringComparison.Ordinal)) { prettySense = prettySense.Substring(12); } if (prettySense.EndsWith('\n')) { prettySense = prettySense.Substring(0, prettySense.Length - 1); } prettySense = prettySense.Replace("\n", " - "); _logSw.WriteLine("SCSI reading LBA {0} error: {1}, {2}.", block, hexSense, prettySense); } else { _logSw.WriteLine("SCSI reading LBA {0} error: {1}", block, hexSense); } } _logSw.Flush(); }
static void CheckGdromReadability(string devPath, Device dev) { string strDev; int item; bool tocIsNotBcd = false; bool sense; byte[] buffer; byte[] senseBuffer; int retries; start: System.Console.Clear(); AaruConsole.WriteLine("Ejecting disc..."); dev.AllowMediumRemoval(out _, dev.Timeout, out _); dev.EjectTray(out _, dev.Timeout, out _); AaruConsole.WriteLine("Please insert trap disc inside..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); AaruConsole.WriteLine("Sending READ FULL TOC to the device..."); retries = 0; do { retries++; sense = dev.ScsiTestUnitReady(out senseBuffer, dev.Timeout, out _); if (!sense) { break; } var decodedSense = Sense.Decode(senseBuffer); if (decodedSense.Value.ASC != 0x04) { break; } if (decodedSense.Value.ASCQ != 0x01) { break; } Thread.Sleep(2000); } while(retries < 25); sense = dev.ReadRawToc(out buffer, out senseBuffer, 1, dev.Timeout, out _); if (sense) { AaruConsole.WriteLine("READ FULL TOC failed..."); AaruConsole.WriteLine("{0}", Sense.PrettifySense(senseBuffer)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } FullTOC.CDFullTOC?decodedToc = FullTOC.Decode(buffer); if (decodedToc is null) { AaruConsole.WriteLine("Could not decode TOC..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } FullTOC.CDFullTOC toc = decodedToc.Value; FullTOC.TrackDataDescriptor leadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2); if (leadOutTrack.POINT != 0xA2) { AaruConsole.WriteLine("Cannot find lead-out..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } int min = 0, sec, frame; if (leadOutTrack.PMIN == 122) { tocIsNotBcd = true; } if (leadOutTrack.PMIN >= 0xA0 && !tocIsNotBcd) { min += 90; leadOutTrack.PMIN -= 0x90; } if (tocIsNotBcd) { min = leadOutTrack.PMIN; sec = leadOutTrack.PSEC; frame = leadOutTrack.PFRAME; } else { min += ((leadOutTrack.PMIN >> 4) * 10) + (leadOutTrack.PMIN & 0x0F); sec = ((leadOutTrack.PSEC >> 4) * 10) + (leadOutTrack.PSEC & 0x0F); frame = ((leadOutTrack.PFRAME >> 4) * 10) + (leadOutTrack.PFRAME & 0x0F); } int sectors = ((min * 60 * 75) + (sec * 75) + frame) - 150; AaruConsole.WriteLine("Trap disc shows {0} sectors...", sectors); if (sectors < 450000) { AaruConsole.WriteLine("Trap disc doesn't have enough sectors..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } AaruConsole.WriteLine("Stopping motor..."); dev.StopUnit(out _, dev.Timeout, out _); AaruConsole.WriteLine("Please MANUALLY get the trap disc out and put the GD-ROM disc inside..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); AaruConsole.WriteLine("Waiting 5 seconds..."); Thread.Sleep(5000); AaruConsole.WriteLine("Sending READ FULL TOC to the device..."); retries = 0; do { retries++; sense = dev.ReadRawToc(out buffer, out senseBuffer, 1, dev.Timeout, out _); if (!sense) { break; } var decodedSense = Sense.Decode(senseBuffer); if (decodedSense.Value.ASC != 0x04) { break; } if (decodedSense.Value.ASCQ != 0x01) { break; } } while(retries < 25); if (sense) { AaruConsole.WriteLine("READ FULL TOC failed..."); AaruConsole.WriteLine("{0}", Sense.PrettifySense(senseBuffer)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } decodedToc = FullTOC.Decode(buffer); if (decodedToc is null) { AaruConsole.WriteLine("Could not decode TOC..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } toc = decodedToc.Value; FullTOC.TrackDataDescriptor newLeadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2); if (newLeadOutTrack.POINT != 0xA2) { AaruConsole.WriteLine("Cannot find lead-out..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } if (newLeadOutTrack.PMIN >= 0xA0 && !tocIsNotBcd) { newLeadOutTrack.PMIN -= 0x90; } if (newLeadOutTrack.PMIN != leadOutTrack.PMIN || newLeadOutTrack.PSEC != leadOutTrack.PSEC || newLeadOutTrack.PFRAME != leadOutTrack.PFRAME) { AaruConsole.WriteLine("Lead-out has changed, this drive does not support hot swapping discs..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } dev.SetCdSpeed(out _, RotationalControl.PureCav, 170, 0, dev.Timeout, out _); AaruConsole.Write("Reading LBA 0... "); bool lba0Result = dev.ReadCd(out byte[] lba0Buffer, out byte[] lba0Sense, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); AaruConsole.WriteLine(lba0Result ? "FAIL!" : "Success!"); AaruConsole.Write("Reading LBA 0 as audio (scrambled)... "); bool lba0ScrambledResult = dev.ReadCd(out byte[] lba0ScrambledBuffer, out byte[] lba0ScrambledSense, 0, 2352, 1, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); AaruConsole.WriteLine(lba0ScrambledResult ? "FAIL!" : "Success!"); AaruConsole.Write("Reading LBA 100000... "); bool lba100000Result = dev.ReadCd(out byte[] lba100000Buffer, out byte[] lba100000Sense, 100000, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); AaruConsole.WriteLine(lba100000Result ? "FAIL!" : "Success!"); AaruConsole.Write("Reading LBA 50000... "); bool lba50000Result = dev.ReadCd(out byte[] lba50000Buffer, out byte[] lba50000Sense, 50000, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); AaruConsole.WriteLine(lba50000Result ? "FAIL!" : "Success!"); AaruConsole.Write("Reading LBA 450000... "); bool lba450000Result = dev.ReadCd(out byte[] lba450000Buffer, out byte[] lba450000Sense, 450000, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); AaruConsole.WriteLine(lba450000Result ? "FAIL!" : "Success!"); AaruConsole.Write("Reading LBA 400000... "); bool lba400000Result = dev.ReadCd(out byte[] lba400000Buffer, out byte[] lba400000Sense, 400000, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); AaruConsole.WriteLine(lba400000Result ? "FAIL!" : "Success!"); AaruConsole.Write("Reading LBA 45000... "); bool lba45000Result = dev.ReadCd(out byte[] lba45000Buffer, out byte[] lba45000Sense, 45000, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); AaruConsole.WriteLine(lba45000Result ? "FAIL!" : "Success!"); AaruConsole.Write("Reading LBA 44990... "); bool lba44990Result = dev.ReadCd(out byte[] lba44990Buffer, out byte[] lba44990Sense, 44990, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); AaruConsole.WriteLine(lba44990Result ? "FAIL!" : "Success!"); menu: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("Device {0} read HD area.", lba450000Result ? "cannot" : "can"); AaruConsole.WriteLine("LBA 0 sense is {0}, buffer is {1}, sense buffer is {2}.", lba0Result, lba0Buffer is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(lba0Buffer) ? "empty" : $"{lba0Buffer.Length} bytes", lba0Sense is null ? "null" : ArrayHelpers. ArrayIsNullOrEmpty(lba0Sense) ? "empty" : $"{lba0Sense.Length}"); AaruConsole.WriteLine("LBA 0 (scrambled) sense is {0}, buffer is {1}, sense buffer is {2}.", lba0ScrambledResult, lba0ScrambledBuffer is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(lba0ScrambledBuffer) ? "empty" : $"{lba0ScrambledBuffer.Length} bytes", lba0ScrambledSense is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(lba0ScrambledSense) ? "empty" : $"{lba0ScrambledSense.Length}"); AaruConsole.WriteLine("LBA 44990 sense is {0}, buffer is {1}, sense buffer is {2}.", lba44990Result, lba44990Buffer is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(lba44990Buffer) ? "empty" : $"{lba44990Buffer.Length} bytes", lba44990Sense is null ? "null" : ArrayHelpers. ArrayIsNullOrEmpty(lba44990Sense) ? "empty" : $"{lba44990Sense.Length}"); AaruConsole.WriteLine("LBA 45000 sense is {0}, buffer is {1}, sense buffer is {2}.", lba45000Result, lba45000Buffer is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(lba45000Buffer) ? "empty" : $"{lba45000Buffer.Length} bytes", lba45000Sense is null ? "null" : ArrayHelpers. ArrayIsNullOrEmpty(lba45000Sense) ? "empty" : $"{lba45000Sense.Length}"); AaruConsole.WriteLine("LBA 50000 sense is {0}, buffer is {1}, sense buffer is {2}.", lba50000Result, lba50000Buffer is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(lba50000Buffer) ? "empty" : $"{lba50000Buffer.Length} bytes", lba50000Sense is null ? "null" : ArrayHelpers. ArrayIsNullOrEmpty(lba50000Sense) ? "empty" : $"{lba50000Sense.Length}"); AaruConsole.WriteLine("LBA 100000 sense is {0}, buffer is {1}, sense buffer is {2}.", lba100000Result, lba100000Buffer is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(lba100000Buffer) ? "empty" : $"{lba100000Buffer.Length} bytes", lba100000Sense is null ? "null" : ArrayHelpers. ArrayIsNullOrEmpty(lba100000Sense) ? "empty" : $"{lba100000Sense.Length}"); AaruConsole.WriteLine("LBA 400000 sense is {0}, buffer is {1}, sense buffer is {2}.", lba400000Result, lba400000Buffer is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(lba400000Buffer) ? "empty" : $"{lba400000Buffer.Length} bytes", lba400000Sense is null ? "null" : ArrayHelpers. ArrayIsNullOrEmpty(lba400000Sense) ? "empty" : $"{lba400000Sense.Length}"); AaruConsole.WriteLine("LBA 450000 sense is {0}, buffer is {1}, sense buffer is {2}.", lba450000Result, lba450000Buffer is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(lba450000Buffer) ? "empty" : $"{lba450000Buffer.Length} bytes", lba450000Sense is null ? "null" : ArrayHelpers. ArrayIsNullOrEmpty(lba450000Sense) ? "empty" : $"{lba450000Sense.Length}"); AaruConsole.WriteLine(); AaruConsole.WriteLine("Choose what to do:"); AaruConsole.WriteLine("1.- Print LBA 0 buffer."); AaruConsole.WriteLine("2.- Print LBA 0 sense buffer."); AaruConsole.WriteLine("3.- Decode LBA 0 sense buffer."); AaruConsole.WriteLine("4.- Print LBA 0 (scrambled) buffer."); AaruConsole.WriteLine("5.- Print LBA 0 (scrambled) sense buffer."); AaruConsole.WriteLine("6.- Decode LBA 0 (scrambled) sense buffer."); AaruConsole.WriteLine("7.- Print LBA 44990 buffer."); AaruConsole.WriteLine("8.- Print LBA 44990 sense buffer."); AaruConsole.WriteLine("9.- Decode LBA 44990 sense buffer."); AaruConsole.WriteLine("10.- Print LBA 45000 buffer."); AaruConsole.WriteLine("11.- Print LBA 45000 sense buffer."); AaruConsole.WriteLine("12.- Decode LBA 45000 sense buffer."); AaruConsole.WriteLine("13.- Print LBA 50000 buffer."); AaruConsole.WriteLine("14.- Print LBA 50000 sense buffer."); AaruConsole.WriteLine("15.- Decode LBA 50000 sense buffer."); AaruConsole.WriteLine("16.- Print LBA 100000 buffer."); AaruConsole.WriteLine("17.- Print LBA 100000 sense buffer."); AaruConsole.WriteLine("18.- Decode LBA 100000 sense buffer."); AaruConsole.WriteLine("19.- Print LBA 400000 buffer."); AaruConsole.WriteLine("20.- Print LBA 400000 sense buffer."); AaruConsole.WriteLine("21.- Decode LBA 400000 sense buffer."); AaruConsole.WriteLine("22.- Print LBA 450000 buffer."); AaruConsole.WriteLine("23.- Print LBA 450000 sense buffer."); AaruConsole.WriteLine("24.- Decode LBA 450000 sense buffer."); AaruConsole.WriteLine("25.- Send command again."); AaruConsole.WriteLine("0.- Return to special SCSI MultiMedia Commands menu."); AaruConsole.Write("Choose: "); strDev = System.Console.ReadLine(); if (!int.TryParse(strDev, out item)) { AaruConsole.WriteLine("Not a number. Press any key to continue..."); System.Console.ReadKey(); System.Console.Clear(); goto menu; } switch (item) { case 0: AaruConsole.WriteLine("Returning to special SCSI MultiMedia Commands menu..."); return; case 1: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 0 response:"); if (buffer != null) { PrintHex.PrintHexArray(lba0Buffer, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 2: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 0 sense:"); if (senseBuffer != null) { PrintHex.PrintHexArray(lba0Sense, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 3: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 0 decoded sense:"); AaruConsole.Write("{0}", Sense.PrettifySense(lba0Sense)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 4: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 0 (scrambled) response:"); if (buffer != null) { PrintHex.PrintHexArray(lba0ScrambledBuffer, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 5: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 0 (scrambled) sense:"); if (senseBuffer != null) { PrintHex.PrintHexArray(lba0ScrambledSense, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 6: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 0 (scrambled) decoded sense:"); AaruConsole.Write("{0}", Sense.PrettifySense(lba0ScrambledSense)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 7: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 44990 response:"); if (buffer != null) { PrintHex.PrintHexArray(lba44990Buffer, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 8: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 44990 sense:"); if (senseBuffer != null) { PrintHex.PrintHexArray(lba44990Sense, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 9: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 44990 decoded sense:"); AaruConsole.Write("{0}", Sense.PrettifySense(lba44990Sense)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 10: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 45000 response:"); if (buffer != null) { PrintHex.PrintHexArray(lba45000Buffer, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 11: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 45000 sense:"); if (senseBuffer != null) { PrintHex.PrintHexArray(lba45000Sense, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 12: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 45000 decoded sense:"); AaruConsole.Write("{0}", Sense.PrettifySense(lba45000Sense)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 13: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 50000 response:"); if (buffer != null) { PrintHex.PrintHexArray(lba50000Buffer, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 14: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 50000 sense:"); if (senseBuffer != null) { PrintHex.PrintHexArray(lba50000Sense, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 15: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 50000 decoded sense:"); AaruConsole.Write("{0}", Sense.PrettifySense(lba50000Sense)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 16: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 100000 response:"); if (buffer != null) { PrintHex.PrintHexArray(lba100000Buffer, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 17: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 100000 sense:"); if (senseBuffer != null) { PrintHex.PrintHexArray(lba100000Sense, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 18: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 100000 decoded sense:"); AaruConsole.Write("{0}", Sense.PrettifySense(lba100000Sense)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 19: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 400000 response:"); if (buffer != null) { PrintHex.PrintHexArray(lba400000Buffer, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 20: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 400000 sense:"); if (senseBuffer != null) { PrintHex.PrintHexArray(lba400000Sense, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 21: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 400000 decoded sense:"); AaruConsole.Write("{0}", Sense.PrettifySense(lba400000Sense)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 22: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 450000 response:"); if (buffer != null) { PrintHex.PrintHexArray(lba450000Buffer, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 23: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 450000 sense:"); if (senseBuffer != null) { PrintHex.PrintHexArray(lba450000Sense, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 24: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA 450000 decoded sense:"); AaruConsole.Write("{0}", Sense.PrettifySense(lba450000Sense)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 25: goto start; default: AaruConsole.WriteLine("Incorrect option. Press any key to continue..."); System.Console.ReadKey(); System.Console.Clear(); goto menu; } }
bool ScsiFindReadCommand() { if (Blocks == 0) { GetDeviceBlocks(); } if (Blocks == 0) { return(true); } byte[] senseBuf; int tries = 0; uint lba = 0; bool mediumScan = false; if (_dev.ScsiType == PeripheralDeviceTypes.OpticalDevice) { mediumScan = !_dev.MediumScan(out _, true, false, false, false, false, lba, 1, (uint)Blocks, out uint foundLba, out _, _timeout, out _); if (mediumScan) { lba = foundLba; } } var rnd = new Random(); while (tries < 10) { _read6 = !_dev.Read6(out _, out senseBuf, lba, LogicalBlockSize, _timeout, out _); _read10 = !_dev.Read10(out _, out senseBuf, 0, false, false, false, false, lba, LogicalBlockSize, 0, 1, _timeout, out _); _read12 = !_dev.Read12(out _, out senseBuf, 0, false, false, false, false, lba, LogicalBlockSize, 0, 1, false, _timeout, out _); _read16 = !_dev.Read16(out _, out senseBuf, 0, false, false, false, lba, LogicalBlockSize, 0, 1, false, _timeout, out _); if (_read6 || _read10 || _read12 || _read16) { break; } lba = (uint)rnd.Next(1, (int)Blocks); if (mediumScan) { mediumScan = !_dev.MediumScan(out _, true, false, false, false, false, lba, 1, (uint)Blocks, out uint foundLba, out _, _timeout, out _); if (mediumScan) { lba = foundLba; } } tries++; } if (!_read6 && !_read10 && !_read12 && !_read16) { // Magneto-opticals may have empty LBA 0 but we know they work with READ(12) if (_dev.ScsiType == PeripheralDeviceTypes.OpticalDevice) { ErrorMessage = "Cannot read medium, aborting scan..."; return(true); } _read12 = true; } if (_read6 && !_read10 && !_read12 && !_read16 && Blocks > 0x001FFFFF + 1) { ErrorMessage = $"Device only supports SCSI READ (6) but has more than {0x001FFFFF + 1} blocks ({Blocks} blocks total)"; return(true); } if (Blocks > 0x001FFFFF + 1) { _read6 = false; } if (_read10) { _read12 = false; } if (!_read16 && Blocks > 0xFFFFFFFF + (long)1) { ErrorMessage = $"Device only supports SCSI READ (10) but has more than {0xFFFFFFFF + (long)1} blocks ({Blocks} blocks total)"; return(true); } if (Blocks > 0xFFFFFFFF + (long)1) { _read10 = false; _read12 = false; } _seek6 = !_dev.Seek6(out senseBuf, lba, _timeout, out _); _seek10 = !_dev.Seek10(out senseBuf, lba, _timeout, out _); if (CanReadRaw) { bool testSense; CanReadRaw = false; if (_dev.ScsiType != PeripheralDeviceTypes.MultiMediaDevice) { /*testSense = dev.ReadLong16(out readBuffer, out senseBuf, false, 0, 0xFFFF, timeout, out duration); * if (testSense && !dev.Error) * { * decSense = Decoders.SCSI.Sense.DecodeFixed(senseBuf); * if (decSense.HasValue) * { * if (decSense.Value.SenseKey == Aaru.Decoders.SCSI.SenseKeys.IllegalRequest && * decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) * { * readRaw = true; * if (decSense.Value.InformationValid && decSense.Value.ILI) * { * longBlockSize = 0xFFFF - (decSense.Value.Information & 0xFFFF); * readLong16 = !dev.ReadLong16(out readBuffer, out senseBuf, false, 0, longBlockSize, timeout, out duration); * } * } * } * }*/ testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 0xFFFF, _timeout, out _); DecodedSense?decSense; if (testSense && !_dev.Error) { decSense = Sense.Decode(senseBuf); if (decSense?.SenseKey == SenseKeys.IllegalRequest && decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) { CanReadRaw = true; bool valid = decSense?.Fixed?.InformationValid == true; bool ili = decSense?.Fixed?.ILI == true; uint information = decSense?.Fixed?.Information ?? 0; if (decSense.Value.Descriptor.HasValue && decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00))
ScanResults Scsi() { var results = new ScanResults(); MhddLog mhddLog; IbgLog ibgLog; byte[] senseBuf; bool sense = false; uint blockSize = 0; ushort currentProfile = 0x0001; results.Blocks = 0; if (_dev.IsRemovable) { sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); if (sense) { InitProgress?.Invoke(); DecodedSense?decSense = Sense.Decode(senseBuf); if (decSense.HasValue) { if (decSense.Value.ASC == 0x3A) { int leftRetries = 5; while (leftRetries > 0) { PulseProgress?.Invoke("Waiting for drive to become ready"); Thread.Sleep(2000); sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); if (!sense) { break; } leftRetries--; } if (sense) { StoppingErrorMessage?.Invoke("Please insert media in drive"); return(results); } } else if (decSense.Value.ASC == 0x04 && decSense.Value.ASCQ == 0x01) { int leftRetries = 10; while (leftRetries > 0) { PulseProgress?.Invoke("Waiting for drive to become ready"); Thread.Sleep(2000); sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); if (!sense) { break; } leftRetries--; } if (sense) { StoppingErrorMessage?. Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); return(results); } } // These should be trapped by the OS but seems in some cases they're not else if (decSense.Value.ASC == 0x28) { int leftRetries = 10; while (leftRetries > 0) { PulseProgress?.Invoke("Waiting for drive to become ready"); Thread.Sleep(2000); sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); if (!sense) { break; } leftRetries--; } if (sense) { StoppingErrorMessage?. Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); return(results); } } else { StoppingErrorMessage?. Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); return(results); } } else { StoppingErrorMessage?.Invoke("Unknown testing unit was ready."); return(results); } EndProgress?.Invoke(); } } Reader scsiReader = null; switch (_dev.ScsiType) { case PeripheralDeviceTypes.DirectAccess: case PeripheralDeviceTypes.MultiMediaDevice: case PeripheralDeviceTypes.OCRWDevice: case PeripheralDeviceTypes.OpticalDevice: case PeripheralDeviceTypes.SimplifiedDevice: case PeripheralDeviceTypes.WriteOnceDevice: scsiReader = new Reader(_dev, _dev.Timeout, null, null); results.Blocks = scsiReader.GetDeviceBlocks(); if (scsiReader.FindReadCommand()) { StoppingErrorMessage?.Invoke("Unable to read medium."); return(results); } blockSize = scsiReader.LogicalBlockSize; if (results.Blocks != 0 && blockSize != 0) { results.Blocks++; ulong totalSize = results.Blocks * blockSize; if (totalSize > 1099511627776) { UpdateStatus?. Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"); } else if (totalSize > 1073741824) { UpdateStatus?. Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"); } else if (totalSize > 1048576) { UpdateStatus?. Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"); } else if (totalSize > 1024) { UpdateStatus?. Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"); } else { UpdateStatus?. Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)"); } } break; case PeripheralDeviceTypes.SequentialAccess: StoppingErrorMessage?.Invoke("Scanning will never be supported on SCSI Streaming Devices." + Environment.NewLine + "It has no sense to do it, and it will put too much strain on the tape."); return(results); } if (results.Blocks == 0) { StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present..."); return(results); } bool compactDisc = true; FullTOC.CDFullTOC?toc = null; if (_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) { sense = _dev.GetConfiguration(out byte[] cmdBuf, out senseBuf, 0, MmcGetConfigurationRt.Current, _dev.Timeout, out _); if (!sense) { Features.SeparatedFeatures ftr = Features.Separate(cmdBuf); currentProfile = ftr.CurrentProfile; switch (ftr.CurrentProfile) { case 0x0005: case 0x0008: case 0x0009: case 0x000A: case 0x0020: case 0x0021: case 0x0022: break; default: compactDisc = false; break; } } if (compactDisc) { currentProfile = 0x0008; // We discarded all discs that falsify a TOC before requesting a real TOC // No TOC, no CD (or an empty one) bool tocSense = _dev.ReadRawToc(out cmdBuf, out senseBuf, 1, _dev.Timeout, out _); if (!tocSense) { toc = FullTOC.Decode(cmdBuf); } } } else { compactDisc = false; } uint blocksToRead = 64; results.A = 0; // <3ms results.B = 0; // >=3ms, <10ms results.C = 0; // >=10ms, <50ms results.D = 0; // >=50ms, <150ms results.E = 0; // >=150ms, <500ms results.F = 0; // >=500ms results.Errored = 0; DateTime start; DateTime end; results.ProcessingTime = 0; results.TotalTime = 0; double currentSpeed = 0; results.MaxSpeed = double.MinValue; results.MinSpeed = double.MaxValue; results.UnreadableSectors = new List <ulong>(); if (compactDisc) { if (toc == null) { StoppingErrorMessage?.Invoke("Error trying to decode TOC..."); return(results); } bool readcd = !_dev.ReadCd(out _, out senseBuf, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, out _); if (readcd) { UpdateStatus?.Invoke("Using MMC READ CD command."); } start = DateTime.UtcNow; while (true) { if (readcd) { sense = _dev.ReadCd(out _, out senseBuf, 0, 2352, blocksToRead, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, out _); if (_dev.Error) { blocksToRead /= 2; } } if (!_dev.Error || blocksToRead == 1) { break; } } if (_dev.Error) { StoppingErrorMessage?. Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); return(results); } UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, currentProfile); mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); ibgLog = new IbgLog(_ibgLogPath, currentProfile); DateTime timeSpeedStart = DateTime.UtcNow; ulong sectorSpeedStart = 0; InitProgress?.Invoke(); for (ulong i = 0; i < results.Blocks; i += blocksToRead) { if (_aborted) { break; } double cmdDuration = 0; if (results.Blocks - i < blocksToRead) { blocksToRead = (uint)(results.Blocks - i); } if (currentSpeed > results.MaxSpeed && currentSpeed > 0) { results.MaxSpeed = currentSpeed; } if (currentSpeed < results.MinSpeed && currentSpeed > 0) { results.MinSpeed = currentSpeed; } UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, (long)results.Blocks); if (readcd) { sense = _dev.ReadCd(out _, out senseBuf, (uint)i, 2352, blocksToRead, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, out cmdDuration); results.ProcessingTime += cmdDuration; } if (!sense) { if (cmdDuration >= 500) { results.F += blocksToRead; } else if (cmdDuration >= 150) { results.E += blocksToRead; } else if (cmdDuration >= 50) { results.D += blocksToRead; } else if (cmdDuration >= 10) { results.C += blocksToRead; } else if (cmdDuration >= 3) { results.B += blocksToRead; } else { results.A += blocksToRead; } ScanTime?.Invoke(i, cmdDuration); mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); } else { AaruConsole.DebugWriteLine("Media-Scan", "READ CD error:\n{0}", Sense.PrettifySense(senseBuf)); DecodedSense?senseDecoded = Sense.Decode(senseBuf); if (senseDecoded.HasValue) { // TODO: This error happens when changing from track type afaik. Need to solve that more cleanly // LOGICAL BLOCK ADDRESS OUT OF RANGE if ((senseDecoded.Value.ASC != 0x21 || senseDecoded.Value.ASCQ != 0x00) && // ILLEGAL MODE FOR THIS TRACK (requesting sectors as-is, this is a firmware misconception when audio sectors // are in a track where subchannel indicates data) (senseDecoded.Value.ASC != 0x64 || senseDecoded.Value.ASCQ != 0x00)) { results.Errored += blocksToRead; for (ulong b = i; b < i + blocksToRead; b++) { results.UnreadableSectors.Add(b); } ScanUnreadable?.Invoke(i); mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(i, 0); } } else { ScanUnreadable?.Invoke(i); results.Errored += blocksToRead; for (ulong b = i; b < i + blocksToRead; b++) { results.UnreadableSectors.Add(b); } mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(i, 0); } } sectorSpeedStart += blocksToRead; double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; if (elapsed < 1) { continue; } currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); ScanSpeed?.Invoke(i, currentSpeed * 1024); sectorSpeedStart = 0; timeSpeedStart = DateTime.UtcNow; } end = DateTime.UtcNow; EndProgress?.Invoke(); mhddLog.Close(); ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), _devicePath); } else { start = DateTime.UtcNow; UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, currentProfile); mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); ibgLog = new IbgLog(_ibgLogPath, currentProfile); DateTime timeSpeedStart = DateTime.UtcNow; ulong sectorSpeedStart = 0; InitProgress?.Invoke(); for (ulong i = 0; i < results.Blocks; i += blocksToRead) { if (_aborted) { break; } if (results.Blocks - i < blocksToRead) { blocksToRead = (uint)(results.Blocks - i); } if (currentSpeed > results.MaxSpeed && currentSpeed > 0) { results.MaxSpeed = currentSpeed; } if (currentSpeed < results.MinSpeed && currentSpeed > 0) { results.MinSpeed = currentSpeed; } UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, (long)results.Blocks); sense = scsiReader.ReadBlocks(out _, i, blocksToRead, out double cmdDuration, out _, out _); results.ProcessingTime += cmdDuration; if (!sense && !_dev.Error) { if (cmdDuration >= 500) { results.F += blocksToRead; } else if (cmdDuration >= 150) { results.E += blocksToRead; } else if (cmdDuration >= 50) { results.D += blocksToRead; } else if (cmdDuration >= 10) { results.C += blocksToRead; } else if (cmdDuration >= 3) { results.B += blocksToRead; } else { results.A += blocksToRead; } ScanTime?.Invoke(i, cmdDuration); mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); } // TODO: Separate errors on kind of errors. else { ScanUnreadable?.Invoke(i); results.Errored += blocksToRead; for (ulong b = i; b < i + blocksToRead; b++) { results.UnreadableSectors.Add(b); } mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(i, 0); } sectorSpeedStart += blocksToRead; double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; if (elapsed < 1) { continue; } currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); ScanSpeed?.Invoke(i, currentSpeed * 1024); sectorSpeedStart = 0; timeSpeedStart = DateTime.UtcNow; } end = DateTime.UtcNow; EndProgress?.Invoke(); mhddLog.Close(); ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), _devicePath); } results.SeekMax = double.MinValue; results.SeekMin = double.MaxValue; results.SeekTotal = 0; const int seekTimes = 1000; var rnd = new Random(); InitProgress?.Invoke(); for (int i = 0; i < seekTimes; i++) { if (_aborted || !_seekTest) { break; } uint seekPos = (uint)rnd.Next((int)results.Blocks); PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t"); double seekCur; if (scsiReader.CanSeek) { scsiReader.Seek(seekPos, out seekCur); } else { scsiReader.ReadBlock(out _, seekPos, out seekCur, out _, out _); } if (seekCur > results.SeekMax && seekCur > 0) { results.SeekMax = seekCur; } if (seekCur < results.SeekMin && seekCur > 0) { results.SeekMin = seekCur; } results.SeekTotal += seekCur; GC.Collect(); } EndProgress?.Invoke(); results.ProcessingTime /= 1000; results.TotalTime = (end - start).TotalSeconds; results.AvgSpeed = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime; results.SeekTimes = seekTimes; return(results); }
static void ReadLeadOutUsingTrapDisc(string devPath, Device dev) { string strDev; int item; bool tocIsNotBcd = false; bool sense; byte[] buffer; byte[] senseBuffer; int retries; start: System.Console.Clear(); AaruConsole.WriteLine("Ejecting disc..."); dev.AllowMediumRemoval(out _, dev.Timeout, out _); dev.EjectTray(out _, dev.Timeout, out _); AaruConsole.WriteLine("Please insert a data only disc inside..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); AaruConsole.WriteLine("Sending READ FULL TOC to the device..."); retries = 0; do { retries++; sense = dev.ScsiTestUnitReady(out senseBuffer, dev.Timeout, out _); if (!sense) { break; } DecodedSense?decodedSense = Sense.Decode(senseBuffer); if (decodedSense.Value.ASC != 0x04) { break; } if (decodedSense.Value.ASCQ != 0x01) { break; } Thread.Sleep(2000); } while(retries < 25); sense = dev.ReadRawToc(out buffer, out senseBuffer, 1, dev.Timeout, out _); if (sense) { AaruConsole.WriteLine("READ FULL TOC failed..."); AaruConsole.WriteLine("{0}", Sense.PrettifySense(senseBuffer)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } FullTOC.CDFullTOC?decodedToc = FullTOC.Decode(buffer); if (decodedToc is null) { AaruConsole.WriteLine("Could not decode TOC..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } FullTOC.CDFullTOC toc = decodedToc.Value; FullTOC.TrackDataDescriptor leadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2); if (leadOutTrack.POINT != 0xA2) { AaruConsole.WriteLine("Cannot find lead-out..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } int min = ((leadOutTrack.PMIN >> 4) * 10) + (leadOutTrack.PMIN & 0x0F); int sec = ((leadOutTrack.PSEC >> 4) * 10) + (leadOutTrack.PSEC & 0x0F); int frame = ((leadOutTrack.PFRAME >> 4) * 10) + (leadOutTrack.PFRAME & 0x0F); int sectors = (min * 60 * 75) + (sec * 75) + frame - 150; AaruConsole.WriteLine("Data disc shows {0} sectors...", sectors); AaruConsole.WriteLine("Ejecting disc..."); dev.AllowMediumRemoval(out _, dev.Timeout, out _); dev.EjectTray(out _, dev.Timeout, out _); AaruConsole.WriteLine("Please insert the trap disc inside..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); AaruConsole.WriteLine("Sending READ FULL TOC to the device..."); retries = 0; do { retries++; sense = dev.ScsiTestUnitReady(out senseBuffer, dev.Timeout, out _); if (!sense) { break; } DecodedSense?decodedSense = Sense.Decode(senseBuffer); if (decodedSense.Value.ASC != 0x04) { break; } if (decodedSense.Value.ASCQ != 0x01) { break; } Thread.Sleep(2000); } while(retries < 25); sense = dev.ReadRawToc(out buffer, out senseBuffer, 1, dev.Timeout, out _); if (sense) { AaruConsole.WriteLine("READ FULL TOC failed..."); AaruConsole.WriteLine("{0}", Sense.PrettifySense(senseBuffer)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } decodedToc = FullTOC.Decode(buffer); if (decodedToc is null) { AaruConsole.WriteLine("Could not decode TOC..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } toc = decodedToc.Value; leadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2); if (leadOutTrack.POINT != 0xA2) { AaruConsole.WriteLine("Cannot find lead-out..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } min = 0; if (leadOutTrack.PMIN == 122) { tocIsNotBcd = true; } if (leadOutTrack.PMIN >= 0xA0 && !tocIsNotBcd) { min += 90; leadOutTrack.PMIN -= 0x90; } if (tocIsNotBcd) { min = leadOutTrack.PMIN; sec = leadOutTrack.PSEC; frame = leadOutTrack.PFRAME; } else { min += ((leadOutTrack.PMIN >> 4) * 10) + (leadOutTrack.PMIN & 0x0F); sec = ((leadOutTrack.PSEC >> 4) * 10) + (leadOutTrack.PSEC & 0x0F); frame = ((leadOutTrack.PFRAME >> 4) * 10) + (leadOutTrack.PFRAME & 0x0F); } int trapSectors = (min * 60 * 75) + (sec * 75) + frame - 150; AaruConsole.WriteLine("Trap disc shows {0} sectors...", trapSectors); if (trapSectors < sectors + 100) { AaruConsole.WriteLine("Trap disc doesn't have enough sectors..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } AaruConsole.WriteLine("Stopping motor..."); dev.StopUnit(out _, dev.Timeout, out _); AaruConsole.WriteLine("Please MANUALLY get the trap disc out and put the data disc back inside..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); AaruConsole.WriteLine("Waiting 5 seconds..."); Thread.Sleep(5000); AaruConsole.WriteLine("Sending READ FULL TOC to the device..."); retries = 0; do { retries++; sense = dev.ReadRawToc(out buffer, out senseBuffer, 1, dev.Timeout, out _); if (!sense) { break; } DecodedSense?decodedSense = Sense.Decode(senseBuffer); if (decodedSense.Value.ASC != 0x04) { break; } if (decodedSense.Value.ASCQ != 0x01) { break; } } while(retries < 25); if (sense) { AaruConsole.WriteLine("READ FULL TOC failed..."); AaruConsole.WriteLine("{0}", Sense.PrettifySense(senseBuffer)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } decodedToc = FullTOC.Decode(buffer); if (decodedToc is null) { AaruConsole.WriteLine("Could not decode TOC..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } toc = decodedToc.Value; FullTOC.TrackDataDescriptor newLeadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2); if (newLeadOutTrack.POINT != 0xA2) { AaruConsole.WriteLine("Cannot find lead-out..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } if (newLeadOutTrack.PMIN >= 0xA0 && !tocIsNotBcd) { newLeadOutTrack.PMIN -= 0x90; } if (newLeadOutTrack.PMIN != leadOutTrack.PMIN || newLeadOutTrack.PSEC != leadOutTrack.PSEC || newLeadOutTrack.PFRAME != leadOutTrack.PFRAME) { AaruConsole.WriteLine("Lead-out has changed, this drive does not support hot swapping discs..."); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadLine(); return; } AaruConsole.Write("Reading LBA {0}... ", sectors + 5); bool dataResult = dev.ReadCd(out byte[] dataBuffer, out byte[] dataSense, (uint)(sectors + 5), 2352, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); AaruConsole.WriteLine(dataResult ? "FAIL!" : "Success!"); AaruConsole.Write("Reading LBA {0} as audio (scrambled)... ", sectors + 5); bool scrambledResult = dev.ReadCd(out byte[] scrambledBuffer, out byte[] scrambledSense, (uint)(sectors + 5), 2352, 1, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); AaruConsole.WriteLine(scrambledResult ? "FAIL!" : "Success!"); AaruConsole.Write("Reading LBA {0}'s PQ subchannel... ", sectors + 5); bool pqResult = dev.ReadCd(out byte[] pqBuffer, out byte[] pqSense, (uint)(sectors + 5), 16, 1, MmcSectorTypes.AllTypes, false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, out _); if (pqResult) { pqResult = dev.ReadCd(out pqBuffer, out pqSense, (uint)(sectors + 5), 16, 1, MmcSectorTypes.AllTypes, false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, out _); } AaruConsole.WriteLine(pqResult ? "FAIL!" : "Success!"); AaruConsole.Write("Reading LBA {0}'s PQ subchannel... ", sectors + 5); bool rwResult = dev.ReadCd(out byte[] rwBuffer, out byte[] rwSense, (uint)(sectors + 5), 16, 1, MmcSectorTypes.AllTypes, false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Rw, dev.Timeout, out _); if (rwResult) { rwResult = dev.ReadCd(out rwBuffer, out rwSense, (uint)(sectors + 5), 16, 1, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Rw, dev.Timeout, out _); } AaruConsole.WriteLine(pqResult ? "FAIL!" : "Success!"); menu: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("Device {0} read Lead-Out.", dataResult && scrambledResult ? "cannot" : "can"); AaruConsole.WriteLine("LBA {0} sense is {1}, buffer is {2}, sense buffer is {3}.", sectors + 5, dataResult, dataBuffer is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(dataBuffer) ? "empty" : $"{dataBuffer.Length} bytes", dataSense is null ? "null" : ArrayHelpers. ArrayIsNullOrEmpty(dataSense) ? "empty" : $"{dataSense.Length}"); AaruConsole.WriteLine("LBA {0} (scrambled) sense is {1}, buffer is {2}, sense buffer is {3}.", sectors + 5, scrambledResult, scrambledBuffer is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(scrambledBuffer) ? "empty" : $"{scrambledBuffer.Length} bytes", scrambledSense is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(scrambledSense) ? "empty" : $"{scrambledSense.Length}"); AaruConsole.WriteLine("LBA {0}'s PQ sense is {1}, buffer is {2}, sense buffer is {3}.", sectors + 5, pqResult, pqBuffer is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(pqBuffer) ? "empty" : $"{pqBuffer.Length} bytes", pqSense is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(pqSense) ? "empty" : $"{pqSense.Length}"); AaruConsole.WriteLine("LBA {0}'s RW sense is {1}, buffer is {2}, sense buffer is {3}.", sectors + 5, rwResult, rwBuffer is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(rwBuffer) ? "empty" : $"{rwBuffer.Length} bytes", rwSense is null ? "null" : ArrayHelpers.ArrayIsNullOrEmpty(rwSense) ? "empty" : $"{rwSense.Length}"); AaruConsole.WriteLine(); AaruConsole.WriteLine("Choose what to do:"); AaruConsole.WriteLine("1.- Print LBA {0} buffer.", sectors + 5); AaruConsole.WriteLine("2.- Print LBA {0} sense buffer.", sectors + 5); AaruConsole.WriteLine("3.- Decode LBA {0} sense buffer.", sectors + 5); AaruConsole.WriteLine("4.- Print LBA {0} (scrambled) buffer.", sectors + 5); AaruConsole.WriteLine("5.- Print LBA {0} (scrambled) sense buffer.", sectors + 5); AaruConsole.WriteLine("6.- Decode LBA {0} (scrambled) sense buffer.", sectors + 5); AaruConsole.WriteLine("7.- Print LBA {0}'s PQ buffer.", sectors + 5); AaruConsole.WriteLine("8.- Print LBA {0}'s PQ sense buffer.", sectors + 5); AaruConsole.WriteLine("9.- Decode LBA {0}'s PQ sense buffer.", sectors + 5); AaruConsole.WriteLine("10.- Print LBA {0}'s RW buffer.", sectors + 5); AaruConsole.WriteLine("11.- Print LBA {0}'s RW sense buffer.", sectors + 5); AaruConsole.WriteLine("12.- Decode LBA {0}'s RW sense buffer.", sectors + 5); AaruConsole.WriteLine("13.- Send command again."); AaruConsole.WriteLine("0.- Return to special SCSI MultiMedia Commands menu."); AaruConsole.Write("Choose: "); strDev = System.Console.ReadLine(); if (!int.TryParse(strDev, out item)) { AaruConsole.WriteLine("Not a number. Press any key to continue..."); System.Console.ReadKey(); System.Console.Clear(); goto menu; } switch (item) { case 0: AaruConsole.WriteLine("Returning to special SCSI MultiMedia Commands menu..."); return; case 1: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA {0} response:", sectors + 5); if (buffer != null) { PrintHex.PrintHexArray(dataBuffer, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 2: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA {0} sense:", sectors + 5); if (senseBuffer != null) { PrintHex.PrintHexArray(dataSense, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 3: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA {0} decoded sense:", sectors + 5); AaruConsole.Write("{0}", Sense.PrettifySense(dataSense)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 4: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA {0} (scrambled) response:", sectors + 5); if (buffer != null) { PrintHex.PrintHexArray(scrambledBuffer, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 5: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA {0} (scrambled) sense:", sectors + 5); if (senseBuffer != null) { PrintHex.PrintHexArray(scrambledSense, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 6: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA {0} (scrambled) decoded sense:", sectors + 5); AaruConsole.Write("{0}", Sense.PrettifySense(scrambledSense)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 7: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA's PQ {0} response:", sectors + 5); if (buffer != null) { PrintHex.PrintHexArray(pqBuffer, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 8: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA's PQ {0} sense:", sectors + 5); if (senseBuffer != null) { PrintHex.PrintHexArray(pqSense, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 9: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA's PQ {0} decoded sense:", sectors + 5); AaruConsole.Write("{0}", Sense.PrettifySense(pqSense)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 10: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA's RW {0} response:", sectors + 5); if (buffer != null) { PrintHex.PrintHexArray(rwBuffer, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 11: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA's RW {0} sense:", sectors + 5); if (senseBuffer != null) { PrintHex.PrintHexArray(rwSense, 64); } AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 12: System.Console.Clear(); AaruConsole.WriteLine("Device: {0}", devPath); AaruConsole.WriteLine("LBA's RW {0} decoded sense:", sectors + 5); AaruConsole.Write("{0}", Sense.PrettifySense(rwSense)); AaruConsole.WriteLine("Press any key to continue..."); System.Console.ReadKey(); goto menu; case 13: goto start; default: AaruConsole.WriteLine("Incorrect option. Press any key to continue..."); System.Console.ReadKey(); System.Console.Clear(); goto menu; } }
// TODO: Get cartridge serial number from Certance vendor EVPD /// <summary>Dumps a SCSI Block Commands device or a Reduced Block Commands devices</summary> void Scsi() { int resets = 0; if (_dev.IsRemovable) { InitProgress?.Invoke(); deviceGotReset: bool sense = _dev.ScsiTestUnitReady(out byte[] senseBuf, _dev.Timeout, out _); if (sense) { var decSense = Sense.Decode(senseBuf); if (decSense.HasValue) { ErrorMessage?. Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h"); _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); // Just retry, for 5 times if (decSense.Value.ASC == 0x29) { resets++; if (resets < 5) { goto deviceGotReset; } } if (decSense.Value.ASC == 0x3A) { int leftRetries = 5; while (leftRetries > 0) { PulseProgress?.Invoke("Waiting for drive to become ready"); Thread.Sleep(2000); sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); if (!sense) { break; } decSense = Sense.Decode(senseBuf); if (decSense.HasValue) { ErrorMessage?. Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h"); _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); } leftRetries--; } if (sense) { StoppingErrorMessage?.Invoke("Please insert media in drive"); return; } } else if (decSense.Value.ASC == 0x04 && decSense.Value.ASCQ == 0x01) { int leftRetries = 50; while (leftRetries > 0) { PulseProgress?.Invoke("Waiting for drive to become ready"); Thread.Sleep(2000); sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); if (!sense) { break; } decSense = Sense.Decode(senseBuf); if (decSense.HasValue) { ErrorMessage?. Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h"); _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); } leftRetries--; } if (sense) { StoppingErrorMessage?. Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); return; } } /*else if (decSense.Value.ASC == 0x29 && decSense.Value.ASCQ == 0x00) * { * if (!deviceReset) * { * deviceReset = true; * ErrorMessage?.Invoke("Device did reset, retrying..."); * goto retryTestReady; * } * * StoppingErrorMessage?.Invoke(string.Format("Error testing unit was ready:\n{0}", * Decoders.SCSI.Sense.PrettifySense(senseBuf))); * return; * }*/ // These should be trapped by the OS but seems in some cases they're not else if (decSense.Value.ASC == 0x28) { int leftRetries = 10; while (leftRetries > 0) { PulseProgress?.Invoke("Waiting for drive to become ready"); Thread.Sleep(2000); sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); if (!sense) { break; } decSense = Sense.Decode(senseBuf); if (decSense.HasValue) { ErrorMessage?. Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h"); _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); } leftRetries--; } if (sense) { StoppingErrorMessage?. Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); return; } } else { StoppingErrorMessage?. Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); return; } } else { StoppingErrorMessage?.Invoke("Unknown testing unit was ready."); return; } } EndProgress?.Invoke(); } switch (_dev.ScsiType) { case PeripheralDeviceTypes.SequentialAccess: if (_dumpRaw) { StoppingErrorMessage?.Invoke("Tapes cannot be dumped raw."); return; } if (_outputPlugin is IWritableTapeImage) { Ssc(); } else { StoppingErrorMessage?. Invoke("The specified plugin does not support storing streaming tape images."); } return; case PeripheralDeviceTypes.MultiMediaDevice: if (_outputPlugin is IWritableOpticalImage) { Mmc(); } else { StoppingErrorMessage?. Invoke("The specified plugin does not support storing optical disc images."); } return; case PeripheralDeviceTypes.BridgingExpander when _dev.Model.StartsWith("MDM", StringComparison.InvariantCulture) || _dev.Model.StartsWith("MDH", StringComparison.InvariantCulture): MiniDisc(); break; default: Sbc(null, MediaType.Unknown, false); break; } }
/// <summary>Scan the medium for a contiguous set of written or blank logical blocks</summary> /// <param name="senseBuffer">Sense buffer.</param> /// <param name="relAddr">Set to <c>true</c> if <paramref name="lba" /> is relative</param> /// <param name="lba">Logical block address where to start the search.</param> /// <param name="scanLength">Number of blocks to scan</param> /// <param name="foundBlocks">How many blocks were found</param> /// <param name="timeout">Timeout.</param> /// <param name="duration">Duration.</param> /// <param name="written"> /// If set to <c>true</c> drive will search for written blocks, otherwise it will search for blank /// blocks /// </param> /// <param name="advancedScan">If set to <c>true</c> drive will consider the search area has contiguous blocks</param> /// <param name="reverse">If set to <c>true</c> drive will search in reverse</param> /// <param name="partial"> /// If set to <c>true</c> return even if the total number of blocks requested is not found but the /// other parameters are met /// </param> /// <param name="requested">Number of contiguous blocks to find</param> /// <param name="foundLba">First LBA found</param> public bool MediumScan(out byte[] senseBuffer, bool written, bool advancedScan, bool reverse, bool partial, bool relAddr, uint lba, uint requested, uint scanLength, out uint foundLba, out uint foundBlocks, uint timeout, out double duration) { senseBuffer = new byte[64]; byte[] cdb = new byte[10]; byte[] buffer = new byte[0]; foundLba = 0; foundBlocks = 0; cdb[0] = (byte)ScsiCommands.MediumScan; if (written) { cdb[1] += 0x10; } if (advancedScan) { cdb[1] += 0x08; } if (reverse) { cdb[1] += 0x04; } if (partial) { cdb[1] += 0x02; } if (relAddr) { cdb[1] += 0x01; } cdb[2] = (byte)((lba & 0xFF000000) >> 24); cdb[3] = (byte)((lba & 0xFF0000) >> 16); cdb[4] = (byte)((lba & 0xFF00) >> 8); cdb[5] = (byte)(lba & 0xFF); if (requested > 0 || scanLength > 1) { buffer = new byte[8]; buffer[0] = (byte)((requested & 0xFF000000) >> 24); buffer[1] = (byte)((requested & 0xFF0000) >> 16); buffer[2] = (byte)((requested & 0xFF00) >> 8); buffer[3] = (byte)(requested & 0xFF); buffer[4] = (byte)((scanLength & 0xFF000000) >> 24); buffer[5] = (byte)((scanLength & 0xFF0000) >> 16); buffer[6] = (byte)((scanLength & 0xFF00) >> 8); buffer[7] = (byte)(scanLength & 0xFF); cdb[8] = 8; } LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, buffer.Length == 0 ? ScsiDirection.None : ScsiDirection.Out, out duration, out bool sense); Error = LastError != 0; AaruConsole.DebugWriteLine("SCSI Device", "MEDIUM SCAN took {0} ms.", duration); if (Error) { return(sense); } DecodedSense?decodedSense = Sense.Decode(senseBuffer); switch (decodedSense?.SenseKey) { case SenseKeys.NoSense: return(false); case SenseKeys.Equal when decodedSense.Value.Fixed?.InformationValid == true: foundBlocks = decodedSense.Value.Fixed.Value.CommandSpecific; foundLba = decodedSense.Value.Fixed.Value.Information; return(false); default: return(sense); } }
/// <summary>Retried errored sectors in CompactDisc</summary> /// <param name="audioExtents">Extents with audio sectors</param> /// <param name="blockSize">Size of the read sector in bytes</param> /// <param name="currentTry">Current dump hardware try</param> /// <param name="extents">Extents</param> /// <param name="offsetBytes">Read offset</param> /// <param name="readcd">Device supports READ CD</param> /// <param name="sectorsForOffset">Sectors needed to fix offset</param> /// <param name="subSize">Subchannel size in bytes</param> /// <param name="supportedSubchannel">Drive's maximum supported subchannel</param> /// <param name="supportsLongSectors">Supports reading EDC and ECC</param> /// <param name="totalDuration">Total commands duration</param> /// <param name="tracks">Disc tracks</param> /// <param name="subLog">Subchannel log</param> /// <param name="desiredSubchannel">Subchannel desired to save</param> /// <param name="isrcs">List of disc ISRCs</param> /// <param name="mcn">Disc media catalogue number</param> /// <param name="subchannelExtents">List of subchannels not yet dumped correctly</param> /// <param name="smallestPregapLbaPerTrack">List of smallest pregap relative address per track</param> void RetryCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry, ExtentsULong extents, int offsetBytes, bool readcd, int sectorsForOffset, uint subSize, MmcSubchannel supportedSubchannel, ref double totalDuration, SubchannelLog subLog, MmcSubchannel desiredSubchannel, Track[] tracks, Dictionary <byte, string> isrcs, ref string mcn, HashSet <int> subchannelExtents, Dictionary <byte, int> smallestPregapLbaPerTrack, bool supportsLongSectors) { bool sense = true; // Sense indicator byte[] cmdBuf = null; // Data buffer double cmdDuration; // Command execution time const uint sectorSize = 2352; // Full sector size byte[] senseBuf = null; // Sense buffer PlextorSubchannel supportedPlextorSubchannel; switch (supportedSubchannel) { case MmcSubchannel.None: supportedPlextorSubchannel = PlextorSubchannel.None; break; case MmcSubchannel.Raw: supportedPlextorSubchannel = PlextorSubchannel.Pack; break; case MmcSubchannel.Q16: supportedPlextorSubchannel = PlextorSubchannel.Q16; break; default: supportedPlextorSubchannel = PlextorSubchannel.None; break; } if (_resume.BadBlocks.Count <= 0 || _aborted || _retryPasses <= 0) { return; } int pass = 1; bool forward = true; bool runningPersistent = false; Modes.ModePage?currentModePage = null; byte[] md6; byte[] md10; if (_persistent) { Modes.ModePage_01_MMC pgMmc; sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout, out _); if (sense) { sense = _dev.ModeSense10(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout, out _); if (!sense) { Modes.DecodedMode?dcMode10 = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); if (dcMode10?.Pages != null) { foreach (Modes.ModePage modePage in dcMode10.Value.Pages.Where(modePage => modePage.Page == 0x01 && modePage.Subpage == 0x00)) { currentModePage = modePage; } } } } else { Modes.DecodedMode?dcMode6 = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); if (dcMode6?.Pages != null) { foreach (Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => modePage.Page == 0x01 && modePage.Subpage == 0x00)) { currentModePage = modePage; } } } if (currentModePage == null) { pgMmc = new Modes.ModePage_01_MMC { PS = false, ReadRetryCount = 32, Parameter = 0x00 }; currentModePage = new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) }; } pgMmc = new Modes.ModePage_01_MMC { PS = false, ReadRetryCount = 255, Parameter = 0x20 }; var md = new Modes.DecodedMode { Header = new Modes.ModeHeader(), Pages = new[] { new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) } } }; md6 = Modes.EncodeMode6(md, _dev.ScsiType); md10 = Modes.EncodeMode10(md, _dev.ScsiType); UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks)."); _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); if (sense) { sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); } if (sense) { UpdateStatus?. Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); _dumpLog.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); } else { runningPersistent = true; } } InitProgress?.Invoke(); cdRepeatRetry: ulong[] tmpArray = _resume.BadBlocks.ToArray(); List <ulong> sectorsNotEvenPartial = new List <ulong>(); for (int i = 0; i < tmpArray.Length; i++) { ulong badSector = tmpArray[i]; if (_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); _dumpLog.WriteLine("Aborted!"); break; } PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, forward ? "forward" : "reverse", runningPersistent ? "recovering partial data, " : "")); Track track = tracks.OrderBy(t => t.TrackStartSector). LastOrDefault(t => badSector >= t.TrackStartSector); byte sectorsToReRead = 1; uint badSectorToReRead = (uint)badSector; if (_fixOffset && audioExtents.Contains(badSector) && offsetBytes != 0) { if (offsetBytes > 0) { badSectorToReRead -= (uint)sectorsForOffset; } sectorsToReRead = (byte)(sectorsForOffset + 1); } if (_supportsPlextorD8 && audioExtents.Contains(badSector)) { sense = ReadPlextorWithSubchannel(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, sectorsToReRead, supportedPlextorSubchannel, out cmdDuration); totalDuration += cmdDuration; } else if (readcd) { sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, sectorsToReRead, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); totalDuration += cmdDuration; } if (sense || _dev.Error) { _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); if (!runningPersistent) { continue; } DecodedSense?decSense = Sense.Decode(senseBuf); // MEDIUM ERROR, retry with ignore error below if (decSense.HasValue && decSense.Value.ASC == 0x11) { if (!sectorsNotEvenPartial.Contains(badSector)) { sectorsNotEvenPartial.Add(badSector); } } } // Because one block has been partially used to fix the offset if (_fixOffset && audioExtents.Contains(badSector) && offsetBytes != 0) { uint blocksToRead = sectorsToReRead; FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead, subSize, ref cmdBuf, blockSize, false); } if (!sense && !_dev.Error) { _resume.BadBlocks.Remove(badSector); extents.Add(badSector); UpdateStatus?.Invoke($"Correctly retried sector {badSector} in pass {pass}."); _dumpLog.WriteLine("Correctly retried sector {0} in pass {1}.", badSector, pass); sectorsNotEvenPartial.Remove(badSector); } else { _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); } if (supportedSubchannel != MmcSubchannel.None) { byte[] data = new byte[sectorSize]; byte[] sub = new byte[subSize]; Array.Copy(cmdBuf, 0, data, 0, sectorSize); Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); if (supportsLongSectors) { _outputPlugin.WriteSectorLong(data, badSector); } else { _outputPlugin.WriteSector(Sector.GetUserData(data), badSector); } bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents, _fixSubchannelPosition, _outputPlugin, _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); // Set tracks and go back if (!indexesChanged) { continue; } (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList()); i--; } else { if (supportsLongSectors) { _outputPlugin.WriteSectorLong(cmdBuf, badSector); } else { _outputPlugin.WriteSector(Sector.GetUserData(cmdBuf), badSector); } } } if (pass < _retryPasses && !_aborted && _resume.BadBlocks.Count > 0) { pass++; forward = !forward; _resume.BadBlocks.Sort(); if (!forward) { _resume.BadBlocks.Reverse(); } goto cdRepeatRetry; } EndProgress?.Invoke(); // TODO: Enable when underlying images support lead-outs /* * RetryCdLeadOuts(blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration, * leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, read6, read10, read12, read16, readcd, * supportedSubchannel, subSize, ref totalDuration); */ // Try to ignore read errors, on some drives this allows to recover partial even if damaged data if (_persistent && sectorsNotEvenPartial.Count > 0) { var pgMmc = new Modes.ModePage_01_MMC { PS = false, ReadRetryCount = 255, Parameter = 0x01 }; var md = new Modes.DecodedMode { Header = new Modes.ModeHeader(), Pages = new[] { new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) } } }; md6 = Modes.EncodeMode6(md, _dev.ScsiType); md10 = Modes.EncodeMode10(md, _dev.ScsiType); _dumpLog.WriteLine("Sending MODE SELECT to drive (ignore error correction)."); sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); if (sense) { sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); } if (!sense) { runningPersistent = true; InitProgress?.Invoke(); for (int i = 0; i < sectorsNotEvenPartial.Count; i++) { ulong badSector = sectorsNotEvenPartial[i]; if (_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); _dumpLog.WriteLine("Aborted!"); break; } PulseProgress?.Invoke($"Trying to get partial data for sector {badSector}"); Track track = tracks.OrderBy(t => t.TrackStartSector). LastOrDefault(t => badSector >= t.TrackStartSector); if (readcd) { sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)badSector, blockSize, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); totalDuration += cmdDuration; } if (sense || _dev.Error) { _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); continue; } _dumpLog.WriteLine("Got partial data for sector {0} in pass {1}.", badSector, pass); if (supportedSubchannel != MmcSubchannel.None) { byte[] data = new byte[sectorSize]; byte[] sub = new byte[subSize]; Array.Copy(cmdBuf, 0, data, 0, sectorSize); Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); if (supportsLongSectors) { _outputPlugin.WriteSectorLong(data, badSector); } else { _outputPlugin.WriteSector(Sector.GetUserData(data), badSector); } bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents, _fixSubchannelPosition, _outputPlugin, _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); // Set tracks and go back if (indexesChanged) { (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList()); i--; } } else { if (supportsLongSectors) { _outputPlugin.WriteSectorLong(cmdBuf, badSector); } else { _outputPlugin.WriteSector(Sector.GetUserData(cmdBuf), badSector); } } } EndProgress?.Invoke(); } } if (runningPersistent && currentModePage.HasValue) { var md = new Modes.DecodedMode { Header = new Modes.ModeHeader(), Pages = new[] { currentModePage.Value } }; md6 = Modes.EncodeMode6(md, _dev.ScsiType); md10 = Modes.EncodeMode10(md, _dev.ScsiType); _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); if (sense) { _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); } } EndProgress?.Invoke(); }
public TestedMedia ReportScsiMedia() { var mediaTest = new TestedMedia(); AaruConsole.WriteLine("Querying SCSI READ CAPACITY..."); bool sense = _dev.ReadCapacity(out byte[] buffer, out byte[] senseBuffer, _dev.Timeout, out _); if (!sense && !_dev.Error) { mediaTest.SupportsReadCapacity = true; mediaTest.Blocks = (ulong)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]) + 1; mediaTest.BlockSize = (uint)((buffer[4] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]); } AaruConsole.WriteLine("Querying SCSI READ CAPACITY (16)..."); sense = _dev.ReadCapacity16(out buffer, out buffer, _dev.Timeout, out _); if (!sense && !_dev.Error) { mediaTest.SupportsReadCapacity16 = true; byte[] temp = new byte[8]; Array.Copy(buffer, 0, temp, 0, 8); Array.Reverse(temp); mediaTest.Blocks = BitConverter.ToUInt64(temp, 0) + 1; mediaTest.BlockSize = (uint)((buffer[8] << 24) + (buffer[9] << 16) + (buffer[10] << 8) + buffer[11]); } Modes.DecodedMode?decMode = null; AaruConsole.WriteLine("Querying SCSI MODE SENSE (10)..."); sense = _dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, _dev.Timeout, out _); if (!sense && !_dev.Error) { decMode = Modes.DecodeMode10(buffer, _dev.ScsiType); mediaTest.ModeSense10Data = buffer; } AaruConsole.WriteLine("Querying SCSI MODE SENSE..."); sense = _dev.ModeSense(out buffer, out senseBuffer, _dev.Timeout, out _); if (!sense && !_dev.Error) { decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType); mediaTest.ModeSense6Data = buffer; } if (decMode.HasValue) { mediaTest.MediumType = (byte)decMode.Value.Header.MediumType; if (decMode.Value.Header.BlockDescriptors?.Length > 0) { mediaTest.Density = (byte)decMode.Value.Header.BlockDescriptors[0].Density; } } AaruConsole.WriteLine("Trying SCSI READ (6)..."); mediaTest.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 0, mediaTest.BlockSize ?? 512, _dev.Timeout, out _); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead6); mediaTest.Read6Data = buffer; AaruConsole.WriteLine("Trying SCSI READ (10)..."); mediaTest.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, false, false, false, 0, mediaTest.BlockSize ?? 512, 0, 1, _dev.Timeout, out _); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead10); mediaTest.Read10Data = buffer; AaruConsole.WriteLine("Trying SCSI READ (12)..."); mediaTest.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, false, false, false, 0, mediaTest.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead12); mediaTest.Read12Data = buffer; AaruConsole.WriteLine("Trying SCSI READ (16)..."); mediaTest.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, false, false, 0, mediaTest.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead16); mediaTest.Read16Data = buffer; mediaTest.LongBlockSize = mediaTest.BlockSize; AaruConsole.WriteLine("Trying SCSI READ LONG (10)..."); sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 0xFFFF, _dev.Timeout, out _); if (sense && !_dev.Error) { DecodedSense?decSense = Sense.Decode(senseBuffer); if (decSense?.SenseKey == SenseKeys.IllegalRequest && decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) { mediaTest.SupportsReadLong = true; bool valid = decSense?.Fixed?.InformationValid == true; bool ili = decSense?.Fixed?.ILI == true; uint information = decSense?.Fixed?.Information ?? 0; if (decSense?.Descriptor.HasValue == true && decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00))