diff --git a/DiscImageChef.Core/Devices/Dumping/ATA.cs b/DiscImageChef.Core/Devices/Dumping/ATA.cs index ee421a41..8b894bcf 100644 --- a/DiscImageChef.Core/Devices/Dumping/ATA.cs +++ b/DiscImageChef.Core/Devices/Dumping/ATA.cs @@ -54,7 +54,7 @@ namespace DiscImageChef.Core.Devices.Dumping { public class ATA { - public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref Metadata.Resume resume) + public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref Metadata.Resume resume, ref DumpLog dumpLog) { bool aborted; MHDDLog mhddLog; @@ -78,6 +78,7 @@ namespace DiscImageChef.Core.Devices.Dumping uint timeout = 5; double duration; + dumpLog.WriteLine("Requesting ATA IDENTIFY DEVICE."); sense = dev.AtaIdentify(out byte[] cmdBuf, out Decoders.ATA.AtaErrorRegistersCHS errorChs); if(!sense && Decoders.ATA.Identify.Decode(cmdBuf).HasValue) { @@ -90,6 +91,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(dev.IsUSB) { + dumpLog.WriteLine("Reading USB descriptors."); sidecar.BlockMedia[0].USB = new USBType { ProductID = dev.USBProductID, @@ -106,6 +108,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(dev.IsPCMCIA) { + dumpLog.WriteLine("Reading PCMCIA CIS."); sidecar.BlockMedia[0].PCMCIA = new PCMCIAType { CIS = new DumpType @@ -116,6 +119,7 @@ namespace DiscImageChef.Core.Devices.Dumping } }; DataFile.WriteTo("ATA Dump", sidecar.BlockMedia[0].PCMCIA.CIS.Image, dev.CIS); + dumpLog.WriteLine("Decoding PCMCIA CIS."); Decoders.PCMCIA.Tuple[] tuples = CIS.GetTuples(dev.CIS); if(tuples != null) { @@ -178,12 +182,14 @@ namespace DiscImageChef.Core.Devices.Dumping DataFile dumpFile; // Initializate reader + dumpLog.WriteLine("Initializing reader."); Reader ataReader = new Reader(dev, timeout, cmdBuf); // Fill reader blocks ulong blocks = ataReader.GetDeviceBlocks(); // Check block sizes if(ataReader.GetBlockSize()) { + dumpLog.WriteLine("ERROR: Cannot get block size: {0}.", ataReader.ErrorMessage); DicConsole.ErrorWriteLine(ataReader.ErrorMessage); return; } @@ -191,12 +197,14 @@ namespace DiscImageChef.Core.Devices.Dumping uint physicalsectorsize = ataReader.PhysicalBlockSize; if(ataReader.FindReadCommand()) { + dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", ataReader.ErrorMessage); DicConsole.ErrorWriteLine(ataReader.ErrorMessage); return; } // Check how many blocks to read, if error show and return if(ataReader.GetBlocksToRead()) { + dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", ataReader.ErrorMessage); DicConsole.ErrorWriteLine(ataReader.ErrorMessage); return; } @@ -205,6 +213,12 @@ namespace DiscImageChef.Core.Devices.Dumping byte heads = ataReader.Heads; byte sectors = ataReader.Sectors; + dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); + dumpLog.WriteLine("Device reports {0} cylinders {1} heads {2} sectors per track.", cylinders, heads, sectors); + 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.", physicalsectorsize); + bool removable = false || (!dev.IsCompactFlash && ataId.GeneralConfiguration.HasFlag(Decoders.ATA.Identify.GeneralConfigurationBit.Removable)); DumpHardwareType currentTry = null; ExtentsULong extents = null; @@ -219,6 +233,9 @@ namespace DiscImageChef.Core.Devices.Dumping mhddLog = new MHDDLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); ibgLog = new IBGLog(outputPrefix + ".ibg", currentProfile); dumpFile = new DataFile(outputPrefix + ".bin"); + if(resume.NextBlock > 0) + dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock); + dumpFile.Seek(resume.NextBlock, blockSize); start = DateTime.UtcNow; @@ -227,6 +244,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(aborted) { currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } @@ -262,6 +280,7 @@ namespace DiscImageChef.Core.Devices.Dumping ibgLog.Write(i, 0); dumpFile.Write(new byte[blockSize * blocksToRead]); + dumpLog.WriteLine("Error reading {0} blocks from block {1}.", blocksToRead, i); } #pragma warning disable IDE0004 // Cast is necessary, otherwise incorrect value is created @@ -276,11 +295,12 @@ namespace DiscImageChef.Core.Devices.Dumping #pragma warning disable IDE0004 // Cast is necessary, otherwise incorrect value is created ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalDuration / 1000), devicePath); #pragma warning restore IDE0004 // Cast is necessary, otherwise incorrect value is created + 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)); #region Error handling if(resume.BadBlocks.Count > 0 && !aborted) { - int pass = 0; bool forward = true; bool runningPersistent = false; @@ -292,6 +312,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(aborted) { currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } @@ -306,6 +327,7 @@ namespace DiscImageChef.Core.Devices.Dumping resume.BadBlocks.Remove(badSector); extents.Add(badSector); dumpFile.WriteAt(cmdBuf, badSector, blockSize); + dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } else if(runningPersistent) dumpFile.WriteAt(cmdBuf, badSector, blockSize); @@ -344,6 +366,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(aborted) { currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } @@ -366,6 +389,7 @@ namespace DiscImageChef.Core.Devices.Dumping ibgLog.Write(currentBlock, currentSpeed * 1024); dumpFile.Write(cmdBuf); extents.Add(currentBlock); + dumpLog.WriteLine("Error reading cylinder {0} head {1} sector {2}.", Cy, Hd, Sc); } else { @@ -394,15 +418,21 @@ namespace DiscImageChef.Core.Devices.Dumping #pragma warning disable IDE0004 // Cast is necessary, otherwise incorrect value is created ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalDuration / 1000), devicePath); #pragma warning disable IDE0004 // Cast is necessary, otherwise incorrect value is created + 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)); } dataChk = new Checksum(); dumpFile.Seek(0, SeekOrigin.Begin); blocksToRead = 500; + dumpLog.WriteLine("Checksum starts."); for(ulong i = 0; i < blocks; i += blocksToRead) { if(aborted) + { + dumpLog.WriteLine("Aborted!"); break; + } if((blocks - i) < blocksToRead) blocksToRead = (byte)(blocks - i); @@ -423,6 +453,8 @@ namespace DiscImageChef.Core.Devices.Dumping DicConsole.WriteLine(); dumpFile.Close(); end = DateTime.UtcNow; + dumpLog.WriteLine("Checksum finished in {0} seconds.", (end - start).TotalSeconds); + dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalChkDuration / 1000)); PluginBase plugins = new PluginBase(); plugins.RegisterAllPlugins(); @@ -452,8 +484,10 @@ namespace DiscImageChef.Core.Devices.Dumping if(_imageFormat != null) { + dumpLog.WriteLine("Getting partitions."); List partitions = Partitions.GetAll(_imageFormat); Partitions.AddSchemesToStats(partitions); + dumpLog.WriteLine("Found {0} partitions.", partitions.Count); if(partitions.Count > 0) { @@ -470,6 +504,8 @@ namespace DiscImageChef.Core.Devices.Dumping Type = partitions[i].Type }; List lstFs = new List(); + dumpLog.WriteLine("Getting filesystems on partition {0}, starting at {1}, ending at {2}, with type {3}, under scheme {4}.", + i, partitions[i].Start, partitions[i].End, partitions[i].Type, partitions[i].Scheme); foreach(Filesystem _plugin in plugins.PluginsList.Values) { @@ -480,6 +516,7 @@ namespace DiscImageChef.Core.Devices.Dumping _plugin.GetInformation(_imageFormat, partitions[i], out string foo); lstFs.Add(_plugin.XmlFSType); Statistics.AddFilesystem(_plugin.XmlFSType.Type); + dumpLog.WriteLine("Filesystem {0} found.", _plugin.XmlFSType.Type); } } #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body @@ -496,6 +533,8 @@ namespace DiscImageChef.Core.Devices.Dumping } else { + dumpLog.WriteLine("Getting filesystem for whole device."); + xmlFileSysInfo = new PartitionType[1]; xmlFileSysInfo[0] = new PartitionType { @@ -520,6 +559,7 @@ namespace DiscImageChef.Core.Devices.Dumping _plugin.GetInformation(_imageFormat, wholePart, out string foo); lstFs.Add(_plugin.XmlFSType); Statistics.AddFilesystem(_plugin.XmlFSType.Type); + dumpLog.WriteLine("Filesystem {0} found.", _plugin.XmlFSType.Type); } } #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body diff --git a/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs b/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs index 89fb8f5e..af92288f 100644 --- a/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs +++ b/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs @@ -52,7 +52,7 @@ namespace DiscImageChef.Core.Devices.Dumping internal class CompactDisc { // TODO: Add support for resume file - internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar, ref MediaType dskType, bool separateSubchannel, ref Metadata.Resume resume) + internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar, ref MediaType dskType, bool separateSubchannel, ref Metadata.Resume resume, ref DumpLog dumpLog) { MHDDLog mhddLog; IBGLog ibgLog; @@ -84,6 +84,7 @@ namespace DiscImageChef.Core.Devices.Dumping // We discarded all discs that falsify a TOC before requesting a real TOC // No TOC, no CD (or an empty one) + dumpLog.WriteLine("Reading full TOC"); bool tocSense = dev.ReadRawToc(out byte[] cmdBuf, out byte[] senseBuf, 1, dev.Timeout, out double duration); if(!tocSense) { @@ -101,6 +102,7 @@ namespace DiscImageChef.Core.Devices.Dumping DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].TOC.Image, tmpBuf); // ATIP exists on blank CDs + dumpLog.WriteLine("Reading ATIP"); sense = dev.ReadAtip(out cmdBuf, out senseBuf, dev.Timeout, out duration); if(!sense) { @@ -128,6 +130,7 @@ namespace DiscImageChef.Core.Devices.Dumping } } + dumpLog.WriteLine("Reading Disc Information"); sense = dev.ReadDiscInformation(out cmdBuf, out senseBuf, MmcDiscInformationDataTypes.DiscInformation, dev.Timeout, out duration); if(!sense) { @@ -153,6 +156,7 @@ namespace DiscImageChef.Core.Devices.Dumping int sessions = 1; int firstTrackLastSession = 0; + dumpLog.WriteLine("Reading Session Information"); sense = dev.ReadSessionInfo(out cmdBuf, out senseBuf, dev.Timeout, out duration); if(!sense) { @@ -205,6 +209,7 @@ namespace DiscImageChef.Core.Devices.Dumping dskType = MediaType.CDV; } + dumpLog.WriteLine("Reading PMA"); sense = dev.ReadPma(out cmdBuf, out senseBuf, dev.Timeout, out duration); if(!sense) { @@ -222,6 +227,7 @@ namespace DiscImageChef.Core.Devices.Dumping } } + dumpLog.WriteLine("Reading CD-Text from Lead-In"); sense = dev.ReadCdText(out cmdBuf, out senseBuf, dev.Timeout, out duration); if(!sense) { @@ -395,10 +401,14 @@ namespace DiscImageChef.Core.Devices.Dumping readBuffer = null; + dumpLog.WriteLine("Reading Lead-in"); for(int leadInBlock = -150; leadInBlock < 0 && resume.NextBlock == 0; leadInBlock++) { if(aborted) + { + dumpLog.WriteLine("Aborted!"); break; + } #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator if(currentSpeed > maxSpeed && currentSpeed != 0) @@ -454,6 +464,7 @@ namespace DiscImageChef.Core.Devices.Dumping DicConsole.WriteLine(); DicConsole.WriteLine("Got {0} lead-in sectors.", leadInSectorsGood); + dumpLog.WriteLine("Got {0} Lead-in sectors.", leadInSectorsGood); while(true) { @@ -471,12 +482,19 @@ namespace DiscImageChef.Core.Devices.Dumping if(dev.Error) { + DicConsole.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; } DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead); + 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("SCSI device type: {0}.", dev.SCSIType); + dumpLog.WriteLine("Media identified as {0}.", dskType); + dumpFile = new DataFile(outputPrefix + ".bin"); DataFile subFile = null; if(separateSubchannel) @@ -487,10 +505,15 @@ namespace DiscImageChef.Core.Devices.Dumping dumpFile.Seek(resume.NextBlock, (ulong)sectorSize); if(separateSubchannel) subFile.Seek(resume.NextBlock, subSize); - + + if(resume.NextBlock > 0) + dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock); + start = DateTime.UtcNow; for(int t = 0; t < tracks.Count(); t++) { + dumpLog.WriteLine("Reading track {0}", t); + tracks[t].BytesPerSector = sectorSize; tracks[t].Image = new ImageType { @@ -527,6 +550,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(aborted) { currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } @@ -592,6 +616,7 @@ namespace DiscImageChef.Core.Devices.Dumping mhddLog.Write(i, cmdDuration); ibgLog.Write(i, 0); + dumpLog.WriteLine("Error reading {0} sectors from sector {1}.", blocksToRead, i); } if(tracks[t].TrackType1 == TrackTypeTrackType.mode1 && !checkedDataFormat) @@ -630,6 +655,8 @@ namespace DiscImageChef.Core.Devices.Dumping #pragma warning disable IDE0004 // Remove Unnecessary Cast ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalDuration / 1000), devicePath); #pragma warning restore IDE0004 // Remove Unnecessary Cast + 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)); #region Compact Disc Error handling if(resume.BadBlocks.Count > 0 && !aborted) @@ -645,6 +672,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(aborted) { currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } @@ -665,6 +693,7 @@ namespace DiscImageChef.Core.Devices.Dumping { resume.BadBlocks.Remove(badSector); extents.Add(badSector); + dumpLog.WriteLine("Correctly retried sector {0} in pass {1}.", badSector, pass); } if(separateSubchannel) @@ -728,6 +757,7 @@ namespace DiscImageChef.Core.Devices.Dumping md6 = Decoders.SCSI.Modes.EncodeMode6(md, dev.SCSIType); md10 = Decoders.SCSI.Modes.EncodeMode10(md, dev.SCSIType); + dumpLog.WriteLine("Sending MODE SELECT to drive."); sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out duration); if(sense) { @@ -754,6 +784,7 @@ namespace DiscImageChef.Core.Devices.Dumping md6 = Decoders.SCSI.Modes.EncodeMode6(md, dev.SCSIType); md10 = Decoders.SCSI.Modes.EncodeMode10(md, dev.SCSIType); + dumpLog.WriteLine("Sending MODE SELECT to drive."); sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out duration); if(sense) { @@ -773,6 +804,7 @@ namespace DiscImageChef.Core.Devices.Dumping subFile.Seek(0, SeekOrigin.Begin); blocksToRead = 500; + dumpLog.WriteLine("Checksum starts."); for(int t = 0; t < tracks.Count(); t++) { Checksum trkChk = new Checksum(); @@ -781,7 +813,10 @@ namespace DiscImageChef.Core.Devices.Dumping for(ulong i = (ulong)tracks[t].StartSector; i <= (ulong)tracks[t].EndSector; i += blocksToRead) { if(aborted) + { + dumpLog.WriteLine("Aborted!"); break; + } if(((ulong)tracks[t].EndSector + 1 - i) < blocksToRead) blocksToRead = (uint)((ulong)tracks[t].EndSector + 1 - i); @@ -828,6 +863,8 @@ namespace DiscImageChef.Core.Devices.Dumping DicConsole.WriteLine(); dumpFile.Close(); end = DateTime.UtcNow; + dumpLog.WriteLine("Checksum finished in {0} seconds.", (end - start).TotalSeconds); + dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalChkDuration / 1000)); // TODO: Correct this sidecar.OpticalDisc[0].Checksums = dataChk.End().ToArray(); diff --git a/DiscImageChef.Core/Devices/Dumping/MMC.cs b/DiscImageChef.Core/Devices/Dumping/MMC.cs index 17700c66..7780127c 100644 --- a/DiscImageChef.Core/Devices/Dumping/MMC.cs +++ b/DiscImageChef.Core/Devices/Dumping/MMC.cs @@ -39,13 +39,14 @@ using System; using DiscImageChef.CommonTypes; using DiscImageChef.Console; using DiscImageChef.Devices; +using DiscImageChef.Core.Logging; using Schemas; namespace DiscImageChef.Core.Devices.Dumping { internal static class MMC { - internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar, ref MediaType dskType, bool separateSubchannel, ref Metadata.Resume resume) + internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar, ref MediaType dskType, bool separateSubchannel, ref Metadata.Resume resume, ref DumpLog dumpLog) { byte[] cmdBuf = null; byte[] senseBuf = null; @@ -60,11 +61,14 @@ namespace DiscImageChef.Core.Devices.Dumping sidecar.OpticalDisc = new OpticalDiscType[1]; sidecar.OpticalDisc[0] = new OpticalDiscType(); + // TODO: Log not only what is it reading, but if it was read correctly or not. + sense = dev.GetConfiguration(out cmdBuf, out senseBuf, 0, MmcGetConfigurationRt.Current, dev.Timeout, out duration); if(!sense) { Decoders.SCSI.MMC.Features.SeparatedFeatures ftr = Decoders.SCSI.MMC.Features.Separate(cmdBuf); currentProfile = ftr.CurrentProfile; + dumpLog.WriteLine("Device reports current profile is 0x{0:X4}", ftr.CurrentProfile); switch(ftr.CurrentProfile) { @@ -163,16 +167,18 @@ namespace DiscImageChef.Core.Devices.Dumping if(compactDisc) { - CompactDisc.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType, separateSubchannel, ref resume); + CompactDisc.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType, separateSubchannel, ref resume, ref dumpLog); return; } Reader scsiReader = new Reader(dev, dev.Timeout, null, dumpRaw); blocks = scsiReader.GetDeviceBlocks(); + dumpLog.WriteLine("Device reports disc has {0} blocks", blocks); #region Nintendo if(dskType == MediaType.Unknown && blocks > 0) { + dumpLog.WriteLine("Reading Physical Format Information"); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.PhysicalInformation, 0, dev.Timeout, out duration); if(!sense) { @@ -182,6 +188,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(nintendoPfi.Value.DiskCategory == Decoders.DVD.DiskCategory.Nintendo && nintendoPfi.Value.PartVersion == 15) { + dumpLog.WriteLine("Dumping Nintendo GameCube or Wii discs is not yet implemented."); throw new NotImplementedException("Dumping Nintendo GameCube or Wii discs is not yet implemented."); } } @@ -200,7 +207,7 @@ namespace DiscImageChef.Core.Devices.Dumping dskType == MediaType.HDDVDROM || dskType == MediaType.HDDVDRW || dskType == MediaType.HDDVDRWDL) { - + dumpLog.WriteLine("Reading Physical Format Information"); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.PhysicalInformation, 0, dev.Timeout, out duration); if(!sense) { @@ -279,6 +286,8 @@ namespace DiscImageChef.Core.Devices.Dumping } } } + + dumpLog.WriteLine("Reading Disc Manufacturing Information"); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.DiscManufacturingInformation, 0, dev.Timeout, out duration); if(!sense) { @@ -301,7 +310,10 @@ namespace DiscImageChef.Core.Devices.Dumping if(sense || !Decoders.SCSI.Inquiry.Decode(inqBuf).HasValue || (Decoders.SCSI.Inquiry.Decode(inqBuf).HasValue && !Decoders.SCSI.Inquiry.Decode(inqBuf).Value.KreonPresent)) + { + dumpLog.WriteLine("Dumping Xbox Game Discs requires a drive with Kreon firmware."); throw new NotImplementedException("Dumping Xbox Game Discs requires a drive with Kreon firmware."); + } if(dumpRaw && !force) { @@ -332,6 +344,7 @@ namespace DiscImageChef.Core.Devices.Dumping #region DVD-ROM if(dskType == MediaType.DVDDownload || dskType == MediaType.DVDROM) { + dumpLog.WriteLine("Reading Lead-in Copyright Information."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.CopyrightInformation, 0, dev.Timeout, out duration); if(!sense) { @@ -359,6 +372,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(dskType == MediaType.DVDDownload || dskType == MediaType.DVDROM || dskType == MediaType.HDDVDROM) { + dumpLog.WriteLine("Reading Burst Cutting Area."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.BurstCuttingArea, 0, dev.Timeout, out duration); if(!sense) { @@ -378,6 +392,7 @@ namespace DiscImageChef.Core.Devices.Dumping #region DVD-RAM and HD DVD-RAM if(dskType == MediaType.DVDRAM || dskType == MediaType.HDDVDRAM) { + dumpLog.WriteLine("Reading Disc Description Structure."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.DVDRAM_DDS, 0, dev.Timeout, out duration); if(!sense) { @@ -395,6 +410,7 @@ namespace DiscImageChef.Core.Devices.Dumping } } + dumpLog.WriteLine("Reading Spare Area Information."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.DVDRAM_SpareAreaInformation, 0, dev.Timeout, out duration); if(!sense) { @@ -417,6 +433,7 @@ namespace DiscImageChef.Core.Devices.Dumping #region DVD-R and DVD-RW if(dskType == MediaType.DVDR || dskType == MediaType.DVDRW) { + dumpLog.WriteLine("Reading Pre-Recorded Information."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.PreRecordedInfo, 0, dev.Timeout, out duration); if(!sense) { @@ -436,6 +453,7 @@ namespace DiscImageChef.Core.Devices.Dumping #region DVD-R, DVD-RW and HD DVD-R if(dskType == MediaType.DVDR || dskType == MediaType.DVDRW || dskType == MediaType.HDDVDR) { + dumpLog.WriteLine("Reading Media Identifier."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.DVDR_MediaIdentifier, 0, dev.Timeout, out duration); if(!sense) { @@ -450,6 +468,7 @@ namespace DiscImageChef.Core.Devices.Dumping DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].MediaID.Image, tmpBuf); } + dumpLog.WriteLine("Reading Recordable Physical Information."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.DVDR_PhysicalInformation, 0, dev.Timeout, out duration); if(!sense) { @@ -470,6 +489,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(dskType == MediaType.DVDPR || dskType == MediaType.DVDPRDL || dskType == MediaType.DVDPRW || dskType == MediaType.DVDPRWDL) { + dumpLog.WriteLine("Reading ADdress In Pregroove."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.ADIP, 0, dev.Timeout, out duration); if(!sense) { @@ -484,6 +504,7 @@ namespace DiscImageChef.Core.Devices.Dumping DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].ADIP.Image, tmpBuf); } + dumpLog.WriteLine("Reading Disc Control Blocks."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.DCB, 0, dev.Timeout, out duration); if(!sense) { @@ -503,6 +524,7 @@ namespace DiscImageChef.Core.Devices.Dumping #region HD DVD-ROM if(dskType == MediaType.HDDVDROM) { + dumpLog.WriteLine("Reading Lead-in Copyright Information."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.HDDVD_CopyrightInformation, 0, dev.Timeout, out duration); if(!sense) { @@ -523,6 +545,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(dskType == MediaType.BDR || dskType == MediaType.BDRE || dskType == MediaType.BDROM || dskType == MediaType.BDRXL || dskType == MediaType.BDREXL) { + dumpLog.WriteLine("Reading Disc Information."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.BD, 0, 0, MmcDiscStructureFormat.DiscInformation, 0, dev.Timeout, out duration); if(!sense) { @@ -540,6 +563,7 @@ namespace DiscImageChef.Core.Devices.Dumping } } + dumpLog.WriteLine("Reading PAC."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.BD, 0, 0, MmcDiscStructureFormat.PAC, 0, dev.Timeout, out duration); if(!sense) { @@ -560,6 +584,7 @@ namespace DiscImageChef.Core.Devices.Dumping #region BD-ROM only if(dskType == MediaType.BDROM) { + dumpLog.WriteLine("Reading Burst Cutting Area."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.BD, 0, 0, MmcDiscStructureFormat.BD_BurstCuttingArea, 0, dev.Timeout, out duration); if(!sense) { @@ -580,6 +605,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(dskType == MediaType.BDR || dskType == MediaType.BDRE || dskType == MediaType.BDRXL || dskType == MediaType.BDREXL) { + dumpLog.WriteLine("Reading Disc Definition Structure."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.BD, 0, 0, MmcDiscStructureFormat.BD_DDS, 0, dev.Timeout, out duration); if(!sense) { @@ -594,6 +620,7 @@ namespace DiscImageChef.Core.Devices.Dumping DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].DDS.Image, tmpBuf); } + dumpLog.WriteLine("Reading Spare Area Information."); sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.BD, 0, 0, MmcDiscStructureFormat.BD_SpareAreaInformation, 0, dev.Timeout, out duration); if(!sense) { @@ -612,11 +639,11 @@ namespace DiscImageChef.Core.Devices.Dumping if(isXbox) { - XGD.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType, ref resume); + XGD.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType, ref resume, ref dumpLog); return; } - SBC.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType, true, ref resume); + SBC.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType, true, ref resume, ref dumpLog); } } } diff --git a/DiscImageChef.Core/Devices/Dumping/NVMe.cs b/DiscImageChef.Core/Devices/Dumping/NVMe.cs index edf5c718..bc421942 100644 --- a/DiscImageChef.Core/Devices/Dumping/NVMe.cs +++ b/DiscImageChef.Core/Devices/Dumping/NVMe.cs @@ -37,12 +37,13 @@ // //$Id$ using System; using DiscImageChef.Devices; +using DiscImageChef.Core.Logging; namespace DiscImageChef.Core.Devices.Dumping { public static class NVMe { - public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref Metadata.Resume resume) + public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref Metadata.Resume resume, ref DumpLog dumpLog) { throw new NotImplementedException("NVMe devices not yet supported."); } diff --git a/DiscImageChef.Core/Devices/Dumping/SBC.cs b/DiscImageChef.Core/Devices/Dumping/SBC.cs index 601ec9da..21eed860 100644 --- a/DiscImageChef.Core/Devices/Dumping/SBC.cs +++ b/DiscImageChef.Core/Devices/Dumping/SBC.cs @@ -53,7 +53,7 @@ namespace DiscImageChef.Core.Devices.Dumping { internal static class SBC { - internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar, ref MediaType dskType, bool opticalDisc, ref Metadata.Resume resume) + internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar, ref MediaType dskType, bool opticalDisc, ref Metadata.Resume resume, ref DumpLog dumpLog) { MHDDLog mhddLog; IBGLog ibgLog; @@ -87,11 +87,13 @@ namespace DiscImageChef.Core.Devices.Dumping e.Cancel = aborted = true; }; + dumpLog.WriteLine("Initializing reader."); Reader scsiReader = new Reader(dev, dev.Timeout, null, dumpRaw); blocks = scsiReader.GetDeviceBlocks(); blockSize = scsiReader.LogicalBlockSize; if(scsiReader.FindReadCommand()) { + dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", scsiReader.ErrorMessage); DicConsole.ErrorWriteLine("Unable to read medium."); return; } @@ -105,6 +107,7 @@ namespace DiscImageChef.Core.Devices.Dumping // Check how many blocks to read, if error show and return if(scsiReader.GetBlocksToRead()) { + dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", scsiReader.ErrorMessage); DicConsole.ErrorWriteLine(scsiReader.ErrorMessage); return; } @@ -114,6 +117,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(blocks == 0) { + dumpLog.WriteLine("ERROR: Unable to read medium or empty medium present..."); DicConsole.ErrorWriteLine("Unable to read medium or empty medium present..."); return; } @@ -126,6 +130,16 @@ namespace DiscImageChef.Core.Devices.Dumping DicConsole.WriteLine("Media identified as {0}", 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("SCSI density type: {0}.", scsiDensityCode); + dumpLog.WriteLine("SCSI floppy mode page present: {0}.", containsFloppyPage); + dumpLog.WriteLine("Media identified as {0}.", dskType); + if(!opticalDisc) { sidecar.BlockMedia = new BlockMediaType[1]; @@ -136,6 +150,7 @@ namespace DiscImageChef.Core.Devices.Dumping { if(dev.IsUSB) { + dumpLog.WriteLine("Reading USB descriptors."); sidecar.BlockMedia[0].USB = new USBType { ProductID = dev.USBProductID, @@ -152,6 +167,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(dev.Type == DeviceType.ATAPI) { + dumpLog.WriteLine("Requesting ATAPI IDENTIFY PACKET DEVICE."); sense = dev.AtapiIdentify(out cmdBuf, out Decoders.ATA.AtaErrorRegistersCHS errorRegs); if(!sense) { @@ -171,6 +187,7 @@ namespace DiscImageChef.Core.Devices.Dumping sense = dev.ScsiInquiry(out cmdBuf, out senseBuf); if(!sense) { + dumpLog.WriteLine("Requesting SCSI INQUIRY."); sidecar.BlockMedia[0].SCSI = new SCSIType { Inquiry = new DumpType @@ -182,6 +199,7 @@ namespace DiscImageChef.Core.Devices.Dumping }; DataFile.WriteTo("SCSI Dump", sidecar.BlockMedia[0].SCSI.Inquiry.Image, cmdBuf); + dumpLog.WriteLine("Reading SCSI Extended Vendor Page Descriptors."); sense = dev.ScsiInquiry(out cmdBuf, out senseBuf, 0x00); if(!sense) { @@ -192,6 +210,7 @@ namespace DiscImageChef.Core.Devices.Dumping List evpds = new List(); foreach(byte page in pages) { + dumpLog.WriteLine("Requesting page {0:X2}h.", page); sense = dev.ScsiInquiry(out cmdBuf, out senseBuf, page); if(!sense) { @@ -212,6 +231,7 @@ namespace DiscImageChef.Core.Devices.Dumping } } + 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) { @@ -235,6 +255,7 @@ namespace DiscImageChef.Core.Devices.Dumping } } + 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); @@ -327,12 +348,15 @@ namespace DiscImageChef.Core.Devices.Dumping if(currentTry == null || extents == null) throw new Exception("Could not process resume file, not continuing..."); dumpFile.Seek(resume.NextBlock, blockSize); + if(resume.NextBlock > 0) + dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock); for(ulong i = resume.NextBlock; i < blocks; i += blocksToRead) { if(aborted) { currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } @@ -376,6 +400,7 @@ namespace DiscImageChef.Core.Devices.Dumping mhddLog.Write(i, cmdDuration); ibgLog.Write(i, 0); + dumpLog.WriteLine("Error reading {0} blocks from block {1}.", blocksToRead, i); } #pragma warning disable IDE0004 // Remove Unnecessary Cast @@ -389,6 +414,8 @@ namespace DiscImageChef.Core.Devices.Dumping #pragma warning disable IDE0004 // Remove Unnecessary Cast ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalDuration / 1000), devicePath); #pragma warning restore IDE0004 // Remove Unnecessary Cast + 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)); #region Error handling if(resume.BadBlocks.Count > 0 && !aborted) @@ -404,6 +431,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(aborted) { currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } @@ -417,6 +445,7 @@ namespace DiscImageChef.Core.Devices.Dumping resume.BadBlocks.Remove(badSector); extents.Add(badSector); dumpFile.WriteAt(readBuffer, badSector, blockSize); + dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } else if(runningPersistent) dumpFile.WriteAt(readBuffer, badSector, blockSize); @@ -507,6 +536,7 @@ namespace DiscImageChef.Core.Devices.Dumping md10 = Decoders.SCSI.Modes.EncodeMode10(md, dev.SCSIType); } + dumpLog.WriteLine("Sending MODE SELECT to drive."); sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out duration); if(sense) { @@ -533,6 +563,7 @@ namespace DiscImageChef.Core.Devices.Dumping md6 = Decoders.SCSI.Modes.EncodeMode6(md, dev.SCSIType); md10 = Decoders.SCSI.Modes.EncodeMode10(md, dev.SCSIType); + dumpLog.WriteLine("Sending MODE SELECT to drive."); sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out duration); if(sense) { @@ -550,10 +581,14 @@ namespace DiscImageChef.Core.Devices.Dumping dumpFile.Seek(0, SeekOrigin.Begin); blocksToRead = 500; + dumpLog.WriteLine("Checksum starts."); for(ulong i = 0; i < blocks; i += blocksToRead) { if(aborted) + { + dumpLog.WriteLine("Aborted!"); break; + } if((blocks - i) < blocksToRead) blocksToRead = (uint)(blocks - i); @@ -576,6 +611,8 @@ namespace DiscImageChef.Core.Devices.Dumping DicConsole.WriteLine(); dumpFile.Close(); end = DateTime.UtcNow; + dumpLog.WriteLine("Checksum finished in {0} seconds.", (end - start).TotalSeconds); + dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalChkDuration / 1000)); PluginBase plugins = new PluginBase(); plugins.RegisterAllPlugins(); @@ -604,8 +641,10 @@ namespace DiscImageChef.Core.Devices.Dumping if(_imageFormat != null) { + dumpLog.WriteLine("Getting partitions."); List partitions = Partitions.GetAll(_imageFormat); Partitions.AddSchemesToStats(partitions); + dumpLog.WriteLine("Found {0} partitions.", partitions.Count); if(partitions.Count > 0) { @@ -622,6 +661,8 @@ namespace DiscImageChef.Core.Devices.Dumping Type = partitions[i].Type }; List lstFs = new List(); + dumpLog.WriteLine("Getting filesystems on partition {0}, starting at {1}, ending at {2}, with type {3}, under scheme {4}.", + i, partitions[i].Start, partitions[i].End, partitions[i].Type, partitions[i].Scheme); foreach(Filesystem _plugin in plugins.PluginsList.Values) { @@ -632,6 +673,7 @@ namespace DiscImageChef.Core.Devices.Dumping _plugin.GetInformation(_imageFormat, partitions[i], out string foo); lstFs.Add(_plugin.XmlFSType); Statistics.AddFilesystem(_plugin.XmlFSType.Type); + dumpLog.WriteLine("Filesystem {0} found.", _plugin.XmlFSType.Type); if(_plugin.XmlFSType.Type == "Opera") dskType = MediaType.ThreeDO; @@ -657,6 +699,7 @@ namespace DiscImageChef.Core.Devices.Dumping } else { + dumpLog.WriteLine("Getting filesystem for whole device."); xmlFileSysInfo = new PartitionType[1]; xmlFileSysInfo[0] = new PartitionType { @@ -681,6 +724,7 @@ namespace DiscImageChef.Core.Devices.Dumping _plugin.GetInformation(_imageFormat, wholePart, out string foo); lstFs.Add(_plugin.XmlFSType); Statistics.AddFilesystem(_plugin.XmlFSType.Type); + dumpLog.WriteLine("Filesystem {0} found.", _plugin.XmlFSType.Type); if(_plugin.XmlFSType.Type == "Opera") dskType = MediaType.ThreeDO; diff --git a/DiscImageChef.Core/Devices/Dumping/SCSI.cs b/DiscImageChef.Core/Devices/Dumping/SCSI.cs index 9eada20b..a76e8997 100644 --- a/DiscImageChef.Core/Devices/Dumping/SCSI.cs +++ b/DiscImageChef.Core/Devices/Dumping/SCSI.cs @@ -40,6 +40,7 @@ using System.IO; using DiscImageChef.CommonTypes; using DiscImageChef.Console; using DiscImageChef.Devices; +using DiscImageChef.Core.Logging; using Schemas; namespace DiscImageChef.Core.Devices.Dumping @@ -47,7 +48,7 @@ namespace DiscImageChef.Core.Devices.Dumping public class SCSI { // TODO: Get cartridge serial number from Certance vendor EVPD - public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, bool separateSubchannel, ref Metadata.Resume resume) + public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, bool separateSubchannel, ref Metadata.Resume resume, ref DumpLog dumpLog) { byte[] senseBuf = null; bool sense = false; @@ -61,6 +62,7 @@ namespace DiscImageChef.Core.Devices.Dumping Decoders.SCSI.FixedSense? decSense = Decoders.SCSI.Sense.DecodeFixed(senseBuf); if(decSense.HasValue) { + dumpLog.WriteLine("Device not ready. Sense {0:X2}h ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); if(decSense.Value.ASC == 0x3A) { int leftRetries = 5; @@ -72,6 +74,9 @@ namespace DiscImageChef.Core.Devices.Dumping if(!sense) break; + decSense = Decoders.SCSI.Sense.DecodeFixed(senseBuf); + if(decSense.HasValue) + dumpLog.WriteLine("Device not ready. Sense {0:X2}h ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); leftRetries--; } @@ -92,6 +97,9 @@ namespace DiscImageChef.Core.Devices.Dumping if(!sense) break; + decSense = Decoders.SCSI.Sense.DecodeFixed(senseBuf); + if(decSense.HasValue) + dumpLog.WriteLine("Device not ready. Sense {0:X2}h ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); leftRetries--; } @@ -125,6 +133,9 @@ namespace DiscImageChef.Core.Devices.Dumping if(!sense) break; + decSense = Decoders.SCSI.Sense.DecodeFixed(senseBuf); + if(decSense.HasValue) + dumpLog.WriteLine("Device not ready. Sense {0:X2}h ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); leftRetries--; } @@ -155,17 +166,17 @@ namespace DiscImageChef.Core.Devices.Dumping if(dumpRaw) throw new ArgumentException("Tapes cannot be dumped raw."); - SSC.Dump(dev, outputPrefix, devicePath, ref sidecar, ref resume); + SSC.Dump(dev, outputPrefix, devicePath, ref sidecar, ref resume, ref dumpLog); return; } if(dev.SCSIType == Decoders.SCSI.PeripheralDeviceTypes.MultiMediaDevice) { - MMC.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType, separateSubchannel, ref resume); + MMC.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType, separateSubchannel, ref resume, ref dumpLog); return; } - SBC.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType, false, ref resume); + SBC.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType, false, ref resume, ref dumpLog); } } } diff --git a/DiscImageChef.Core/Devices/Dumping/SSC.cs b/DiscImageChef.Core/Devices/Dumping/SSC.cs index 6fd1d477..9d9a1519 100644 --- a/DiscImageChef.Core/Devices/Dumping/SSC.cs +++ b/DiscImageChef.Core/Devices/Dumping/SSC.cs @@ -48,7 +48,7 @@ namespace DiscImageChef.Core.Devices.Dumping { internal static class SSC { - internal static void Dump(Device dev, string outputPrefix, string devicePath, ref CICMMetadataType sidecar, ref Metadata.Resume resume) + internal static void Dump(Device dev, string outputPrefix, string devicePath, ref CICMMetadataType sidecar, ref Metadata.Resume resume, ref DumpLog dumpLog) { Decoders.SCSI.FixedSense? fxSense; bool aborted; @@ -73,6 +73,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(fxSense.HasValue && fxSense.Value.SenseKey != Decoders.SCSI.SenseKeys.NoSense) { + dumpLog.WriteLine("Device not ready. Sense {0:X2}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; @@ -81,6 +82,7 @@ namespace DiscImageChef.Core.Devices.Dumping // Not in BOM/P if(fxSense.HasValue && fxSense.Value.ASC == 0x00 && fxSense.Value.ASCQ != 0x00 && fxSense.Value.ASCQ != 0x04 && fxSense.Value.SenseKey != Decoders.SCSI.SenseKeys.IllegalRequest) { + dumpLog.WriteLine("Rewinding, please wait..."); DicConsole.Write("Rewinding, please wait..."); // Rewind, let timeout apply sense = dev.Rewind(out senseBuf, dev.Timeout, out duration); @@ -104,6 +106,8 @@ namespace DiscImageChef.Core.Devices.Dumping 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:X2}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } @@ -123,6 +127,8 @@ namespace DiscImageChef.Core.Devices.Dumping { 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:X2}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } @@ -132,6 +138,7 @@ namespace DiscImageChef.Core.Devices.Dumping 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) @@ -139,6 +146,8 @@ namespace DiscImageChef.Core.Devices.Dumping 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:X2}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } @@ -159,6 +168,8 @@ namespace DiscImageChef.Core.Devices.Dumping 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:X2}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } @@ -168,6 +179,8 @@ namespace DiscImageChef.Core.Devices.Dumping fxSense = Decoders.SCSI.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:X2}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } @@ -175,6 +188,7 @@ namespace DiscImageChef.Core.Devices.Dumping 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; } @@ -190,6 +204,7 @@ namespace DiscImageChef.Core.Devices.Dumping 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) { @@ -213,6 +228,7 @@ namespace DiscImageChef.Core.Devices.Dumping } } + 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); @@ -241,6 +257,7 @@ namespace DiscImageChef.Core.Devices.Dumping 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; @@ -250,6 +267,11 @@ namespace DiscImageChef.Core.Devices.Dumping 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; @@ -279,6 +301,8 @@ namespace DiscImageChef.Core.Devices.Dumping 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:X2}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } @@ -291,6 +315,8 @@ namespace DiscImageChef.Core.Devices.Dumping 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:X2}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } @@ -299,6 +325,8 @@ namespace DiscImageChef.Core.Devices.Dumping 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:X2}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } @@ -306,6 +334,7 @@ namespace DiscImageChef.Core.Devices.Dumping { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Cannot read device, don't know why, exiting..."); + dumpLog.WriteLine("Cannot read device, don't know why, exiting..."); return; } } @@ -319,6 +348,8 @@ namespace DiscImageChef.Core.Devices.Dumping 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:X2}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } @@ -374,12 +405,16 @@ namespace DiscImageChef.Core.Devices.Dumping 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); @@ -451,6 +486,7 @@ namespace DiscImageChef.Core.Devices.Dumping 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; @@ -462,6 +498,8 @@ namespace DiscImageChef.Core.Devices.Dumping 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:X2}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } @@ -475,6 +513,7 @@ namespace DiscImageChef.Core.Devices.Dumping DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Cannot dump a blank tape..."); dumpFile.Close(); + dumpLog.WriteLine("Cannot dump a blank tape..."); return; } @@ -483,12 +522,14 @@ namespace DiscImageChef.Core.Devices.Dumping { // TODO: Detect end of partition endOfMedia = true; + dumpLog.WriteLine("Found end-of-tape/partition..."); continue; } DicConsole.WriteLine(); DicConsole.WriteLine("Blank block found, end of tape?"); endOfMedia = true; + dumpLog.WriteLine("Blank block found, end of tape?..."); continue; } @@ -497,6 +538,7 @@ namespace DiscImageChef.Core.Devices.Dumping { // TODO: Detect end of partition endOfMedia = true; + dumpLog.WriteLine("Found end-of-tape/partition..."); continue; } @@ -527,6 +569,7 @@ namespace DiscImageChef.Core.Devices.Dumping 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; } @@ -534,6 +577,8 @@ namespace DiscImageChef.Core.Devices.Dumping fxSense = Decoders.SCSI.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:X2}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } @@ -562,6 +607,9 @@ namespace DiscImageChef.Core.Devices.Dumping end = DateTime.UtcNow; mhddLog.Close(); ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (((double)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); #pragma warning disable IDE0004 // Cast is necessary, otherwise incorrect value is created diff --git a/DiscImageChef.Core/Devices/Dumping/SecureDigital.cs b/DiscImageChef.Core/Devices/Dumping/SecureDigital.cs index c6ecfa38..93d48f43 100644 --- a/DiscImageChef.Core/Devices/Dumping/SecureDigital.cs +++ b/DiscImageChef.Core/Devices/Dumping/SecureDigital.cs @@ -53,7 +53,7 @@ namespace DiscImageChef.Core.Devices.Dumping { public class SecureDigital { - public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref Metadata.Resume resume) + public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref Metadata.Resume resume, ref DumpLog dumpLog) { bool aborted; MHDDLog mhddLog; @@ -99,6 +99,7 @@ namespace DiscImageChef.Core.Devices.Dumping ExtendedCSD ecsdDecoded = new ExtendedCSD(); CSD csdDecoded = new CSD(); + dumpLog.WriteLine("Reading Extended CSD"); sense = dev.ReadExtendedCSD(out ecsd, out response, timeout, out duration); if(!sense) { @@ -116,6 +117,7 @@ namespace DiscImageChef.Core.Devices.Dumping else ecsd = null; + dumpLog.WriteLine("Reading CSD"); sense = dev.ReadCSD(out csd, out response, timeout, out duration); if(!sense) { @@ -129,6 +131,7 @@ namespace DiscImageChef.Core.Devices.Dumping else csd = null; + dumpLog.WriteLine("Reading OCR"); sense = dev.ReadOCR(out ocr, out response, timeout, out duration); if(sense) ocr = null; @@ -139,6 +142,7 @@ namespace DiscImageChef.Core.Devices.Dumping { Decoders.SecureDigital.CSD csdDecoded = new Decoders.SecureDigital.CSD(); + dumpLog.WriteLine("Reading CSD"); sense = dev.ReadCSD(out csd, out response, timeout, out duration); if(!sense) { @@ -151,10 +155,12 @@ namespace DiscImageChef.Core.Devices.Dumping else csd = null; + dumpLog.WriteLine("Reading OCR"); sense = dev.ReadSDOCR(out ocr, out response, timeout, out duration); if(sense) ocr = null; + dumpLog.WriteLine("Reading SCR"); sense = dev.ReadSCR(out scr, out response, timeout, out duration); if(sense) scr = null; @@ -162,6 +168,7 @@ namespace DiscImageChef.Core.Devices.Dumping sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType(); } + dumpLog.WriteLine("Reading CID"); sense = dev.ReadCID(out cid, out response, timeout, out duration); if(sense) cid = null; @@ -253,9 +260,11 @@ namespace DiscImageChef.Core.Devices.Dumping if(blocks == 0) { + dumpLog.WriteLine("Cannot get device size."); DicConsole.ErrorWriteLine("Unable to get device size."); return; } + dumpLog.WriteLine("Device reports {0} blocks.", blocks); byte[] cmdBuf; bool error = true; @@ -274,138 +283,151 @@ namespace DiscImageChef.Core.Devices.Dumping if(error) { blocksToRead = 1; + dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", dev.LastError); DicConsole.ErrorWriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError); return; } + dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); -// bool removable = false || (!dev.IsCompactFlash && ataId.GeneralConfiguration.HasFlag(Decoders.ATA.Identify.GeneralConfigurationBit.Removable)); DumpHardwareType currentTry = null; ExtentsULong extents = null; ResumeSupport.Process(true, false, blocks, dev.Manufacturer, dev.Model, dev.Serial, dev.PlatformID, ref resume, ref currentTry, ref extents); if(currentTry == null || extents == null) throw new Exception("Could not process resume file, not continuing..."); - DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead); + DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead); - mhddLog = new MHDDLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); - ibgLog = new IBGLog(outputPrefix + ".ibg", currentProfile); - dumpFile = new DataFile(outputPrefix + ".bin"); - dumpFile.Seek(resume.NextBlock, blockSize); + mhddLog = new MHDDLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); + ibgLog = new IBGLog(outputPrefix + ".ibg", currentProfile); + dumpFile = new DataFile(outputPrefix + ".bin"); + dumpFile.Seek(resume.NextBlock, blockSize); + if(resume.NextBlock > 0) + dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock); - start = DateTime.UtcNow; - for(ulong i = resume.NextBlock; i < blocks; i += blocksToRead) + start = DateTime.UtcNow; + for(ulong i = resume.NextBlock; i < blocks; i += blocksToRead) + { + if(aborted) + { + currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); + break; + } + + if((blocks - i) < blocksToRead) + blocksToRead = (byte)(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 + + DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed); + + error = dev.Read(out cmdBuf, out response, (uint)i, blockSize, blocksToRead, byteAddressed, timeout, out duration); + + if(!error) + { + mhddLog.Write(i, duration); + ibgLog.Write(i, currentSpeed * 1024); + dumpFile.Write(cmdBuf); + extents.Add(i, blocksToRead, true); + } + else + { + for(ulong b = i; b < i + blocksToRead; b++) + resume.BadBlocks.Add(b); + if(duration < 500) + mhddLog.Write(i, 65535); + else + mhddLog.Write(i, duration); + + ibgLog.Write(i, 0); + dumpFile.Write(new byte[blockSize * blocksToRead]); + dumpLog.WriteLine("Error reading {0} blocks from block {1}.", blocksToRead, i); + } + +#pragma warning disable IDE0004 // Cast is necessary, otherwise incorrect value is created + currentSpeed = ((double)blockSize * blocksToRead / (double)1048576) / (duration / (double)1000); +#pragma warning restore IDE0004 // Cast is necessary, otherwise incorrect value is created + GC.Collect(); + resume.NextBlock = i + blocksToRead; + } + end = DateTime.Now; + DicConsole.WriteLine(); + mhddLog.Close(); +#pragma warning disable IDE0004 // Cast is necessary, otherwise incorrect value is created + ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalDuration / 1000), devicePath); +#pragma warning restore IDE0004 // Cast is necessary, otherwise incorrect value is created + 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)); + + #region Error handling + if(resume.BadBlocks.Count > 0 && !aborted) + { + + int pass = 0; + bool forward = true; + bool runningPersistent = false; + + repeatRetryLba: + ulong[] tmpArray = resume.BadBlocks.ToArray(); + foreach(ulong badSector in tmpArray) { if(aborted) { currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } - if((blocks - i) < blocksToRead) - blocksToRead = (byte)(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 - - DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed); - - error = dev.Read(out cmdBuf, out response, (uint)i, blockSize, blocksToRead, byteAddressed, timeout, out duration); - - if(!error) - { - mhddLog.Write(i, duration); - ibgLog.Write(i, currentSpeed * 1024); - dumpFile.Write(cmdBuf); - extents.Add(i, blocksToRead, true); - } - else - { - for(ulong b = i; b < i + blocksToRead; b++) - resume.BadBlocks.Add(b); - if(duration < 500) - mhddLog.Write(i, 65535); - else - mhddLog.Write(i, duration); - - ibgLog.Write(i, 0); - dumpFile.Write(new byte[blockSize * blocksToRead]); - } - -#pragma warning disable IDE0004 // Cast is necessary, otherwise incorrect value is created - currentSpeed = ((double)blockSize * blocksToRead / (double)1048576) / (duration / (double)1000); -#pragma warning restore IDE0004 // Cast is necessary, otherwise incorrect value is created - GC.Collect(); - resume.NextBlock = i + blocksToRead; - } - end = DateTime.Now; - DicConsole.WriteLine(); - mhddLog.Close(); -#pragma warning disable IDE0004 // Cast is necessary, otherwise incorrect value is created - ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalDuration / 1000), devicePath); -#pragma warning restore IDE0004 // Cast is necessary, otherwise incorrect value is created - - #region Error handling - if(resume.BadBlocks.Count > 0 && !aborted) - { - - int pass = 0; - bool forward = true; - bool runningPersistent = false; - - repeatRetryLba: - ulong[] tmpArray = resume.BadBlocks.ToArray(); - foreach(ulong badSector in tmpArray) - { - if(aborted) - { - currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); - break; - } - - DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass + 1, forward ? "forward" : "reverse", runningPersistent ? "recovering partial data, " : ""); + DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass + 1, forward ? "forward" : "reverse", runningPersistent ? "recovering partial data, " : ""); error = dev.Read(out cmdBuf, out response, (uint)badSector, blockSize, 1, byteAddressed, timeout, out duration); - totalDuration += duration; + totalDuration += duration; - if(!error) - { - resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - dumpFile.WriteAt(cmdBuf, badSector, blockSize); - } - else if(runningPersistent) - dumpFile.WriteAt(cmdBuf, badSector, blockSize); - } - - if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0) + if(!error) { - pass++; - forward = !forward; - resume.BadBlocks.Sort(); - resume.BadBlocks.Reverse(); - goto repeatRetryLba; + resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + dumpFile.WriteAt(cmdBuf, badSector, blockSize); + dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } - - DicConsole.WriteLine(); + else if(runningPersistent) + dumpFile.WriteAt(cmdBuf, badSector, blockSize); } - #endregion Error handling - currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0) + { + pass++; + forward = !forward; + resume.BadBlocks.Sort(); + resume.BadBlocks.Reverse(); + goto repeatRetryLba; + } - dataChk = new Checksum(); + DicConsole.WriteLine(); + } + #endregion Error handling + + currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + + dataChk = new Checksum(); dumpFile.Seek(0, SeekOrigin.Begin); blocksToRead = 500; + dumpLog.WriteLine("Checksum starts."); for(ulong i = 0; i < blocks; i += blocksToRead) { if(aborted) + { + dumpLog.WriteLine("Aborted!"); break; + } if((blocks - i) < blocksToRead) blocksToRead = (byte)(blocks - i); @@ -426,6 +448,8 @@ namespace DiscImageChef.Core.Devices.Dumping DicConsole.WriteLine(); dumpFile.Close(); end = DateTime.UtcNow; + dumpLog.WriteLine("Checksum finished in {0} seconds.", (end - start).TotalSeconds); + dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalChkDuration / 1000)); PluginBase plugins = new PluginBase(); plugins.RegisterAllPlugins(); @@ -455,8 +479,10 @@ namespace DiscImageChef.Core.Devices.Dumping if(_imageFormat != null) { + dumpLog.WriteLine("Getting partitions."); List partitions = Partitions.GetAll(_imageFormat); Partitions.AddSchemesToStats(partitions); + dumpLog.WriteLine("Found {0} partitions.", partitions.Count); if(partitions.Count > 0) { @@ -473,6 +499,8 @@ namespace DiscImageChef.Core.Devices.Dumping Type = partitions[i].Type }; List lstFs = new List(); + dumpLog.WriteLine("Getting filesystems on partition {0}, starting at {1}, ending at {2}, with type {3}, under scheme {4}.", + i, partitions[i].Start, partitions[i].End, partitions[i].Type, partitions[i].Scheme); foreach(Filesystem _plugin in plugins.PluginsList.Values) { @@ -483,6 +511,7 @@ namespace DiscImageChef.Core.Devices.Dumping _plugin.GetInformation(_imageFormat, partitions[i], out string foo); lstFs.Add(_plugin.XmlFSType); Statistics.AddFilesystem(_plugin.XmlFSType.Type); + dumpLog.WriteLine("Filesystem {0} found.", _plugin.XmlFSType.Type); } } #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body @@ -499,6 +528,8 @@ namespace DiscImageChef.Core.Devices.Dumping } else { + dumpLog.WriteLine("Getting filesystem for whole device."); + xmlFileSysInfo = new PartitionType[1]; xmlFileSysInfo[0] = new PartitionType { @@ -523,6 +554,7 @@ namespace DiscImageChef.Core.Devices.Dumping _plugin.GetInformation(_imageFormat, wholePart, out string foo); lstFs.Add(_plugin.XmlFSType); Statistics.AddFilesystem(_plugin.XmlFSType.Type); + dumpLog.WriteLine("Filesystem {0} found.", _plugin.XmlFSType.Type); } } #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body diff --git a/DiscImageChef.Core/Devices/Dumping/XGD.cs b/DiscImageChef.Core/Devices/Dumping/XGD.cs index 91271215..c627fc7f 100644 --- a/DiscImageChef.Core/Devices/Dumping/XGD.cs +++ b/DiscImageChef.Core/Devices/Dumping/XGD.cs @@ -53,7 +53,7 @@ namespace DiscImageChef.Core.Devices.Dumping { internal static class XGD { - internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar, ref MediaType dskType, ref Metadata.Resume resume) + internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar, ref MediaType dskType, ref Metadata.Resume resume, ref DumpLog dumpLog) { MHDDLog mhddLog; IBGLog ibgLog; @@ -77,16 +77,20 @@ namespace DiscImageChef.Core.Devices.Dumping e.Cancel = aborted = true; }; + dumpLog.WriteLine("Reading Xbox Security Sector."); sense = dev.KreonExtractSS(out byte[] ssBuf, out byte[] senseBuf, dev.Timeout, out double duration); 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."); Decoders.Xbox.SS.SecuritySector? xboxSS = Decoders.Xbox.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; } @@ -116,22 +120,28 @@ namespace DiscImageChef.Core.Devices.Dumping // 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 duration); 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 duration); 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 duration); if(sense) { + dumpLog.WriteLine("Cannot get PFI."); DicConsole.ErrorWriteLine("Cannot get PFI."); return; } @@ -147,9 +157,11 @@ namespace DiscImageChef.Core.Devices.Dumping DicConsole.DebugWriteLine("Dump-media command", "Video partition total size: {0} sectors", totalSize); l0Video = Decoders.DVD.PFI.Decode(readBuffer).Value.Layer0EndPSN - Decoders.DVD.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 duration); if(sense) { + dumpLog.WriteLine("Cannot get DMI."); DicConsole.ErrorWriteLine("Cannot get DMI."); return; } @@ -165,15 +177,19 @@ namespace DiscImageChef.Core.Devices.Dumping // 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 duration); 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 duration); if(sense) { + dumpLog.WriteLine("Cannot get disc capacity."); DicConsole.ErrorWriteLine("Cannot get disc capacity."); return; } @@ -182,22 +198,28 @@ namespace DiscImageChef.Core.Devices.Dumping // 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 duration); 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 duration); 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 duration); if(sense) { + dumpLog.WriteLine("Cannot get PFI."); DicConsole.ErrorWriteLine("Cannot get PFI."); return; } @@ -215,9 +237,11 @@ namespace DiscImageChef.Core.Devices.Dumping }; DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].Xbox.PFI.Image, tmpBuf, "Unlocked PFI", true); + dumpLog.WriteLine("Reading Disc Manufacturing Information."); sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.DiscManufacturingInformation, 0, 0, out duration); if(sense) { + dumpLog.WriteLine("Cannot get DMI."); DicConsole.ErrorWriteLine("Cannot get DMI."); return; } @@ -243,13 +267,22 @@ namespace DiscImageChef.Core.Devices.Dumping 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, blockSize, 0, 1, false, dev.Timeout, out duration); 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) @@ -267,11 +300,12 @@ namespace DiscImageChef.Core.Devices.Dumping 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; } - + dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead); DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead); mhddLog = new MHDDLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); @@ -291,13 +325,17 @@ namespace DiscImageChef.Core.Devices.Dumping throw new Exception("Could not process resume file, not continuing..."); ulong currentSector = resume.NextBlock; dumpFile.Seek(resume.NextBlock, blockSize); + if(resume.NextBlock > 0) + dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock); + dumpLog.WriteLine("Reading game partition."); for(int e = 0; e <= 16; e++) { if(aborted) { resume.NextBlock = currentSector; currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } @@ -334,6 +372,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(aborted) { currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } @@ -378,6 +417,11 @@ namespace DiscImageChef.Core.Devices.Dumping mhddLog.Write(i, cmdDuration); ibgLog.Write(i, 0); + + dumpLog.WriteLine("Error reading {0} blocks from block {1}.", blocksToRead, i); + string[] senseLines = Decoders.SCSI.Sense.PrettifySense(senseBuf).Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + foreach(string senseLine in senseLines) + dumpLog.WriteLine(senseLine); } #pragma warning disable IDE0004 // Remove Unnecessary Cast @@ -394,6 +438,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(aborted) { currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } @@ -414,11 +459,13 @@ namespace DiscImageChef.Core.Devices.Dumping } // 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 = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } @@ -438,9 +485,11 @@ namespace DiscImageChef.Core.Devices.Dumping blocksToRead = saveBlocksToRead; + dumpLog.WriteLine("Locking drive."); sense = dev.KreonLock(out senseBuf, dev.Timeout, out duration); if(sense) { + dumpLog.WriteLine("Cannot lock drive, not continuing."); DicConsole.ErrorWriteLine("Cannot lock drive, not continuing."); return; } @@ -452,11 +501,13 @@ namespace DiscImageChef.Core.Devices.Dumping } // 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 = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } @@ -501,6 +552,10 @@ namespace DiscImageChef.Core.Devices.Dumping mhddLog.Write(l1, cmdDuration); ibgLog.Write(l1, 0); + dumpLog.WriteLine("Error reading {0} blocks from block {1}.", blocksToRead, l1); + string[] senseLines = Decoders.SCSI.Sense.PrettifySense(senseBuf).Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + foreach(string senseLine in senseLines) + dumpLog.WriteLine(senseLine); } #pragma warning disable IDE0004 // Remove Unnecessary Cast @@ -510,9 +565,11 @@ namespace DiscImageChef.Core.Devices.Dumping resume.NextBlock = currentSector; } + dumpLog.WriteLine("Unlocking drive (Wxripper)."); sense = dev.KreonUnlockWxripper(out senseBuf, dev.Timeout, out duration); if(sense) { + dumpLog.WriteLine("Cannot unlock drive, not continuing."); DicConsole.ErrorWriteLine("Cannot unlock drive, not continuing."); return; } @@ -529,6 +586,8 @@ namespace DiscImageChef.Core.Devices.Dumping #pragma warning disable IDE0004 // Remove Unnecessary Cast ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalDuration / 1000), devicePath); #pragma warning restore IDE0004 // Remove Unnecessary Cast + 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)); #region Error handling if(resume.BadBlocks.Count > 0 && !aborted) @@ -556,6 +615,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(aborted) { currentTry.Extents = Metadata.ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); break; } @@ -571,6 +631,7 @@ namespace DiscImageChef.Core.Devices.Dumping resume.BadBlocks.Remove(badSector); extents.Add(badSector); dumpFile.WriteAt(readBuffer, badSector, blockSize); + dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } else if(runningPersistent) dumpFile.WriteAt(readBuffer, badSector, blockSize); @@ -661,6 +722,7 @@ namespace DiscImageChef.Core.Devices.Dumping md10 = Decoders.SCSI.Modes.EncodeMode10(md, dev.SCSIType); } + dumpLog.WriteLine("Sending MODE SELECT to drive."); sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out duration); if(sense) { @@ -687,6 +749,7 @@ namespace DiscImageChef.Core.Devices.Dumping md6 = Decoders.SCSI.Modes.EncodeMode6(md, dev.SCSIType); md10 = Decoders.SCSI.Modes.EncodeMode10(md, dev.SCSIType); + dumpLog.WriteLine("Sending MODE SELECT to drive."); sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out duration); if(sense) { @@ -706,10 +769,14 @@ namespace DiscImageChef.Core.Devices.Dumping blocks = totalSize; + dumpLog.WriteLine("Checksum starts."); for(ulong i = 0; i < blocks; i += blocksToRead) { if(aborted) + { + dumpLog.WriteLine("Aborted!"); break; + } if((blocks - i) < blocksToRead) blocksToRead = (uint)(blocks - i); @@ -732,6 +799,8 @@ namespace DiscImageChef.Core.Devices.Dumping DicConsole.WriteLine(); dumpFile.Close(); end = DateTime.UtcNow; + dumpLog.WriteLine("Checksum finished in {0} seconds.", (end - start).TotalSeconds); + dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalChkDuration / 1000)); PluginBase plugins = new PluginBase(); plugins.RegisterAllPlugins(); @@ -760,8 +829,10 @@ namespace DiscImageChef.Core.Devices.Dumping if(_imageFormat != null) { + dumpLog.WriteLine("Getting partitions."); List partitions = Partitions.GetAll(_imageFormat); Partitions.AddSchemesToStats(partitions); + dumpLog.WriteLine("Found {0} partitions.", partitions.Count); if(partitions.Count > 0) { @@ -778,6 +849,8 @@ namespace DiscImageChef.Core.Devices.Dumping Type = partitions[i].Type }; List lstFs = new List(); + dumpLog.WriteLine("Getting filesystems on partition {0}, starting at {1}, ending at {2}, with type {3}, under scheme {4}.", + i, partitions[i].Start, partitions[i].End, partitions[i].Type, partitions[i].Scheme); foreach(Filesystem _plugin in plugins.PluginsList.Values) { @@ -788,6 +861,7 @@ namespace DiscImageChef.Core.Devices.Dumping _plugin.GetInformation(_imageFormat, partitions[i], out string foo); lstFs.Add(_plugin.XmlFSType); Statistics.AddFilesystem(_plugin.XmlFSType.Type); + dumpLog.WriteLine("Filesystem {0} found.", _plugin.XmlFSType.Type); if(_plugin.XmlFSType.Type == "Opera") dskType = MediaType.ThreeDO; @@ -813,6 +887,7 @@ namespace DiscImageChef.Core.Devices.Dumping } else { + dumpLog.WriteLine("Getting filesystem for whole device."); xmlFileSysInfo = new PartitionType[1]; xmlFileSysInfo[0] = new PartitionType { @@ -837,6 +912,7 @@ namespace DiscImageChef.Core.Devices.Dumping _plugin.GetInformation(_imageFormat, wholePart, out string foo); lstFs.Add(_plugin.XmlFSType); Statistics.AddFilesystem(_plugin.XmlFSType.Type); + dumpLog.WriteLine("Filesystem {0} found.", _plugin.XmlFSType.Type); if(_plugin.XmlFSType.Type == "Opera") dskType = MediaType.ThreeDO; diff --git a/DiscImageChef.Core/DiscImageChef.Core.csproj b/DiscImageChef.Core/DiscImageChef.Core.csproj index df0d1d27..b6b9cfb2 100644 --- a/DiscImageChef.Core/DiscImageChef.Core.csproj +++ b/DiscImageChef.Core/DiscImageChef.Core.csproj @@ -87,6 +87,7 @@ + @@ -161,7 +162,7 @@ - + diff --git a/DiscImageChef.Core/Logging/DumpLog.cs b/DiscImageChef.Core/Logging/DumpLog.cs new file mode 100644 index 00000000..f3147aa5 --- /dev/null +++ b/DiscImageChef.Core/Logging/DumpLog.cs @@ -0,0 +1,138 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : DumpLog.cs +// Version : 1.0 +// Author(s) : Natalia Portillo +// +// Component : Component +// +// Revision : $Revision$ +// Last change by : $Author$ +// Date : $Date$ +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright (C) 2011-2015 Claunia.com +// ****************************************************************************/ +// //$Id$ +using System; +using System.IO; +using System.Reflection; +using DiscImageChef.Devices; +namespace DiscImageChef.Core.Logging +{ + public class DumpLog + { + readonly StreamWriter logSw; + + public DumpLog(string outputFile, Device dev) + { + if(!string.IsNullOrEmpty(outputFile)) + { + logSw = new StreamWriter(outputFile, true); + + logSw.WriteLine("Start logging at {0}", DateTime.Now); + + Interop.PlatformID platId = Interop.DetectOS.GetRealPlatformID(); + string platVer = Interop.DetectOS.GetVersion(); + Type monoRunType = Type.GetType("Mono.Runtime"); + + logSw.WriteLine("################# System information #################"); + logSw.WriteLine("{0} {1} ({2}-bit)", Interop.DetectOS.GetPlatformName(platId, platVer), platVer, Environment.Is64BitOperatingSystem ? 64 : 32); + if(monoRunType != null) + { + string monoVer = "unknown version"; + MethodInfo monoDisplayName = monoRunType.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static); + if(monoDisplayName != null) + monoVer = (string)monoDisplayName.Invoke(null, null); + logSw.WriteLine("Mono {0}", monoVer); + } + else + logSw.WriteLine(".NET Framework {0}", Environment.Version); + logSw.WriteLine(); + + logSw.WriteLine("################# Program information ################"); + logSw.WriteLine("DiscImageChef {0} running in {1}-bit", Version.GetVersion(), Environment.Is64BitProcess ? 64 : 32); +#if DEBUG + logSw.WriteLine("DEBUG version"); +#endif + logSw.WriteLine("Command line: {0}", Environment.CommandLine); + logSw.WriteLine(); + + logSw.WriteLine("################# Device information #################"); + logSw.WriteLine("Manufacturer: {0}", dev.Manufacturer); + logSw.WriteLine("Model: {0}", dev.Model); + logSw.WriteLine("Firmware revision: {0}", dev.Revision); + logSw.WriteLine("Serial number: {0}", dev.Serial); + logSw.WriteLine("Removable device: {0}", dev.IsRemovable); + logSw.WriteLine("Device type: {0}", dev.Type); + logSw.WriteLine("CompactFlash device: {0}", dev.IsCompactFlash); + logSw.WriteLine("PCMCIA device: {0}", dev.IsPCMCIA); + logSw.WriteLine("USB device: {0}", dev.IsUSB); + if(dev.IsUSB) + { + logSw.WriteLine("USB manufacturer: {0}", dev.USBManufacturerString); + logSw.WriteLine("USB product: {0}", dev.USBProductString); + logSw.WriteLine("USB serial: {0}", dev.USBSerialString); + logSw.WriteLine("USB vendor ID: {0:X4}h", dev.USBVendorID); + logSw.WriteLine("USB product ID: {0:X4}h", dev.USBProductID); + } + logSw.WriteLine("FireWire device: {0}", dev.IsFireWire); + if(dev.IsFireWire) + { + logSw.WriteLine("FireWire vendor: {0}", dev.FireWireVendorName); + logSw.WriteLine("FireWire model: {0}", dev.FireWireModelName); + logSw.WriteLine("FireWire GUID: 0x{0:X16}", dev.FireWireGUID); + logSw.WriteLine("FireWire vendor ID: 0x{0:X8}", dev.FireWireVendor); + logSw.WriteLine("FireWire product ID: 0x{0:X8}", dev.FireWireModel); + } + logSw.WriteLine(); + logSw.WriteLine("######################################################"); + + logSw.WriteLine(); + logSw.WriteLine("################ Dumping progress log ################"); + logSw.Flush(); + } + } + + public void WriteLine(string format, params object[] args) + { + if(logSw != null) + { + string text = string.Format(format, args); + logSw.WriteLine("{0:s} {1}", DateTime.Now, text); + logSw.Flush(); + } + } + + public void Close() + { + if(logSw != null) + { + logSw.WriteLine("######################################################"); + logSw.WriteLine("End logging at {0}", DateTime.Now); + logSw.Close(); + } + } + } +} diff --git a/DiscImageChef/Commands/DumpMedia.cs b/DiscImageChef/Commands/DumpMedia.cs index 6452cce7..d065474c 100644 --- a/DiscImageChef/Commands/DumpMedia.cs +++ b/DiscImageChef/Commands/DumpMedia.cs @@ -105,23 +105,27 @@ namespace DiscImageChef.Commands return; } + DumpLog dumpLog = new DumpLog(options.OutputPrefix + ".log", dev); + switch(dev.Type) { case DeviceType.ATA: - ATA.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses, options.Force, options.Raw, options.Persistent, options.StopOnError, ref resume); + ATA.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses, options.Force, options.Raw, options.Persistent, options.StopOnError, ref resume, ref dumpLog); break; case DeviceType.MMC: case DeviceType.SecureDigital: - SecureDigital.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses, options.Force, options.Raw, options.Persistent, options.StopOnError, ref resume); + SecureDigital.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses, options.Force, options.Raw, options.Persistent, options.StopOnError, ref resume, ref dumpLog); break; case DeviceType.NVMe: - NVMe.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses, options.Force, options.Raw, options.Persistent, options.StopOnError, ref resume); + NVMe.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses, options.Force, options.Raw, options.Persistent, options.StopOnError, ref resume, ref dumpLog); break; case DeviceType.ATAPI: case DeviceType.SCSI: - SCSI.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses, options.Force, options.Raw, options.Persistent, options.StopOnError, options.SeparateSubchannel, ref resume); + SCSI.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses, options.Force, options.Raw, options.Persistent, options.StopOnError, options.SeparateSubchannel, ref resume, ref dumpLog); break; default: + dumpLog.WriteLine("Unknown device type."); + dumpLog.Close(); throw new NotSupportedException("Unknown device type."); } @@ -139,6 +143,8 @@ namespace DiscImageChef.Commands fs.Close(); } + dumpLog.Close(); + Core.Statistics.AddCommand("dump-media"); } }