From 8e4e4cd27ac11ed37459695197399faaa62ca5d5 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 7 Jun 2017 23:25:06 +0100 Subject: [PATCH] Added support for dumping subchannel in a separate file. --- .../Devices/Dumping/CompactDisc.cs | 101 +++++++++++++++--- DiscImageChef.Core/Devices/Dumping/MMC.cs | 4 +- DiscImageChef.Core/Devices/Dumping/SCSI.cs | 4 +- DiscImageChef/ChangeLog | 4 + DiscImageChef/Commands/DumpMedia.cs | 3 +- DiscImageChef/Options.cs | 3 + 6 files changed, 99 insertions(+), 20 deletions(-) diff --git a/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs b/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs index 90ae4075..8d58c21c 100644 --- a/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs +++ b/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs @@ -50,7 +50,7 @@ namespace DiscImageChef.Core.Devices.Dumping { internal class CompactDisc { - internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar, ref MediaType dskType) + internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar, ref MediaType dskType, bool separateSubchannel) { MHDDLog mhddLog; IBGLog ibgLog; @@ -60,7 +60,8 @@ namespace DiscImageChef.Core.Devices.Dumping double duration; ulong blocks = 0; // TODO: Check subchannel support - uint blockSize = 2448; + uint blockSize = 0; + uint subSize = 0; byte[] tmpBuf; Decoders.CD.FullTOC.CDFullTOC? toc = null; DateTime start; @@ -234,7 +235,14 @@ namespace DiscImageChef.Core.Devices.Dumping } } - blockSize = blockSize; + // TODO: Support variable subchannel kinds + blockSize = 2448; + subSize = 96; + int sectorSize; + if(separateSubchannel) + sectorSize = (int)(blockSize - subSize); + else + sectorSize = (int)blockSize; if(toc == null) { @@ -452,20 +460,39 @@ namespace DiscImageChef.Core.Devices.Dumping DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead); dumpFile = new DataFile(outputPrefix + ".bin"); + DataFile subFile = null; + if(separateSubchannel) + subFile = new DataFile(outputPrefix + ".sub"); mhddLog = new MHDDLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); ibgLog = new IBGLog(outputPrefix + ".ibg", 0x0008); start = DateTime.UtcNow; for(int t = 0; t < tracks.Count(); t++) { - tracks[t].BytesPerSector = (int)blockSize; - //tracks[0].Checksums = sidecar.OpticalDisc[0].Checksums; + tracks[t].BytesPerSector = (int)sectorSize; tracks[t].Image = new ImageType(); tracks[t].Image.format = "BINARY"; tracks[t].Image.offset = dumpFile.Position; tracks[t].Image.offsetSpecified = true; tracks[t].Image.Value = outputPrefix + ".bin"; - tracks[t].Size = (long)((tracks[t].EndSector - tracks[t].StartSector + 1) * blockSize); + tracks[t].Size = (long)((tracks[t].EndSector - tracks[t].StartSector + 1) * sectorSize); + tracks[t].SubChannel = new SubChannelType(); + tracks[t].SubChannel.Image = new ImageType(); + tracks[t].SubChannel.Image.format = "rw_raw"; + tracks[t].SubChannel.Image.offsetSpecified = true; + tracks[t].SubChannel.Size = (long)((tracks[t].EndSector - tracks[t].StartSector + 1) * subSize); + + if(separateSubchannel) + { + tracks[t].SubChannel.Image.offset = subFile.Position; + tracks[t].SubChannel.Image.Value = outputPrefix + ".sub"; + } + else + { + tracks[t].SubChannel.Image.offset = tracks[t].Image.offset; + tracks[t].SubChannel.Image.Value = tracks[t].Image.Value; + } + bool checkedDataFormat = false; for(ulong i = (ulong)tracks[t].StartSector; i <= (ulong)tracks[t].EndSector; i += blocksToRead) @@ -498,7 +525,16 @@ namespace DiscImageChef.Core.Devices.Dumping { mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); - dumpFile.Write(readBuffer); + if(separateSubchannel) + { + for(int b = 0; b < blocksToRead; b++) + { + dumpFile.Write(readBuffer, (int)(0 + b * blockSize), sectorSize); + subFile.Write(readBuffer, (int)(sectorSize + b * blockSize), (int)subSize); + } + } + else + dumpFile.Write(readBuffer); } else { @@ -507,7 +543,13 @@ namespace DiscImageChef.Core.Devices.Dumping return; // TODO: Return more cleanly // Write empty data - dumpFile.Write(new byte[blockSize * blocksToRead]); + if(separateSubchannel) + { + dumpFile.Write(new byte[sectorSize * blocksToRead]); + subFile.Write(new byte[subSize * blocksToRead]); + } + else + dumpFile.Write(new byte[blockSize * blocksToRead]); // TODO: Record error on mapfile @@ -595,13 +637,19 @@ namespace DiscImageChef.Core.Devices.Dumping totalDuration += cmdDuration; } - if(!sense && !dev.Error) + if((!sense && !dev.Error) || runningPersistent) { - unreadableSectors.Remove(badSector); - dumpFile.WriteAt(readBuffer, badSector, blockSize); + if(!sense && !dev.Error) + unreadableSectors.Remove(badSector); + + if(separateSubchannel) + { + dumpFile.WriteAt(readBuffer, badSector, (uint)sectorSize, 0, sectorSize); + subFile.WriteAt(readBuffer, badSector, subSize, sectorSize, (int)subSize); + } + else + dumpFile.WriteAt(readBuffer, badSector, blockSize); } - else if(runningPersistent) - dumpFile.WriteAt(readBuffer, badSector, blockSize); } if(pass < retryPasses && !aborted && unreadableSectors.Count > 0) @@ -683,11 +731,14 @@ namespace DiscImageChef.Core.Devices.Dumping dataChk = new Checksum(); dumpFile.Seek(0, SeekOrigin.Begin); + if(separateSubchannel) + subFile.Seek(0, SeekOrigin.Begin); blocksToRead = 500; for(int t = 0; t < tracks.Count(); t++) { Checksum trkChk = new Checksum(); + Checksum subChk = new Checksum(); for(ulong i = (ulong)tracks[t].StartSector; i <= (ulong)tracks[t].EndSector; i += blocksToRead) { @@ -702,8 +753,24 @@ namespace DiscImageChef.Core.Devices.Dumping DateTime chkStart = DateTime.UtcNow; byte[] dataToCheck = new byte[blockSize * blocksToRead]; dumpFile.Read(dataToCheck, 0, (int)(blockSize * blocksToRead)); - dataChk.Update(dataToCheck); - trkChk.Update(dataToCheck); + if(separateSubchannel) + { + byte[] data = new byte[sectorSize]; + byte[] sub = new byte[subSize]; + for(int b = 0; b < blocksToRead; b++) + { + Array.Copy(dataToCheck, 0, data, 0, sectorSize); + Array.Copy(dataToCheck, sectorSize, sub, 0, subSize); + dataChk.Update(data); + trkChk.Update(data); + subChk.Update(sub); + } + } + else + { + dataChk.Update(dataToCheck); + trkChk.Update(dataToCheck); + } DateTime chkEnd = DateTime.UtcNow; double chkDuration = (chkEnd - chkStart).TotalMilliseconds; @@ -715,6 +782,10 @@ namespace DiscImageChef.Core.Devices.Dumping } tracks[t].Checksums = trkChk.End().ToArray(); + if(separateSubchannel) + tracks[t].SubChannel.Checksums = subChk.End().ToArray(); + else + tracks[t].SubChannel.Checksums = tracks[t].Checksums; } DicConsole.WriteLine(); dumpFile.Close(); diff --git a/DiscImageChef.Core/Devices/Dumping/MMC.cs b/DiscImageChef.Core/Devices/Dumping/MMC.cs index cebcec98..eef48c12 100644 --- a/DiscImageChef.Core/Devices/Dumping/MMC.cs +++ b/DiscImageChef.Core/Devices/Dumping/MMC.cs @@ -45,7 +45,7 @@ namespace DiscImageChef.Core.Devices.Dumping { internal static class MMC { - internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar, ref MediaType dskType) + internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar, ref MediaType dskType, bool separateSubchannel) { byte[] cmdBuf = null; byte[] senseBuf = null; @@ -163,7 +163,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(compactDisc) { - CompactDisc.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType); + CompactDisc.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType, separateSubchannel); return; } diff --git a/DiscImageChef.Core/Devices/Dumping/SCSI.cs b/DiscImageChef.Core/Devices/Dumping/SCSI.cs index 3196e45c..72e118b0 100644 --- a/DiscImageChef.Core/Devices/Dumping/SCSI.cs +++ b/DiscImageChef.Core/Devices/Dumping/SCSI.cs @@ -47,7 +47,7 @@ namespace DiscImageChef.Core.Devices.Dumping public class SCSI { // TODO: Get cartridge serial number from Certance vendor EVPD - public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError) + public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, bool separateSubchannel) { byte[] senseBuf = null; bool sense = false; @@ -145,7 +145,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(dev.SCSIType == Decoders.SCSI.PeripheralDeviceTypes.MultiMediaDevice) { - MMC.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType); + MMC.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar, ref dskType, separateSubchannel); DicConsole.WriteLine("Writing metadata sidecar"); diff --git a/DiscImageChef/ChangeLog b/DiscImageChef/ChangeLog index 575bfa02..2126a8da 100644 --- a/DiscImageChef/ChangeLog +++ b/DiscImageChef/ChangeLog @@ -1,3 +1,7 @@ +* Options.cs: +* Commands/DumpMedia.cs: + Added support for dumping subchannel in a separate file. + * Commands/Ls.cs: * Commands/ExtractFiles.cs: Add support for encodings. diff --git a/DiscImageChef/Commands/DumpMedia.cs b/DiscImageChef/Commands/DumpMedia.cs index c246343d..8c6a5ee1 100644 --- a/DiscImageChef/Commands/DumpMedia.cs +++ b/DiscImageChef/Commands/DumpMedia.cs @@ -63,6 +63,7 @@ namespace DiscImageChef.Commands DicConsole.DebugWriteLine("Dump-Media command", "--force={0}", options.Force); DicConsole.DebugWriteLine("Dump-Media command", "--retry-passes={0}", options.RetryPasses); DicConsole.DebugWriteLine("Dump-Media command", "--persistent={0}", options.Persistent); + DicConsole.DebugWriteLine("Dump-Media command", "--separate-subchannel={0}", options.SeparateSubchannel); if(!File.Exists(options.DevicePath)) { @@ -100,7 +101,7 @@ namespace DiscImageChef.Commands break; case DeviceType.ATAPI: case DeviceType.SCSI: - SCSI.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses, options.Force, options.Raw, options.Persistent, options.StopOnError); + SCSI.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses, options.Force, options.Raw, options.Persistent, options.StopOnError, options.SeparateSubchannel); break; default: throw new NotSupportedException("Unknown device type."); diff --git a/DiscImageChef/Options.cs b/DiscImageChef/Options.cs index 61ba3925..c8ceba9c 100644 --- a/DiscImageChef/Options.cs +++ b/DiscImageChef/Options.cs @@ -299,6 +299,9 @@ namespace DiscImageChef HelpText = "Try to recover partial or incorrect data.")] public bool Persistent { get; set; } + [Option("separate-subchannel", Default = false, + HelpText = "Save subchannel in a separate file. Only applicable to CD/DDCD/GD.")] + public bool SeparateSubchannel { get; set; } } [Verb("device-report", HelpText = "Tests the device capabilities and creates an XML report of them.")]