/// <summary> /// Dumps an Xbox Game Disc using a Kreon drive /// </summary> /// <param name="dev">Device</param> /// <param name="devicePath">Path to the device</param> /// <param name="outputPrefix">Prefix for output data files</param> /// <param name="outputPlugin">Plugin for output file</param> /// <param name="retryPasses">How many times to retry</param> /// <param name="force">Force to continue dump whenever possible</param> /// <param name="dumpRaw">Dump raw/long sectors</param> /// <param name="persistent">Store whatever data the drive returned on error</param> /// <param name="stopOnError">Stop dump on first error</param> /// <param name="resume">Information for dump resuming</param> /// <param name="dumpLog">Dump logger</param> /// <param name="encoding">Encoding to use when analyzing dump</param> /// <param name="mediaTags">Media tags as retrieved in MMC layer</param> /// <param name="dskType">Disc type as detected in MMC layer</param> /// <param name="outputPath">Path to output file</param> /// <param name="formatOptions">Formats to pass to output file plugin</param> /// <exception cref="InvalidOperationException"> /// If the provided resume does not correspond with the current in progress /// dump /// </exception> internal static void Dump(Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, Dictionary <MediaTagType, byte[]> mediaTags, ref MediaType dskType, ref Resume resume, ref DumpLog dumpLog, Encoding encoding, string outputPrefix, string outputPath, Dictionary <string, string> formatOptions, CICMMetadataType preSidecar, uint skip, bool nometadata, bool notrim) { bool sense; ulong blocks; const uint BLOCK_SIZE = 2048; uint blocksToRead = 64; DateTime start; DateTime end; double totalDuration = 0; double currentSpeed = 0; double maxSpeed = double.MinValue; double minSpeed = double.MaxValue; bool aborted = false; System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true; if (mediaTags.ContainsKey(MediaTagType.DVD_PFI)) { mediaTags.Remove(MediaTagType.DVD_PFI); } if (mediaTags.ContainsKey(MediaTagType.DVD_DMI)) { mediaTags.Remove(MediaTagType.DVD_DMI); } dumpLog.WriteLine("Reading Xbox Security Sector."); sense = dev.KreonExtractSs(out byte[] ssBuf, out byte[] senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot get Xbox Security Sector, not continuing."); DicConsole.ErrorWriteLine("Cannot get Xbox Security Sector, not continuing."); return; } dumpLog.WriteLine("Decoding Xbox Security Sector."); SS.SecuritySector?xboxSs = SS.Decode(ssBuf); if (!xboxSs.HasValue) { dumpLog.WriteLine("Cannot decode Xbox Security Sector, not continuing."); DicConsole.ErrorWriteLine("Cannot decode Xbox Security Sector, not continuing."); return; } byte[] tmpBuf = new byte[ssBuf.Length - 4]; Array.Copy(ssBuf, 4, tmpBuf, 0, ssBuf.Length - 4); mediaTags.Add(MediaTagType.Xbox_SecuritySector, tmpBuf); ulong l0Video, l1Video, middleZone, gameSize, totalSize, layerBreak; // Get video partition size DicConsole.DebugWriteLine("Dump-media command", "Getting video partition size"); dumpLog.WriteLine("Locking drive."); sense = dev.KreonLock(out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot lock drive, not continuing."); DicConsole.ErrorWriteLine("Cannot lock drive, not continuing."); return; } dumpLog.WriteLine("Getting video partition size."); sense = dev.ReadCapacity(out byte[] readBuffer, out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot get disc capacity."); DicConsole.ErrorWriteLine("Cannot get disc capacity."); return; } totalSize = (ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]); dumpLog.WriteLine("Reading Physical Format Information."); sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); if (sense) { dumpLog.WriteLine("Cannot get PFI."); DicConsole.ErrorWriteLine("Cannot get PFI."); return; } tmpBuf = new byte[readBuffer.Length - 4]; Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); mediaTags.Add(MediaTagType.DVD_PFI, tmpBuf); DicConsole.DebugWriteLine("Dump-media command", "Video partition total size: {0} sectors", totalSize); l0Video = PFI.Decode(readBuffer).Value.Layer0EndPSN - PFI.Decode(readBuffer).Value.DataAreaStartPSN + 1; l1Video = totalSize - l0Video + 1; dumpLog.WriteLine("Reading Disc Manufacturing Information."); sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, MmcDiscStructureFormat.DiscManufacturingInformation, 0, 0, out _); if (sense) { dumpLog.WriteLine("Cannot get DMI."); DicConsole.ErrorWriteLine("Cannot get DMI."); return; } tmpBuf = new byte[readBuffer.Length - 4]; Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); mediaTags.Add(MediaTagType.DVD_DMI, tmpBuf); // Get game partition size DicConsole.DebugWriteLine("Dump-media command", "Getting game partition size"); dumpLog.WriteLine("Unlocking drive (Xtreme)."); sense = dev.KreonUnlockXtreme(out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot unlock drive, not continuing."); DicConsole.ErrorWriteLine("Cannot unlock drive, not continuing."); return; } dumpLog.WriteLine("Getting game partition size."); sense = dev.ReadCapacity(out readBuffer, out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot get disc capacity."); DicConsole.ErrorWriteLine("Cannot get disc capacity."); return; } gameSize = (ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]) + 1; DicConsole.DebugWriteLine("Dump-media command", "Game partition total size: {0} sectors", gameSize); // Get middle zone size DicConsole.DebugWriteLine("Dump-media command", "Getting middle zone size"); dumpLog.WriteLine("Unlocking drive (Wxripper)."); sense = dev.KreonUnlockWxripper(out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot unlock drive, not continuing."); DicConsole.ErrorWriteLine("Cannot unlock drive, not continuing."); return; } dumpLog.WriteLine("Getting disc size."); sense = dev.ReadCapacity(out readBuffer, out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot get disc capacity."); DicConsole.ErrorWriteLine("Cannot get disc capacity."); return; } totalSize = (ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]); dumpLog.WriteLine("Reading Physical Format Information."); sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); if (sense) { dumpLog.WriteLine("Cannot get PFI."); DicConsole.ErrorWriteLine("Cannot get PFI."); return; } DicConsole.DebugWriteLine("Dump-media command", "Unlocked total size: {0} sectors", totalSize); blocks = totalSize + 1; middleZone = totalSize - (PFI.Decode(readBuffer).Value.Layer0EndPSN - PFI.Decode(readBuffer).Value.DataAreaStartPSN + 1) - gameSize + 1; tmpBuf = new byte[readBuffer.Length - 4]; Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); mediaTags.Add(MediaTagType.Xbox_PFI, tmpBuf); dumpLog.WriteLine("Reading Disc Manufacturing Information."); sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, MmcDiscStructureFormat.DiscManufacturingInformation, 0, 0, out _); if (sense) { dumpLog.WriteLine("Cannot get DMI."); DicConsole.ErrorWriteLine("Cannot get DMI."); return; } tmpBuf = new byte[readBuffer.Length - 4]; Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); mediaTags.Add(MediaTagType.Xbox_DMI, tmpBuf); totalSize = l0Video + l1Video + middleZone * 2 + gameSize; layerBreak = l0Video + middleZone + gameSize / 2; DicConsole.WriteLine("Video layer 0 size: {0} sectors", l0Video); DicConsole.WriteLine("Video layer 1 size: {0} sectors", l1Video); DicConsole.WriteLine("Middle zone size: {0} sectors", middleZone); DicConsole.WriteLine("Game data size: {0} sectors", gameSize); DicConsole.WriteLine("Total size: {0} sectors", totalSize); DicConsole.WriteLine("Real layer break: {0}", layerBreak); DicConsole.WriteLine(); dumpLog.WriteLine("Video layer 0 size: {0} sectors", l0Video); dumpLog.WriteLine("Video layer 1 size: {0} sectors", l1Video); dumpLog.WriteLine("Middle zone 0 size: {0} sectors", middleZone); dumpLog.WriteLine("Game data 0 size: {0} sectors", gameSize); dumpLog.WriteLine("Total 0 size: {0} sectors", totalSize); dumpLog.WriteLine("Real layer break: {0}", layerBreak); bool read12 = !dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, 0, BLOCK_SIZE, 0, 1, false, dev.Timeout, out _); if (!read12) { dumpLog.WriteLine("Cannot read medium, aborting scan..."); DicConsole.ErrorWriteLine("Cannot read medium, aborting scan..."); return; } dumpLog.WriteLine("Using SCSI READ (12) command."); DicConsole.WriteLine("Using SCSI READ (12) command."); while (true) { if (read12) { sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, 0, BLOCK_SIZE, 0, blocksToRead, false, dev.Timeout, out _); if (sense || dev.Error) { blocksToRead /= 2; } } if (!dev.Error || blocksToRead == 1) { break; } } if (dev.Error) { dumpLog.WriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError); DicConsole.ErrorWriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError); return; } if (skip < blocksToRead) { skip = blocksToRead; } bool ret = true; foreach (MediaTagType tag in mediaTags.Keys) { if (outputPlugin.SupportedMediaTags.Contains(tag)) { continue; } ret = false; dumpLog.WriteLine($"Output format does not support {tag}."); DicConsole.ErrorWriteLine($"Output format does not support {tag}."); } if (!ret) { dumpLog.WriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not "); DicConsole.ErrorWriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not "); if (!force) { return; } } dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead); DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead); MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, BLOCK_SIZE, blocksToRead); IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", 0x0010); ret = outputPlugin.Create(outputPath, dskType, formatOptions, blocks, BLOCK_SIZE); // Cannot create image if (!ret) { dumpLog.WriteLine("Error creating output image, not continuing."); dumpLog.WriteLine(outputPlugin.ErrorMessage); DicConsole.ErrorWriteLine("Error creating output image, not continuing."); DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage); return; } start = DateTime.UtcNow; double imageWriteDuration = 0; double cmdDuration = 0; uint saveBlocksToRead = blocksToRead; DumpHardwareType currentTry = null; ExtentsULong extents = null; ResumeSupport.Process(true, true, totalSize, dev.Manufacturer, dev.Model, dev.Serial, dev.PlatformId, ref resume, ref currentTry, ref extents); if (currentTry == null || extents == null) { throw new NotImplementedException("Could not process resume file, not continuing..."); } outputPlugin.SetTracks(new List <Track> { new Track { TrackBytesPerSector = (int)BLOCK_SIZE, TrackEndSector = blocks - 1, TrackSequence = 1, TrackRawBytesPerSector = (int)BLOCK_SIZE, TrackSubchannelType = TrackSubchannelType.None, TrackSession = 1, TrackType = TrackType.Data } }); ulong currentSector = resume.NextBlock; if (resume.NextBlock > 0) { dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock); } bool newTrim = false; dumpLog.WriteLine("Reading game partition."); for (int e = 0; e <= 16; e++) { if (aborted) { resume.NextBlock = currentSector; currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } if (currentSector >= blocks) { break; } ulong extentStart, extentEnd; // Extents if (e < 16) { if (xboxSs.Value.Extents[e].StartPSN <= xboxSs.Value.Layer0EndPSN) { extentStart = xboxSs.Value.Extents[e].StartPSN - 0x30000; } else { extentStart = (xboxSs.Value.Layer0EndPSN + 1) * 2 - ((xboxSs.Value.Extents[e].StartPSN ^ 0xFFFFFF) + 1) - 0x30000; } if (xboxSs.Value.Extents[e].EndPSN <= xboxSs.Value.Layer0EndPSN) { extentEnd = xboxSs.Value.Extents[e].EndPSN - 0x30000; } else { extentEnd = (xboxSs.Value.Layer0EndPSN + 1) * 2 - ((xboxSs.Value.Extents[e].EndPSN ^ 0xFFFFFF) + 1) - 0x30000; } } // After last extent else { extentStart = blocks; extentEnd = blocks; } if (currentSector > extentEnd) { continue; } for (ulong i = currentSector; i < extentStart; i += blocksToRead) { saveBlocksToRead = blocksToRead; if (aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } if (extentStart - i < blocksToRead) { blocksToRead = (uint)(extentStart - i); } #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 sector {0} of {1} ({2:F3} MiB/sec.)", i, totalSize, currentSpeed); sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)i, BLOCK_SIZE, 0, blocksToRead, false, dev.Timeout, out cmdDuration); totalDuration += cmdDuration; if (!sense && !dev.Error) { mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); DateTime writeStart = DateTime.Now; outputPlugin.WriteSectors(readBuffer, i, blocksToRead); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; extents.Add(i, blocksToRead, true); } else { // 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; outputPlugin.WriteSectors(new byte[BLOCK_SIZE * 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; string[] senseLines = Sense.PrettifySense(senseBuf).Split(new[] { Environment.NewLine }, StringSplitOptions .RemoveEmptyEntries); foreach (string senseLine in senseLines) { dumpLog.WriteLine(senseLine); } newTrim = true; } double newSpeed = (double)BLOCK_SIZE * blocksToRead / 1048576 / (cmdDuration / 1000); if (!double.IsInfinity(newSpeed)) { currentSpeed = newSpeed; } blocksToRead = saveBlocksToRead; currentSector = i + 1; resume.NextBlock = currentSector; } for (ulong i = extentStart; i <= extentEnd; i += blocksToRead) { saveBlocksToRead = blocksToRead; if (aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } if (extentEnd - i < blocksToRead) { blocksToRead = (uint)(extentEnd - i) + 1; } mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); // Write empty data DateTime writeStart = DateTime.Now; outputPlugin.WriteSectors(new byte[BLOCK_SIZE * blocksToRead], i, blocksToRead); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; blocksToRead = saveBlocksToRead; extents.Add(i, blocksToRead, true); currentSector = i + 1; resume.NextBlock = currentSector; } if (!aborted) { currentSector = extentEnd + 1; } } // Middle Zone D dumpLog.WriteLine("Writing Middle Zone D (empty)."); for (ulong middle = currentSector - blocks - 1; middle < middleZone - 1; middle += blocksToRead) { if (aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } if (middleZone - 1 - middle < blocksToRead) { blocksToRead = (uint)(middleZone - 1 - middle); } DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", middle + currentSector, totalSize, currentSpeed); mhddLog.Write(middle + currentSector, cmdDuration); ibgLog.Write(middle + currentSector, currentSpeed * 1024); // Write empty data DateTime writeStart = DateTime.Now; outputPlugin.WriteSectors(new byte[BLOCK_SIZE * blocksToRead], middle + currentSector, blocksToRead); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; extents.Add(currentSector, blocksToRead, true); currentSector += blocksToRead; resume.NextBlock = currentSector; } blocksToRead = saveBlocksToRead; dumpLog.WriteLine("Locking drive."); sense = dev.KreonLock(out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot lock drive, not continuing."); DicConsole.ErrorWriteLine("Cannot lock drive, not continuing."); return; } sense = dev.ReadCapacity(out readBuffer, out senseBuf, dev.Timeout, out _); if (sense) { DicConsole.ErrorWriteLine("Cannot get disc capacity."); return; } // Video Layer 1 dumpLog.WriteLine("Reading Video Layer 1."); for (ulong l1 = currentSector - blocks - middleZone + l0Video; l1 < l0Video + l1Video; l1 += blocksToRead) { if (aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } if (l0Video + l1Video - l1 < blocksToRead) { blocksToRead = (uint)(l0Video + l1Video - l1); } #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 sector {0} of {1} ({2:F3} MiB/sec.)", currentSector, totalSize, currentSpeed); sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)l1, BLOCK_SIZE, 0, blocksToRead, false, dev.Timeout, out cmdDuration); totalDuration += cmdDuration; if (!sense && !dev.Error) { mhddLog.Write(currentSector, cmdDuration); ibgLog.Write(currentSector, currentSpeed * 1024); DateTime writeStart = DateTime.Now; outputPlugin.WriteSectors(readBuffer, currentSector, blocksToRead); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; extents.Add(currentSector, blocksToRead, true); } else { // TODO: Reset device after X errors if (stopOnError) { return; // TODO: Return more cleanly } // Write empty data DateTime writeStart = DateTime.Now; outputPlugin.WriteSectors(new byte[BLOCK_SIZE * skip], currentSector, skip); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; // TODO: Handle errors in video partition //errored += blocksToRead; //resume.BadBlocks.Add(l1); DicConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); mhddLog.Write(l1, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(l1, 0); dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", skip, l1); l1 += skip - blocksToRead; string[] senseLines = Sense.PrettifySense(senseBuf).Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); foreach (string senseLine in senseLines) { dumpLog.WriteLine(senseLine); } } double newSpeed = (double)BLOCK_SIZE * blocksToRead / 1048576 / (cmdDuration / 1000); if (!double.IsInfinity(newSpeed)) { currentSpeed = newSpeed; } currentSector += blocksToRead; resume.NextBlock = currentSector; } dumpLog.WriteLine("Unlocking drive (Wxripper)."); sense = dev.KreonUnlockWxripper(out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot unlock drive, not continuing."); DicConsole.ErrorWriteLine("Cannot unlock drive, not continuing."); return; } sense = dev.ReadCapacity(out readBuffer, out senseBuf, dev.Timeout, out _); if (sense) { DicConsole.ErrorWriteLine("Cannot get disc capacity."); return; } end = DateTime.UtcNow; DicConsole.WriteLine(); mhddLog.Close(); ibgLog.Close(dev, blocks, BLOCK_SIZE, (end - start).TotalSeconds, currentSpeed * 1024, BLOCK_SIZE * (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)BLOCK_SIZE * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", (double)BLOCK_SIZE * (double)(blocks + 1) / 1024 / imageWriteDuration); #region Trimming if (resume.BadBlocks.Count > 0 && !aborted && !notrim && newTrim) { start = DateTime.UtcNow; dumpLog.WriteLine("Trimming bad sectors"); ulong[] tmpArray = resume.BadBlocks.ToArray(); foreach (ulong badSector in tmpArray) { if (aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } DicConsole.Write("\rTrimming sector {0}", badSector); sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)badSector, BLOCK_SIZE, 0, 1, false, dev.Timeout, out cmdDuration); totalDuration += cmdDuration; if (sense || dev.Error) { continue; } resume.BadBlocks.Remove(badSector); extents.Add(badSector); outputPlugin.WriteSector(readBuffer, badSector); } DicConsole.WriteLine(); end = DateTime.UtcNow; dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds); } #endregion Trimming #region Error handling if (resume.BadBlocks.Count > 0 && !aborted && retryPasses > 0) { List <ulong> tmpList = new List <ulong>(); foreach (ulong ur in resume.BadBlocks) { for (ulong i = ur; i < ur + blocksToRead; i++) { tmpList.Add(i); } } tmpList.Sort(); int pass = 1; bool forward = true; bool runningPersistent = false; resume.BadBlocks = tmpList; Modes.ModePage?currentModePage = null; byte[] md6; byte[] md10; if (persistent) { Modes.ModePage_01_MMC pgMmc; sense = dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, dev.Timeout, out _); if (sense) { sense = dev.ModeSense10(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, dev.Timeout, out _); if (!sense) { Modes.DecodedMode?dcMode10 = Modes.DecodeMode10(readBuffer, PeripheralDeviceTypes.MultiMediaDevice); if (dcMode10.HasValue) { foreach (Modes.ModePage modePage in dcMode10.Value.Pages) { if (modePage.Page == 0x01 && modePage.Subpage == 0x00) { currentModePage = modePage; } } } } } else { Modes.DecodedMode?dcMode6 = Modes.DecodeMode6(readBuffer, PeripheralDeviceTypes.MultiMediaDevice); if (dcMode6.HasValue) { 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 = 0x20, 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 }; Modes.DecodedMode 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 (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) { DicConsole .WriteLine("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; } } repeatRetry: ulong[] tmpArray = resume.BadBlocks.ToArray(); foreach (ulong badSector in tmpArray) { if (aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass, forward ? "forward" : "reverse", runningPersistent ? "recovering partial data, " : ""); sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)badSector, BLOCK_SIZE, 0, 1, false, dev.Timeout, out cmdDuration); totalDuration += cmdDuration; if (!sense && !dev.Error) { resume.BadBlocks.Remove(badSector); extents.Add(badSector); outputPlugin.WriteSector(readBuffer, badSector); dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } else if (runningPersistent) { outputPlugin.WriteSector(readBuffer, badSector); } } if (pass < retryPasses && !aborted && resume.BadBlocks.Count > 0) { pass++; forward = !forward; resume.BadBlocks.Sort(); resume.BadBlocks.Reverse(); goto repeatRetry; } if (runningPersistent && currentModePage.HasValue) { Modes.DecodedMode 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 _); } } DicConsole.WriteLine(); } #endregion Error handling resume.BadBlocks.Sort(); currentTry.Extents = ExtentsConverter.ToMetadata(extents); foreach (KeyValuePair <MediaTagType, byte[]> tag in mediaTags) { ret = outputPlugin.WriteMediaTag(tag.Value, tag.Key); if (ret || force) { continue; } // Cannot write tag to image dumpLog.WriteLine($"Cannot write tag {tag.Key}."); throw new ArgumentException(outputPlugin.ErrorMessage); } resume.BadBlocks.Sort(); foreach (ulong bad in resume.BadBlocks) { dumpLog.WriteLine("Sector {0} could not be read.", bad); } currentTry.Extents = ExtentsConverter.ToMetadata(extents); outputPlugin.SetDumpHardware(resume.Tries); if (preSidecar != null) { outputPlugin.SetCicmMetadata(preSidecar); } dumpLog.WriteLine("Closing output file."); DicConsole.WriteLine("Closing output file."); DateTime closeStart = DateTime.Now; outputPlugin.Close(); DateTime closeEnd = DateTime.Now; dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); if (aborted) { dumpLog.WriteLine("Aborted!"); return; } double totalChkDuration = 0; if (!nometadata) { dumpLog.WriteLine("Creating sidecar."); FiltersList filters = new FiltersList(); IFilter filter = filters.GetFilter(outputPath); IMediaImage inputPlugin = ImageFormat.Detect(filter); if (!inputPlugin.Open(filter)) { throw new ArgumentException("Could not open created image."); } DateTime chkStart = DateTime.UtcNow; CICMMetadataType sidecar = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding); end = DateTime.UtcNow; if (preSidecar != null) { preSidecar.OpticalDisc = sidecar.OpticalDisc; sidecar = preSidecar; } totalChkDuration = (end - chkStart).TotalMilliseconds; dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", (double)BLOCK_SIZE * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); foreach (KeyValuePair <MediaTagType, byte[]> tag in mediaTags) { Mmc.AddMediaTagToSidecar(outputPath, tag, ref sidecar); } List <(ulong start, string type)> filesystems = new List <(ulong start, string type)>(); if (sidecar.OpticalDisc[0].Track != null) { filesystems.AddRange(from xmlTrack in sidecar.OpticalDisc[0].Track where xmlTrack.FileSystemInformation != null from partition in xmlTrack.FileSystemInformation where partition.FileSystems != null from fileSystem in partition.FileSystems select((ulong)partition.StartSector, fileSystem.Type)); } if (filesystems.Count > 0) { foreach (var filesystem in filesystems.Select(o => new { o.start, o.type }).Distinct()) { dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start); } } sidecar.OpticalDisc[0].Layers = new LayersType { type = LayersTypeType.OTP, typeSpecified = true, Sectors = new SectorsType[1] }; sidecar.OpticalDisc[0].Layers.Sectors[0] = new SectorsType { Value = (long)layerBreak }; sidecar.OpticalDisc[0].Sessions = 1; sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp, out string xmlDskSubTyp); sidecar.OpticalDisc[0].DiscType = xmlDskTyp; sidecar.OpticalDisc[0].DiscSubType = xmlDskSubTyp; foreach (KeyValuePair <MediaTagType, byte[]> tag in mediaTags) { if (outputPlugin.SupportedMediaTags.Contains(tag.Key)) { Mmc.AddMediaTagToSidecar(outputPath, tag, ref sidecar); } } 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(); } DicConsole.WriteLine(); DicConsole.WriteLine("Took a total of {0:F3} seconds ({1:F3} processing commands, {2:F3} checksumming, {3:F3} writing, {4:F3} closing).", (end - start).TotalSeconds, totalDuration / 1000, totalChkDuration / 1000, imageWriteDuration, (closeEnd - closeStart).TotalSeconds); DicConsole.WriteLine("Avegare speed: {0:F3} MiB/sec.", (double)BLOCK_SIZE * (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); DicConsole.WriteLine("{0} sectors could not be read.", resume.BadBlocks.Count); DicConsole.WriteLine(); Statistics.AddMedia(dskType, true); }
public ImageInfoViewModel(string imagePath, IFilter filter, IMediaImage imageFormat, Window view) { _imagePath = imagePath; _filter = filter; _imageFormat = imageFormat; _view = view; IAssetLoader assets = AvaloniaLocator.Current.GetService <IAssetLoader>(); MediaTagsList = new ObservableCollection <string>(); SectorTagsList = new ObservableCollection <string>(); Sessions = new ObservableCollection <Session>(); Tracks = new ObservableCollection <Track>(); DumpHardwareList = new ObservableCollection <DumpHardwareModel>(); EntropyCommand = ReactiveCommand.Create(ExecuteEntropyCommand); VerifyCommand = ReactiveCommand.Create(ExecuteVerifyCommand); ChecksumCommand = ReactiveCommand.Create(ExecuteChecksumCommand); ConvertCommand = ReactiveCommand.Create(ExecuteConvertCommand); CreateSidecarCommand = ReactiveCommand.Create(ExecuteCreateSidecarCommand); ViewSectorsCommand = ReactiveCommand.Create(ExecuteViewSectorsCommand); DecodeMediaTagCommand = ReactiveCommand.Create(ExecuteDecodeMediaTagCommand); var genericHddIcon = new Bitmap(assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-harddisk.png"))); var genericOpticalIcon = new Bitmap(assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-optical.png"))); var genericFolderIcon = new Bitmap(assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/inode-directory.png"))); var mediaResource = new Uri($"avares://Aaru.Gui/Assets/Logos/Media/{imageFormat.Info.MediaType}.png"); MediaLogo = assets.Exists(mediaResource) ? new Bitmap(assets.Open(mediaResource)) : imageFormat.Info.XmlMediaType == XmlMediaType.BlockMedia ? genericHddIcon : imageFormat.Info.XmlMediaType == XmlMediaType.OpticalDisc ? genericOpticalIcon : genericFolderIcon; ImagePathText = $"Path: {imagePath}"; FilterText = $"Filter: {filter.Name}"; ImageIdentifiedText = $"Image format identified by {imageFormat.Name} ({imageFormat.Id})."; ImageFormatText = !string.IsNullOrWhiteSpace(imageFormat.Info.Version) ? $"Format: {imageFormat.Format} version {imageFormat.Info.Version}" : $"Format: {imageFormat.Format}"; ImageSizeText = $"Image without headers is {imageFormat.Info.ImageSize} bytes long"; SectorsText = $"Contains a media of {imageFormat.Info.Sectors} sectors with a maximum sector size of {imageFormat.Info.SectorSize} bytes (if all sectors are of the same size this would be {imageFormat.Info.Sectors * imageFormat.Info.SectorSize} bytes)"; MediaTypeText = $"Contains a media of type {imageFormat.Info.MediaType} and XML type {imageFormat.Info.XmlMediaType}"; HasPartitionsText = $"{(imageFormat.Info.HasPartitions ? "Has" : "Doesn't have")} partitions"; HasSessionsText = $"{(imageFormat.Info.HasSessions ? "Has" : "Doesn't have")} sessions"; if (!string.IsNullOrWhiteSpace(imageFormat.Info.Application)) { ApplicationText = !string.IsNullOrWhiteSpace(imageFormat.Info.ApplicationVersion) ? $"Was created with {imageFormat.Info.Application} version {imageFormat.Info.ApplicationVersion}" : $"Was created with {imageFormat.Info.Application}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.Creator)) { CreatorText = $"Created by: {imageFormat.Info.Creator}"; } if (imageFormat.Info.CreationTime != DateTime.MinValue) { CreationTimeText = $"Created on {imageFormat.Info.CreationTime}"; } if (imageFormat.Info.LastModificationTime != DateTime.MinValue) { LastModificationTimeText = $"Last modified on {imageFormat.Info.LastModificationTime}"; } if (imageFormat.Info.MediaSequence != 0 && imageFormat.Info.LastMediaSequence != 0) { MediaSequenceText = $"Media is number {imageFormat.Info.MediaSequence} on a set of {imageFormat.Info.LastMediaSequence} medias"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaTitle)) { MediaTitleText = $"Media title: {imageFormat.Info.MediaTitle}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaManufacturer)) { MediaManufacturerText = $"Media manufacturer: {imageFormat.Info.MediaManufacturer}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaModel)) { MediaModelText = $"Media model: {imageFormat.Info.MediaModel}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaSerialNumber)) { MediaSerialNumberText = $"Media serial number: {imageFormat.Info.MediaSerialNumber}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaBarcode)) { MediaBarcodeText = $"Media barcode: {imageFormat.Info.MediaBarcode}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaPartNumber)) { MediaPartNumberText = $"Media part number: {imageFormat.Info.MediaPartNumber}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.DriveManufacturer)) { DriveManufacturerText = $"Drive manufacturer: {imageFormat.Info.DriveManufacturer}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.DriveModel)) { DriveModelText = $"Drive model: {imageFormat.Info.DriveModel}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.DriveSerialNumber)) { DriveSerialNumberText = $"Drive serial number: {imageFormat.Info.DriveSerialNumber}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.DriveFirmwareRevision)) { DriveFirmwareRevisionText = $"Drive firmware info: {imageFormat.Info.DriveFirmwareRevision}"; } if (imageFormat.Info.Cylinders > 0 && imageFormat.Info.Heads > 0 && imageFormat.Info.SectorsPerTrack > 0 && imageFormat.Info.XmlMediaType != XmlMediaType.OpticalDisc && (!(imageFormat is ITapeImage tapeImage) || !tapeImage.IsTape)) { MediaGeometryText = $"Media geometry: {imageFormat.Info.Cylinders} cylinders, {imageFormat.Info.Heads} heads, {imageFormat.Info.SectorsPerTrack} sectors per track"; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Count > 0) { foreach (MediaTagType tag in imageFormat.Info.ReadableMediaTags.OrderBy(t => t)) { MediaTagsList.Add(tag.ToString()); } } if (imageFormat.Info.ReadableSectorTags != null && imageFormat.Info.ReadableSectorTags.Count > 0) { foreach (SectorTagType tag in imageFormat.Info.ReadableSectorTags.OrderBy(t => t)) { SectorTagsList.Add(tag.ToString()); } } PeripheralDeviceTypes scsiDeviceType = PeripheralDeviceTypes.DirectAccess; byte[] scsiInquiryData = null; Inquiry?scsiInquiry = null; Modes.DecodedMode?scsiMode = null; byte[] scsiModeSense6 = null; byte[] scsiModeSense10 = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SCSI_INQUIRY)) { scsiInquiryData = imageFormat.ReadDiskTag(MediaTagType.SCSI_INQUIRY); scsiDeviceType = (PeripheralDeviceTypes)(scsiInquiryData[0] & 0x1F); scsiInquiry = Inquiry.Decode(scsiInquiryData); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SCSI_MODESENSE_6)) { scsiModeSense6 = imageFormat.ReadDiskTag(MediaTagType.SCSI_MODESENSE_6); scsiMode = Modes.DecodeMode6(scsiModeSense6, scsiDeviceType); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SCSI_MODESENSE_10)) { scsiModeSense10 = imageFormat.ReadDiskTag(MediaTagType.SCSI_MODESENSE_10); scsiMode = Modes.DecodeMode10(scsiModeSense10, scsiDeviceType); } ScsiInfo = new ScsiInfo { DataContext = new ScsiInfoViewModel(scsiInquiryData, scsiInquiry, null, scsiMode, scsiDeviceType, scsiModeSense6, scsiModeSense10, null, _view) }; byte[] ataIdentify = null; byte[] atapiIdentify = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.ATA_IDENTIFY)) { ataIdentify = imageFormat.ReadDiskTag(MediaTagType.ATA_IDENTIFY); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.ATAPI_IDENTIFY)) { atapiIdentify = imageFormat.ReadDiskTag(MediaTagType.ATAPI_IDENTIFY); } AtaInfo = new AtaInfo { DataContext = new AtaInfoViewModel(ataIdentify, atapiIdentify, null, _view) }; byte[] toc = null; TOC.CDTOC? decodedToc = null; byte[] fullToc = null; FullTOC.CDFullTOC? decodedFullToc = null; byte[] pma = null; byte[] atip = null; ATIP.CDATIP? decodedAtip = null; byte[] cdtext = null; CDTextOnLeadIn.CDText?decodedCdText = null; string mediaCatalogueNumber = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_TOC)) { toc = imageFormat.ReadDiskTag(MediaTagType.CD_TOC); if (toc.Length > 0) { ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(toc, 0)); if (dataLen + 2 != toc.Length) { byte[] tmp = new byte[toc.Length + 2]; Array.Copy(toc, 0, tmp, 2, toc.Length); tmp[0] = (byte)((toc.Length & 0xFF00) >> 8); tmp[1] = (byte)(toc.Length & 0xFF); toc = tmp; } decodedToc = TOC.Decode(toc); } } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_FullTOC)) { fullToc = imageFormat.ReadDiskTag(MediaTagType.CD_FullTOC); if (fullToc.Length > 0) { ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(fullToc, 0)); if (dataLen + 2 != fullToc.Length) { byte[] tmp = new byte[fullToc.Length + 2]; Array.Copy(fullToc, 0, tmp, 2, fullToc.Length); tmp[0] = (byte)((fullToc.Length & 0xFF00) >> 8); tmp[1] = (byte)(fullToc.Length & 0xFF); fullToc = tmp; } decodedFullToc = FullTOC.Decode(fullToc); } } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_PMA)) { pma = imageFormat.ReadDiskTag(MediaTagType.CD_PMA); if (pma.Length > 0) { ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(pma, 0)); if (dataLen + 2 != pma.Length) { byte[] tmp = new byte[pma.Length + 2]; Array.Copy(pma, 0, tmp, 2, pma.Length); tmp[0] = (byte)((pma.Length & 0xFF00) >> 8); tmp[1] = (byte)(pma.Length & 0xFF); pma = tmp; } } } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_ATIP)) { atip = imageFormat.ReadDiskTag(MediaTagType.CD_ATIP); uint dataLen = Swapping.Swap(BitConverter.ToUInt32(atip, 0)); if (dataLen + 4 != atip.Length) { byte[] tmp = new byte[atip.Length + 4]; Array.Copy(atip, 0, tmp, 4, atip.Length); tmp[0] = (byte)((atip.Length & 0xFF000000) >> 24); tmp[1] = (byte)((atip.Length & 0xFF0000) >> 16); tmp[2] = (byte)((atip.Length & 0xFF00) >> 8); tmp[3] = (byte)(atip.Length & 0xFF); atip = tmp; } decodedAtip = ATIP.Decode(atip); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_TEXT)) { cdtext = imageFormat.ReadDiskTag(MediaTagType.CD_TEXT); uint dataLen = Swapping.Swap(BitConverter.ToUInt32(cdtext, 0)); if (dataLen + 4 != cdtext.Length) { byte[] tmp = new byte[cdtext.Length + 4]; Array.Copy(cdtext, 0, tmp, 4, cdtext.Length); tmp[0] = (byte)((cdtext.Length & 0xFF000000) >> 24); tmp[1] = (byte)((cdtext.Length & 0xFF0000) >> 16); tmp[2] = (byte)((cdtext.Length & 0xFF00) >> 8); tmp[3] = (byte)(cdtext.Length & 0xFF); cdtext = tmp; } decodedCdText = CDTextOnLeadIn.Decode(cdtext); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_MCN)) { byte[] mcn = imageFormat.ReadDiskTag(MediaTagType.CD_MCN); mediaCatalogueNumber = Encoding.UTF8.GetString(mcn); } CompactDiscInfo = new CompactDiscInfo { DataContext = new CompactDiscInfoViewModel(toc, atip, null, null, fullToc, pma, cdtext, decodedToc, decodedAtip, null, decodedFullToc, decodedCdText, null, mediaCatalogueNumber, null, _view) }; byte[] dvdPfi = null; byte[] dvdDmi = null; byte[] dvdCmi = null; byte[] hddvdCopyrightInformation = null; byte[] dvdBca = null; PFI.PhysicalFormatInformation?decodedPfi = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVD_PFI)) { dvdPfi = imageFormat.ReadDiskTag(MediaTagType.DVD_PFI); decodedPfi = PFI.Decode(dvdPfi); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVD_DMI)) { dvdDmi = imageFormat.ReadDiskTag(MediaTagType.DVD_DMI); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVD_CMI)) { dvdCmi = imageFormat.ReadDiskTag(MediaTagType.DVD_CMI); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.HDDVD_CPI)) { hddvdCopyrightInformation = imageFormat.ReadDiskTag(MediaTagType.HDDVD_CPI); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVD_BCA)) { dvdBca = imageFormat.ReadDiskTag(MediaTagType.DVD_BCA); } DvdInfo = new DvdInfo { DataContext = new DvdInfoViewModel(imageFormat.Info.MediaType, dvdPfi, dvdDmi, dvdCmi, hddvdCopyrightInformation, dvdBca, null, decodedPfi, _view) }; byte[] dvdRamDds = null; byte[] dvdRamCartridgeStatus = null; byte[] dvdRamSpareArea = null; byte[] lastBorderOutRmd = null; byte[] dvdPreRecordedInfo = null; byte[] dvdrMediaIdentifier = null; byte[] dvdrPhysicalInformation = null; byte[] hddvdrMediumStatus = null; byte[] dvdrLayerCapacity = null; byte[] dvdrDlMiddleZoneStart = null; byte[] dvdrDlJumpIntervalSize = null; byte[] dvdrDlManualLayerJumpStartLba = null; byte[] dvdPlusAdip = null; byte[] dvdPlusDcb = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDRAM_DDS)) { dvdRamDds = imageFormat.ReadDiskTag(MediaTagType.DVDRAM_DDS); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDRAM_MediumStatus)) { dvdRamCartridgeStatus = imageFormat.ReadDiskTag(MediaTagType.DVDRAM_MediumStatus); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDRAM_SpareArea)) { dvdRamSpareArea = imageFormat.ReadDiskTag(MediaTagType.DVDRAM_SpareArea); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDR_RMD)) { lastBorderOutRmd = imageFormat.ReadDiskTag(MediaTagType.DVDR_RMD); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDR_PreRecordedInfo)) { dvdPreRecordedInfo = imageFormat.ReadDiskTag(MediaTagType.DVDR_PreRecordedInfo); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDR_MediaIdentifier)) { dvdrMediaIdentifier = imageFormat.ReadDiskTag(MediaTagType.DVDR_MediaIdentifier); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDR_PFI)) { dvdrPhysicalInformation = imageFormat.ReadDiskTag(MediaTagType.DVDR_PFI); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.HDDVD_MediumStatus)) { hddvdrMediumStatus = imageFormat.ReadDiskTag(MediaTagType.HDDVD_MediumStatus); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDDL_LayerCapacity)) { dvdrLayerCapacity = imageFormat.ReadDiskTag(MediaTagType.DVDDL_LayerCapacity); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDDL_MiddleZoneAddress)) { dvdrDlMiddleZoneStart = imageFormat.ReadDiskTag(MediaTagType.DVDDL_MiddleZoneAddress); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDDL_JumpIntervalSize)) { dvdrDlJumpIntervalSize = imageFormat.ReadDiskTag(MediaTagType.DVDDL_JumpIntervalSize); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDDL_ManualLayerJumpLBA)) { dvdrDlManualLayerJumpStartLba = imageFormat.ReadDiskTag(MediaTagType.DVDDL_ManualLayerJumpLBA); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVD_ADIP)) { dvdPlusAdip = imageFormat.ReadDiskTag(MediaTagType.DVD_ADIP); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DCB)) { dvdPlusDcb = imageFormat.ReadDiskTag(MediaTagType.DCB); } DvdWritableInfo = new DvdWritableInfo { DataContext = new DvdWritableInfoViewModel(imageFormat.Info.MediaType, dvdRamDds, dvdRamCartridgeStatus, dvdRamSpareArea, lastBorderOutRmd, dvdPreRecordedInfo, dvdrMediaIdentifier, dvdrPhysicalInformation, hddvdrMediumStatus, null, dvdrLayerCapacity, dvdrDlMiddleZoneStart, dvdrDlJumpIntervalSize, dvdrDlManualLayerJumpStartLba, null, dvdPlusAdip, dvdPlusDcb, _view) }; byte[] blurayBurstCuttingArea = null; byte[] blurayCartridgeStatus = null; byte[] blurayDds = null; byte[] blurayDiscInformation = null; byte[] blurayPowResources = null; byte[] bluraySpareAreaInformation = null; byte[] blurayTrackResources = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.BD_BCA)) { blurayBurstCuttingArea = imageFormat.ReadDiskTag(MediaTagType.BD_BCA); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.BD_CartridgeStatus)) { blurayCartridgeStatus = imageFormat.ReadDiskTag(MediaTagType.BD_CartridgeStatus); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.BD_DDS)) { blurayDds = imageFormat.ReadDiskTag(MediaTagType.BD_DDS); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.BD_DI)) { blurayDiscInformation = imageFormat.ReadDiskTag(MediaTagType.BD_DI); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_POWResourcesInformation)) { blurayPowResources = imageFormat.ReadDiskTag(MediaTagType.MMC_POWResourcesInformation); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.BD_SpareArea)) { bluraySpareAreaInformation = imageFormat.ReadDiskTag(MediaTagType.BD_SpareArea); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_TrackResourcesInformation)) { bluraySpareAreaInformation = imageFormat.ReadDiskTag(MediaTagType.MMC_TrackResourcesInformation); } BlurayInfo = new BlurayInfo { DataContext = new BlurayInfoViewModel(blurayDiscInformation, blurayBurstCuttingArea, blurayDds, blurayCartridgeStatus, bluraySpareAreaInformation, blurayPowResources, blurayTrackResources, null, null, _view) }; byte[] xboxDmi = null; byte[] xboxSecuritySector = null; SS.SecuritySector?decodedXboxSecuritySector = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.Xbox_DMI)) { xboxDmi = imageFormat.ReadDiskTag(MediaTagType.Xbox_DMI); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.Xbox_SecuritySector)) { xboxSecuritySector = imageFormat.ReadDiskTag(MediaTagType.Xbox_SecuritySector); decodedXboxSecuritySector = SS.Decode(xboxSecuritySector); } XboxInfo = new XboxInfo { DataContext = new XboxInfoViewModel(null, xboxDmi, xboxSecuritySector, decodedXboxSecuritySector, _view) }; byte[] pcmciaCis = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.PCMCIA_CIS)) { pcmciaCis = imageFormat.ReadDiskTag(MediaTagType.PCMCIA_CIS); } PcmciaInfo = new PcmciaInfo { DataContext = new PcmciaInfoViewModel(pcmciaCis, _view) }; DeviceType deviceType = DeviceType.Unknown; byte[] cid = null; byte[] csd = null; byte[] ocr = null; byte[] extendedCsd = null; byte[] scr = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SD_CID)) { cid = imageFormat.ReadDiskTag(MediaTagType.SD_CID); deviceType = DeviceType.SecureDigital; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SD_CSD)) { csd = imageFormat.ReadDiskTag(MediaTagType.SD_CSD); deviceType = DeviceType.SecureDigital; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SD_OCR)) { ocr = imageFormat.ReadDiskTag(MediaTagType.SD_OCR); deviceType = DeviceType.SecureDigital; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SD_SCR)) { scr = imageFormat.ReadDiskTag(MediaTagType.SD_SCR); deviceType = DeviceType.SecureDigital; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_CID)) { cid = imageFormat.ReadDiskTag(MediaTagType.MMC_CID); deviceType = DeviceType.MMC; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_CSD)) { csd = imageFormat.ReadDiskTag(MediaTagType.MMC_CSD); deviceType = DeviceType.MMC; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_OCR)) { ocr = imageFormat.ReadDiskTag(MediaTagType.MMC_OCR); deviceType = DeviceType.MMC; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_ExtendedCSD)) { extendedCsd = imageFormat.ReadDiskTag(MediaTagType.MMC_ExtendedCSD); deviceType = DeviceType.MMC; } SdMmcInfo = new SdMmcInfo { DataContext = new SdMmcInfoViewModel(deviceType, cid, csd, ocr, extendedCsd, scr) }; if (imageFormat is IOpticalMediaImage opticalMediaImage) { try { if (opticalMediaImage.Sessions != null && opticalMediaImage.Sessions.Count > 0) { foreach (Session session in opticalMediaImage.Sessions) { Sessions.Add(session); } } } catch { // ignored } try { if (opticalMediaImage.Tracks != null && opticalMediaImage.Tracks.Count > 0) { foreach (Track track in opticalMediaImage.Tracks) { Tracks.Add(track); } } } catch { // ignored } } if (imageFormat.DumpHardware is null) { return; } foreach (DumpHardwareType dump in imageFormat.DumpHardware) { foreach (ExtentType extent in dump.Extents) { DumpHardwareList.Add(new DumpHardwareModel { Manufacturer = dump.Manufacturer, Model = dump.Model, Serial = dump.Serial, SoftwareName = dump.Software.Name, SoftwareVersion = dump.Software.Version, OperatingSystem = dump.Software.OperatingSystem, Start = extent.Start, End = extent.End }); } } }
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(); }
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(); }
public DeviceInfo(Device dev) { Type = dev.Type; Manufacturer = dev.Manufacturer; Model = dev.Model; Revision = dev.Revision; Serial = dev.Serial; ScsiType = dev.ScsiType; IsRemovable = dev.IsRemovable; IsUsb = dev.IsUsb; UsbVendorId = dev.UsbVendorId; UsbProductId = dev.UsbProductId; UsbDescriptors = dev.UsbDescriptors; UsbManufacturerString = dev.UsbManufacturerString; UsbProductString = dev.UsbProductString; UsbSerialString = dev.UsbSerialString; IsFireWire = dev.IsFireWire; FireWireGuid = dev.FireWireGuid; FireWireModel = dev.FireWireModel; FireWireModelName = dev.FireWireModelName; FireWireVendor = dev.FireWireVendor; FireWireVendorName = dev.FireWireVendorName; IsCompactFlash = dev.IsCompactFlash; IsPcmcia = dev.IsPcmcia; Cis = dev.Cis; switch (dev.Type) { case DeviceType.ATA: { bool sense = dev.AtaIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters); if (sense) { DicConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status); DicConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error); DicConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}", errorRegisters.SectorCount); DicConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector); DicConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}", errorRegisters.CylinderHigh); DicConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}", errorRegisters.CylinderLow); DicConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}", errorRegisters.DeviceHead); DicConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError); break; } if (dev.Error) { DicConsole.ErrorWriteLine("Error {0} querying ATA IDENTIFY", dev.LastError); break; } AtaIdentify = ataBuf; dev.EnableMediaCardPassThrough(out errorRegisters, dev.Timeout, out _); if (errorRegisters.Sector == 0xAA && errorRegisters.SectorCount == 0x55) { AtaMcptError = errorRegisters; } break; } case DeviceType.ATAPI: { bool sense = dev.AtapiIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters); if (sense) { DicConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status); DicConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error); DicConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}", errorRegisters.SectorCount); DicConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector); DicConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}", errorRegisters.CylinderHigh); DicConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}", errorRegisters.CylinderLow); DicConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}", errorRegisters.DeviceHead); DicConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError); break; } if (!dev.Error) { AtapiIdentify = ataBuf; } else { DicConsole.ErrorWriteLine("Error {0} querying ATA PACKET IDENTIFY", dev.LastError); } // ATAPI devices are also SCSI devices goto case DeviceType.SCSI; } case DeviceType.SCSI: { bool sense = dev.ScsiInquiry(out byte[] inqBuf, out byte[] senseBuf); if (sense) { DicConsole.ErrorWriteLine("SCSI error:\n{0}", Sense.PrettifySense(senseBuf)); break; } ScsiInquiryData = inqBuf; ScsiInquiry = Inquiry.Decode(inqBuf); sense = dev.ScsiInquiry(out inqBuf, out senseBuf, 0x00); if (!sense) { ScsiEvpdPages = new Dictionary <byte, byte[]>(); byte[] pages = EVPD.DecodePage00(inqBuf); if (pages != null) { foreach (byte page in pages) { sense = dev.ScsiInquiry(out inqBuf, out senseBuf, page); if (sense) { continue; } ScsiEvpdPages.Add(page, inqBuf); } } } PeripheralDeviceTypes devType = (PeripheralDeviceTypes)ScsiInquiry.Value.PeripheralDeviceType; sense = dev.ModeSense10(out byte[] modeBuf, out senseBuf, false, true, ScsiModeSensePageControl.Current, 0x3F, 0xFF, 5, out _); if (!sense && !dev.Error) { ScsiModeSense10 = modeBuf; } if (sense || dev.Error) { sense = dev.ModeSense10(out modeBuf, out senseBuf, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _); if (!sense && !dev.Error) { ScsiModeSense10 = modeBuf; } } if (!sense && !dev.Error) { ScsiMode = Modes.DecodeMode10(modeBuf, devType); } bool useMode10 = !(sense || dev.Error || !ScsiMode.HasValue); sense = dev.ModeSense6(out modeBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0xFF, 5, out _); if (!sense && !dev.Error) { ScsiModeSense6 = modeBuf; } if (sense || dev.Error) { sense = dev.ModeSense6(out modeBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _); if (!sense && !dev.Error) { ScsiModeSense6 = modeBuf; } } if (sense || dev.Error) { sense = dev.ModeSense(out modeBuf, out senseBuf, 5, out _); if (!sense && !dev.Error) { ScsiModeSense6 = modeBuf; } } if (!sense && !dev.Error && !useMode10) { ScsiMode = Modes.DecodeMode6(modeBuf, devType); } switch (devType) { case PeripheralDeviceTypes.MultiMediaDevice: { sense = dev.GetConfiguration(out byte[] confBuf, out senseBuf, dev.Timeout, out _); if (!sense) { MmcConfiguration = confBuf; } // TODO: DVD drives respond correctly to BD status. // While specification says if no medium is present // it should inform all possible capabilities, // testing drives show only supported media capabilities. /* * byte[] strBuf; * sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _); * * if (!sense) * { * Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf); * if (caps != null) * { * foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps) * { * if (cap.SDS && cap.RDS) * DicConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); * else if (cap.SDS) * DicConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); * else if (cap.RDS) * DicConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode); * } * } * } * * sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.BD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _); * * if (!sense) * { * Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf); * if (caps != null) * { * foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps) * { * if (cap.SDS && cap.RDS) * DicConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); * else if (cap.SDS) * DicConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); * else if (cap.RDS) * DicConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode); * } * } * } */ #region Plextor if (dev.Manufacturer == "PLEXTOR") { bool plxtSense = true; bool plxtDvd = false; byte[] plxtBuf = null; switch (dev.Model) { case "DVDR PX-708A": case "DVDR PX-708A2": case "DVDR PX-712A": plxtDvd = true; plxtSense = dev.PlextorReadEeprom(out plxtBuf, out senseBuf, dev.Timeout, out _); break; case "DVDR PX-714A": case "DVDR PX-716A": case "DVDR PX-716AL": case "DVDR PX-755A": case "DVDR PX-760A": { plxtBuf = new byte[256 * 4]; for (byte i = 0; i < 4; i++) { plxtSense = dev.PlextorReadEepromBlock(out byte[] plxtBufSmall, out senseBuf, i, 256, dev.Timeout, out _); if (plxtSense) { break; } Array.Copy(plxtBufSmall, 0, plxtBuf, i * 256, 256); } plxtDvd = true; break; } default: { if (dev.Model.StartsWith("CD-R ", StringComparison.Ordinal)) { plxtSense = dev.PlextorReadEepromCdr(out plxtBuf, out senseBuf, dev.Timeout, out _); } break; } } PlextorFeatures = new Plextor { IsDvd = plxtDvd }; if (!plxtSense) { PlextorFeatures.Eeprom = plxtBuf; if (plxtDvd) { PlextorFeatures.Discs = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0120); PlextorFeatures.CdReadTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0122); PlextorFeatures.CdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0126); PlextorFeatures.DvdReadTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012A); PlextorFeatures.DvdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012E); } else { PlextorFeatures.Discs = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0078); PlextorFeatures.CdReadTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x006C); PlextorFeatures.CdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x007A); } } plxtSense = dev.PlextorGetPoweRec(out senseBuf, out bool plxtPwrRecEnabled, out ushort plxtPwrRecSpeed, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.PoweRec = true; if (plxtPwrRecEnabled) { PlextorFeatures.PoweRecEnabled = true; PlextorFeatures.PoweRecRecommendedSpeed = plxtPwrRecSpeed; plxtSense = dev.PlextorGetSpeeds(out senseBuf, out ushort plxtPwrRecSelected, out ushort plxtPwrRecMax, out ushort plxtPwrRecLast, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.PoweRecSelected = plxtPwrRecSelected; PlextorFeatures.PoweRecMax = plxtPwrRecMax; PlextorFeatures.PoweRecLast = plxtPwrRecLast; } } } // TODO: Check it with a drive plxtSense = dev.PlextorGetSilentMode(out plxtBuf, out senseBuf, dev.Timeout, out _); if (!plxtSense) { if (plxtBuf[0] == 1) { PlextorFeatures.SilentModeEnabled = true; PlextorFeatures.AccessTimeLimit = plxtBuf[1]; PlextorFeatures.CdReadSpeedLimit = plxtBuf[2]; PlextorFeatures.DvdReadSpeedLimit = plxtBuf[3]; PlextorFeatures.CdWriteSpeedLimit = plxtBuf[4]; // TODO: Check which one is each one /* * if(plxtBuf[6] > 0) * DicConsole.WriteLine("\tTray eject speed limited to {0}", * -(plxtBuf[6] + 48)); * if(plxtBuf[7] > 0) * DicConsole.WriteLine("\tTray eject speed limited to {0}", * plxtBuf[7] - 47); */ } } plxtSense = dev.PlextorGetGigaRec(out plxtBuf, out senseBuf, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.GigaRec = true; } plxtSense = dev.PlextorGetSecuRec(out plxtBuf, out senseBuf, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.SecuRec = true; } plxtSense = dev.PlextorGetSpeedRead(out plxtBuf, out senseBuf, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.SpeedRead = true; if ((plxtBuf[2] & 0x01) == 0x01) { PlextorFeatures.SpeedReadEnabled = true; } } plxtSense = dev.PlextorGetHiding(out plxtBuf, out senseBuf, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.Hiding = true; if ((plxtBuf[2] & 0x02) == 0x02) { PlextorFeatures.HidesRecordables = true; } if ((plxtBuf[2] & 0x01) == 0x01) { PlextorFeatures.HidesSessions = true; } } plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, false, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.VariRec = true; } if (plxtDvd) { plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, true, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.VariRecDvd = true; } plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, false, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.BitSetting = true; } plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, true, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.BitSettingDl = true; } plxtSense = dev.PlextorGetTestWriteDvdPlus(out plxtBuf, out senseBuf, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.DvdPlusWriteTest = true; } } } #endregion Plextor if (ScsiInquiry.Value.KreonPresent) { if (!dev.KreonGetFeatureList(out senseBuf, out KreonFeatures krFeatures, dev.Timeout, out _)) { KreonFeatures = krFeatures; } } break; } case PeripheralDeviceTypes.SequentialAccess: { sense = dev.ReadBlockLimits(out byte[] seqBuf, out senseBuf, dev.Timeout, out _); if (sense) { DicConsole.ErrorWriteLine("READ BLOCK LIMITS:\n{0}", Sense.PrettifySense(senseBuf)); } else { BlockLimits = seqBuf; } sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, dev.Timeout, out _); if (sense) { DicConsole.ErrorWriteLine("REPORT DENSITY SUPPORT:\n{0}", Sense.PrettifySense(senseBuf)); } else { DensitySupport = seqBuf; DensitySupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeDensity(seqBuf); } sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, true, false, dev.Timeout, out _); if (sense) { DicConsole.ErrorWriteLine("REPORT DENSITY SUPPORT (MEDIUM):\n{0}", Sense.PrettifySense(senseBuf)); } else { MediumDensitySupport = seqBuf; MediaTypeSupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeMediumType(seqBuf); } break; } } break; } case DeviceType.MMC: { bool sense = dev.ReadCid(out byte[] mmcBuf, out _, dev.Timeout, out _); if (!sense) { CID = mmcBuf; } sense = dev.ReadCsd(out mmcBuf, out _, dev.Timeout, out _); if (!sense) { CSD = mmcBuf; } sense = dev.ReadOcr(out mmcBuf, out _, dev.Timeout, out _); if (!sense) { OCR = mmcBuf; } sense = dev.ReadExtendedCsd(out mmcBuf, out _, dev.Timeout, out _); if (!sense) { ExtendedCSD = mmcBuf; } } break; case DeviceType.SecureDigital: { bool sense = dev.ReadCid(out byte[] sdBuf, out _, dev.Timeout, out _); if (!sense) { CID = sdBuf; } sense = dev.ReadCsd(out sdBuf, out _, dev.Timeout, out _); if (!sense) { CSD = sdBuf; } sense = dev.ReadSdocr(out sdBuf, out _, dev.Timeout, out _); if (!sense) { OCR = sdBuf; } sense = dev.ReadScr(out sdBuf, out _, dev.Timeout, out _); if (!sense) { SCR = sdBuf; } } break; default: DicConsole.ErrorWriteLine("Unknown device type {0}, cannot get information.", dev.Type); break; } }
/// <summary>Retries errored data when dumping from a SCSI Block Commands compliant device</summary> /// <param name="currentTry">Resume information</param> /// <param name="extents">Correctly dump extents</param> /// <param name="totalDuration">Total time spent in commands</param> /// <param name="scsiReader">SCSI reader</param> /// <param name="blankExtents">Blank extents</param> void RetrySbcData(Reader scsiReader, DumpHardwareType currentTry, ExtentsULong extents, ref double totalDuration, ExtentsULong blankExtents) { int pass = 1; bool forward = true; bool runningPersistent = false; bool sense; byte[] buffer; bool recoveredError; Modes.ModePage?currentModePage = null; byte[] md6; byte[] md10; bool blankCheck; bool newBlank = false; if (_persistent) { Modes.ModePage_01_MMC pgMmc; Modes.ModePage_01 pg; sense = _dev.ModeSense6(out buffer, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout, out _); if (sense) { sense = _dev.ModeSense10(out buffer, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout, out _); if (!sense) { Modes.DecodedMode?dcMode10 = Modes.DecodeMode10(buffer, _dev.ScsiType); if (dcMode10?.Pages != null) { foreach (Modes.ModePage modePage in dcMode10.Value.Pages.Where(modePage => modePage.Page == 0x01 && modePage.Subpage == 0x00)) { currentModePage = modePage; } } } } else { Modes.DecodedMode?dcMode6 = Modes.DecodeMode6(buffer, _dev.ScsiType); if (dcMode6?.Pages != null) { foreach (Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => modePage.Page == 0x01 && modePage.Subpage == 0x00)) { currentModePage = modePage; } } } if (currentModePage == null) { if (_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) { 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) }; } else { pg = new Modes.ModePage_01 { PS = false, AWRE = true, ARRE = true, TB = false, RC = false, EER = true, PER = false, DTE = true, DCR = false, ReadRetryCount = 32 }; currentModePage = new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg) }; } } if (_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) { 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); } else { pg = new Modes.ModePage_01 { PS = false, AWRE = false, ARRE = false, TB = true, RC = false, EER = true, PER = false, DTE = false, DCR = false, ReadRetryCount = 255 }; var md = new Modes.DecodedMode { Header = new Modes.ModeHeader(), Pages = new[] { new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg) } } }; 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 byte[] 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(); repeatRetry: ulong[] tmpArray = _resume.BadBlocks.ToArray(); foreach (ulong badSector in tmpArray) { if (_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); _dumpLog.WriteLine("Aborted!"); break; } PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, forward ? "forward" : "reverse", runningPersistent ? "recovering partial data, " : "")); sense = scsiReader.ReadBlock(out buffer, badSector, out double cmdDuration, out recoveredError, out blankCheck); totalDuration += cmdDuration; if (blankCheck) { _resume.BadBlocks.Remove(badSector); blankExtents.Add(badSector, badSector); newBlank = true; UpdateStatus?.Invoke($"Found blank block {badSector} in pass {pass}."); _dumpLog.WriteLine("Found blank block {0} in pass {1}.", badSector, pass); continue; } if ((!sense && !_dev.Error) || recoveredError) { _resume.BadBlocks.Remove(badSector); extents.Add(badSector); _outputPlugin.WriteSector(buffer, badSector); UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } else if (runningPersistent) { _outputPlugin.WriteSector(buffer, badSector); } } if (pass < _retryPasses && !_aborted && _resume.BadBlocks.Count > 0) { pass++; forward = !forward; _resume.BadBlocks.Sort(); if (!forward) { _resume.BadBlocks.Reverse(); } goto repeatRetry; } 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); UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status)."); _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); sense = _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _); if (sense) { _dev.ModeSelect10(md10, out _, true, false, _dev.Timeout, out _); } } if (newBlank) { _resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents); } EndProgress?.Invoke(); }
/// <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); }
public pnlImageInfo(string imagePath, IFilter filter, IMediaImage imageFormat) { this.imagePath = imagePath; this.filter = filter; this.imageFormat = imageFormat; XamlReader.Load(this); Stream logo = ResourceHandler .GetResourceStream($"DiscImageChef.Gui.Assets.Logos.Media.{imageFormat.Info.MediaType}.svg"); /* if(logo != null) * { * svgMediaLogo.SvgStream = logo; * svgMediaLogo.Visible = true; * } * else * {*/ logo = ResourceHandler .GetResourceStream($"DiscImageChef.Gui.Assets.Logos.Media.{imageFormat.Info.MediaType}.png"); if (logo != null) { imgMediaLogo.Image = new Bitmap(logo); imgMediaLogo.Visible = true; } //} lblImagePath.Text = $"Path: {imagePath}"; lblFilter.Text = $"Filter: {filter.Name}"; lblImageFormat.Text = $"Image format identified by {imageFormat.Name} ({imageFormat.Id})."; lblImageFormat.Text = !string.IsNullOrWhiteSpace(imageFormat.Info.Version) ? $"Format: {imageFormat.Format} version {imageFormat.Info.Version}" : $"Format: {imageFormat.Format}"; lblImageSize.Text = $"Image without headers is {imageFormat.Info.ImageSize} bytes long"; lblSectors.Text = $"Contains a media of {imageFormat.Info.Sectors} sectors with a maximum sector size of {imageFormat.Info.SectorSize} bytes (if all sectors are of the same size this would be {imageFormat.Info.Sectors * imageFormat.Info.SectorSize} bytes)"; lblMediaType.Text = $"Contains a media of type {imageFormat.Info.MediaType} and XML type {imageFormat.Info.XmlMediaType}"; lblHasPartitions.Text = $"{(imageFormat.Info.HasPartitions ? "Has" : "Doesn't have")} partitions"; lblHasSessions.Text = $"{(imageFormat.Info.HasSessions ? "Has" : "Doesn't have")} sessions"; if (!string.IsNullOrWhiteSpace(imageFormat.Info.Application)) { lblApplication.Visible = true; lblApplication.Text = !string.IsNullOrWhiteSpace(imageFormat.Info.ApplicationVersion) ? $"Was created with {imageFormat.Info.Application} version {imageFormat.Info.ApplicationVersion}" : $"Was created with {imageFormat.Info.Application}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.Creator)) { lblCreator.Visible = true; lblCreator.Text = $"Created by: {imageFormat.Info.Creator}"; } if (imageFormat.Info.CreationTime != DateTime.MinValue) { lblCreationTime.Visible = true; lblCreationTime.Text = $"Created on {imageFormat.Info.CreationTime}"; } if (imageFormat.Info.LastModificationTime != DateTime.MinValue) { lblLastModificationTime.Visible = true; lblLastModificationTime.Text = $"Last modified on {imageFormat.Info.LastModificationTime}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.Comments)) { grpComments.Visible = true; txtComments.Text = imageFormat.Info.Comments; } if (imageFormat.Info.MediaSequence != 0 && imageFormat.Info.LastMediaSequence != 0) { lblMediaSequence.Visible = true; lblMediaSequence.Text = $"Media is number {imageFormat.Info.MediaSequence} on a set of {imageFormat.Info.LastMediaSequence} medias"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaTitle)) { lblMediaTitle.Visible = true; lblMediaTitle.Text = $"Media title: {imageFormat.Info.MediaTitle}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaManufacturer)) { lblMediaManufacturer.Visible = true; lblMediaManufacturer.Text = $"Media manufacturer: {imageFormat.Info.MediaManufacturer}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaModel)) { lblMediaModel.Visible = true; lblMediaModel.Text = $"Media model: {imageFormat.Info.MediaModel}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaSerialNumber)) { lblMediaSerialNumber.Visible = true; lblMediaSerialNumber.Text = $"Media serial number: {imageFormat.Info.MediaSerialNumber}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaBarcode)) { lblMediaBarcode.Visible = true; lblMediaBarcode.Text = $"Media barcode: {imageFormat.Info.MediaBarcode}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaPartNumber)) { lblMediaPartNumber.Visible = true; lblMediaPartNumber.Text = $"Media part number: {imageFormat.Info.MediaPartNumber}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.DriveManufacturer)) { lblDriveManufacturer.Visible = true; lblDriveManufacturer.Text = $"Drive manufacturer: {imageFormat.Info.DriveManufacturer}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.DriveModel)) { lblDriveModel.Visible = true; lblDriveModel.Text = $"Drive model: {imageFormat.Info.DriveModel}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.DriveSerialNumber)) { lblDriveSerialNumber.Visible = true; lblDriveSerialNumber.Text = $"Drive serial number: {imageFormat.Info.DriveSerialNumber}"; } if (!string.IsNullOrWhiteSpace(imageFormat.Info.DriveFirmwareRevision)) { lblDriveFirmwareRevision.Visible = true; lblDriveFirmwareRevision.Text = $"Drive firmware info: {imageFormat.Info.DriveFirmwareRevision}"; } if (imageFormat.Info.Cylinders > 0 && imageFormat.Info.Heads > 0 && imageFormat.Info.SectorsPerTrack > 0 && imageFormat.Info.XmlMediaType != XmlMediaType.OpticalDisc && (!(imageFormat is ITapeImage tapeImage) || !tapeImage.IsTape)) { lblMediaGeometry.Visible = true; lblMediaGeometry.Text = $"Media geometry: {imageFormat.Info.Cylinders} cylinders, {imageFormat.Info.Heads} heads, {imageFormat.Info.SectorsPerTrack} sectors per track"; } grpMediaInfo.Visible = lblMediaSequence.Visible || lblMediaTitle.Visible || lblMediaManufacturer.Visible || lblMediaModel.Visible || lblMediaSerialNumber.Visible || lblMediaBarcode.Visible || lblMediaPartNumber.Visible; grpDriveInfo.Visible = lblDriveManufacturer.Visible || lblDriveModel.Visible || lblDriveSerialNumber.Visible || lblDriveFirmwareRevision.Visible || lblMediaGeometry.Visible; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Count > 0) { TreeGridItemCollection mediaTagList = new TreeGridItemCollection(); treeMediaTags.Columns.Add(new GridColumn { HeaderText = "Tag", DataCell = new TextBoxCell(0) }); treeMediaTags.AllowMultipleSelection = false; treeMediaTags.ShowHeader = false; treeMediaTags.DataStore = mediaTagList; foreach (MediaTagType tag in imageFormat.Info.ReadableMediaTags.OrderBy(t => t)) { mediaTagList.Add(new TreeGridItem { Values = new object[] { tag.ToString() } }); } grpMediaTags.Visible = true; } if (imageFormat.Info.ReadableSectorTags != null && imageFormat.Info.ReadableSectorTags.Count > 0) { TreeGridItemCollection sectorTagList = new TreeGridItemCollection(); treeSectorTags.Columns.Add(new GridColumn { HeaderText = "Tag", DataCell = new TextBoxCell(0) }); treeSectorTags.AllowMultipleSelection = false; treeSectorTags.ShowHeader = false; treeSectorTags.DataStore = sectorTagList; foreach (SectorTagType tag in imageFormat.Info.ReadableSectorTags.OrderBy(t => t)) { sectorTagList.Add(new TreeGridItem { Values = new object[] { tag.ToString() } }); } grpSectorTags.Visible = true; } PeripheralDeviceTypes scsiDeviceType = PeripheralDeviceTypes.DirectAccess; byte[] scsiInquiryData = null; Inquiry.SCSIInquiry?scsiInquiry = null; Modes.DecodedMode? scsiMode = null; byte[] scsiModeSense6 = null; byte[] scsiModeSense10 = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SCSI_INQUIRY)) { scsiInquiryData = imageFormat.ReadDiskTag(MediaTagType.SCSI_INQUIRY); scsiDeviceType = (PeripheralDeviceTypes)(scsiInquiryData[0] & 0x1F); scsiInquiry = Inquiry.Decode(scsiInquiryData); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SCSI_MODESENSE_6)) { scsiModeSense6 = imageFormat.ReadDiskTag(MediaTagType.SCSI_MODESENSE_6); scsiMode = Modes.DecodeMode6(scsiModeSense6, scsiDeviceType); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SCSI_MODESENSE_10)) { scsiModeSense10 = imageFormat.ReadDiskTag(MediaTagType.SCSI_MODESENSE_10); scsiMode = Modes.DecodeMode10(scsiModeSense10, scsiDeviceType); } tabScsiInfo tabScsiInfo = new tabScsiInfo(); tabScsiInfo.LoadData(scsiInquiryData, scsiInquiry, null, scsiMode, scsiDeviceType, scsiModeSense6, scsiModeSense10, null); tabInfos.Pages.Add(tabScsiInfo); byte[] ataIdentify = null; byte[] atapiIdentify = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.ATA_IDENTIFY)) { ataIdentify = imageFormat.ReadDiskTag(MediaTagType.ATA_IDENTIFY); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.ATAPI_IDENTIFY)) { atapiIdentify = imageFormat.ReadDiskTag(MediaTagType.ATAPI_IDENTIFY); } tabAtaInfo tabAtaInfo = new tabAtaInfo(); tabAtaInfo.LoadData(ataIdentify, atapiIdentify, null); tabInfos.Pages.Add(tabAtaInfo); byte[] toc = null; TOC.CDTOC? decodedToc = null; byte[] fullToc = null; FullTOC.CDFullTOC? decodedFullToc = null; byte[] pma = null; byte[] atip = null; ATIP.CDATIP? decodedAtip = null; byte[] cdtext = null; CDTextOnLeadIn.CDText?decodedCdText = null; string mediaCatalogueNumber = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_TOC)) { toc = imageFormat.ReadDiskTag(MediaTagType.CD_TOC); if (toc.Length > 0) { ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(toc, 0)); if (dataLen + 2 != toc.Length) { byte[] tmp = new byte[toc.Length + 2]; Array.Copy(toc, 0, tmp, 2, toc.Length); tmp[0] = (byte)((toc.Length & 0xFF00) >> 8); tmp[1] = (byte)(toc.Length & 0xFF); toc = tmp; } decodedToc = TOC.Decode(toc); } } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_FullTOC)) { fullToc = imageFormat.ReadDiskTag(MediaTagType.CD_FullTOC); if (fullToc.Length > 0) { ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(fullToc, 0)); if (dataLen + 2 != fullToc.Length) { byte[] tmp = new byte[fullToc.Length + 2]; Array.Copy(fullToc, 0, tmp, 2, fullToc.Length); tmp[0] = (byte)((fullToc.Length & 0xFF00) >> 8); tmp[1] = (byte)(fullToc.Length & 0xFF); fullToc = tmp; } decodedFullToc = FullTOC.Decode(fullToc); } } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_PMA)) { pma = imageFormat.ReadDiskTag(MediaTagType.CD_PMA); if (pma.Length > 0) { ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(pma, 0)); if (dataLen + 2 != pma.Length) { byte[] tmp = new byte[pma.Length + 2]; Array.Copy(pma, 0, tmp, 2, pma.Length); tmp[0] = (byte)((pma.Length & 0xFF00) >> 8); tmp[1] = (byte)(pma.Length & 0xFF); pma = tmp; } } } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_ATIP)) { atip = imageFormat.ReadDiskTag(MediaTagType.CD_ATIP); uint dataLen = Swapping.Swap(BitConverter.ToUInt32(atip, 0)); if (dataLen + 4 != atip.Length) { byte[] tmp = new byte[atip.Length + 4]; Array.Copy(atip, 0, tmp, 4, atip.Length); tmp[0] = (byte)((atip.Length & 0xFF000000) >> 24); tmp[1] = (byte)((atip.Length & 0xFF0000) >> 16); tmp[2] = (byte)((atip.Length & 0xFF00) >> 8); tmp[3] = (byte)(atip.Length & 0xFF); atip = tmp; } decodedAtip = ATIP.Decode(atip); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_TEXT)) { cdtext = imageFormat.ReadDiskTag(MediaTagType.CD_TEXT); uint dataLen = Swapping.Swap(BitConverter.ToUInt32(cdtext, 0)); if (dataLen + 4 != cdtext.Length) { byte[] tmp = new byte[cdtext.Length + 4]; Array.Copy(cdtext, 0, tmp, 4, cdtext.Length); tmp[0] = (byte)((cdtext.Length & 0xFF000000) >> 24); tmp[1] = (byte)((cdtext.Length & 0xFF0000) >> 16); tmp[2] = (byte)((cdtext.Length & 0xFF00) >> 8); tmp[3] = (byte)(cdtext.Length & 0xFF); cdtext = tmp; } decodedCdText = CDTextOnLeadIn.Decode(cdtext); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_MCN)) { byte[] mcn = imageFormat.ReadDiskTag(MediaTagType.CD_MCN); mediaCatalogueNumber = Encoding.UTF8.GetString(mcn); } tabCompactDiscInfo tabCompactDiscInfo = new tabCompactDiscInfo(); tabCompactDiscInfo.LoadData(toc, atip, null, null, fullToc, pma, cdtext, decodedToc, decodedAtip, null, decodedFullToc, decodedCdText, null, mediaCatalogueNumber, null); tabInfos.Pages.Add(tabCompactDiscInfo); byte[] dvdPfi = null; byte[] dvdDmi = null; byte[] dvdCmi = null; byte[] hddvdCopyrightInformation = null; byte[] dvdBca = null; PFI.PhysicalFormatInformation?decodedPfi = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVD_PFI)) { dvdPfi = imageFormat.ReadDiskTag(MediaTagType.DVD_PFI); decodedPfi = PFI.Decode(dvdPfi); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVD_DMI)) { dvdDmi = imageFormat.ReadDiskTag(MediaTagType.DVD_DMI); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVD_CMI)) { dvdCmi = imageFormat.ReadDiskTag(MediaTagType.DVD_CMI); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.HDDVD_CPI)) { hddvdCopyrightInformation = imageFormat.ReadDiskTag(MediaTagType.HDDVD_CPI); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVD_BCA)) { dvdBca = imageFormat.ReadDiskTag(MediaTagType.DVD_BCA); } tabDvdInfo tabDvdInfo = new tabDvdInfo(); tabDvdInfo.LoadData(imageFormat.Info.MediaType, dvdPfi, dvdDmi, dvdCmi, hddvdCopyrightInformation, dvdBca, null, decodedPfi); tabInfos.Pages.Add(tabDvdInfo); byte[] dvdRamDds = null; byte[] dvdRamCartridgeStatus = null; byte[] dvdRamSpareArea = null; byte[] lastBorderOutRmd = null; byte[] dvdPreRecordedInfo = null; byte[] dvdrMediaIdentifier = null; byte[] dvdrPhysicalInformation = null; byte[] hddvdrMediumStatus = null; byte[] dvdrLayerCapacity = null; byte[] dvdrDlMiddleZoneStart = null; byte[] dvdrDlJumpIntervalSize = null; byte[] dvdrDlManualLayerJumpStartLba = null; byte[] dvdPlusAdip = null; byte[] dvdPlusDcb = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDRAM_DDS)) { dvdRamDds = imageFormat.ReadDiskTag(MediaTagType.DVDRAM_DDS); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDRAM_MediumStatus)) { dvdRamCartridgeStatus = imageFormat.ReadDiskTag(MediaTagType.DVDRAM_MediumStatus); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDRAM_SpareArea)) { dvdRamSpareArea = imageFormat.ReadDiskTag(MediaTagType.DVDRAM_SpareArea); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDR_RMD)) { lastBorderOutRmd = imageFormat.ReadDiskTag(MediaTagType.DVDR_RMD); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDR_PreRecordedInfo)) { dvdPreRecordedInfo = imageFormat.ReadDiskTag(MediaTagType.DVDR_PreRecordedInfo); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDR_MediaIdentifier)) { dvdrMediaIdentifier = imageFormat.ReadDiskTag(MediaTagType.DVDR_MediaIdentifier); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDR_PFI)) { dvdrPhysicalInformation = imageFormat.ReadDiskTag(MediaTagType.DVDR_PFI); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.HDDVD_MediumStatus)) { hddvdrMediumStatus = imageFormat.ReadDiskTag(MediaTagType.HDDVD_MediumStatus); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDDL_LayerCapacity)) { dvdrLayerCapacity = imageFormat.ReadDiskTag(MediaTagType.DVDDL_LayerCapacity); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDDL_MiddleZoneAddress)) { dvdrDlMiddleZoneStart = imageFormat.ReadDiskTag(MediaTagType.DVDDL_MiddleZoneAddress); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDDL_JumpIntervalSize)) { dvdrDlJumpIntervalSize = imageFormat.ReadDiskTag(MediaTagType.DVDDL_JumpIntervalSize); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDDL_ManualLayerJumpLBA)) { dvdrDlManualLayerJumpStartLba = imageFormat.ReadDiskTag(MediaTagType.DVDDL_ManualLayerJumpLBA); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVD_ADIP)) { dvdPlusAdip = imageFormat.ReadDiskTag(MediaTagType.DVD_ADIP); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DCB)) { dvdPlusDcb = imageFormat.ReadDiskTag(MediaTagType.DCB); } tabDvdWritableInfo tabDvdWritableInfo = new tabDvdWritableInfo(); tabDvdWritableInfo.LoadData(imageFormat.Info.MediaType, dvdRamDds, dvdRamCartridgeStatus, dvdRamSpareArea, lastBorderOutRmd, dvdPreRecordedInfo, dvdrMediaIdentifier, dvdrPhysicalInformation, hddvdrMediumStatus, null, dvdrLayerCapacity, dvdrDlMiddleZoneStart, dvdrDlJumpIntervalSize, dvdrDlManualLayerJumpStartLba, null, dvdPlusAdip, dvdPlusDcb); tabInfos.Pages.Add(tabDvdWritableInfo); byte[] blurayBurstCuttingArea = null; byte[] blurayCartridgeStatus = null; byte[] blurayDds = null; byte[] blurayDiscInformation = null; byte[] blurayPowResources = null; byte[] bluraySpareAreaInformation = null; byte[] blurayTrackResources = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.BD_BCA)) { blurayBurstCuttingArea = imageFormat.ReadDiskTag(MediaTagType.BD_BCA); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.BD_CartridgeStatus)) { blurayCartridgeStatus = imageFormat.ReadDiskTag(MediaTagType.BD_CartridgeStatus); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.BD_DDS)) { blurayDds = imageFormat.ReadDiskTag(MediaTagType.BD_DDS); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.BD_DI)) { blurayDiscInformation = imageFormat.ReadDiskTag(MediaTagType.BD_DI); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_POWResourcesInformation)) { blurayPowResources = imageFormat.ReadDiskTag(MediaTagType.MMC_POWResourcesInformation); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.BD_SpareArea)) { bluraySpareAreaInformation = imageFormat.ReadDiskTag(MediaTagType.BD_SpareArea); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_TrackResourcesInformation)) { bluraySpareAreaInformation = imageFormat.ReadDiskTag(MediaTagType.MMC_TrackResourcesInformation); } tabBlurayInfo tabBlurayInfo = new tabBlurayInfo(); tabBlurayInfo.LoadData(blurayDiscInformation, blurayBurstCuttingArea, blurayDds, blurayCartridgeStatus, bluraySpareAreaInformation, blurayPowResources, blurayTrackResources, null, null); tabInfos.Pages.Add(tabBlurayInfo); byte[] xboxDmi = null; byte[] xboxSecuritySector = null; SS.SecuritySector?decodedXboxSecuritySector = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.Xbox_DMI)) { xboxDmi = imageFormat.ReadDiskTag(MediaTagType.Xbox_DMI); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.Xbox_SecuritySector)) { xboxSecuritySector = imageFormat.ReadDiskTag(MediaTagType.Xbox_SecuritySector); decodedXboxSecuritySector = SS.Decode(xboxSecuritySector); } tabXboxInfo tabXboxInfo = new tabXboxInfo(); tabXboxInfo.LoadData(null, xboxDmi, xboxSecuritySector, decodedXboxSecuritySector); tabInfos.Pages.Add(tabXboxInfo); byte[] pcmciaCis = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.PCMCIA_CIS)) { pcmciaCis = imageFormat.ReadDiskTag(MediaTagType.PCMCIA_CIS); } tabPcmciaInfo tabPcmciaInfo = new tabPcmciaInfo(); tabPcmciaInfo.LoadData(pcmciaCis); tabInfos.Pages.Add(tabPcmciaInfo); DeviceType deviceType = DeviceType.Unknown; byte[] cid = null; byte[] csd = null; byte[] ocr = null; byte[] extendedCsd = null; byte[] scr = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SD_CID)) { cid = imageFormat.ReadDiskTag(MediaTagType.SD_CID); deviceType = DeviceType.SecureDigital; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SD_CSD)) { csd = imageFormat.ReadDiskTag(MediaTagType.SD_CSD); deviceType = DeviceType.SecureDigital; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SD_OCR)) { ocr = imageFormat.ReadDiskTag(MediaTagType.SD_OCR); deviceType = DeviceType.SecureDigital; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SD_SCR)) { scr = imageFormat.ReadDiskTag(MediaTagType.SD_SCR); deviceType = DeviceType.SecureDigital; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_CID)) { cid = imageFormat.ReadDiskTag(MediaTagType.MMC_CID); deviceType = DeviceType.MMC; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_CSD)) { csd = imageFormat.ReadDiskTag(MediaTagType.MMC_CSD); deviceType = DeviceType.MMC; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_OCR)) { ocr = imageFormat.ReadDiskTag(MediaTagType.MMC_OCR); deviceType = DeviceType.MMC; } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_ExtendedCSD)) { extendedCsd = imageFormat.ReadDiskTag(MediaTagType.MMC_ExtendedCSD); deviceType = DeviceType.MMC; } tabSdMmcInfo tabSdMmcInfo = new tabSdMmcInfo(); tabSdMmcInfo.LoadData(deviceType, cid, csd, ocr, extendedCsd, scr); tabInfos.Pages.Add(tabSdMmcInfo); if (imageFormat is IOpticalMediaImage opticalMediaImage) { try { if (opticalMediaImage.Sessions != null && opticalMediaImage.Sessions.Count > 0) { TreeGridItemCollection sessionList = new TreeGridItemCollection(); treeSessions.Columns.Add(new GridColumn { HeaderText = "Session", DataCell = new TextBoxCell(0) }); treeSessions.Columns.Add(new GridColumn { HeaderText = "First track", DataCell = new TextBoxCell(1) }); treeSessions.Columns.Add(new GridColumn { HeaderText = "Last track", DataCell = new TextBoxCell(2) }); treeSessions.Columns.Add(new GridColumn { HeaderText = "Start", DataCell = new TextBoxCell(3) }); treeSessions.Columns.Add(new GridColumn { HeaderText = "End", DataCell = new TextBoxCell(4) }); treeSessions.AllowMultipleSelection = false; treeSessions.ShowHeader = true; treeSessions.DataStore = sessionList; foreach (Session session in opticalMediaImage.Sessions) { sessionList.Add(new TreeGridItem { Values = new object[] { session.SessionSequence, session.StartTrack, session.EndTrack, session.StartSector, session.EndSector } }); } tabSessions.Visible = true; } } catch { // ignored } try { if (opticalMediaImage.Tracks != null && opticalMediaImage.Tracks.Count > 0) { TreeGridItemCollection tracksList = new TreeGridItemCollection(); treeTracks.Columns.Add(new GridColumn { HeaderText = "Track", DataCell = new TextBoxCell(0) }); treeTracks.Columns.Add(new GridColumn { HeaderText = "Type", DataCell = new TextBoxCell(1) }); treeTracks.Columns.Add(new GridColumn { HeaderText = "Bps", DataCell = new TextBoxCell(2) }); treeTracks.Columns.Add(new GridColumn { HeaderText = "Raw bps", DataCell = new TextBoxCell(3) }); treeTracks.Columns.Add(new GridColumn { HeaderText = "Subchannel", DataCell = new TextBoxCell(4) }); treeTracks.Columns.Add(new GridColumn { HeaderText = "Pregap", DataCell = new TextBoxCell(5) }); treeTracks.Columns.Add(new GridColumn { HeaderText = "Start", DataCell = new TextBoxCell(6) }); treeTracks.Columns.Add(new GridColumn { HeaderText = "End", DataCell = new TextBoxCell(7) }); treeTracks.AllowMultipleSelection = false; treeTracks.ShowHeader = true; treeTracks.DataStore = tracksList; foreach (Track track in opticalMediaImage.Tracks) { tracksList.Add(new TreeGridItem { Values = new object[] { track.TrackSequence, track.TrackType, track.TrackBytesPerSector, track.TrackRawBytesPerSector, track.TrackSubchannelType, track.TrackPregap, track.TrackStartSector, track.TrackEndSector } }); } tabTracks.Visible = true; } } catch { // ignored } } if (imageFormat.DumpHardware == null) { return; } TreeGridItemCollection dumpHardwareList = new TreeGridItemCollection(); treeDumpHardware.Columns.Add(new GridColumn { HeaderText = "Manufacturer", DataCell = new TextBoxCell(0) }); treeDumpHardware.Columns.Add(new GridColumn { HeaderText = "Model", DataCell = new TextBoxCell(1) }); treeDumpHardware.Columns.Add(new GridColumn { HeaderText = "Serial", DataCell = new TextBoxCell(2) }); treeDumpHardware.Columns.Add(new GridColumn { HeaderText = "Software", DataCell = new TextBoxCell(3) }); treeDumpHardware.Columns.Add(new GridColumn { HeaderText = "Version", DataCell = new TextBoxCell(4) }); treeDumpHardware.Columns.Add(new GridColumn { HeaderText = "Operating system", DataCell = new TextBoxCell(5) }); treeDumpHardware.Columns.Add(new GridColumn { HeaderText = "Start", DataCell = new TextBoxCell(6) }); treeDumpHardware.Columns.Add(new GridColumn { HeaderText = "End", DataCell = new TextBoxCell(7) }); treeDumpHardware.AllowMultipleSelection = false; treeDumpHardware.ShowHeader = true; treeDumpHardware.DataStore = dumpHardwareList; foreach (DumpHardwareType dump in imageFormat.DumpHardware) { foreach (ExtentType extent in dump.Extents) { dumpHardwareList.Add(new TreeGridItem { Values = new object[] { dump.Manufacturer, dump.Model, dump.Serial, dump.Software.Name, dump.Software.Version, dump.Software.OperatingSystem, extent.Start, extent.End } }); } } tabDumpHardware.Visible = true; }
/// <summary>Dumps an optical disc</summary> void Mmc() { MediaType dskType = MediaType.Unknown; bool sense; byte[] tmpBuf; bool compactDisc = true; bool gotConfiguration = false; bool isXbox = false; DVDDecryption dvdDecrypt = null; _speedMultiplier = 1; // TODO: Log not only what is it reading, but if it was read correctly or not. sense = _dev.GetConfiguration(out byte[] cmdBuf, out _, 0, MmcGetConfigurationRt.Current, _dev.Timeout, out _); if (!sense) { gotConfiguration = true; Features.SeparatedFeatures ftr = Features.Separate(cmdBuf); _dumpLog.WriteLine("Device reports current profile is 0x{0:X4}", ftr.CurrentProfile); switch (ftr.CurrentProfile) { case 0x0001: dskType = MediaType.GENERIC_HDD; _speedMultiplier = -1; goto default; case 0x0002: dskType = MediaType.PD650; _speedMultiplier = -1; goto default; case 0x0005: dskType = MediaType.CDMO; break; case 0x0008: dskType = MediaType.CD; break; case 0x0009: dskType = MediaType.CDR; break; case 0x000A: dskType = MediaType.CDRW; break; case 0x0010: dskType = MediaType.DVDROM; _speedMultiplier = 9; goto default; case 0x0011: dskType = MediaType.DVDR; _speedMultiplier = 9; goto default; case 0x0012: dskType = MediaType.DVDRAM; _speedMultiplier = 9; goto default; case 0x0013: case 0x0014: dskType = MediaType.DVDRW; _speedMultiplier = 9; goto default; case 0x0015: case 0x0016: dskType = MediaType.DVDRDL; _speedMultiplier = 9; goto default; case 0x0017: dskType = MediaType.DVDRWDL; _speedMultiplier = 9; goto default; case 0x0018: dskType = MediaType.DVDDownload; _speedMultiplier = 9; goto default; case 0x001A: dskType = MediaType.DVDPRW; _speedMultiplier = 9; goto default; case 0x001B: dskType = MediaType.DVDPR; _speedMultiplier = 9; goto default; case 0x0020: dskType = MediaType.DDCD; goto default; case 0x0021: dskType = MediaType.DDCDR; goto default; case 0x0022: dskType = MediaType.DDCDRW; goto default; case 0x002A: dskType = MediaType.DVDPRWDL; _speedMultiplier = 9; goto default; case 0x002B: dskType = MediaType.DVDPRDL; _speedMultiplier = 9; goto default; case 0x0040: dskType = MediaType.BDROM; _speedMultiplier = 30; goto default; case 0x0041: case 0x0042: dskType = MediaType.BDR; _speedMultiplier = 30; goto default; case 0x0043: dskType = MediaType.BDRE; _speedMultiplier = 30; goto default; case 0x0050: dskType = MediaType.HDDVDROM; _speedMultiplier = 30; goto default; case 0x0051: dskType = MediaType.HDDVDR; _speedMultiplier = 30; goto default; case 0x0052: dskType = MediaType.HDDVDRAM; _speedMultiplier = 30; goto default; case 0x0053: dskType = MediaType.HDDVDRW; _speedMultiplier = 30; goto default; case 0x0058: dskType = MediaType.HDDVDRDL; _speedMultiplier = 30; goto default; case 0x005A: dskType = MediaType.HDDVDRWDL; _speedMultiplier = 30; goto default; default: compactDisc = false; break; } } Modes.DecodedMode?decMode = null; sense = _dev.ModeSense6(out cmdBuf, out _, true, ScsiModeSensePageControl.Current, 0x00, _dev.Timeout, out _); if (sense || _dev.Error) { sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x00, _dev.Timeout, out _); if (!sense && !_dev.Error) { decMode = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); } } else { decMode = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); } if (decMode is null) { sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, _dev.Timeout, out _); if (sense || _dev.Error) { sense = _dev.ModeSense10(out cmdBuf, out _, false, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, _dev.Timeout, out _); if (sense || _dev.Error) { sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x00, 0x00, _dev.Timeout, out _); if (sense || _dev.Error) { sense = _dev.ModeSense10(out cmdBuf, out _, false, false, ScsiModeSensePageControl.Current, 0x00, 0x00, _dev.Timeout, out _); if (!sense && !_dev.Error) { decMode = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); } } else { decMode = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); } } else { decMode = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); } } else { decMode = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); } } if (decMode.HasValue && _dev.IsUsb && !gotConfiguration && (decMode.Value.Header.MediumType == MediumTypes.UnknownBlockDevice || decMode.Value.Header.MediumType == MediumTypes.ReadOnlyBlockDevice || decMode.Value.Header.MediumType == MediumTypes.ReadWriteBlockDevice)) { _speedMultiplier = -1; Sbc(null, MediaType.Unknown, false); return; } if (compactDisc) { _speedMultiplier *= 177; CompactDisc(); return; } _speedMultiplier *= 150; var scsiReader = new Reader(_dev, _dev.Timeout, null, _errorLog, _dumpRaw); ulong blocks = scsiReader.GetDeviceBlocks(); _dumpLog.WriteLine("Device reports disc has {0} blocks", blocks); Dictionary <MediaTagType, byte[]> mediaTags = new Dictionary <MediaTagType, byte[]>(); if (dskType == MediaType.PD650) { switch (blocks + 1) { case 1281856: dskType = MediaType.PD650_WORM; break; case 58620544: dskType = MediaType.REV120; break; case 17090880: dskType = MediaType.REV35; break; case 34185728: dskType = MediaType.REV70; break; } } #region Nintendo switch (dskType) { case MediaType.Unknown when blocks > 0: _dumpLog.WriteLine("Reading Physical Format Information"); sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, MmcDiscStructureFormat.PhysicalInformation, 0, _dev.Timeout, out _); if (!sense) { PFI.PhysicalFormatInformation?nintendoPfi = PFI.Decode(cmdBuf); if (nintendoPfi?.DiskCategory == DiskCategory.Nintendo && nintendoPfi.Value.PartVersion == 15) { _dumpLog.WriteLine("Dumping Nintendo GameCube or Wii discs is not yet implemented."); StoppingErrorMessage?. Invoke("Dumping Nintendo GameCube or Wii discs is not yet implemented."); return; } } break; case MediaType.DVDDownload: case MediaType.DVDPR: case MediaType.DVDPRDL: case MediaType.DVDPRW: case MediaType.DVDPRWDL: case MediaType.DVDR: case MediaType.DVDRAM: case MediaType.DVDRDL: case MediaType.DVDROM: case MediaType.DVDRW: case MediaType.DVDRWDL: case MediaType.HDDVDR: case MediaType.HDDVDRAM: case MediaType.HDDVDRDL: case MediaType.HDDVDROM: case MediaType.HDDVDRW: case MediaType.HDDVDRWDL: _dumpLog.WriteLine("Reading Physical Format Information"); sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, MmcDiscStructureFormat.PhysicalInformation, 0, _dev.Timeout, out _); if (!sense) { if (PFI.Decode(cmdBuf).HasValue) { tmpBuf = new byte[cmdBuf.Length - 4]; Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); mediaTags.Add(MediaTagType.DVD_PFI, tmpBuf); PFI.PhysicalFormatInformation decPfi = PFI.Decode(cmdBuf).Value; UpdateStatus?.Invoke($"PFI:\n{PFI.Prettify(decPfi)}"); // False book types switch (decPfi.DiskCategory) { case DiskCategory.DVDPR: dskType = MediaType.DVDPR; break; case DiskCategory.DVDPRDL: dskType = MediaType.DVDPRDL; break; case DiskCategory.DVDPRW: dskType = MediaType.DVDPRW; break; case DiskCategory.DVDPRWDL: dskType = MediaType.DVDPRWDL; break; case DiskCategory.DVDR: dskType = decPfi.PartVersion >= 6 ? MediaType.DVDRDL : MediaType.DVDR; break; case DiskCategory.DVDRAM: dskType = MediaType.DVDRAM; break; default: dskType = MediaType.DVDROM; break; case DiskCategory.DVDRW: dskType = decPfi.PartVersion >= 15 ? MediaType.DVDRWDL : MediaType.DVDRW; break; case DiskCategory.HDDVDR: dskType = MediaType.HDDVDR; break; case DiskCategory.HDDVDRAM: dskType = MediaType.HDDVDRAM; break; case DiskCategory.HDDVDROM: dskType = MediaType.HDDVDROM; break; case DiskCategory.HDDVDRW: dskType = MediaType.HDDVDRW; break; case DiskCategory.Nintendo: dskType = decPfi.DiscSize == DVDSize.Eighty ? MediaType.GOD : MediaType.WOD; break; case DiskCategory.UMD: dskType = MediaType.UMD; break; } } } _dumpLog.WriteLine("Reading Disc Manufacturing Information"); sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, MmcDiscStructureFormat.DiscManufacturingInformation, 0, _dev.Timeout, out _); if (!sense) { if (DMI.IsXbox(cmdBuf) || DMI.IsXbox360(cmdBuf)) { if (DMI.IsXbox(cmdBuf)) { dskType = MediaType.XGD; } else if (DMI.IsXbox360(cmdBuf)) { dskType = MediaType.XGD2; // All XGD3 all have the same number of blocks if (blocks + 1 == 25063 || // Locked (or non compatible drive) blocks + 1 == 4229664 || // Xtreme unlock blocks + 1 == 4246304) // Wxripper unlock { dskType = MediaType.XGD3; } } isXbox = true; sense = _dev.ScsiInquiry(out byte[] inqBuf, out _); if (sense || !Inquiry.Decode(inqBuf).HasValue || (Inquiry.Decode(inqBuf).HasValue&& !Inquiry.Decode(inqBuf).Value.KreonPresent)) { _dumpLog.WriteLine("Dumping Xbox Game Discs requires a drive with Kreon firmware."); StoppingErrorMessage?. Invoke("Dumping Xbox Game Discs requires a drive with Kreon firmware."); if (!_force) { return; } isXbox = false; } if (_dumpRaw && !_force) { StoppingErrorMessage?. Invoke("Not continuing. If you want to continue reading cooked data when raw is not available use the force option."); // TODO: Exit more gracefully return; } } if (cmdBuf.Length == 2052) { tmpBuf = new byte[cmdBuf.Length - 4]; Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); mediaTags.Add(MediaTagType.DVD_DMI, tmpBuf); } } break; } #endregion Nintendo #region All DVD and HD DVD types #endregion All DVD and HD DVD types #region DVD-ROM if (dskType == MediaType.DVDDownload || dskType == MediaType.DVDROM) { _dumpLog.WriteLine("Reading Lead-in Copyright Information."); sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, MmcDiscStructureFormat.CopyrightInformation, 0, _dev.Timeout, out _); if (!sense) { if (CSS_CPRM.DecodeLeadInCopyright(cmdBuf).HasValue) { tmpBuf = new byte[cmdBuf.Length - 4]; Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); mediaTags.Add(MediaTagType.DVD_CMI, tmpBuf); CSS_CPRM.LeadInCopyright?cmi = CSS_CPRM.DecodeLeadInCopyright(cmdBuf); if (cmi !.Value.CopyrightType == CopyrightType.NoProtection) { UpdateStatus?.Invoke("Drive reports no copy protection on disc."); } else { if (!Settings.Settings.Current.EnableDecryption) { UpdateStatus?.Invoke("Drive reports the disc uses copy protection. " + "The dump will be incorrect unless decryption is enabled."); } else { if (cmi !.Value.CopyrightType == CopyrightType.CSS) { UpdateStatus?.Invoke("Drive reports disc uses CSS copy protection."); dvdDecrypt = new DVDDecryption(_dev); sense = dvdDecrypt.ReadBusKey(out cmdBuf, out _, CSS_CPRM.DecodeLeadInCopyright(cmdBuf) !.Value. CopyrightType, _dev.Timeout, out _); if (!sense) { byte[] busKey = cmdBuf; UpdateStatus?.Invoke("Reading disc key."); sense = dvdDecrypt.ReadDiscKey(out cmdBuf, out _, _dev.Timeout, out _); if (!sense) { CSS_CPRM.DiscKey?decodedDiscKey = CSS.DecodeDiscKey(cmdBuf, busKey); sense = dvdDecrypt.ReadAsf(out cmdBuf, out _, DvdCssKeyClass.DvdCssCppmOrCprm, _dev.Timeout, out _); if (!sense) { if (cmdBuf[7] == 1) { UpdateStatus?.Invoke("Disc and drive authentication succeeded."); sense = dvdDecrypt.ReadRpc(out cmdBuf, out _, DvdCssKeyClass.DvdCssCppmOrCprm, _dev.Timeout, out _); if (!sense) { CSS_CPRM.RegionalPlaybackControlState?rpc = CSS_CPRM.DecodeRegionalPlaybackControlState(cmdBuf); if (rpc.HasValue) { UpdateStatus?.Invoke(CSS.CheckRegion(rpc.Value, cmi.Value) ? "Disc and drive regions match." : "Disc and drive regions do not match. The dump will be incorrect"); } } if (decodedDiscKey.HasValue) { mediaTags.Add(MediaTagType.DVD_DiscKey, decodedDiscKey.Value.Key); UpdateStatus?.Invoke("Decrypting disc key."); CSS.DecryptDiscKey(decodedDiscKey.Value.Key, out byte[] discKey); if (discKey != null) { UpdateStatus?.Invoke("Decryption of disc key succeeded."); mediaTags.Add(MediaTagType.DVD_DiscKey_Decrypted, discKey); } else { UpdateStatus?.Invoke("Decryption of disc key failed."); } } } } } } } else { UpdateStatus?. Invoke($"Drive reports disc uses {CSS_CPRM.DecodeLeadInCopyright(cmdBuf)!.Value.CopyrightType.ToString()} copy protection. " + "This is not yet supported and the dump will be incorrect."); } } }
/// <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(); }
/// <summary>Dumps a CFW PlayStation Portable UMD</summary> void PlayStationPortable() { if (!_outputPlugin.SupportedMediaTypes.Contains(MediaType.MemoryStickDuo) && !_outputPlugin.SupportedMediaTypes.Contains(MediaType.MemoryStickProDuo) && !_outputPlugin.SupportedMediaTypes.Contains(MediaType.UMD)) { _dumpLog.WriteLine("Selected output plugin does not support MemoryStick Duo or UMD, cannot dump..."); StoppingErrorMessage?. Invoke("Selected output plugin does not support MemoryStick Duo or UMD, cannot dump..."); return; } UpdateStatus?.Invoke("Checking if media is UMD or MemoryStick..."); _dumpLog.WriteLine("Checking if media is UMD or MemoryStick..."); bool sense = _dev.ModeSense6(out byte[] buffer, out _, false, ScsiModeSensePageControl.Current, 0, _dev.Timeout, out _); if (sense) { _dumpLog.WriteLine("Could not get MODE SENSE..."); StoppingErrorMessage?.Invoke("Could not get MODE SENSE..."); return; } Modes.DecodedMode?decoded = Modes.DecodeMode6(buffer, PeripheralDeviceTypes.DirectAccess); if (!decoded.HasValue) { _dumpLog.WriteLine("Could not decode MODE SENSE..."); StoppingErrorMessage?.Invoke("Could not decode MODE SENSE..."); return; } // UMDs are always write protected if (!decoded.Value.Header.WriteProtected) { DumpMs(); return; } sense = _dev.Read12(out buffer, out _, 0, false, true, false, false, 0, 512, 0, 1, false, _dev.Timeout, out _); if (sense) { _dumpLog.WriteLine("Could not read..."); StoppingErrorMessage?.Invoke("Could not read..."); return; } byte[] tmp = new byte[8]; Array.Copy(buffer, 0x36, tmp, 0, 8); // UMDs are stored inside a FAT16 volume if (!tmp.SequenceEqual(_fatSignature)) { DumpMs(); return; } ushort fatStart = (ushort)((buffer[0x0F] << 8) + buffer[0x0E]); ushort sectorsPerFat = (ushort)((buffer[0x17] << 8) + buffer[0x16]); ushort rootStart = (ushort)((sectorsPerFat * 2) + fatStart); UpdateStatus?.Invoke($"Reading root directory in sector {rootStart}..."); _dumpLog.WriteLine("Reading root directory in sector {0}...", rootStart); sense = _dev.Read12(out buffer, out _, 0, false, true, false, false, rootStart, 512, 0, 1, false, _dev.Timeout, out _); if (sense) { StoppingErrorMessage?.Invoke("Could not read..."); _dumpLog.WriteLine("Could not read..."); return; } tmp = new byte[3]; Array.Copy(buffer, 0x28, tmp, 0, 3); if (!tmp.SequenceEqual(_isoExtension)) { DumpMs(); return; } UpdateStatus?.Invoke($"FAT starts at sector {fatStart} and runs for {sectorsPerFat} sectors..."); _dumpLog.WriteLine("FAT starts at sector {0} and runs for {1} sectors...", fatStart, sectorsPerFat); UpdateStatus?.Invoke("Reading FAT..."); _dumpLog.WriteLine("Reading FAT..."); byte[] fat = new byte[sectorsPerFat * 512]; uint position = 0; while (position < sectorsPerFat) { uint transfer = 64; if (transfer + position > sectorsPerFat) { transfer = sectorsPerFat - position; } sense = _dev.Read12(out buffer, out _, 0, false, true, false, false, position + fatStart, 512, 0, transfer, false, _dev.Timeout, out _); if (sense) { StoppingErrorMessage?.Invoke("Could not read..."); _dumpLog.WriteLine("Could not read..."); return; } Array.Copy(buffer, 0, fat, position * 512, transfer * 512); position += transfer; } UpdateStatus?.Invoke("Traversing FAT..."); _dumpLog.WriteLine("Traversing FAT..."); ushort previousCluster = BitConverter.ToUInt16(fat, 4); for (int i = 3; i < fat.Length / 2; i++) { ushort nextCluster = BitConverter.ToUInt16(fat, i * 2); if (nextCluster == previousCluster + 1) { previousCluster = nextCluster; continue; } if (nextCluster == 0xFFFF) { break; } DumpMs(); return; } if (_outputPlugin is IWritableOpticalImage) { DumpUmd(); } else { StoppingErrorMessage?.Invoke("The specified plugin does not support storing optical disc images."); } }
/// <summary>Dumps a MiniDisc Data device</summary> internal void MiniDisc() { bool sense; byte scsiMediumType = 0; const ushort sbcProfile = 0x0001; DateTime start; DateTime end; double totalDuration = 0; double currentSpeed = 0; double maxSpeed = double.MinValue; double minSpeed = double.MaxValue; byte[] readBuffer; Modes.DecodedMode?decMode = null; Dictionary <MediaTagType, byte[]> mediaTags = new Dictionary <MediaTagType, byte[]>(); byte[] cmdBuf; bool ret; _dumpLog.WriteLine("Initializing reader."); var scsiReader = new Reader(_dev, _dev.Timeout, null); ulong blocks = scsiReader.GetDeviceBlocks(); uint blockSize = scsiReader.LogicalBlockSize; _dumpLog.WriteLine("Requesting MODE SENSE (6)."); UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); sense = _dev.ModeSense6(out cmdBuf, out _, true, ScsiModeSensePageControl.Current, 0x3F, 5, out _); if (!sense && !_dev.Error && Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue) { decMode = Modes.DecodeMode6(cmdBuf, _dev.ScsiType); } if (decMode.HasValue) { scsiMediumType = (byte)decMode.Value.Header.MediumType; } if (blockSize != 2048) { _dumpLog.WriteLine("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped."); StoppingErrorMessage?. Invoke("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped."); return; } MediaType dskType = MediaType.MDData; if (scsiReader.FindReadCommand()) { _dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", scsiReader.ErrorMessage); StoppingErrorMessage?.Invoke("Unable to read medium."); return; } if (blocks != 0 && blockSize != 0) { blocks++; UpdateStatus?. Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {blocks * (ulong)blockSize} bytes)"); } // Check how many blocks to read, if error show and return // 64 works, gets maximum speed (150KiB/s), slow I know... if (scsiReader.GetBlocksToRead()) { _dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", scsiReader.ErrorMessage); StoppingErrorMessage?.Invoke(scsiReader.ErrorMessage); return; } uint blocksToRead = scsiReader.BlocksToRead; uint logicalBlockSize = blockSize; uint physicalBlockSize = scsiReader.PhysicalBlockSize; if (blocks == 0) { _dumpLog.WriteLine("ERROR: Unable to read medium or empty medium present..."); StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present..."); return; } UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); UpdateStatus?.Invoke($"Device reports {scsiReader.LongBlockSize} bytes per physical block."); UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); UpdateStatus?.Invoke($"SCSI medium type: {scsiMediumType}."); UpdateStatus?.Invoke($"Media identified as {dskType}"); _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize); _dumpLog.WriteLine("Device reports {0} bytes per physical block.", scsiReader.LongBlockSize); _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType); _dumpLog.WriteLine("SCSI medium type: {0}.", scsiMediumType); _dumpLog.WriteLine("Media identified as {0}.", dskType); sense = _dev.MiniDiscGetType(out cmdBuf, out _, _dev.Timeout, out _); if (!sense && !_dev.Error) { mediaTags.Add(MediaTagType.MiniDiscType, cmdBuf); } sense = _dev.MiniDiscD5(out cmdBuf, out _, _dev.Timeout, out _); if (!sense && !_dev.Error) { mediaTags.Add(MediaTagType.MiniDiscD5, cmdBuf); } sense = _dev.MiniDiscReadDataTOC(out cmdBuf, out _, _dev.Timeout, out _); if (!sense && !_dev.Error) { mediaTags.Add(MediaTagType.MiniDiscDTOC, cmdBuf); } var utocMs = new MemoryStream(); for (uint i = 0; i < 3; i++) { sense = _dev.MiniDiscReadUserTOC(out cmdBuf, out _, i, _dev.Timeout, out _); if (sense || _dev.Error) { break; } utocMs.Write(cmdBuf, 0, 2336); } if (utocMs.Length > 0) { mediaTags.Add(MediaTagType.MiniDiscUTOC, utocMs.ToArray()); } ret = true; foreach (MediaTagType tag in mediaTags.Keys) { if (_outputPlugin.SupportedMediaTags.Contains(tag)) { continue; } ret = false; _dumpLog.WriteLine($"Output format does not support {tag}."); ErrorMessage?.Invoke($"Output format does not support {tag}."); } if (!ret) { if (_force) { _dumpLog.WriteLine("Several media tags not supported, continuing..."); ErrorMessage?.Invoke("Several media tags not supported, continuing..."); } else { _dumpLog.WriteLine("Several media tags not supported, not continuing..."); StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); return; } } UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); _dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead); var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private); var ibgLog = new IbgLog(_outputPrefix + ".ibg", sbcProfile); ret = _outputPlugin.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); // Cannot create image if (!ret) { _dumpLog.WriteLine("Error creating output image, not continuing."); _dumpLog.WriteLine(_outputPlugin.ErrorMessage); StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + _outputPlugin.ErrorMessage); return; } start = DateTime.UtcNow; double imageWriteDuration = 0; if (decMode?.Pages != null) { bool setGeometry = false; foreach (Modes.ModePage page in decMode.Value.Pages) { if (page.Page == 0x04 && page.Subpage == 0x00) { Modes.ModePage_04?rigidPage = Modes.DecodeModePage_04(page.PageResponse); if (!rigidPage.HasValue || setGeometry) { continue; } _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track", rigidPage.Value.Cylinders, rigidPage.Value.Heads, (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))); UpdateStatus?. Invoke($"Setting geometry to {rigidPage.Value.Cylinders} cylinders, {rigidPage.Value.Heads} heads, {(uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))} sectors per track"); _outputPlugin.SetGeometry(rigidPage.Value.Cylinders, rigidPage.Value.Heads, (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))); setGeometry = true; } else if (page.Page == 0x05 && page.Subpage == 0x00) { Modes.ModePage_05?flexiblePage = Modes.DecodeModePage_05(page.PageResponse); if (!flexiblePage.HasValue) { continue; } _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track", flexiblePage.Value.Cylinders, flexiblePage.Value.Heads, flexiblePage.Value.SectorsPerTrack); UpdateStatus?. Invoke($"Setting geometry to {flexiblePage.Value.Cylinders} cylinders, {flexiblePage.Value.Heads} heads, {flexiblePage.Value.SectorsPerTrack} sectors per track"); _outputPlugin.SetGeometry(flexiblePage.Value.Cylinders, flexiblePage.Value.Heads, flexiblePage.Value.SectorsPerTrack); setGeometry = true; } } } DumpHardwareType currentTry = null; ExtentsULong extents = null; ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, _private); if (currentTry == null || extents == null) { StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); return; } if (_resume.NextBlock > 0) { UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}."); _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); } bool newTrim = false; DateTime timeSpeedStart = DateTime.UtcNow; ulong sectorSpeedStart = 0; InitProgress?.Invoke(); for (ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) { if (_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); _dumpLog.WriteLine("Aborted!"); break; } if (blocks - i < blocksToRead) { blocksToRead = (uint)(blocks - i); } #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 UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, (long)blocks); sense = _dev.Read6(out readBuffer, out _, (uint)i, blockSize, (byte)blocksToRead, _dev.Timeout, out double cmdDuration); totalDuration += cmdDuration; if (!sense && !_dev.Error) { mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); DateTime writeStart = DateTime.Now; _outputPlugin.WriteSectors(readBuffer, i, blocksToRead); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; extents.Add(i, blocksToRead, true); } else { // 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; _outputPlugin.WriteSectors(new byte[blockSize * _skip], i, _skip); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; for (ulong b = i; b < i + _skip; b++) { _resume.BadBlocks.Add(b); } 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; } end = DateTime.UtcNow; EndProgress?.Invoke(); mhddLog.Close(); ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000), _devicePath); UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); UpdateStatus?. Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec."); UpdateStatus?. Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec."); _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 write speed {0:F3} KiB/sec.", ((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration); #region Trimming if (_resume.BadBlocks.Count > 0 && !_aborted && _trim && newTrim) { start = DateTime.UtcNow; UpdateStatus?.Invoke("Trimming skipped sectors"); _dumpLog.WriteLine("Trimming skipped sectors"); ulong[] tmpArray = _resume.BadBlocks.ToArray(); InitProgress?.Invoke(); foreach (ulong badSector in tmpArray) { if (_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); _dumpLog.WriteLine("Aborted!"); break; } PulseProgress?.Invoke($"Trimming sector {badSector}"); sense = _dev.Read6(out readBuffer, out _, (uint)badSector, blockSize, 1, _dev.Timeout, out double cmdDuration); if (sense || _dev.Error) { continue; } _resume.BadBlocks.Remove(badSector); extents.Add(badSector); _outputPlugin.WriteSector(readBuffer, badSector); } EndProgress?.Invoke(); end = DateTime.UtcNow; UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds."); _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); } #endregion Trimming #region Error handling if (_resume.BadBlocks.Count > 0 && !_aborted && _retryPasses > 0) { int pass = 1; bool forward = true; bool runningPersistent = false; Modes.ModePage?currentModePage = null; byte[] md6; byte[] md10; if (_persistent) { Modes.ModePage_01_MMC pgMmc; Modes.ModePage_01 pg; sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout, out _); if (!sense) { Modes.DecodedMode?dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType); if (dcMode6.HasValue && dcMode6.Value.Pages != null) { foreach (Modes.ModePage modePage in dcMode6.Value.Pages) { if (modePage.Page == 0x01 && modePage.Subpage == 0x00) { currentModePage = modePage; } } } } if (currentModePage == null) { pg = new Modes.ModePage_01 { PS = false, AWRE = true, ARRE = true, TB = false, RC = false, EER = true, PER = false, DTE = true, DCR = false, ReadRetryCount = 32 }; currentModePage = new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg) }; } pg = new Modes.ModePage_01 { PS = false, AWRE = false, ARRE = false, TB = true, RC = false, EER = true, PER = false, DTE = false, DCR = false, ReadRetryCount = 255 }; var md = new Modes.DecodedMode { Header = new Modes.ModeHeader(), Pages = new[] { new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg) } } }; md6 = Modes.EncodeMode6(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 byte[] 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(); repeatRetry: ulong[] tmpArray = _resume.BadBlocks.ToArray(); foreach (ulong badSector in tmpArray) { if (_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); _dumpLog.WriteLine("Aborted!"); break; } PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, forward ? "forward" : "reverse", runningPersistent ? "recovering partial data, " : "")); sense = _dev.Read6(out readBuffer, out _, (uint)badSector, blockSize, 1, _dev.Timeout, out double cmdDuration); totalDuration += cmdDuration; if (!sense && !_dev.Error) { _resume.BadBlocks.Remove(badSector); extents.Add(badSector); _outputPlugin.WriteSector(readBuffer, badSector); UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } else if (runningPersistent) { _outputPlugin.WriteSector(readBuffer, badSector); } } if (pass < _retryPasses && !_aborted && _resume.BadBlocks.Count > 0) { pass++; forward = !forward; _resume.BadBlocks.Sort(); if (!forward) { _resume.BadBlocks.Reverse(); } goto repeatRetry; } if (runningPersistent && currentModePage.HasValue) { var md = new Modes.DecodedMode { Header = new Modes.ModeHeader(), Pages = new[] { currentModePage.Value } }; md6 = Modes.EncodeMode6(md, _dev.ScsiType); UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status)."); _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _); } EndProgress?.Invoke(); } #endregion Error handling _resume.BadBlocks.Sort(); foreach (ulong bad in _resume.BadBlocks) { _dumpLog.WriteLine("Sector {0} could not be read.", bad); } currentTry.Extents = ExtentsConverter.ToMetadata(extents); _outputPlugin.SetDumpHardware(_resume.Tries); var metadata = new CommonTypes.Structs.ImageInfo { Application = "Aaru", ApplicationVersion = Version.GetVersion() }; if (!_outputPlugin.SetMetadata(metadata)) { ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + _outputPlugin.ErrorMessage); } if (_preSidecar != null) { _outputPlugin.SetCicmMetadata(_preSidecar); } _dumpLog.WriteLine("Closing output file."); UpdateStatus?.Invoke("Closing output file."); DateTime closeStart = DateTime.Now; _outputPlugin.Close(); DateTime closeEnd = DateTime.Now; UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); if (_aborted) { UpdateStatus?.Invoke("Aborted!"); _dumpLog.WriteLine("Aborted!"); return; } double totalChkDuration = 0; if (_metadata) { UpdateStatus?.Invoke("Creating sidecar."); _dumpLog.WriteLine("Creating sidecar."); var filters = new FiltersList(); IFilter filter = filters.GetFilter(_outputPath); IMediaImage inputPlugin = ImageFormat.Detect(filter); if (!inputPlugin.Open(filter)) { StoppingErrorMessage?.Invoke("Could not open created image."); return; } DateTime chkStart = DateTime.UtcNow; _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); _sidecarClass.InitProgressEvent += InitProgress; _sidecarClass.UpdateProgressEvent += UpdateProgress; _sidecarClass.EndProgressEvent += EndProgress; _sidecarClass.InitProgressEvent2 += InitProgress2; _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; _sidecarClass.EndProgressEvent2 += EndProgress2; _sidecarClass.UpdateStatusEvent += UpdateStatus; CICMMetadataType sidecar = _sidecarClass.Create(); end = DateTime.UtcNow; totalChkDuration = (end - chkStart).TotalMilliseconds; UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds."); UpdateStatus?. Invoke($"Average checksum speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", ((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000)); if (_preSidecar != null) { _preSidecar.BlockMedia = sidecar.BlockMedia; sidecar = _preSidecar; } List <(ulong start, string type)> filesystems = new List <(ulong start, string type)>(); if (sidecar.BlockMedia[0].FileSystemInformation != null) { filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation where partition.FileSystems != null from fileSystem in partition.FileSystems select(partition.StartSector, fileSystem.Type)); } if (filesystems.Count > 0) { foreach (var filesystem in filesystems.Select(o => new { o.start, o.type }).Distinct()) { UpdateStatus?.Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}"); _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start); } } sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); (string type, string subType)xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); sidecar.BlockMedia[0].DiskType = xmlType.type; sidecar.BlockMedia[0].DiskSubType = xmlType.subType; if (!_dev.IsRemovable || _dev.IsUsb) { if (_dev.Type == DeviceType.ATAPI) { sidecar.BlockMedia[0].Interface = "ATAPI"; } else if (_dev.IsUsb) { sidecar.BlockMedia[0].Interface = "USB"; } else if (_dev.IsFireWire) { sidecar.BlockMedia[0].Interface = "FireWire"; } else { sidecar.BlockMedia[0].Interface = "SCSI"; } } sidecar.BlockMedia[0].LogicalBlocks = blocks; sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize; sidecar.BlockMedia[0].LogicalBlockSize = logicalBlockSize; sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; sidecar.BlockMedia[0].Model = _dev.Model; if (!_private) { sidecar.BlockMedia[0].Serial = _dev.Serial; } sidecar.BlockMedia[0].Size = blocks * blockSize; if (_dev.IsRemovable) { sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray(); } UpdateStatus?.Invoke("Writing metadata sidecar"); var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); xmlSer.Serialize(xmlFs, sidecar); xmlFs.Close(); } UpdateStatus?.Invoke(""); UpdateStatus?. Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); UpdateStatus?. Invoke($"Average speed: {((double)blockSize * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); if (maxSpeed > 0) { UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); } if (minSpeed > 0 && minSpeed < double.MaxValue) { UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); } UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); UpdateStatus?.Invoke(""); Statistics.AddMedia(dskType, true); }
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); }
public static void PrintImageInfo(IMediaImage imageFormat) { DicConsole.WriteLine("Image information:"); if (!string.IsNullOrWhiteSpace(imageFormat.Info.Version)) { DicConsole.WriteLine("Format: {0} version {1}", imageFormat.Format, imageFormat.Info.Version); } else { DicConsole.WriteLine("Format: {0}", imageFormat.Format); } if (!string.IsNullOrWhiteSpace(imageFormat.Info.Application) && !string.IsNullOrWhiteSpace(imageFormat.Info.ApplicationVersion)) { DicConsole.WriteLine("Was created with {0} version {1}", imageFormat.Info.Application, imageFormat.Info.ApplicationVersion); } else if (!string.IsNullOrWhiteSpace(imageFormat.Info.Application)) { DicConsole.WriteLine("Was created with {0}", imageFormat.Info.Application); } DicConsole.WriteLine("Image without headers is {0} bytes long", imageFormat.Info.ImageSize); DicConsole.WriteLine("Contains a media of {0} sectors with a maximum sector size of {1} bytes (if all sectors are of the same size this would be {2} bytes)", imageFormat.Info.Sectors, imageFormat.Info.SectorSize, imageFormat.Info.Sectors * imageFormat.Info.SectorSize); if (!string.IsNullOrWhiteSpace(imageFormat.Info.Creator)) { DicConsole.WriteLine("Created by: {0}", imageFormat.Info.Creator); } if (imageFormat.Info.CreationTime != DateTime.MinValue) { DicConsole.WriteLine("Created on {0}", imageFormat.Info.CreationTime); } if (imageFormat.Info.LastModificationTime != DateTime.MinValue) { DicConsole.WriteLine("Last modified on {0}", imageFormat.Info.LastModificationTime); } DicConsole.WriteLine("Contains a media of type {0} and XML type {1}", imageFormat.Info.MediaType, imageFormat.Info.XmlMediaType); DicConsole.WriteLine("{0} partitions", imageFormat.Info.HasPartitions ? "Has" : "Doesn't have"); DicConsole.WriteLine("{0} sessions", imageFormat.Info.HasSessions ? "Has" : "Doesn't have"); if (!string.IsNullOrWhiteSpace(imageFormat.Info.Comments)) { DicConsole.WriteLine("Comments: {0}", imageFormat.Info.Comments); } if (imageFormat.Info.MediaSequence != 0 && imageFormat.Info.LastMediaSequence != 0) { DicConsole.WriteLine("Media is number {0} on a set of {1} medias", imageFormat.Info.MediaSequence, imageFormat.Info.LastMediaSequence); } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaTitle)) { DicConsole.WriteLine("Media title: {0}", imageFormat.Info.MediaTitle); } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaManufacturer)) { DicConsole.WriteLine("Media manufacturer: {0}", imageFormat.Info.MediaManufacturer); } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaModel)) { DicConsole.WriteLine("Media model: {0}", imageFormat.Info.MediaModel); } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaSerialNumber)) { DicConsole.WriteLine("Media serial number: {0}", imageFormat.Info.MediaSerialNumber); } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaBarcode)) { DicConsole.WriteLine("Media barcode: {0}", imageFormat.Info.MediaBarcode); } if (!string.IsNullOrWhiteSpace(imageFormat.Info.MediaPartNumber)) { DicConsole.WriteLine("Media part number: {0}", imageFormat.Info.MediaPartNumber); } if (!string.IsNullOrWhiteSpace(imageFormat.Info.DriveManufacturer)) { DicConsole.WriteLine("Drive manufacturer: {0}", imageFormat.Info.DriveManufacturer); } if (!string.IsNullOrWhiteSpace(imageFormat.Info.DriveModel)) { DicConsole.WriteLine("Drive model: {0}", imageFormat.Info.DriveModel); } if (!string.IsNullOrWhiteSpace(imageFormat.Info.DriveSerialNumber)) { DicConsole.WriteLine("Drive serial number: {0}", imageFormat.Info.DriveSerialNumber); } if (!string.IsNullOrWhiteSpace(imageFormat.Info.DriveFirmwareRevision)) { DicConsole.WriteLine("Drive firmware info: {0}", imageFormat.Info.DriveFirmwareRevision); } if (imageFormat.Info.Cylinders > 0 && imageFormat.Info.Heads > 0 && imageFormat.Info.SectorsPerTrack > 0 && imageFormat.Info.XmlMediaType != XmlMediaType.OpticalDisc) { DicConsole.WriteLine("Media geometry: {0} cylinders, {1} heads, {2} sectors per track", imageFormat.Info.Cylinders, imageFormat.Info.Heads, imageFormat.Info.SectorsPerTrack); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Count > 0) { DicConsole.WriteLine("Contains {0} readable media tags:", imageFormat.Info.ReadableMediaTags.Count); foreach (MediaTagType tag in imageFormat.Info.ReadableMediaTags.OrderBy(t => t)) { DicConsole.Write("{0} ", tag); } DicConsole.WriteLine(); } if (imageFormat.Info.ReadableSectorTags != null && imageFormat.Info.ReadableSectorTags.Count > 0) { DicConsole.WriteLine("Contains {0} readable sector tags:", imageFormat.Info.ReadableSectorTags.Count); foreach (SectorTagType tag in imageFormat.Info.ReadableSectorTags.OrderBy(t => t)) { DicConsole.Write("{0} ", tag); } DicConsole.WriteLine(); } DicConsole.WriteLine(); PeripheralDeviceTypes scsiDeviceType = PeripheralDeviceTypes.DirectAccess; byte[] scsiVendorId = null; if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SCSI_INQUIRY)) { byte[] inquiry = imageFormat.ReadDiskTag(MediaTagType.SCSI_INQUIRY); scsiDeviceType = (PeripheralDeviceTypes)(inquiry[0] & 0x1F); if (inquiry.Length >= 16) { scsiVendorId = new byte[8]; Array.Copy(inquiry, 8, scsiVendorId, 0, 8); } DicConsole.WriteLine("SCSI INQUIRY contained in image:"); DicConsole.Write("{0}", Inquiry.Prettify(inquiry)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.ATA_IDENTIFY)) { byte[] identify = imageFormat.ReadDiskTag(MediaTagType.ATA_IDENTIFY); DicConsole.WriteLine("ATA IDENTIFY contained in image:"); DicConsole.Write("{0}", Identify.Prettify(identify)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.ATAPI_IDENTIFY)) { byte[] identify = imageFormat.ReadDiskTag(MediaTagType.ATAPI_IDENTIFY); DicConsole.WriteLine("ATAPI IDENTIFY contained in image:"); DicConsole.Write("{0}", Identify.Prettify(identify)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SCSI_MODESENSE_10)) { byte[] modeSense10 = imageFormat.ReadDiskTag(MediaTagType.SCSI_MODESENSE_10); Modes.DecodedMode?decMode = Modes.DecodeMode10(modeSense10, scsiDeviceType); if (decMode.HasValue) { DicConsole.WriteLine("SCSI MODE SENSE (10) contained in image:"); PrintScsiModePages.Print(decMode.Value, scsiDeviceType, scsiVendorId); DicConsole.WriteLine(); } } else if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SCSI_MODESENSE_6)) { byte[] modeSense6 = imageFormat.ReadDiskTag(MediaTagType.SCSI_MODESENSE_6); Modes.DecodedMode?decMode = Modes.DecodeMode6(modeSense6, scsiDeviceType); if (decMode.HasValue) { DicConsole.WriteLine("SCSI MODE SENSE (6) contained in image:"); PrintScsiModePages.Print(decMode.Value, scsiDeviceType, scsiVendorId); DicConsole.WriteLine(); } } else if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SCSI_MODEPAGE_2A)) { byte[] mode2A = imageFormat.ReadDiskTag(MediaTagType.SCSI_MODEPAGE_2A); DicConsole.Write("{0}", Modes.PrettifyModePage_2A(mode2A)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_FullTOC)) { byte[] toc = imageFormat.ReadDiskTag(MediaTagType.CD_FullTOC); if (toc.Length > 0) { ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(toc, 0)); if (dataLen + 2 != toc.Length) { byte[] tmp = new byte[toc.Length + 2]; Array.Copy(toc, 0, tmp, 2, toc.Length); tmp[0] = (byte)((toc.Length & 0xFF00) >> 8); tmp[1] = (byte)(toc.Length & 0xFF); toc = tmp; } DicConsole.WriteLine("CompactDisc Table of Contents contained in image:"); DicConsole.Write("{0}", FullTOC.Prettify(toc)); DicConsole.WriteLine(); } } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_PMA)) { byte[] pma = imageFormat.ReadDiskTag(MediaTagType.CD_PMA); if (pma.Length > 0) { ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(pma, 0)); if (dataLen + 2 != pma.Length) { byte[] tmp = new byte[pma.Length + 2]; Array.Copy(pma, 0, tmp, 2, pma.Length); tmp[0] = (byte)((pma.Length & 0xFF00) >> 8); tmp[1] = (byte)(pma.Length & 0xFF); pma = tmp; } DicConsole.WriteLine("CompactDisc Power Management Area contained in image:"); DicConsole.Write("{0}", PMA.Prettify(pma)); DicConsole.WriteLine(); } } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_ATIP)) { byte[] atip = imageFormat.ReadDiskTag(MediaTagType.CD_ATIP); uint dataLen = Swapping.Swap(BitConverter.ToUInt32(atip, 0)); if (dataLen + 4 != atip.Length) { byte[] tmp = new byte[atip.Length + 4]; Array.Copy(atip, 0, tmp, 4, atip.Length); tmp[0] = (byte)((atip.Length & 0xFF000000) >> 24); tmp[1] = (byte)((atip.Length & 0xFF0000) >> 16); tmp[2] = (byte)((atip.Length & 0xFF00) >> 8); tmp[3] = (byte)(atip.Length & 0xFF); atip = tmp; } DicConsole.WriteLine("CompactDisc Absolute Time In Pregroove (ATIP) contained in image:"); DicConsole.Write("{0}", ATIP.Prettify(atip)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_TEXT)) { byte[] cdtext = imageFormat.ReadDiskTag(MediaTagType.CD_TEXT); uint dataLen = Swapping.Swap(BitConverter.ToUInt32(cdtext, 0)); if (dataLen + 4 != cdtext.Length) { byte[] tmp = new byte[cdtext.Length + 4]; Array.Copy(cdtext, 0, tmp, 4, cdtext.Length); tmp[0] = (byte)((cdtext.Length & 0xFF000000) >> 24); tmp[1] = (byte)((cdtext.Length & 0xFF0000) >> 16); tmp[2] = (byte)((cdtext.Length & 0xFF00) >> 8); tmp[3] = (byte)(cdtext.Length & 0xFF); cdtext = tmp; } DicConsole.WriteLine("CompactDisc Lead-in's CD-Text contained in image:"); DicConsole.Write("{0}", CDTextOnLeadIn.Prettify(cdtext)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.CD_MCN)) { byte[] mcn = imageFormat.ReadDiskTag(MediaTagType.CD_MCN); DicConsole.WriteLine("CompactDisc Media Catalogue Number contained in image: {0}", Encoding.UTF8.GetString(mcn)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVD_PFI)) { byte[] pfi = imageFormat.ReadDiskTag(MediaTagType.DVD_PFI); DicConsole.WriteLine("DVD Physical Format Information contained in image:"); DicConsole.Write("{0}", PFI.Prettify(pfi)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDRAM_DDS)) { byte[] dds = imageFormat.ReadDiskTag(MediaTagType.DVDRAM_DDS); DicConsole.WriteLine("DVD-RAM Disc Definition Structure contained in image:"); DicConsole.Write("{0}", DDS.Prettify(dds)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.DVDR_PFI)) { byte[] pfi = imageFormat.ReadDiskTag(MediaTagType.DVDR_PFI); DicConsole.WriteLine("DVD-R Physical Format Information contained in image:"); DicConsole.Write("{0}", PFI.Prettify(pfi)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.BD_DI)) { byte[] di = imageFormat.ReadDiskTag(MediaTagType.BD_DI); DicConsole.WriteLine("Bluray Disc Information contained in image:"); DicConsole.Write("{0}", DI.Prettify(di)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.BD_DDS)) { byte[] dds = imageFormat.ReadDiskTag(MediaTagType.BD_DDS); DicConsole.WriteLine("Bluray Disc Definition Structure contained in image:"); DicConsole.Write("{0}", Decoders.Bluray.DDS.Prettify(dds)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.PCMCIA_CIS)) { byte[] cis = imageFormat.ReadDiskTag(MediaTagType.PCMCIA_CIS); DicConsole.WriteLine("PCMCIA CIS:"); Tuple[] tuples = CIS.GetTuples(cis); if (tuples != null) { foreach (Tuple tuple in tuples) { switch (tuple.Code) { case TupleCodes.CISTPL_NULL: case TupleCodes.CISTPL_END: break; case TupleCodes.CISTPL_DEVICEGEO: case TupleCodes.CISTPL_DEVICEGEO_A: DicConsole.WriteLine("{0}", CIS.PrettifyDeviceGeometryTuple(tuple)); break; case TupleCodes.CISTPL_MANFID: DicConsole.WriteLine("{0}", CIS.PrettifyManufacturerIdentificationTuple(tuple)); break; case TupleCodes.CISTPL_VERS_1: DicConsole.WriteLine("{0}", CIS.PrettifyLevel1VersionTuple(tuple)); break; case TupleCodes.CISTPL_ALTSTR: case TupleCodes.CISTPL_BAR: case TupleCodes.CISTPL_BATTERY: case TupleCodes.CISTPL_BYTEORDER: case TupleCodes.CISTPL_CFTABLE_ENTRY: case TupleCodes.CISTPL_CFTABLE_ENTRY_CB: case TupleCodes.CISTPL_CHECKSUM: case TupleCodes.CISTPL_CONFIG: case TupleCodes.CISTPL_CONFIG_CB: case TupleCodes.CISTPL_DATE: case TupleCodes.CISTPL_DEVICE: case TupleCodes.CISTPL_DEVICE_A: case TupleCodes.CISTPL_DEVICE_OA: case TupleCodes.CISTPL_DEVICE_OC: case TupleCodes.CISTPL_EXTDEVIC: case TupleCodes.CISTPL_FORMAT: case TupleCodes.CISTPL_FORMAT_A: case TupleCodes.CISTPL_FUNCE: case TupleCodes.CISTPL_FUNCID: case TupleCodes.CISTPL_GEOMETRY: case TupleCodes.CISTPL_INDIRECT: case TupleCodes.CISTPL_JEDEC_A: case TupleCodes.CISTPL_JEDEC_C: case TupleCodes.CISTPL_LINKTARGET: case TupleCodes.CISTPL_LONGLINK_A: case TupleCodes.CISTPL_LONGLINK_C: case TupleCodes.CISTPL_LONGLINK_CB: case TupleCodes.CISTPL_LONGLINK_MFC: case TupleCodes.CISTPL_NO_LINK: case TupleCodes.CISTPL_ORG: case TupleCodes.CISTPL_PWR_MGMNT: case TupleCodes.CISTPL_SPCL: case TupleCodes.CISTPL_SWIL: case TupleCodes.CISTPL_VERS_2: DicConsole.DebugWriteLine("Device-Info command", "Found undecoded tuple ID {0}", tuple.Code); break; default: DicConsole.DebugWriteLine("Device-Info command", "Found unknown tuple ID 0x{0:X2}", (byte)tuple.Code); break; } } } else { DicConsole.DebugWriteLine("Device-Info command", "Could not get tuples"); } } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SD_CID)) { byte[] cid = imageFormat.ReadDiskTag(MediaTagType.SD_CID); DicConsole.WriteLine("SecureDigital CID contained in image:"); DicConsole.Write("{0}", Decoders.SecureDigital.Decoders.PrettifyCID(cid)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SD_CSD)) { byte[] csd = imageFormat.ReadDiskTag(MediaTagType.SD_CSD); DicConsole.WriteLine("SecureDigital CSD contained in image:"); DicConsole.Write("{0}", Decoders.SecureDigital.Decoders.PrettifyCSD(csd)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SD_SCR)) { byte[] scr = imageFormat.ReadDiskTag(MediaTagType.SD_SCR); DicConsole.WriteLine("SecureDigital SCR contained in image:"); DicConsole.Write("{0}", Decoders.SecureDigital.Decoders.PrettifySCR(scr)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.SD_OCR)) { byte[] ocr = imageFormat.ReadDiskTag(MediaTagType.SD_OCR); DicConsole.WriteLine("SecureDigital OCR contained in image:"); DicConsole.Write("{0}", Decoders.SecureDigital.Decoders.PrettifyOCR(ocr)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_CID)) { byte[] cid = imageFormat.ReadDiskTag(MediaTagType.MMC_CID); DicConsole.WriteLine("MultiMediaCard CID contained in image:"); DicConsole.Write("{0}", Decoders.MMC.Decoders.PrettifyCID(cid)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_CSD)) { byte[] csd = imageFormat.ReadDiskTag(MediaTagType.MMC_CSD); DicConsole.WriteLine("MultiMediaCard CSD contained in image:"); DicConsole.Write("{0}", Decoders.MMC.Decoders.PrettifyCSD(csd)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_ExtendedCSD)) { byte[] ecsd = imageFormat.ReadDiskTag(MediaTagType.MMC_ExtendedCSD); DicConsole.WriteLine("MultiMediaCard ExtendedCSD contained in image:"); DicConsole.Write("{0}", Decoders.MMC.Decoders.PrettifyExtendedCSD(ecsd)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.MMC_OCR)) { byte[] ocr = imageFormat.ReadDiskTag(MediaTagType.MMC_OCR); DicConsole.WriteLine("MultiMediaCard OCR contained in image:"); DicConsole.Write("{0}", Decoders.MMC.Decoders.PrettifyOCR(ocr)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.Xbox_PFI)) { byte[] xpfi = imageFormat.ReadDiskTag(MediaTagType.Xbox_PFI); DicConsole.WriteLine("Xbox Physical Format Information contained in image:"); DicConsole.Write("{0}", PFI.Prettify(xpfi)); DicConsole.WriteLine(); } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.Xbox_DMI)) { byte[] xdmi = imageFormat.ReadDiskTag(MediaTagType.Xbox_DMI); if (DMI.IsXbox(xdmi)) { DMI.XboxDMI?xmi = DMI.DecodeXbox(xdmi); if (xmi.HasValue) { DicConsole.WriteLine("Xbox DMI contained in image:"); DicConsole.Write("{0}", DMI.PrettifyXbox(xmi)); DicConsole.WriteLine(); } } if (DMI.IsXbox360(xdmi)) { DMI.Xbox360DMI?xmi = DMI.DecodeXbox360(xdmi); if (xmi.HasValue) { DicConsole.WriteLine("Xbox 360 DMI contained in image:"); DicConsole.Write("{0}", DMI.PrettifyXbox360(xmi)); DicConsole.WriteLine(); } } } if (imageFormat.Info.ReadableMediaTags != null && imageFormat.Info.ReadableMediaTags.Contains(MediaTagType.Xbox_SecuritySector)) { byte[] toc = imageFormat.ReadDiskTag(MediaTagType.Xbox_SecuritySector); DicConsole.WriteLine("Xbox Security Sectors contained in image:"); DicConsole.Write("{0}", SS.Prettify(toc)); DicConsole.WriteLine(); } try { if (imageFormat.Sessions != null && imageFormat.Sessions.Count > 0) { DicConsole.WriteLine("Image sessions:"); DicConsole.WriteLine("{0,-9}{1,-13}{2,-12}{3,-12}{4,-12}", "Session", "First track", "Last track", "Start", "End"); DicConsole.WriteLine("========================================================="); foreach (Session session in imageFormat.Sessions) { DicConsole.WriteLine("{0,-9}{1,-13}{2,-12}{3,-12}{4,-12}", session.SessionSequence, session.StartTrack, session.EndTrack, session.StartSector, session.EndSector); } DicConsole.WriteLine(); } } catch { // ignored } try { if (imageFormat.Tracks != null && imageFormat.Tracks.Count > 0) { DicConsole.WriteLine("Image tracks:"); DicConsole.WriteLine("{0,-7}{1,-17}{2,-6}{3,-8}{4,-12}{5,-8}{6,-12}{7,-12}", "Track", "Type", "Bps", "Raw bps", "Subchannel", "Pregap", "Start", "End"); DicConsole .WriteLine("================================================================================="); foreach (Track track in imageFormat.Tracks) { DicConsole.WriteLine("{0,-7}{1,-17}{2,-6}{3,-8}{4,-12}{5,-8}{6,-12}{7,-12}", track.TrackSequence, track.TrackType, track.TrackBytesPerSector, track.TrackRawBytesPerSector, track.TrackSubchannelType, track.TrackPregap, track.TrackStartSector, track.TrackEndSector); } DicConsole.WriteLine(); } } catch { // ignored } if (imageFormat.DumpHardware == null) { return; } const string MANUFACTURER_STRING = "Manufacturer"; const string MODEL_STRING = "Model"; const string SERIAL_STRING = "Serial"; const string SOFTWARE_STRING = "Software"; const string VERSION_STRING = "Version"; const string OS_STRING = "Operating system"; const string START_STRING = "Start"; const string END_STRING = "End"; int manufacturerLen = MANUFACTURER_STRING.Length; int modelLen = MODEL_STRING.Length; int serialLen = SERIAL_STRING.Length; int softwareLen = SOFTWARE_STRING.Length; int versionLen = VERSION_STRING.Length; int osLen = OS_STRING.Length; int sectorLen = START_STRING.Length; foreach (DumpHardwareType dump in imageFormat.DumpHardware) { if (dump.Manufacturer?.Length > manufacturerLen) { manufacturerLen = dump.Manufacturer.Length; } if (dump.Model?.Length > modelLen) { modelLen = dump.Model.Length; } if (dump.Serial?.Length > serialLen) { serialLen = dump.Serial.Length; } if (dump.Software?.Name?.Length > softwareLen) { softwareLen = dump.Software.Name.Length; } if (dump.Software?.Version?.Length > versionLen) { versionLen = dump.Software.Version.Length; } if (dump.Software?.OperatingSystem?.Length > osLen) { osLen = dump.Software.OperatingSystem.Length; } foreach (ExtentType extent in dump.Extents) { if ($"{extent.Start}".Length > sectorLen) { sectorLen = $"{extent.Start}".Length; } if ($"{extent.End}".Length > sectorLen) { sectorLen = $"{extent.End}".Length; } } } manufacturerLen += 2; modelLen += 2; serialLen += 2; softwareLen += 2; versionLen += 2; osLen += 2; sectorLen += 2; sectorLen += 2; char[] separator = new char[manufacturerLen + modelLen + serialLen + softwareLen + versionLen + osLen + sectorLen + sectorLen]; for (int i = 0; i < separator.Length; i++) { separator[i] = '='; } string format = $"{{0,-{manufacturerLen}}}{{1,-{modelLen}}}{{2,-{serialLen}}}{{3,-{softwareLen}}}{{4,-{versionLen}}}{{5,-{osLen}}}{{6,-{sectorLen}}}{{7,-{sectorLen}}}"; DicConsole.WriteLine("Dump hardware information:"); DicConsole.WriteLine(format, MANUFACTURER_STRING, MODEL_STRING, SERIAL_STRING, SOFTWARE_STRING, VERSION_STRING, OS_STRING, START_STRING, END_STRING); DicConsole.WriteLine(new string(separator)); foreach (DumpHardwareType dump in imageFormat.DumpHardware) { foreach (ExtentType extent in dump.Extents) { DicConsole.WriteLine(format, dump.Manufacturer, dump.Model, dump.Serial, dump.Software.Name, dump.Software.Version, dump.Software.OperatingSystem, extent.Start, extent.End); } } DicConsole.WriteLine(); }
public void ReportScsiModes(ref DeviceReportV2 report, out byte[] cdromMode) { Modes.DecodedMode? decMode = null; PeripheralDeviceTypes devType = _dev.ScsiType; byte[] mode10CurrentBuffer; byte[] mode10ChangeableBuffer; byte[] mode6CurrentBuffer; byte[] mode6ChangeableBuffer; DicConsole.WriteLine("Querying all mode pages and subpages using SCSI MODE SENSE (10)..."); bool sense = _dev.ModeSense10(out byte[] mode10Buffer, out _, false, true, ScsiModeSensePageControl.Default, 0x3F, 0xFF, _dev.Timeout, out _); if (sense || _dev.Error) { DicConsole.WriteLine("Querying all mode pages using SCSI MODE SENSE (10)..."); sense = _dev.ModeSense10(out mode10Buffer, out _, false, true, ScsiModeSensePageControl.Default, 0x3F, 0x00, _dev.Timeout, out _); if (!sense && !_dev.Error) { report.SCSI.SupportsModeSense10 = true; report.SCSI.SupportsModeSubpages = false; decMode = Modes.DecodeMode10(mode10Buffer, devType); { sense = _dev.ModeSense10(out mode10CurrentBuffer, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, _dev.Timeout, out _); if (!sense && !_dev.Error) { report.SCSI.ModeSense10CurrentData = mode10CurrentBuffer; } sense = _dev.ModeSense10(out mode10ChangeableBuffer, out _, false, true, ScsiModeSensePageControl.Changeable, 0x3F, 0x00, _dev.Timeout, out _); if (!sense && !_dev.Error) { report.SCSI.ModeSense10ChangeableData = mode10ChangeableBuffer; } } } } else { report.SCSI.SupportsModeSense10 = true; report.SCSI.SupportsModeSubpages = true; decMode = Modes.DecodeMode10(mode10Buffer, devType); { sense = _dev.ModeSense10(out mode10CurrentBuffer, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, 0xFF, _dev.Timeout, out _); if (!sense && !_dev.Error) { report.SCSI.ModeSense10CurrentData = mode10CurrentBuffer; } sense = _dev.ModeSense10(out mode10ChangeableBuffer, out _, false, true, ScsiModeSensePageControl.Changeable, 0x3F, 0xFF, _dev.Timeout, out _); if (!sense && !_dev.Error) { report.SCSI.ModeSense10ChangeableData = mode10ChangeableBuffer; } } } DicConsole.WriteLine("Querying all mode pages and subpages using SCSI MODE SENSE (6)..."); sense = _dev.ModeSense6(out byte[] mode6Buffer, out _, false, ScsiModeSensePageControl.Default, 0x3F, 0xFF, _dev.Timeout, out _); if (sense || _dev.Error) { DicConsole.WriteLine("Querying all mode pages using SCSI MODE SENSE (6)..."); sense = _dev.ModeSense6(out mode6Buffer, out _, false, ScsiModeSensePageControl.Default, 0x3F, 0x00, _dev.Timeout, out _); if (sense || _dev.Error) { DicConsole.WriteLine("Querying SCSI MODE SENSE (6)..."); sense = _dev.ModeSense(out mode6Buffer, out _, _dev.Timeout, out _); } else { sense = _dev.ModeSense6(out mode6CurrentBuffer, out _, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, _dev.Timeout, out _); if (!sense && !_dev.Error) { report.SCSI.ModeSense6CurrentData = mode6CurrentBuffer; } sense = _dev.ModeSense6(out mode6ChangeableBuffer, out _, false, ScsiModeSensePageControl.Changeable, 0x3F, 0x00, _dev.Timeout, out _); if (!sense && !_dev.Error) { report.SCSI.ModeSense6ChangeableData = mode6ChangeableBuffer; } } } else { report.SCSI.SupportsModeSubpages = true; { sense = _dev.ModeSense6(out mode6CurrentBuffer, out _, false, ScsiModeSensePageControl.Current, 0x3F, 0xFF, _dev.Timeout, out _); if (!sense && !_dev.Error) { report.SCSI.ModeSense6CurrentData = mode6CurrentBuffer; } sense = _dev.ModeSense6(out mode6ChangeableBuffer, out _, false, ScsiModeSensePageControl.Changeable, 0x3F, 0xFF, _dev.Timeout, out _); if (!sense && !_dev.Error) { report.SCSI.ModeSense6ChangeableData = mode6ChangeableBuffer; } } } if (!sense && !_dev.Error && !decMode.HasValue) { decMode = Modes.DecodeMode6(mode6Buffer, devType); } report.SCSI.SupportsModeSense6 |= !sense && !_dev.Error; cdromMode = null; if (report.SCSI.SupportsModeSense6) { report.SCSI.ModeSense6Data = mode6Buffer; } if (report.SCSI.SupportsModeSense10) { report.SCSI.ModeSense10Data = mode10Buffer; } if (!decMode.HasValue) { return; } report.SCSI.ModeSense = new ScsiMode { BlankCheckEnabled = decMode.Value.Header.EBC, DPOandFUA = decMode.Value.Header.DPOFUA, WriteProtected = decMode.Value.Header.WriteProtected }; if (decMode.Value.Header.BufferedMode > 0) { report.SCSI.ModeSense.BufferedMode = decMode.Value.Header.BufferedMode; } if (decMode.Value.Header.Speed > 0) { report.SCSI.ModeSense.Speed = decMode.Value.Header.Speed; } if (decMode.Value.Pages == null) { return; } List <ScsiPage> modePages = new List <ScsiPage>(); foreach (Modes.ModePage page in decMode.Value.Pages) { var modePage = new ScsiPage { page = page.Page, subpage = page.Subpage, value = page.PageResponse }; modePages.Add(modePage); if (modePage.page == 0x2A && modePage.subpage == 0x00) { cdromMode = page.PageResponse; } } if (modePages.Count > 0) { report.SCSI.ModeSense.ModePages = modePages; } }
void DumpMs() { const ushort SBC_PROFILE = 0x0001; const uint BLOCK_SIZE = 512; double totalDuration = 0; double currentSpeed = 0; double maxSpeed = double.MinValue; double minSpeed = double.MaxValue; uint blocksToRead = 64; DateTime start; DateTime end; MediaType dskType; bool sense; sense = _dev.ReadCapacity(out byte[] readBuffer, out _, _dev.Timeout, out _); if (sense) { _dumpLog.WriteLine("Could not detect capacity..."); StoppingErrorMessage?.Invoke("Could not detect capacity..."); return; } uint blocks = (uint)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]); blocks++; UpdateStatus?. Invoke($"Media has {blocks} blocks of {BLOCK_SIZE} bytes/each. (for a total of {blocks * (ulong)BLOCK_SIZE} bytes)"); if (blocks == 0) { _dumpLog.WriteLine("ERROR: Unable to read medium or empty medium present..."); StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present..."); return; } UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * BLOCK_SIZE} bytes)."); UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); UpdateStatus?.Invoke($"Device reports {BLOCK_SIZE} bytes per logical block."); UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); if (blocks > 262144) { dskType = MediaType.MemoryStickProDuo; _dumpLog.WriteLine("Media detected as MemoryStick Pro Duo..."); UpdateStatus?.Invoke("Media detected as MemoryStick Pro Duo..."); } else { dskType = MediaType.MemoryStickDuo; _dumpLog.WriteLine("Media detected as MemoryStick Duo..."); UpdateStatus?.Invoke("Media detected as MemoryStick Duo..."); } bool ret; var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, BLOCK_SIZE, blocksToRead, _private); var ibgLog = new IbgLog(_outputPrefix + ".ibg", SBC_PROFILE); ret = _outputPlugin.Create(_outputPath, dskType, _formatOptions, blocks, BLOCK_SIZE); // Cannot create image if (!ret) { _dumpLog.WriteLine("Error creating output image, not continuing."); _dumpLog.WriteLine(_outputPlugin.ErrorMessage); StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + _outputPlugin.ErrorMessage); return; } start = DateTime.UtcNow; double imageWriteDuration = 0; DumpHardwareType currentTry = null; ExtentsULong extents = null; ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, _private); if (currentTry == null || extents == null) { StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); return; } if (_resume.NextBlock > 0) { _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); } bool newTrim = false; DateTime timeSpeedStart = DateTime.UtcNow; ulong sectorSpeedStart = 0; InitProgress?.Invoke(); for (ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) { if (_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); _dumpLog.WriteLine("Aborted!"); break; } if (blocks - i < blocksToRead) { blocksToRead = (uint)(blocks - i); } #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 UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, blocks); sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, (uint)i, BLOCK_SIZE, 0, blocksToRead, false, _dev.Timeout, out double cmdDuration); totalDuration += cmdDuration; if (!sense && !_dev.Error) { mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); DateTime writeStart = DateTime.Now; _outputPlugin.WriteSectors(readBuffer, i, blocksToRead); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; extents.Add(i, blocksToRead, true); } else { // 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; _outputPlugin.WriteSectors(new byte[BLOCK_SIZE * _skip], i, _skip); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; for (ulong b = i; b < i + _skip; b++) { _resume.BadBlocks.Add(b); } 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 * BLOCK_SIZE) / (1048576 * elapsed); sectorSpeedStart = 0; timeSpeedStart = DateTime.UtcNow; } end = DateTime.UtcNow; EndProgress?.Invoke(); mhddLog.Close(); ibgLog.Close(_dev, blocks, BLOCK_SIZE, (end - start).TotalSeconds, currentSpeed * 1024, (BLOCK_SIZE * (double)(blocks + 1)) / 1024 / (totalDuration / 1000), _devicePath); UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); UpdateStatus?. Invoke($"Average dump speed {((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec."); UpdateStatus?. Invoke($"Average write speed {((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec."); _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", ((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / (totalDuration / 1000)); _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", ((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / imageWriteDuration); #region Trimming if (_resume.BadBlocks.Count > 0 && !_aborted && _trim && newTrim) { start = DateTime.UtcNow; UpdateStatus?.Invoke("Trimming skipped sectors"); _dumpLog.WriteLine("Trimming skipped sectors"); ulong[] tmpArray = _resume.BadBlocks.ToArray(); InitProgress?.Invoke(); foreach (ulong badSector in tmpArray) { if (_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); _dumpLog.WriteLine("Aborted!"); break; } PulseProgress?.Invoke($"Trimming sector {badSector}"); sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, (uint)badSector, BLOCK_SIZE, 0, 1, false, _dev.Timeout, out double cmdDuration); if (sense || _dev.Error) { continue; } _resume.BadBlocks.Remove(badSector); extents.Add(badSector); _outputPlugin.WriteSector(readBuffer, badSector); } EndProgress?.Invoke(); end = DateTime.UtcNow; _dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds); } #endregion Trimming #region Error handling if (_resume.BadBlocks.Count > 0 && !_aborted && _retryPasses > 0) { int pass = 1; bool forward = true; bool runningPersistent = false; Modes.ModePage?currentModePage = null; byte[] md6; if (_persistent) { Modes.ModePage_01 pg; sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout, out _); if (sense) { sense = _dev.ModeSense10(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout, out _); if (!sense) { Modes.DecodedMode?dcMode10 = Modes.DecodeMode10(readBuffer, _dev.ScsiType); if (dcMode10.HasValue) { foreach (Modes.ModePage modePage in dcMode10.Value.Pages) { if (modePage.Page == 0x01 && modePage.Subpage == 0x00) { currentModePage = modePage; } } } } } else { Modes.DecodedMode?dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType); if (dcMode6.HasValue) { foreach (Modes.ModePage modePage in dcMode6.Value.Pages) { if (modePage.Page == 0x01 && modePage.Subpage == 0x00) { currentModePage = modePage; } } } } if (currentModePage == null) { pg = new Modes.ModePage_01 { PS = false, AWRE = true, ARRE = true, TB = false, RC = false, EER = true, PER = false, DTE = true, DCR = false, ReadRetryCount = 32 }; currentModePage = new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg) }; } pg = new Modes.ModePage_01 { PS = false, AWRE = false, ARRE = false, TB = true, RC = false, EER = true, PER = false, DTE = false, DCR = false, ReadRetryCount = 255 }; var md = new Modes.DecodedMode { Header = new Modes.ModeHeader(), Pages = new[] { new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg) } } }; md6 = Modes.EncodeMode6(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 byte[] 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(); repeatRetry: ulong[] tmpArray = _resume.BadBlocks.ToArray(); 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, " : "")); sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, (uint)badSector, BLOCK_SIZE, 0, 1, false, _dev.Timeout, out double cmdDuration); totalDuration += cmdDuration; if (!sense && !_dev.Error) { _resume.BadBlocks.Remove(badSector); extents.Add(badSector); _outputPlugin.WriteSector(readBuffer, badSector); UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } else if (runningPersistent) { _outputPlugin.WriteSector(readBuffer, badSector); } } if (pass < _retryPasses && !_aborted && _resume.BadBlocks.Count > 0) { pass++; forward = !forward; _resume.BadBlocks.Sort(); if (!forward) { _resume.BadBlocks.Reverse(); } goto repeatRetry; } if (runningPersistent && currentModePage.HasValue) { var md = new Modes.DecodedMode { Header = new Modes.ModeHeader(), Pages = new[] { currentModePage.Value } }; md6 = Modes.EncodeMode6(md, _dev.ScsiType); UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status)."); _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); sense = _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _); } EndProgress?.Invoke(); } #endregion Error handling _resume.BadBlocks.Sort(); foreach (ulong bad in _resume.BadBlocks) { _dumpLog.WriteLine("Sector {0} could not be read.", bad); } currentTry.Extents = ExtentsConverter.ToMetadata(extents); var metadata = new CommonTypes.Structs.ImageInfo { Application = "Aaru", ApplicationVersion = Version.GetVersion() }; if (!_outputPlugin.SetMetadata(metadata)) { ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + _outputPlugin.ErrorMessage); } _outputPlugin.SetDumpHardware(_resume.Tries); if (_preSidecar != null) { _outputPlugin.SetCicmMetadata(_preSidecar); } _dumpLog.WriteLine("Closing output file."); UpdateStatus?.Invoke("Closing output file."); DateTime closeStart = DateTime.Now; _outputPlugin.Close(); DateTime closeEnd = DateTime.Now; UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); if (_aborted) { UpdateStatus?.Invoke("Aborted!"); _dumpLog.WriteLine("Aborted!"); return; } double totalChkDuration = 0; if (_metadata) { UpdateStatus?.Invoke("Creating sidecar."); _dumpLog.WriteLine("Creating sidecar."); var filters = new FiltersList(); IFilter filter = filters.GetFilter(_outputPath); IMediaImage inputPlugin = ImageFormat.Detect(filter); if (!inputPlugin.Open(filter)) { StoppingErrorMessage?.Invoke("Could not open created image."); return; } DateTime chkStart = DateTime.UtcNow; _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); _sidecarClass.InitProgressEvent += InitProgress; _sidecarClass.UpdateProgressEvent += UpdateProgress; _sidecarClass.EndProgressEvent += EndProgress; _sidecarClass.InitProgressEvent2 += InitProgress2; _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; _sidecarClass.EndProgressEvent2 += EndProgress2; _sidecarClass.UpdateStatusEvent += UpdateStatus; CICMMetadataType sidecar = _sidecarClass.Create(); end = DateTime.UtcNow; totalChkDuration = (end - chkStart).TotalMilliseconds; UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds."); UpdateStatus?. Invoke($"Average checksum speed {((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", ((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000)); if (_preSidecar != null) { _preSidecar.BlockMedia = sidecar.BlockMedia; sidecar = _preSidecar; } List <(ulong start, string type)> filesystems = new List <(ulong start, string type)>(); if (sidecar.BlockMedia[0].FileSystemInformation != null) { filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation where partition.FileSystems != null from fileSystem in partition.FileSystems select(partition.StartSector, fileSystem.Type)); } if (filesystems.Count > 0) { foreach (var filesystem in filesystems.Select(o => new { o.start, o.type }).Distinct()) { UpdateStatus?.Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}"); _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start); } } sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); (string type, string subType)xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); sidecar.BlockMedia[0].DiskType = xmlType.type; sidecar.BlockMedia[0].DiskSubType = xmlType.subType; sidecar.BlockMedia[0].Interface = "USB"; sidecar.BlockMedia[0].LogicalBlocks = blocks; sidecar.BlockMedia[0].PhysicalBlockSize = (int)BLOCK_SIZE; sidecar.BlockMedia[0].LogicalBlockSize = (int)BLOCK_SIZE; sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; sidecar.BlockMedia[0].Model = _dev.Model; if (!_private) { sidecar.BlockMedia[0].Serial = _dev.Serial; } sidecar.BlockMedia[0].Size = blocks * BLOCK_SIZE; if (_dev.IsRemovable) { sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray(); } UpdateStatus?.Invoke("Writing metadata sidecar"); var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); xmlSer.Serialize(xmlFs, sidecar); xmlFs.Close(); } UpdateStatus?.Invoke(""); UpdateStatus?. Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); UpdateStatus?. Invoke($"Average speed: {((double)BLOCK_SIZE * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); if (maxSpeed > 0) { UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); } if (minSpeed > 0 && minSpeed < double.MaxValue) { UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); } UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); UpdateStatus?.Invoke(""); Statistics.AddMedia(dskType, true); }
public void ReportScsiModes(ref DeviceReportV2 report, out byte[] cdromMode, out MediumTypes mediumType) { Modes.DecodedMode? decMode = null; PeripheralDeviceTypes devType = _dev.ScsiType; byte[] mode10Buffer; byte[] mode6Buffer; bool sense; mediumType = 0; AaruConsole.WriteLine("Querying all mode pages and subpages using SCSI MODE SENSE (10)..."); foreach (ScsiModeSensePageControl pageControl in new[] { ScsiModeSensePageControl.Default, ScsiModeSensePageControl.Current, ScsiModeSensePageControl.Changeable }) { bool saveBuffer = false; sense = _dev.ModeSense10(out mode10Buffer, out _, false, true, pageControl, 0x3F, 0xFF, _dev.Timeout, out _); if (sense || _dev.Error) { sense = _dev.ModeSense10(out mode10Buffer, out _, false, false, pageControl, 0x3F, 0xFF, _dev.Timeout, out _); if (sense || _dev.Error) { sense = _dev.ModeSense10(out mode10Buffer, out _, false, true, pageControl, 0x3F, 0x00, _dev.Timeout, out _); if (sense || _dev.Error) { sense = _dev.ModeSense10(out mode10Buffer, out _, false, false, pageControl, 0x3F, 0x00, _dev.Timeout, out _); if (!sense && !_dev.Error) { report.SCSI.SupportsModeSense10 = true; decMode ??= Modes.DecodeMode10(mode10Buffer, devType); saveBuffer = true; } } else { report.SCSI.SupportsModeSense10 = true; decMode ??= Modes.DecodeMode10(mode10Buffer, devType); saveBuffer = true; } } else { report.SCSI.SupportsModeSense10 = true; report.SCSI.SupportsModeSubpages = true; decMode ??= Modes.DecodeMode10(mode10Buffer, devType); saveBuffer = true; } } else { report.SCSI.SupportsModeSense10 = true; report.SCSI.SupportsModeSubpages = true; decMode ??= Modes.DecodeMode10(mode10Buffer, devType); saveBuffer = true; } if (!saveBuffer) { continue; } switch (pageControl) { case ScsiModeSensePageControl.Default: report.SCSI.ModeSense10Data = mode10Buffer; break; case ScsiModeSensePageControl.Changeable: report.SCSI.ModeSense10ChangeableData = mode10Buffer; break; case ScsiModeSensePageControl.Current: report.SCSI.ModeSense10CurrentData = mode10Buffer; break; } } AaruConsole.WriteLine("Querying all mode pages and subpages using SCSI MODE SENSE (6)..."); foreach (ScsiModeSensePageControl pageControl in new[] { ScsiModeSensePageControl.Default, ScsiModeSensePageControl.Current, ScsiModeSensePageControl.Changeable }) { bool saveBuffer = false; sense = _dev.ModeSense6(out mode6Buffer, out _, true, pageControl, 0x3F, 0xFF, _dev.Timeout, out _); if (sense || _dev.Error) { sense = _dev.ModeSense6(out mode6Buffer, out _, false, pageControl, 0x3F, 0xFF, _dev.Timeout, out _); if (sense || _dev.Error) { sense = _dev.ModeSense6(out mode6Buffer, out _, true, pageControl, 0x3F, 0x00, _dev.Timeout, out _); if (sense || _dev.Error) { sense = _dev.ModeSense6(out mode6Buffer, out _, false, pageControl, 0x3F, 0x00, _dev.Timeout, out _); if (sense || _dev.Error) { sense = _dev.ModeSense6(out mode6Buffer, out _, true, pageControl, 0x00, 0x00, _dev.Timeout, out _); if (sense || _dev.Error) { sense = _dev.ModeSense6(out mode6Buffer, out _, false, pageControl, 0x00, 0x00, _dev.Timeout, out _); if (!sense && !_dev.Error) { report.SCSI.SupportsModeSense6 = true; decMode ??= Modes.DecodeMode6(mode6Buffer, devType); saveBuffer = true; } } else { report.SCSI.SupportsModeSense6 = true; decMode ??= Modes.DecodeMode6(mode6Buffer, devType); saveBuffer = true; } } else { report.SCSI.SupportsModeSense6 = true; decMode ??= Modes.DecodeMode6(mode6Buffer, devType); saveBuffer = true; } } else { report.SCSI.SupportsModeSense6 = true; decMode ??= Modes.DecodeMode6(mode6Buffer, devType); saveBuffer = true; } } else { report.SCSI.SupportsModeSense10 = true; report.SCSI.SupportsModeSubpages = true; decMode ??= Modes.DecodeMode6(mode6Buffer, devType); saveBuffer = true; } } else { report.SCSI.SupportsModeSense6 = true; report.SCSI.SupportsModeSubpages = true; decMode ??= Modes.DecodeMode6(mode6Buffer, devType); saveBuffer = true; } if (!saveBuffer) { continue; } switch (pageControl) { case ScsiModeSensePageControl.Default: report.SCSI.ModeSense6Data = mode6Buffer; break; case ScsiModeSensePageControl.Changeable: report.SCSI.ModeSense6ChangeableData = mode6Buffer; break; case ScsiModeSensePageControl.Current: report.SCSI.ModeSense6CurrentData = mode6Buffer; break; } } cdromMode = null; if (!decMode.HasValue) { return; } mediumType = decMode.Value.Header.MediumType; report.SCSI.ModeSense = new ScsiMode { BlankCheckEnabled = decMode.Value.Header.EBC, DPOandFUA = decMode.Value.Header.DPOFUA, WriteProtected = decMode.Value.Header.WriteProtected }; if (decMode.Value.Header.BufferedMode > 0) { report.SCSI.ModeSense.BufferedMode = decMode.Value.Header.BufferedMode; } if (decMode.Value.Header.Speed > 0) { report.SCSI.ModeSense.Speed = decMode.Value.Header.Speed; } if (decMode.Value.Pages == null) { return; } List <ScsiPage> modePages = new List <ScsiPage>(); foreach (Modes.ModePage page in decMode.Value.Pages) { var modePage = new ScsiPage { page = page.Page, subpage = page.Subpage, value = page.PageResponse }; modePages.Add(modePage); if (modePage.page == 0x2A && modePage.subpage == 0x00) { cdromMode = page.PageResponse; } } if (modePages.Count > 0) { report.SCSI.ModeSense.ModePages = modePages; } }
void DumpUmd() { const uint BLOCK_SIZE = 2048; const MediaType DSK_TYPE = MediaType.UMD; uint blocksToRead = 16; double totalDuration = 0; double currentSpeed = 0; double maxSpeed = double.MinValue; double minSpeed = double.MaxValue; DateTime start; DateTime end; bool sense = _dev.Read12(out byte[] readBuffer, out _, 0, false, true, false, false, 0, 512, 0, 1, false, _dev.Timeout, out _); if (sense) { _dumpLog.WriteLine("Could not read..."); StoppingErrorMessage?.Invoke("Could not read..."); return; } ushort fatStart = (ushort)((readBuffer[0x0F] << 8) + readBuffer[0x0E]); ushort sectorsPerFat = (ushort)((readBuffer[0x17] << 8) + readBuffer[0x16]); ushort rootStart = (ushort)((sectorsPerFat * 2) + fatStart); ushort rootSize = (ushort)((((readBuffer[0x12] << 8) + readBuffer[0x11]) * 32) / 512); ushort umdStart = (ushort)(rootStart + rootSize); UpdateStatus?.Invoke($"Reading root directory in sector {rootStart}..."); _dumpLog.WriteLine("Reading root directory in sector {0}...", rootStart); sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, rootStart, 512, 0, 1, false, _dev.Timeout, out _); if (sense) { _dumpLog.WriteLine("Could not read..."); StoppingErrorMessage?.Invoke("Could not read..."); return; } uint umdSizeInBytes = BitConverter.ToUInt32(readBuffer, 0x3C); ulong blocks = umdSizeInBytes / BLOCK_SIZE; string mediaPartNumber = Encoding.ASCII.GetString(readBuffer, 0, 11).Trim(); UpdateStatus?. Invoke($"Media has {blocks} blocks of {BLOCK_SIZE} bytes/each. (for a total of {blocks * (ulong)BLOCK_SIZE} bytes)"); UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * BLOCK_SIZE} bytes)."); UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); UpdateStatus?.Invoke($"Device reports {BLOCK_SIZE} bytes per logical block."); UpdateStatus?.Invoke($"Device reports {2048} bytes per physical block."); UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); UpdateStatus?.Invoke($"Media identified as {DSK_TYPE}."); UpdateStatus?.Invoke($"Media part number is {mediaPartNumber}."); _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * BLOCK_SIZE); _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); _dumpLog.WriteLine("Device reports {0} bytes per logical block.", BLOCK_SIZE); _dumpLog.WriteLine("Device reports {0} bytes per physical block.", 2048); _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType); _dumpLog.WriteLine("Media identified as {0}.", DSK_TYPE); _dumpLog.WriteLine("Media part number is {0}.", mediaPartNumber); bool ret; var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, BLOCK_SIZE, blocksToRead, _private); var ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0010); ret = _outputPlugin.Create(_outputPath, DSK_TYPE, _formatOptions, blocks, BLOCK_SIZE); // Cannot create image if (!ret) { _dumpLog.WriteLine("Error creating output image, not continuing."); _dumpLog.WriteLine(_outputPlugin.ErrorMessage); StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + _outputPlugin.ErrorMessage); return; } start = DateTime.UtcNow; double imageWriteDuration = 0; (_outputPlugin as IWritableOpticalImage).SetTracks(new List <Track> { new Track { TrackBytesPerSector = (int)BLOCK_SIZE, TrackEndSector = blocks - 1, TrackSequence = 1, TrackRawBytesPerSector = (int)BLOCK_SIZE, TrackSubchannelType = TrackSubchannelType.None, TrackSession = 1, TrackType = TrackType.Data } }); DumpHardwareType currentTry = null; ExtentsULong extents = null; ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, _private); if (currentTry == null || extents == null) { StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); return; } if (_resume.NextBlock > 0) { _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); } bool newTrim = false; DateTime timeSpeedStart = DateTime.UtcNow; ulong sectorSpeedStart = 0; InitProgress?.Invoke(); for (ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) { if (_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); _dumpLog.WriteLine("Aborted!"); break; } if (blocks - i < blocksToRead) { blocksToRead = (uint)(blocks - i); } #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 UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, (long)blocks); sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, (uint)(umdStart + (i * 4)), 512, 0, blocksToRead * 4, false, _dev.Timeout, out double cmdDuration); totalDuration += cmdDuration; if (!sense && !_dev.Error) { mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); DateTime writeStart = DateTime.Now; _outputPlugin.WriteSectors(readBuffer, i, blocksToRead); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; extents.Add(i, blocksToRead, true); } else { // 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; _outputPlugin.WriteSectors(new byte[BLOCK_SIZE * _skip], i, _skip); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; for (ulong b = i; b < i + _skip; b++) { _resume.BadBlocks.Add(b); } 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 * BLOCK_SIZE) / (1048576 * elapsed); sectorSpeedStart = 0; timeSpeedStart = DateTime.UtcNow; } end = DateTime.UtcNow; EndProgress?.Invoke(); mhddLog.Close(); ibgLog.Close(_dev, blocks, BLOCK_SIZE, (end - start).TotalSeconds, currentSpeed * 1024, (BLOCK_SIZE * (double)(blocks + 1)) / 1024 / (totalDuration / 1000), _devicePath); UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); UpdateStatus?. Invoke($"Average dump speed {((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec."); UpdateStatus?. Invoke($"Average write speed {((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec."); _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", ((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / (totalDuration / 1000)); _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", ((double)BLOCK_SIZE * (double)(blocks + 1)) / 1024 / imageWriteDuration); #region Trimming if (_resume.BadBlocks.Count > 0 && !_aborted && _trim && newTrim) { start = DateTime.UtcNow; _dumpLog.WriteLine("Trimming skipped sectors"); ulong[] tmpArray = _resume.BadBlocks.ToArray(); InitProgress?.Invoke(); foreach (ulong badSector in tmpArray) { if (_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); _dumpLog.WriteLine("Aborted!"); break; } PulseProgress?.Invoke($"Trimming sector {badSector}"); sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, (uint)(umdStart + (badSector * 4)), 512, 0, 4, false, _dev.Timeout, out double cmdDuration); if (sense || _dev.Error) { continue; } _resume.BadBlocks.Remove(badSector); extents.Add(badSector); _outputPlugin.WriteSector(readBuffer, badSector); } EndProgress?.Invoke(); end = DateTime.UtcNow; _dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds); } #endregion Trimming #region Error handling if (_resume.BadBlocks.Count > 0 && !_aborted && _retryPasses > 0) { int pass = 1; bool forward = true; bool runningPersistent = false; Modes.ModePage?currentModePage = null; byte[] md6; if (_persistent) { Modes.ModePage_01 pg; sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout, out _); if (!sense) { Modes.DecodedMode?dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType); if (dcMode6.HasValue) { foreach (Modes.ModePage modePage in dcMode6.Value.Pages) { if (modePage.Page == 0x01 && modePage.Subpage == 0x00) { currentModePage = modePage; } } } } if (currentModePage == null) { pg = new Modes.ModePage_01 { PS = false, AWRE = true, ARRE = true, TB = false, RC = false, EER = true, PER = false, DTE = true, DCR = false, ReadRetryCount = 32 }; currentModePage = new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg) }; } pg = new Modes.ModePage_01 { PS = false, AWRE = false, ARRE = false, TB = true, RC = false, EER = true, PER = false, DTE = false, DCR = false, ReadRetryCount = 255 }; var md = new Modes.DecodedMode { Header = new Modes.ModeHeader(), Pages = new[] { new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg) } } }; md6 = Modes.EncodeMode6(md, _dev.ScsiType); _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); sense = _dev.ModeSelect(md6, out byte[] 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(); repeatRetry: ulong[] tmpArray = _resume.BadBlocks.ToArray(); foreach (ulong badSector in tmpArray) { if (_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); _dumpLog.WriteLine("Aborted!"); break; } PulseProgress?. Invoke($"Retrying sector {badSector}, pass {pass}, {(runningPersistent ? "recovering partial data, " : "")}{(forward ? "forward" : "reverse")}"); sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, (uint)(umdStart + (badSector * 4)), 512, 0, 4, false, _dev.Timeout, out double cmdDuration); totalDuration += cmdDuration; if (!sense && !_dev.Error) { _resume.BadBlocks.Remove(badSector); extents.Add(badSector); _outputPlugin.WriteSector(readBuffer, badSector); UpdateStatus?.Invoke(string.Format("Correctly retried block {0} in pass {1}.", badSector, pass)); _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } else if (runningPersistent) { _outputPlugin.WriteSector(readBuffer, badSector); } } if (pass < _retryPasses && !_aborted && _resume.BadBlocks.Count > 0) { pass++; forward = !forward; _resume.BadBlocks.Sort(); if (!forward) { _resume.BadBlocks.Reverse(); } goto repeatRetry; } if (runningPersistent && currentModePage.HasValue) { var md = new Modes.DecodedMode { Header = new Modes.ModeHeader(), Pages = new[] { currentModePage.Value } }; md6 = Modes.EncodeMode6(md, _dev.ScsiType); _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); sense = _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _); } EndProgress?.Invoke(); AaruConsole.WriteLine(); } #endregion Error handling _resume.BadBlocks.Sort(); foreach (ulong bad in _resume.BadBlocks) { _dumpLog.WriteLine("Sector {0} could not be read.", bad); } currentTry.Extents = ExtentsConverter.ToMetadata(extents); var metadata = new CommonTypes.Structs.ImageInfo { Application = "Aaru", ApplicationVersion = Version.GetVersion(), MediaPartNumber = mediaPartNumber }; if (!_outputPlugin.SetMetadata(metadata)) { ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + _outputPlugin.ErrorMessage); } _outputPlugin.SetDumpHardware(_resume.Tries); if (_preSidecar != null) { _outputPlugin.SetCicmMetadata(_preSidecar); } _dumpLog.WriteLine("Closing output file."); UpdateStatus?.Invoke("Closing output file."); DateTime closeStart = DateTime.Now; _outputPlugin.Close(); DateTime closeEnd = DateTime.Now; _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); if (_aborted) { UpdateStatus?.Invoke("Aborted!"); _dumpLog.WriteLine("Aborted!"); return; } double totalChkDuration = 0; if (_metadata) { WriteOpticalSidecar(BLOCK_SIZE, blocks, DSK_TYPE, null, null, 1, out totalChkDuration, null); } UpdateStatus?.Invoke(""); UpdateStatus?. Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); UpdateStatus?. Invoke($"Average speed: {((double)BLOCK_SIZE * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); if (maxSpeed > 0) { UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); } if (minSpeed > 0 && minSpeed < double.MaxValue) { UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); } UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); UpdateStatus?.Invoke(""); Statistics.AddMedia(DSK_TYPE, true); }
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); }
public TestedSequentialMedia ReportSscMedia() { var seqTest = new TestedSequentialMedia(); Modes.DecodedMode?decMode = null; AaruConsole.WriteLine("Querying SCSI MODE SENSE (10)..."); bool sense = _dev.ModeSense10(out byte[] buffer, out byte[] _, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, _dev.Timeout, out _); if (!sense && !_dev.Error) { decMode = Modes.DecodeMode10(buffer, _dev.ScsiType); seqTest.ModeSense10Data = buffer; } AaruConsole.WriteLine("Querying SCSI MODE SENSE..."); sense = _dev.ModeSense(out buffer, out byte[] _, _dev.Timeout, out _); if (!sense && !_dev.Error) { decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType); seqTest.ModeSense6Data = buffer; } if (decMode.HasValue) { seqTest.MediumType = (byte)decMode.Value.Header.MediumType; if (decMode.Value.Header.BlockDescriptors?.Length > 0) { seqTest.Density = (byte)decMode.Value.Header.BlockDescriptors?[0].Density; } } AaruConsole.WriteLine("Querying SCSI REPORT DENSITY SUPPORT for current media..."); sense = _dev.ReportDensitySupport(out buffer, out byte[] _, false, true, _dev.Timeout, out _); if (!sense) { DensitySupport.DensitySupportHeader?dsh = DensitySupport.DecodeDensity(buffer); if (dsh.HasValue) { SupportedDensity[] array = new SupportedDensity[dsh.Value.descriptors.Length]; for (int i = 0; i < dsh.Value.descriptors.Length; i++) { array[i] = new SupportedDensity { BitsPerMm = dsh.Value.descriptors[i].bpmm, Capacity = dsh.Value.descriptors[i].capacity, DefaultDensity = dsh.Value.descriptors[i].defaultDensity, Description = dsh.Value.descriptors[i].description, Duplicate = dsh.Value.descriptors[i].duplicate, Name = dsh.Value.descriptors[i].name, Organization = dsh.Value.descriptors[i].organization, PrimaryCode = dsh.Value.descriptors[i].primaryCode, SecondaryCode = dsh.Value.descriptors[i].secondaryCode, Tracks = dsh.Value.descriptors[i].tracks, Width = dsh.Value.descriptors[i].width, Writable = dsh.Value.descriptors[i].writable } } ; seqTest.SupportedDensities = array.ToList(); } } AaruConsole.WriteLine("Querying SCSI REPORT DENSITY SUPPORT for medium types for current media..."); sense = _dev.ReportDensitySupport(out buffer, out byte[] _, true, true, _dev.Timeout, out _); if (!sense) { DensitySupport.MediaTypeSupportHeader?mtsh = DensitySupport.DecodeMediumType(buffer); if (mtsh.HasValue) { SscSupportedMedia[] array = new SscSupportedMedia[mtsh.Value.descriptors.Length]; for (int i = 0; i < mtsh.Value.descriptors.Length; i++) { array[i] = new SscSupportedMedia { Description = mtsh.Value.descriptors[i].description, Length = mtsh.Value.descriptors[i].length, MediumType = mtsh.Value.descriptors[i].mediumType, Name = mtsh.Value.descriptors[i].name, Organization = mtsh.Value.descriptors[i].organization, Width = mtsh.Value.descriptors[i].width }; if (mtsh.Value.descriptors[i].densityCodes == null) { continue; } DensityCode[] array2 = new DensityCode[mtsh.Value.descriptors[i].densityCodes.Length]; for (int j = 0; j < mtsh.Value.descriptors[i].densityCodes.Length; j++) { array2[j] = new DensityCode { Code = mtsh.Value.descriptors[i].densityCodes[j] } } ; array[i].DensityCodes = array2.Distinct().ToList(); } seqTest.SupportedMediaTypes = array.ToList(); } } AaruConsole.WriteLine("Trying SCSI READ MEDIA SERIAL NUMBER..."); seqTest.CanReadMediaSerial = !_dev.ReadMediaSerialNumber(out buffer, out byte[] _, _dev.Timeout, out _); return(seqTest); } } }
public TestedMedia ReportScsiMedia() { var mediaTest = new TestedMedia(); AaruConsole.WriteLine("Querying SCSI READ CAPACITY..."); bool sense = _dev.ReadCapacity(out byte[] buffer, out byte[] senseBuffer, _dev.Timeout, out _); if (!sense && !_dev.Error) { mediaTest.SupportsReadCapacity = true; mediaTest.Blocks = (ulong)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]) + 1; mediaTest.BlockSize = (uint)((buffer[4] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]); } AaruConsole.WriteLine("Querying SCSI READ CAPACITY (16)..."); sense = _dev.ReadCapacity16(out buffer, out buffer, _dev.Timeout, out _); if (!sense && !_dev.Error) { mediaTest.SupportsReadCapacity16 = true; byte[] temp = new byte[8]; Array.Copy(buffer, 0, temp, 0, 8); Array.Reverse(temp); mediaTest.Blocks = BitConverter.ToUInt64(temp, 0) + 1; mediaTest.BlockSize = (uint)((buffer[8] << 24) + (buffer[9] << 16) + (buffer[10] << 8) + buffer[11]); } Modes.DecodedMode?decMode = null; AaruConsole.WriteLine("Querying SCSI MODE SENSE (10)..."); sense = _dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, _dev.Timeout, out _); if (!sense && !_dev.Error) { decMode = Modes.DecodeMode10(buffer, _dev.ScsiType); mediaTest.ModeSense10Data = buffer; } AaruConsole.WriteLine("Querying SCSI MODE SENSE..."); sense = _dev.ModeSense(out buffer, out senseBuffer, _dev.Timeout, out _); if (!sense && !_dev.Error) { decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType); mediaTest.ModeSense6Data = buffer; } if (decMode.HasValue) { mediaTest.MediumType = (byte)decMode.Value.Header.MediumType; if (decMode.Value.Header.BlockDescriptors?.Length > 0) { mediaTest.Density = (byte)decMode.Value.Header.BlockDescriptors[0].Density; } } AaruConsole.WriteLine("Trying SCSI READ (6)..."); mediaTest.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 0, mediaTest.BlockSize ?? 512, _dev.Timeout, out _); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead6); mediaTest.Read6Data = buffer; AaruConsole.WriteLine("Trying SCSI READ (10)..."); mediaTest.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, false, false, false, 0, mediaTest.BlockSize ?? 512, 0, 1, _dev.Timeout, out _); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead10); mediaTest.Read10Data = buffer; AaruConsole.WriteLine("Trying SCSI READ (12)..."); mediaTest.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, false, false, false, 0, mediaTest.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead12); mediaTest.Read12Data = buffer; AaruConsole.WriteLine("Trying SCSI READ (16)..."); mediaTest.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, false, false, 0, mediaTest.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead16); mediaTest.Read16Data = buffer; mediaTest.LongBlockSize = mediaTest.BlockSize; AaruConsole.WriteLine("Trying SCSI READ LONG (10)..."); sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 0xFFFF, _dev.Timeout, out _); if (sense && !_dev.Error) { DecodedSense?decSense = Sense.Decode(senseBuffer); if (decSense?.SenseKey == SenseKeys.IllegalRequest && decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) { mediaTest.SupportsReadLong = true; bool valid = decSense?.Fixed?.InformationValid == true; bool ili = decSense?.Fixed?.ILI == true; uint information = decSense?.Fixed?.Information ?? 0; if (decSense?.Descriptor.HasValue == true && decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00))