From 586536fdbf62a05c352393ed8742c7858301a773 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Mon, 18 Nov 2019 20:59:16 +0000 Subject: [PATCH] Use tuples to convert MediaType to type and subtype. --- .../.idea/contentModel.xml | 1 - DiscImageChef.CommonTypes | 2 +- DiscImageChef.Core/Devices/Dumping/ATA.cs | 273 +++- .../Devices/Dumping/CompactDisc.cs | 1433 ++++++++++------- .../Devices/Dumping/PlayStationPortable.cs | 507 +++--- DiscImageChef.Core/Devices/Dumping/SBC.cs | 917 ++++++----- DiscImageChef.Core/Devices/Dumping/SSC.cs | 13 +- .../Devices/Dumping/SecureDigital.cs | 277 ++-- DiscImageChef.Core/Devices/Dumping/XGD.cs | 8 +- DiscImageChef.Core/Sidecar/BlockMedia.cs | 375 +++-- DiscImageChef.Core/Sidecar/OpticalDisc.cs | 328 ++-- 11 files changed, 2532 insertions(+), 1602 deletions(-) diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml index 06ae227ef..11f1a2a0f 100644 --- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml +++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml @@ -1,7 +1,6 @@ - diff --git a/DiscImageChef.CommonTypes b/DiscImageChef.CommonTypes index 681fd8e8b..374c14556 160000 --- a/DiscImageChef.CommonTypes +++ b/DiscImageChef.CommonTypes @@ -1 +1 @@ -Subproject commit 681fd8e8b9578276346630e0817cdd244f2ad931 +Subproject commit 374c1455625c672373741bc7a830e69eb13d8c3a diff --git a/DiscImageChef.Core/Devices/Dumping/ATA.cs b/DiscImageChef.Core/Devices/Dumping/ATA.cs index 225a4b1ce..238eedbf8 100644 --- a/DiscImageChef.Core/Devices/Dumping/ATA.cs +++ b/DiscImageChef.Core/Devices/Dumping/ATA.cs @@ -47,22 +47,20 @@ using Tuple = DiscImageChef.Decoders.PCMCIA.Tuple; namespace DiscImageChef.Core.Devices.Dumping { - /// - /// Implements dumping ATA devices - /// + /// Implements dumping ATA devices public partial class Dump { - /// - /// Dumps an ATA device - /// + /// Dumps an ATA device public void Ata() { if(dumpRaw) { - if(force) ErrorMessage?.Invoke("Raw dumping not yet supported in ATA devices, continuing..."); + if(force) + ErrorMessage?.Invoke("Raw dumping not yet supported in ATA devices, continuing..."); else { StoppingErrorMessage?.Invoke("Raw dumping not yet supported in ATA devices, aborting..."); + return; } } @@ -74,9 +72,12 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke("Requesting ATA IDENTIFY DEVICE."); dumpLog.WriteLine("Requesting ATA IDENTIFY DEVICE."); bool sense = dev.AtaIdentify(out byte[] cmdBuf, out _); - if(!sense && Identify.Decode(cmdBuf).HasValue) + + if(!sense && + Identify.Decode(cmdBuf).HasValue) { Identify.IdentifyDevice? ataIdNullable = Identify.Decode(cmdBuf); + if(ataIdNullable != null) { Identify.IdentifyDevice ataId = ataIdNullable.Value; @@ -93,23 +94,28 @@ namespace DiscImageChef.Core.Devices.Dumping // Initializate reader UpdateStatus?.Invoke("Initializing reader."); dumpLog.WriteLine("Initializing reader."); - Reader ataReader = new Reader(dev, TIMEOUT, ataIdentify); + var ataReader = new Reader(dev, TIMEOUT, ataIdentify); + // Fill reader blocks ulong blocks = ataReader.GetDeviceBlocks(); + // Check block sizes if(ataReader.GetBlockSize()) { dumpLog.WriteLine("ERROR: Cannot get block size: {0}.", ataReader.ErrorMessage); ErrorMessage(ataReader.ErrorMessage); + return; } uint blockSize = ataReader.LogicalBlockSize; uint physicalsectorsize = ataReader.PhysicalBlockSize; + if(ataReader.FindReadCommand()) { dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", ataReader.ErrorMessage); ErrorMessage(ataReader.ErrorMessage); + return; } @@ -118,6 +124,7 @@ namespace DiscImageChef.Core.Devices.Dumping { dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", ataReader.ErrorMessage); ErrorMessage(ataReader.ErrorMessage); + return; } @@ -127,27 +134,36 @@ namespace DiscImageChef.Core.Devices.Dumping byte sectors = ataReader.Sectors; UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); - UpdateStatus - ?.Invoke($"Device reports {cylinders} cylinders {heads} heads {sectors} sectors per track."); + + UpdateStatus?. + Invoke($"Device reports {cylinders} cylinders {heads} heads {sectors} sectors per track."); + UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); UpdateStatus?.Invoke($"Device reports {physicalsectorsize} bytes per physical block."); 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 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 = !dev.IsCompactFlash && ataId.GeneralConfiguration.HasFlag(Identify.GeneralConfigurationBit.Removable); + DumpHardwareType currentTry = null; ExtentsULong extents = null; + ResumeSupport.Process(ataReader.IsLba, removable, blocks, dev.Manufacturer, dev.Model, dev.Serial, dev.PlatformId, ref resume, ref currentTry, ref extents); - if(currentTry == null || extents == null) + + if(currentTry == null || + extents == null) { StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + return; } @@ -157,7 +173,8 @@ namespace DiscImageChef.Core.Devices.Dumping bool ret = true; - if(dev.IsUsb && dev.UsbDescriptors != null && + if(dev.IsUsb && + dev.UsbDescriptors != null && !outputPlugin.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors)) { ret = false; @@ -165,7 +182,8 @@ namespace DiscImageChef.Core.Devices.Dumping ErrorMessage("Output format does not support USB descriptors."); } - if(dev.IsPcmcia && dev.Cis != null && + if(dev.IsPcmcia && + dev.Cis != null && !outputPlugin.SupportedMediaTags.Contains(MediaTagType.PCMCIA_CIS)) { ret = false; @@ -183,10 +201,13 @@ namespace DiscImageChef.Core.Devices.Dumping if(!ret) { dumpLog.WriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not "); - if(force) ErrorMessage("Several media tags not supported, continuing..."); + + if(force) + ErrorMessage("Several media tags not supported, continuing..."); else { StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); + return; } } @@ -200,9 +221,11 @@ namespace DiscImageChef.Core.Devices.Dumping { 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; } @@ -212,7 +235,9 @@ namespace DiscImageChef.Core.Devices.Dumping if(ataReader.IsLba) { UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); - if(skip < blocksToRead) skip = blocksToRead; + + if(skip < blocksToRead) + skip = blocksToRead; mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); ibgLog = new IbgLog(outputPrefix + ".ibg", ATA_PROFILE); @@ -229,6 +254,7 @@ namespace DiscImageChef.Core.Devices.Dumping DateTime timeSpeedStart = DateTime.UtcNow; ulong sectorSpeedStart = 0; InitProgress?.Invoke(); + for(ulong i = resume.NextBlock; i < blocks; i += blocksToRead) { if(aborted) @@ -236,14 +262,21 @@ namespace DiscImageChef.Core.Devices.Dumping currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } - if(blocks - i < blocksToRead) blocksToRead = (byte)(blocks - i); + 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; + 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.)", @@ -262,9 +295,11 @@ namespace DiscImageChef.Core.Devices.Dumping } else { - if(i + skip > blocks) skip = (uint)(blocks - i); + if(i + skip > blocks) + skip = (uint)(blocks - i); - for(ulong b = i; b < i + skip; b++) resume.BadBlocks.Add(b); + for(ulong b = i; b < i + skip; b++) + resume.BadBlocks.Add(b); mhddLog.Write(i, duration < 500 ? 65535 : duration); @@ -281,7 +316,9 @@ namespace DiscImageChef.Core.Devices.Dumping resume.NextBlock = i + blocksToRead; double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - if(elapsed < 1) continue; + + if(elapsed < 1) + continue; currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); sectorSpeedStart = 0; @@ -291,23 +328,32 @@ namespace DiscImageChef.Core.Devices.Dumping end = DateTime.Now; 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."); + + 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 && !notrim && newTrim) + if(resume.BadBlocks.Count > 0 && + !aborted && + !notrim && + newTrim) { start = DateTime.UtcNow; UpdateStatus?.Invoke("Trimming bad sectors"); @@ -315,6 +361,7 @@ namespace DiscImageChef.Core.Devices.Dumping ulong[] tmpArray = resume.BadBlocks.ToArray(); InitProgress?.Invoke(); + foreach(ulong badSector in tmpArray) { if(aborted) @@ -322,6 +369,7 @@ namespace DiscImageChef.Core.Devices.Dumping currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } @@ -331,7 +379,8 @@ namespace DiscImageChef.Core.Devices.Dumping totalDuration += duration; - if(error) continue; + if(error) + continue; resume.BadBlocks.Remove(badSector); extents.Add(badSector); @@ -346,7 +395,9 @@ namespace DiscImageChef.Core.Devices.Dumping #endregion Trimming #region Error handling - if(resume.BadBlocks.Count > 0 && !aborted && retryPasses > 0) + if(resume.BadBlocks.Count > 0 && + !aborted && + retryPasses > 0) { int pass = 1; bool forward = true; @@ -354,6 +405,7 @@ namespace DiscImageChef.Core.Devices.Dumping InitProgress?.Invoke(); repeatRetryLba: ulong[] tmpArray = resume.BadBlocks.ToArray(); + foreach(ulong badSector in tmpArray) { if(aborted) @@ -361,6 +413,7 @@ namespace DiscImageChef.Core.Devices.Dumping currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } @@ -380,15 +433,19 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } - else if(persistent) outputPlugin.WriteSector(cmdBuf, badSector); + else if(persistent) + outputPlugin.WriteSector(cmdBuf, badSector); } - if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0) + if(pass < retryPasses && + !aborted && + resume.BadBlocks.Count > 0) { pass++; forward = !forward; resume.BadBlocks.Sort(); resume.BadBlocks.Reverse(); + goto repeatRetryLba; } @@ -409,6 +466,7 @@ namespace DiscImageChef.Core.Devices.Dumping DateTime timeSpeedStart = DateTime.UtcNow; ulong sectorSpeedStart = 0; InitProgress?.Invoke(); + for(ushort cy = 0; cy < cylinders; cy++) { for(byte hd = 0; hd < heads; hd++) @@ -420,16 +478,22 @@ namespace DiscImageChef.Core.Devices.Dumping currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } #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; + 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 - PulseProgress - ?.Invoke($"Reading cylinder {cy} head {hd} sector {sc} ({currentSpeed:F3} MiB/sec.)"); + PulseProgress?. + Invoke($"Reading cylinder {cy} head {hd} sector {sc} ({currentSpeed:F3} MiB/sec.)"); bool error = ataReader.ReadChs(out cmdBuf, cy, hd, sc, out duration); @@ -440,10 +504,13 @@ namespace DiscImageChef.Core.Devices.Dumping mhddLog.Write(currentBlock, duration); ibgLog.Write(currentBlock, currentSpeed * 1024); DateTime writeStart = DateTime.Now; + outputPlugin.WriteSector(cmdBuf, (ulong)((cy * heads + hd) * sectors + (sc - 1))); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; extents.Add(currentBlock); + dumpLog.WriteLine("Error reading cylinder {0} head {1} sector {2}.", cy, hd, sc); } @@ -454,8 +521,10 @@ namespace DiscImageChef.Core.Devices.Dumping ibgLog.Write(currentBlock, 0); DateTime writeStart = DateTime.Now; + outputPlugin.WriteSector(new byte[blockSize], (ulong)((cy * heads + hd) * sectors + (sc - 1))); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; } @@ -463,7 +532,9 @@ namespace DiscImageChef.Core.Devices.Dumping currentBlock++; double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - if(elapsed < 1) continue; + + if(elapsed < 1) + continue; currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); sectorSpeedStart = 0; @@ -475,25 +546,37 @@ namespace DiscImageChef.Core.Devices.Dumping end = DateTime.Now; 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 / 1000):F3} KiB/sec."); + + 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 / 1000):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 / 1000)); } - foreach(ulong bad in resume.BadBlocks) dumpLog.WriteLine("Sector {0} could not be read.", bad); + foreach(ulong bad in resume.BadBlocks) + dumpLog.WriteLine("Sector {0} could not be read.", bad); + outputPlugin.SetDumpHardware(resume.Tries); - if(preSidecar != null) outputPlugin.SetCicmMetadata(preSidecar); + + if(preSidecar != null) + outputPlugin.SetCicmMetadata(preSidecar); + dumpLog.WriteLine("Closing output file."); UpdateStatus?.Invoke("Closing output file."); DateTime closeStart = DateTime.Now; @@ -506,20 +589,24 @@ namespace DiscImageChef.Core.Devices.Dumping { dumpLog.WriteLine("Aborted!"); UpdateStatus?.Invoke("Aborted!"); + return; } double totalChkDuration = 0; + if(!nometadata) { dumpLog.WriteLine("Creating sidecar."); UpdateStatus?.Invoke("Creating sidecar."); - FiltersList filters = new FiltersList(); + 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; } @@ -533,13 +620,15 @@ namespace DiscImageChef.Core.Devices.Dumping sidecarClass.EndProgressEvent2 += EndProgress2; sidecarClass.UpdateStatusEvent += UpdateStatus; CICMMetadataType sidecar = sidecarClass.Create(); + if(preSidecar != null) { preSidecar.BlockMedia = sidecar.BlockMedia; sidecar = preSidecar; } - if(dev.IsUsb && dev.UsbDescriptors != null) + if(dev.IsUsb && + dev.UsbDescriptors != null) { dumpLog.WriteLine("Reading USB descriptors."); UpdateStatus?.Invoke("Reading USB descriptors."); @@ -548,18 +637,16 @@ namespace DiscImageChef.Core.Devices.Dumping if(ret) sidecar.BlockMedia[0].USB = new USBType { - ProductID = dev.UsbProductId, - VendorID = dev.UsbVendorId, - Descriptors = new DumpType + ProductID = dev.UsbProductId, VendorID = dev.UsbVendorId, Descriptors = new DumpType { - Image = outputPath, - Size = (ulong)dev.UsbDescriptors.Length, + Image = outputPath, Size = (ulong)dev.UsbDescriptors.Length, Checksums = Checksum.GetChecksums(dev.UsbDescriptors).ToArray() } }; } - if(dev.IsPcmcia && dev.Cis != null) + if(dev.IsPcmcia && + dev.Cis != null) { dumpLog.WriteLine("Reading PCMCIA CIS."); UpdateStatus?.Invoke("Reading PCMCIA CIS."); @@ -570,8 +657,7 @@ namespace DiscImageChef.Core.Devices.Dumping { CIS = new DumpType { - Image = outputPath, - Size = (ulong)dev.Cis.Length, + Image = outputPath, Size = (ulong)dev.Cis.Length, Checksums = Checksum.GetChecksums(dev.Cis).ToArray() } }; @@ -579,6 +665,7 @@ namespace DiscImageChef.Core.Devices.Dumping dumpLog.WriteLine("Decoding PCMCIA CIS."); UpdateStatus?.Invoke("Decoding PCMCIA CIS."); Tuple[] tuples = CIS.GetTuples(dev.Cis); + if(tuples != null) foreach(Tuple tuple in tuples) switch(tuple.Code) @@ -591,6 +678,7 @@ namespace DiscImageChef.Core.Devices.Dumping { sidecar.BlockMedia[0].PCMCIA.ManufacturerCode = manfid.ManufacturerID; + sidecar.BlockMedia[0].PCMCIA.CardCode = manfid.CardID; sidecar.BlockMedia[0].PCMCIA.ManufacturerCodeSpecified = true; sidecar.BlockMedia[0].PCMCIA.CardCodeSpecified = true; @@ -604,8 +692,10 @@ namespace DiscImageChef.Core.Devices.Dumping { sidecar.BlockMedia[0].PCMCIA.Manufacturer = vers.Manufacturer; sidecar.BlockMedia[0].PCMCIA.ProductName = vers.Product; + sidecar.BlockMedia[0].PCMCIA.Compliance = $"{vers.MajorVersion}.{vers.MinorVersion}"; + sidecar.BlockMedia[0].PCMCIA.AdditionalInformation = vers.AdditionalInformation; } @@ -621,8 +711,7 @@ namespace DiscImageChef.Core.Devices.Dumping { Identify = new DumpType { - Image = outputPath, - Size = (ulong)cmdBuf.Length, + Image = outputPath, Size = (ulong)cmdBuf.Length, Checksums = Checksum.GetChecksums(cmdBuf).ToArray() } }; @@ -631,13 +720,17 @@ namespace DiscImageChef.Core.Devices.Dumping totalChkDuration = (chkEnd - chkStart).TotalMilliseconds; UpdateStatus?.Invoke($"Sidecar created in {(chkEnd - chkStart).TotalSeconds} seconds."); - UpdateStatus - ?.Invoke($"Average checksum speed {(double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); + + UpdateStatus?. + Invoke($"Average checksum speed {(double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); + dumpLog.WriteLine("Sidecar created in {0} seconds.", (chkEnd - chkStart).TotalSeconds); + dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); 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 @@ -645,26 +738,29 @@ namespace DiscImageChef.Core.Devices.Dumping select (partition.StartSector, fileSystem.Type)); if(filesystems.Count > 0) - foreach(var filesystem in filesystems.Select(o => new {o.start, o.type}).Distinct()) + foreach(var filesystem in filesystems.Select(o => new { - UpdateStatus - ?.Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}"); + 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); } - string xmlDskTyp, xmlDskSubTyp; + (string type, string subType) xmlType; + if(dev.IsCompactFlash) - CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.CompactFlash, out xmlDskTyp, - out xmlDskSubTyp); + xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.CompactFlash); else if(dev.IsPcmcia) - CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.PCCardTypeI, out xmlDskTyp, - out xmlDskSubTyp); + xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.PCCardTypeI); else - CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.GENERIC_HDD, out xmlDskTyp, - out xmlDskSubTyp); - sidecar.BlockMedia[0].DiskType = xmlDskTyp; - sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp; + xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.GENERIC_HDD); + + sidecar.BlockMedia[0].DiskType = xmlType.type; + sidecar.BlockMedia[0].DiskSubType = xmlType.subType; sidecar.BlockMedia[0].Interface = "ATA"; sidecar.BlockMedia[0].LogicalBlocks = blocks; sidecar.BlockMedia[0].PhysicalBlockSize = physicalsectorsize; @@ -673,7 +769,10 @@ namespace DiscImageChef.Core.Devices.Dumping sidecar.BlockMedia[0].Model = dev.Model; sidecar.BlockMedia[0].Serial = dev.Serial; sidecar.BlockMedia[0].Size = blocks * blockSize; - if(cylinders > 0 && heads > 0 && sectors > 0) + + if(cylinders > 0 && + heads > 0 && + sectors > 0) { sidecar.BlockMedia[0].Cylinders = cylinders; sidecar.BlockMedia[0].CylindersSpecified = true; @@ -685,30 +784,40 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke("Writing metadata sidecar"); - FileStream xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create); + var xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create); - XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType)); + 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."); + + 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."); + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); UpdateStatus?.Invoke($"{resume.BadBlocks.Count} sectors could not be read."); - if(resume.BadBlocks.Count > 0) resume.BadBlocks.Sort(); + + if(resume.BadBlocks.Count > 0) + resume.BadBlocks.Sort(); + UpdateStatus?.Invoke(""); } - if(dev.IsCompactFlash) Statistics.AddMedia(MediaType.CompactFlash, true); - else if(dev.IsPcmcia) Statistics.AddMedia(MediaType.PCCardTypeI, true); - else Statistics.AddMedia(MediaType.GENERIC_HDD, true); + if(dev.IsCompactFlash) + Statistics.AddMedia(MediaType.CompactFlash, true); + else if(dev.IsPcmcia) + Statistics.AddMedia(MediaType.PCCardTypeI, true); + else + Statistics.AddMedia(MediaType.GENERIC_HDD, true); } - else StoppingErrorMessage?.Invoke("Unable to communicate with ATA device."); + else + StoppingErrorMessage?.Invoke("Unable to communicate with ATA device."); } } } \ No newline at end of file diff --git a/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs b/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs index 6b5c6f9fe..b7a56aa5d 100644 --- a/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs +++ b/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs @@ -57,54 +57,54 @@ using TrackType = DiscImageChef.CommonTypes.Enums.TrackType; namespace DiscImageChef.Core.Devices.Dumping { - /// - /// Implement dumping Compact Discs - /// + /// Implement dumping Compact Discs + // TODO: Barcode and pregaps partial class Dump { - /// - /// Dumps a compact disc - /// + /// Dumps a compact disc /// Disc type as detected in MMC layer internal void CompactDisc(ref MediaType dskType) { - uint subSize; - DateTime start; - DateTime end; - bool readcd; - bool read6 = false, read10 = false, read12 = false, read16 = false; - var sense = false; - const uint SECTOR_SIZE = 2352; - FullTOC.CDFullTOC? toc = null; - double totalDuration = 0; - double currentSpeed = 0; - var maxSpeed = double.MinValue; - var minSpeed = double.MaxValue; - uint blocksToRead = 64; - var mediaTags = new Dictionary(); + uint subSize; + DateTime start; + DateTime end; + bool readcd; + bool read6 = false, read10 = false, read12 = false, read16 = false; + bool sense = false; + const uint SECTOR_SIZE = 2352; + FullTOC.CDFullTOC? toc = null; + double totalDuration = 0; + double currentSpeed = 0; + double maxSpeed = double.MinValue; + double minSpeed = double.MaxValue; + uint blocksToRead = 64; + Dictionary mediaTags = new Dictionary(); - if (dumpRaw) + if(dumpRaw) { dumpLog.WriteLine("Raw CD dumping not yet implemented"); StoppingErrorMessage?.Invoke("Raw CD dumping not yet implemented"); + return; } dskType = MediaType.CD; - var sessions = 1; + int sessions = 1; // We discarded all discs that falsify a TOC before requesting a real TOC // No TOC, no CD (or an empty one) dumpLog.WriteLine("Reading full TOC"); UpdateStatus?.Invoke("Reading full TOC"); - var tocSense = dev.ReadRawToc(out var cmdBuf, out var senseBuf, 0, dev.Timeout, out _); - if (!tocSense) + bool tocSense = dev.ReadRawToc(out byte[] cmdBuf, out byte[] senseBuf, 0, dev.Timeout, out _); + + if(!tocSense) { toc = FullTOC.Decode(cmdBuf); - if (toc.HasValue) + + if(toc.HasValue) { - var tmpBuf = new byte[cmdBuf.Length - 2]; + byte[] tmpBuf = new byte[cmdBuf.Length - 2]; Array.Copy(cmdBuf, 2, tmpBuf, 0, cmdBuf.Length - 2); mediaTags.Add(MediaTagType.CD_FullTOC, tmpBuf); @@ -112,10 +112,12 @@ namespace DiscImageChef.Core.Devices.Dumping dumpLog.WriteLine("Reading ATIP"); UpdateStatus?.Invoke("Reading ATIP"); sense = dev.ReadAtip(out cmdBuf, out senseBuf, dev.Timeout, out _); - if (!sense) + + if(!sense) { - var atip = ATIP.Decode(cmdBuf); - if (atip.HasValue) + ATIP.CDATIP? atip = ATIP.Decode(cmdBuf); + + if(atip.HasValue) { // Only CD-R and CD-RW have ATIP dskType = atip.Value.DiscType ? MediaType.CDRW : MediaType.CDR; @@ -128,57 +130,65 @@ namespace DiscImageChef.Core.Devices.Dumping dumpLog.WriteLine("Reading Disc Information"); UpdateStatus?.Invoke("Reading Disc Information"); + sense = dev.ReadDiscInformation(out cmdBuf, out senseBuf, - MmcDiscInformationDataTypes.DiscInformation, dev.Timeout, out _); - if (!sense) + MmcDiscInformationDataTypes.DiscInformation, dev.Timeout, out _); + + if(!sense) { - var discInfo = DiscInformation.Decode000b(cmdBuf); - if (discInfo.HasValue) - if (dskType == MediaType.CD) - switch (discInfo.Value.DiscType) + DiscInformation.StandardDiscInformation? discInfo = DiscInformation.Decode000b(cmdBuf); + + if(discInfo.HasValue) + if(dskType == MediaType.CD) + switch(discInfo.Value.DiscType) { case 0x10: dskType = MediaType.CDI; + break; case 0x20: dskType = MediaType.CDROMXA; + break; } } - var firstTrackLastSession = 0; + int firstTrackLastSession = 0; dumpLog.WriteLine("Reading Session Information"); UpdateStatus?.Invoke("Reading Session Information"); sense = dev.ReadSessionInfo(out cmdBuf, out senseBuf, dev.Timeout, out _); - if (!sense) + + if(!sense) { - var session = Session.Decode(cmdBuf); - if (session.HasValue) + Session.CDSessionInfo? session = Session.Decode(cmdBuf); + + if(session.HasValue) { - sessions = session.Value.LastCompleteSession; + sessions = session.Value.LastCompleteSession; firstTrackLastSession = session.Value.TrackDescriptors[0].TrackNumber; } } - if (dskType == MediaType.CD || dskType == MediaType.CDROMXA) + if(dskType == MediaType.CD || + dskType == MediaType.CDROMXA) { - var hasDataTrack = false; - var hasAudioTrack = false; - var allFirstSessionTracksAreAudio = true; - var hasVideoTrack = false; + bool hasDataTrack = false; + bool hasAudioTrack = false; + bool allFirstSessionTracksAreAudio = true; + bool hasVideoTrack = false; - foreach (var track in toc.Value.TrackDescriptors) + foreach(FullTOC.TrackDataDescriptor track in toc.Value.TrackDescriptors) { - if (track.TNO == 1 && ((TocControl) (track.CONTROL & 0x0D) == TocControl.DataTrack || - (TocControl) (track.CONTROL & 0x0D) == - TocControl.DataTrackIncremental) - ) allFirstSessionTracksAreAudio &= firstTrackLastSession != 1; + if(track.TNO == 1 && + ((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack || + (TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental)) + allFirstSessionTracksAreAudio &= firstTrackLastSession != 1; - if ((TocControl) (track.CONTROL & 0x0D) == TocControl.DataTrack || - (TocControl) (track.CONTROL & 0x0D) == TocControl.DataTrackIncremental) + if((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack || + (TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental) { - hasDataTrack = true; + hasDataTrack = true; allFirstSessionTracksAreAudio &= track.POINT >= firstTrackLastSession; } else @@ -189,18 +199,34 @@ namespace DiscImageChef.Core.Devices.Dumping hasVideoTrack |= track.ADR == 4; } - if (hasDataTrack && hasAudioTrack && allFirstSessionTracksAreAudio && sessions == 2) + if(hasDataTrack && + hasAudioTrack && + allFirstSessionTracksAreAudio && + sessions == 2) dskType = MediaType.CDPLUS; - if (!hasDataTrack && hasAudioTrack && sessions == 1) dskType = MediaType.CDDA; - if (hasDataTrack && !hasAudioTrack && sessions == 1) dskType = MediaType.CDROM; - if (hasVideoTrack && !hasDataTrack && sessions == 1) dskType = MediaType.CDV; + + if(!hasDataTrack && + hasAudioTrack && + sessions == 1) + dskType = MediaType.CDDA; + + if(hasDataTrack && + !hasAudioTrack && + sessions == 1) + dskType = MediaType.CDROM; + + if(hasVideoTrack && + !hasDataTrack && + sessions == 1) + dskType = MediaType.CDV; } dumpLog.WriteLine("Reading PMA"); UpdateStatus?.Invoke("Reading PMA"); sense = dev.ReadPma(out cmdBuf, out senseBuf, dev.Timeout, out _); - if (!sense) - if (PMA.Decode(cmdBuf).HasValue) + + if(!sense) + if(PMA.Decode(cmdBuf).HasValue) { tmpBuf = new byte[cmdBuf.Length - 4]; Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); @@ -210,8 +236,9 @@ namespace DiscImageChef.Core.Devices.Dumping dumpLog.WriteLine("Reading CD-Text from Lead-In"); UpdateStatus?.Invoke("Reading CD-Text from Lead-In"); sense = dev.ReadCdText(out cmdBuf, out senseBuf, dev.Timeout, out _); - if (!sense) - if (CDTextOnLeadIn.Decode(cmdBuf).HasValue) + + if(!sense) + if(CDTextOnLeadIn.Decode(cmdBuf).HasValue) { tmpBuf = new byte[cmdBuf.Length - 4]; Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); @@ -223,22 +250,24 @@ namespace DiscImageChef.Core.Devices.Dumping // TODO: Add other detectors here dumpLog.WriteLine("Detecting disc type..."); UpdateStatus?.Invoke("Detecting disc type..."); - var videoNowColorFrame = new byte[9 * 2352]; - for (var i = 0; i < 9; i++) + byte[] videoNowColorFrame = new byte[9 * 2352]; + + for(int i = 0; i < 9; i++) { - sense = dev.ReadCd(out cmdBuf, out senseBuf, (uint) i, 2352, 1, MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, - dev.Timeout, out _); + sense = dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, 2352, 1, MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, + dev.Timeout, out _); - if (sense || dev.Error) + if(sense || dev.Error) { - sense = dev.ReadCd(out cmdBuf, out senseBuf, (uint) i, 2352, 1, MmcSectorTypes.Cdda, false, false, - true, MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None, - dev.Timeout, out _); + sense = dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, 2352, 1, MmcSectorTypes.Cdda, false, false, + true, MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None, + dev.Timeout, out _); - if (sense || !dev.Error) + if(sense || !dev.Error) { videoNowColorFrame = null; + break; } } @@ -246,15 +275,18 @@ namespace DiscImageChef.Core.Devices.Dumping Array.Copy(cmdBuf, 0, videoNowColorFrame, i * 2352, 2352); } - if (MMC.IsVideoNowColor(videoNowColorFrame)) dskType = MediaType.VideoNowColor; + if(MMC.IsVideoNowColor(videoNowColorFrame)) + dskType = MediaType.VideoNowColor; var supportedSubchannel = MmcSubchannel.Raw; dumpLog.WriteLine("Checking if drive supports full raw subchannel reading..."); UpdateStatus?.Invoke("Checking if drive supports full raw subchannel reading..."); - readcd = !dev.ReadCd(out var readBuffer, out senseBuf, 0, SECTOR_SIZE + 96, 1, MmcSectorTypes.AllTypes, - false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - supportedSubchannel, dev.Timeout, out _); - if (readcd) + + readcd = !dev.ReadCd(out byte[] readBuffer, out senseBuf, 0, SECTOR_SIZE + 96, 1, MmcSectorTypes.AllTypes, + false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + supportedSubchannel, dev.Timeout, out _); + + if(readcd) { dumpLog.WriteLine("Full raw subchannel reading supported..."); UpdateStatus?.Invoke("Full raw subchannel reading supported..."); @@ -265,19 +297,20 @@ namespace DiscImageChef.Core.Devices.Dumping supportedSubchannel = MmcSubchannel.Q16; dumpLog.WriteLine("Checking if drive supports PQ subchannel reading..."); UpdateStatus?.Invoke("Checking if drive supports PQ subchannel reading..."); - readcd = !dev.ReadCd(out readBuffer, out senseBuf, 0, SECTOR_SIZE + 16, 1, MmcSectorTypes.AllTypes, - false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - supportedSubchannel, dev.Timeout, out _); - if (readcd) + readcd = !dev.ReadCd(out readBuffer, out senseBuf, 0, SECTOR_SIZE + 16, 1, MmcSectorTypes.AllTypes, + false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + supportedSubchannel, dev.Timeout, out _); + + if(readcd) { dumpLog.WriteLine("PQ subchannel reading supported..."); - dumpLog.WriteLine( - "WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); + dumpLog.WriteLine("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); UpdateStatus?.Invoke("PQ subchannel reading supported..."); - UpdateStatus - ?.Invoke( - "WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); + + UpdateStatus?. + Invoke("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); + subSize = 16; } else @@ -285,11 +318,12 @@ namespace DiscImageChef.Core.Devices.Dumping supportedSubchannel = MmcSubchannel.None; dumpLog.WriteLine("Checking if drive supports reading without subchannel..."); UpdateStatus?.Invoke("Checking if drive supports reading without subchannel..."); - readcd = !dev.ReadCd(out readBuffer, out senseBuf, 0, SECTOR_SIZE, 1, MmcSectorTypes.AllTypes, - false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - supportedSubchannel, dev.Timeout, out _); - if (!readcd) + readcd = !dev.ReadCd(out readBuffer, out senseBuf, 0, SECTOR_SIZE, 1, MmcSectorTypes.AllTypes, + false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + supportedSubchannel, dev.Timeout, out _); + + if(!readcd) { dumpLog.WriteLine("Drive does not support READ CD, trying SCSI READ commands..."); ErrorMessage?.Invoke("Drive does not support READ CD, trying SCSI READ commands..."); @@ -299,43 +333,52 @@ namespace DiscImageChef.Core.Devices.Dumping read6 = !dev.Read6(out readBuffer, out senseBuf, 0, 2048, 1, dev.Timeout, out _); dumpLog.WriteLine("Checking if drive supports READ(10)..."); UpdateStatus?.Invoke("Checking if drive supports READ(10)..."); + read10 = !dev.Read10(out readBuffer, out senseBuf, 0, false, true, false, false, 0, 2048, 0, 1, - dev.Timeout, out _); + dev.Timeout, out _); + dumpLog.WriteLine("Checking if drive supports READ(12)..."); UpdateStatus?.Invoke("Checking if drive supports READ(12)..."); + read12 = !dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, 0, 2048, 0, 1, - false, dev.Timeout, out _); + false, dev.Timeout, out _); + dumpLog.WriteLine("Checking if drive supports READ(16)..."); UpdateStatus?.Invoke("Checking if drive supports READ(16)..."); - read16 = !dev.Read16(out readBuffer, out senseBuf, 0, false, true, false, 0, 2048, 0, 1, false, - dev.Timeout, out _); - if (!read6 && !read10 && !read12 && !read16) + read16 = !dev.Read16(out readBuffer, out senseBuf, 0, false, true, false, 0, 2048, 0, 1, false, + dev.Timeout, out _); + + if(!read6 && + !read10 && + !read12 && + !read16) { dumpLog.WriteLine("Cannot read from disc, not continuing..."); StoppingErrorMessage?.Invoke("Cannot read from disc, not continuing..."); + return; } - if (read6) + if(read6) { dumpLog.WriteLine("Drive supports READ(6)..."); UpdateStatus?.Invoke("Drive supports READ(6)..."); } - if (read10) + if(read10) { dumpLog.WriteLine("Drive supports READ(10)..."); UpdateStatus?.Invoke("Drive supports READ(10)..."); } - if (read12) + if(read12) { dumpLog.WriteLine("Drive supports READ(12)..."); UpdateStatus?.Invoke("Drive supports READ(12)..."); } - if (read16) + if(read16) { dumpLog.WriteLine("Drive supports READ(16)..."); UpdateStatus?.Invoke("Drive supports READ(16)..."); @@ -343,21 +386,21 @@ namespace DiscImageChef.Core.Devices.Dumping } dumpLog.WriteLine("Drive can only read without subchannel..."); - dumpLog.WriteLine( - "WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); + dumpLog.WriteLine("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); UpdateStatus?.Invoke("Drive can only read without subchannel..."); - UpdateStatus - ?.Invoke( - "WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); + + UpdateStatus?. + Invoke("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); + subSize = 0; } } // Check if output format supports subchannels - if (!outputPlugin.SupportedSectorTags.Contains(SectorTagType.CdSectorSubchannel) && - supportedSubchannel != MmcSubchannel.None) + if(!outputPlugin.SupportedSectorTags.Contains(SectorTagType.CdSectorSubchannel) && + supportedSubchannel != MmcSubchannel.None) { - if (!force) + if(!force) { dumpLog.WriteLine("Output format does not support subchannels, continuing..."); UpdateStatus?.Invoke("Output format does not support subchannels, continuing..."); @@ -366,126 +409,134 @@ namespace DiscImageChef.Core.Devices.Dumping { dumpLog.WriteLine("Output format does not support subchannels, not continuing..."); StoppingErrorMessage?.Invoke("Output format does not support subchannels, not continuing..."); + return; } supportedSubchannel = MmcSubchannel.None; - subSize = 0; + subSize = 0; } TrackSubchannelType subType; - switch (supportedSubchannel) + switch(supportedSubchannel) { case MmcSubchannel.None: subType = TrackSubchannelType.None; + break; case MmcSubchannel.Raw: subType = TrackSubchannelType.Raw; + break; case MmcSubchannel.Q16: subType = TrackSubchannelType.Q16; + break; default: dumpLog.WriteLine("Handling subchannel type {0} not supported, exiting...", supportedSubchannel); - StoppingErrorMessage - ?.Invoke($"Handling subchannel type {supportedSubchannel} not supported, exiting..."); + + StoppingErrorMessage?. + Invoke($"Handling subchannel type {supportedSubchannel} not supported, exiting..."); + return; } - var blockSize = SECTOR_SIZE + subSize; + uint blockSize = SECTOR_SIZE + subSize; UpdateStatus?.Invoke("Building track map..."); dumpLog.WriteLine("Building track map..."); - var trackList = new List(); - long lastSector = 0; - var trackFlags = new Dictionary(); - var firstTrackType = TrackType.Audio; - var leadOutStarts = new Dictionary(); + List trackList = new List(); + long lastSector = 0; + Dictionary trackFlags = new Dictionary(); + var firstTrackType = TrackType.Audio; + Dictionary leadOutStarts = new Dictionary(); - if (toc.HasValue) + if(toc.HasValue) { - var sortedTracks = + FullTOC.TrackDataDescriptor[] sortedTracks = toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray(); - foreach (var trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4)) - if (trk.POINT >= 0x01 && trk.POINT <= 0x63) + foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4)) + if(trk.POINT >= 0x01 && + trk.POINT <= 0x63) { trackList.Add(new Track { - TrackSequence = trk.POINT, - TrackSession = trk.SessionNumber, - TrackType = - (TocControl) (trk.CONTROL & 0x0D) == TocControl.DataTrack || - (TocControl) (trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental - ? TrackType.Data - : TrackType.Audio, + TrackSequence = trk.POINT, TrackSession = trk.SessionNumber, + TrackType = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || + (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental + ? TrackType.Data : TrackType.Audio, TrackStartSector = - (ulong) (trk.PHOUR * 3600 * 75 + trk.PMIN * 60 * 75 + trk.PSEC * 75 + - trk.PFRAME - 150), - TrackBytesPerSector = (int) SECTOR_SIZE, - TrackRawBytesPerSector = (int) SECTOR_SIZE, + (ulong)(trk.PHOUR * 3600 * 75 + trk.PMIN * 60 * 75 + trk.PSEC * 75 + trk.PFRAME - 150), + TrackBytesPerSector = (int)SECTOR_SIZE, TrackRawBytesPerSector = (int)SECTOR_SIZE, TrackSubchannelType = subType }); + trackFlags.Add(trk.POINT, trk.CONTROL); } - else if (trk.POINT == 0xA2) + else if(trk.POINT == 0xA2) { int phour, pmin, psec, pframe; - if (trk.PFRAME == 0) + + if(trk.PFRAME == 0) { pframe = 74; - if (trk.PSEC == 0) + if(trk.PSEC == 0) { psec = 59; - if (trk.PMIN == 0) + if(trk.PMIN == 0) { - pmin = 59; + pmin = 59; phour = trk.PHOUR - 1; } else { - pmin = trk.PMIN - 1; + pmin = trk.PMIN - 1; phour = trk.PHOUR; } } else { - psec = trk.PSEC - 1; - pmin = trk.PMIN; + psec = trk.PSEC - 1; + pmin = trk.PMIN; phour = trk.PHOUR; } } else { pframe = trk.PFRAME - 1; - psec = trk.PSEC; - pmin = trk.PMIN; - phour = trk.PHOUR; + psec = trk.PSEC; + pmin = trk.PMIN; + phour = trk.PHOUR; } lastSector = phour * 3600 * 75 + pmin * 60 * 75 + psec * 75 + pframe - 150; leadOutStarts.Add(trk.SessionNumber, lastSector + 1); } - else if (trk.POINT == 0xA0 && trk.ADR == 1) + else if(trk.POINT == 0xA0 && + trk.ADR == 1) { - switch (trk.PSEC) + switch(trk.PSEC) { case 0x10: dskType = MediaType.CDI; + break; case 0x20: - if (dskType == MediaType.CD || dskType == MediaType.CDROM) dskType = MediaType.CDROMXA; + if(dskType == MediaType.CD || + dskType == MediaType.CDROM) + dskType = MediaType.CDROMXA; + break; } firstTrackType = - (TocControl) (trk.CONTROL & 0x0D) == TocControl.DataTrack || - (TocControl) (trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental - ? TrackType.Data + (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || + (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data : TrackType.Audio; } } @@ -495,150 +546,158 @@ namespace DiscImageChef.Core.Devices.Dumping dumpLog.WriteLine("Cannot read RAW TOC, requesting processed one..."); tocSense = dev.ReadToc(out cmdBuf, out senseBuf, false, 0, dev.Timeout, out _); - var oldToc = TOC.Decode(cmdBuf); - if ((tocSense || !oldToc.HasValue) && !force) + TOC.CDTOC? oldToc = TOC.Decode(cmdBuf); + + if((tocSense || !oldToc.HasValue) && + !force) { - dumpLog.WriteLine( - "Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000..."); - StoppingErrorMessage - ?.Invoke( - "Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000..."); + dumpLog.WriteLine("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000..."); + + StoppingErrorMessage?. + Invoke("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000..."); + return; } - foreach (var trk in oldToc - .Value.TrackDescriptors.OrderBy(t => t.TrackNumber) - .Where(trk => trk.ADR == 1 || trk.ADR == 4)) - if (trk.TrackNumber >= 0x01 && trk.TrackNumber <= 0x63) + foreach(TOC.CDTOCTrackDataDescriptor trk in oldToc. + Value.TrackDescriptors.OrderBy(t => t.TrackNumber). + Where(trk => trk.ADR == 1 || trk.ADR == 4)) + if(trk.TrackNumber >= 0x01 && + trk.TrackNumber <= 0x63) { trackList.Add(new Track { - TrackSequence = trk.TrackNumber, - TrackSession = 1, - TrackType = - (TocControl) (trk.CONTROL & 0x0D) == TocControl.DataTrack || - (TocControl) (trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental - ? TrackType.Data - : TrackType.Audio, - TrackStartSector = trk.TrackStartAddress, - TrackBytesPerSector = (int) SECTOR_SIZE, - TrackRawBytesPerSector = (int) SECTOR_SIZE, - TrackSubchannelType = subType + TrackSequence = trk.TrackNumber, TrackSession = 1, + TrackType = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || + (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental + ? TrackType.Data : TrackType.Audio, + TrackStartSector = trk.TrackStartAddress, TrackBytesPerSector = (int)SECTOR_SIZE, + TrackRawBytesPerSector = (int)SECTOR_SIZE, TrackSubchannelType = subType }); + trackFlags.Add(trk.TrackNumber, trk.CONTROL); } - else if (trk.TrackNumber == 0xAA) + else if(trk.TrackNumber == 0xAA) { firstTrackType = - (TocControl) (trk.CONTROL & 0x0D) == TocControl.DataTrack || - (TocControl) (trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental - ? TrackType.Data + (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || + (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data : TrackType.Audio; + lastSector = trk.TrackStartAddress - 1; } } - if (trackList.Count == 0) + if(trackList.Count == 0) { UpdateStatus?.Invoke("No tracks found, adding a single track from 0 to Lead-Out"); dumpLog.WriteLine("No tracks found, adding a single track from 0 to Lead-Out"); trackList.Add(new Track { - TrackSequence = 1, - TrackSession = 1, - TrackType = firstTrackType, - TrackStartSector = 0, - TrackBytesPerSector = (int) SECTOR_SIZE, - TrackRawBytesPerSector = (int) SECTOR_SIZE, + TrackSequence = 1, TrackSession = 1, TrackType = firstTrackType, + TrackStartSector = 0, + TrackBytesPerSector = (int)SECTOR_SIZE, TrackRawBytesPerSector = (int)SECTOR_SIZE, TrackSubchannelType = subType }); - trackFlags.Add(1, (byte) (firstTrackType == TrackType.Audio ? 0 : 4)); + + trackFlags.Add(1, (byte)(firstTrackType == TrackType.Audio ? 0 : 4)); } - if (lastSector == 0) + if(lastSector == 0) { sense = dev.ReadCapacity16(out readBuffer, out senseBuf, dev.Timeout, out _); - if (!sense) + + if(!sense) { - var temp = new byte[8]; + byte[] temp = new byte[8]; Array.Copy(cmdBuf, 0, temp, 0, 8); Array.Reverse(temp); - lastSector = (long) BitConverter.ToUInt64(temp, 0); - blockSize = (uint) ((cmdBuf[5] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]); + lastSector = (long)BitConverter.ToUInt64(temp, 0); + blockSize = (uint)((cmdBuf[5] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]); } else { sense = dev.ReadCapacity(out cmdBuf, out senseBuf, dev.Timeout, out _); - if (!sense) + + if(!sense) { lastSector = (cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]; - blockSize = (uint) ((cmdBuf[5] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]); + blockSize = (uint)((cmdBuf[5] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]); } } - if (lastSector <= 0) + if(lastSector <= 0) { - if (!force) + if(!force) { - StoppingErrorMessage - ?.Invoke( - "Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors..."); - dumpLog.WriteLine( - "Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors..."); + StoppingErrorMessage?. + Invoke("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors..."); + + dumpLog. + WriteLine("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors..."); + return; } - UpdateStatus - ?.Invoke( - "WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before..."); - dumpLog.WriteLine( - "WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before..."); + UpdateStatus?. + Invoke("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before..."); + + dumpLog.WriteLine("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before..."); lastSector = 360000; } } - var tracks = trackList.ToArray(); - for (var t = 1; t < tracks.Length; t++) tracks[t - 1].TrackEndSector = tracks[t].TrackStartSector - 1; + Track[] tracks = trackList.ToArray(); - tracks[tracks.Length - 1].TrackEndSector = (ulong) lastSector; - var blocks = (ulong) (lastSector + 1); + for(int t = 1; t < tracks.Length; t++) + tracks[t - 1].TrackEndSector = tracks[t].TrackStartSector - 1; - if (blocks == 0) + tracks[tracks.Length - 1].TrackEndSector = (ulong)lastSector; + ulong blocks = (ulong)(lastSector + 1); + + if(blocks == 0) { StoppingErrorMessage?.Invoke("Cannot dump blank media."); + return; } var leadOutExtents = new ExtentsULong(); - if (leadOutStarts.Any()) + if(leadOutStarts.Any()) { UpdateStatus?.Invoke("Solving lead-outs..."); - foreach (var leadOuts in leadOutStarts) - for (var i = 0; i < tracks.Length; i++) - { - if (tracks[i].TrackSession != leadOuts.Key) continue; - if (tracks[i].TrackEndSector >= (ulong) leadOuts.Value) - tracks[i].TrackEndSector = (ulong) leadOuts.Value - 1; + foreach(KeyValuePair leadOuts in leadOutStarts) + for(int i = 0; i < tracks.Length; i++) + { + if(tracks[i].TrackSession != leadOuts.Key) + continue; + + if(tracks[i].TrackEndSector >= (ulong)leadOuts.Value) + tracks[i].TrackEndSector = (ulong)leadOuts.Value - 1; } var dataExtents = new ExtentsULong(); - foreach (var trk in tracks) dataExtents.Add(trk.TrackStartSector, trk.TrackEndSector); - var dataExtentsArray = dataExtents.ToArray(); - for (var i = 0; i < dataExtentsArray.Length - 1; i++) + foreach(Track trk in tracks) + dataExtents.Add(trk.TrackStartSector, trk.TrackEndSector); + + Tuple[] dataExtentsArray = dataExtents.ToArray(); + + for(int i = 0; i < dataExtentsArray.Length - 1; i++) leadOutExtents.Add(dataExtentsArray[i].Item2 + 1, dataExtentsArray[i + 1].Item1 - 1); } // Check if output format supports all disc tags we have retrieved so far - foreach (var tag in mediaTags.Keys) + foreach(MediaTagType tag in mediaTags.Keys) { - if (outputPlugin.SupportedMediaTags.Contains(tag)) continue; + if(outputPlugin.SupportedMediaTags.Contains(tag)) + continue; - if (!force) + if(!force) { dumpLog.WriteLine("Output format does not support {0}, continuing...", tag); ErrorMessage?.Invoke($"Output format does not support {tag}, continuing..."); @@ -647,60 +706,72 @@ namespace DiscImageChef.Core.Devices.Dumping { dumpLog.WriteLine("Output format does not support {0}, not continuing...", tag); StoppingErrorMessage?.Invoke($"Output format does not support {tag}, not continuing..."); + return; } } // Check for hidden data before start of track 1 - if (tracks.First(t => t.TrackSequence == 1).TrackStartSector > 0 && readcd) + if(tracks.First(t => t.TrackSequence == 1).TrackStartSector > 0 && readcd) { dumpLog.WriteLine("First track starts after sector 0, checking for a hidden track..."); UpdateStatus?.Invoke("First track starts after sector 0, checking for a hidden track..."); sense = dev.ReadCd(out readBuffer, out senseBuf, 0, blockSize, 1, MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, supportedSubchannel, - dev.Timeout, out _); + true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, supportedSubchannel, + dev.Timeout, out _); - if (dev.Error || sense) + if(dev.Error || sense) { dumpLog.WriteLine("Could not read sector 0, continuing..."); UpdateStatus?.Invoke("Could not read sector 0, continuing..."); } else { - byte[] syncMark = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}; - byte[] cdiMark = {0x01, 0x43, 0x44, 0x2D}; - var testMark = new byte[12]; + byte[] syncMark = + { + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 + }; + + byte[] cdiMark = + { + 0x01, 0x43, 0x44, 0x2D + }; + + byte[] testMark = new byte[12]; Array.Copy(readBuffer, 0, testMark, 0, 12); - var hiddenData = syncMark.SequenceEqual(testMark) && - (readBuffer[0xF] == 0 || readBuffer[0xF] == 1 || readBuffer[0xF] == 2); + bool hiddenData = syncMark.SequenceEqual(testMark) && + (readBuffer[0xF] == 0 || readBuffer[0xF] == 1 || readBuffer[0xF] == 2); - if (hiddenData && readBuffer[0xF] == 2) + if(hiddenData && readBuffer[0xF] == 2) { sense = dev.ReadCd(out readBuffer, out senseBuf, 16, blockSize, 1, MmcSectorTypes.AllTypes, - false, false, true, MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, supportedSubchannel, dev.Timeout, out _); + false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, supportedSubchannel, dev.Timeout, out _); - if (!dev.Error && !sense) + if(!dev.Error && + !sense) { testMark = new byte[4]; Array.Copy(readBuffer, 24, testMark, 0, 4); - if (cdiMark.SequenceEqual(testMark)) dskType = MediaType.CDIREADY; + + if(cdiMark.SequenceEqual(testMark)) + dskType = MediaType.CDIREADY; } - var trkList = new List + List trkList = new List { new Track { - TrackSequence = 0, - TrackSession = 1, - TrackType = hiddenData ? TrackType.Data : TrackType.Audio, - TrackStartSector = 0, - TrackBytesPerSector = (int) SECTOR_SIZE, - TrackRawBytesPerSector = (int) SECTOR_SIZE, - TrackSubchannelType = subType, - TrackEndSector = tracks.First(t => t.TrackSequence == 1).TrackStartSector - 1 + TrackSequence = 0, + TrackSession = 1, + TrackType = hiddenData ? TrackType.Data : TrackType.Audio, + TrackStartSector = 0, + TrackBytesPerSector = (int)SECTOR_SIZE, + TrackRawBytesPerSector = (int)SECTOR_SIZE, + TrackSubchannelType = subType, + TrackEndSector = tracks.First(t => t.TrackSequence == 1).TrackStartSector - 1 } }; @@ -711,51 +782,58 @@ namespace DiscImageChef.Core.Devices.Dumping } // Check mode for tracks - for (var t = 0; t < tracks.Length; t++) + for(int t = 0; t < tracks.Length; t++) { - if (!readcd) + if(!readcd) { tracks[t].TrackType = TrackType.CdMode1; + continue; } - if (tracks[t].TrackType == TrackType.Audio) continue; + if(tracks[t].TrackType == TrackType.Audio) + continue; dumpLog.WriteLine("Checking mode for track {0}...", tracks[t].TrackSequence); UpdateStatus?.Invoke($"Checking mode for track {tracks[t].TrackSequence}..."); - readcd = !dev.ReadCd(out readBuffer, out senseBuf, (uint) tracks[t].TrackStartSector, blockSize, 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, supportedSubchannel, dev.Timeout, out _); + readcd = !dev.ReadCd(out readBuffer, out senseBuf, (uint)tracks[t].TrackStartSector, blockSize, 1, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, supportedSubchannel, dev.Timeout, out _); - if (!readcd) + if(!readcd) { dumpLog.WriteLine("Unable to guess mode for track {0}, continuing...", tracks[t].TrackSequence); UpdateStatus?.Invoke($"Unable to guess mode for track {tracks[t].TrackSequence}, continuing..."); + continue; } - switch (readBuffer[15]) + switch(readBuffer[15]) { case 1: UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE1"); dumpLog.WriteLine("Track {0} is MODE1", tracks[t].TrackSequence); tracks[t].TrackType = TrackType.CdMode1; + break; case 2: - if (dskType == MediaType.CDI || dskType == MediaType.CDIREADY) + if(dskType == MediaType.CDI || + dskType == MediaType.CDIREADY) { UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE2"); dumpLog.WriteLine("Track {0} is MODE2", tracks[t].TrackSequence); tracks[t].TrackType = TrackType.CdMode2Formless; + break; } - if ((readBuffer[0x012] & 0x20) == 0x20) // mode 2 form 2 + if((readBuffer[0x012] & 0x20) == 0x20) // mode 2 form 2 { UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE2 FORM 2"); dumpLog.WriteLine("Track {0} is MODE2 FORM 2", tracks[t].TrackSequence); tracks[t].TrackType = TrackType.CdMode2Form2; + break; } @@ -764,39 +842,45 @@ namespace DiscImageChef.Core.Devices.Dumping tracks[t].TrackType = TrackType.CdMode2Form1; // These media type specifications do not legally allow mode 2 tracks to be present - if (dskType == MediaType.CDROM || dskType == MediaType.CDPLUS || dskType == MediaType.CDV) + if(dskType == MediaType.CDROM || + dskType == MediaType.CDPLUS || + dskType == MediaType.CDV) dskType = MediaType.CD; break; default: UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is unknown mode {readBuffer[15]}"); dumpLog.WriteLine("Track {0} is unknown mode {1}", tracks[t].TrackSequence, readBuffer[15]); + break; } } - var supportsLongSectors = true; + bool supportsLongSectors = true; - if (outputPlugin.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000")) + if(outputPlugin.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000")) { - if (tracks.Length > 1) + if(tracks.Length > 1) { StoppingErrorMessage?.Invoke("Output format does not support more than 1 track, not continuing..."); dumpLog.WriteLine("Output format does not support more than 1 track, not continuing..."); + return; } - if (tracks.Any(t => t.TrackType == TrackType.Audio)) + if(tracks.Any(t => t.TrackType == TrackType.Audio)) { StoppingErrorMessage?.Invoke("Output format does not support audio tracks, not continuing..."); dumpLog.WriteLine("Output format does not support audio tracks, not continuing..."); + return; } - if (tracks.Any(t => t.TrackType != TrackType.CdMode1)) + if(tracks.Any(t => t.TrackType != TrackType.CdMode1)) { StoppingErrorMessage?.Invoke("Output format only supports MODE 1 tracks, not continuing..."); dumpLog.WriteLine("Output format only supports MODE 1 tracks, not continuing..."); + return; } @@ -804,34 +888,35 @@ namespace DiscImageChef.Core.Devices.Dumping } // Check if something prevents from dumping the first track pregap - if (dumpFirstTrackPregap && readcd) + if(dumpFirstTrackPregap && readcd) { - if (dev.PlatformId == PlatformID.FreeBSD) + if(dev.PlatformId == PlatformID.FreeBSD) { - if (force) + if(force) { - dumpLog.WriteLine( - "FreeBSD panics when reading CD first track pregap, see upstream bug #224253. continuing"); - ErrorMessage - ?.Invoke( - "FreeBSD panics when reading CD first track pregap, see upstream bug #224253. continuing"); + dumpLog. + WriteLine("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. continuing"); + + ErrorMessage?. + Invoke("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. continuing"); } else { - dumpLog.WriteLine( - "FreeBSD panics when reading CD first track pregap, see upstream bug #224253. Not continuing"); - StoppingErrorMessage - ?.Invoke( - "FreeBSD panics when reading CD first track pregap, see upstream bug #224253. Not continuing"); + dumpLog. + WriteLine("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. Not continuing"); + + StoppingErrorMessage?. + Invoke("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. Not continuing"); + return; } dumpFirstTrackPregap = false; } - if (!outputPlugin.SupportedMediaTags.Contains(MediaTagType.CD_FirstTrackPregap)) + if(!outputPlugin.SupportedMediaTags.Contains(MediaTagType.CD_FirstTrackPregap)) { - if (force) + if(force) { dumpLog.WriteLine("Output format does not support CD first track pregap, continuing..."); ErrorMessage?.Invoke("Output format does not support CD first track pregap, continuing..."); @@ -839,8 +924,10 @@ namespace DiscImageChef.Core.Devices.Dumping else { dumpLog.WriteLine("Output format does not support CD first track pregap, not continuing..."); - StoppingErrorMessage - ?.Invoke("Output format does not support CD first track pregap, not continuing..."); + + StoppingErrorMessage?. + Invoke("Output format does not support CD first track pregap, not continuing..."); + return; } @@ -849,73 +936,81 @@ namespace DiscImageChef.Core.Devices.Dumping } DumpHardwareType currentTry = null; - ExtentsULong extents = null; + ExtentsULong extents = null; + ResumeSupport.Process(true, true, blocks, dev.Manufacturer, dev.Model, dev.Serial, dev.PlatformId, - ref resume, ref currentTry, ref extents); - if (currentTry == null || extents == null) + ref resume, ref currentTry, ref extents); + + if(currentTry == null || + extents == null) { StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + return; } - var timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; // Try to read the first track pregap - if (dumpFirstTrackPregap && readcd) + if(dumpFirstTrackPregap && readcd) { - var gotFirstTrackPregap = false; - var firstTrackPregapSectorsGood = 0; - var firstTrackPregapMs = new MemoryStream(); + bool gotFirstTrackPregap = false; + int firstTrackPregapSectorsGood = 0; + var firstTrackPregapMs = new MemoryStream(); readBuffer = null; dumpLog.WriteLine("Reading first track pregap"); UpdateStatus?.Invoke("Reading first track pregap"); InitProgress?.Invoke(); - for (var firstTrackPregapBlock = -150; - firstTrackPregapBlock < 0 && resume.NextBlock == 0; + + for(int firstTrackPregapBlock = -150; firstTrackPregapBlock < 0 && resume.NextBlock == 0; firstTrackPregapBlock++) { - if (aborted) + if(aborted) { dumpLog.WriteLine("Aborted!"); UpdateStatus?.Invoke("Aborted!"); + break; } - PulseProgress - ?.Invoke( - $"Trying to read first track pregap sector {firstTrackPregapBlock} ({currentSpeed:F3} MiB/sec.)"); + PulseProgress?. + Invoke($"Trying to read first track pregap sector {firstTrackPregapBlock} ({currentSpeed:F3} MiB/sec.)"); - sense = dev.ReadCd(out readBuffer, out senseBuf, (uint) firstTrackPregapBlock, blockSize, 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, supportedSubchannel, dev.Timeout, - out var cmdDuration); + sense = dev.ReadCd(out readBuffer, out senseBuf, (uint)firstTrackPregapBlock, blockSize, 1, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, supportedSubchannel, dev.Timeout, + out double cmdDuration); - if (!sense && !dev.Error) + if(!sense && + !dev.Error) { - firstTrackPregapMs.Write(readBuffer, 0, (int) blockSize); + firstTrackPregapMs.Write(readBuffer, 0, (int)blockSize); gotFirstTrackPregap = true; firstTrackPregapSectorsGood++; } else { // Write empty data - if (gotFirstTrackPregap) firstTrackPregapMs.Write(new byte[blockSize], 0, (int) blockSize); + if(gotFirstTrackPregap) + firstTrackPregapMs.Write(new byte[blockSize], 0, (int)blockSize); } sectorSpeedStart++; - var elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - if (elapsed < 1) continue; + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + if(elapsed < 1) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; + timeSpeedStart = DateTime.UtcNow; } - if (firstTrackPregapSectorsGood > 0) + if(firstTrackPregapSectorsGood > 0) mediaTags.Add(MediaTagType.CD_FirstTrackPregap, firstTrackPregapMs.ToArray()); EndProgress?.Invoke(); @@ -926,47 +1021,60 @@ namespace DiscImageChef.Core.Devices.Dumping } // Try how many blocks are readable at once - while (true) + while(true) { - if (readcd) + if(readcd) { sense = dev.ReadCd(out readBuffer, out senseBuf, 0, blockSize, blocksToRead, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, supportedSubchannel, dev.Timeout, out _); - if (dev.Error || sense) blocksToRead /= 2; + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, supportedSubchannel, dev.Timeout, out _); + + if(dev.Error || sense) + blocksToRead /= 2; } - else if (read16) + else if(read16) { sense = dev.Read16(out readBuffer, out senseBuf, 0, false, true, false, 0, blockSize, 0, - blocksToRead, false, dev.Timeout, out _); - if (dev.Error || sense) blocksToRead /= 2; + blocksToRead, false, dev.Timeout, out _); + + if(dev.Error || sense) + blocksToRead /= 2; } - else if (read12) + else if(read12) { sense = dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, 0, blockSize, 0, - blocksToRead, false, dev.Timeout, out _); - if (dev.Error || sense) blocksToRead /= 2; + blocksToRead, false, dev.Timeout, out _); + + if(dev.Error || sense) + blocksToRead /= 2; } - else if (read10) + else if(read10) { sense = dev.Read10(out readBuffer, out senseBuf, 0, false, true, false, false, 0, blockSize, 0, - (ushort) blocksToRead, dev.Timeout, out _); - if (dev.Error || sense) blocksToRead /= 2; + (ushort)blocksToRead, dev.Timeout, out _); + + if(dev.Error || sense) + blocksToRead /= 2; } - else if (read6) + else if(read6) { - sense = dev.Read6(out readBuffer, out senseBuf, 0, blockSize, (byte) blocksToRead, dev.Timeout, - out _); - if (dev.Error || sense) blocksToRead /= 2; + sense = dev.Read6(out readBuffer, out senseBuf, 0, blockSize, (byte)blocksToRead, dev.Timeout, + out _); + + if(dev.Error || sense) + blocksToRead /= 2; } - if (!dev.Error || blocksToRead == 1) break; + if(!dev.Error || + blocksToRead == 1) + break; } - if (dev.Error || sense) + if(dev.Error || sense) { dumpLog.WriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError); StoppingErrorMessage?.Invoke($"Device error {dev.LastError} trying to guess ideal transfer length."); + return; } @@ -985,115 +1093,144 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke($"Media identified as {dskType}."); var mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); - var ibgLog = new IbgLog(outputPrefix + ".ibg", 0x0008); - var ret = outputPlugin.Create(outputPath, dskType, formatOptions, blocks, - supportsLongSectors ? blockSize : 2048); + var ibgLog = new IbgLog(outputPrefix + ".ibg", 0x0008); + + bool ret = outputPlugin.Create(outputPath, dskType, formatOptions, blocks, + supportsLongSectors ? blockSize : 2048); // Cannot create image - if (!ret) + 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; } // Send tracklist to output plugin. This may fail if subchannel is set but unsupported. ret = (outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList()); - if (!ret && supportedSubchannel == MmcSubchannel.None) + + if(!ret && + supportedSubchannel == MmcSubchannel.None) { dumpLog.WriteLine("Error sending tracks to output image, not continuing."); dumpLog.WriteLine(outputPlugin.ErrorMessage); + StoppingErrorMessage?.Invoke("Error sending tracks to output image, not continuing." + - Environment.NewLine + + Environment.NewLine + outputPlugin.ErrorMessage); + return; } // If a subchannel is supported, check if output plugin allows us to write it. - if (supportedSubchannel != MmcSubchannel.None) + if(supportedSubchannel != MmcSubchannel.None) { dev.ReadCd(out readBuffer, out senseBuf, 0, blockSize, 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, supportedSubchannel, dev.Timeout, - out _); + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, supportedSubchannel, dev.Timeout, + out _); - var tmpBuf = new byte[subSize]; + byte[] tmpBuf = new byte[subSize]; Array.Copy(readBuffer, SECTOR_SIZE, tmpBuf, 0, subSize); ret = outputPlugin.WriteSectorTag(tmpBuf, 0, SectorTagType.CdSectorSubchannel); - if (!ret) + if(!ret) { - if (force) + if(force) { dumpLog.WriteLine("Error writing subchannel to output image, {0}continuing...", - force ? "" : "not "); + force ? "" : "not "); + dumpLog.WriteLine(outputPlugin.ErrorMessage); + ErrorMessage?.Invoke("Error writing subchannel to output image, continuing..." + - Environment.NewLine + + Environment.NewLine + outputPlugin.ErrorMessage); } else { StoppingErrorMessage?.Invoke("Error writing subchannel to output image, not continuing..." + - Environment.NewLine + + Environment.NewLine + outputPlugin.ErrorMessage); + return; } supportedSubchannel = MmcSubchannel.None; - subSize = 0; - blockSize = SECTOR_SIZE + subSize; - for (var t = 0; t < tracks.Length; t++) tracks[t].TrackSubchannelType = TrackSubchannelType.None; + subSize = 0; + blockSize = SECTOR_SIZE + subSize; + + for(int t = 0; t < tracks.Length; t++) + tracks[t].TrackSubchannelType = TrackSubchannelType.None; + ret = (outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList()); - if (!ret) + + if(!ret) { dumpLog.WriteLine("Error sending tracks to output image, not continuing."); dumpLog.WriteLine(outputPlugin.ErrorMessage); + StoppingErrorMessage?.Invoke("Error sending tracks to output image, not continuing..." + - Environment.NewLine + + Environment.NewLine + outputPlugin.ErrorMessage); + return; } } } // Set track flags - foreach (var kvp in trackFlags) + foreach(KeyValuePair kvp in trackFlags) { - var track = tracks.FirstOrDefault(t => t.TrackSequence == kvp.Key); + Track track = tracks.FirstOrDefault(t => t.TrackSequence == kvp.Key); - if (track.TrackSequence == 0) continue; + if(track.TrackSequence == 0) + continue; dumpLog.WriteLine("Setting flags for track {0}...", track.TrackSequence); UpdateStatus?.Invoke($"Setting flags for track {track.TrackSequence}..."); - outputPlugin.WriteSectorTag(new[] {kvp.Value}, track.TrackStartSector, SectorTagType.CdTrackFlags); + + outputPlugin.WriteSectorTag(new[] + { + kvp.Value + }, track.TrackStartSector, SectorTagType.CdTrackFlags); } // Set MCN - sense = dev.ReadMcn(out var mcn, out _, out _, dev.Timeout, out _); - if (!sense && mcn != null && mcn != "0000000000000") - if (outputPlugin.WriteMediaTag(Encoding.ASCII.GetBytes(mcn), MediaTagType.CD_MCN)) + sense = dev.ReadMcn(out string mcn, out _, out _, dev.Timeout, out _); + + if(!sense && + mcn != null && + mcn != "0000000000000") + if(outputPlugin.WriteMediaTag(Encoding.ASCII.GetBytes(mcn), MediaTagType.CD_MCN)) { UpdateStatus?.Invoke($"Setting disc Media Catalogue Number to {mcn}"); dumpLog.WriteLine("Setting disc Media Catalogue Number to {0}", mcn); } // Set ISRCs - foreach (var trk in tracks) + foreach(Track trk in tracks) { - sense = dev.ReadIsrc((byte) trk.TrackSequence, out var isrc, out _, out _, dev.Timeout, out _); - if (sense || isrc == null || isrc == "000000000000") continue; + sense = dev.ReadIsrc((byte)trk.TrackSequence, out string isrc, out _, out _, dev.Timeout, out _); - if (!outputPlugin.WriteSectorTag(Encoding.ASCII.GetBytes(isrc), trk.TrackStartSector, - SectorTagType.CdTrackIsrc)) continue; + if(sense || + isrc == null || + isrc == "000000000000") + continue; + + if(!outputPlugin.WriteSectorTag(Encoding.ASCII.GetBytes(isrc), trk.TrackStartSector, + SectorTagType.CdTrackIsrc)) + continue; UpdateStatus?.Invoke($"Setting ISRC for track {trk.TrackSequence} to {isrc}"); dumpLog.WriteLine("Setting ISRC for track {0} to {1}", trk.TrackSequence, isrc); } - if (resume.NextBlock > 0) + if(resume.NextBlock > 0) { UpdateStatus?.Invoke($"Resuming from block {resume.NextBlock}."); dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock); @@ -1101,107 +1238,118 @@ namespace DiscImageChef.Core.Devices.Dumping double imageWriteDuration = 0; - if (skip < blocksToRead) skip = blocksToRead; - var newTrim = false; + if(skip < blocksToRead) + skip = blocksToRead; -#if DEBUG - foreach (var trk in tracks) - UpdateStatus - ?.Invoke( - $"Track {trk.TrackSequence} starts at LBA {trk.TrackStartSector} and ends at LBA {trk.TrackEndSector}"); -#endif + bool newTrim = false; - if (dskType == MediaType.CDIREADY) + #if DEBUG + foreach(Track trk in tracks) + UpdateStatus?. + Invoke($"Track {trk.TrackSequence} starts at LBA {trk.TrackStartSector} and ends at LBA {trk.TrackEndSector}"); + #endif + + if(dskType == MediaType.CDIREADY) { - dumpLog.WriteLine( - "There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them."); - UpdateStatus - ?.Invoke( - "There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them."); + dumpLog.WriteLine("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them."); + + UpdateStatus?. + Invoke("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them."); } // Start reading - start = DateTime.UtcNow; - currentSpeed = 0; + start = DateTime.UtcNow; + currentSpeed = 0; sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; + timeSpeedStart = DateTime.UtcNow; InitProgress?.Invoke(); - for (var t = 0; t < tracks.Length; t++) + + for(int t = 0; t < tracks.Length; t++) { dumpLog.WriteLine("Reading track {0}", tracks[t].TrackSequence); - if (resume.NextBlock < tracks[t].TrackStartSector) resume.NextBlock = tracks[t].TrackStartSector; - for (var i = resume.NextBlock; i <= tracks[t].TrackEndSector; i += blocksToRead) + if(resume.NextBlock < tracks[t].TrackStartSector) + resume.NextBlock = tracks[t].TrackStartSector; + + for(ulong i = resume.NextBlock; i <= tracks[t].TrackEndSector; i += blocksToRead) { - if (aborted) + if(aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } double cmdDuration = 0; - if (tracks[t].TrackEndSector + 1 - i < blocksToRead) - blocksToRead = (uint) (tracks[t].TrackEndSector + 1 - i); + if(tracks[t].TrackEndSector + 1 - i < blocksToRead) + blocksToRead = (uint)(tracks[t].TrackEndSector + 1 - 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 + #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator + if(currentSpeed > maxSpeed && + currentSpeed != 0) + maxSpeed = currentSpeed; - UpdateProgress - ?.Invoke( - string.Format("Reading sector {0} of {1} at track {3} ({2:F3} MiB/sec.)", i, blocks, - currentSpeed, tracks[t].TrackSequence), - (long) i, (long) blocks); + if(currentSpeed < minSpeed && + currentSpeed != 0) + minSpeed = currentSpeed; + #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator - if (readcd) + UpdateProgress?. + Invoke(string.Format("Reading sector {0} of {1} at track {3} ({2:F3} MiB/sec.)", i, blocks, currentSpeed, tracks[t].TrackSequence), + (long)i, (long)blocks); + + if(readcd) { - sense = dev.ReadCd(out readBuffer, out senseBuf, (uint) i, blockSize, blocksToRead, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, supportedSubchannel, dev.Timeout, out cmdDuration); + sense = dev.ReadCd(out readBuffer, out senseBuf, (uint)i, blockSize, blocksToRead, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, supportedSubchannel, dev.Timeout, out cmdDuration); + totalDuration += cmdDuration; } - else if (read16) + else if(read16) { sense = dev.Read16(out readBuffer, out senseBuf, 0, false, true, false, i, blockSize, 0, - blocksToRead, false, dev.Timeout, out cmdDuration); + blocksToRead, false, dev.Timeout, out cmdDuration); } - else if (read12) + else if(read12) { - sense = dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint) i, - blockSize, 0, blocksToRead, false, dev.Timeout, out cmdDuration); + sense = dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)i, + blockSize, 0, blocksToRead, false, dev.Timeout, out cmdDuration); } - else if (read10) + else if(read10) { - sense = dev.Read10(out readBuffer, out senseBuf, 0, false, true, false, false, (uint) i, - blockSize, 0, (ushort) blocksToRead, dev.Timeout, out cmdDuration); + sense = dev.Read10(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)i, + blockSize, 0, (ushort)blocksToRead, dev.Timeout, out cmdDuration); } - else if (read6) + else if(read6) { - sense = dev.Read6(out readBuffer, out senseBuf, (uint) i, blockSize, (byte) blocksToRead, - dev.Timeout, out cmdDuration); + sense = dev.Read6(out readBuffer, out senseBuf, (uint)i, blockSize, (byte)blocksToRead, + dev.Timeout, out cmdDuration); } - if (!sense && !dev.Error) + if(!sense && + !dev.Error) { mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); extents.Add(i, blocksToRead, true); - var writeStart = DateTime.Now; - if (supportedSubchannel != MmcSubchannel.None) - { - var data = new byte[SECTOR_SIZE * blocksToRead]; - var sub = new byte[subSize * blocksToRead]; + DateTime writeStart = DateTime.Now; - for (var b = 0; b < blocksToRead; b++) + if(supportedSubchannel != MmcSubchannel.None) + { + byte[] data = new byte[SECTOR_SIZE * blocksToRead]; + byte[] sub = new byte[subSize * blocksToRead]; + + for(int b = 0; b < blocksToRead; b++) { - Array.Copy(readBuffer, (int) (0 + b * blockSize), data, SECTOR_SIZE * b, - SECTOR_SIZE); - Array.Copy(readBuffer, (int) (SECTOR_SIZE + b * blockSize), sub, subSize * b, - subSize); + Array.Copy(readBuffer, (int)(0 + b * blockSize), data, SECTOR_SIZE * b, + SECTOR_SIZE); + + Array.Copy(readBuffer, (int)(SECTOR_SIZE + b * blockSize), sub, subSize * b, + subSize); } outputPlugin.WriteSectorsLong(data, i, blocksToRead); @@ -1209,18 +1357,18 @@ namespace DiscImageChef.Core.Devices.Dumping } else { - if (supportsLongSectors) + if(supportsLongSectors) { outputPlugin.WriteSectorsLong(readBuffer, i, blocksToRead); } else { - if (readBuffer.Length % 2352 == 0) + if(readBuffer.Length % 2352 == 0) { - var data = new byte[2048 * blocksToRead]; + byte[] data = new byte[2048 * blocksToRead]; - for (var b = 0; b < blocksToRead; b++) - Array.Copy(readBuffer, (int) (16 + b * blockSize), data, 2048 * b, 2048); + for(int b = 0; b < blocksToRead; b++) + Array.Copy(readBuffer, (int)(16 + b * blockSize), data, 2048 * b, 2048); outputPlugin.WriteSectors(data, i, blocksToRead); } @@ -1236,55 +1384,63 @@ namespace DiscImageChef.Core.Devices.Dumping else { // TODO: Reset device after X errors - if (stopOnError) return; // TODO: Return more cleanly + if(stopOnError) + return; // TODO: Return more cleanly - if (i + skip > blocks) skip = (uint) (blocks - i); + if(i + skip > blocks) + skip = (uint)(blocks - i); // Write empty data - var writeStart = DateTime.Now; - if (supportedSubchannel != MmcSubchannel.None) + DateTime writeStart = DateTime.Now; + + if(supportedSubchannel != MmcSubchannel.None) { outputPlugin.WriteSectorsLong(new byte[SECTOR_SIZE * skip], i, skip); + outputPlugin.WriteSectorsTag(new byte[subSize * skip], i, skip, - SectorTagType.CdSectorSubchannel); + SectorTagType.CdSectorSubchannel); } else { - if (supportsLongSectors) + if(supportsLongSectors) { outputPlugin.WriteSectorsLong(new byte[blockSize * skip], i, skip); } else { - if (readBuffer.Length % 2352 == 0) + if(readBuffer.Length % 2352 == 0) outputPlugin.WriteSectors(new byte[2048 * skip], i, skip); - else outputPlugin.WriteSectorsLong(new byte[blockSize * skip], i, skip); + else + outputPlugin.WriteSectorsLong(new byte[blockSize * skip], i, skip); } } imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - for (var b = i; b < i + skip; b++) resume.BadBlocks.Add(b); + for(ulong b = i; b < i + skip; b++) + resume.BadBlocks.Add(b); DicConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(i, 0); dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", skip, i); - i += skip - blocksToRead; - newTrim = true; + i += skip - blocksToRead; + newTrim = true; } sectorSpeedStart += blocksToRead; resume.NextBlock = i + blocksToRead; - var elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - if (elapsed < 1) continue; + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + if(elapsed < 1) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; + timeSpeedStart = DateTime.UtcNow; } } @@ -1399,39 +1555,48 @@ namespace DiscImageChef.Core.Devices.Dumping end = DateTime.UtcNow; mhddLog.Close(); + ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double) (blocks + 1) / 1024 / (totalDuration / 1000), - devicePath); + 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."); + + 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)); + (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); + dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", - (double) blockSize * (double) (blocks + 1) / 1024 / imageWriteDuration); + (double)blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); #region Compact Disc Error trimming - - if (resume.BadBlocks.Count > 0 && !aborted && !notrim && newTrim) + if(resume.BadBlocks.Count > 0 && + !aborted && + !notrim && + newTrim) { start = DateTime.UtcNow; UpdateStatus?.Invoke("Trimming bad sectors"); dumpLog.WriteLine("Trimming bad sectors"); - var tmpArray = resume.BadBlocks.ToArray(); + ulong[] tmpArray = resume.BadBlocks.ToArray(); InitProgress?.Invoke(); - foreach (var badSector in tmpArray) + + foreach(ulong badSector in tmpArray) { - if (aborted) + if(aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } @@ -1439,38 +1604,39 @@ namespace DiscImageChef.Core.Devices.Dumping double cmdDuration = 0; - if (readcd) - sense = dev.ReadCd(out readBuffer, out senseBuf, (uint) badSector, blockSize, 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, supportedSubchannel, dev.Timeout, out cmdDuration); - else if (read16) + if(readcd) + sense = dev.ReadCd(out readBuffer, out senseBuf, (uint)badSector, blockSize, 1, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, supportedSubchannel, dev.Timeout, out cmdDuration); + else if(read16) sense = dev.Read16(out readBuffer, out senseBuf, 0, false, true, false, badSector, blockSize, 0, - blocksToRead, false, dev.Timeout, out cmdDuration); - else if (read12) - sense = dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint) badSector, - blockSize, 0, blocksToRead, false, dev.Timeout, out cmdDuration); - else if (read10) - sense = dev.Read10(out readBuffer, out senseBuf, 0, false, true, false, false, (uint) badSector, - blockSize, 0, (ushort) blocksToRead, dev.Timeout, out cmdDuration); - else if (read6) - sense = dev.Read6(out readBuffer, out senseBuf, (uint) badSector, blockSize, - (byte) blocksToRead, - dev.Timeout, out cmdDuration); + blocksToRead, false, dev.Timeout, out cmdDuration); + else if(read12) + sense = dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)badSector, + blockSize, 0, blocksToRead, false, dev.Timeout, out cmdDuration); + else if(read10) + sense = dev.Read10(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)badSector, + blockSize, 0, (ushort)blocksToRead, dev.Timeout, out cmdDuration); + else if(read6) + sense = dev.Read6(out readBuffer, out senseBuf, (uint)badSector, blockSize, (byte)blocksToRead, + dev.Timeout, out cmdDuration); totalDuration += cmdDuration; - if (sense || dev.Error) continue; + if(sense || dev.Error) + continue; - if (!sense && !dev.Error) + if(!sense && + !dev.Error) { resume.BadBlocks.Remove(badSector); extents.Add(badSector); } - if (supportedSubchannel != MmcSubchannel.None) + if(supportedSubchannel != MmcSubchannel.None) { - var data = new byte[SECTOR_SIZE]; - var sub = new byte[subSize]; + byte[] data = new byte[SECTOR_SIZE]; + byte[] sub = new byte[subSize]; Array.Copy(readBuffer, 0, data, 0, SECTOR_SIZE); Array.Copy(readBuffer, SECTOR_SIZE, sub, 0, subSize); outputPlugin.WriteSectorLong(data, badSector); @@ -1478,17 +1644,18 @@ namespace DiscImageChef.Core.Devices.Dumping } else { - if (supportsLongSectors) + if(supportsLongSectors) { outputPlugin.WriteSectorLong(readBuffer, badSector); } else { - if (readBuffer.Length % 2352 == 0) + if(readBuffer.Length % 2352 == 0) { - var data = new byte[2048]; + byte[] data = new byte[2048]; - for (var b = 0; b < blocksToRead; b++) Array.Copy(readBuffer, 16, data, 0, 2048); + for(int b = 0; b < blocksToRead; b++) + Array.Copy(readBuffer, 16, data, 0, 2048); outputPlugin.WriteSector(data, badSector); } @@ -1505,93 +1672,107 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke($"Trimmming finished in {(end - start).TotalSeconds} seconds."); dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds); } - #endregion Compact Disc Error trimming #region Compact Disc Error handling - - if (resume.BadBlocks.Count > 0 && !aborted && retryPasses > 0) + if(resume.BadBlocks.Count > 0 && + !aborted && + retryPasses > 0) { - var pass = 1; - var forward = true; - var runningPersistent = false; + int pass = 1; + bool forward = true; + bool runningPersistent = false; Modes.ModePage? currentModePage = null; - byte[] md6; - byte[] md10; + byte[] md6; + byte[] md10; - if (persistent) + if(persistent) { Modes.ModePage_01_MMC pgMmc; sense = dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, - dev.Timeout, out _); - if (sense) + dev.Timeout, out _); + + if(sense) { sense = dev.ModeSense10(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, - dev.Timeout, out _); + dev.Timeout, out _); - if (!sense) + if(!sense) { - var dcMode10 = + Modes.DecodedMode? dcMode10 = Modes.DecodeMode10(readBuffer, PeripheralDeviceTypes.MultiMediaDevice); - if (dcMode10.HasValue && dcMode10.Value.Pages != null) - foreach (var modePage in dcMode10.Value.Pages) - if (modePage.Page == 0x01 && modePage.Subpage == 0x00) + if(dcMode10.HasValue && + dcMode10.Value.Pages != null) + foreach(Modes.ModePage modePage in dcMode10.Value.Pages) + if(modePage.Page == 0x01 && + modePage.Subpage == 0x00) currentModePage = modePage; } } else { - var dcMode6 = + Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(readBuffer, PeripheralDeviceTypes.MultiMediaDevice); - if (dcMode6.HasValue && dcMode6.Value.Pages != null) - foreach (var modePage in dcMode6.Value.Pages) - if (modePage.Page == 0x01 && modePage.Subpage == 0x00) + 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) + if(currentModePage == null) { - pgMmc = new Modes.ModePage_01_MMC {PS = false, ReadRetryCount = 32, Parameter = 0x00}; + 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}; + pgMmc = new Modes.ModePage_01_MMC + { + PS = false, ReadRetryCount = 255, Parameter = 0x20 + }; + var md = new Modes.DecodedMode { - Header = new Modes.ModeHeader(), - Pages = new[] + Header = new Modes.ModeHeader(), Pages = new[] { new Modes.ModePage { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) + Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) } } }; - md6 = Modes.EncodeMode6(md, dev.ScsiType); + + 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) + 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."); + 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."); + + dumpLog. + WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); } else { @@ -1601,43 +1782,49 @@ namespace DiscImageChef.Core.Devices.Dumping InitProgress?.Invoke(); cdRepeatRetry: - var tmpArray = resume.BadBlocks.ToArray(); - var sectorsNotEvenPartial = new List(); - foreach (var badSector in tmpArray) + ulong[] tmpArray = resume.BadBlocks.ToArray(); + List sectorsNotEvenPartial = new List(); + + foreach(ulong badSector in tmpArray) { - if (aborted) + 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, " : "")); + forward ? "forward" : "reverse", + runningPersistent ? "recovering partial data, " : "")); - if (readcd) + if(readcd) { - sense = dev.ReadCd(out readBuffer, out senseBuf, (uint) badSector, blockSize, 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, supportedSubchannel, dev.Timeout, - out var cmdDuration); + sense = dev.ReadCd(out readBuffer, out senseBuf, (uint)badSector, blockSize, 1, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, supportedSubchannel, dev.Timeout, + out double cmdDuration); + totalDuration += cmdDuration; } - if (sense || dev.Error) + if(sense || dev.Error) { - if (!runningPersistent) continue; + if(!runningPersistent) + continue; - var decSense = Sense.DecodeFixed(senseBuf); + FixedSense? decSense = Sense.DecodeFixed(senseBuf); // MEDIUM ERROR, retry with ignore error below - if (decSense.HasValue && decSense.Value.ASC == 0x11) - if (!sectorsNotEvenPartial.Contains(badSector)) + if(decSense.HasValue && + decSense.Value.ASC == 0x11) + if(!sectorsNotEvenPartial.Contains(badSector)) sectorsNotEvenPartial.Add(badSector); } - if (!sense && !dev.Error) + if(!sense && + !dev.Error) { resume.BadBlocks.Remove(badSector); extents.Add(badSector); @@ -1646,10 +1833,10 @@ namespace DiscImageChef.Core.Devices.Dumping sectorsNotEvenPartial.Remove(badSector); } - if (supportedSubchannel != MmcSubchannel.None) + if(supportedSubchannel != MmcSubchannel.None) { - var data = new byte[SECTOR_SIZE]; - var sub = new byte[subSize]; + byte[] data = new byte[SECTOR_SIZE]; + byte[] sub = new byte[subSize]; Array.Copy(readBuffer, 0, data, 0, SECTOR_SIZE); Array.Copy(readBuffer, SECTOR_SIZE, sub, 0, subSize); outputPlugin.WriteSectorLong(data, badSector); @@ -1661,75 +1848,85 @@ namespace DiscImageChef.Core.Devices.Dumping } } - if (pass < retryPasses && !aborted && resume.BadBlocks.Count > 0) + if(pass < retryPasses && + !aborted && + resume.BadBlocks.Count > 0) { pass++; forward = !forward; resume.BadBlocks.Sort(); resume.BadBlocks.Reverse(); + goto cdRepeatRetry; } EndProgress?.Invoke(); // Try to ignore read errors, on some drives this allows to recover partial even if damaged data - if (persistent && sectorsNotEvenPartial.Count > 0) + if(persistent && sectorsNotEvenPartial.Count > 0) { - var pgMmc = - new Modes.ModePage_01_MMC {PS = false, ReadRetryCount = 255, Parameter = 0x01}; + var pgMmc = new Modes.ModePage_01_MMC + { + PS = false, ReadRetryCount = 255, Parameter = 0x01 + }; + var md = new Modes.DecodedMode { - Header = new Modes.ModeHeader(), - Pages = new[] + Header = new Modes.ModeHeader(), Pages = new[] { new Modes.ModePage { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) + Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) } } }; - md6 = Modes.EncodeMode6(md, dev.ScsiType); + + 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) + if(sense) + sense = dev.ModeSelect10(md10, out senseBuf, true, false, dev.Timeout, out _); + + if(!sense) { runningPersistent = true; InitProgress?.Invoke(); - foreach (var badSector in sectorsNotEvenPartial) + + foreach(ulong badSector in sectorsNotEvenPartial) { - if (aborted) + if(aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); + break; } PulseProgress?.Invoke($"Trying to get partial data for sector {badSector}"); - if (readcd) + if(readcd) { - sense = dev.ReadCd(out readBuffer, out senseBuf, (uint) badSector, blockSize, 1, - MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - supportedSubchannel, dev.Timeout, out var cmdDuration); + sense = dev.ReadCd(out readBuffer, out senseBuf, (uint)badSector, blockSize, 1, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + supportedSubchannel, dev.Timeout, out double cmdDuration); + totalDuration += cmdDuration; } - if (sense || dev.Error) continue; + if(sense || dev.Error) + continue; dumpLog.WriteLine("Got partial data for sector {0} in pass {1}.", badSector, pass); - if (supportedSubchannel != MmcSubchannel.None) + if(supportedSubchannel != MmcSubchannel.None) { - var data = new byte[SECTOR_SIZE]; - var sub = new byte[subSize]; + byte[] data = new byte[SECTOR_SIZE]; + byte[] sub = new byte[subSize]; Array.Copy(readBuffer, 0, data, 0, SECTOR_SIZE); Array.Copy(readBuffer, SECTOR_SIZE, sub, 0, subSize); outputPlugin.WriteSectorLong(data, badSector); @@ -1745,7 +1942,7 @@ namespace DiscImageChef.Core.Devices.Dumping } } - if (runningPersistent && currentModePage.HasValue) + if(runningPersistent && currentModePage.HasValue) { // TODO: Enable when underlying images support lead-outs /* @@ -1853,118 +2050,139 @@ namespace DiscImageChef.Core.Devices.Dumping var md = new Modes.DecodedMode { - Header = new Modes.ModeHeader(), Pages = new[] {currentModePage.Value} + Header = new Modes.ModeHeader(), Pages = new[] + { + currentModePage.Value + } }; - md6 = Modes.EncodeMode6(md, dev.ScsiType); + + 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 _); + + if(sense) + dev.ModeSelect10(md10, out senseBuf, true, false, dev.Timeout, out _); } EndProgress?.Invoke(); } - #endregion Compact Disc Error handling // Write media tags to image - if (!aborted) - foreach (var tag in mediaTags) + if(!aborted) + foreach(KeyValuePair tag in mediaTags) { - if (tag.Value is null) + if(tag.Value is null) { DicConsole.ErrorWriteLine("Error: Tag type {0} is null, skipping...", tag.Key); + continue; } ret = outputPlugin.WriteMediaTag(tag.Value, tag.Key); - if (ret || force) continue; + if(ret || force) + continue; // Cannot write tag to image dumpLog.WriteLine($"Cannot write tag {tag.Key}."); StoppingErrorMessage?.Invoke(outputPlugin.ErrorMessage); + return; } resume.BadBlocks.Sort(); - foreach (var bad in resume.BadBlocks) dumpLog.WriteLine("Sector {0} could not be read.", bad); + + 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); + + if(preSidecar != null) + outputPlugin.SetCicmMetadata(preSidecar); + dumpLog.WriteLine("Closing output file."); UpdateStatus?.Invoke("Closing output file."); - var closeStart = DateTime.Now; + DateTime closeStart = DateTime.Now; outputPlugin.Close(); - var closeEnd = DateTime.Now; + DateTime closeEnd = DateTime.Now; UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); - if (aborted) + if(aborted) { dumpLog.WriteLine("Aborted!"); + return; } double totalChkDuration = 0; - if (!nometadata) + + if(!nometadata) { dumpLog.WriteLine("Creating sidecar."); - var filters = new FiltersList(); - var filter = filters.GetFilter(outputPath); - var inputPlugin = ImageFormat.Detect(filter); - if (!inputPlugin.Open(filter)) + 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; } - var chkStart = DateTime.UtcNow; - sidecarClass = new Sidecar(inputPlugin, outputPath, filter.Id, encoding); - sidecarClass.InitProgressEvent += InitProgress; - sidecarClass.UpdateProgressEvent += UpdateProgress; - sidecarClass.EndProgressEvent += EndProgress; - sidecarClass.InitProgressEvent2 += InitProgress2; + 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; - var sidecar = sidecarClass.Create(); + sidecarClass.EndProgressEvent2 += EndProgress2; + sidecarClass.UpdateStatusEvent += UpdateStatus; + CICMMetadataType sidecar = sidecarClass.Create(); end = DateTime.UtcNow; totalChkDuration = (end - chkStart).TotalMilliseconds; 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) + dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", + (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); + + if(preSidecar != null) { preSidecar.OpticalDisc = sidecar.OpticalDisc; - sidecar = preSidecar; + sidecar = preSidecar; } - var 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 (partition.StartSector, fileSystem.Type)); + List<(ulong start, string type)> filesystems = new List<(ulong start, string type)>(); - if (filesystems.Count > 0) - foreach (var filesystem in filesystems.Select(o => new {o.start, o.type}).Distinct()) + 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 (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].Dimensions = Dimensions.DimensionsFromMediaType(dskType); - CommonTypes.Metadata.MediaType.MediaTypeToString(dskType, out var xmlDskTyp, - out var xmlDskSubTyp); - sidecar.OpticalDisc[0].DiscType = xmlDskTyp; - sidecar.OpticalDisc[0].DiscSubType = xmlDskSubTyp; + (string type, string subType) discType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); + sidecar.OpticalDisc[0].DiscType = discType.type; + sidecar.OpticalDisc[0].DiscSubType = discType.subType; sidecar.OpticalDisc[0].DumpHardwareArray = resume.Tries.ToArray(); - foreach (var tag in mediaTags) - if (outputPlugin.SupportedMediaTags.Contains(tag.Key)) + foreach(KeyValuePair tag in mediaTags) + if(outputPlugin.SupportedMediaTags.Contains(tag.Key)) AddMediaTagToSidecar(outputPath, tag, ref sidecar); UpdateStatus?.Invoke("Writing metadata sidecar"); @@ -1977,12 +2195,13 @@ namespace DiscImageChef.Core.Devices.Dumping } 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."); + + 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."); + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); UpdateStatus?.Invoke($"{resume.BadBlocks.Count} sectors could not be read."); diff --git a/DiscImageChef.Core/Devices/Dumping/PlayStationPortable.cs b/DiscImageChef.Core/Devices/Dumping/PlayStationPortable.cs index 4948c7b2a..50d1a948f 100644 --- a/DiscImageChef.Core/Devices/Dumping/PlayStationPortable.cs +++ b/DiscImageChef.Core/Devices/Dumping/PlayStationPortable.cs @@ -23,12 +23,16 @@ namespace DiscImageChef.Core.Devices.Dumping { public partial class Dump { - static readonly byte[] FatSignature = {0x46, 0x41, 0x54, 0x31, 0x36, 0x20, 0x20, 0x20}; - static readonly byte[] IsoExtension = {0x49, 0x53, 0x4F}; + static readonly byte[] FatSignature = + { + 0x46, 0x41, 0x54, 0x31, 0x36, 0x20, 0x20, 0x20 + }; + static readonly byte[] IsoExtension = + { + 0x49, 0x53, 0x4F + }; - /// - /// Dumps a CFW PlayStation Portable UMD - /// + /// Dumps a CFW PlayStation Portable UMD void PlayStationPortable() { if(!outputPlugin.SupportedMediaTypes.Contains(MediaType.MemoryStickDuo) && @@ -36,8 +40,10 @@ namespace DiscImageChef.Core.Devices.Dumping !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..."); + + StoppingErrorMessage?. + Invoke("Selected output plugin does not support MemoryStick Duo or UMD, cannot dump..."); + return; } @@ -51,6 +57,7 @@ namespace DiscImageChef.Core.Devices.Dumping { dumpLog.WriteLine("Could not get MODE SENSE..."); StoppingErrorMessage?.Invoke("Could not get MODE SENSE..."); + return; } @@ -60,6 +67,7 @@ namespace DiscImageChef.Core.Devices.Dumping { dumpLog.WriteLine("Could not decode MODE SENSE..."); StoppingErrorMessage?.Invoke("Could not decode MODE SENSE..."); + return; } @@ -67,6 +75,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(!decoded.Value.Header.WriteProtected) { DumpMs(); + return; } @@ -77,6 +86,7 @@ namespace DiscImageChef.Core.Devices.Dumping { dumpLog.WriteLine("Could not read..."); StoppingErrorMessage?.Invoke("Could not read..."); + return; } @@ -88,6 +98,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(!tmp.SequenceEqual(FatSignature)) { DumpMs(); + return; } @@ -105,6 +116,7 @@ namespace DiscImageChef.Core.Devices.Dumping { StoppingErrorMessage?.Invoke("Could not read..."); dumpLog.WriteLine("Could not read..."); + return; } @@ -114,6 +126,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(!tmp.SequenceEqual(IsoExtension)) { DumpMs(); + return; } @@ -129,8 +142,11 @@ namespace DiscImageChef.Core.Devices.Dumping while(position < sectorsPerFat) { - uint transfer = 64; - if(transfer + position > sectorsPerFat) transfer = sectorsPerFat - position; + 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 _); @@ -138,6 +154,7 @@ namespace DiscImageChef.Core.Devices.Dumping { StoppingErrorMessage?.Invoke("Could not read..."); dumpLog.WriteLine("Could not read..."); + return; } @@ -158,17 +175,22 @@ namespace DiscImageChef.Core.Devices.Dumping if(nextCluster == previousCluster + 1) { previousCluster = nextCluster; + continue; } - if(nextCluster == 0xFFFF) break; + if(nextCluster == 0xFFFF) + break; DumpMs(); + return; } - if(outputPlugin is IWritableOpticalImage) DumpUmd(); - else StoppingErrorMessage?.Invoke("The specified plugin does not support storing optical disc images."); + if(outputPlugin is IWritableOpticalImage) + DumpUmd(); + else + StoppingErrorMessage?.Invoke("The specified plugin does not support storing optical disc images."); } void DumpUmd() @@ -190,6 +212,7 @@ namespace DiscImageChef.Core.Devices.Dumping { dumpLog.WriteLine("Could not read..."); StoppingErrorMessage?.Invoke("Could not read..."); + return; } @@ -209,6 +232,7 @@ namespace DiscImageChef.Core.Devices.Dumping { dumpLog.WriteLine("Could not read..."); StoppingErrorMessage?.Invoke("Could not read..."); + return; } @@ -216,8 +240,8 @@ namespace DiscImageChef.Core.Devices.Dumping 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($"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."); @@ -226,18 +250,18 @@ namespace DiscImageChef.Core.Devices.Dumping 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} 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); + 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; - MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, BLOCK_SIZE, blocksToRead); - IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", 0x0010); + var mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, BLOCK_SIZE, blocksToRead); + var ibgLog = new IbgLog(outputPrefix + ".ibg", 0x0010); ret = outputPlugin.Create(outputPath, DSK_TYPE, formatOptions, blocks, BLOCK_SIZE); // Cannot create image @@ -245,8 +269,10 @@ namespace DiscImageChef.Core.Devices.Dumping { 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; } @@ -257,33 +283,35 @@ namespace DiscImageChef.Core.Devices.Dumping { new Track { - TrackBytesPerSector = (int)BLOCK_SIZE, - TrackEndSector = blocks - 1, - TrackSequence = 1, - TrackRawBytesPerSector = (int)BLOCK_SIZE, - TrackSubchannelType = - TrackSubchannelType.None, - TrackSession = 1, - TrackType = TrackType.Data + 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); - if(currentTry == null || extents == null) + + 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); + 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) @@ -291,14 +319,21 @@ namespace DiscImageChef.Core.Devices.Dumping currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } - if(blocks - i < blocksToRead) blocksToRead = (uint)(blocks - i); + 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; + 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, @@ -306,9 +341,11 @@ namespace DiscImageChef.Core.Devices.Dumping 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) + if(!sense && + !dev.Error) { mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); @@ -320,16 +357,19 @@ namespace DiscImageChef.Core.Devices.Dumping else { // TODO: Reset device after X errors - if(stopOnError) return; // TODO: Return more cleanly + if(stopOnError) + return; // TODO: Return more cleanly - if(i + skip > blocks) skip = (uint)(blocks - i); + 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); + for(ulong b = i; b < i + skip; b++) + resume.BadBlocks.Add(b); mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); @@ -343,7 +383,9 @@ namespace DiscImageChef.Core.Devices.Dumping resume.NextBlock = i + blocksToRead; double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - if(elapsed < 1) continue; + + if(elapsed < 1) + continue; currentSpeed = sectorSpeedStart * BLOCK_SIZE / (1048576 * elapsed); sectorSpeedStart = 0; @@ -353,34 +395,46 @@ namespace DiscImageChef.Core.Devices.Dumping 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."); + + 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 && !notrim && newTrim) + if(resume.BadBlocks.Count > 0 && + !aborted && + !notrim && + newTrim) { start = DateTime.UtcNow; dumpLog.WriteLine("Trimming bad sectors"); ulong[] tmpArray = resume.BadBlocks.ToArray(); InitProgress?.Invoke(); + foreach(ulong badSector in tmpArray) { if(aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); + break; } @@ -390,7 +444,8 @@ namespace DiscImageChef.Core.Devices.Dumping (uint)(umdStart + badSector * 4), 512, 0, 4, false, dev.Timeout, out double cmdDuration); - if(sense || dev.Error) continue; + if(sense || dev.Error) + continue; resume.BadBlocks.Remove(badSector); extents.Add(badSector); @@ -404,7 +459,9 @@ namespace DiscImageChef.Core.Devices.Dumping #endregion Trimming #region Error handling - if(resume.BadBlocks.Count > 0 && !aborted && retryPasses > 0) + if(resume.BadBlocks.Count > 0 && + !aborted && + retryPasses > 0) { int pass = 1; bool forward = true; @@ -419,13 +476,15 @@ namespace DiscImageChef.Core.Devices.Dumping 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) + if(modePage.Page == 0x01 && + modePage.Subpage == 0x00) currentModePage = modePage; } @@ -433,16 +492,9 @@ namespace DiscImageChef.Core.Devices.Dumping { 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 + PS = false, AWRE = true, ARRE = true, TB = false, + RC = false, EER = true, PER = false, DTE = true, + DCR = false, ReadRetryCount = 32 }; currentModePage = new Modes.ModePage @@ -453,21 +505,14 @@ namespace DiscImageChef.Core.Devices.Dumping 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 + PS = false, AWRE = false, ARRE = false, TB = true, + RC = false, EER = true, PER = false, DTE = false, + DCR = false, ReadRetryCount = 255 }; - Modes.DecodedMode md = new Modes.DecodedMode + + var md = new Modes.DecodedMode { - Header = new Modes.ModeHeader(), - Pages = new[] + Header = new Modes.ModeHeader(), Pages = new[] { new Modes.ModePage { @@ -475,6 +520,7 @@ namespace DiscImageChef.Core.Devices.Dumping } } }; + md6 = Modes.EncodeMode6(md, dev.ScsiType); dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); @@ -482,61 +528,79 @@ namespace DiscImageChef.Core.Devices.Dumping if(sense) { - UpdateStatus - ?.Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); + 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."); + + dumpLog. + WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); } - else runningPersistent = true; + 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")}"); + 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) + 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); + else if(runningPersistent) + outputPlugin.WriteSector(readBuffer, badSector); } - if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0) + 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 + var md = new Modes.DecodedMode { - Header = new Modes.ModeHeader(), Pages = new[] {currentModePage.Value} + 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)."); @@ -549,14 +613,16 @@ namespace DiscImageChef.Core.Devices.Dumping #endregion Error handling resume.BadBlocks.Sort(); - foreach(ulong bad in resume.BadBlocks) dumpLog.WriteLine("Sector {0} could not be read.", bad); + + foreach(ulong bad in resume.BadBlocks) + dumpLog.WriteLine("Sector {0} could not be read.", bad); + currentTry.Extents = ExtentsConverter.ToMetadata(extents); - CommonTypes.Structs.ImageInfo metadata = new CommonTypes.Structs.ImageInfo + var metadata = new CommonTypes.Structs.ImageInfo { - Application = "DiscImageChef", - ApplicationVersion = Version.GetVersion(), - MediaPartNumber = mediaPartNumber + Application = "DiscImageChef", ApplicationVersion = Version.GetVersion(), + MediaPartNumber = mediaPartNumber }; if(!outputPlugin.SetMetadata(metadata)) @@ -564,7 +630,10 @@ namespace DiscImageChef.Core.Devices.Dumping outputPlugin.ErrorMessage); outputPlugin.SetDumpHardware(resume.Tries); - if(preSidecar != null) outputPlugin.SetCicmMetadata(preSidecar); + + if(preSidecar != null) + outputPlugin.SetCicmMetadata(preSidecar); + dumpLog.WriteLine("Closing output file."); UpdateStatus?.Invoke("Closing output file."); DateTime closeStart = DateTime.Now; @@ -576,20 +645,24 @@ namespace DiscImageChef.Core.Devices.Dumping { UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + return; } double totalChkDuration = 0; + if(!nometadata) { UpdateStatus?.Invoke("Creating sidecar."); dumpLog.WriteLine("Creating sidecar."); - FiltersList filters = new FiltersList(); + 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; } @@ -607,9 +680,12 @@ namespace DiscImageChef.Core.Devices.Dumping 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."); + + 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)); @@ -620,16 +696,19 @@ namespace DiscImageChef.Core.Devices.Dumping } 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)); + 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()) + 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); @@ -637,26 +716,28 @@ namespace DiscImageChef.Core.Devices.Dumping // TODO: Implement layers sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(DSK_TYPE); - CommonTypes.Metadata.MediaType.MediaTypeToString(DSK_TYPE, out string xmlDskTyp, - out string xmlDskSubTyp); - sidecar.OpticalDisc[0].DiscType = xmlDskTyp; - sidecar.OpticalDisc[0].DiscSubType = xmlDskSubTyp; + (string type, string subType) discType = CommonTypes.Metadata.MediaType.MediaTypeToString(DSK_TYPE); + sidecar.OpticalDisc[0].DiscType = discType.type; + sidecar.OpticalDisc[0].DiscSubType = discType.subType; sidecar.OpticalDisc[0].DumpHardwareArray = resume.Tries.ToArray(); UpdateStatus?.Invoke("Writing metadata sidecar"); - FileStream xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create); + var xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create); - XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType)); + 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."); + + 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."); + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); UpdateStatus?.Invoke($"{resume.BadBlocks.Count} sectors could not be read."); @@ -685,19 +766,22 @@ namespace DiscImageChef.Core.Devices.Dumping { 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)"); + + 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; } @@ -721,8 +805,8 @@ namespace DiscImageChef.Core.Devices.Dumping bool ret; - MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, BLOCK_SIZE, blocksToRead); - IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", SBC_PROFILE); + var mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, BLOCK_SIZE, blocksToRead); + var ibgLog = new IbgLog(outputPrefix + ".ibg", SBC_PROFILE); ret = outputPlugin.Create(outputPath, dskType, formatOptions, blocks, BLOCK_SIZE); // Cannot create image @@ -730,8 +814,10 @@ namespace DiscImageChef.Core.Devices.Dumping { 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; } @@ -740,20 +826,27 @@ namespace DiscImageChef.Core.Devices.Dumping 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); - if(currentTry == null || extents == null) + + 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); + 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) @@ -761,23 +854,32 @@ namespace DiscImageChef.Core.Devices.Dumping currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } - if(blocks - i < blocksToRead) blocksToRead = (uint)(blocks - i); + 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; + 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) + if(!sense && + !dev.Error) { mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); @@ -789,16 +891,19 @@ namespace DiscImageChef.Core.Devices.Dumping else { // TODO: Reset device after X errors - if(stopOnError) return; // TODO: Return more cleanly + if(stopOnError) + return; // TODO: Return more cleanly - if(i + skip > blocks) skip = (uint)(blocks - i); + 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); + for(ulong b = i; b < i + skip; b++) + resume.BadBlocks.Add(b); mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); @@ -812,7 +917,9 @@ namespace DiscImageChef.Core.Devices.Dumping resume.NextBlock = i + blocksToRead; double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - if(elapsed < 1) continue; + + if(elapsed < 1) + continue; currentSpeed = sectorSpeedStart * BLOCK_SIZE / (1048576 * elapsed); sectorSpeedStart = 0; @@ -822,22 +929,32 @@ namespace DiscImageChef.Core.Devices.Dumping 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."); + + 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 && !notrim && newTrim) + if(resume.BadBlocks.Count > 0 && + !aborted && + !notrim && + newTrim) { start = DateTime.UtcNow; UpdateStatus?.Invoke("Trimming bad sectors"); @@ -845,6 +962,7 @@ namespace DiscImageChef.Core.Devices.Dumping ulong[] tmpArray = resume.BadBlocks.ToArray(); InitProgress?.Invoke(); + foreach(ulong badSector in tmpArray) { if(aborted) @@ -852,6 +970,7 @@ namespace DiscImageChef.Core.Devices.Dumping currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } @@ -860,7 +979,8 @@ namespace DiscImageChef.Core.Devices.Dumping 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; + if(sense || dev.Error) + continue; resume.BadBlocks.Remove(badSector); extents.Add(badSector); @@ -874,7 +994,9 @@ namespace DiscImageChef.Core.Devices.Dumping #endregion Trimming #region Error handling - if(resume.BadBlocks.Count > 0 && !aborted && retryPasses > 0) + if(resume.BadBlocks.Count > 0 && + !aborted && + retryPasses > 0) { int pass = 1; bool forward = true; @@ -889,6 +1011,7 @@ namespace DiscImageChef.Core.Devices.Dumping 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, @@ -900,7 +1023,8 @@ namespace DiscImageChef.Core.Devices.Dumping if(dcMode10.HasValue) foreach(Modes.ModePage modePage in dcMode10.Value.Pages) - if(modePage.Page == 0x01 && modePage.Subpage == 0x00) + if(modePage.Page == 0x01 && + modePage.Subpage == 0x00) currentModePage = modePage; } } @@ -910,7 +1034,8 @@ namespace DiscImageChef.Core.Devices.Dumping if(dcMode6.HasValue) foreach(Modes.ModePage modePage in dcMode6.Value.Pages) - if(modePage.Page == 0x01 && modePage.Subpage == 0x00) + if(modePage.Page == 0x01 && + modePage.Subpage == 0x00) currentModePage = modePage; } @@ -918,16 +1043,9 @@ namespace DiscImageChef.Core.Devices.Dumping { 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 + PS = false, AWRE = true, ARRE = true, TB = false, + RC = false, EER = true, PER = false, DTE = true, + DCR = false, ReadRetryCount = 32 }; currentModePage = new Modes.ModePage @@ -938,21 +1056,14 @@ namespace DiscImageChef.Core.Devices.Dumping 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 + PS = false, AWRE = false, ARRE = false, TB = true, + RC = false, EER = true, PER = false, DTE = false, + DCR = false, ReadRetryCount = 255 }; - Modes.DecodedMode md = new Modes.DecodedMode + + var md = new Modes.DecodedMode { - Header = new Modes.ModeHeader(), - Pages = new[] + Header = new Modes.ModeHeader(), Pages = new[] { new Modes.ModePage { @@ -960,6 +1071,7 @@ namespace DiscImageChef.Core.Devices.Dumping } } }; + md6 = Modes.EncodeMode6(md, dev.ScsiType); UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks)."); @@ -968,23 +1080,29 @@ namespace DiscImageChef.Core.Devices.Dumping if(sense) { - UpdateStatus - ?.Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); + 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."); + + dumpLog. + WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); } - else runningPersistent = true; + 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; } @@ -994,9 +1112,11 @@ namespace DiscImageChef.Core.Devices.Dumping 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) + if(!sense && + !dev.Error) { resume.BadBlocks.Remove(badSector); extents.Add(badSector); @@ -1004,24 +1124,32 @@ namespace DiscImageChef.Core.Devices.Dumping 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); + else if(runningPersistent) + outputPlugin.WriteSector(readBuffer, badSector); } - if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0) + 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 + var md = new Modes.DecodedMode { - Header = new Modes.ModeHeader(), Pages = new[] {currentModePage.Value} + 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)."); @@ -1034,11 +1162,17 @@ namespace DiscImageChef.Core.Devices.Dumping #endregion Error handling resume.BadBlocks.Sort(); - foreach(ulong bad in resume.BadBlocks) dumpLog.WriteLine("Sector {0} could not be read.", bad); + + 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); + + if(preSidecar != null) + outputPlugin.SetCicmMetadata(preSidecar); + dumpLog.WriteLine("Closing output file."); UpdateStatus?.Invoke("Closing output file."); DateTime closeStart = DateTime.Now; @@ -1051,20 +1185,24 @@ namespace DiscImageChef.Core.Devices.Dumping { UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + return; } double totalChkDuration = 0; + if(!nometadata) { UpdateStatus?.Invoke("Creating sidecar."); dumpLog.WriteLine("Creating sidecar."); - FiltersList filters = new FiltersList(); + 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; } @@ -1082,9 +1220,12 @@ namespace DiscImageChef.Core.Devices.Dumping 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."); + + 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)); @@ -1095,24 +1236,26 @@ namespace DiscImageChef.Core.Devices.Dumping } 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 ((ulong)partition.StartSector, fileSystem.Type)); + 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()) + 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); - CommonTypes.Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp, - out string xmlDskSubTyp); - sidecar.BlockMedia[0].DiskType = xmlDskTyp; - sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp; + (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; @@ -1122,22 +1265,26 @@ namespace DiscImageChef.Core.Devices.Dumping sidecar.BlockMedia[0].Serial = dev.Serial; sidecar.BlockMedia[0].Size = blocks * BLOCK_SIZE; - if(dev.IsRemovable) sidecar.BlockMedia[0].DumpHardwareArray = resume.Tries.ToArray(); + if(dev.IsRemovable) + sidecar.BlockMedia[0].DumpHardwareArray = resume.Tries.ToArray(); UpdateStatus?.Invoke("Writing metadata sidecar"); - FileStream xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create); + var xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create); - XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType)); + 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."); + + 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."); + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); UpdateStatus?.Invoke($"{resume.BadBlocks.Count} sectors could not be read."); diff --git a/DiscImageChef.Core/Devices/Dumping/SBC.cs b/DiscImageChef.Core/Devices/Dumping/SBC.cs index 443bc1d16..7bb68b4a8 100644 --- a/DiscImageChef.Core/Devices/Dumping/SBC.cs +++ b/DiscImageChef.Core/Devices/Dumping/SBC.cs @@ -51,103 +51,117 @@ using TrackType = DiscImageChef.CommonTypes.Enums.TrackType; namespace DiscImageChef.Core.Devices.Dumping { - /// - /// Implements dumping SCSI Block Commands and Reduced Block Commands devices - /// + /// Implements dumping SCSI Block Commands and Reduced Block Commands devices partial class Dump { - /// - /// Dumps a SCSI Block Commands device or a Reduced Block Commands devices - /// + /// Dumps a SCSI Block Commands device or a Reduced Block Commands devices /// If device contains an optical disc (e.g. DVD or BD) /// Media tags as retrieved in MMC layer /// Disc type as detected in SCSI or MMC layer internal void Sbc(Dictionary mediaTags, ref MediaType dskType, bool opticalDisc) { - bool sense; - byte scsiMediumType = 0; - byte scsiDensityCode = 0; - var containsFloppyPage = false; - const ushort SBC_PROFILE = 0x0001; - DateTime start; - DateTime end; - double totalDuration = 0; - double currentSpeed = 0; - var maxSpeed = double.MinValue; - var minSpeed = double.MaxValue; - byte[] readBuffer; + bool sense; + byte scsiMediumType = 0; + byte scsiDensityCode = 0; + bool containsFloppyPage = false; + const ushort SBC_PROFILE = 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; - if (opticalDisc) - switch (dskType) + if(opticalDisc) + switch(dskType) { case MediaType.REV35: case MediaType.REV70: case MediaType.REV120: opticalDisc = false; + break; } dumpLog.WriteLine("Initializing reader."); - var scsiReader = new Reader(dev, dev.Timeout, null, dumpRaw); - var blocks = scsiReader.GetDeviceBlocks(); - var blockSize = scsiReader.LogicalBlockSize; - if (scsiReader.FindReadCommand()) + var scsiReader = new Reader(dev, dev.Timeout, null, dumpRaw); + ulong blocks = scsiReader.GetDeviceBlocks(); + uint blockSize = scsiReader.LogicalBlockSize; + + 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) + if(blocks != 0 && + blockSize != 0) { blocks++; - UpdateStatus - ?.Invoke( - $"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {blocks * (ulong) blockSize} bytes)"); + + 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 - if (scsiReader.GetBlocksToRead()) + if(scsiReader.GetBlocksToRead()) { dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", scsiReader.ErrorMessage); StoppingErrorMessage?.Invoke(scsiReader.ErrorMessage); + return; } - var blocksToRead = scsiReader.BlocksToRead; - var logicalBlockSize = blockSize; - var physicalBlockSize = scsiReader.PhysicalBlockSize; + uint blocksToRead = scsiReader.BlocksToRead; + uint logicalBlockSize = blockSize; + uint physicalBlockSize = scsiReader.PhysicalBlockSize; - if (blocks == 0) + 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; } - if (!opticalDisc) + if(!opticalDisc) { mediaTags = new Dictionary(); - if (dev.IsUsb && dev.UsbDescriptors != null) mediaTags.Add(MediaTagType.USB_Descriptors, null); - if (dev.Type == DeviceType.ATAPI) mediaTags.Add(MediaTagType.ATAPI_IDENTIFY, null); - if (dev.IsPcmcia && dev.Cis != null) mediaTags.Add(MediaTagType.PCMCIA_CIS, null); + if(dev.IsUsb && + dev.UsbDescriptors != null) + mediaTags.Add(MediaTagType.USB_Descriptors, null); - sense = dev.ScsiInquiry(out var cmdBuf, out _); + if(dev.Type == DeviceType.ATAPI) + mediaTags.Add(MediaTagType.ATAPI_IDENTIFY, null); + + if(dev.IsPcmcia && + dev.Cis != null) + mediaTags.Add(MediaTagType.PCMCIA_CIS, null); + + sense = dev.ScsiInquiry(out byte[] cmdBuf, out _); mediaTags.Add(MediaTagType.SCSI_INQUIRY, cmdBuf); - if (!sense) + + if(!sense) { dumpLog.WriteLine("Requesting MODE SENSE (10)."); UpdateStatus?.Invoke("Requesting MODE SENSE (10)."); - sense = dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, - 0xFF, 5, out _); - if (!sense || dev.Error) - sense = dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, - 0x00, 5, out _); - if (!sense && !dev.Error) - if (Modes.DecodeMode10(cmdBuf, dev.ScsiType).HasValue) + sense = dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, + 0xFF, 5, out _); + + if(!sense || + dev.Error) + sense = dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, + 0x00, 5, out _); + + if(!sense && + !dev.Error) + if(Modes.DecodeMode10(cmdBuf, dev.ScsiType).HasValue) { mediaTags.Add(MediaTagType.SCSI_MODESENSE_10, cmdBuf); decMode = Modes.DecodeMode10(cmdBuf, dev.ScsiType); @@ -155,39 +169,49 @@ namespace DiscImageChef.Core.Devices.Dumping dumpLog.WriteLine("Requesting MODE SENSE (6)."); UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); - sense = dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, - out _); - if (sense || dev.Error) - sense = dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, - 5, out _); - if (sense || dev.Error) sense = dev.ModeSense(out cmdBuf, out _, 5, out _); - if (!sense && !dev.Error) - if (Modes.DecodeMode6(cmdBuf, dev.ScsiType).HasValue) + sense = dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, + out _); + + if(sense || dev.Error) + sense = dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, + 5, out _); + + if(sense || dev.Error) + sense = dev.ModeSense(out cmdBuf, out _, 5, out _); + + if(!sense && + !dev.Error) + if(Modes.DecodeMode6(cmdBuf, dev.ScsiType).HasValue) { mediaTags.Add(MediaTagType.SCSI_MODESENSE_6, cmdBuf); decMode = Modes.DecodeMode6(cmdBuf, dev.ScsiType); } - if (decMode.HasValue) + if(decMode.HasValue) { - scsiMediumType = (byte) decMode.Value.Header.MediumType; - if (decMode.Value.Header.BlockDescriptors != null && - decMode.Value.Header.BlockDescriptors.Length >= 1) - scsiDensityCode = (byte) decMode.Value.Header.BlockDescriptors[0].Density; + scsiMediumType = (byte)decMode.Value.Header.MediumType; + + if(decMode.Value.Header.BlockDescriptors != null && + decMode.Value.Header.BlockDescriptors.Length >= 1) + scsiDensityCode = (byte)decMode.Value.Header.BlockDescriptors[0].Density; containsFloppyPage = decMode.Value.Pages != null && decMode.Value.Pages.Aggregate(containsFloppyPage, - (current, modePage) => current | (modePage.Page == 0x05)); + (current, modePage) => + current | (modePage.Page == 0x05)); } } } - if (dskType == MediaType.Unknown) - dskType = MediaTypeFromScsi.Get((byte) dev.ScsiType, dev.Manufacturer, dev.Model, scsiMediumType, - scsiDensityCode, blocks, blockSize); + if(dskType == MediaType.Unknown) + dskType = MediaTypeFromScsi.Get((byte)dev.ScsiType, dev.Manufacturer, dev.Model, scsiMediumType, + scsiDensityCode, blocks, blockSize); - if (dskType == MediaType.Unknown && dev.IsUsb && containsFloppyPage) dskType = MediaType.FlashDrive; + if(dskType == MediaType.Unknown && + dev.IsUsb && + containsFloppyPage) + dskType = MediaType.FlashDrive; UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); @@ -209,20 +233,20 @@ namespace DiscImageChef.Core.Devices.Dumping dumpLog.WriteLine("SCSI floppy mode page present: {0}.", containsFloppyPage); dumpLog.WriteLine("Media identified as {0}.", dskType); - var longBlockSize = scsiReader.LongBlockSize; + uint longBlockSize = scsiReader.LongBlockSize; - if (dumpRaw) - if (blockSize == longBlockSize) + if(dumpRaw) + if(blockSize == longBlockSize) { ErrorMessage?.Invoke(!scsiReader.CanReadRaw - ? "Device doesn't seem capable of reading raw data from media." - : "Device is capable of reading raw data but I've been unable to guess correct sector size."); + ? "Device doesn't seem capable of reading raw data from media." + : "Device is capable of reading raw data but I've been unable to guess correct sector size."); - if (!force) + if(!force) { - StoppingErrorMessage - ?.Invoke( - "Not continuing. If you want to continue reading cooked data when raw is not available use the force option."); + 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; } @@ -232,28 +256,30 @@ namespace DiscImageChef.Core.Devices.Dumping else { // Only a block will be read, but it contains 16 sectors and command expect sector number not block number - blocksToRead = (uint) (longBlockSize == 37856 ? 16 : 1); - UpdateStatus - ?.Invoke( - $"Reading {longBlockSize} raw bytes ({blockSize * blocksToRead} cooked bytes) per sector."); + blocksToRead = (uint)(longBlockSize == 37856 ? 16 : 1); + + UpdateStatus?. + Invoke($"Reading {longBlockSize} raw bytes ({blockSize * blocksToRead} cooked bytes) per sector."); + physicalBlockSize = longBlockSize; - blockSize = longBlockSize; + blockSize = longBlockSize; } - var ret = true; + bool ret = true; - foreach (var tag in mediaTags.Keys) + foreach(MediaTagType tag in mediaTags.Keys) { - if (outputPlugin.SupportedMediaTags.Contains(tag)) continue; + 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(!ret) { - if (force) + if(force) { dumpLog.WriteLine("Several media tags not supported, continuing..."); ErrorMessage?.Invoke("Several media tags not supported, continuing..."); @@ -262,6 +288,7 @@ namespace DiscImageChef.Core.Devices.Dumping { dumpLog.WriteLine("Several media tags not supported, not continuing..."); StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); + return; } } @@ -270,37 +297,36 @@ namespace DiscImageChef.Core.Devices.Dumping dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead); var mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); - var ibgLog = new IbgLog(outputPrefix + ".ibg", SBC_PROFILE); + var ibgLog = new IbgLog(outputPrefix + ".ibg", SBC_PROFILE); ret = outputPlugin.Create(outputPath, dskType, formatOptions, blocks, blockSize); // Cannot create image - if (!ret) + 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 (opticalDisc) + if(opticalDisc) { - if (outputPlugin is IWritableOpticalImage opticalPlugin) + if(outputPlugin is IWritableOpticalImage opticalPlugin) { opticalPlugin.SetTracks(new List { new Track { - TrackBytesPerSector = (int) blockSize, - TrackEndSector = blocks - 1, - TrackSequence = 1, - TrackRawBytesPerSector = (int) blockSize, - TrackSubchannelType = TrackSubchannelType.None, - TrackSession = 1, - TrackType = TrackType.Data + TrackBytesPerSector = (int)blockSize, TrackEndSector = blocks - 1, + TrackSequence = 1, + TrackRawBytesPerSector = (int)blockSize, TrackSubchannelType = TrackSubchannelType.None, + TrackSession = 1, TrackType = TrackType.Data } }); } @@ -308,95 +334,118 @@ namespace DiscImageChef.Core.Devices.Dumping { dumpLog.WriteLine("The specified plugin does not support storing optical disc images.."); StoppingErrorMessage?.Invoke("The specified plugin does not support storing optical disc images."); + return; } } - else if (decMode?.Pages != null) + else if(decMode?.Pages != null) { - var setGeometry = false; + bool setGeometry = false; - foreach (var page in decMode.Value.Pages) - if (page.Page == 0x04 && page.Subpage == 0x00) + foreach(Modes.ModePage page in decMode.Value.Pages) + if(page.Page == 0x04 && + page.Subpage == 0x00) { - var rigidPage = Modes.DecodeModePage_04(page.PageResponse); - if (!rigidPage.HasValue || setGeometry) continue; + 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"); + 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))); + (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))); setGeometry = true; } - else if (page.Page == 0x05 && page.Subpage == 0x00) + else if(page.Page == 0x05 && + page.Subpage == 0x00) { - var flexiblePage = Modes.DecodeModePage_05(page.PageResponse); - if (!flexiblePage.HasValue) continue; + 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"); + 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); + flexiblePage.Value.SectorsPerTrack); + setGeometry = true; } } DumpHardwareType currentTry = null; - ExtentsULong extents = null; + ExtentsULong extents = null; + ResumeSupport.Process(true, dev.IsRemovable, blocks, dev.Manufacturer, dev.Model, dev.Serial, - dev.PlatformId, ref resume, ref currentTry, ref extents); - if (currentTry == null || extents == null) + dev.PlatformId, ref resume, ref currentTry, ref extents); + + if(currentTry == null || + extents == null) { StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + return; } - if (resume.NextBlock > 0) + if(resume.NextBlock > 0) { UpdateStatus?.Invoke($"Resuming from block {resume.NextBlock}."); dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock); } - var newTrim = false; - var timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; + bool newTrim = false; + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; InitProgress?.Invoke(); - for (var i = resume.NextBlock; i < blocks; i += blocksToRead) + + for(ulong i = resume.NextBlock; i < blocks; i += blocksToRead) { - if (aborted) + if(aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } - if (blocks - i < blocksToRead) blocksToRead = (uint) (blocks - i); + 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 + #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator + if(currentSpeed > maxSpeed && + currentSpeed != 0) + maxSpeed = currentSpeed; - UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long) i, - (long) blocks); + if(currentSpeed < minSpeed && + currentSpeed != 0) + minSpeed = currentSpeed; + #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator - sense = scsiReader.ReadBlocks(out readBuffer, i, blocksToRead, out var cmdDuration); + UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, + (long)blocks); + + sense = scsiReader.ReadBlocks(out readBuffer, i, blocksToRead, out double cmdDuration); totalDuration += cmdDuration; - if (!sense && !dev.Error) + if(!sense && + !dev.Error) { mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); - var writeStart = DateTime.Now; + DateTime writeStart = DateTime.Now; outputPlugin.WriteSectors(readBuffer, i, blocksToRead); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; extents.Add(i, blocksToRead, true); @@ -404,80 +453,95 @@ namespace DiscImageChef.Core.Devices.Dumping else { // TODO: Reset device after X errors - if (stopOnError) return; // TODO: Return more cleanly + if(stopOnError) + return; // TODO: Return more cleanly - if (i + skip > blocks) skip = (uint) (blocks - i); + if(i + skip > blocks) + skip = (uint)(blocks - i); // Write empty data - var writeStart = DateTime.Now; + DateTime writeStart = DateTime.Now; outputPlugin.WriteSectors(new byte[blockSize * skip], i, skip); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - for (var b = i; b < i + skip; b++) resume.BadBlocks.Add(b); + 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; + i += skip - blocksToRead; + newTrim = true; } sectorSpeedStart += blocksToRead; - resume.NextBlock = i + blocksToRead; + resume.NextBlock = i + blocksToRead; - var elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - if (elapsed < 1) continue; + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + if(elapsed < 1) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; + 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); + 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."); + + 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)); + (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); + dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", - (double) blockSize * (double) (blocks + 1) / 1024 / imageWriteDuration); + (double)blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); #region Trimming - - if (resume.BadBlocks.Count > 0 && !aborted && !notrim && newTrim) + if(resume.BadBlocks.Count > 0 && + !aborted && + !notrim && + newTrim) { start = DateTime.UtcNow; UpdateStatus?.Invoke("Trimming bad sectors"); dumpLog.WriteLine("Trimming bad sectors"); - var tmpArray = resume.BadBlocks.ToArray(); + ulong[] tmpArray = resume.BadBlocks.ToArray(); InitProgress?.Invoke(); - foreach (var badSector in tmpArray) + + foreach(ulong badSector in tmpArray) { - if (aborted) + if(aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } PulseProgress?.Invoke($"Trimming sector {badSector}"); - sense = scsiReader.ReadBlock(out readBuffer, badSector, out var cmdDuration); + sense = scsiReader.ReadBlock(out readBuffer, badSector, out double cmdDuration); - if (sense || dev.Error) continue; + if(sense || dev.Error) + continue; resume.BadBlocks.Remove(badSector); extents.Add(badSector); @@ -489,58 +553,67 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke($"Trimmming finished in {(end - start).TotalSeconds} seconds."); dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds); } - #endregion Trimming #region Error handling - - if (resume.BadBlocks.Count > 0 && !aborted && retryPasses > 0) + if(resume.BadBlocks.Count > 0 && + !aborted && + retryPasses > 0) { - var pass = 1; - var forward = true; - var runningPersistent = false; + int pass = 1; + bool forward = true; + bool runningPersistent = false; Modes.ModePage? currentModePage = null; - byte[] md6; - byte[] md10; + byte[] md6; + byte[] md10; - if (persistent) + if(persistent) { Modes.ModePage_01_MMC pgMmc; - Modes.ModePage_01 pg; + Modes.ModePage_01 pg; sense = dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, - dev.Timeout, out _); - if (sense) + dev.Timeout, out _); + + if(sense) { sense = dev.ModeSense10(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, - dev.Timeout, out _); + dev.Timeout, out _); - if (!sense) + if(!sense) { - var dcMode10 = Modes.DecodeMode10(readBuffer, dev.ScsiType); + Modes.DecodedMode? dcMode10 = Modes.DecodeMode10(readBuffer, dev.ScsiType); - if (dcMode10.HasValue && dcMode10.Value.Pages != null) - foreach (var modePage in dcMode10.Value.Pages) - if (modePage.Page == 0x01 && modePage.Subpage == 0x00) + if(dcMode10.HasValue && + dcMode10.Value.Pages != null) + foreach(Modes.ModePage modePage in dcMode10.Value.Pages) + if(modePage.Page == 0x01 && + modePage.Subpage == 0x00) currentModePage = modePage; } } else { - var dcMode6 = Modes.DecodeMode6(readBuffer, dev.ScsiType); + Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(readBuffer, dev.ScsiType); - if (dcMode6.HasValue && dcMode6.Value.Pages != null) - foreach (var modePage in dcMode6.Value.Pages) - if (modePage.Page == 0x01 && modePage.Subpage == 0x00) + 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) + if(currentModePage == null) { - if (dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) + if(dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) { - pgMmc = new Modes.ModePage_01_MMC {PS = false, ReadRetryCount = 32, Parameter = 0x00}; + 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) @@ -550,16 +623,9 @@ namespace DiscImageChef.Core.Devices.Dumping { 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 + PS = false, AWRE = true, ARRE = true, TB = false, + RC = false, EER = true, PER = false, DTE = true, + DCR = false, ReadRetryCount = 32 }; currentModePage = new Modes.ModePage @@ -569,44 +635,39 @@ namespace DiscImageChef.Core.Devices.Dumping } } - if (dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) + if(dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) { - pgMmc = new Modes.ModePage_01_MMC {PS = false, ReadRetryCount = 255, Parameter = 0x20}; + pgMmc = new Modes.ModePage_01_MMC + { + PS = false, ReadRetryCount = 255, Parameter = 0x20 + }; + var md = new Modes.DecodedMode { - Header = new Modes.ModeHeader(), - Pages = new[] + Header = new Modes.ModeHeader(), Pages = new[] { new Modes.ModePage { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) + Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) } } }; - md6 = Modes.EncodeMode6(md, dev.ScsiType); + + 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 + 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[] + Header = new Modes.ModeHeader(), Pages = new[] { new Modes.ModePage { @@ -614,23 +675,27 @@ namespace DiscImageChef.Core.Devices.Dumping } } }; - md6 = Modes.EncodeMode6(md, dev.ScsiType); + + 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 var senseBuf, true, false, dev.Timeout, out _); - if (sense) sense = dev.ModeSelect10(md10, out senseBuf, true, false, dev.Timeout, out _); + sense = dev.ModeSelect(md6, out byte[] senseBuf, true, false, dev.Timeout, out _); - if (sense) + 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."); + 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."); + + dumpLog. + WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); } else { @@ -640,25 +705,28 @@ namespace DiscImageChef.Core.Devices.Dumping InitProgress?.Invoke(); repeatRetry: - var tmpArray = resume.BadBlocks.ToArray(); - foreach (var badSector in tmpArray) + ulong[] tmpArray = resume.BadBlocks.ToArray(); + + foreach(ulong badSector in tmpArray) { - if (aborted) + 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, " : "")); + forward ? "forward" : "reverse", + runningPersistent ? "recovering partial data, " : "")); - sense = scsiReader.ReadBlock(out readBuffer, badSector, out var cmdDuration); + sense = scsiReader.ReadBlock(out readBuffer, badSector, out double cmdDuration); totalDuration += cmdDuration; - if (!sense && !dev.Error) + if(!sense && + !dev.Error) { resume.BadBlocks.Remove(badSector); extents.Add(badSector); @@ -666,165 +734,206 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } - else if (runningPersistent) + else if(runningPersistent) { outputPlugin.WriteSector(readBuffer, badSector); } } - if (pass < retryPasses && !aborted && resume.BadBlocks.Count > 0) + if(pass < retryPasses && + !aborted && + resume.BadBlocks.Count > 0) { pass++; forward = !forward; resume.BadBlocks.Sort(); resume.BadBlocks.Reverse(); + goto repeatRetry; } - if (runningPersistent && currentModePage.HasValue) + if(runningPersistent && currentModePage.HasValue) { var md = new Modes.DecodedMode { - Header = new Modes.ModeHeader(), Pages = new[] {currentModePage.Value} + Header = new Modes.ModeHeader(), Pages = new[] + { + currentModePage.Value + } }; - md6 = Modes.EncodeMode6(md, dev.ScsiType); + + 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(sense) + dev.ModeSelect10(md10, out _, true, false, dev.Timeout, out _); } EndProgress?.Invoke(); } - #endregion Error handling - if (!aborted) - if (opticalDisc) + if(!aborted) + if(opticalDisc) { - foreach (var tag in mediaTags) + foreach(KeyValuePair tag in mediaTags) { - if (tag.Value is null) + if(tag.Value is null) { DicConsole.ErrorWriteLine("Error: Tag type {0} is null, skipping...", tag.Key); + continue; } ret = outputPlugin.WriteMediaTag(tag.Value, tag.Key); - if (ret || force) continue; + if(ret || force) + continue; // Cannot write tag to image StoppingErrorMessage?.Invoke($"Cannot write tag {tag.Key}."); + dumpLog.WriteLine($"Cannot write tag {tag.Key}." + Environment.NewLine + outputPlugin.ErrorMessage); + return; } } else { - if (!dev.IsRemovable || dev.IsUsb) + if(!dev.IsRemovable || + dev.IsUsb) { - if (dev.IsUsb && dev.UsbDescriptors != null) + if(dev.IsUsb && + dev.UsbDescriptors != null) { UpdateStatus?.Invoke("Reading USB descriptors."); dumpLog.WriteLine("Reading USB descriptors."); ret = outputPlugin.WriteMediaTag(dev.UsbDescriptors, MediaTagType.USB_Descriptors); - if (!ret && !force) + if(!ret && + !force) { dumpLog.WriteLine("Cannot write USB descriptors."); + StoppingErrorMessage?.Invoke("Cannot write USB descriptors." + Environment.NewLine + outputPlugin.ErrorMessage); + return; } } byte[] cmdBuf; - if (dev.Type == DeviceType.ATAPI) + + if(dev.Type == DeviceType.ATAPI) { UpdateStatus?.Invoke("Requesting ATAPI IDENTIFY PACKET DEVICE."); dumpLog.WriteLine("Requesting ATAPI IDENTIFY PACKET DEVICE."); sense = dev.AtapiIdentify(out cmdBuf, out _); - if (!sense) + + if(!sense) { ret = outputPlugin.WriteMediaTag(cmdBuf, MediaTagType.ATAPI_IDENTIFY); - if (!ret && !force) + if(!ret && + !force) { dumpLog.WriteLine("Cannot write ATAPI IDENTIFY PACKET DEVICE."); + StoppingErrorMessage?.Invoke("Cannot write ATAPI IDENTIFY PACKET DEVICE." + - Environment.NewLine + + Environment.NewLine + outputPlugin.ErrorMessage); + return; } } } sense = dev.ScsiInquiry(out cmdBuf, out _); - if (!sense) + + if(!sense) { UpdateStatus?.Invoke("Requesting SCSI INQUIRY."); dumpLog.WriteLine("Requesting SCSI INQUIRY."); ret = outputPlugin.WriteMediaTag(cmdBuf, MediaTagType.SCSI_INQUIRY); - if (!ret && !force) + if(!ret && + !force) { StoppingErrorMessage?.Invoke("Cannot write SCSI INQUIRY."); + dumpLog.WriteLine("Cannot write SCSI INQUIRY." + Environment.NewLine + outputPlugin.ErrorMessage); + return; } UpdateStatus?.Invoke("Requesting MODE SENSE (10)."); dumpLog.WriteLine("Requesting MODE SENSE (10)."); + sense = dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, - 0x3F, 0xFF, 5, out _); - if (!sense || dev.Error) + 0x3F, 0xFF, 5, out _); + + if(!sense || + dev.Error) sense = dev.ModeSense10(out cmdBuf, out _, false, true, - ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _); + ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _); decMode = null; - if (!sense && !dev.Error) - if (Modes.DecodeMode10(cmdBuf, dev.ScsiType).HasValue) + if(!sense && + !dev.Error) + if(Modes.DecodeMode10(cmdBuf, dev.ScsiType).HasValue) { decMode = Modes.DecodeMode10(cmdBuf, dev.ScsiType); - ret = outputPlugin.WriteMediaTag(cmdBuf, MediaTagType.SCSI_MODESENSE_10); + ret = outputPlugin.WriteMediaTag(cmdBuf, MediaTagType.SCSI_MODESENSE_10); - if (!ret && !force) + if(!ret && + !force) { dumpLog.WriteLine("Cannot write SCSI MODE SENSE (10)."); + StoppingErrorMessage?.Invoke("Cannot write SCSI MODE SENSE (10)." + - Environment.NewLine + + Environment.NewLine + outputPlugin.ErrorMessage); + return; } } UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); dumpLog.WriteLine("Requesting MODE SENSE (6)."); - sense = dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, - 0x00, 5, out _); - if (sense || dev.Error) - sense = dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, - 0x00, 5, out _); - if (sense || dev.Error) sense = dev.ModeSense(out cmdBuf, out _, 5, out _); - if (!sense && !dev.Error) - if (Modes.DecodeMode6(cmdBuf, dev.ScsiType).HasValue) + sense = dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, + 0x00, 5, out _); + + if(sense || dev.Error) + sense = dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, + 0x00, 5, out _); + + if(sense || dev.Error) + sense = dev.ModeSense(out cmdBuf, out _, 5, out _); + + if(!sense && + !dev.Error) + if(Modes.DecodeMode6(cmdBuf, dev.ScsiType).HasValue) { decMode = Modes.DecodeMode6(cmdBuf, dev.ScsiType); - ret = outputPlugin.WriteMediaTag(cmdBuf, MediaTagType.SCSI_MODESENSE_6); + ret = outputPlugin.WriteMediaTag(cmdBuf, MediaTagType.SCSI_MODESENSE_6); - if (!ret && !force) + if(!ret && + !force) { dumpLog.WriteLine("Cannot write SCSI MODE SENSE (6)."); + StoppingErrorMessage?.Invoke("Cannot write SCSI MODE SENSE (6)." + - Environment.NewLine + + Environment.NewLine + outputPlugin.ErrorMessage); + return; } } @@ -833,80 +942,96 @@ namespace DiscImageChef.Core.Devices.Dumping } resume.BadBlocks.Sort(); - foreach (var bad in resume.BadBlocks) dumpLog.WriteLine("Sector {0} could not be read.", bad); + + 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); + + if(preSidecar != null) + outputPlugin.SetCicmMetadata(preSidecar); + dumpLog.WriteLine("Closing output file."); UpdateStatus?.Invoke("Closing output file."); - var closeStart = DateTime.Now; + DateTime closeStart = DateTime.Now; outputPlugin.Close(); - var closeEnd = DateTime.Now; + DateTime closeEnd = DateTime.Now; UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); - if (aborted) + if(aborted) { UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + return; } double totalChkDuration = 0; - if (!nometadata) + + if(!nometadata) { UpdateStatus?.Invoke("Creating sidecar."); dumpLog.WriteLine("Creating sidecar."); - var filters = new FiltersList(); - var filter = filters.GetFilter(outputPath); - var inputPlugin = ImageFormat.Detect(filter); - if (!inputPlugin.Open(filter)) + 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; } - var chkStart = DateTime.UtcNow; - sidecarClass = new Sidecar(inputPlugin, outputPath, filter.Id, encoding); - sidecarClass.InitProgressEvent += InitProgress; - sidecarClass.UpdateProgressEvent += UpdateProgress; - sidecarClass.EndProgressEvent += EndProgress; - sidecarClass.InitProgressEvent2 += InitProgress2; + 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; - var sidecar = sidecarClass.Create(); + 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 (opticalDisc) + 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(opticalDisc) { - if (preSidecar != null) + if(preSidecar != null) { preSidecar.OpticalDisc = sidecar.OpticalDisc; - sidecar = preSidecar; + sidecar = preSidecar; } - var 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 (partition.StartSector, fileSystem.Type)); + List<(ulong start, string type)> filesystems = new List<(ulong start, string type)>(); - if (filesystems.Count > 0) - foreach (var filesystem in filesystems.Select(o => new {o.start, o.type}).Distinct()) + 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 (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); @@ -914,68 +1039,67 @@ namespace DiscImageChef.Core.Devices.Dumping // TODO: Implement layers sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); - CommonTypes.Metadata.MediaType.MediaTypeToString(dskType, out var xmlDskTyp, - out var xmlDskSubTyp); - sidecar.OpticalDisc[0].DiscType = xmlDskTyp; - sidecar.OpticalDisc[0].DiscSubType = xmlDskSubTyp; + (string type, string subType) xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); + sidecar.OpticalDisc[0].DiscType = xmlType.type; + sidecar.OpticalDisc[0].DiscSubType = xmlType.subType; sidecar.OpticalDisc[0].DumpHardwareArray = resume.Tries.ToArray(); - foreach (var tag in mediaTags) - if (outputPlugin.SupportedMediaTags.Contains(tag.Key)) + foreach(KeyValuePair tag in mediaTags) + if(outputPlugin.SupportedMediaTags.Contains(tag.Key)) AddMediaTagToSidecar(outputPath, tag, ref sidecar); } else { - if (preSidecar != null) + if(preSidecar != null) { preSidecar.BlockMedia = sidecar.BlockMedia; - sidecar = preSidecar; + sidecar = preSidecar; } // All USB flash drives report as removable, even if the media is not removable - if (!dev.IsRemovable || dev.IsUsb) + if(!dev.IsRemovable || + dev.IsUsb) { - if (dev.IsUsb && dev.UsbDescriptors != null) - if (outputPlugin.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors)) + if(dev.IsUsb && + dev.UsbDescriptors != null) + if(outputPlugin.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors)) sidecar.BlockMedia[0].USB = new USBType { - ProductID = dev.UsbProductId, - VendorID = dev.UsbVendorId, - Descriptors = new DumpType + ProductID = dev.UsbProductId, VendorID = dev.UsbVendorId, Descriptors = new DumpType { - Image = outputPath, - Size = (ulong) dev.UsbDescriptors.Length, + Image = outputPath, Size = (ulong)dev.UsbDescriptors.Length, Checksums = Checksum.GetChecksums(dev.UsbDescriptors).ToArray() } }; byte[] cmdBuf; - if (dev.Type == DeviceType.ATAPI) + + if(dev.Type == DeviceType.ATAPI) { sense = dev.AtapiIdentify(out cmdBuf, out _); - if (!sense) - if (outputPlugin.SupportedMediaTags.Contains(MediaTagType.ATAPI_IDENTIFY)) + + if(!sense) + if(outputPlugin.SupportedMediaTags.Contains(MediaTagType.ATAPI_IDENTIFY)) sidecar.BlockMedia[0].ATA = new ATAType { Identify = new DumpType { - Image = outputPath, - Size = (ulong) cmdBuf.Length, + Image = outputPath, Size = (ulong)cmdBuf.Length, Checksums = Checksum.GetChecksums(cmdBuf).ToArray() } }; } sense = dev.ScsiInquiry(out cmdBuf, out _); - if (!sense) + + if(!sense) { - if (outputPlugin.SupportedMediaTags.Contains(MediaTagType.SCSI_INQUIRY)) + if(outputPlugin.SupportedMediaTags.Contains(MediaTagType.SCSI_INQUIRY)) sidecar.BlockMedia[0].SCSI = new SCSIType { Inquiry = new DumpType { - Image = outputPath, - Size = (ulong) cmdBuf.Length, + Image = outputPath, Size = (ulong)cmdBuf.Length, Checksums = Checksum.GetChecksums(cmdBuf).ToArray() } }; @@ -1016,79 +1140,97 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke("Requesting MODE SENSE (10)."); dumpLog.WriteLine("Requesting MODE SENSE (10)."); + sense = dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, - 0x3F, 0xFF, 5, out _); - if (!sense || dev.Error) + 0x3F, 0xFF, 5, out _); + + if(!sense || + dev.Error) sense = dev.ModeSense10(out cmdBuf, out _, false, true, - ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _); + ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _); decMode = null; - if (!sense && !dev.Error) - if (Modes.DecodeMode10(cmdBuf, dev.ScsiType).HasValue) - if (outputPlugin.SupportedMediaTags.Contains(MediaTagType.SCSI_MODESENSE_10)) + if(!sense && + !dev.Error) + if(Modes.DecodeMode10(cmdBuf, dev.ScsiType).HasValue) + if(outputPlugin.SupportedMediaTags.Contains(MediaTagType.SCSI_MODESENSE_10)) sidecar.BlockMedia[0].SCSI.ModeSense10 = new DumpType { - Image = outputPath, - Size = (ulong) cmdBuf.Length, + Image = outputPath, Size = (ulong)cmdBuf.Length, Checksums = Checksum.GetChecksums(cmdBuf).ToArray() }; UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); dumpLog.WriteLine("Requesting MODE SENSE (6)."); - sense = dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, - 0x00, 5, out _); - if (sense || dev.Error) - sense = dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, - 0x00, 5, out _); - if (sense || dev.Error) sense = dev.ModeSense(out cmdBuf, out _, 5, out _); - if (!sense && !dev.Error) - if (Modes.DecodeMode6(cmdBuf, dev.ScsiType).HasValue) - if (outputPlugin.SupportedMediaTags.Contains(MediaTagType.SCSI_MODESENSE_6)) + sense = dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, + 0x00, 5, out _); + + if(sense || dev.Error) + sense = dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, + 0x00, 5, out _); + + if(sense || dev.Error) + sense = dev.ModeSense(out cmdBuf, out _, 5, out _); + + if(!sense && + !dev.Error) + if(Modes.DecodeMode6(cmdBuf, dev.ScsiType).HasValue) + if(outputPlugin.SupportedMediaTags.Contains(MediaTagType.SCSI_MODESENSE_6)) sidecar.BlockMedia[0].SCSI.ModeSense = new DumpType { - Image = outputPath, - Size = (ulong) cmdBuf.Length, + Image = outputPath, Size = (ulong)cmdBuf.Length, Checksums = Checksum.GetChecksums(cmdBuf).ToArray() }; } } - var 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)); + List<(ulong start, string type)> filesystems = new List<(ulong start, string type)>(); - if (filesystems.Count > 0) - foreach (var filesystem in filesystems.Select(o => new {o.start, o.type}).Distinct()) + 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); - CommonTypes.Metadata.MediaType.MediaTypeToString(dskType, out var xmlDskTyp, - out var xmlDskSubTyp); - sidecar.BlockMedia[0].DiskType = xmlDskTyp; - sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp; - // TODO: Implement device firmware revision - 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; - sidecar.BlockMedia[0].Serial = dev.Serial; - sidecar.BlockMedia[0].Size = blocks * blockSize; + (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) sidecar.BlockMedia[0].DumpHardwareArray = resume.Tries.ToArray(); + // TODO: Implement device firmware revision + 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; + 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"); @@ -1101,12 +1243,13 @@ namespace DiscImageChef.Core.Devices.Dumping } 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."); + + 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."); + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); UpdateStatus?.Invoke($"{resume.BadBlocks.Count} sectors could not be read."); diff --git a/DiscImageChef.Core/Devices/Dumping/SSC.cs b/DiscImageChef.Core/Devices/Dumping/SSC.cs index 43073a70d..4af3ea8cb 100644 --- a/DiscImageChef.Core/Devices/Dumping/SSC.cs +++ b/DiscImageChef.Core/Devices/Dumping/SSC.cs @@ -400,8 +400,7 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke($"Blocksize changed to {blockSize} bytes at block {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); + sense = dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, dev.Timeout, out duration); totalDuration += duration; @@ -891,8 +890,7 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke($"Blocksize changed to {blockSize} bytes at block {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); + sense = dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, dev.Timeout, out duration); totalDuration += duration; @@ -1314,11 +1312,10 @@ namespace DiscImageChef.Core.Devices.Dumping sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); - CommonTypes.Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp, - out string xmlDskSubTyp); + (string type, string subType) xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); - sidecar.BlockMedia[0].DiskType = xmlDskTyp; - sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp; + sidecar.BlockMedia[0].DiskType = xmlType.type; + sidecar.BlockMedia[0].DiskSubType = xmlType.subType; // TODO: Implement device firmware revision if(!dev.IsRemovable || diff --git a/DiscImageChef.Core/Devices/Dumping/SecureDigital.cs b/DiscImageChef.Core/Devices/Dumping/SecureDigital.cs index 3cf1471c0..7150ade03 100644 --- a/DiscImageChef.Core/Devices/Dumping/SecureDigital.cs +++ b/DiscImageChef.Core/Devices/Dumping/SecureDigital.cs @@ -47,25 +47,22 @@ using MediaType = DiscImageChef.CommonTypes.MediaType; namespace DiscImageChef.Core.Devices.Dumping { - /// - /// Implements dumping a MultiMediaCard or SecureDigital flash card - /// + /// Implements dumping a MultiMediaCard or SecureDigital flash card public partial class Dump { - /// - /// Dumps a MultiMediaCard or SecureDigital flash card - /// + /// Dumps a MultiMediaCard or SecureDigital flash card public void SecureDigital() { if(dumpRaw) { if(force) - ErrorMessage - ?.Invoke("Raw dumping is not supported in MultiMediaCard or SecureDigital devices. Continuing..."); + ErrorMessage?. + Invoke("Raw dumping is not supported in MultiMediaCard or SecureDigital devices. Continuing..."); else { - StoppingErrorMessage - ?.Invoke("Raw dumping is not supported in MultiMediaCard or SecureDigital devices. Aborting..."); + StoppingErrorMessage?. + Invoke("Raw dumping is not supported in MultiMediaCard or SecureDigital devices. Aborting..."); + return; } } @@ -94,23 +91,30 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke("Reading Extended CSD"); dumpLog.WriteLine("Reading Extended CSD"); sense = dev.ReadExtendedCsd(out ecsd, out _, TIMEOUT, out duration); + if(!sense) { ExtendedCSD ecsdDecoded = Decoders.MMC.Decoders.DecodeExtendedCSD(ecsd); blocksToRead = ecsdDecoded.OptimalReadSize; blocks = ecsdDecoded.SectorCount; blockSize = (uint)(ecsdDecoded.SectorSize == 1 ? 4096 : 512); - if(ecsdDecoded.NativeSectorSize == 0) physicalBlockSize = 512; - else if(ecsdDecoded.NativeSectorSize == 1) physicalBlockSize = 4096; + + if(ecsdDecoded.NativeSectorSize == 0) + physicalBlockSize = 512; + else if(ecsdDecoded.NativeSectorSize == 1) + physicalBlockSize = 4096; + // Supposing it's high-capacity MMC if it has Extended CSD... byteAddressed = false; mediaTags.Add(MediaTagType.MMC_ExtendedCSD, null); } - else ecsd = null; + else + ecsd = null; UpdateStatus?.Invoke("Reading CSD"); dumpLog.WriteLine("Reading CSD"); sense = dev.ReadCsd(out csd, out _, TIMEOUT, out duration); + if(!sense) { if(blocks == 0) @@ -122,13 +126,17 @@ namespace DiscImageChef.Core.Devices.Dumping mediaTags.Add(MediaTagType.MMC_CSD, null); } - else csd = null; + else + csd = null; UpdateStatus?.Invoke("Reading OCR"); dumpLog.WriteLine("Reading OCR"); sense = dev.ReadOcr(out ocr, out _, TIMEOUT, out duration); - if(sense) ocr = null; - else mediaTags.Add(MediaTagType.MMC_OCR, null); + + if(sense) + ocr = null; + else + mediaTags.Add(MediaTagType.MMC_OCR, null); break; } @@ -138,30 +146,41 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke("Reading CSD"); dumpLog.WriteLine("Reading CSD"); sense = dev.ReadCsd(out csd, out _, TIMEOUT, out duration); + if(!sense) { Decoders.SecureDigital.CSD csdDecoded = Decoders.SecureDigital.Decoders.DecodeCSD(csd); + blocks = (ulong)(csdDecoded.Structure == 0 ? (csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2) : (csdDecoded.Size + 1) * 1024); + blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength); + // Structure >=1 for SDHC/SDXC, so that's block addressed byteAddressed = csdDecoded.Structure == 0; mediaTags.Add(MediaTagType.SD_CSD, null); } - else csd = null; + else + csd = null; UpdateStatus?.Invoke("Reading OCR"); dumpLog.WriteLine("Reading OCR"); sense = dev.ReadSdocr(out ocr, out _, TIMEOUT, out duration); - if(sense) ocr = null; - else mediaTags.Add(MediaTagType.SD_OCR, null); + + if(sense) + ocr = null; + else + mediaTags.Add(MediaTagType.SD_OCR, null); UpdateStatus?.Invoke("Reading SCR"); dumpLog.WriteLine("Reading SCR"); sense = dev.ReadScr(out scr, out _, TIMEOUT, out duration); - if(sense) scr = null; - else mediaTags.Add(MediaTagType.SD_SCR, null); + + if(sense) + scr = null; + else + mediaTags.Add(MediaTagType.SD_SCR, null); break; } @@ -170,8 +189,11 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke("Reading CID"); dumpLog.WriteLine("Reading CID"); sense = dev.ReadCid(out byte[] cid, out _, TIMEOUT, out duration); - if(sense) cid = null; - else mediaTags.Add(dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID : MediaTagType.MMC_CID, null); + + if(sense) + cid = null; + else + mediaTags.Add(dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID : MediaTagType.MMC_CID, null); DateTime start; DateTime end; @@ -184,6 +206,7 @@ namespace DiscImageChef.Core.Devices.Dumping { dumpLog.WriteLine("Unable to get device size."); StoppingErrorMessage?.Invoke("Unable to get device size."); + return; } @@ -197,30 +220,39 @@ namespace DiscImageChef.Core.Devices.Dumping { error = dev.Read(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, TIMEOUT, out duration); - if(error) blocksToRead /= 2; + if(error) + blocksToRead /= 2; - if(!error || blocksToRead == 1) break; + if(!error || + blocksToRead == 1) + break; } if(error) { dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", dev.LastError); StoppingErrorMessage?.Invoke($"Device error {dev.LastError} trying to guess ideal transfer length."); + return; } UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); - if(skip < blocksToRead) skip = blocksToRead; + if(skip < blocksToRead) + skip = blocksToRead; 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) + + if(currentTry == null || + extents == null) { StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + return; } @@ -228,7 +260,8 @@ namespace DiscImageChef.Core.Devices.Dumping foreach(MediaTagType tag in mediaTags.Keys) { - if(outputPlugin.SupportedMediaTags.Contains(tag)) continue; + if(outputPlugin.SupportedMediaTags.Contains(tag)) + continue; ret = false; dumpLog.WriteLine($"Output format does not support {tag}."); @@ -246,12 +279,14 @@ namespace DiscImageChef.Core.Devices.Dumping { dumpLog.WriteLine("Several media tags not supported, not continuing..."); StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); + return; } } - MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); - IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", SD_PROFILE); + var mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); + var ibgLog = new IbgLog(outputPrefix + ".ibg", SD_PROFILE); + ret = outputPlugin.Create(outputPath, dev.Type == DeviceType.SecureDigital ? MediaType.SecureDigital : MediaType.MMC, formatOptions, blocks, blockSize); @@ -261,8 +296,10 @@ namespace DiscImageChef.Core.Devices.Dumping { 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; } @@ -279,6 +316,7 @@ namespace DiscImageChef.Core.Devices.Dumping ulong sectorSpeedStart = 0; InitProgress?.Invoke(); + for(ulong i = resume.NextBlock; i < blocks; i += blocksToRead) { if(aborted) @@ -286,14 +324,21 @@ namespace DiscImageChef.Core.Devices.Dumping currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } - if(blocks - i < blocksToRead) blocksToRead = (byte)(blocks - i); + 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; + 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, @@ -313,9 +358,11 @@ namespace DiscImageChef.Core.Devices.Dumping } else { - if(i + skip > blocks) skip = (uint)(blocks - i); + if(i + skip > blocks) + skip = (uint)(blocks - i); - for(ulong b = i; b < i + skip; b++) resume.BadBlocks.Add(b); + for(ulong b = i; b < i + skip; b++) + resume.BadBlocks.Add(b); mhddLog.Write(i, duration < 500 ? 65535 : duration); @@ -332,7 +379,9 @@ namespace DiscImageChef.Core.Devices.Dumping resume.NextBlock = i + blocksToRead; double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - if(elapsed < 1) continue; + + if(elapsed < 1) + continue; currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); sectorSpeedStart = 0; @@ -342,22 +391,32 @@ namespace DiscImageChef.Core.Devices.Dumping end = DateTime.Now; 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."); + + 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 && !notrim && newTrim) + if(resume.BadBlocks.Count > 0 && + !aborted && + !notrim && + newTrim) { start = DateTime.UtcNow; UpdateStatus?.Invoke("Trimming bad sectors"); @@ -365,6 +424,7 @@ namespace DiscImageChef.Core.Devices.Dumping ulong[] tmpArray = resume.BadBlocks.ToArray(); InitProgress?.Invoke(); + foreach(ulong badSector in tmpArray) { if(aborted) @@ -372,6 +432,7 @@ namespace DiscImageChef.Core.Devices.Dumping currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } @@ -382,7 +443,8 @@ namespace DiscImageChef.Core.Devices.Dumping totalDuration += duration; - if(error) continue; + if(error) + continue; resume.BadBlocks.Remove(badSector); extents.Add(badSector); @@ -397,7 +459,9 @@ namespace DiscImageChef.Core.Devices.Dumping #endregion Trimming #region Error handling - if(resume.BadBlocks.Count > 0 && !aborted && retryPasses > 0) + if(resume.BadBlocks.Count > 0 && + !aborted && + retryPasses > 0) { int pass = 1; bool forward = true; @@ -406,6 +470,7 @@ namespace DiscImageChef.Core.Devices.Dumping InitProgress?.Invoke(); repeatRetryLba: ulong[] tmpArray = resume.BadBlocks.ToArray(); + foreach(ulong badSector in tmpArray) { if(aborted) @@ -413,6 +478,7 @@ namespace DiscImageChef.Core.Devices.Dumping currentTry.Extents = ExtentsConverter.ToMetadata(extents); UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + break; } @@ -433,15 +499,19 @@ namespace DiscImageChef.Core.Devices.Dumping 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(cmdBuf, badSector); + else if(runningPersistent) + outputPlugin.WriteSector(cmdBuf, badSector); } - if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0) + if(pass < retryPasses && + !aborted && + resume.BadBlocks.Count > 0) { pass++; forward = !forward; resume.BadBlocks.Sort(); resume.BadBlocks.Reverse(); + goto repeatRetryLba; } @@ -452,7 +522,10 @@ namespace DiscImageChef.Core.Devices.Dumping currentTry.Extents = ExtentsConverter.ToMetadata(extents); outputPlugin.SetDumpHardware(resume.Tries); - if(preSidecar != null) outputPlugin.SetCicmMetadata(preSidecar); + + if(preSidecar != null) + outputPlugin.SetCicmMetadata(preSidecar); + dumpLog.WriteLine("Closing output file."); UpdateStatus?.Invoke("Closing output file."); DateTime closeStart = DateTime.Now; @@ -465,18 +538,22 @@ namespace DiscImageChef.Core.Devices.Dumping { UpdateStatus?.Invoke("Aborted!"); dumpLog.WriteLine("Aborted!"); + return; } double totalChkDuration = 0; + if(!nometadata) { UpdateStatus?.Invoke("Creating sidecar."); dumpLog.WriteLine("Creating sidecar."); - FiltersList filters = new FiltersList(); + 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."); + + if(!inputPlugin.Open(filter)) + StoppingErrorMessage?.Invoke("Could not open created image."); DateTime chkStart = DateTime.UtcNow; sidecarClass = new Sidecar(inputPlugin, outputPath, filter.Id, encoding); @@ -499,9 +576,11 @@ namespace DiscImageChef.Core.Devices.Dumping { case DeviceType.MMC: sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType(); + break; case DeviceType.SecureDigital: sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType(); + break; } @@ -513,23 +592,23 @@ namespace DiscImageChef.Core.Devices.Dumping { cidDump = new DumpType { - Image = outputPath, - Size = (ulong)cid.Length, - Checksums = Checksum.GetChecksums(cid).ToArray() + Image = outputPath, Size = (ulong)cid.Length, Checksums = Checksum.GetChecksums(cid).ToArray() }; ret = outputPlugin.WriteMediaTag(cid, - dev.Type == DeviceType.SecureDigital - ? MediaTagType.SD_CID + dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID : MediaTagType.MMC_CID); // Cannot write CID to image - if(!ret && !force) + if(!ret && + !force) { dumpLog.WriteLine("Cannot write CID to output image."); + StoppingErrorMessage?.Invoke("Cannot write CID to output image." + Environment.NewLine + outputPlugin.ErrorMessage); + return; } } @@ -538,23 +617,23 @@ namespace DiscImageChef.Core.Devices.Dumping { csdDump = new DumpType { - Image = outputPath, - Size = (ulong)csd.Length, - Checksums = Checksum.GetChecksums(csd).ToArray() + Image = outputPath, Size = (ulong)csd.Length, Checksums = Checksum.GetChecksums(csd).ToArray() }; ret = outputPlugin.WriteMediaTag(csd, - dev.Type == DeviceType.SecureDigital - ? MediaTagType.SD_CSD + dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CSD : MediaTagType.MMC_CSD); // Cannot write CSD to image - if(!ret && !force) + if(!ret && + !force) { dumpLog.WriteLine("Cannot write CSD to output image."); + StoppingErrorMessage?.Invoke("Cannot write CSD to output image." + Environment.NewLine + outputPlugin.ErrorMessage); + return; } } @@ -563,20 +642,21 @@ namespace DiscImageChef.Core.Devices.Dumping { sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD = new DumpType { - Image = outputPath, - Size = (ulong)ecsd.Length, - Checksums = Checksum.GetChecksums(ecsd).ToArray() + Image = outputPath, Size = (ulong)ecsd.Length, Checksums = Checksum.GetChecksums(ecsd).ToArray() }; ret = outputPlugin.WriteMediaTag(ecsd, MediaTagType.MMC_ExtendedCSD); // Cannot write Extended CSD to image - if(!ret && !force) + if(!ret && + !force) { dumpLog.WriteLine("Cannot write Extended CSD to output image."); + StoppingErrorMessage?.Invoke("Cannot write Extended CSD to output image." + Environment.NewLine + outputPlugin.ErrorMessage); + return; } } @@ -585,23 +665,23 @@ namespace DiscImageChef.Core.Devices.Dumping { ocrDump = new DumpType { - Image = outputPath, - Size = (ulong)ocr.Length, - Checksums = Checksum.GetChecksums(ocr).ToArray() + Image = outputPath, Size = (ulong)ocr.Length, Checksums = Checksum.GetChecksums(ocr).ToArray() }; ret = outputPlugin.WriteMediaTag(ocr, - dev.Type == DeviceType.SecureDigital - ? MediaTagType.SD_OCR + dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_OCR : MediaTagType.MMC_OCR); // Cannot write OCR to image - if(!ret && !force) + if(!ret && + !force) { dumpLog.WriteLine("Cannot write OCR to output image."); + StoppingErrorMessage?.Invoke("Cannot write OCR to output image." + Environment.NewLine + outputPlugin.ErrorMessage); + return; } } @@ -610,19 +690,20 @@ namespace DiscImageChef.Core.Devices.Dumping { sidecar.BlockMedia[0].SecureDigital.SCR = new DumpType { - Image = outputPath, - Size = (ulong)scr.Length, - Checksums = Checksum.GetChecksums(scr).ToArray() + Image = outputPath, Size = (ulong)scr.Length, Checksums = Checksum.GetChecksums(scr).ToArray() }; ret = outputPlugin.WriteMediaTag(scr, MediaTagType.SD_SCR); // Cannot write SCR to image - if(!ret && !force) + if(!ret && + !force) { dumpLog.WriteLine("Cannot write SCR to output image."); + StoppingErrorMessage?.Invoke("Cannot write SCR to output image." + Environment.NewLine + outputPlugin.ErrorMessage); + return; } } @@ -633,11 +714,13 @@ namespace DiscImageChef.Core.Devices.Dumping sidecar.BlockMedia[0].MultiMediaCard.CID = cidDump; sidecar.BlockMedia[0].MultiMediaCard.CSD = csdDump; sidecar.BlockMedia[0].MultiMediaCard.OCR = ocrDump; + break; case DeviceType.SecureDigital: sidecar.BlockMedia[0].SecureDigital.CID = cidDump; sidecar.BlockMedia[0].SecureDigital.CSD = csdDump; sidecar.BlockMedia[0].SecureDigital.OCR = ocrDump; + break; } @@ -645,29 +728,36 @@ namespace DiscImageChef.Core.Devices.Dumping 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."); + + 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)); - string xmlDskTyp = null, xmlDskSubTyp = null; + (string type, string subType) xmlType = (null, null); + switch(dev.Type) { case DeviceType.MMC: - CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.MMC, out xmlDskTyp, - out xmlDskSubTyp); + xmlType = + CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.MMC); + sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.MMC); + break; case DeviceType.SecureDigital: - CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.SecureDigital, out xmlDskTyp, - out xmlDskSubTyp); + CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.SecureDigital); sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.SecureDigital); + break; } - sidecar.BlockMedia[0].DiskType = xmlDskTyp; - sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp; + sidecar.BlockMedia[0].DiskType = xmlType.type; + sidecar.BlockMedia[0].DiskSubType = xmlType.subType; + // TODO: Implement device firmware revision sidecar.BlockMedia[0].LogicalBlocks = blocks; sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize > 0 ? physicalBlockSize : blockSize; @@ -679,31 +769,38 @@ namespace DiscImageChef.Core.Devices.Dumping UpdateStatus?.Invoke("Writing metadata sidecar"); - FileStream xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create); + var xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create); - XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType)); + 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."); + + 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."); + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); UpdateStatus?.Invoke($"{resume.BadBlocks.Count} sectors could not be read."); UpdateStatus?.Invoke(""); - if(resume.BadBlocks.Count > 0) resume.BadBlocks.Sort(); + + if(resume.BadBlocks.Count > 0) + resume.BadBlocks.Sort(); switch(dev.Type) { case DeviceType.MMC: Statistics.AddMedia(MediaType.MMC, true); + break; case DeviceType.SecureDigital: Statistics.AddMedia(MediaType.SecureDigital, true); + break; } } diff --git a/DiscImageChef.Core/Devices/Dumping/XGD.cs b/DiscImageChef.Core/Devices/Dumping/XGD.cs index 934ee2c60..22b4971af 100644 --- a/DiscImageChef.Core/Devices/Dumping/XGD.cs +++ b/DiscImageChef.Core/Devices/Dumping/XGD.cs @@ -81,11 +81,13 @@ namespace DiscImageChef.Core.Devices.Dumping { DicConsole. ErrorWriteLine("Because of the commands sent to a device, dumping XGD must be done with administrative privileges. Cannot continue."); + dumpLog.WriteLine("Cannot dump XGD without administrative privileges."); return; } } + if(mediaTags.ContainsKey(MediaTagType.DVD_PFI)) mediaTags.Remove(MediaTagType.DVD_PFI); @@ -1232,10 +1234,10 @@ namespace DiscImageChef.Core.Devices.Dumping sidecar.OpticalDisc[0].Sessions = 1; sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); - var xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); + (string type, string subType) xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); - sidecar.OpticalDisc[0].DiscType = xmlDskTyp; - sidecar.OpticalDisc[0].DiscSubType = xmlDskSubTyp; + sidecar.OpticalDisc[0].DiscType = xmlType.type; + sidecar.OpticalDisc[0].DiscSubType = xmlType.subType; foreach(KeyValuePair tag in mediaTags) if(outputPlugin.SupportedMediaTags.Contains(tag.Key)) diff --git a/DiscImageChef.Core/Sidecar/BlockMedia.cs b/DiscImageChef.Core/Sidecar/BlockMedia.cs index ec555c4e7..ef59b3197 100644 --- a/DiscImageChef.Core/Sidecar/BlockMedia.cs +++ b/DiscImageChef.Core/Sidecar/BlockMedia.cs @@ -53,9 +53,7 @@ namespace DiscImageChef.Core { public partial class Sidecar { - /// - /// Creates a metadata sidecar for a block media (e.g. floppy, hard disk, flash card, usb stick) - /// + /// Creates a metadata sidecar for a block media (e.g. floppy, hard disk, flash card, usb stick) /// Image /// Filter uuid /// Image path @@ -63,30 +61,29 @@ namespace DiscImageChef.Core /// Image plugins /// List of image checksums /// Metadata sidecar - void BlockMedia(IMediaImage image, Guid filterId, string imagePath, FileInfo fi, - PluginBase plugins, + void BlockMedia(IMediaImage image, Guid filterId, string imagePath, FileInfo fi, PluginBase plugins, List imgChecksums, ref CICMMetadataType sidecar, Encoding encoding) { - if(aborted) return; + if(aborted) + return; sidecar.BlockMedia = new[] { new BlockMediaType { - Checksums = imgChecksums.ToArray(), - Image = new ImageType + Checksums = imgChecksums.ToArray(), Image = new ImageType { - format = image.Format, - offset = 0, - offsetSpecified = true, - Value = Path.GetFileName(imagePath) + format = image.Format, offset = 0, offsetSpecified = true, Value = Path.GetFileName(imagePath) }, - Size = (ulong)fi.Length, - Sequence = new SequenceType {MediaTitle = image.Info.MediaTitle} + Size = (ulong)fi.Length, Sequence = new SequenceType + { + MediaTitle = image.Info.MediaTitle + } } }; - if(image.Info.MediaSequence != 0 && image.Info.LastMediaSequence != 0) + if(image.Info.MediaSequence != 0 && + image.Info.LastMediaSequence != 0) { sidecar.BlockMedia[0].Sequence.MediaSequence = (uint)image.Info.MediaSequence; sidecar.BlockMedia[0].Sequence.TotalMedia = (uint)image.Info.LastMediaSequence; @@ -98,9 +95,11 @@ namespace DiscImageChef.Core } UpdateStatus("Hashing media tags..."); + foreach(MediaTagType tagType in image.Info.ReadableMediaTags) { - if(aborted) return; + if(aborted) + return; switch(tagType) { @@ -109,27 +108,28 @@ namespace DiscImageChef.Core { Identify = new DumpType { - Checksums = - Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.ATAPI_IDENTIFY)) - .ToArray(), + Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.ATAPI_IDENTIFY)). + ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.ATAPI_IDENTIFY).Length } }; + break; case MediaTagType.ATA_IDENTIFY: sidecar.BlockMedia[0].ATA = new ATAType { Identify = new DumpType { - Checksums = Checksum - .GetChecksums(image.ReadDiskTag(MediaTagType.ATA_IDENTIFY)) - .ToArray(), + Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.ATA_IDENTIFY)). + ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.ATA_IDENTIFY).Length } }; + break; case MediaTagType.PCMCIA_CIS: byte[] cis = image.ReadDiskTag(MediaTagType.PCMCIA_CIS); + sidecar.BlockMedia[0].PCMCIA = new PCMCIAType { CIS = new DumpType @@ -137,7 +137,9 @@ namespace DiscImageChef.Core Checksums = Checksum.GetChecksums(cis).ToArray(), Size = (ulong)cis.Length } }; + Tuple[] tuples = CIS.GetTuples(cis); + if(tuples != null) foreach(Tuple tuple in tuples) switch(tuple.Code) @@ -150,6 +152,7 @@ namespace DiscImageChef.Core { sidecar.BlockMedia[0].PCMCIA.ManufacturerCode = manfid.ManufacturerID; + sidecar.BlockMedia[0].PCMCIA.CardCode = manfid.CardID; sidecar.BlockMedia[0].PCMCIA.ManufacturerCodeSpecified = true; sidecar.BlockMedia[0].PCMCIA.CardCodeSpecified = true; @@ -163,8 +166,10 @@ namespace DiscImageChef.Core { sidecar.BlockMedia[0].PCMCIA.Manufacturer = vers.Manufacturer; sidecar.BlockMedia[0].PCMCIA.ProductName = vers.Product; + sidecar.BlockMedia[0].PCMCIA.Compliance = $"{vers.MajorVersion}.{vers.MinorVersion}"; + sidecar.BlockMedia[0].PCMCIA.AdditionalInformation = vers.AdditionalInformation; } @@ -178,112 +183,137 @@ namespace DiscImageChef.Core { Inquiry = new DumpType { - Checksums = Checksum - .GetChecksums(image.ReadDiskTag(MediaTagType.SCSI_INQUIRY)) - .ToArray(), + Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SCSI_INQUIRY)). + ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.SCSI_INQUIRY).Length } }; + break; case MediaTagType.SD_CID: if(sidecar.BlockMedia[0].SecureDigital == null) sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType(); + sidecar.BlockMedia[0].SecureDigital.CID = new DumpType { Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_CID)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.SD_CID).Length }; + break; case MediaTagType.SD_CSD: if(sidecar.BlockMedia[0].SecureDigital == null) sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType(); + sidecar.BlockMedia[0].SecureDigital.CSD = new DumpType { Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_CSD)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.SD_CSD).Length }; + break; case MediaTagType.SD_SCR: if(sidecar.BlockMedia[0].SecureDigital == null) sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType(); + sidecar.BlockMedia[0].SecureDigital.SCR = new DumpType { Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_SCR)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.SD_SCR).Length }; + break; case MediaTagType.SD_OCR: if(sidecar.BlockMedia[0].SecureDigital == null) sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType(); + sidecar.BlockMedia[0].SecureDigital.OCR = new DumpType { Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_OCR)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.SD_OCR).Length }; + break; case MediaTagType.MMC_CID: if(sidecar.BlockMedia[0].MultiMediaCard == null) sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType(); + sidecar.BlockMedia[0].MultiMediaCard.CID = new DumpType { Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_CID)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.SD_CID).Length }; + break; case MediaTagType.MMC_CSD: if(sidecar.BlockMedia[0].MultiMediaCard == null) sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType(); + sidecar.BlockMedia[0].MultiMediaCard.CSD = new DumpType { Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_CSD)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.SD_CSD).Length }; + break; case MediaTagType.MMC_OCR: if(sidecar.BlockMedia[0].MultiMediaCard == null) sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType(); + sidecar.BlockMedia[0].MultiMediaCard.OCR = new DumpType { Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_OCR)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.SD_OCR).Length }; + break; case MediaTagType.MMC_ExtendedCSD: if(sidecar.BlockMedia[0].MultiMediaCard == null) sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType(); + sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD = new DumpType { - Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.MMC_ExtendedCSD)) - .ToArray(), + Checksums = + Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.MMC_ExtendedCSD)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.MMC_ExtendedCSD).Length }; + break; case MediaTagType.USB_Descriptors: - if(sidecar.BlockMedia[0].USB == null) sidecar.BlockMedia[0].USB = new USBType(); + if(sidecar.BlockMedia[0].USB == null) + sidecar.BlockMedia[0].USB = new USBType(); + sidecar.BlockMedia[0].USB.Descriptors = new DumpType { - Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.USB_Descriptors)) - .ToArray(), + Checksums = + Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.USB_Descriptors)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.USB_Descriptors).Length }; + break; case MediaTagType.SCSI_MODESENSE_6: - if(sidecar.BlockMedia[0].SCSI == null) sidecar.BlockMedia[0].SCSI = new SCSIType(); + if(sidecar.BlockMedia[0].SCSI == null) + sidecar.BlockMedia[0].SCSI = new SCSIType(); + sidecar.BlockMedia[0].SCSI.ModeSense = new DumpType { - Checksums = - Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_6)).ToArray(), + Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_6)). + ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_6).Length }; + break; case MediaTagType.SCSI_MODESENSE_10: - if(sidecar.BlockMedia[0].SCSI == null) sidecar.BlockMedia[0].SCSI = new SCSIType(); + if(sidecar.BlockMedia[0].SCSI == null) + sidecar.BlockMedia[0].SCSI = new SCSIType(); + sidecar.BlockMedia[0].SCSI.ModeSense10 = new DumpType { - Checksums = - Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_10)).ToArray(), + Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_10)). + ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_10).Length }; + break; } } @@ -296,7 +326,7 @@ namespace DiscImageChef.Core { UpdateStatus("Hashing sectors..."); - Checksum contentChkWorker = new Checksum(); + var contentChkWorker = new Checksum(); // For fast debugging, skip checksum //goto skipImageChecksum; @@ -306,11 +336,13 @@ namespace DiscImageChef.Core ulong doneSectors = 0; InitProgress2(); + while(doneSectors < sectors) { if(aborted) { EndProgress2(); + return; } @@ -342,29 +374,30 @@ namespace DiscImageChef.Core EndProgress2(); } - MediaType.MediaTypeToString(image.Info.MediaType, out string dskType, out string dskSubType); - sidecar.BlockMedia[0].DiskType = dskType; - sidecar.BlockMedia[0].DiskSubType = dskSubType; + (string type, string subType) diskType = MediaType.MediaTypeToString(image.Info.MediaType); + sidecar.BlockMedia[0].DiskType = diskType.type; + sidecar.BlockMedia[0].DiskSubType = diskType.subType; Statistics.AddMedia(image.Info.MediaType, false); sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(image.Info.MediaType); sidecar.BlockMedia[0].LogicalBlocks = image.Info.Sectors; sidecar.BlockMedia[0].LogicalBlockSize = image.Info.SectorSize; + // TODO: Detect it sidecar.BlockMedia[0].PhysicalBlockSize = image.Info.SectorSize; - if(image is ITapeImage tapeImage && tapeImage.IsTape) + if(image is ITapeImage tapeImage && + tapeImage.IsTape) { List tapePartitions = new List(); + foreach(TapePartition tapePartition in tapeImage.TapePartitions) { - TapePartitionType thisPartition = new TapePartitionType + var thisPartition = new TapePartitionType { - Image = sidecar.BlockMedia[0].Image, - Sequence = tapePartition.Number, - StartBlock = tapePartition.FirstBlock, - EndBlock = tapePartition.LastBlock + Image = sidecar.BlockMedia[0].Image, Sequence = tapePartition.Number, + StartBlock = tapePartition.FirstBlock, EndBlock = tapePartition.LastBlock }; if(tapeImage.TapePartitions.Count == 1) @@ -373,9 +406,10 @@ namespace DiscImageChef.Core { UpdateStatus($"Hashing partition {tapePartition.Number}..."); - if(aborted) return; + if(aborted) + return; - Checksum tapePartitionChk = new Checksum(); + var tapePartitionChk = new Checksum(); // For fast debugging, skip checksum //goto skipImageChecksum; @@ -385,11 +419,13 @@ namespace DiscImageChef.Core ulong doneSectors = 0; InitProgress2(); + while(doneSectors < sectors) { if(aborted) { EndProgress2(); + return; } @@ -405,6 +441,7 @@ namespace DiscImageChef.Core { sector = image.ReadSectors(tapePartition.FirstBlock + doneSectors, (uint)(sectors - doneSectors)); + UpdateProgress2("Hashing blocks {0} of {1}", (long)doneSectors, (long)sectors); doneSectors += sectors - doneSectors; } @@ -428,14 +465,11 @@ namespace DiscImageChef.Core foreach(TapeFile tapeFile in tapeImage.Files.Where(f => f.Partition == tapePartition.Number)) { - TapeFileType thisFile = new TapeFileType + var thisFile = new TapeFileType { - Sequence = tapeFile.File, - StartBlock = tapeFile.FirstBlock, - EndBlock = tapeFile.LastBlock, - Image = sidecar.BlockMedia[0].Image, - Size = 0, - BlockSize = 0 + Sequence = tapeFile.File, StartBlock = tapeFile.FirstBlock, + EndBlock = tapeFile.LastBlock, + Image = sidecar.BlockMedia[0].Image, Size = 0, BlockSize = 0 }; if(tapeImage.Files.Count(f => f.Partition == tapePartition.Number) == 1) @@ -447,9 +481,10 @@ namespace DiscImageChef.Core { UpdateStatus($"Hashing file {tapeFile.File}..."); - if(aborted) return; + if(aborted) + return; - Checksum tapeFileChk = new Checksum(); + var tapeFileChk = new Checksum(); // For fast debugging, skip checksum //goto skipImageChecksum; @@ -459,11 +494,13 @@ namespace DiscImageChef.Core ulong doneSectors = 0; InitProgress2(); + while(doneSectors < sectors) { if(aborted) { EndProgress2(); + return; } @@ -479,6 +516,7 @@ namespace DiscImageChef.Core { sector = image.ReadSectors(tapeFile.FirstBlock + doneSectors, (uint)(sectors - doneSectors)); + UpdateProgress2("Hashing blocks {0} of {1}", (long)doneSectors, (long)sectors); doneSectors += sectors - doneSectors; } @@ -513,36 +551,40 @@ namespace DiscImageChef.Core UpdateStatus("Checking filesystems..."); - if(aborted) return; + if(aborted) + return; List partitions = Partitions.GetAll(image); Partitions.AddSchemesToStats(partitions); sidecar.BlockMedia[0].FileSystemInformation = new PartitionType[1]; + if(partitions.Count > 0) { sidecar.BlockMedia[0].FileSystemInformation = new PartitionType[partitions.Count]; + for(int i = 0; i < partitions.Count; i++) { - if(aborted) return; + if(aborted) + return; sidecar.BlockMedia[0].FileSystemInformation[i] = new PartitionType { - Description = partitions[i].Description, - EndSector = partitions[i].End, - Name = partitions[i].Name, - Sequence = (uint)partitions[i].Sequence, - StartSector = partitions[i].Start, - Type = partitions[i].Type + Description = partitions[i].Description, EndSector = partitions[i].End, + Name = partitions[i].Name, Sequence = (uint)partitions[i].Sequence, + StartSector = partitions[i].Start, Type = partitions[i].Type }; + List lstFs = new List(); foreach(IFilesystem plugin in plugins.PluginsList.Values) try { - if(aborted) return; + if(aborted) + return; - if(!plugin.Identify(image, partitions[i])) continue; + if(!plugin.Identify(image, partitions[i])) + continue; if(plugin is IReadOnlyFilesystem fsPlugin && fsPlugin.Mount(image, partitions[i], encoding, null, null) == Errno.NoError) @@ -553,7 +595,8 @@ namespace DiscImageChef.Core fsPlugin.Unmount(); } - else plugin.GetInformation(image, partitions[i], out _, encoding); + else + plugin.GetInformation(image, partitions[i], out _, encoding); lstFs.Add(plugin.XmlFsType); Statistics.AddFilesystem(plugin.XmlFsType.Type); @@ -565,21 +608,24 @@ namespace DiscImageChef.Core //DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name); } - if(lstFs.Count > 0) sidecar.BlockMedia[0].FileSystemInformation[i].FileSystems = lstFs.ToArray(); + if(lstFs.Count > 0) + sidecar.BlockMedia[0].FileSystemInformation[i].FileSystems = lstFs.ToArray(); } } else { - if(aborted) return; + if(aborted) + return; - sidecar.BlockMedia[0].FileSystemInformation[0] = - new PartitionType {StartSector = 0, EndSector = image.Info.Sectors - 1}; - - Partition wholePart = new Partition + sidecar.BlockMedia[0].FileSystemInformation[0] = new PartitionType { - Name = "Whole device", - Length = image.Info.Sectors, - Size = image.Info.Sectors * image.Info.SectorSize + StartSector = 0, EndSector = image.Info.Sectors - 1 + }; + + var wholePart = new Partition + { + Name = "Whole device", Length = image.Info.Sectors, + Size = image.Info.Sectors * image.Info.SectorSize }; List lstFs = new List(); @@ -587,9 +633,11 @@ namespace DiscImageChef.Core foreach(IFilesystem plugin in plugins.PluginsList.Values) try { - if(aborted) return; + if(aborted) + return; - if(!plugin.Identify(image, wholePart)) continue; + if(!plugin.Identify(image, wholePart)) + continue; if(plugin is IReadOnlyFilesystem fsPlugin && fsPlugin.Mount(image, wholePart, encoding, null, null) == Errno.NoError) @@ -600,7 +648,8 @@ namespace DiscImageChef.Core fsPlugin.Unmount(); } - else plugin.GetInformation(image, wholePart, out _, encoding); + else + plugin.GetInformation(image, wholePart, out _, encoding); lstFs.Add(plugin.XmlFsType); Statistics.AddFilesystem(plugin.XmlFsType.Type); @@ -612,12 +661,15 @@ namespace DiscImageChef.Core //DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name); } - if(lstFs.Count > 0) sidecar.BlockMedia[0].FileSystemInformation[0].FileSystems = lstFs.ToArray(); + if(lstFs.Count > 0) + sidecar.BlockMedia[0].FileSystemInformation[0].FileSystems = lstFs.ToArray(); } UpdateStatus("Saving metadata..."); - if(image.Info.Cylinders > 0 && image.Info.Heads > 0 && image.Info.SectorsPerTrack > 0) + if(image.Info.Cylinders > 0 && + image.Info.Heads > 0 && + image.Info.SectorsPerTrack > 0) { sidecar.BlockMedia[0].CylindersSpecified = true; sidecar.BlockMedia[0].HeadsSpecified = true; @@ -630,8 +682,10 @@ namespace DiscImageChef.Core if(image.Info.ReadableMediaTags.Contains(MediaTagType.ATA_IDENTIFY)) { Identify.IdentifyDevice? ataId = Identify.Decode(image.ReadDiskTag(MediaTagType.ATA_IDENTIFY)); + if(ataId.HasValue) - if(ataId.Value.CurrentCylinders > 0 && ataId.Value.CurrentHeads > 0 && + if(ataId.Value.CurrentCylinders > 0 && + ataId.Value.CurrentHeads > 0 && ataId.Value.CurrentSectorsPerTrack > 0) { sidecar.BlockMedia[0].CylindersSpecified = true; @@ -641,7 +695,9 @@ namespace DiscImageChef.Core sidecar.BlockMedia[0].Heads = ataId.Value.CurrentHeads; sidecar.BlockMedia[0].SectorsPerTrack = ataId.Value.CurrentSectorsPerTrack; } - else if(ataId.Value.Cylinders > 0 && ataId.Value.Heads > 0 && ataId.Value.SectorsPerTrack > 0) + else if(ataId.Value.Cylinders > 0 && + ataId.Value.Heads > 0 && + ataId.Value.SectorsPerTrack > 0) { sidecar.BlockMedia[0].CylindersSpecified = true; sidecar.BlockMedia[0].HeadsSpecified = true; @@ -652,7 +708,8 @@ namespace DiscImageChef.Core } } - if(image.DumpHardware != null) sidecar.BlockMedia[0].DumpHardwareArray = image.DumpHardware.ToArray(); + if(image.DumpHardware != null) + sidecar.BlockMedia[0].DumpHardwareArray = image.DumpHardware.ToArray(); // TODO: This is more of a hack, redo it planned for >4.0 string trkFormat = null; @@ -662,17 +719,21 @@ namespace DiscImageChef.Core case CommonTypes.MediaType.Apple32SS: case CommonTypes.MediaType.Apple32DS: trkFormat = "Apple GCR (DOS 3.2)"; + break; case CommonTypes.MediaType.Apple33SS: case CommonTypes.MediaType.Apple33DS: trkFormat = "Apple GCR (DOS 3.3)"; + break; case CommonTypes.MediaType.AppleSonySS: case CommonTypes.MediaType.AppleSonyDS: trkFormat = "Apple GCR (Sony)"; + break; case CommonTypes.MediaType.AppleFileWare: trkFormat = "Apple GCR (Twiggy)"; + break; case CommonTypes.MediaType.DOS_525_SS_DD_9: case CommonTypes.MediaType.DOS_525_DS_DD_8: @@ -720,6 +781,7 @@ namespace DiscImageChef.Core case CommonTypes.MediaType.Apricot_35: case CommonTypes.MediaType.CompactFloppy: trkFormat = "IBM MFM"; + break; case CommonTypes.MediaType.ATARI_525_SD: case CommonTypes.MediaType.NEC_8_SD: @@ -733,18 +795,22 @@ namespace DiscImageChef.Core case CommonTypes.MediaType.IBM43FD_128: case CommonTypes.MediaType.IBM43FD_256: trkFormat = "IBM FM"; + break; case CommonTypes.MediaType.CBM_35_DD: trkFormat = "Commodore MFM"; + break; case CommonTypes.MediaType.CBM_AMIGA_35_HD: case CommonTypes.MediaType.CBM_AMIGA_35_DD: trkFormat = "Amiga MFM"; + break; case CommonTypes.MediaType.CBM_1540: case CommonTypes.MediaType.CBM_1540_Ext: case CommonTypes.MediaType.CBM_1571: trkFormat = "Commodore GCR"; + break; case CommonTypes.MediaType.SHARP_525_9: case CommonTypes.MediaType.SHARP_35_9: break; @@ -752,6 +818,7 @@ namespace DiscImageChef.Core case CommonTypes.MediaType.ECMA_99_26: case CommonTypes.MediaType.ECMA_99_8: trkFormat = "ISO MFM"; + break; case CommonTypes.MediaType.ECMA_54: case CommonTypes.MediaType.ECMA_59: @@ -763,9 +830,11 @@ namespace DiscImageChef.Core case CommonTypes.MediaType.ECMA_78: case CommonTypes.MediaType.ECMA_78_2: trkFormat = "ISO FM"; + break; default: trkFormat = "Unknown"; + break; } @@ -773,22 +842,27 @@ namespace DiscImageChef.Core string scpFilePath = Path.Combine(Path.GetDirectoryName(imagePath), Path.GetFileNameWithoutExtension(imagePath) + ".scp"); - if(aborted) return; + if(aborted) + return; if(File.Exists(scpFilePath)) { UpdateStatus("Hashing SuperCardPro image..."); - SuperCardPro scpImage = new SuperCardPro(); - ZZZNoFilter scpFilter = new ZZZNoFilter(); + var scpImage = new SuperCardPro(); + var scpFilter = new ZZZNoFilter(); scpFilter.Open(scpFilePath); - if(image.Info.Heads <= 2 && scpImage.Identify(scpFilter)) + if(image.Info.Heads <= 2 && + scpImage.Identify(scpFilter)) { - try { scpImage.Open(scpFilter); } + try + { + scpImage.Open(scpFilter); + } catch(NotImplementedException) { } - if(image.Info.Heads == 2 && scpImage.Header.heads == 0 || image.Info.Heads == 1 && - (scpImage.Header.heads == 1 || scpImage.Header.heads == 2)) + if(image.Info.Heads == 2 && scpImage.Header.heads == 0 || + image.Info.Heads == 1 && (scpImage.Header.heads == 1 || scpImage.Header.heads == 2)) if(scpImage.Header.end + 1 >= image.Info.Cylinders) { List scpBlockTrackTypes = new List(); @@ -797,18 +871,17 @@ namespace DiscImageChef.Core for(byte t = scpImage.Header.start; t <= scpImage.Header.end; t++) { - if(aborted) return; + if(aborted) + return; - BlockTrackType scpBlockTrackType = new BlockTrackType + var scpBlockTrackType = new BlockTrackType { - Cylinder = t / image.Info.Heads, - Head = (ushort)(t % image.Info.Heads), - Image = new ImageType - { - format = scpImage.Format, - Value = Path.GetFileName(scpFilePath), - offset = scpImage.Header.offsets[t] - } + Cylinder = t / image.Info.Heads, Head = (ushort)(t % image.Info.Heads), Image = + new ImageType + { + format = scpImage.Format, Value = Path.GetFileName(scpFilePath), + offset = scpImage.Header.offsets[t] + } }; if(scpBlockTrackType.Cylinder < image.Info.Cylinders) @@ -826,6 +899,7 @@ namespace DiscImageChef.Core byte[] trackContents = new byte[scpTrack.Entries.Last().dataOffset + scpTrack.Entries.Last().trackLength - scpImage.Header.offsets[t] + 1]; + scpStream.Position = scpImage.Header.offsets[t]; scpStream.Read(trackContents, 0, trackContents.Length); scpBlockTrackType.Size = (ulong)trackContents.Length; @@ -839,12 +913,12 @@ namespace DiscImageChef.Core scpBlockTrackTypes.OrderBy(t => t.Cylinder).ThenBy(t => t.Head).ToArray(); } else - DicConsole - .ErrorWriteLine("SuperCardPro image do not contain same number of tracks ({0}) than disk image ({1}), ignoring...", + DicConsole. + ErrorWriteLine("SuperCardPro image do not contain same number of tracks ({0}) than disk image ({1}), ignoring...", scpImage.Header.end + 1, image.Info.Cylinders); else - DicConsole - .ErrorWriteLine("SuperCardPro image do not contain same number of heads ({0}) than disk image ({1}), ignoring...", + DicConsole. + ErrorWriteLine("SuperCardPro image do not contain same number of heads ({0}) than disk image ({1}), ignoring...", 2, image.Info.Heads); } } @@ -852,33 +926,45 @@ namespace DiscImageChef.Core #region KryoFlux string kfFile = null; + string basename = Path.Combine(Path.GetDirectoryName(imagePath), Path.GetFileNameWithoutExtension(imagePath)); + bool kfDir = false; - if(aborted) return; + + if(aborted) + return; if(Directory.Exists(basename)) { string[] possibleKfStarts = Directory.GetFiles(basename, "*.raw", SearchOption.TopDirectoryOnly); + if(possibleKfStarts.Length > 0) { kfFile = possibleKfStarts[0]; kfDir = true; } } - else if(File.Exists(basename + "00.0.raw")) kfFile = basename + "00.0.raw"; - else if(File.Exists(basename + "00.1.raw")) kfFile = basename + "00.1.raw"; + else if(File.Exists(basename + "00.0.raw")) + kfFile = basename + "00.0.raw"; + else if(File.Exists(basename + "00.1.raw")) + kfFile = basename + "00.1.raw"; if(kfFile != null) { UpdateStatus("Hashing KryoFlux images..."); - KryoFlux kfImage = new KryoFlux(); - ZZZNoFilter kfFilter = new ZZZNoFilter(); + var kfImage = new KryoFlux(); + var kfFilter = new ZZZNoFilter(); kfFilter.Open(kfFile); - if(image.Info.Heads <= 2 && kfImage.Identify(kfFilter)) + + if(image.Info.Heads <= 2 && + kfImage.Identify(kfFilter)) { - try { kfImage.Open(kfFilter); } + try + { + kfImage.Open(kfFilter); + } catch(NotImplementedException) { } if(kfImage.Info.Heads == image.Info.Heads) @@ -890,20 +976,19 @@ namespace DiscImageChef.Core foreach(KeyValuePair kvp in kfImage.tracks) { - if(aborted) return; + if(aborted) + return; - BlockTrackType kfBlockTrackType = new BlockTrackType + var kfBlockTrackType = new BlockTrackType { - Cylinder = kvp.Key / image.Info.Heads, - Head = (ushort)(kvp.Key % image.Info.Heads), + Cylinder = kvp.Key / image.Info.Heads, Head = (ushort)(kvp.Key % image.Info.Heads), Image = new ImageType { format = kfImage.Format, Value = kfDir - ? Path - .Combine(Path.GetFileName(Path.GetDirectoryName(kvp.Value.GetBasePath())), - kvp.Value.GetFilename()) - : kvp.Value.GetFilename(), + ? Path. + Combine(Path.GetFileName(Path.GetDirectoryName(kvp.Value.GetBasePath())), + kvp.Value.GetFilename()) : kvp.Value.GetFilename(), offset = 0 } }; @@ -932,12 +1017,12 @@ namespace DiscImageChef.Core kfBlockTrackTypes.OrderBy(t => t.Cylinder).ThenBy(t => t.Head).ToArray(); } else - DicConsole - .ErrorWriteLine("KryoFlux image do not contain same number of tracks ({0}) than disk image ({1}), ignoring...", + DicConsole. + ErrorWriteLine("KryoFlux image do not contain same number of tracks ({0}) than disk image ({1}), ignoring...", kfImage.Info.Cylinders, image.Info.Cylinders); else - DicConsole - .ErrorWriteLine("KryoFluximage do not contain same number of heads ({0}) than disk image ({1}), ignoring...", + DicConsole. + ErrorWriteLine("KryoFluximage do not contain same number of heads ({0}) than disk image ({1}), ignoring...", kfImage.Info.Heads, image.Info.Heads); } } @@ -947,16 +1032,23 @@ namespace DiscImageChef.Core string dfiFilePath = Path.Combine(Path.GetDirectoryName(imagePath), Path.GetFileNameWithoutExtension(imagePath) + ".dfi"); - if(aborted) return; - if(!File.Exists(dfiFilePath)) return; + if(aborted) + return; - DiscFerret dfiImage = new DiscFerret(); - ZZZNoFilter dfiFilter = new ZZZNoFilter(); + if(!File.Exists(dfiFilePath)) + return; + + var dfiImage = new DiscFerret(); + var dfiFilter = new ZZZNoFilter(); dfiFilter.Open(dfiFilePath); - if(!dfiImage.Identify(dfiFilter)) return; + if(!dfiImage.Identify(dfiFilter)) + return; - try { dfiImage.Open(dfiFilter); } + try + { + dfiImage.Open(dfiFilter); + } catch(NotImplementedException) { } UpdateStatus("Hashing DiscFerret image..."); @@ -970,13 +1062,16 @@ namespace DiscImageChef.Core foreach(int t in dfiImage.TrackOffsets.Keys) { - if(aborted) return; + if(aborted) + return; - BlockTrackType dfiBlockTrackType = new BlockTrackType + var dfiBlockTrackType = new BlockTrackType { - Cylinder = (uint)(t / image.Info.Heads), - Head = (ushort)(t % image.Info.Heads), - Image = new ImageType {format = dfiImage.Format, Value = Path.GetFileName(dfiFilePath)} + Cylinder = (uint)(t / image.Info.Heads), Head = (ushort)(t % image.Info.Heads), Image = + new ImageType + { + format = dfiImage.Format, Value = Path.GetFileName(dfiFilePath) + } }; if(dfiBlockTrackType.Cylinder < image.Info.Cylinders) @@ -1007,12 +1102,12 @@ namespace DiscImageChef.Core dfiBlockTrackTypes.OrderBy(t => t.Cylinder).ThenBy(t => t.Head).ToArray(); } else - DicConsole - .ErrorWriteLine("DiscFerret image do not contain same number of tracks ({0}) than disk image ({1}), ignoring...", + DicConsole. + ErrorWriteLine("DiscFerret image do not contain same number of tracks ({0}) than disk image ({1}), ignoring...", dfiImage.Info.Cylinders, image.Info.Cylinders); else - DicConsole - .ErrorWriteLine("DiscFerret image do not contain same number of heads ({0}) than disk image ({1}), ignoring...", + DicConsole. + ErrorWriteLine("DiscFerret image do not contain same number of heads ({0}) than disk image ({1}), ignoring...", dfiImage.Info.Heads, image.Info.Heads); #endregion diff --git a/DiscImageChef.Core/Sidecar/OpticalDisc.cs b/DiscImageChef.Core/Sidecar/OpticalDisc.cs index 6234c1b6d..cad21233f 100644 --- a/DiscImageChef.Core/Sidecar/OpticalDisc.cs +++ b/DiscImageChef.Core/Sidecar/OpticalDisc.cs @@ -52,9 +52,7 @@ namespace DiscImageChef.Core { public partial class Sidecar { - /// - /// Creates a metadata sidecar for an optical disc (e.g. CD, DVD, GD, BD, XGD, GOD) - /// + /// Creates a metadata sidecar for an optical disc (e.g. CD, DVD, GD, BD, XGD, GOD) /// Image /// Filter uuid /// Image path @@ -62,30 +60,29 @@ namespace DiscImageChef.Core /// Image plugins /// List of image checksums /// Metadata sidecar - void OpticalDisc(IOpticalMediaImage image, Guid filterId, string imagePath, - FileInfo fi, PluginBase plugins, + void OpticalDisc(IOpticalMediaImage image, Guid filterId, string imagePath, FileInfo fi, PluginBase plugins, List imgChecksums, ref CICMMetadataType sidecar, Encoding encoding) { - if(aborted) return; + if(aborted) + return; sidecar.OpticalDisc = new[] { new OpticalDiscType { - Checksums = imgChecksums.ToArray(), - Image = new ImageType + Checksums = imgChecksums.ToArray(), Image = new ImageType { - format = image.Format, - offset = 0, - offsetSpecified = true, - Value = Path.GetFileName(imagePath) + format = image.Format, offset = 0, offsetSpecified = true, Value = Path.GetFileName(imagePath) }, - Size = (ulong)fi.Length, - Sequence = new SequenceType {MediaTitle = image.Info.MediaTitle} + Size = (ulong)fi.Length, Sequence = new SequenceType + { + MediaTitle = image.Info.MediaTitle + } } }; - if(image.Info.MediaSequence != 0 && image.Info.LastMediaSequence != 0) + if(image.Info.MediaSequence != 0 && + image.Info.LastMediaSequence != 0) { sidecar.OpticalDisc[0].Sequence.MediaSequence = (uint)image.Info.MediaSequence; sidecar.OpticalDisc[0].Sequence.TotalMedia = (uint)image.Info.LastMediaSequence; @@ -99,9 +96,11 @@ namespace DiscImageChef.Core MediaType dskType = image.Info.MediaType; UpdateStatus("Hashing media tags..."); + foreach(MediaTagType tagType in image.Info.ReadableMediaTags) { - if(aborted) return; + if(aborted) + return; switch(tagType) { @@ -112,10 +111,15 @@ namespace DiscImageChef.Core Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_ATIP)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.CD_ATIP).Length }; + ATIP.CDATIP? atip = ATIP.Decode(image.ReadDiskTag(MediaTagType.CD_ATIP)); + if(atip.HasValue) - if(atip.Value.DDCD) dskType = atip.Value.DiscType ? MediaType.DDCDRW : MediaType.DDCDR; - else dskType = atip.Value.DiscType ? MediaType.CDRW : MediaType.CDR; + if(atip.Value.DDCD) + dskType = atip.Value.DiscType ? MediaType.DDCDRW : MediaType.DDCDR; + else + dskType = atip.Value.DiscType ? MediaType.CDRW : MediaType.CDR; + break; case MediaTagType.DVD_BCA: sidecar.OpticalDisc[0].BCA = new DumpType @@ -124,6 +128,7 @@ namespace DiscImageChef.Core Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_BCA)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.DVD_BCA).Length }; + break; case MediaTagType.BD_BCA: sidecar.OpticalDisc[0].BCA = new DumpType @@ -132,6 +137,7 @@ namespace DiscImageChef.Core Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.BD_BCA)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.BD_BCA).Length }; + break; case MediaTagType.DVD_CMI: sidecar.OpticalDisc[0].CMI = new DumpType @@ -140,19 +146,24 @@ namespace DiscImageChef.Core Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_CMI)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.DVD_CMI).Length }; + CSS_CPRM.LeadInCopyright? cmi = CSS_CPRM.DecodeLeadInCopyright(image.ReadDiskTag(MediaTagType.DVD_CMI)); + if(cmi.HasValue) switch(cmi.Value.CopyrightType) { case CopyrightType.AACS: sidecar.OpticalDisc[0].CopyProtection = "AACS"; + break; case CopyrightType.CSS: sidecar.OpticalDisc[0].CopyProtection = "CSS"; + break; case CopyrightType.CPRM: sidecar.OpticalDisc[0].CopyProtection = "CPRM"; + break; } @@ -164,15 +175,24 @@ namespace DiscImageChef.Core Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_DMI)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.DVD_DMI).Length }; + if(DMI.IsXbox(image.ReadDiskTag(MediaTagType.DVD_DMI))) { - dskType = MediaType.XGD; - sidecar.OpticalDisc[0].Dimensions = new DimensionsType {Diameter = 120, Thickness = 1.2}; + dskType = MediaType.XGD; + + sidecar.OpticalDisc[0].Dimensions = new DimensionsType + { + Diameter = 120, Thickness = 1.2 + }; } else if(DMI.IsXbox360(image.ReadDiskTag(MediaTagType.DVD_DMI))) { - dskType = MediaType.XGD2; - sidecar.OpticalDisc[0].Dimensions = new DimensionsType {Diameter = 120, Thickness = 1.2}; + dskType = MediaType.XGD2; + + sidecar.OpticalDisc[0].Dimensions = new DimensionsType + { + Diameter = 120, Thickness = 1.2 + }; } break; @@ -183,65 +203,91 @@ namespace DiscImageChef.Core Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_PFI)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.DVD_PFI).Length }; + PFI.PhysicalFormatInformation? pfi = PFI.Decode(image.ReadDiskTag(MediaTagType.DVD_PFI)); + if(pfi.HasValue) - if(dskType != MediaType.XGD && dskType != MediaType.XGD2 && + if(dskType != MediaType.XGD && + dskType != MediaType.XGD2 && dskType != MediaType.XGD3 && - dskType != MediaType.PS2DVD && dskType != MediaType.PS3DVD && dskType != MediaType.Nuon) + dskType != MediaType.PS2DVD && + dskType != MediaType.PS3DVD && + dskType != MediaType.Nuon) { switch(pfi.Value.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 = MediaType.DVDR; + break; case DiskCategory.DVDRAM: dskType = MediaType.DVDRAM; + break; case DiskCategory.DVDROM: dskType = MediaType.DVDROM; + break; case DiskCategory.DVDRW: dskType = 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 = MediaType.GOD; + break; case DiskCategory.UMD: dskType = MediaType.UMD; + break; } - if(dskType == MediaType.DVDR && pfi.Value.PartVersion == 6) dskType = MediaType.DVDRDL; - if(dskType == MediaType.DVDRW && pfi.Value.PartVersion == 3) + if(dskType == MediaType.DVDR && + pfi.Value.PartVersion == 6) + dskType = MediaType.DVDRDL; + + if(dskType == MediaType.DVDRW && + pfi.Value.PartVersion == 3) dskType = MediaType.DVDRWDL; - if(dskType == MediaType.GOD && pfi.Value.DiscSize == DVDSize.OneTwenty) + + if(dskType == MediaType.GOD && + pfi.Value.DiscSize == DVDSize.OneTwenty) dskType = MediaType.WOD; sidecar.OpticalDisc[0].Dimensions = new DimensionsType(); + if(dskType == MediaType.UMD) { sidecar.OpticalDisc[0].Dimensions.Height = 64; @@ -257,11 +303,13 @@ namespace DiscImageChef.Core sidecar.OpticalDisc[0].Dimensions.Diameter = 80; sidecar.OpticalDisc[0].Dimensions.DiameterSpecified = true; sidecar.OpticalDisc[0].Dimensions.Thickness = 1.2; + break; case DVDSize.OneTwenty: sidecar.OpticalDisc[0].Dimensions.Diameter = 120; sidecar.OpticalDisc[0].Dimensions.DiameterSpecified = true; sidecar.OpticalDisc[0].Dimensions.Thickness = 1.2; + break; } } @@ -274,6 +322,7 @@ namespace DiscImageChef.Core Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_PMA)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.CD_PMA).Length }; + break; case MediaTagType.CD_FullTOC: sidecar.OpticalDisc[0].TOC = new DumpType @@ -282,6 +331,7 @@ namespace DiscImageChef.Core Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_FullTOC)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.CD_FullTOC).Length }; + break; case MediaTagType.CD_FirstTrackPregap: sidecar.OpticalDisc[0].FirstTrackPregrap = new[] @@ -289,42 +339,39 @@ namespace DiscImageChef.Core new BorderType { Image = Path.GetFileName(imagePath), - Checksums = Checksum - .GetChecksums(image.ReadDiskTag(MediaTagType.CD_FirstTrackPregap)) - .ToArray(), + Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_FirstTrackPregap)). + ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.CD_FirstTrackPregap).Length } }; + break; case MediaTagType.CD_LeadIn: sidecar.OpticalDisc[0].LeadIn = new[] { new BorderType { - Image = Path.GetFileName(imagePath), - Checksums = Checksum - .GetChecksums(image.ReadDiskTag(MediaTagType.CD_LeadIn)).ToArray(), - Size = (ulong)image.ReadDiskTag(MediaTagType.CD_LeadIn).Length + Image = Path.GetFileName(imagePath), + Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_LeadIn)).ToArray(), + Size = (ulong)image.ReadDiskTag(MediaTagType.CD_LeadIn).Length } }; + break; case MediaTagType.Xbox_SecuritySector: - if(sidecar.OpticalDisc[0].Xbox == null) sidecar.OpticalDisc[0].Xbox = new XboxType(); + if(sidecar.OpticalDisc[0].Xbox == null) + sidecar.OpticalDisc[0].Xbox = new XboxType(); sidecar.OpticalDisc[0].Xbox.SecuritySectors = new[] { new XboxSecuritySectorsType { - RequestNumber = 0, - RequestVersion = 1, - SecuritySectors = new DumpType + RequestNumber = 0, RequestVersion = 1, SecuritySectors = new DumpType { Image = Path.GetFileName(imagePath), - Checksums = - Checksum - .GetChecksums(image.ReadDiskTag(MediaTagType - .Xbox_SecuritySector)) - .ToArray(), + Checksums = Checksum. + GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_SecuritySector)). + ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.Xbox_SecuritySector).Length } } @@ -332,7 +379,8 @@ namespace DiscImageChef.Core break; case MediaTagType.Xbox_PFI: - if(sidecar.OpticalDisc[0].Xbox == null) sidecar.OpticalDisc[0].Xbox = new XboxType(); + if(sidecar.OpticalDisc[0].Xbox == null) + sidecar.OpticalDisc[0].Xbox = new XboxType(); sidecar.OpticalDisc[0].Xbox.PFI = new DumpType { @@ -340,9 +388,11 @@ namespace DiscImageChef.Core Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_PFI)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.Xbox_PFI).Length }; + break; case MediaTagType.Xbox_DMI: - if(sidecar.OpticalDisc[0].Xbox == null) sidecar.OpticalDisc[0].Xbox = new XboxType(); + if(sidecar.OpticalDisc[0].Xbox == null) + sidecar.OpticalDisc[0].Xbox = new XboxType(); sidecar.OpticalDisc[0].Xbox.DMI = new DumpType { @@ -350,6 +400,7 @@ namespace DiscImageChef.Core Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_DMI)).ToArray(), Size = (ulong)image.ReadDiskTag(MediaTagType.Xbox_DMI).Length }; + break; } } @@ -359,10 +410,14 @@ namespace DiscImageChef.Core List sessions = image.Sessions; sidecar.OpticalDisc[0].Sessions = (uint)(sessions?.Count ?? 1); } - catch { sidecar.OpticalDisc[0].Sessions = 1; } + catch + { + sidecar.OpticalDisc[0].Sessions = 1; + } List tracks = image.Tracks; List trksLst = null; + if(tracks != null) { sidecar.OpticalDisc[0].Tracks = new uint[1]; @@ -370,10 +425,12 @@ namespace DiscImageChef.Core trksLst = new List(); } - if(sidecar.OpticalDisc[0].Dimensions == null && image.Info.MediaType != MediaType.Unknown) + if(sidecar.OpticalDisc[0].Dimensions == null && + image.Info.MediaType != MediaType.Unknown) sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(image.Info.MediaType); - if(aborted) return; + if(aborted) + return; InitProgress(); @@ -382,78 +439,100 @@ namespace DiscImageChef.Core Partitions.AddSchemesToStats(partitions); UpdateStatus("Hashing tracks..."); + foreach(Track trk in tracks) { if(aborted) { EndProgress(); + return; } - TrackType xmlTrk = new TrackType(); + var xmlTrk = new TrackType(); + switch(trk.TrackType) { case CommonTypes.Enums.TrackType.Audio: xmlTrk.TrackType1 = TrackTypeTrackType.audio; + break; case CommonTypes.Enums.TrackType.CdMode2Form2: xmlTrk.TrackType1 = TrackTypeTrackType.m2f2; + break; case CommonTypes.Enums.TrackType.CdMode2Formless: xmlTrk.TrackType1 = TrackTypeTrackType.mode2; + break; case CommonTypes.Enums.TrackType.CdMode2Form1: xmlTrk.TrackType1 = TrackTypeTrackType.m2f1; + break; case CommonTypes.Enums.TrackType.CdMode1: xmlTrk.TrackType1 = TrackTypeTrackType.mode1; + break; case CommonTypes.Enums.TrackType.Data: switch(sidecar.OpticalDisc[0].DiscType) { - case "BD": + case"BD": xmlTrk.TrackType1 = TrackTypeTrackType.bluray; + break; - case "DDCD": + case"DDCD": xmlTrk.TrackType1 = TrackTypeTrackType.ddcd; + break; - case "DVD": + case"DVD": xmlTrk.TrackType1 = TrackTypeTrackType.dvd; + break; - case "HD DVD": + case"HD DVD": xmlTrk.TrackType1 = TrackTypeTrackType.hddvd; + break; default: xmlTrk.TrackType1 = TrackTypeTrackType.mode1; + break; } break; } - xmlTrk.Sequence = - new TrackSequenceType {Session = trk.TrackSession, TrackNumber = trk.TrackSequence}; + xmlTrk.Sequence = new TrackSequenceType + { + Session = trk.TrackSession, TrackNumber = trk.TrackSequence + }; + xmlTrk.StartSector = trk.TrackStartSector; xmlTrk.EndSector = trk.TrackEndSector; - if(trk.Indexes != null && trk.Indexes.ContainsKey(0)) + if(trk.Indexes != null && + trk.Indexes.ContainsKey(0)) if(trk.Indexes.TryGetValue(0, out ulong idx0)) xmlTrk.StartSector = idx0; switch(sidecar.OpticalDisc[0].DiscType) { - case "CD": - case "GD": + case"CD": + case"GD": xmlTrk.StartMSF = LbaToMsf((long)xmlTrk.StartSector); xmlTrk.EndMSF = LbaToMsf((long)xmlTrk.EndSector); + break; - case "DDCD": + case"DDCD": xmlTrk.StartMSF = DdcdLbaToMsf((long)xmlTrk.StartSector); xmlTrk.EndMSF = DdcdLbaToMsf((long)xmlTrk.EndSector); + break; } - xmlTrk.Image = new ImageType {Value = Path.GetFileName(trk.TrackFile), format = trk.TrackFileType}; + xmlTrk.Image = new ImageType + { + Value = Path.GetFileName(trk.TrackFile), format = trk.TrackFileType + }; if(trk.TrackFileOffset > 0) { @@ -470,8 +549,10 @@ namespace DiscImageChef.Core // If there is only one track, and it's the same as the image file (e.g. ".iso" files), don't re-checksum. if(image.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") && + // Only if filter is none... (filterId == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") || + // ...or AppleDouble filterId == new Guid("1b2165ee-c9df-4b21-bbbb-9e5892b2df4d"))) xmlTrk.Checksums = sidecar.OpticalDisc[0].Checksums; @@ -482,15 +563,17 @@ namespace DiscImageChef.Core // For fast debugging, skip checksum //goto skipChecksum; - Checksum trkChkWorker = new Checksum(); + var trkChkWorker = new Checksum(); InitProgress2(); + while(doneSectors < sectors) { if(aborted) { EndProgress(); EndProgress2(); + return; } @@ -499,16 +582,20 @@ namespace DiscImageChef.Core if(sectors - doneSectors >= sectorsToRead) { sector = image.ReadSectorsLong(doneSectors, sectorsToRead, xmlTrk.Sequence.TrackNumber); + UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors, (long)(trk.TrackEndSector - trk.TrackStartSector + 1)); + doneSectors += sectorsToRead; } else { sector = image.ReadSectorsLong(doneSectors, (uint)(sectors - doneSectors), xmlTrk.Sequence.TrackNumber); + UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors, (long)(trk.TrackEndSector - trk.TrackStartSector + 1)); + doneSectors += sectors - doneSectors; } @@ -526,7 +613,11 @@ namespace DiscImageChef.Core { xmlTrk.SubChannel = new SubChannelType { - Image = new ImageType {Value = trk.TrackSubchannelFile}, + Image = new ImageType + { + Value = trk.TrackSubchannelFile + }, + // TODO: Packed subchannel has different size? Size = (xmlTrk.EndSector - xmlTrk.StartSector + 1) * 96 }; @@ -536,14 +627,17 @@ namespace DiscImageChef.Core case TrackSubchannelType.Packed: case TrackSubchannelType.PackedInterleaved: xmlTrk.SubChannel.Image.format = "rw"; + break; case TrackSubchannelType.Raw: case TrackSubchannelType.RawInterleaved: xmlTrk.SubChannel.Image.format = "rw_raw"; + break; case TrackSubchannelType.Q16: case TrackSubchannelType.Q16Interleaved: xmlTrk.SubChannel.Image.format = "q16"; + break; } @@ -553,18 +647,20 @@ namespace DiscImageChef.Core xmlTrk.SubChannel.Image.offsetSpecified = true; } - Checksum subChkWorker = new Checksum(); + var subChkWorker = new Checksum(); sectors = xmlTrk.EndSector - xmlTrk.StartSector + 1; doneSectors = 0; InitProgress2(); + while(doneSectors < sectors) { if(aborted) { EndProgress(); EndProgress2(); + return; } @@ -574,8 +670,10 @@ namespace DiscImageChef.Core { sector = image.ReadSectorsTag(doneSectors, sectorsToRead, xmlTrk.Sequence.TrackNumber, SectorTagType.CdSectorSubchannel); + UpdateProgress2("Hashings subchannel sector {0} of {1}", (long)doneSectors, (long)(trk.TrackEndSector - trk.TrackStartSector + 1)); + doneSectors += sectorsToRead; } else @@ -583,8 +681,10 @@ namespace DiscImageChef.Core sector = image.ReadSectorsTag(doneSectors, (uint)(sectors - doneSectors), xmlTrk.Sequence.TrackNumber, SectorTagType.CdSectorSubchannel); + UpdateProgress2("Hashings subchannel sector {0} of {1}", (long)doneSectors, (long)(trk.TrackEndSector - trk.TrackStartSector + 1)); + doneSectors += sectors - doneSectors; } @@ -601,25 +701,25 @@ namespace DiscImageChef.Core // For fast debugging, skip checksum //skipChecksum: - List trkPartitions = partitions - .Where(p => p.Start >= trk.TrackStartSector && + List trkPartitions = partitions. + Where(p => p.Start >= trk.TrackStartSector && p.End <= trk.TrackEndSector).ToList(); xmlTrk.FileSystemInformation = new PartitionType[1]; + if(trkPartitions.Count > 0) { xmlTrk.FileSystemInformation = new PartitionType[trkPartitions.Count]; + for(int i = 0; i < trkPartitions.Count; i++) { xmlTrk.FileSystemInformation[i] = new PartitionType { - Description = trkPartitions[i].Description, - EndSector = trkPartitions[i].End, - Name = trkPartitions[i].Name, - Sequence = (uint)trkPartitions[i].Sequence, - StartSector = trkPartitions[i].Start, - Type = trkPartitions[i].Type + Description = trkPartitions[i].Description, EndSector = trkPartitions[i].End, + Name = trkPartitions[i].Name, Sequence = (uint)trkPartitions[i].Sequence, + StartSector = trkPartitions[i].Start, Type = trkPartitions[i].Type }; + List lstFs = new List(); foreach(IFilesystem plugin in plugins.PluginsList.Values) @@ -628,10 +728,12 @@ namespace DiscImageChef.Core if(aborted) { EndProgress(); + return; } - if(!plugin.Identify(image, trkPartitions[i])) continue; + if(!plugin.Identify(image, trkPartitions[i])) + continue; plugin.GetInformation(image, trkPartitions[i], out _, encoding); lstFs.Add(plugin.XmlFsType); @@ -639,17 +741,21 @@ namespace DiscImageChef.Core switch(plugin.XmlFsType.Type) { - case "Opera": + case"Opera": dskType = MediaType.ThreeDO; + break; - case "PC Engine filesystem": + case"PC Engine filesystem": dskType = MediaType.SuperCDROM2; + break; - case "Nintendo Wii filesystem": + case"Nintendo Wii filesystem": dskType = MediaType.WOD; + break; - case "Nintendo Gamecube filesystem": + case"Nintendo Gamecube filesystem": dskType = MediaType.GOD; + break; } } @@ -660,7 +766,8 @@ namespace DiscImageChef.Core //DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name); } - if(lstFs.Count > 0) xmlTrk.FileSystemInformation[i].FileSystems = lstFs.ToArray(); + if(lstFs.Count > 0) + xmlTrk.FileSystemInformation[i].FileSystems = lstFs.ToArray(); } } else @@ -669,26 +776,27 @@ namespace DiscImageChef.Core { EndSector = xmlTrk.EndSector, StartSector = xmlTrk.StartSector }; + List lstFs = new List(); - Partition xmlPart = new Partition + var xmlPart = new Partition { - Start = xmlTrk.StartSector, - Length = xmlTrk.EndSector - xmlTrk.StartSector + 1, - Type = xmlTrk.TrackType1.ToString(), - Size = xmlTrk.Size, - Sequence = xmlTrk.Sequence.TrackNumber + Start = xmlTrk.StartSector, Length = xmlTrk.EndSector - xmlTrk.StartSector + 1, + Type = xmlTrk.TrackType1.ToString(), Size = xmlTrk.Size, Sequence = xmlTrk.Sequence.TrackNumber }; + foreach(IFilesystem plugin in plugins.PluginsList.Values) try { if(aborted) { EndProgress(); + return; } - if(!plugin.Identify(image, xmlPart)) continue; + if(!plugin.Identify(image, xmlPart)) + continue; plugin.GetInformation(image, xmlPart, out _, encoding); lstFs.Add(plugin.XmlFsType); @@ -696,17 +804,21 @@ namespace DiscImageChef.Core switch(plugin.XmlFsType.Type) { - case "Opera": + case"Opera": dskType = MediaType.ThreeDO; + break; - case "PC Engine filesystem": + case"PC Engine filesystem": dskType = MediaType.SuperCDROM2; + break; - case "Nintendo Wii filesystem": + case"Nintendo Wii filesystem": dskType = MediaType.WOD; + break; - case "Nintendo Gamecube filesystem": + case"Nintendo Gamecube filesystem": dskType = MediaType.GOD; + break; } } @@ -717,7 +829,8 @@ namespace DiscImageChef.Core //DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name); } - if(lstFs.Count > 0) xmlTrk.FileSystemInformation[0].FileSystems = lstFs.ToArray(); + if(lstFs.Count > 0) + xmlTrk.FileSystemInformation[0].FileSystems = lstFs.ToArray(); } trksLst.Add(xmlTrk); @@ -725,25 +838,29 @@ namespace DiscImageChef.Core EndProgress(); - if(trksLst != null) sidecar.OpticalDisc[0].Track = trksLst.ToArray(); + if(trksLst != null) + sidecar.OpticalDisc[0].Track = trksLst.ToArray(); // All XGD3 all have the same number of blocks - if(dskType == MediaType.XGD2 && sidecar.OpticalDisc[0].Track.Length == 1) + if(dskType == MediaType.XGD2 && + sidecar.OpticalDisc[0].Track.Length == 1) { ulong blocks = sidecar.OpticalDisc[0].Track[0].EndSector - sidecar.OpticalDisc[0].Track[0].StartSector + 1; + if(blocks == 25063 || // Locked (or non compatible drive) blocks == 4229664 || // Xtreme unlock blocks == 4246304) // Wxripper unlock dskType = MediaType.XGD3; } - CommonTypes.Metadata.MediaType.MediaTypeToString(dskType, out string dscType, out string dscSubType); - sidecar.OpticalDisc[0].DiscType = dscType; - sidecar.OpticalDisc[0].DiscSubType = dscSubType; + (string type, string subType) discType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); + sidecar.OpticalDisc[0].DiscType = discType.type; + sidecar.OpticalDisc[0].DiscSubType = discType.subType; Statistics.AddMedia(dskType, false); - if(image.DumpHardware != null) sidecar.OpticalDisc[0].DumpHardwareArray = image.DumpHardware.ToArray(); + if(image.DumpHardware != null) + sidecar.OpticalDisc[0].DumpHardwareArray = image.DumpHardware.ToArray(); else if(!string.IsNullOrEmpty(image.Info.DriveManufacturer) || !string.IsNullOrEmpty(image.Info.DriveModel) || !string.IsNullOrEmpty(image.Info.DriveFirmwareRevision) || @@ -752,15 +869,20 @@ namespace DiscImageChef.Core { new DumpHardwareType { - Extents = new[] {new ExtentType {Start = 0, End = image.Info.Sectors}}, - Manufacturer = image.Info.DriveManufacturer, - Model = image.Info.DriveModel, - Firmware = image.Info.DriveFirmwareRevision, - Serial = image.Info.DriveSerialNumber, - Software = new SoftwareType + Extents = new[] { - Name = image.Info.Application, Version = image.Info.ApplicationVersion - } + new ExtentType + { + Start = 0, End = image.Info.Sectors + } + }, + Manufacturer = image.Info.DriveManufacturer, Model = image.Info.DriveModel, + Firmware = image.Info.DriveFirmwareRevision, Serial = image.Info.DriveSerialNumber, + Software = + new SoftwareType + { + Name = image.Info.Application, Version = image.Info.ApplicationVersion + } } }; }