/// <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="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> 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[32]; 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); } FixedSense?decodedSense = Sense.DecodeFixed(senseBuffer); switch (decodedSense?.SenseKey) { case SenseKeys.NoSense: return(false); case SenseKeys.Equal when decodedSense.Value.InformationValid: foundBlocks = decodedSense.Value.CommandSpecific; foundLba = decodedSense.Value.Information; return(false); default: return(true); } }
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) { 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.All; break; case MmcSubchannel.Q16: supportedPlextorSubchannel = PlextorSubchannel.Q16; break; case MmcSubchannel.Rw: supportedPlextorSubchannel = PlextorSubchannel.Pack; 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) { if (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) { if (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) { if (!runningPersistent) { continue; } FixedSense?decSense = Sense.DecodeFixed(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); } 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); _outputPlugin.WriteSectorLong(data, badSector); bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents); // Set tracks and go back if (indexesChanged) { (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList()); i--; } } else { _outputPlugin.WriteSectorLong(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) { 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); _outputPlugin.WriteSectorLong(data, badSector); bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents); // Set tracks and go back if (indexesChanged) { (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList()); i--; } } else { _outputPlugin.WriteSectorLong(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(); }
/// <summary>Reads all CD user data</summary> /// <param name="audioExtents">Extents with audio sectors</param> /// <param name="blocks">Total number of positive sectors</param> /// <param name="blockSize">Size of the read sector in bytes</param> /// <param name="currentSpeed">Current read speed</param> /// <param name="currentTry">Current dump hardware try</param> /// <param name="extents">Extents</param> /// <param name="ibgLog">IMGBurn log</param> /// <param name="imageWriteDuration">Duration of image write</param> /// <param name="lastSector">Last sector number</param> /// <param name="leadOutExtents">Lead-out extents</param> /// <param name="maxSpeed">Maximum speed</param> /// <param name="mhddLog">MHDD log</param> /// <param name="minSpeed">Minimum speed</param> /// <param name="newTrim">Is trim a new one?</param> /// <param name="nextData">Next cluster of sectors is all data</param> /// <param name="offsetBytes">Read offset</param> /// <param name="read6">Device supports READ(6)</param> /// <param name="read10">Device supports READ(10)</param> /// <param name="read12">Device supports READ(12)</param> /// <param name="read16">Device supports READ(16)</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> void ReadCdData(ExtentsULong audioExtents, ulong blocks, uint blockSize, ref double currentSpeed, DumpHardwareType currentTry, ExtentsULong extents, IbgLog ibgLog, ref double imageWriteDuration, long lastSector, ExtentsULong leadOutExtents, ref double maxSpeed, MhddLog mhddLog, ref double minSpeed, out bool newTrim, bool nextData, int offsetBytes, bool read6, bool read10, bool read12, bool read16, bool readcd, int sectorsForOffset, uint subSize, MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration, Track[] tracks) { ulong sectorSpeedStart = 0; // Used to calculate correct speed DateTime timeSpeedStart = DateTime.UtcNow; // Time of start for speed calculation uint blocksToRead = 0; // How many sectors to read at once bool sense = true; // Sense indicator byte[] cmdBuf = null; // Data buffer byte[] senseBuf = null; // Sense buffer double cmdDuration = 0; // Command execution time const uint sectorSize = 2352; // Full sector size byte[] tmpBuf; // Temporary buffer newTrim = false; InitProgress?.Invoke(); bool crossingLeadOut = false; bool failedCrossingLeadOut = false; for (ulong i = _resume.NextBlock; (long)i <= lastSector; i += blocksToRead) { if (_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); _dumpLog.WriteLine("Aborted!"); break; } while (leadOutExtents.Contains(i)) { i++; } if ((long)i > lastSector) { break; } uint firstSectorToRead = (uint)i; Track track = tracks.OrderBy(t => t.TrackStartSector).LastOrDefault(t => i >= t.TrackStartSector); blocksToRead = 0; bool inData = nextData; for (ulong j = i; j < i + _maximumReadable; j++) { if (j > (ulong)lastSector) { if (!failedCrossingLeadOut) { blocksToRead += (uint)sectorsForOffset; } if (sectorsForOffset > 0) { crossingLeadOut = true; } break; } if (nextData) { if (audioExtents.Contains(j)) { nextData = false; break; } blocksToRead++; } else { if (!audioExtents.Contains(j)) { nextData = true; break; } blocksToRead++; } } if (track.TrackSequence != 0 && (i + blocksToRead) - (ulong)sectorsForOffset > track.TrackEndSector + 1) { blocksToRead = (uint)(((track.TrackEndSector + 1) - i) + (ulong)sectorsForOffset); } if (blocksToRead == 1) { blocksToRead += (uint)sectorsForOffset; } if (_fixOffset && !inData) { // TODO: FreeBSD bug if (offsetBytes < 0) { if (i == 0) { firstSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1 } else { firstSectorToRead -= (uint)sectorsForOffset; } } } #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator // ReSharper disable CompareOfFloatsByEqualityOperator if (currentSpeed > maxSpeed && currentSpeed != 0) { maxSpeed = currentSpeed; } if (currentSpeed < minSpeed && currentSpeed != 0) { minSpeed = currentSpeed; } // ReSharper restore CompareOfFloatsByEqualityOperator #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, (long)blocks); if (readcd) { sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); totalDuration += cmdDuration; } else if (read16) { sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, firstSectorToRead, blockSize, 0, blocksToRead, false, _dev.Timeout, out cmdDuration); } else if (read12) { sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead, blockSize, 0, blocksToRead, false, _dev.Timeout, out cmdDuration); } else if (read10) { sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead, blockSize, 0, (ushort)blocksToRead, _dev.Timeout, out cmdDuration); } else if (read6) { sense = _dev.Read6(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, (byte)blocksToRead, _dev.Timeout, out cmdDuration); } if (!sense && !_dev.Error) { // Because one block has been partially used to fix the offset if (_fixOffset && !inData && offsetBytes != 0) { int offsetFix = offsetBytes < 0 ? (int)(sectorSize - (offsetBytes * -1)) : offsetBytes; if (supportedSubchannel != MmcSubchannel.None) { // De-interleave subchannel byte[] data = new byte[sectorSize * blocksToRead]; byte[] sub = new byte[subSize * blocksToRead]; for (int b = 0; b < blocksToRead; b++) { Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); } if (failedCrossingLeadOut) { blocksToRead += (uint)sectorsForOffset; tmpBuf = new byte[sectorSize * blocksToRead]; Array.Copy(data, 0, tmpBuf, 0, data.Length); data = tmpBuf; tmpBuf = new byte[subSize * blocksToRead]; Array.Copy(sub, 0, tmpBuf, 0, sub.Length); sub = tmpBuf; } tmpBuf = new byte[sectorSize * (blocksToRead - sectorsForOffset)]; Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length); data = tmpBuf; blocksToRead -= (uint)sectorsForOffset; // Re-interleave subchannel cmdBuf = new byte[blockSize * blocksToRead]; for (int b = 0; b < blocksToRead; b++) { Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize); Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize); } } else { if (failedCrossingLeadOut) { blocksToRead += (uint)sectorsForOffset; tmpBuf = new byte[blockSize * blocksToRead]; Array.Copy(cmdBuf, 0, tmpBuf, 0, cmdBuf.Length); cmdBuf = tmpBuf; } tmpBuf = new byte[blockSize * (blocksToRead - sectorsForOffset)]; Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length); cmdBuf = tmpBuf; blocksToRead -= (uint)sectorsForOffset; } } mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); extents.Add(i, blocksToRead, true); DateTime writeStart = DateTime.Now; if (supportedSubchannel != MmcSubchannel.None) { byte[] data = new byte[sectorSize * blocksToRead]; byte[] sub = new byte[subSize * blocksToRead]; for (int b = 0; b < blocksToRead; b++) { Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); } _outputPlugin.WriteSectorsLong(data, i, blocksToRead); _outputPlugin.WriteSectorsTag(sub, i, blocksToRead, SectorTagType.CdSectorSubchannel); } else { if (supportsLongSectors) { _outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead); } else { if (cmdBuf.Length % sectorSize == 0) { byte[] data = new byte[2048 * blocksToRead]; for (int b = 0; b < blocksToRead; b++) { Array.Copy(cmdBuf, (int)(16 + (b * blockSize)), data, 2048 * b, 2048); } _outputPlugin.WriteSectors(data, i, blocksToRead); } else { _outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead); } } } imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; } else { if (crossingLeadOut && Sense.DecodeFixed(senseBuf)?.ASC == 0x21) { failedCrossingLeadOut = true; blocksToRead = 0; continue; } // TODO: Reset device after X errors if (_stopOnError) { return; // TODO: Return more cleanly } if (i + _skip > blocks) { _skip = (uint)(blocks - i); } // Write empty data DateTime writeStart = DateTime.Now; if (supportedSubchannel != MmcSubchannel.None) { _outputPlugin.WriteSectorsLong(new byte[sectorSize * _skip], i, _skip); _outputPlugin.WriteSectorsTag(new byte[subSize * _skip], i, _skip, SectorTagType.CdSectorSubchannel); } else { if (supportsLongSectors) { _outputPlugin.WriteSectorsLong(new byte[blockSize * _skip], i, _skip); } else { if (cmdBuf.Length % sectorSize == 0) { _outputPlugin.WriteSectors(new byte[2048 * _skip], i, _skip); } else { _outputPlugin.WriteSectorsLong(new byte[blockSize * _skip], i, _skip); } } } imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; for (ulong b = i; b < i + _skip; b++) { _resume.BadBlocks.Add(b); } DicConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(i, 0); _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); i += _skip - blocksToRead; newTrim = true; } sectorSpeedStart += blocksToRead; _resume.NextBlock = i + blocksToRead; double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; if (elapsed < 1) { continue; } currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed); sectorSpeedStart = 0; timeSpeedStart = DateTime.UtcNow; } EndProgress?.Invoke(); if (!failedCrossingLeadOut) { return; } _dumpLog.WriteLine("Failed crossing into Lead-Out, dump may not be correct."); UpdateStatus?.Invoke("Failed crossing into Lead-Out, dump may not be correct."); }
public TestedMedia ReportScsi() { var capabilities = new TestedMedia { MediaIsRecognized = true }; AaruConsole.WriteLine("Querying SCSI READ CAPACITY..."); bool sense = _dev.ReadCapacity(out byte[] buffer, out byte[] senseBuffer, _dev.Timeout, out _); if (!sense && !_dev.Error) { capabilities.SupportsReadCapacity = true; capabilities.Blocks = (ulong)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]) + 1; capabilities.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) { capabilities.SupportsReadCapacity16 = true; byte[] temp = new byte[8]; Array.Copy(buffer, 0, temp, 0, 8); Array.Reverse(temp); capabilities.Blocks = BitConverter.ToUInt64(temp, 0) + 1; capabilities.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); capabilities.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); capabilities.ModeSense6Data = buffer; } if (decMode.HasValue) { capabilities.MediumType = (byte)decMode.Value.Header.MediumType; if (decMode.Value.Header.BlockDescriptors?.Length > 0) { capabilities.Density = (byte)decMode.Value.Header.BlockDescriptors[0].Density; } } AaruConsole.WriteLine("Trying SCSI READ (6)..."); capabilities.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 0, capabilities.BlockSize ?? 512, _dev.Timeout, out _); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead6); capabilities.Read6Data = buffer; AaruConsole.WriteLine("Trying SCSI READ (10)..."); capabilities.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, false, false, false, 0, capabilities.BlockSize ?? 512, 0, 1, _dev.Timeout, out _); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead10); capabilities.Read10Data = buffer; AaruConsole.WriteLine("Trying SCSI READ (12)..."); capabilities.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, false, false, false, 0, capabilities.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead12); capabilities.Read12Data = buffer; AaruConsole.WriteLine("Trying SCSI READ (16)..."); capabilities.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, false, false, 0, capabilities.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead16); capabilities.Read16Data = buffer; capabilities.LongBlockSize = capabilities.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) { FixedSense?decSense = Sense.DecodeFixed(senseBuffer); if (decSense?.SenseKey == SenseKeys.IllegalRequest && decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) { capabilities.SupportsReadLong = true; if (decSense.Value.InformationValid && decSense.Value.ILI) { capabilities.LongBlockSize = 0xFFFF - (decSense.Value.Information & 0xFFFF); } } } if (capabilities.SupportsReadLong != true || capabilities.LongBlockSize != capabilities.BlockSize) { return(capabilities); } if (capabilities.BlockSize == 512) { foreach (ushort testSize in new ushort[] { // Long sector sizes for floppies 514, // Long sector sizes for SuperDisk 536, 558, // Long sector sizes for 512-byte magneto-opticals 600, 610, 630 }) { sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, testSize, _dev.Timeout, out _); if (sense || _dev.Error) { continue; } capabilities.SupportsReadLong = true; capabilities.LongBlockSize = testSize; break; } } else if (capabilities.BlockSize == 1024) { foreach (ushort testSize in new ushort[] { // Long sector sizes for floppies 1026, // Long sector sizes for 1024-byte magneto-opticals 1200 }) { sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, testSize, _dev.Timeout, out _); if (sense || _dev.Error) { continue; } capabilities.SupportsReadLong = true; capabilities.LongBlockSize = testSize; break; } } else if (capabilities.BlockSize == 2048) { sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 2380, _dev.Timeout, out _); if (sense || _dev.Error) { return(capabilities); } capabilities.SupportsReadLong = true; capabilities.LongBlockSize = 2380; } else if (capabilities.BlockSize == 4096) { sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 4760, _dev.Timeout, out _); if (sense || _dev.Error) { return(capabilities); } capabilities.SupportsReadLong = true; capabilities.LongBlockSize = 4760; } else if (capabilities.BlockSize == 8192) { sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 9424, _dev.Timeout, out _); if (sense || _dev.Error) { return(capabilities); } capabilities.SupportsReadLong = true; capabilities.LongBlockSize = 9424; } return(capabilities); }
void RetryCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry, ExtentsULong extents, int offsetBytes, bool readcd, int sectorsForOffset, uint subSize, MmcSubchannel supportedSubchannel, ref double totalDuration) { bool sense = true; // Sense indicator byte[] cmdBuf = null; // Data buffer double cmdDuration; // Command execution time const uint sectorSize = 2352; // Full sector size byte[] tmpBuf; // Temporary buffer byte[] senseBuf = null; // Sense buffer 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) { if (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) { if (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."); DicConsole.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>(); foreach (ulong badSector in tmpArray) { 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, " : "")); 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 (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) { if (!runningPersistent) { continue; } FixedSense?decSense = Sense.DecodeFixed(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) { int offsetFix = offsetBytes > 0 ? (int)(sectorSize - (offsetBytes * -1)) : offsetBytes; if (supportedSubchannel != MmcSubchannel.None) { // De-interleave subchannel byte[] data = new byte[sectorSize * sectorsToReRead]; byte[] sub = new byte[subSize * sectorsToReRead]; for (int b = 0; b < sectorsToReRead; b++) { Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); } tmpBuf = new byte[sectorSize * (sectorsToReRead - sectorsForOffset)]; Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length); data = tmpBuf; // Re-interleave subchannel cmdBuf = new byte[blockSize * sectorsToReRead]; for (int b = 0; b < sectorsToReRead; b++) { Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize); Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize); } } else { tmpBuf = new byte[blockSize * (sectorsToReRead - sectorsForOffset)]; Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length); cmdBuf = tmpBuf; } } 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); } 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); _outputPlugin.WriteSectorLong(data, badSector); _outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel); } else { _outputPlugin.WriteSectorLong(cmdBuf, badSector); } } if (pass < _retryPasses && !_aborted && _resume.BadBlocks.Count > 0) { pass++; forward = !forward; _resume.BadBlocks.Sort(); _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(); foreach (ulong badSector in sectorsNotEvenPartial) { if (_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); _dumpLog.WriteLine("Aborted!"); break; } PulseProgress?.Invoke($"Trying to get partial data for sector {badSector}"); 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) { 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); _outputPlugin.WriteSectorLong(data, badSector); _outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel); } else { _outputPlugin.WriteSectorLong(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(); }
/// <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; } } FixedSense? decodedFixedSense = Sense.DecodeFixed(senseBuffer); DescriptorSense?decodedDescriptorSense = Sense.DecodeDescriptor(senseBuffer); string prettySense = Sense.PrettifySense(senseBuffer); string hexSense = string.Join(' ', senseBuffer.Select(b => $"{b:X2}")); if (decodedFixedSense.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, decodedFixedSense?.SenseKey, decodedFixedSense?.ASC, decodedFixedSense?.ASCQ, hexSense, prettySense); } else { _logSw.WriteLine("SCSI reading LBA {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}.", block, decodedFixedSense?.SenseKey, decodedFixedSense?.ASC, decodedFixedSense?.ASCQ, hexSense); } } else if (decodedDescriptorSense.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, decodedDescriptorSense?.SenseKey, decodedDescriptorSense?.ASC, decodedDescriptorSense?.ASCQ, hexSense, prettySense); } else { _logSw.WriteLine("SCSI reading LBA {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}.", block, decodedDescriptorSense?.SenseKey, decodedDescriptorSense?.ASC, decodedDescriptorSense?.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(); }
bool ScsiFindReadCommand() { read6 = !dev.Read6(out _, out byte[] senseBuf, 0, LogicalBlockSize, timeout, out _); read10 = !dev.Read10(out _, out senseBuf, 0, false, true, false, false, 0, LogicalBlockSize, 0, 1, timeout, out _); read12 = !dev.Read12(out _, out senseBuf, 0, false, true, false, false, 0, LogicalBlockSize, 0, 1, false, timeout, out _); read16 = !dev.Read16(out _, out senseBuf, 0, false, true, false, 0, LogicalBlockSize, 0, 1, false, timeout, out _); seek6 = !dev.Seek6(out senseBuf, 0, timeout, out _); seek10 = !dev.Seek10(out senseBuf, 0, timeout, out _); if (!read6 && !read10 && !read12 && !read16) { ErrorMessage = "Cannot read medium, aborting scan..."; return(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 (!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 (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 _); FixedSense?decSense; if (testSense && !dev.Error) { decSense = Sense.DecodeFixed(senseBuf); if (decSense.HasValue) { if (decSense.Value.SenseKey == SenseKeys.IllegalRequest && decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) { CanReadRaw = true; if (decSense.Value.InformationValid && decSense.Value.ILI) { LongBlockSize = 0xFFFF - (decSense.Value.Information & 0xFFFF); readLong10 = !dev.ReadLong10(out _, out senseBuf, false, false, 0, (ushort)LongBlockSize, timeout, out _); } } } } if (CanReadRaw && LongBlockSize == LogicalBlockSize) { if (LogicalBlockSize == 512) { foreach (int i in new[] { // Long sector sizes for floppies 514, // Long sector sizes for SuperDisk 536, 558, // Long sector sizes for 512-byte magneto-opticals 600, 610, 630 }) { ushort testSize = (ushort)i; testSense = dev.ReadLong16(out _, out senseBuf, false, 0, testSize, timeout, out _); if (!testSense && !dev.Error) { readLong16 = true; LongBlockSize = testSize; CanReadRaw = true; break; } testSense = dev.ReadLong10(out _, out senseBuf, false, false, 0, testSize, timeout, out _); if (testSense || dev.Error) { continue; } readLong10 = true; LongBlockSize = testSize; CanReadRaw = true; break; } } else if (LogicalBlockSize == 1024) { foreach (int i in new[] { // Long sector sizes for floppies 1026, // Long sector sizes for 1024-byte magneto-opticals 1200 }) { ushort testSize = (ushort)i; testSense = dev.ReadLong16(out _, out senseBuf, false, 0, testSize, timeout, out _); if (!testSense && !dev.Error) { readLong16 = true; LongBlockSize = testSize; CanReadRaw = true; break; } testSense = dev.ReadLong10(out _, out senseBuf, false, false, 0, testSize, timeout, out _); if (testSense || dev.Error) { continue; } readLong10 = true; LongBlockSize = testSize; CanReadRaw = true; break; } } else if (LogicalBlockSize == 2048) { testSense = dev.ReadLong16(out _, out senseBuf, false, 0, 2380, timeout, out _); if (!testSense && !dev.Error) { readLong16 = true; LongBlockSize = 2380; CanReadRaw = true; } else { testSense = dev.ReadLong10(out _, out senseBuf, false, false, 0, 2380, timeout, out _); if (!testSense && !dev.Error) { readLong10 = true; LongBlockSize = 2380; CanReadRaw = true; } } } else if (LogicalBlockSize == 4096) { testSense = dev.ReadLong16(out _, out senseBuf, false, 0, 4760, timeout, out _); if (!testSense && !dev.Error) { readLong16 = true; LongBlockSize = 4760; CanReadRaw = true; } else { testSense = dev.ReadLong10(out _, out senseBuf, false, false, 0, 4760, timeout, out _); if (!testSense && !dev.Error) { readLong10 = true; LongBlockSize = 4760; CanReadRaw = true; } } } else if (LogicalBlockSize == 8192) { testSense = dev.ReadLong16(out _, out senseBuf, false, 0, 9424, timeout, out _); if (!testSense && !dev.Error) { readLong16 = true; LongBlockSize = 9424; CanReadRaw = true; } else { testSense = dev.ReadLong10(out _, out senseBuf, false, false, 0, 9424, timeout, out _); if (!testSense && !dev.Error) { readLong10 = true; LongBlockSize = 9424; CanReadRaw = true; } } } } if (!CanReadRaw && dev.Manufacturer == "SYQUEST") { testSense = dev.SyQuestReadLong10(out _, out senseBuf, 0, 0xFFFF, timeout, out _); if (testSense) { decSense = Sense.DecodeFixed(senseBuf); if (decSense.HasValue) { if (decSense.Value.SenseKey == SenseKeys.IllegalRequest && decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) { CanReadRaw = true; if (decSense.Value.InformationValid && decSense.Value.ILI) { LongBlockSize = 0xFFFF - (decSense.Value.Information & 0xFFFF); syqReadLong10 = !dev.SyQuestReadLong10(out _, out senseBuf, 0, LongBlockSize, timeout, out _); } } else { testSense = dev.SyQuestReadLong6(out _, out senseBuf, 0, 0xFFFF, timeout, out _); if (testSense) { decSense = Sense.DecodeFixed(senseBuf); if (decSense.HasValue) { if (decSense.Value.SenseKey == SenseKeys.IllegalRequest && decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) { CanReadRaw = true; if (decSense.Value.InformationValid && decSense.Value.ILI) { LongBlockSize = 0xFFFF - (decSense.Value.Information & 0xFFFF); syqReadLong6 = !dev.SyQuestReadLong6(out _, out senseBuf, 0, LongBlockSize, timeout, out _); } } } } } } } if (!CanReadRaw && LogicalBlockSize == 256) { testSense = dev.SyQuestReadLong6(out _, out senseBuf, 0, 262, timeout, out _); if (!testSense && !dev.Error) { syqReadLong6 = true; LongBlockSize = 262; CanReadRaw = true; } } } } else { switch (dev.Manufacturer) { case "HL-DT-ST": hldtstReadRaw = !dev.HlDtStReadRawDvd(out _, out senseBuf, 0, 1, timeout, out _); break; case "PLEXTOR": plextorReadRaw = !dev.PlextorReadRawDvd(out _, out senseBuf, 0, 1, timeout, out _); break; } if (hldtstReadRaw || plextorReadRaw) { CanReadRaw = true; LongBlockSize = 2064; } // READ LONG (10) for some DVD drives if (!CanReadRaw && dev.Manufacturer == "MATSHITA") { testSense = dev.ReadLong10(out _, out senseBuf, false, false, 0, 37856, timeout, out _); if (!testSense && !dev.Error) { readLongDvd = true; LongBlockSize = 37856; CanReadRaw = true; } } } } if (CanReadRaw) { if (readLong16) { AaruConsole.WriteLine("Using SCSI READ LONG (16) command."); } else if (readLong10 || readLongDvd) { AaruConsole.WriteLine("Using SCSI READ LONG (10) command."); } else if (syqReadLong10) { AaruConsole.WriteLine("Using SyQuest READ LONG (10) command."); } else if (syqReadLong6) { AaruConsole.WriteLine("Using SyQuest READ LONG (6) command."); } else if (hldtstReadRaw) { AaruConsole.WriteLine("Using HL-DT-ST raw DVD reading."); } else if (plextorReadRaw) { AaruConsole.WriteLine("Using Plextor raw DVD reading."); } } else if (read16) { AaruConsole.WriteLine("Using SCSI READ (16) command."); } else if (read12) { AaruConsole.WriteLine("Using SCSI READ (12) command."); } else if (read10) { AaruConsole.WriteLine("Using SCSI READ (10) command."); } else if (read6) { AaruConsole.WriteLine("Using SCSI READ (6) command."); } return(false); }
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; } FixedSense?decodedSense = Sense.DecodeFixed(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; } FixedSense?decodedSense = Sense.DecodeFixed(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; } FixedSense?decodedSense = Sense.DecodeFixed(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; } }
/// <summary> /// Fills a SCSI device report with parameters and media tests specific to a Streaming device /// </summary> /// <param name="dev">Device</param> /// <param name="report">Device report</param> /// <param name="debug">If debug is enabled</param> internal static void Report(Device dev, ref DeviceReport report, bool debug) { if (report == null) { return; } bool sense; const uint TIMEOUT = 5; ConsoleKeyInfo pressedKey; report.SCSI.SequentialDevice = new sscType(); DicConsole.WriteLine("Querying SCSI READ BLOCK LIMITS..."); sense = dev.ReadBlockLimits(out byte[] buffer, out byte[] senseBuffer, TIMEOUT, out _); if (!sense) { BlockLimits.BlockLimitsData?decBl = BlockLimits.Decode(buffer); if (decBl.HasValue) { if (decBl.Value.granularity > 0) { report.SCSI.SequentialDevice.BlockSizeGranularitySpecified = true; report.SCSI.SequentialDevice.BlockSizeGranularity = decBl.Value.granularity; } if (decBl.Value.maxBlockLen > 0) { report.SCSI.SequentialDevice.MaxBlockLengthSpecified = true; report.SCSI.SequentialDevice.MaxBlockLength = decBl.Value.maxBlockLen; } if (decBl.Value.minBlockLen > 0) { report.SCSI.SequentialDevice.MinBlockLengthSpecified = true; report.SCSI.SequentialDevice.MinBlockLength = decBl.Value.minBlockLen; } } } DicConsole.WriteLine("Querying SCSI REPORT DENSITY SUPPORT..."); sense = dev.ReportDensitySupport(out buffer, out senseBuffer, false, false, TIMEOUT, out _); if (!sense) { DensitySupport.DensitySupportHeader?dsh = DensitySupport.DecodeDensity(buffer); if (dsh.HasValue) { report.SCSI.SequentialDevice.SupportedDensities = new SupportedDensity[dsh.Value.descriptors.Length]; for (int i = 0; i < dsh.Value.descriptors.Length; i++) { report.SCSI.SequentialDevice.SupportedDensities[i].BitsPerMm = dsh.Value.descriptors[i].bpmm; report.SCSI.SequentialDevice.SupportedDensities[i].Capacity = dsh.Value.descriptors[i].capacity; report.SCSI.SequentialDevice.SupportedDensities[i].DefaultDensity = dsh.Value.descriptors[i].defaultDensity; report.SCSI.SequentialDevice.SupportedDensities[i].Description = dsh.Value.descriptors[i].description; report.SCSI.SequentialDevice.SupportedDensities[i].Duplicate = dsh.Value.descriptors[i].duplicate; report.SCSI.SequentialDevice.SupportedDensities[i].Name = dsh.Value.descriptors[i].name; report.SCSI.SequentialDevice.SupportedDensities[i].Organization = dsh.Value.descriptors[i].organization; report.SCSI.SequentialDevice.SupportedDensities[i].PrimaryCode = dsh.Value.descriptors[i].primaryCode; report.SCSI.SequentialDevice.SupportedDensities[i].SecondaryCode = dsh.Value.descriptors[i].secondaryCode; report.SCSI.SequentialDevice.SupportedDensities[i].Tracks = dsh.Value.descriptors[i].tracks; report.SCSI.SequentialDevice.SupportedDensities[i].Width = dsh.Value.descriptors[i].width; report.SCSI.SequentialDevice.SupportedDensities[i].Writable = dsh.Value.descriptors[i].writable; } } } DicConsole.WriteLine("Querying SCSI REPORT DENSITY SUPPORT for medium types..."); sense = dev.ReportDensitySupport(out buffer, out senseBuffer, true, false, TIMEOUT, out _); if (!sense) { DensitySupport.MediaTypeSupportHeader?mtsh = DensitySupport.DecodeMediumType(buffer); if (mtsh.HasValue) { report.SCSI.SequentialDevice.SupportedMediaTypes = new SupportedMedia[mtsh.Value.descriptors.Length]; for (int i = 0; i < mtsh.Value.descriptors.Length; i++) { report.SCSI.SequentialDevice.SupportedMediaTypes[i].Description = mtsh.Value.descriptors[i].description; report.SCSI.SequentialDevice.SupportedMediaTypes[i].Length = mtsh.Value.descriptors[i].length; report.SCSI.SequentialDevice.SupportedMediaTypes[i].MediumType = mtsh.Value.descriptors[i].mediumType; report.SCSI.SequentialDevice.SupportedMediaTypes[i].Name = mtsh.Value.descriptors[i].name; report.SCSI.SequentialDevice.SupportedMediaTypes[i].Organization = mtsh.Value.descriptors[i].organization; report.SCSI.SequentialDevice.SupportedMediaTypes[i].Width = mtsh.Value.descriptors[i].width; if (mtsh.Value.descriptors[i].densityCodes == null) { continue; } report.SCSI.SequentialDevice.SupportedMediaTypes[i].DensityCodes = new int[mtsh.Value.descriptors[i].densityCodes.Length]; for (int j = 0; j < mtsh.Value.descriptors.Length; j++) { report.SCSI.SequentialDevice.SupportedMediaTypes[i].DensityCodes[j] = mtsh.Value.descriptors[i].densityCodes[j]; } } } } List <SequentialMedia> seqTests = new List <SequentialMedia>(); pressedKey = new ConsoleKeyInfo(); while (pressedKey.Key != ConsoleKey.N) { pressedKey = new ConsoleKeyInfo(); while (pressedKey.Key != ConsoleKey.Y && pressedKey.Key != ConsoleKey.N) { DicConsole.Write("Do you have media that you can insert in the drive? (Y/N): "); pressedKey = System.Console.ReadKey(); DicConsole.WriteLine(); } if (pressedKey.Key != ConsoleKey.Y) { continue; } DicConsole.WriteLine("Please insert it in the drive and press any key when it is ready."); System.Console.ReadKey(true); SequentialMedia seqTest = new SequentialMedia(); DicConsole.Write("Please write a description of the media type and press enter: "); seqTest.MediumTypeName = System.Console.ReadLine(); DicConsole.Write("Please write the media manufacturer and press enter: "); seqTest.Manufacturer = System.Console.ReadLine(); DicConsole.Write("Please write the media model and press enter: "); seqTest.Model = System.Console.ReadLine(); seqTest.MediaIsRecognized = true; dev.Load(out senseBuffer, TIMEOUT, out _); sense = dev.ScsiTestUnitReady(out senseBuffer, TIMEOUT, out _); if (sense) { FixedSense?decSense = Sense.DecodeFixed(senseBuffer); if (decSense.HasValue) { if (decSense.Value.ASC == 0x3A) { int leftRetries = 20; while (leftRetries > 0) { DicConsole.Write("\rWaiting for drive to become ready"); Thread.Sleep(2000); sense = dev.ScsiTestUnitReady(out senseBuffer, TIMEOUT, out _); if (!sense) { break; } leftRetries--; } seqTest.MediaIsRecognized &= !sense; } else if (decSense.Value.ASC == 0x04 && decSense.Value.ASCQ == 0x01) { int leftRetries = 20; while (leftRetries > 0) { DicConsole.Write("\rWaiting for drive to become ready"); Thread.Sleep(2000); sense = dev.ScsiTestUnitReady(out senseBuffer, TIMEOUT, out _); if (!sense) { break; } leftRetries--; } seqTest.MediaIsRecognized &= !sense; } else { seqTest.MediaIsRecognized = false; } } else { seqTest.MediaIsRecognized = false; } } if (seqTest.MediaIsRecognized) { Modes.DecodedMode?decMode = null; DicConsole.WriteLine("Querying SCSI MODE SENSE (10)..."); sense = dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, TIMEOUT, out _); if (!sense && !dev.Error) { report.SCSI.SupportsModeSense10 = true; decMode = Modes.DecodeMode10(buffer, dev.ScsiType); if (debug) { seqTest.ModeSense10Data = buffer; } } DicConsole.WriteLine("Querying SCSI MODE SENSE..."); sense = dev.ModeSense(out buffer, out senseBuffer, TIMEOUT, out _); if (!sense && !dev.Error) { report.SCSI.SupportsModeSense6 = true; if (!decMode.HasValue) { decMode = Modes.DecodeMode6(buffer, dev.ScsiType); } if (debug) { seqTest.ModeSense6Data = buffer; } } if (decMode.HasValue) { seqTest.MediumType = (byte)decMode.Value.Header.MediumType; seqTest.MediumTypeSpecified = true; if (decMode.Value.Header.BlockDescriptors != null && decMode.Value.Header.BlockDescriptors.Length > 0) { seqTest.Density = (byte)decMode.Value.Header.BlockDescriptors[0].Density; seqTest.DensitySpecified = true; } } } DicConsole.WriteLine("Querying SCSI REPORT DENSITY SUPPORT for current media..."); sense = dev.ReportDensitySupport(out buffer, out senseBuffer, false, true, TIMEOUT, out _); if (!sense) { DensitySupport.DensitySupportHeader?dsh = DensitySupport.DecodeDensity(buffer); if (dsh.HasValue) { seqTest.SupportedDensities = new SupportedDensity[dsh.Value.descriptors.Length]; for (int i = 0; i < dsh.Value.descriptors.Length; i++) { seqTest.SupportedDensities[i].BitsPerMm = dsh.Value.descriptors[i].bpmm; seqTest.SupportedDensities[i].Capacity = dsh.Value.descriptors[i].capacity; seqTest.SupportedDensities[i].DefaultDensity = dsh.Value.descriptors[i].defaultDensity; seqTest.SupportedDensities[i].Description = dsh.Value.descriptors[i].description; seqTest.SupportedDensities[i].Duplicate = dsh.Value.descriptors[i].duplicate; seqTest.SupportedDensities[i].Name = dsh.Value.descriptors[i].name; seqTest.SupportedDensities[i].Organization = dsh.Value.descriptors[i].organization; seqTest.SupportedDensities[i].PrimaryCode = dsh.Value.descriptors[i].primaryCode; seqTest.SupportedDensities[i].SecondaryCode = dsh.Value.descriptors[i].secondaryCode; seqTest.SupportedDensities[i].Tracks = dsh.Value.descriptors[i].tracks; seqTest.SupportedDensities[i].Width = dsh.Value.descriptors[i].width; seqTest.SupportedDensities[i].Writable = dsh.Value.descriptors[i].writable; } } } DicConsole.WriteLine("Querying SCSI REPORT DENSITY SUPPORT for medium types for current media..."); sense = dev.ReportDensitySupport(out buffer, out senseBuffer, true, true, TIMEOUT, out _); if (!sense) { DensitySupport.MediaTypeSupportHeader?mtsh = DensitySupport.DecodeMediumType(buffer); if (mtsh.HasValue) { seqTest.SupportedMediaTypes = new SupportedMedia[mtsh.Value.descriptors.Length]; for (int i = 0; i < mtsh.Value.descriptors.Length; i++) { seqTest.SupportedMediaTypes[i].Description = mtsh.Value.descriptors[i].description; seqTest.SupportedMediaTypes[i].Length = mtsh.Value.descriptors[i].length; seqTest.SupportedMediaTypes[i].MediumType = mtsh.Value.descriptors[i].mediumType; seqTest.SupportedMediaTypes[i].Name = mtsh.Value.descriptors[i].name; seqTest.SupportedMediaTypes[i].Organization = mtsh.Value.descriptors[i].organization; seqTest.SupportedMediaTypes[i].Width = mtsh.Value.descriptors[i].width; if (mtsh.Value.descriptors[i].densityCodes == null) { continue; } seqTest.SupportedMediaTypes[i].DensityCodes = new int[mtsh.Value.descriptors[i].densityCodes.Length]; for (int j = 0; j < mtsh.Value.descriptors.Length; j++) { seqTest.SupportedMediaTypes[i].DensityCodes[j] = mtsh.Value.descriptors[i].densityCodes[j]; } } } } seqTest.CanReadMediaSerialSpecified = true; DicConsole.WriteLine("Trying SCSI READ MEDIA SERIAL NUMBER..."); seqTest.CanReadMediaSerial = !dev.ReadMediaSerialNumber(out buffer, out senseBuffer, TIMEOUT, out _); seqTests.Add(seqTest); } report.SCSI.SequentialDevice.TestedMedia = seqTests.ToArray(); }
// TODO: Get cartridge serial number from Certance vendor EVPD /// <summary> /// Dumps a SCSI Block Commands device or a Reduced Block Commands devices /// </summary> /// <param name="dev">Device</param> /// <param name="devicePath">Path to the device</param> /// <param name="outputPrefix">Prefix for output data files</param> /// <param name="outputPlugin">Plugin for output file</param> /// <param name="retryPasses">How many times to retry</param> /// <param name="force">Force to continue dump whenever possible</param> /// <param name="dumpRaw">Dump long or scrambled sectors</param> /// <param name="persistent">Store whatever data the drive returned on error</param> /// <param name="stopOnError">Stop dump on first error</param> /// <param name="resume">Information for dump resuming</param> /// <param name="dumpLog">Dump logger</param> /// <param name="encoding">Encoding to use when analyzing dump</param> /// <param name="dumpLeadIn">Try to read and dump as much Lead-in as possible</param> /// <param name="outputPath">Path to output file</param> /// <param name="formatOptions">Formats to pass to output file plugin</param> /// <exception cref="ArgumentException">If you asked to dump long sectors from a SCSI Streaming device</exception> public static void Dump(Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref Resume resume, ref DumpLog dumpLog, bool dumpLeadIn, Encoding encoding, string outputPrefix, string outputPath, Dictionary <string, string> formatOptions, CICMMetadataType preSidecar, uint skip, bool nometadata, bool notrim) { MediaType dskType = MediaType.Unknown; int resets = 0; if (dev.IsRemovable) { deviceGotReset: bool sense = dev.ScsiTestUnitReady(out byte[] senseBuf, dev.Timeout, out _); if (sense) { FixedSense?decSense = Sense.DecodeFixed(senseBuf); if (decSense.HasValue) { 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); // 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) { DicConsole.WriteLine("\rWaiting for drive to become ready"); Thread.Sleep(2000); sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _); if (!sense) { break; } decSense = Sense.DecodeFixed(senseBuf); if (decSense.HasValue) { 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) { DicConsole.ErrorWriteLine("Please insert media in drive"); return; } } else if (decSense.Value.ASC == 0x04 && decSense.Value.ASCQ == 0x01) { int leftRetries = 10; while (leftRetries > 0) { DicConsole.WriteLine("\rWaiting for drive to become ready"); Thread.Sleep(2000); sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _); if (!sense) { break; } decSense = Sense.DecodeFixed(senseBuf); if (decSense.HasValue) { 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) { DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}", Sense.PrettifySense(senseBuf)); return; } } /*else if (decSense.Value.ASC == 0x29 && decSense.Value.ASCQ == 0x00) * { * if (!deviceReset) * { * deviceReset = true; * DicConsole.ErrorWriteLine("Device did reset, retrying..."); * goto retryTestReady; * } * * DicConsole.ErrorWriteLine("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) { DicConsole.WriteLine("\rWaiting for drive to become ready"); Thread.Sleep(2000); sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _); if (!sense) { break; } decSense = Sense.DecodeFixed(senseBuf); if (decSense.HasValue) { 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) { DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}", Sense.PrettifySense(senseBuf)); return; } } else { DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}", Sense.PrettifySense(senseBuf)); return; } } else { DicConsole.ErrorWriteLine("Unknown testing unit was ready."); return; } } } switch (dev.ScsiType) { case PeripheralDeviceTypes.SequentialAccess: if (dumpRaw) { throw new ArgumentException("Tapes cannot be dumped raw."); } Ssc.Dump(dev, outputPrefix, devicePath, ref resume, ref dumpLog, preSidecar); return; case PeripheralDeviceTypes.MultiMediaDevice: Mmc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError, ref dskType, ref resume, ref dumpLog, dumpLeadIn, encoding, outputPrefix, outputPath, formatOptions, preSidecar, skip, nometadata, notrim); return; default: Sbc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError, null, ref dskType, false, ref resume, ref dumpLog, encoding, outputPrefix, outputPath, formatOptions, preSidecar, skip, nometadata, notrim); break; } }
public TestedMedia ReportScsiMedia() { TestedMedia mediaTest = new TestedMedia(); DicConsole.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]); } DicConsole.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; DicConsole.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); if (debug) { mediaTest.ModeSense10Data = buffer; } } DicConsole.WriteLine("Querying SCSI MODE SENSE..."); sense = dev.ModeSense(out buffer, out senseBuffer, dev.Timeout, out _); if (!sense && !dev.Error) { if (!decMode.HasValue) { decMode = Modes.DecodeMode6(buffer, dev.ScsiType); } if (debug) { mediaTest.ModeSense6Data = buffer; } } if (decMode.HasValue) { mediaTest.MediumType = (byte)decMode.Value.Header.MediumType; if (decMode.Value.Header.BlockDescriptors != null && decMode.Value.Header.BlockDescriptors.Length > 0) { mediaTest.Density = (byte)decMode.Value.Header.BlockDescriptors[0].Density; } } DicConsole.WriteLine("Trying SCSI READ (6)..."); mediaTest.SupportsRead6 = !dev.Read6(out buffer, out senseBuffer, 0, mediaTest.BlockSize ?? 512, dev.Timeout, out _); DicConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead6); if (debug) { mediaTest.Read6Data = buffer; } DicConsole.WriteLine("Trying SCSI READ (10)..."); mediaTest.SupportsRead10 = !dev.Read10(out buffer, out senseBuffer, 0, false, true, false, false, 0, mediaTest.BlockSize ?? 512, 0, 1, dev.Timeout, out _); DicConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead10); if (debug) { mediaTest.Read10Data = buffer; } DicConsole.WriteLine("Trying SCSI READ (12)..."); mediaTest.SupportsRead12 = !dev.Read12(out buffer, out senseBuffer, 0, false, true, false, false, 0, mediaTest.BlockSize ?? 512, 0, 1, false, dev.Timeout, out _); DicConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead12); if (debug) { mediaTest.Read12Data = buffer; } DicConsole.WriteLine("Trying SCSI READ (16)..."); mediaTest.SupportsRead16 = !dev.Read16(out buffer, out senseBuffer, 0, false, true, false, 0, mediaTest.BlockSize ?? 512, 0, 1, false, dev.Timeout, out _); DicConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead16); if (debug) { mediaTest.Read16Data = buffer; } mediaTest.LongBlockSize = mediaTest.BlockSize; DicConsole.WriteLine("Trying SCSI READ LONG (10)..."); sense = dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 0xFFFF, dev.Timeout, out _); if (sense && !dev.Error) { FixedSense?decSense = Sense.DecodeFixed(senseBuffer); if (decSense.HasValue) { if (decSense.Value.SenseKey == SenseKeys.IllegalRequest && decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) { mediaTest.SupportsReadLong = true; if (decSense.Value.InformationValid && decSense.Value.ILI) { mediaTest.LongBlockSize = 0xFFFF - (decSense.Value.Information & 0xFFFF); } } } } if (mediaTest.SupportsReadLong == true && mediaTest.LongBlockSize == mediaTest.BlockSize) { if (mediaTest.BlockSize == 512) { foreach (int i in new[] { // Long sector sizes for floppies 514, // Long sector sizes for SuperDisk 536, 558, // Long sector sizes for 512-byte magneto-opticals 600, 610, 630 }) { ushort testSize = (ushort)i; sense = dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, testSize, dev.Timeout, out _); if (sense || dev.Error) { continue; } mediaTest.SupportsReadLong = true; mediaTest.LongBlockSize = testSize; break; } } else if (mediaTest.BlockSize == 1024) { foreach (int i in new[] { // Long sector sizes for floppies 1026, // Long sector sizes for 1024-byte magneto-opticals 1200 }) { ushort testSize = (ushort)i; sense = dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, (ushort)i, dev.Timeout, out _); if (sense || dev.Error) { continue; } mediaTest.SupportsReadLong = true; mediaTest.LongBlockSize = testSize; break; } } else if (mediaTest.BlockSize == 2048) { sense = dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 2380, dev.Timeout, out _); if (!sense && !dev.Error) { mediaTest.SupportsReadLong = true; mediaTest.LongBlockSize = 2380; } } else if (mediaTest.BlockSize == 4096) { sense = dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 4760, dev.Timeout, out _); if (!sense && !dev.Error) { mediaTest.SupportsReadLong = true; mediaTest.LongBlockSize = 4760; } } else if (mediaTest.BlockSize == 8192) { sense = dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 9424, dev.Timeout, out _); if (!sense && !dev.Error) { mediaTest.SupportsReadLong = true; mediaTest.LongBlockSize = 9424; } } } DicConsole.WriteLine("Trying SCSI READ MEDIA SERIAL NUMBER..."); mediaTest.CanReadMediaSerial = !dev.ReadMediaSerialNumber(out buffer, out senseBuffer, dev.Timeout, out _); return(mediaTest); }
bool ScsiReadBlocks(out byte[] buffer, ulong block, uint count, out double duration, out bool recoveredError, out bool blankCheck) { bool sense; byte[] senseBuf; buffer = null; duration = 0; recoveredError = false; blankCheck = false; if (CanReadRaw) { if (_readLong16) { sense = _dev.ReadLong16(out buffer, out senseBuf, false, block, LongBlockSize, _timeout, out duration); } else if (_readLong10) { sense = _dev.ReadLong10(out buffer, out senseBuf, false, false, (uint)block, (ushort)LongBlockSize, _timeout, out duration); } else if (_syqReadLong10) { sense = _dev.SyQuestReadLong10(out buffer, out senseBuf, (uint)block, LongBlockSize, _timeout, out duration); } else if (_syqReadLong6) { sense = _dev.SyQuestReadLong6(out buffer, out senseBuf, (uint)block, LongBlockSize, _timeout, out duration); } else if (_hldtstReadRaw) { sense = _dev.HlDtStReadRawDvd(out buffer, out senseBuf, (uint)block, LongBlockSize, _timeout, out duration); } else if (_plextorReadRaw) { sense = _dev.PlextorReadRawDvd(out buffer, out senseBuf, (uint)block, LongBlockSize, _timeout, out duration); } else { return(true); } } else { if (_read6) { sense = _dev.Read6(out buffer, out senseBuf, (uint)block, LogicalBlockSize, (byte)count, _timeout, out duration); } else if (_read10) { sense = _dev.Read10(out buffer, out senseBuf, 0, false, false, false, false, (uint)block, LogicalBlockSize, 0, (ushort)count, _timeout, out duration); } else if (_read12) { sense = _dev.Read12(out buffer, out senseBuf, 0, false, false, false, false, (uint)block, LogicalBlockSize, 0, count, false, _timeout, out duration); } else if (_read16) { sense = _dev.Read16(out buffer, out senseBuf, 0, false, false, false, block, LogicalBlockSize, 0, count, false, _timeout, out duration); } else { return(true); } } if (sense || _dev.Error) { _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, senseBuf); } if (!sense && !_dev.Error) { return(false); } recoveredError = Sense.DecodeFixed(senseBuf)?.SenseKey == SenseKeys.RecoveredError || Sense.DecodeDescriptor(senseBuf)?.SenseKey == SenseKeys.RecoveredError; blankCheck = Sense.DecodeFixed(senseBuf)?.SenseKey == SenseKeys.BlankCheck || Sense.DecodeDescriptor(senseBuf)?.SenseKey == SenseKeys.BlankCheck; AaruConsole.DebugWriteLine("SCSI Reader", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); return(sense); }
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 _); FixedSense?decSense; if (testSense && !_dev.Error) { decSense = Sense.DecodeFixed(senseBuf); if (decSense?.SenseKey == SenseKeys.IllegalRequest && decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) { CanReadRaw = true; if (decSense.Value.InformationValid && decSense.Value.ILI) { LongBlockSize = 0xFFFF - (decSense.Value.Information & 0xFFFF); _readLong10 = !_dev.ReadLong10(out _, out senseBuf, false, false, 0, (ushort)LongBlockSize, _timeout, out _); } } } if (CanReadRaw && LongBlockSize == LogicalBlockSize) { switch (LogicalBlockSize) { case 512: { foreach (ushort testSize in new ushort[] { // Long sector sizes for floppies 514, // Long sector sizes for SuperDisk 536, 558, // Long sector sizes for 512-byte magneto-opticals 600, 610, 630 }) { testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, testSize, _timeout, out _); if (!testSense && !_dev.Error) { _readLong16 = true; LongBlockSize = testSize; CanReadRaw = true; break; } testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, testSize, _timeout, out _); if (testSense || _dev.Error) { continue; } _readLong10 = true; LongBlockSize = testSize; CanReadRaw = true; break; } break; } case 1024: { foreach (ushort testSize in new ushort[] { // Long sector sizes for floppies 1026, // Long sector sizes for 1024-byte magneto-opticals 1200 }) { testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, testSize, _timeout, out _); if (!testSense && !_dev.Error) { _readLong16 = true; LongBlockSize = testSize; CanReadRaw = true; break; } testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, testSize, _timeout, out _); if (testSense || _dev.Error) { continue; } _readLong10 = true; LongBlockSize = testSize; CanReadRaw = true; break; } break; } case 2048: { testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, 2380, _timeout, out _); if (!testSense && !_dev.Error) { _readLong16 = true; LongBlockSize = 2380; CanReadRaw = true; } else { testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 2380, _timeout, out _); if (!testSense && !_dev.Error) { _readLong10 = true; LongBlockSize = 2380; CanReadRaw = true; } } break; } case 4096: { testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, 4760, _timeout, out _); if (!testSense && !_dev.Error) { _readLong16 = true; LongBlockSize = 4760; CanReadRaw = true; } else { testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 4760, _timeout, out _); if (!testSense && !_dev.Error) { _readLong10 = true; LongBlockSize = 4760; CanReadRaw = true; } } break; } case 8192: { testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, 9424, _timeout, out _); if (!testSense && !_dev.Error) { _readLong16 = true; LongBlockSize = 9424; CanReadRaw = true; } else { testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 9424, _timeout, out _); if (!testSense && !_dev.Error) { _readLong10 = true; LongBlockSize = 9424; CanReadRaw = true; } } break; } } } if (!CanReadRaw && _dev.Manufacturer == "SYQUEST") { testSense = _dev.SyQuestReadLong10(out _, out senseBuf, 0, 0xFFFF, _timeout, out _); if (testSense) { decSense = Sense.DecodeFixed(senseBuf); if (decSense.HasValue) { if (decSense.Value.SenseKey == SenseKeys.IllegalRequest && decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) { CanReadRaw = true; if (decSense.Value.InformationValid && decSense.Value.ILI) { LongBlockSize = 0xFFFF - (decSense.Value.Information & 0xFFFF); _syqReadLong10 = !_dev.SyQuestReadLong10(out _, out senseBuf, 0, LongBlockSize, _timeout, out _); } } else { testSense = _dev.SyQuestReadLong6(out _, out senseBuf, 0, 0xFFFF, _timeout, out _); if (testSense) { decSense = Sense.DecodeFixed(senseBuf); if (decSense?.SenseKey == SenseKeys.IllegalRequest && decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) { CanReadRaw = true; if (decSense.Value.InformationValid && decSense.Value.ILI) { LongBlockSize = 0xFFFF - (decSense.Value.Information & 0xFFFF); _syqReadLong6 = !_dev.SyQuestReadLong6(out _, out senseBuf, 0, LongBlockSize, _timeout, out _); } } } } } } if (!CanReadRaw && LogicalBlockSize == 256) { testSense = _dev.SyQuestReadLong6(out _, out senseBuf, 0, 262, _timeout, out _); if (!testSense && !_dev.Error) { _syqReadLong6 = true; LongBlockSize = 262; CanReadRaw = true; } } } } else { switch (_dev.Manufacturer) { case "HL-DT-ST": _hldtstReadRaw = !_dev.HlDtStReadRawDvd(out _, out senseBuf, 0, 1, _timeout, out _); break; case "PLEXTOR": _plextorReadRaw = !_dev.PlextorReadRawDvd(out _, out senseBuf, 0, 1, _timeout, out _); break; } if (_hldtstReadRaw || _plextorReadRaw) { CanReadRaw = true; LongBlockSize = 2064; } // READ LONG (10) for some DVD drives if (!CanReadRaw && _dev.Manufacturer == "MATSHITA") { testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 37856, _timeout, out _); if (!testSense && !_dev.Error) { _readLongDvd = true; LongBlockSize = 37856; CanReadRaw = true; } } } } if (CanReadRaw) { if (_readLong16) { AaruConsole.WriteLine("Using SCSI READ LONG (16) command."); } else if (_readLong10 || _readLongDvd) { AaruConsole.WriteLine("Using SCSI READ LONG (10) command."); } else if (_syqReadLong10) { AaruConsole.WriteLine("Using SyQuest READ LONG (10) command."); } else if (_syqReadLong6) { AaruConsole.WriteLine("Using SyQuest READ LONG (6) command."); } else if (_hldtstReadRaw) { AaruConsole.WriteLine("Using HL-DT-ST raw DVD reading."); } else if (_plextorReadRaw) { AaruConsole.WriteLine("Using Plextor raw DVD reading."); } } else if (_read6) { AaruConsole.WriteLine("Using SCSI READ (6) command."); } else if (_read10) { AaruConsole.WriteLine("Using SCSI READ (10) command."); } else if (_read12) { AaruConsole.WriteLine("Using SCSI READ (12) command."); } else if (_read16) { AaruConsole.WriteLine("Using SCSI READ (16) command."); } return(false); }
public static ScanResults Scan(string mhddLogPath, string ibgLogPath, string devicePath, Device dev) { ScanResults results = new ScanResults(); bool aborted; MhddLog mhddLog; IbgLog ibgLog; byte[] senseBuf; bool sense = false; results.Blocks = 0; uint blockSize = 0; ushort currentProfile = 0x0001; if (dev.IsRemovable) { sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _); if (sense) { FixedSense?decSense = Sense.DecodeFixed(senseBuf); if (decSense.HasValue) { if (decSense.Value.ASC == 0x3A) { int leftRetries = 5; while (leftRetries > 0) { DicConsole.WriteLine("\rWaiting for drive to become ready"); Thread.Sleep(2000); sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _); if (!sense) { break; } leftRetries--; } if (sense) { DicConsole.ErrorWriteLine("Please insert media in drive"); return(results); } } else if (decSense.Value.ASC == 0x04 && decSense.Value.ASCQ == 0x01) { int leftRetries = 10; while (leftRetries > 0) { DicConsole.WriteLine("\rWaiting for drive to become ready"); Thread.Sleep(2000); sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _); if (!sense) { break; } leftRetries--; } if (sense) { DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}", Sense.PrettifySense(senseBuf)); return(results); } } // These should be trapped by the OS but seems in some cases they're not else if (decSense.Value.ASC == 0x28) { int leftRetries = 10; while (leftRetries > 0) { DicConsole.WriteLine("\rWaiting for drive to become ready"); Thread.Sleep(2000); sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _); if (!sense) { break; } leftRetries--; } if (sense) { DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}", Sense.PrettifySense(senseBuf)); return(results); } } else { DicConsole.ErrorWriteLine("Error testing unit was ready:\n{0}", Sense.PrettifySense(senseBuf)); return(results); } } else { DicConsole.ErrorWriteLine("Unknown testing unit was ready."); return(results); } } } Reader scsiReader = null; switch (dev.ScsiType) { case PeripheralDeviceTypes.DirectAccess: case PeripheralDeviceTypes.MultiMediaDevice: case PeripheralDeviceTypes.OCRWDevice: case PeripheralDeviceTypes.OpticalDevice: case PeripheralDeviceTypes.SimplifiedDevice: case PeripheralDeviceTypes.WriteOnceDevice: scsiReader = new Reader(dev, dev.Timeout, null); results.Blocks = scsiReader.GetDeviceBlocks(); if (scsiReader.FindReadCommand()) { DicConsole.ErrorWriteLine("Unable to read medium."); return(results); } blockSize = scsiReader.LogicalBlockSize; if (results.Blocks != 0 && blockSize != 0) { results.Blocks++; DicConsole.WriteLine("Media has {0} blocks of {1} bytes/each. (for a total of {2} bytes)", results.Blocks, blockSize, results.Blocks * (ulong)blockSize); } break; case PeripheralDeviceTypes.SequentialAccess: DicConsole.WriteLine("Scanning will never be supported on SCSI Streaming Devices."); DicConsole.WriteLine("It has no sense to do it, and it will put too much strain on the tape."); return(results); } if (results.Blocks == 0) { DicConsole.ErrorWriteLine("Unable to read medium or empty medium present..."); return(results); } bool compactDisc = true; FullTOC.CDFullTOC?toc = null; if (dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) { sense = dev.GetConfiguration(out byte[] cmdBuf, out senseBuf, 0, MmcGetConfigurationRt.Current, dev.Timeout, out _); if (!sense) { Features.SeparatedFeatures ftr = Features.Separate(cmdBuf); currentProfile = ftr.CurrentProfile; switch (ftr.CurrentProfile) { case 0x0005: case 0x0008: case 0x0009: case 0x000A: case 0x0020: case 0x0021: case 0x0022: break; default: compactDisc = false; break; } } if (compactDisc) { currentProfile = 0x0008; // We discarded all discs that falsify a TOC before requesting a real TOC // No TOC, no CD (or an empty one) bool tocSense = dev.ReadRawToc(out cmdBuf, out senseBuf, 1, dev.Timeout, out _); if (!tocSense) { toc = FullTOC.Decode(cmdBuf); } } } else { compactDisc = false; } uint blocksToRead = 64; results.A = 0; // <3ms results.B = 0; // >=3ms, <10ms results.C = 0; // >=10ms, <50ms results.D = 0; // >=50ms, <150ms results.E = 0; // >=150ms, <500ms results.F = 0; // >=500ms results.Errored = 0; DateTime start; DateTime end; results.ProcessingTime = 0; results.TotalTime = 0; double currentSpeed = 0; results.MaxSpeed = double.MinValue; results.MinSpeed = double.MaxValue; results.UnreadableSectors = new List <ulong>(); aborted = false; System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true; if (compactDisc) { if (toc == null) { DicConsole.ErrorWriteLine("Error trying to decode TOC..."); return(results); } bool readcd = !dev.ReadCd(out _, out senseBuf, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if (readcd) { DicConsole.WriteLine("Using MMC READ CD command."); } start = DateTime.UtcNow; while (true) { if (readcd) { sense = dev.ReadCd(out _, out senseBuf, 0, 2352, blocksToRead, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if (dev.Error) { blocksToRead /= 2; } } if (!dev.Error || blocksToRead == 1) { break; } } if (dev.Error) { DicConsole.ErrorWriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError); return(results); } DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead); mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead); ibgLog = new IbgLog(ibgLogPath, currentProfile); for (ulong i = 0; i < results.Blocks; i += blocksToRead) { if (aborted) { break; } double cmdDuration = 0; if (results.Blocks - i < blocksToRead) { blocksToRead = (uint)(results.Blocks - i); } #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator if (currentSpeed > results.MaxSpeed && currentSpeed != 0) { results.MaxSpeed = currentSpeed; } if (currentSpeed < results.MinSpeed && currentSpeed != 0) { results.MinSpeed = currentSpeed; } #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, results.Blocks, currentSpeed); if (readcd) { sense = dev.ReadCd(out _, out senseBuf, (uint)i, 2352, blocksToRead, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out cmdDuration); results.ProcessingTime += cmdDuration; } if (!sense) { if (cmdDuration >= 500) { results.F += blocksToRead; } else if (cmdDuration >= 150) { results.E += blocksToRead; } else if (cmdDuration >= 50) { results.D += blocksToRead; } else if (cmdDuration >= 10) { results.C += blocksToRead; } else if (cmdDuration >= 3) { results.B += blocksToRead; } else { results.A += blocksToRead; } mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); } else { DicConsole.DebugWriteLine("Media-Scan", "READ CD error:\n{0}", Sense.PrettifySense(senseBuf)); FixedSense?senseDecoded = Sense.DecodeFixed(senseBuf); if (senseDecoded.HasValue) { // TODO: This error happens when changing from track type afaik. Need to solve that more cleanly // LOGICAL BLOCK ADDRESS OUT OF RANGE if ((senseDecoded.Value.ASC != 0x21 || senseDecoded.Value.ASCQ != 0x00) && // ILLEGAL MODE FOR THIS TRACK (requesting sectors as-is, this is a firmware misconception when audio sectors // are in a track where subchannel indicates data) (senseDecoded.Value.ASC != 0x64 || senseDecoded.Value.ASCQ != 0x00)) { results.Errored += blocksToRead; for (ulong b = i; b < i + blocksToRead; b++) { results.UnreadableSectors.Add(b); } mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(i, 0); } } else { results.Errored += blocksToRead; for (ulong b = i; b < i + blocksToRead; b++) { results.UnreadableSectors.Add(b); } mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(i, 0); } } double newSpeed = (double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000); if (!double.IsInfinity(newSpeed)) { currentSpeed = newSpeed; } } end = DateTime.UtcNow; DicConsole.WriteLine(); mhddLog.Close(); ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), devicePath); } else { start = DateTime.UtcNow; DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead); mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead); ibgLog = new IbgLog(ibgLogPath, currentProfile); for (ulong i = 0; i < results.Blocks; i += blocksToRead) { if (aborted) { break; } if (results.Blocks - i < blocksToRead) { blocksToRead = (uint)(results.Blocks - i); } #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator if (currentSpeed > results.MaxSpeed && currentSpeed != 0) { results.MaxSpeed = currentSpeed; } if (currentSpeed < results.MinSpeed && currentSpeed != 0) { results.MinSpeed = currentSpeed; } #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, results.Blocks, currentSpeed); sense = scsiReader.ReadBlocks(out _, i, blocksToRead, out double cmdDuration); results.ProcessingTime += cmdDuration; if (!sense && !dev.Error) { if (cmdDuration >= 500) { results.F += blocksToRead; } else if (cmdDuration >= 150) { results.E += blocksToRead; } else if (cmdDuration >= 50) { results.D += blocksToRead; } else if (cmdDuration >= 10) { results.C += blocksToRead; } else if (cmdDuration >= 3) { results.B += blocksToRead; } else { results.A += blocksToRead; } mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); } // TODO: Separate errors on kind of errors. else { results.Errored += blocksToRead; for (ulong b = i; b < i + blocksToRead; b++) { results.UnreadableSectors.Add(b); } mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(i, 0); } double newSpeed = (double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000); if (!double.IsInfinity(newSpeed)) { currentSpeed = newSpeed; } } end = DateTime.UtcNow; DicConsole.WriteLine(); mhddLog.Close(); ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), devicePath); } results.SeekMax = double.MinValue; results.SeekMin = double.MaxValue; results.SeekTotal = 0; const int SEEK_TIMES = 1000; Random rnd = new Random(); for (int i = 0; i < SEEK_TIMES; i++) { if (aborted) { break; } uint seekPos = (uint)rnd.Next((int)results.Blocks); DicConsole.Write("\rSeeking to sector {0}...\t\t", seekPos); double seekCur; if (scsiReader.CanSeek) { scsiReader.Seek(seekPos, out seekCur); } else { scsiReader.ReadBlock(out _, seekPos, out seekCur); } #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator if (seekCur > results.SeekMax && seekCur != 0) { results.SeekMax = seekCur; } if (seekCur < results.SeekMin && seekCur != 0) { results.SeekMin = seekCur; } #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator results.SeekTotal += seekCur; GC.Collect(); } DicConsole.WriteLine(); results.ProcessingTime /= 1000; results.TotalTime = (end - start).TotalSeconds; results.AvgSpeed = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime; results.SeekTimes = SEEK_TIMES; return(results); }
ScanResults Scsi() { var results = new ScanResults(); MhddLog mhddLog; IbgLog ibgLog; byte[] senseBuf; bool sense = false; results.Blocks = 0; uint blockSize = 0; ushort currentProfile = 0x0001; if (_dev.IsRemovable) { sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); if (sense) { InitProgress?.Invoke(); FixedSense?decSense = Sense.DecodeFixed(senseBuf); if (decSense.HasValue) { if (decSense.Value.ASC == 0x3A) { int leftRetries = 5; while (leftRetries > 0) { PulseProgress?.Invoke("Waiting for drive to become ready"); Thread.Sleep(2000); sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); if (!sense) { break; } leftRetries--; } if (sense) { StoppingErrorMessage?.Invoke("Please insert media in drive"); return(results); } } else if (decSense.Value.ASC == 0x04 && decSense.Value.ASCQ == 0x01) { int leftRetries = 10; while (leftRetries > 0) { PulseProgress?.Invoke("Waiting for drive to become ready"); Thread.Sleep(2000); sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); if (!sense) { break; } leftRetries--; } if (sense) { StoppingErrorMessage?. Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); return(results); } } // These should be trapped by the OS but seems in some cases they're not else if (decSense.Value.ASC == 0x28) { int leftRetries = 10; while (leftRetries > 0) { PulseProgress?.Invoke("Waiting for drive to become ready"); Thread.Sleep(2000); sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); if (!sense) { break; } leftRetries--; } if (sense) { StoppingErrorMessage?. Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); return(results); } } else { StoppingErrorMessage?. Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); return(results); } } else { StoppingErrorMessage?.Invoke("Unknown testing unit was ready."); return(results); } EndProgress?.Invoke(); } } Reader scsiReader = null; switch (_dev.ScsiType) { case PeripheralDeviceTypes.DirectAccess: case PeripheralDeviceTypes.MultiMediaDevice: case PeripheralDeviceTypes.OCRWDevice: case PeripheralDeviceTypes.OpticalDevice: case PeripheralDeviceTypes.SimplifiedDevice: case PeripheralDeviceTypes.WriteOnceDevice: scsiReader = new Reader(_dev, _dev.Timeout, null, null); results.Blocks = scsiReader.GetDeviceBlocks(); if (scsiReader.FindReadCommand()) { StoppingErrorMessage?.Invoke("Unable to read medium."); return(results); } blockSize = scsiReader.LogicalBlockSize; if (results.Blocks != 0 && blockSize != 0) { results.Blocks++; ulong totalSize = results.Blocks * blockSize; if (totalSize > 1099511627776) { UpdateStatus?. Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"); } else if (totalSize > 1073741824) { UpdateStatus?. Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"); } else if (totalSize > 1048576) { UpdateStatus?. Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"); } else if (totalSize > 1024) { UpdateStatus?. Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"); } else { UpdateStatus?. Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)"); } } break; case PeripheralDeviceTypes.SequentialAccess: StoppingErrorMessage?.Invoke("Scanning will never be supported on SCSI Streaming Devices." + Environment.NewLine + "It has no sense to do it, and it will put too much strain on the tape."); return(results); } if (results.Blocks == 0) { StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present..."); return(results); } bool compactDisc = true; FullTOC.CDFullTOC?toc = null; if (_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) { sense = _dev.GetConfiguration(out byte[] cmdBuf, out senseBuf, 0, MmcGetConfigurationRt.Current, _dev.Timeout, out _); if (!sense) { Features.SeparatedFeatures ftr = Features.Separate(cmdBuf); currentProfile = ftr.CurrentProfile; switch (ftr.CurrentProfile) { case 0x0005: case 0x0008: case 0x0009: case 0x000A: case 0x0020: case 0x0021: case 0x0022: break; default: compactDisc = false; break; } } if (compactDisc) { currentProfile = 0x0008; // We discarded all discs that falsify a TOC before requesting a real TOC // No TOC, no CD (or an empty one) bool tocSense = _dev.ReadRawToc(out cmdBuf, out senseBuf, 1, _dev.Timeout, out _); if (!tocSense) { toc = FullTOC.Decode(cmdBuf); } } } else { compactDisc = false; } uint blocksToRead = 64; results.A = 0; // <3ms results.B = 0; // >=3ms, <10ms results.C = 0; // >=10ms, <50ms results.D = 0; // >=50ms, <150ms results.E = 0; // >=150ms, <500ms results.F = 0; // >=500ms results.Errored = 0; DateTime start; DateTime end; results.ProcessingTime = 0; results.TotalTime = 0; double currentSpeed = 0; results.MaxSpeed = double.MinValue; results.MinSpeed = double.MaxValue; results.UnreadableSectors = new List <ulong>(); if (compactDisc) { if (toc == null) { StoppingErrorMessage?.Invoke("Error trying to decode TOC..."); return(results); } bool readcd = !_dev.ReadCd(out _, out senseBuf, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, out _); if (readcd) { UpdateStatus?.Invoke("Using MMC READ CD command."); } start = DateTime.UtcNow; while (true) { if (readcd) { sense = _dev.ReadCd(out _, out senseBuf, 0, 2352, blocksToRead, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, out _); if (_dev.Error) { blocksToRead /= 2; } } if (!_dev.Error || blocksToRead == 1) { break; } } if (_dev.Error) { StoppingErrorMessage?. Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); return(results); } UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, currentProfile); mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); ibgLog = new IbgLog(_ibgLogPath, currentProfile); DateTime timeSpeedStart = DateTime.UtcNow; ulong sectorSpeedStart = 0; InitProgress?.Invoke(); for (ulong i = 0; i < results.Blocks; i += blocksToRead) { if (_aborted) { break; } double cmdDuration = 0; if (results.Blocks - i < blocksToRead) { blocksToRead = (uint)(results.Blocks - i); } if (currentSpeed > results.MaxSpeed && currentSpeed > 0) { results.MaxSpeed = currentSpeed; } if (currentSpeed < results.MinSpeed && currentSpeed > 0) { results.MinSpeed = currentSpeed; } UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, (long)results.Blocks); if (readcd) { sense = _dev.ReadCd(out _, out senseBuf, (uint)i, 2352, blocksToRead, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, out cmdDuration); results.ProcessingTime += cmdDuration; } if (!sense) { if (cmdDuration >= 500) { results.F += blocksToRead; } else if (cmdDuration >= 150) { results.E += blocksToRead; } else if (cmdDuration >= 50) { results.D += blocksToRead; } else if (cmdDuration >= 10) { results.C += blocksToRead; } else if (cmdDuration >= 3) { results.B += blocksToRead; } else { results.A += blocksToRead; } ScanTime?.Invoke(i, cmdDuration); mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); } else { AaruConsole.DebugWriteLine("Media-Scan", "READ CD error:\n{0}", Sense.PrettifySense(senseBuf)); FixedSense?senseDecoded = Sense.DecodeFixed(senseBuf); if (senseDecoded.HasValue) { // TODO: This error happens when changing from track type afaik. Need to solve that more cleanly // LOGICAL BLOCK ADDRESS OUT OF RANGE if ((senseDecoded.Value.ASC != 0x21 || senseDecoded.Value.ASCQ != 0x00) && // ILLEGAL MODE FOR THIS TRACK (requesting sectors as-is, this is a firmware misconception when audio sectors // are in a track where subchannel indicates data) (senseDecoded.Value.ASC != 0x64 || senseDecoded.Value.ASCQ != 0x00)) { results.Errored += blocksToRead; for (ulong b = i; b < i + blocksToRead; b++) { results.UnreadableSectors.Add(b); } ScanUnreadable?.Invoke(i); mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(i, 0); } } else { ScanUnreadable?.Invoke(i); results.Errored += blocksToRead; for (ulong b = i; b < i + blocksToRead; b++) { results.UnreadableSectors.Add(b); } mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(i, 0); } } sectorSpeedStart += blocksToRead; double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; if (elapsed < 1) { continue; } currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed); ScanSpeed?.Invoke(i, currentSpeed * 1024); sectorSpeedStart = 0; timeSpeedStart = DateTime.UtcNow; } end = DateTime.UtcNow; EndProgress?.Invoke(); mhddLog.Close(); ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (blockSize * (double)(results.Blocks + 1)) / 1024 / (results.ProcessingTime / 1000), _devicePath); } else { start = DateTime.UtcNow; UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, currentProfile); mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); ibgLog = new IbgLog(_ibgLogPath, currentProfile); DateTime timeSpeedStart = DateTime.UtcNow; ulong sectorSpeedStart = 0; InitProgress?.Invoke(); for (ulong i = 0; i < results.Blocks; i += blocksToRead) { if (_aborted) { break; } if (results.Blocks - i < blocksToRead) { blocksToRead = (uint)(results.Blocks - i); } if (currentSpeed > results.MaxSpeed && currentSpeed > 0) { results.MaxSpeed = currentSpeed; } if (currentSpeed < results.MinSpeed && currentSpeed > 0) { results.MinSpeed = currentSpeed; } UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, (long)results.Blocks); sense = scsiReader.ReadBlocks(out _, i, blocksToRead, out double cmdDuration); results.ProcessingTime += cmdDuration; if (!sense && !_dev.Error) { if (cmdDuration >= 500) { results.F += blocksToRead; } else if (cmdDuration >= 150) { results.E += blocksToRead; } else if (cmdDuration >= 50) { results.D += blocksToRead; } else if (cmdDuration >= 10) { results.C += blocksToRead; } else if (cmdDuration >= 3) { results.B += blocksToRead; } else { results.A += blocksToRead; } ScanTime?.Invoke(i, cmdDuration); mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); } // TODO: Separate errors on kind of errors. else { ScanUnreadable?.Invoke(i); results.Errored += blocksToRead; for (ulong b = i; b < i + blocksToRead; b++) { results.UnreadableSectors.Add(b); } mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(i, 0); } sectorSpeedStart += blocksToRead; double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; if (elapsed < 1) { continue; } currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed); ScanSpeed?.Invoke(i, currentSpeed * 1024); sectorSpeedStart = 0; timeSpeedStart = DateTime.UtcNow; } end = DateTime.UtcNow; EndProgress?.Invoke(); mhddLog.Close(); ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (blockSize * (double)(results.Blocks + 1)) / 1024 / (results.ProcessingTime / 1000), _devicePath); } results.SeekMax = double.MinValue; results.SeekMin = double.MaxValue; results.SeekTotal = 0; const int seekTimes = 1000; var rnd = new Random(); InitProgress?.Invoke(); for (int i = 0; i < seekTimes; i++) { if (_aborted || !_seekTest) { break; } uint seekPos = (uint)rnd.Next((int)results.Blocks); PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t"); double seekCur; if (scsiReader.CanSeek) { scsiReader.Seek(seekPos, out seekCur); } else { scsiReader.ReadBlock(out _, seekPos, out seekCur); } if (seekCur > results.SeekMax && seekCur > 0) { results.SeekMax = seekCur; } if (seekCur < results.SeekMin && seekCur > 0) { results.SeekMin = seekCur; } results.SeekTotal += seekCur; GC.Collect(); } EndProgress?.Invoke(); results.ProcessingTime /= 1000; results.TotalTime = (end - start).TotalSeconds; results.AvgSpeed = (blockSize * (double)(results.Blocks + 1)) / 1048576 / results.ProcessingTime; results.SeekTimes = seekTimes; return(results); }
/// <summary> /// Dumps the tape from a SCSI Streaming device /// </summary> /// <param name="dev">Device</param> /// <param name="devicePath">Path to the device</param> /// <param name="outputPrefix">Prefix for output data files</param> /// <param name="resume">Information for dump resuming</param> /// <param name="dumpLog">Dump logger</param> internal static void Dump(Device dev, string outputPrefix, string devicePath, ref Resume resume, ref DumpLog dumpLog, CICMMetadataType preSidecar) { FixedSense? fxSense; bool aborted; bool sense; ulong blocks = 0; uint blockSize; MediaType dskType = MediaType.Unknown; DateTime start; DateTime end; double totalDuration = 0; double totalChkDuration = 0; double currentSpeed = 0; double maxSpeed = double.MinValue; double minSpeed = double.MaxValue; CICMMetadataType sidecar = preSidecar ?? new CICMMetadataType(); dev.RequestSense(out byte[] senseBuf, dev.Timeout, out double duration); fxSense = Sense.DecodeFixed(senseBuf, out string strSense); if (fxSense.HasValue && fxSense.Value.SenseKey != SenseKeys.NoSense) { dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); DicConsole.ErrorWriteLine("Drive has status error, please correct. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); return; } // Not in BOM/P if (fxSense.HasValue && fxSense.Value.ASC == 0x00 && fxSense.Value.ASCQ != 0x00 && fxSense.Value.ASCQ != 0x04 && fxSense.Value.SenseKey != SenseKeys.IllegalRequest) { dumpLog.WriteLine("Rewinding, please wait..."); DicConsole.Write("Rewinding, please wait..."); // Rewind, let timeout apply dev.Rewind(out senseBuf, dev.Timeout, out duration); // Still rewinding? // TODO: Pause? do { DicConsole.Write("\rRewinding, please wait..."); dev.RequestSense(out senseBuf, dev.Timeout, out duration); fxSense = Sense.DecodeFixed(senseBuf, out strSense); }while(fxSense.HasValue && fxSense.Value.ASC == 0x00 && (fxSense.Value.ASCQ == 0x1A || fxSense.Value.ASCQ != 0x04)); dev.RequestSense(out senseBuf, dev.Timeout, out duration); fxSense = Sense.DecodeFixed(senseBuf, out strSense); // And yet, did not rewind! if (fxSense.HasValue && (fxSense.Value.ASC == 0x00 && fxSense.Value.ASCQ != 0x04 || fxSense.Value.ASC != 0x00)) { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not rewind, please correct. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } DicConsole.WriteLine(); } // Check position sense = dev.ReadPosition(out byte[] cmdBuf, out senseBuf, SscPositionForms.Short, dev.Timeout, out duration); if (sense) { // READ POSITION is mandatory starting SCSI-2, so do not cry if the drive does not recognize the command (SCSI-1 or earlier) // Anyway, <=SCSI-1 tapes do not support partitions fxSense = Sense.DecodeFixed(senseBuf, out strSense); if (fxSense.HasValue && (fxSense.Value.ASC == 0x20 && fxSense.Value.ASCQ != 0x00 || fxSense.Value.ASC != 0x20 && fxSense.Value.SenseKey != SenseKeys.IllegalRequest)) { DicConsole.ErrorWriteLine("Could not get position. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Could not get position. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } else { // Not in partition 0 if (cmdBuf[1] != 0) { DicConsole.Write("Drive not in partition 0. Rewinding, please wait..."); dumpLog.WriteLine("Drive not in partition 0. Rewinding, please wait..."); // Rewind, let timeout apply sense = dev.Locate(out senseBuf, false, 0, 0, dev.Timeout, out duration); if (sense) { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not rewind, please correct. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } // Still rewinding? // TODO: Pause? do { Thread.Sleep(1000); DicConsole.Write("\rRewinding, please wait..."); dev.RequestSense(out senseBuf, dev.Timeout, out duration); fxSense = Sense.DecodeFixed(senseBuf, out strSense); }while(fxSense.HasValue && fxSense.Value.ASC == 0x00 && (fxSense.Value.ASCQ == 0x1A || fxSense.Value.ASCQ == 0x19)); // And yet, did not rewind! if (fxSense.HasValue && (fxSense.Value.ASC == 0x00 && fxSense.Value.ASCQ != 0x04 || fxSense.Value.ASC != 0x00)) { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not rewind, please correct. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } sense = dev.ReadPosition(out cmdBuf, out senseBuf, SscPositionForms.Short, dev.Timeout, out duration); if (sense) { fxSense = Sense.DecodeFixed(senseBuf, out strSense); DicConsole.ErrorWriteLine("Drive could not rewind, please correct. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } // Still not in partition 0!!!? if (cmdBuf[1] != 0) { DicConsole.ErrorWriteLine("Drive could not rewind to partition 0 but no error occurred..."); dumpLog.WriteLine("Drive could not rewind to partition 0 but no error occurred..."); return; } DicConsole.WriteLine(); } } sidecar.BlockMedia = new BlockMediaType[1]; sidecar.BlockMedia[0] = new BlockMediaType { SCSI = new SCSIType() }; byte scsiMediumTypeTape = 0; byte scsiDensityCodeTape = 0; dumpLog.WriteLine("Requesting MODE SENSE (10)."); sense = dev.ModeSense10(out cmdBuf, out senseBuf, false, true, ScsiModeSensePageControl.Current, 0x3F, 0xFF, 5, out duration); if (!sense || dev.Error) { sense = dev.ModeSense10(out cmdBuf, out senseBuf, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out duration); } Modes.DecodedMode?decMode = null; if (!sense && !dev.Error) { if (Modes.DecodeMode10(cmdBuf, dev.ScsiType).HasValue) { decMode = Modes.DecodeMode10(cmdBuf, dev.ScsiType); sidecar.BlockMedia[0].SCSI.ModeSense10 = new DumpType { Image = outputPrefix + ".modesense10.bin", Size = cmdBuf.Length, Checksums = Checksum.GetChecksums(cmdBuf).ToArray() }; DataFile.WriteTo("SCSI Dump", sidecar.BlockMedia[0].SCSI.ModeSense10.Image, cmdBuf); } } dumpLog.WriteLine("Requesting MODE SENSE (6)."); sense = dev.ModeSense6(out cmdBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out duration); if (sense || dev.Error) { sense = dev.ModeSense6(out cmdBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out duration); } if (sense || dev.Error) { sense = dev.ModeSense(out cmdBuf, out senseBuf, 5, out duration); } if (!sense && !dev.Error) { if (Modes.DecodeMode6(cmdBuf, dev.ScsiType).HasValue) { decMode = Modes.DecodeMode6(cmdBuf, dev.ScsiType); sidecar.BlockMedia[0].SCSI.ModeSense = new DumpType { Image = outputPrefix + ".modesense.bin", Size = cmdBuf.Length, Checksums = Checksum.GetChecksums(cmdBuf).ToArray() }; DataFile.WriteTo("SCSI Dump", sidecar.BlockMedia[0].SCSI.ModeSense.Image, cmdBuf); } } // TODO: Check partitions page if (decMode.HasValue) { scsiMediumTypeTape = (byte)decMode.Value.Header.MediumType; if (decMode.Value.Header.BlockDescriptors != null && decMode.Value.Header.BlockDescriptors.Length >= 1) { scsiDensityCodeTape = (byte)decMode.Value.Header.BlockDescriptors[0].Density; } blockSize = decMode.Value.Header.BlockDescriptors[0].BlockLength; dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); } else { blockSize = 1; } if (dskType == MediaType.Unknown) { dskType = MediaTypeFromScsi.Get((byte)dev.ScsiType, dev.Manufacturer, dev.Model, scsiMediumTypeTape, scsiDensityCodeTape, blocks, blockSize); } DicConsole.WriteLine("Media identified as {0}", dskType); dumpLog.WriteLine("SCSI device type: {0}.", dev.ScsiType); dumpLog.WriteLine("SCSI medium type: {0}.", scsiMediumTypeTape); dumpLog.WriteLine("SCSI density type: {0}.", scsiDensityCodeTape); dumpLog.WriteLine("Media identified as {0}.", dskType); bool endOfMedia = false; ulong currentBlock = 0; ulong currentFile = 0; byte currentPartition = 0; byte totalPartitions = 1; // TODO: Handle partitions. ulong currentSize = 0; ulong currentPartitionSize = 0; ulong currentFileSize = 0; bool fixedLen = false; uint transferLen = blockSize; sense = dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, dev.Timeout, out duration); if (sense) { fxSense = Sense.DecodeFixed(senseBuf, out strSense); if (fxSense.HasValue) { if (fxSense.Value.SenseKey == SenseKeys.IllegalRequest) { sense = dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, dev.Timeout, out duration); if (sense) { fxSense = Sense.DecodeFixed(senseBuf, out strSense); if (!fxSense.HasValue || !fxSense.Value.EOM) { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not return back. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not return back. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } fixedLen = true; transferLen = 1; sense = dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, dev.Timeout, out duration); if (sense) { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not read. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not read. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } else { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not read. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not read. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } else { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Cannot read device, don't know why, exiting..."); dumpLog.WriteLine("Cannot read device, don't know why, exiting..."); return; } } sense = dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, dev.Timeout, out duration); if (sense) { fxSense = Sense.DecodeFixed(senseBuf, out strSense); if (!fxSense.HasValue || !fxSense.Value.EOM) { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not return back. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not return back. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } List <TapePartitionType> partitions = new List <TapePartitionType>(); List <TapeFileType> files = new List <TapeFileType>(); DicConsole.WriteLine(); DataFile dumpFile = new DataFile(outputPrefix + ".bin"); Checksum dataChk = new Checksum(); start = DateTime.UtcNow; MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, 1); IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", 0x0008); TapeFileType currentTapeFile = new TapeFileType { Image = new ImageType { format = "BINARY", offset = (long)currentSize, offsetSpecified = true, Value = outputPrefix + ".bin" }, Sequence = (long)currentFile, StartBlock = (long)currentBlock, BlockSize = blockSize }; Checksum fileChk = new Checksum(); TapePartitionType currentTapePartition = new TapePartitionType { Image = new ImageType { format = "BINARY", offset = (long)currentSize, offsetSpecified = true, Value = outputPrefix + ".bin" }, Sequence = currentPartition, StartBlock = (long)currentBlock }; Checksum partitionChk = new Checksum(); aborted = false; System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true; while (currentPartition < totalPartitions) { if (aborted) { dumpLog.WriteLine("Aborted!"); break; } if (endOfMedia) { DicConsole.WriteLine(); DicConsole.WriteLine("Finished partition {0}", currentPartition); dumpLog.WriteLine("Finished partition {0}", currentPartition); currentTapePartition.File = files.ToArray(); currentTapePartition.Checksums = partitionChk.End().ToArray(); currentTapePartition.EndBlock = (long)(currentBlock - 1); currentTapePartition.Size = (long)currentPartitionSize; partitions.Add(currentTapePartition); currentPartition++; if (currentPartition < totalPartitions) { currentFile++; currentTapeFile = new TapeFileType { Image = new ImageType { format = "BINARY", offset = (long)currentSize, offsetSpecified = true, Value = outputPrefix + ".bin" }, Sequence = (long)currentFile, StartBlock = (long)currentBlock, BlockSize = blockSize }; currentFileSize = 0; fileChk = new Checksum(); files = new List <TapeFileType>(); currentTapePartition = new TapePartitionType { Image = new ImageType { format = "BINARY", offset = (long)currentSize, offsetSpecified = true, Value = outputPrefix + ".bin" }, Sequence = currentPartition, StartBlock = (long)currentBlock }; currentPartitionSize = 0; partitionChk = new Checksum(); DicConsole.WriteLine("Seeking to partition {0}", currentPartition); dev.Locate(out senseBuf, false, currentPartition, 0, dev.Timeout, out duration); totalDuration += duration; } continue; } #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator if (currentSpeed > maxSpeed && currentSpeed != 0) { maxSpeed = currentSpeed; } if (currentSpeed < minSpeed && currentSpeed != 0) { minSpeed = currentSpeed; } #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator DicConsole.Write("\rReading block {0} ({1:F3} MiB/sec.)", currentBlock, currentSpeed); sense = dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, dev.Timeout, out duration); totalDuration += duration; if (sense) { fxSense = Sense.DecodeFixed(senseBuf, out strSense); if (fxSense.Value.ASC == 0x00 && fxSense.Value.ASCQ == 0x00 && fxSense.Value.ILI && fxSense.Value.InformationValid) { blockSize = (uint)((int)blockSize - BitConverter.ToInt32(BitConverter.GetBytes(fxSense.Value.Information), 0)); currentTapeFile.BlockSize = blockSize; DicConsole.WriteLine(); DicConsole.WriteLine("Blocksize changed to {0} bytes at block {1}", blockSize, currentBlock); dumpLog.WriteLine("Blocksize changed to {0} bytes at block {1}", blockSize, currentBlock); sense = dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, dev.Timeout, out duration); totalDuration += duration; if (sense) { fxSense = Sense.DecodeFixed(senseBuf, out strSense); DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not go back one block. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpFile.Close(); dumpLog.WriteLine("Drive could not go back one block. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } continue; } switch (fxSense.Value.SenseKey) { case SenseKeys.BlankCheck when currentBlock == 0: DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Cannot dump a blank tape..."); dumpFile.Close(); dumpLog.WriteLine("Cannot dump a blank tape..."); return; // For sure this is an end-of-tape/partition case SenseKeys.BlankCheck when fxSense.Value.ASC == 0x00 && (fxSense.Value.ASCQ == 0x02 || fxSense.Value.ASCQ == 0x05 || fxSense.Value.EOM): // TODO: Detect end of partition endOfMedia = true; dumpLog.WriteLine("Found end-of-tape/partition..."); continue; case SenseKeys.BlankCheck: DicConsole.WriteLine(); DicConsole.WriteLine("Blank block found, end of tape?"); endOfMedia = true; dumpLog.WriteLine("Blank block found, end of tape?..."); continue; } if ((fxSense.Value.SenseKey == SenseKeys.NoSense || fxSense.Value.SenseKey == SenseKeys.RecoveredError) && (fxSense.Value.ASCQ == 0x02 || fxSense.Value.ASCQ == 0x05 || fxSense.Value.EOM)) { // TODO: Detect end of partition endOfMedia = true; dumpLog.WriteLine("Found end-of-tape/partition..."); continue; } if ((fxSense.Value.SenseKey == SenseKeys.NoSense || fxSense.Value.SenseKey == SenseKeys.RecoveredError) && (fxSense.Value.ASCQ == 0x01 || fxSense.Value.Filemark)) { currentTapeFile.Checksums = fileChk.End().ToArray(); currentTapeFile.EndBlock = (long)(currentBlock - 1); currentTapeFile.Size = (long)currentFileSize; files.Add(currentTapeFile); currentFile++; currentTapeFile = new TapeFileType { Image = new ImageType { format = "BINARY", offset = (long)currentSize, offsetSpecified = true, Value = outputPrefix + ".bin" }, Sequence = (long)currentFile, StartBlock = (long)currentBlock, BlockSize = blockSize }; currentFileSize = 0; fileChk = new Checksum(); DicConsole.WriteLine(); DicConsole.WriteLine("Changed to file {0} at block {1}", currentFile, currentBlock); dumpLog.WriteLine("Changed to file {0} at block {1}", currentFile, currentBlock); continue; } // TODO: Add error recovering for tapes fxSense = Sense.DecodeFixed(senseBuf, out strSense); DicConsole.ErrorWriteLine("Drive could not read block. Sense follows..."); DicConsole.ErrorWriteLine("{0} {1}", fxSense.Value.SenseKey, strSense); dumpLog.WriteLine("Drive could not read block. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } mhddLog.Write(currentBlock, duration); ibgLog.Write(currentBlock, currentSpeed * 1024); dumpFile.Write(cmdBuf); DateTime chkStart = DateTime.UtcNow; dataChk.Update(cmdBuf); fileChk.Update(cmdBuf); partitionChk.Update(cmdBuf); DateTime chkEnd = DateTime.UtcNow; double chkDuration = (chkEnd - chkStart).TotalMilliseconds; totalChkDuration += chkDuration; if (currentBlock % 10 == 0) { double newSpeed = blockSize / (double)1048576 / (duration / 1000); if (!double.IsInfinity(newSpeed)) { currentSpeed = newSpeed; } } currentBlock++; currentSize += blockSize; currentFileSize += blockSize; currentPartitionSize += blockSize; } blocks = currentBlock + 1; DicConsole.WriteLine(); end = DateTime.UtcNow; mhddLog.Close(); ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), devicePath); dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); DicConsole.WriteLine("Took a total of {0:F3} seconds ({1:F3} processing commands, {2:F3} checksumming).", (end - start).TotalSeconds, totalDuration / 1000, totalChkDuration / 1000); DicConsole.WriteLine("Avegare speed: {0:F3} MiB/sec.", (double)blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000)); DicConsole.WriteLine("Fastest speed burst: {0:F3} MiB/sec.", maxSpeed); DicConsole.WriteLine("Slowest speed burst: {0:F3} MiB/sec.", minSpeed); sidecar.BlockMedia[0].Checksums = dataChk.End().ToArray(); sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); CommonTypes.Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp, out string xmlDskSubTyp); sidecar.BlockMedia[0].DiskType = xmlDskTyp; sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp; // TODO: Implement device firmware revision sidecar.BlockMedia[0].Image = new ImageType { format = "Raw disk image (sector by sector copy)", Value = outputPrefix + ".bin" }; sidecar.BlockMedia[0].LogicalBlocks = (long)blocks; sidecar.BlockMedia[0].Size = (long)currentSize; sidecar.BlockMedia[0].DumpHardwareArray = new DumpHardwareType[1]; sidecar.BlockMedia[0].DumpHardwareArray[0] = new DumpHardwareType { Extents = new ExtentType[1] }; sidecar.BlockMedia[0].DumpHardwareArray[0].Extents[0] = new ExtentType { Start = 0, End = blocks - 1 }; sidecar.BlockMedia[0].DumpHardwareArray[0].Manufacturer = dev.Manufacturer; sidecar.BlockMedia[0].DumpHardwareArray[0].Model = dev.Model; sidecar.BlockMedia[0].DumpHardwareArray[0].Revision = dev.Revision; sidecar.BlockMedia[0].DumpHardwareArray[0].Serial = dev.Serial; sidecar.BlockMedia[0].DumpHardwareArray[0].Software = Version.GetSoftwareType(); sidecar.BlockMedia[0].TapeInformation = partitions.ToArray(); if (!aborted) { DicConsole.WriteLine("Writing metadata sidecar"); FileStream xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create); XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType)); xmlSer.Serialize(xmlFs, sidecar); xmlFs.Close(); } Statistics.AddMedia(dskType, true); }
// 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) { FixedSense? decSense = Sense.DecodeFixed(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.DecodeFixed(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.DecodeFixed(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.DecodeFixed(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; } }
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; } FixedSense?decodedSense = Sense.DecodeFixed(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; } FixedSense?decodedSense = Sense.DecodeFixed(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; } }