Add support for dumping media in any of the writable image formats.

CompactDisc dumping disabled until further notice.
This commit is contained in:
2018-01-19 01:21:01 +00:00
parent de52db7da0
commit 29ac7931cb
14 changed files with 1746 additions and 2339 deletions

View File

@@ -101,7 +101,6 @@
<e p="Devices" t="Include">
<e p="Dumping" t="Include">
<e p="ATA.cs" t="Include" />
<e p="Alcohol120.cs" t="Include" />
<e p="CompactDisc.cs" t="Include" />
<e p="MMC.cs" t="Include" />
<e p="NVMe.cs" t="Include" />

View File

@@ -33,21 +33,20 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using DiscImageChef.CommonTypes;
using DiscImageChef.Console;
using DiscImageChef.Core.Logging;
using DiscImageChef.Decoders.ATA;
using DiscImageChef.Decoders.PCMCIA;
using DiscImageChef.Devices;
using DiscImageChef.DiscImages;
using DiscImageChef.Filesystems;
using DiscImageChef.Filters;
using DiscImageChef.Metadata;
using Extents;
using Schemas;
using MediaType = DiscImageChef.Metadata.MediaType;
using MediaType = DiscImageChef.CommonTypes.MediaType;
using Tuple = DiscImageChef.Decoders.PCMCIA.Tuple;
namespace DiscImageChef.Core.Devices.Dumping
@@ -62,7 +61,8 @@ namespace DiscImageChef.Core.Devices.Dumping
/// </summary>
/// <param name="dev">Device</param>
/// <param name="devicePath">Path to the device</param>
/// <param name="outputPrefix">Prefix for output data files</param>
/// <param name="outputPrefix">Prefix for output log files</param>
/// <param name="outputPlugin">Plugin for output file</param>
/// <param name="retryPasses">How many times to retry</param>
/// <param name="force">Force to continue dump whenever possible</param>
/// <param name="dumpRaw">Dump long sectors</param>
@@ -71,10 +71,16 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="resume">Information for dump resuming</param>
/// <param name="dumpLog">Dump logger</param>
/// <param name="encoding">Encoding to use when analyzing dump</param>
/// <param name="outputPath">Path to output file</param>
/// <param name="formatOptions">Formats to pass to output file plugin</param>
/// <exception cref="InvalidOperationException">If the resume file is invalid</exception>
public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force,
bool dumpRaw, bool persistent, bool stopOnError, ref Resume resume, ref DumpLog dumpLog,
Encoding encoding)
public static void Dump(Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses,
bool force, bool dumpRaw, bool persistent, bool stopOnError,
ref Resume resume,
ref
DumpLog dumpLog, Encoding encoding, string outputPrefix, string outputPath,
Dictionary<string, string>
formatOptions)
{
bool aborted;
@@ -102,83 +108,8 @@ namespace DiscImageChef.Core.Devices.Dumping
if(ataIdNullable != null)
{
Identify.IdentifyDevice ataId = ataIdNullable.Value;
CICMMetadataType sidecar = new CICMMetadataType {BlockMedia = new[] {new BlockMediaType()}};
if(dev.IsUsb)
{
dumpLog.WriteLine("Reading USB descriptors.");
sidecar.BlockMedia[0].USB = new USBType
{
ProductID = dev.UsbProductId,
VendorID = dev.UsbVendorId,
Descriptors = new DumpType
{
Image = outputPrefix + ".usbdescriptors.bin",
Size = dev.UsbDescriptors.Length,
Checksums = Checksum.GetChecksums(dev.UsbDescriptors).ToArray()
}
};
DataFile.WriteTo("ATA Dump", sidecar.BlockMedia[0].USB.Descriptors.Image, dev.UsbDescriptors);
}
if(dev.IsPcmcia)
{
dumpLog.WriteLine("Reading PCMCIA CIS.");
sidecar.BlockMedia[0].PCMCIA = new PCMCIAType
{
CIS = new DumpType
{
Image = outputPrefix + ".cis.bin",
Size = dev.Cis.Length,
Checksums = Checksum.GetChecksums(dev.Cis).ToArray()
}
};
DataFile.WriteTo("ATA Dump", sidecar.BlockMedia[0].PCMCIA.CIS.Image, dev.Cis);
dumpLog.WriteLine("Decoding PCMCIA CIS.");
Tuple[] tuples = CIS.GetTuples(dev.Cis);
if(tuples != null)
foreach(Tuple tuple in tuples)
switch(tuple.Code)
{
case TupleCodes.CISTPL_MANFID:
ManufacturerIdentificationTuple manfid =
CIS.DecodeManufacturerIdentificationTuple(tuple);
if(manfid != null)
{
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;
}
break;
case TupleCodes.CISTPL_VERS_1:
Level1VersionTuple vers = CIS.DecodeLevel1VersionTuple(tuple);
if(vers != null)
{
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;
}
break;
}
}
sidecar.BlockMedia[0].ATA = new ATAType
{
Identify = new DumpType
{
Image = outputPrefix + ".identify.bin",
Size = cmdBuf.Length,
Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
}
};
DataFile.WriteTo("ATA Dump", sidecar.BlockMedia[0].ATA.Identify.Image, cmdBuf);
byte[] ataIdentify = cmdBuf;
cmdBuf = new byte[0];
DateTime start;
DateTime end;
@@ -191,11 +122,9 @@ namespace DiscImageChef.Core.Devices.Dumping
aborted = false;
System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;
DataFile dumpFile;
// Initializate reader
dumpLog.WriteLine("Initializing reader.");
Reader ataReader = new Reader(dev, TIMEOUT, cmdBuf);
Reader ataReader = new Reader(dev, TIMEOUT, ataIdentify);
// Fill reader blocks
ulong blocks = ataReader.GetDeviceBlocks();
// Check block sizes
@@ -214,6 +143,7 @@ namespace DiscImageChef.Core.Devices.Dumping
DicConsole.ErrorWriteLine(ataReader.ErrorMessage);
return;
}
// Check how many blocks to read, if error show and return
if(ataReader.GetBlocksToRead())
{
@@ -246,16 +176,65 @@ namespace DiscImageChef.Core.Devices.Dumping
MhddLog mhddLog;
IbgLog ibgLog;
double duration;
bool ret = true;
if(dev.IsUsb && dev.UsbDescriptors != null &&
!outputPlugin.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors))
{
ret = false;
dumpLog.WriteLine("Output format does not support USB descriptors.");
DicConsole.ErrorWriteLine("Output format does not support USB descriptors.");
}
if(dev.IsPcmcia && dev.Cis != null &&
!outputPlugin.SupportedMediaTags.Contains(MediaTagType.PCMCIA_CIS))
{
ret = false;
dumpLog.WriteLine("Output format does not support PCMCIA CIS descriptors.");
DicConsole.ErrorWriteLine("Output format does not support PCMCIA CIS descriptors.");
}
if(!outputPlugin.SupportedMediaTags.Contains(MediaTagType.ATA_IDENTIFY))
{
ret = false;
dumpLog.WriteLine("Output format does not support ATA IDENTIFY.");
DicConsole.ErrorWriteLine("Output format does not support ATA IDENTIFY.");
}
if(!ret)
{
dumpLog.WriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
DicConsole.ErrorWriteLine("Several media tags not supported, {0}continuing...",
force ? "" : "not ");
if(!force) return;
}
ret = outputPlugin.Create(outputPath,
dev.IsCompactFlash ? MediaType.CompactFlash : MediaType.GENERIC_HDD,
formatOptions, blocks, blockSize);
// Cannot create image
if(!ret)
{
dumpLog.WriteLine("Error creating output image, not continuing.");
dumpLog.WriteLine(outputPlugin.ErrorMessage);
DicConsole.ErrorWriteLine("Error creating output image, not continuing.");
DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage);
return;
}
// Setting geometry
outputPlugin.SetGeometry(cylinders, heads, sectors);
if(ataReader.IsLba)
{
DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead);
mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
ibgLog = new IbgLog(outputPrefix + ".ibg", ATA_PROFILE);
dumpFile = new DataFile(outputPrefix + ".bin");
if(resume.NextBlock > 0) dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock);
dumpFile.Seek(resume.NextBlock, blockSize);
if(resume.NextBlock > 0) dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock);
start = DateTime.UtcNow;
for(ulong i = resume.NextBlock; i < blocks; i += blocksToRead)
@@ -269,10 +248,10 @@ namespace DiscImageChef.Core.Devices.Dumping
if(blocks - i < blocksToRead) blocksToRead = (byte)(blocks - i);
#pragma warning disable 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;
if(currentSpeed < minSpeed && currentSpeed != 0) minSpeed = currentSpeed;
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed);
@@ -282,7 +261,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{
mhddLog.Write(i, duration);
ibgLog.Write(i, currentSpeed * 1024);
dumpFile.Write(cmdBuf);
outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
extents.Add(i, blocksToRead, true);
}
else
@@ -292,11 +271,12 @@ namespace DiscImageChef.Core.Devices.Dumping
mhddLog.Write(i, duration < 500 ? 65535 : duration);
ibgLog.Write(i, 0);
dumpFile.Write(new byte[blockSize * blocksToRead]);
outputPlugin.WriteSectors(new byte[blockSize * blocksToRead], i, blocksToRead);
dumpLog.WriteLine("Error reading {0} blocks from block {1}.", blocksToRead, i);
}
double newSpeed = (double)blockSize * blocksToRead / 1048576 / (duration / 1000);
double newSpeed =
(double)blockSize * blocksToRead / 1048576 / (duration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
resume.NextBlock = i + blocksToRead;
}
@@ -305,8 +285,10 @@ namespace DiscImageChef.Core.Devices.Dumping
DicConsole.WriteLine();
mhddLog.Close();
ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
blockSize *
(double)(blocks + 1) / 1024 / (totalDuration / 1000), devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.",
(end - start).TotalSeconds);
dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
@@ -315,7 +297,6 @@ namespace DiscImageChef.Core.Devices.Dumping
{
int pass = 0;
bool forward = true;
bool runningPersistent = false;
repeatRetryLba:
ulong[] tmpArray = resume.BadBlocks.ToArray();
@@ -330,7 +311,7 @@ namespace DiscImageChef.Core.Devices.Dumping
DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass + 1,
forward ? "forward" : "reverse",
runningPersistent ? "recovering partial data, " : "");
persistent ? "recovering partial data, " : "");
bool error = ataReader.ReadBlock(out cmdBuf, badSector, out duration);
@@ -340,10 +321,11 @@ namespace DiscImageChef.Core.Devices.Dumping
{
resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
dumpFile.WriteAt(cmdBuf, badSector, blockSize);
outputPlugin.WriteSector(cmdBuf, badSector);
dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
}
else if(runningPersistent) dumpFile.WriteAt(cmdBuf, badSector, blockSize);
else if(persistent)
outputPlugin.WriteSector(cmdBuf, badSector);
}
if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0)
@@ -365,7 +347,6 @@ namespace DiscImageChef.Core.Devices.Dumping
{
mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
ibgLog = new IbgLog(outputPrefix + ".ibg", ATA_PROFILE);
dumpFile = new DataFile(outputPrefix + ".bin");
ulong currentBlock = 0;
blocks = (ulong)(cylinders * heads * sectors);
@@ -383,10 +364,10 @@ namespace DiscImageChef.Core.Devices.Dumping
break;
}
#pragma warning disable 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;
if(currentSpeed < minSpeed && currentSpeed != 0) minSpeed = currentSpeed;
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
DicConsole.Write("\rReading cylinder {0} head {1} sector {2} ({3:F3} MiB/sec.)", cy,
hd, sc, currentSpeed);
@@ -399,7 +380,8 @@ namespace DiscImageChef.Core.Devices.Dumping
{
mhddLog.Write(currentBlock, duration);
ibgLog.Write(currentBlock, currentSpeed * 1024);
dumpFile.Write(cmdBuf);
outputPlugin.WriteSector(cmdBuf,
(ulong)((cy * heads + hd) * sectors + (sc - 1)));
extents.Add(currentBlock);
dumpLog.WriteLine("Error reading cylinder {0} head {1} sector {2}.", cy, hd,
sc);
@@ -410,10 +392,12 @@ namespace DiscImageChef.Core.Devices.Dumping
mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration);
ibgLog.Write(currentBlock, 0);
dumpFile.Write(new byte[blockSize]);
outputPlugin.WriteSector(new byte[blockSize],
(ulong)((cy * heads + hd) * sectors + (sc - 1)));
}
double newSpeed = blockSize / (double)1048576 / (duration / 1000);
double newSpeed =
blockSize / (double)1048576 / (duration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
currentBlock++;
@@ -425,166 +409,135 @@ namespace DiscImageChef.Core.Devices.Dumping
DicConsole.WriteLine();
mhddLog.Close();
ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
blockSize *
(double)(blocks + 1) / 1024 / (totalDuration / 1000), devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.",
(end - start).TotalSeconds);
dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
}
Checksum dataChk = new Checksum();
dumpFile.Seek(0, SeekOrigin.Begin);
blocksToRead = 500;
dumpLog.WriteLine("Closing output file.");
DicConsole.WriteLine("Closing output file.");
outputPlugin.Close();
dumpLog.WriteLine("Checksum starts.");
for(ulong i = 0; i < blocks; i += blocksToRead)
{
if(aborted)
{
dumpLog.WriteLine("Aborted!");
break;
}
if(blocks - i < blocksToRead) blocksToRead = (byte)(blocks - i);
DicConsole.Write("\rChecksumming sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed);
DateTime chkStart = DateTime.UtcNow;
byte[] dataToCheck = new byte[blockSize * blocksToRead];
dumpFile.Read(dataToCheck, 0, (int)(blockSize * blocksToRead));
dataChk.Update(dataToCheck);
DateTime chkEnd = DateTime.UtcNow;
double chkDuration = (chkEnd - chkStart).TotalMilliseconds;
totalChkDuration += chkDuration;
double newSpeed = (double)blockSize * blocksToRead / 1048576 / (chkDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
}
DicConsole.WriteLine();
dumpFile.Close();
end = DateTime.UtcNow;
dumpLog.WriteLine("Checksum finished in {0} seconds.", (end - start).TotalSeconds);
dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));
PluginBase plugins = new PluginBase();
FiltersList filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(outputPrefix + ".bin");
if(inputFilter == null)
{
DicConsole.ErrorWriteLine("Cannot open file just created, this should not happen.");
return;
}
IMediaImage imageFormat = ImageFormat.Detect(inputFilter);
PartitionType[] xmlFileSysInfo = null;
dumpLog.WriteLine("Creating sidecar.");
FiltersList filters = new FiltersList();
IFilter filter = filters.GetFilter(outputPath);
IMediaImage inputPlugin = ImageFormat.Detect(filter);
if(!inputPlugin.Open(filter)) throw new ArgumentException("Could not open created image.");
try { if(!imageFormat.Open(inputFilter)) imageFormat = null; }
catch { imageFormat = null; }
DateTime chkStart = DateTime.UtcNow;
CICMMetadataType sidecar = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding);
if(imageFormat != null)
if(dev.IsUsb)
{
dumpLog.WriteLine("Getting partitions.");
List<Partition> partitions = Partitions.GetAll(imageFormat);
Partitions.AddSchemesToStats(partitions);
dumpLog.WriteLine("Found {0} partitions.", partitions.Count);
dumpLog.WriteLine("Reading USB descriptors.");
ret = outputPlugin.WriteMediaTag(dev.UsbDescriptors, MediaTagType.USB_Descriptors);
if(partitions.Count > 0)
if(ret)
sidecar.BlockMedia[0].USB = new USBType
{
xmlFileSysInfo = new PartitionType[partitions.Count];
for(int i = 0; i < partitions.Count; i++)
ProductID = dev.UsbProductId,
VendorID = dev.UsbVendorId,
Descriptors = new DumpType
{
xmlFileSysInfo[i] = new PartitionType
{
Description = partitions[i].Description,
EndSector = (int)(partitions[i].Start + partitions[i].Length - 1),
Name = partitions[i].Name,
Sequence = (int)partitions[i].Sequence,
StartSector = (int)partitions[i].Start,
Type = partitions[i].Type
Image = outputPath,
Size = dev.UsbDescriptors.Length,
Checksums = Checksum.GetChecksums(dev.UsbDescriptors).ToArray()
}
};
List<FileSystemType> lstFs = new List<FileSystemType>();
dumpLog
.WriteLine("Getting filesystems on partition {0}, starting at {1}, ending at {2}, with type {3}, under scheme {4}.",
i, partitions[i].Start, partitions[i].End, partitions[i].Type,
partitions[i].Scheme);
foreach(IFilesystem plugin in plugins.PluginsList.Values)
try
{
if(!plugin.Identify(imageFormat, partitions[i])) continue;
plugin.GetInformation(imageFormat, partitions[i], out _, encoding);
lstFs.Add(plugin.XmlFsType);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
dumpLog.WriteLine("Filesystem {0} found.", plugin.XmlFsType.Type);
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
{
//DicConsole.DebugWriteLine("Dump-media command", "Plugin {0} crashed", _plugin.Name);
}
if(lstFs.Count > 0) xmlFileSysInfo[i].FileSystems = lstFs.ToArray();
}
}
else
if(dev.IsPcmcia)
{
dumpLog.WriteLine("Getting filesystem for whole device.");
dumpLog.WriteLine("Reading PCMCIA CIS.");
ret = outputPlugin.WriteMediaTag(dev.Cis, MediaTagType.PCMCIA_CIS);
xmlFileSysInfo = new PartitionType[1];
xmlFileSysInfo[0] = new PartitionType {EndSector = (int)(blocks - 1), StartSector = 0};
List<FileSystemType> lstFs = new List<FileSystemType>();
Partition wholePart = new Partition
if(ret)
sidecar.BlockMedia[0].PCMCIA = new PCMCIAType
{
Name = "Whole device",
Length = blocks,
Size = blocks * blockSize
CIS = new DumpType
{
Image = outputPath,
Size = dev.Cis.Length,
Checksums = Checksum.GetChecksums(dev.Cis).ToArray()
}
};
foreach(IFilesystem plugin in plugins.PluginsList.Values)
try
dumpLog.WriteLine("Decoding PCMCIA CIS.");
Tuple[] tuples = CIS.GetTuples(dev.Cis);
if(tuples != null)
foreach(Tuple tuple in tuples)
switch(tuple.Code)
{
if(!plugin.Identify(imageFormat, wholePart)) continue;
case TupleCodes.CISTPL_MANFID:
ManufacturerIdentificationTuple manfid =
CIS.DecodeManufacturerIdentificationTuple(tuple);
plugin.GetInformation(imageFormat, wholePart, out _, encoding);
lstFs.Add(plugin.XmlFsType);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
dumpLog.WriteLine("Filesystem {0} found.", plugin.XmlFsType.Type);
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
if(manfid != null)
{
//DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name);
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;
}
if(lstFs.Count > 0) xmlFileSysInfo[0].FileSystems = lstFs.ToArray();
break;
case TupleCodes.CISTPL_VERS_1:
Level1VersionTuple vers = CIS.DecodeLevel1VersionTuple(tuple);
if(vers != null)
{
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;
}
break;
}
}
sidecar.BlockMedia[0].Checksums = dataChk.End().ToArray();
ret = outputPlugin.WriteMediaTag(ataIdentify, MediaTagType.ATA_IDENTIFY);
if(ret)
sidecar.BlockMedia[0].ATA = new ATAType
{
Identify = new DumpType
{
Image = outputPath,
Size = cmdBuf.Length,
Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
}
};
DateTime chkEnd = DateTime.UtcNow;
totalChkDuration = (chkEnd - chkStart).TotalMilliseconds;
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));
DicConsole.WriteLine();
string xmlDskTyp, xmlDskSubTyp;
if(dev.IsCompactFlash)
MediaType.MediaTypeToString(CommonTypes.MediaType.CompactFlash, out xmlDskTyp,
out xmlDskSubTyp);
Metadata.MediaType.MediaTypeToString(MediaType.CompactFlash, out xmlDskTyp, out xmlDskSubTyp);
else if(dev.IsPcmcia)
MediaType.MediaTypeToString(CommonTypes.MediaType.PCCardTypeI, out xmlDskTyp, out xmlDskSubTyp);
Metadata.MediaType.MediaTypeToString(MediaType.PCCardTypeI, out xmlDskTyp, out xmlDskSubTyp);
else
MediaType.MediaTypeToString(CommonTypes.MediaType.GENERIC_HDD, out xmlDskTyp, out xmlDskSubTyp);
Metadata.MediaType.MediaTypeToString(MediaType.GENERIC_HDD, out xmlDskTyp, out xmlDskSubTyp);
sidecar.BlockMedia[0].DiskType = xmlDskTyp;
sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp;
// TODO: Implement device firmware revision
sidecar.BlockMedia[0].Image = new ImageType
{
format = "Raw disk image (sector by sector copy)",
Value = outputPrefix + ".bin"
};
sidecar.BlockMedia[0].Interface = "ATA";
sidecar.BlockMedia[0].LogicalBlocks = (long)blocks;
sidecar.BlockMedia[0].PhysicalBlockSize = (int)physicalsectorsize;
@@ -593,7 +546,6 @@ namespace DiscImageChef.Core.Devices.Dumping
sidecar.BlockMedia[0].Model = dev.Model;
sidecar.BlockMedia[0].Serial = dev.Serial;
sidecar.BlockMedia[0].Size = (long)(blocks * blockSize);
if(xmlFileSysInfo != null) sidecar.BlockMedia[0].FileSystemInformation = xmlFileSysInfo;
if(cylinders > 0 && heads > 0 && sectors > 0)
{
sidecar.BlockMedia[0].Cylinders = cylinders;
@@ -629,7 +581,11 @@ namespace DiscImageChef.Core.Devices.Dumping
}
}
Statistics.AddMedia(CommonTypes.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 DicConsole.ErrorWriteLine("Unable to communicate with ATA device.");
}

View File

@@ -1,534 +0,0 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Alcohol120.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains code for creating Alcohol 120% disc images.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2018 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.DiscImages;
namespace DiscImageChef.Core.Devices.Dumping
{
// TODO: For >4.0, this class must disappear
class Alcohol120
{
byte[] bca;
byte[] dmi;
string extension;
AlcoholFooter footer;
AlcoholHeader header;
string outputPrefix;
byte[] pfi;
List<AlcoholSession> sessions;
Dictionary<byte, uint> trackLengths;
List<AlcoholTrack> tracks;
internal Alcohol120(string outputPrefix)
{
this.outputPrefix = outputPrefix;
header = new AlcoholHeader
{
signature = "MEDIA DESCRIPTOR",
unknown1 = new ushort[] {0x0002, 0x0000},
unknown2 = new uint[2],
unknown3 = new uint[6],
unknown4 = new uint[3],
version = new byte[] {1, 5}
};
header.version[0] = 1;
header.version[1] = 5;
tracks = new List<AlcoholTrack>();
sessions = new List<AlcoholSession>();
trackLengths = new Dictionary<byte, uint>();
footer = new AlcoholFooter {widechar = 1};
}
internal void Close()
{
if(sessions.Count == 0 || tracks.Count == 0) return;
// Calculate first offsets
header.sessions = (ushort)sessions.Count;
header.sessionOffset = 88;
long nextOffset = 88 + sessions.Count * 24;
// Calculate session blocks
AlcoholSession[] sessionsArray = sessions.ToArray();
for(int i = 0; i < sessionsArray.Length; i++)
{
sessionsArray[i].allBlocks = (byte)(sessionsArray[i].lastTrack - sessionsArray[i].firstTrack + 1 +
sessionsArray[i].nonTrackBlocks);
sessionsArray[i].trackOffset = (uint)nextOffset;
nextOffset += sessionsArray[i].allBlocks * 80;
}
// Calculate track blocks
AlcoholTrack[] tracksArray = tracks.ToArray();
AlcoholTrackExtra[] extrasArray = new AlcoholTrackExtra[trackLengths.Count];
for(int i = 0; i < tracksArray.Length; i++)
{
if(tracksArray[i].point >= 0xA0) continue;
if(!trackLengths.TryGetValue(tracksArray[i].point, out uint trkLen)) continue;
if(tracksArray[i].mode == AlcoholTrackMode.Dvd) tracksArray[i].extraOffset = trkLen;
else
{
AlcoholTrackExtra extra = new AlcoholTrackExtra();
if(tracksArray[i].point == 1)
{
extra.pregap = 150;
sessionsArray[0].sessionStart = -150;
}
extra.sectors = trkLen;
extrasArray[tracksArray[i].point - 1] = extra;
tracksArray[i].extraOffset = (uint)nextOffset;
nextOffset += 8;
}
}
// DVD things
if(bca != null && bca.Length > 0)
{
header.bcaOffset = (uint)nextOffset;
header.bcaLength = (ushort)bca.Length;
nextOffset += bca.Length;
}
if(pfi != null && pfi.Length > 0 && dmi != null && dmi.Length > 0)
{
header.structuresOffset = (uint)nextOffset;
nextOffset += 4100;
}
for(int i = 0; i < tracksArray.Length; i++) tracksArray[i].footerOffset = (uint)nextOffset;
footer.filenameOffset = (uint)(nextOffset + 16);
byte[] filename = Encoding.Unicode.GetBytes(outputPrefix + extension);
// Open descriptor file here
FileStream descriptorFile =
new FileStream(outputPrefix + ".mds", FileMode.Create, FileAccess.ReadWrite, FileShare.None);
byte[] tmp = new byte[88];
IntPtr hdrPtr = Marshal.AllocHGlobal(88);
Marshal.StructureToPtr(header, hdrPtr, false);
Marshal.Copy(hdrPtr, tmp, 0, 88);
descriptorFile.Write(tmp, 0, tmp.Length);
foreach(AlcoholSession session in sessionsArray)
{
tmp = new byte[24];
IntPtr sesPtr = Marshal.AllocHGlobal(24);
Marshal.StructureToPtr(session, sesPtr, false);
Marshal.Copy(sesPtr, tmp, 0, 24);
descriptorFile.Write(tmp, 0, tmp.Length);
Marshal.FreeHGlobal(sesPtr);
}
foreach(AlcoholTrack track in tracksArray)
{
tmp = new byte[80];
IntPtr trkPtr = Marshal.AllocHGlobal(80);
Marshal.StructureToPtr(track, trkPtr, false);
Marshal.Copy(trkPtr, tmp, 0, 80);
descriptorFile.Write(tmp, 0, tmp.Length);
Marshal.FreeHGlobal(trkPtr);
}
if(header.type == AlcoholMediumType.Cd || header.type == AlcoholMediumType.Cdr ||
header.type == AlcoholMediumType.Cdrw)
foreach(AlcoholTrackExtra extra in extrasArray)
{
tmp = new byte[8];
IntPtr trkxPtr = Marshal.AllocHGlobal(8);
Marshal.StructureToPtr(extra, trkxPtr, false);
Marshal.Copy(trkxPtr, tmp, 0, 8);
descriptorFile.Write(tmp, 0, tmp.Length);
Marshal.FreeHGlobal(trkxPtr);
}
if(bca != null && bca.Length > 0)
{
header.bcaOffset = (uint)descriptorFile.Position;
header.bcaLength = (ushort)bca.Length;
descriptorFile.Write(bca, 0, bca.Length);
}
if(pfi != null && pfi.Length > 0 && dmi != null && dmi.Length > 0)
{
descriptorFile.Write(new byte[4], 0, 4);
descriptorFile.Write(dmi, 0, 2048);
descriptorFile.Write(pfi, 0, 2048);
}
tmp = new byte[16];
IntPtr ftrPtr = Marshal.AllocHGlobal(16);
Marshal.StructureToPtr(footer, ftrPtr, false);
Marshal.Copy(ftrPtr, tmp, 0, 16);
Marshal.FreeHGlobal(ftrPtr);
descriptorFile.Write(tmp, 0, tmp.Length);
descriptorFile.Write(filename, 0, filename.Length);
// This is because of marshalling strings
descriptorFile.Position = 15;
descriptorFile.WriteByte(0x52);
descriptorFile.Dispose();
}
internal void SetMediaType(MediaType type)
{
switch(type)
{
case MediaType.DVDDownload:
case MediaType.DVDPR:
case MediaType.DVDPRDL:
case MediaType.DVDPRW:
case MediaType.DVDPRWDL:
case MediaType.DVDR:
case MediaType.DVDRAM:
case MediaType.DVDRDL:
case MediaType.DVDRW:
case MediaType.DVDRWDL:
header.type = AlcoholMediumType.Dvdr;
break;
case MediaType.CD:
case MediaType.CDDA:
case MediaType.CDEG:
case MediaType.CDG:
case MediaType.CDI:
case MediaType.CDMIDI:
case MediaType.CDPLUS:
case MediaType.CDROM:
case MediaType.CDROMXA:
case MediaType.CDV:
case MediaType.DDCD:
case MediaType.DTSCD:
case MediaType.JaguarCD:
case MediaType.MEGACD:
case MediaType.PCD:
case MediaType.PS1CD:
case MediaType.PS2CD:
case MediaType.SATURNCD:
case MediaType.SuperCDROM2:
case MediaType.SVCD:
case MediaType.VCD:
case MediaType.VCDHD:
case MediaType.GDROM:
case MediaType.ThreeDO:
header.type = AlcoholMediumType.Cd;
break;
case MediaType.CDR:
case MediaType.DDCDR:
case MediaType.GDR:
header.type = AlcoholMediumType.Cdr;
break;
case MediaType.CDRW:
case MediaType.DDCDRW:
case MediaType.CDMO:
case MediaType.CDMRW:
header.type = AlcoholMediumType.Cdrw;
break;
default:
header.type = AlcoholMediumType.Dvd;
break;
}
}
internal void AddSessions(Session[] cdSessions)
{
foreach(AlcoholSession session in cdSessions.Select(cdSession => new AlcoholSession
{
firstTrack = (ushort)cdSession.StartTrack,
lastTrack = (ushort)cdSession.EndTrack,
sessionSequence = cdSession.SessionSequence
})) sessions.Add(session);
}
internal void SetTrackTypes(byte point, TrackType mode, TrackSubchannelType subMode)
{
AlcoholTrack[] trkArray = tracks.ToArray();
for(int i = 0; i < trkArray.Length; i++)
{
if(trkArray[i].point != point) continue;
switch(mode)
{
case TrackType.Audio:
trkArray[i].mode = AlcoholTrackMode.Audio;
break;
case TrackType.Data:
trkArray[i].mode = AlcoholTrackMode.Dvd;
break;
case TrackType.CdMode1:
trkArray[i].mode = AlcoholTrackMode.Mode1;
break;
case TrackType.CdMode2Formless:
trkArray[i].mode = AlcoholTrackMode.Mode2;
break;
case TrackType.CdMode2Form1:
trkArray[i].mode = AlcoholTrackMode.Mode2F1;
break;
case TrackType.CdMode2Form2:
trkArray[i].mode = AlcoholTrackMode.Mode2F2;
break;
default: throw new ArgumentOutOfRangeException(nameof(mode), mode, null);
}
switch(subMode)
{
case TrackSubchannelType.None:
trkArray[i].subMode = AlcoholSubchannelMode.None;
break;
case TrackSubchannelType.RawInterleaved:
trkArray[i].subMode = AlcoholSubchannelMode.Interleaved;
break;
case TrackSubchannelType.Packed:
case TrackSubchannelType.Raw:
case TrackSubchannelType.PackedInterleaved:
case TrackSubchannelType.Q16:
case TrackSubchannelType.Q16Interleaved:
throw new FeatureUnsupportedImageException("Specified subchannel type is not supported.");
default: throw new ArgumentOutOfRangeException(nameof(subMode), subMode, null);
}
tracks = new List<AlcoholTrack>(trkArray);
break;
}
}
internal void SetTrackSizes(byte point, int sectorSize, long startLba, long startOffset, long length)
{
AlcoholTrack[] trkArray = tracks.ToArray();
for(int i = 0; i < trkArray.Length; i++)
{
if(trkArray[i].point != point) continue;
trkArray[i].sectorSize = (ushort)sectorSize;
trkArray[i].startLba = (uint)startLba;
trkArray[i].startOffset = (ulong)startOffset;
tracks = new List<AlcoholTrack>(trkArray);
break;
}
if(trackLengths.ContainsKey(point)) trackLengths.Remove(point);
trackLengths.Add(point, (uint)length);
AlcoholSession[] sess = sessions.ToArray();
for(int i = 0; i < sess.Length; i++)
{
if(sess[i].firstTrack == point) sess[i].sessionStart = (int)startLba;
if(sess[i].lastTrack == point) sess[i].sessionEnd = (int)(startLba + length);
}
sessions = new List<AlcoholSession>(sess);
}
internal void AddTrack(byte adrCtl, byte tno, byte point, byte min, byte sec, byte frame, byte zero, byte pmin,
byte psec, byte pframe, byte session)
{
AlcoholTrack trk = new AlcoholTrack
{
mode = AlcoholTrackMode.NoData,
subMode = AlcoholSubchannelMode.None,
adrCtl = adrCtl,
tno = tno,
point = point,
min = min,
sec = sec,
frame = frame,
zero = zero,
pmin = pmin,
psec = psec,
pframe = pframe,
unknown = new byte[18],
files = 1,
unknown2 = new byte[24]
};
tracks.Add(trk);
if(point < 0xA0) return;
AlcoholSession[] sess = sessions.ToArray();
for(int i = 0; i < sess.Length; i++) if(sess[i].sessionSequence == session) sess[i].nonTrackBlocks++;
sessions = new List<AlcoholSession>(sess);
}
internal void AddBca(byte[] bca)
{
this.bca = bca;
}
internal void AddPfi(byte[] pfi)
{
if(pfi.Length == 2052)
{
this.pfi = new byte[2048];
Array.Copy(pfi, 4, this.pfi, 0, 2048);
}
else this.pfi = pfi;
}
internal void AddDmi(byte[] dmi)
{
if(dmi.Length == 2052)
{
this.dmi = new byte[2048];
Array.Copy(dmi, 4, this.dmi, 0, 2048);
}
else this.dmi = dmi;
}
internal void SetExtension(string extension)
{
this.extension = extension;
}
#region Internal Structures
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct AlcoholHeader
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] public string signature;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public byte[] version;
public AlcoholMediumType type;
public ushort sessions;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public ushort[] unknown1;
public ushort bcaLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public uint[] unknown2;
public uint bcaOffset;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] public uint[] unknown3;
public uint structuresOffset;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public uint[] unknown4;
public uint sessionOffset;
public uint dpmOffset;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct AlcoholSession
{
public int sessionStart;
public int sessionEnd;
public ushort sessionSequence;
public byte allBlocks;
public byte nonTrackBlocks;
public ushort firstTrack;
public ushort lastTrack;
public uint unknown;
public uint trackOffset;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct AlcoholTrack
{
public AlcoholTrackMode mode;
public AlcoholSubchannelMode subMode;
public byte adrCtl;
public byte tno;
public byte point;
public byte min;
public byte sec;
public byte frame;
public byte zero;
public byte pmin;
public byte psec;
public byte pframe;
public uint extraOffset;
public ushort sectorSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)] public byte[] unknown;
public uint startLba;
public ulong startOffset;
public uint files;
public uint footerOffset;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] public byte[] unknown2;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct AlcoholTrackExtra
{
public uint pregap;
public uint sectors;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct AlcoholFooter
{
public uint filenameOffset;
public uint widechar;
public uint unknown1;
public uint unknown2;
}
#endregion Internal Structures
#region Internal enumerations
enum AlcoholMediumType : ushort
{
Cd = 0x00,
Cdr = 0x01,
Cdrw = 0x02,
Dvd = 0x10,
Dvdr = 0x12
}
enum AlcoholTrackMode : byte
{
NoData = 0x00,
Dvd = 0x02,
Audio = 0xA9,
Mode1 = 0xAA,
Mode2 = 0xAB,
Mode2F1 = 0xAC,
Mode2F2 = 0xAD
}
enum AlcoholSubchannelMode : byte
{
None = 0x00,
Interleaved = 0x08
}
#endregion Internal enumerations
}
}

View File

@@ -32,23 +32,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
using DiscImageChef.Console;
using DiscImageChef.Core.Logging;
using DiscImageChef.Decoders.CD;
using DiscImageChef.Decoders.SCSI;
using DiscImageChef.Decoders.SCSI.MMC;
using DiscImageChef.Devices;
using DiscImageChef.DiscImages;
using DiscImageChef.Metadata;
using Extents;
using Schemas;
using MediaType = DiscImageChef.CommonTypes.MediaType;
using PlatformID = DiscImageChef.Interop.PlatformID;
using Session = DiscImageChef.Decoders.CD.Session;
using TrackType = Schemas.TrackType;
namespace DiscImageChef.Core.Devices.Dumping
{
@@ -63,6 +51,7 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="dev">Device</param>
/// <param name="devicePath">Path to the device</param>
/// <param name="outputPrefix">Prefix for output data files</param>
/// <param name="outputPlugin">Plugin for output file</param>
/// <param name="retryPasses">How many times to retry</param>
/// <param name="force">Force to continue dump whenever possible</param>
/// <param name="dumpRaw">Dump scrambled sectors</param>
@@ -70,20 +59,25 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="stopOnError">Stop dump on first error</param>
/// <param name="resume">Information for dump resuming</param>
/// <param name="dumpLog">Dump logger</param>
/// <param name="sidecar">Partially filled initialized sidecar</param>
/// <param name="dskType">Disc type as detected in MMC layer</param>
/// <param name="separateSubchannel">Write subchannel separate from main channel</param>
/// <param name="alcohol">Alcohol disc image already initialized</param>
/// <param name="dumpLeadIn">Try to read and dump as much Lead-in as possible</param>
/// <param name="outputPath">Path to output file</param>
/// <param name="formatOptions">Formats to pass to output file plugin</param>
/// <exception cref="NotImplementedException">If trying to dump scrambled sectors</exception>
/// <exception cref="InvalidOperationException">If the resume file is invalid</exception>
/// <exception cref="ArgumentOutOfRangeException">If the track type is unknown (never)</exception>
internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force,
bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar,
ref MediaType dskType, bool separateSubchannel, ref Resume resume,
ref DumpLog dumpLog, Alcohol120 alcohol, bool dumpLeadIn)
internal static void Dump(Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses,
bool force, bool dumpRaw, bool persistent, bool stopOnError,
ref MediaType dskType,
ref
Resume resume, ref DumpLog dumpLog, bool dumpLeadIn,
string outputPrefix,
string
outputPath, Dictionary<string, string> formatOptions)
{
bool sense = false;
throw new NotImplementedException("Dumping CompactDisc is disable pending rewrite.");
/*bool sense = false;
ulong blocks;
// TODO: Check subchannel support
uint blockSize;
@@ -99,7 +93,6 @@ namespace DiscImageChef.Core.Devices.Dumping
Checksum dataChk;
bool readcd;
uint blocksToRead = 64;
DataFile dumpFile;
bool aborted = false;
System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;
@@ -902,7 +895,7 @@ namespace DiscImageChef.Core.Devices.Dumping
alcohol.Close();
}
Statistics.AddMedia(dskType, true);
Statistics.AddMedia(dskType, true);*/
}
}
}

View File

@@ -31,6 +31,7 @@
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using DiscImageChef.Console;
using DiscImageChef.Core.Logging;
@@ -39,6 +40,7 @@ using DiscImageChef.Decoders.DVD;
using DiscImageChef.Decoders.SCSI;
using DiscImageChef.Decoders.SCSI.MMC;
using DiscImageChef.Devices;
using DiscImageChef.DiscImages;
using DiscImageChef.Metadata;
using Schemas;
using DDS = DiscImageChef.Decoders.DVD.DDS;
@@ -59,6 +61,7 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="dev">Device</param>
/// <param name="devicePath">Path to the device</param>
/// <param name="outputPrefix">Prefix for output data files</param>
/// <param name="outputPlugin">Plugin for output file</param>
/// <param name="retryPasses">How many times to retry</param>
/// <param name="force">Force to continue dump whenever possible</param>
/// <param name="dumpRaw">Dump raw/long sectors</param>
@@ -67,25 +70,25 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="resume">Information for dump resuming</param>
/// <param name="dumpLog">Dump logger</param>
/// <param name="encoding">Encoding to use when analyzing dump</param>
/// <param name="sidecar">Partially filled initialized sidecar</param>
/// <param name="dskType">Disc type as detected in MMC layer</param>
/// <param name="separateSubchannel">Write subchannel separate from main channel</param>
/// <param name="dumpLeadIn">Try to read and dump as much Lead-in as possible</param>
/// <param name="outputPath">Path to output file</param>
/// <param name="formatOptions">Formats to pass to output file plugin</param>
/// <exception cref="NotImplementedException">If trying to dump GOD or WOD, or XGDs without a Kreon drive</exception>
internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force,
bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar,
ref MediaType dskType, bool separateSubchannel, ref Resume resume,
ref DumpLog dumpLog, bool dumpLeadIn, Encoding encoding)
internal static void Dump(Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses,
bool force, bool dumpRaw, bool persistent, bool stopOnError,
ref MediaType dskType,
ref
Resume resume, ref DumpLog dumpLog, bool dumpLeadIn,
Encoding encoding,
string
outputPrefix, string outputPath, Dictionary<string, string> formatOptions)
{
bool sense;
ulong blocks;
byte[] tmpBuf;
bool compactDisc = true;
bool isXbox = false;
Alcohol120 alcohol = new Alcohol120(outputPrefix);
sidecar.OpticalDisc = new OpticalDiscType[1];
sidecar.OpticalDisc[0] = new OpticalDiscType();
// TODO: Log not only what is it reading, but if it was read correctly or not.
@@ -193,15 +196,16 @@ namespace DiscImageChef.Core.Devices.Dumping
if(compactDisc)
{
CompactDisc.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError,
ref sidecar, ref dskType, separateSubchannel, ref resume, ref dumpLog, alcohol,
dumpLeadIn);
CompactDisc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError,
ref dskType, ref resume, ref dumpLog, dumpLeadIn, outputPrefix, outputPath,
formatOptions);
return;
}
Reader scsiReader = new Reader(dev, dev.Timeout, null, dumpRaw);
blocks = scsiReader.GetDeviceBlocks();
dumpLog.WriteLine("Device reports disc has {0} blocks", blocks);
Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>();
#region Nintendo
switch(dskType)
@@ -245,19 +249,11 @@ namespace DiscImageChef.Core.Devices.Dumping
sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
MmcDiscStructureFormat.PhysicalInformation, 0, dev.Timeout, out _);
if(!sense)
{
alcohol.AddPfi(cmdBuf);
if(PFI.Decode(cmdBuf).HasValue)
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].PFI = new DumpType
{
Image = outputPrefix + ".pfi.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].PFI.Image, tmpBuf);
mediaTags.Add(MediaTagType.DVD_PFI, tmpBuf);
PFI.PhysicalFormatInformation decPfi = PFI.Decode(cmdBuf).Value;
DicConsole.WriteLine("PFI:\n{0}", PFI.Prettify(decPfi));
@@ -310,7 +306,6 @@ namespace DiscImageChef.Core.Devices.Dumping
break;
}
}
}
dumpLog.WriteLine("Reading Disc Manufacturing Information");
sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
@@ -353,19 +348,11 @@ namespace DiscImageChef.Core.Devices.Dumping
isXbox = true;
}
alcohol.AddDmi(cmdBuf);
if(cmdBuf.Length == 2052)
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].DMI = new DumpType
{
Image = outputPrefix + ".dmi.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].DMI.Image, tmpBuf);
mediaTags.Add(MediaTagType.DVD_DMI, tmpBuf);
}
}
@@ -387,17 +374,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].CMI = new DumpType
{
Image = outputPrefix + ".cmi.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].CMI.Image, tmpBuf);
CSS_CPRM.LeadInCopyright cpy = CSS_CPRM.DecodeLeadInCopyright(cmdBuf).Value;
if(cpy.CopyrightType != CopyrightType.NoProtection)
sidecar.OpticalDisc[0].CopyProtection = cpy.CopyrightType.ToString();
mediaTags.Add(MediaTagType.DVD_CMI, tmpBuf);
}
}
#endregion DVD-ROM
@@ -415,15 +392,9 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
alcohol.AddBca(tmpBuf);
sidecar.OpticalDisc[0].BCA = new DumpType
{
Image = outputPrefix + ".bca.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].BCA.Image, tmpBuf);
mediaTags.Add(MediaTagType.DVD_BCA, tmpBuf);
}
break;
#endregion DVD-ROM and HD DVD-ROM
@@ -438,13 +409,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].DDS = new DumpType
{
Image = outputPrefix + ".dds.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].DDS.Image, tmpBuf);
mediaTags.Add(MediaTagType.DVDRAM_DDS, tmpBuf);
}
dumpLog.WriteLine("Reading Spare Area Information.");
@@ -456,14 +421,9 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].SAI = new DumpType
{
Image = outputPrefix + ".sai.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].SAI.Image, tmpBuf);
mediaTags.Add(MediaTagType.DVDRAM_SpareArea, tmpBuf);
}
break;
#endregion DVD-RAM and HD DVD-RAM
@@ -477,14 +437,9 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].PRI = new DumpType
{
Image = outputPrefix + ".pri.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].SAI.Image, tmpBuf);
mediaTags.Add(MediaTagType.DVDR_PreRecordedInfo, tmpBuf);
}
break;
#endregion DVD-R and DVD-RW
}
@@ -502,13 +457,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].MediaID = new DumpType
{
Image = outputPrefix + ".mid.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].MediaID.Image, tmpBuf);
mediaTags.Add(MediaTagType.DVDR_MediaIdentifier, tmpBuf);
}
dumpLog.WriteLine("Reading Recordable Physical Information.");
@@ -519,14 +468,9 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].PFIR = new DumpType
{
Image = outputPrefix + ".pfir.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].PFIR.Image, tmpBuf);
mediaTags.Add(MediaTagType.DVDR_PFI, tmpBuf);
}
break;
#endregion DVD-R, DVD-RW and HD DVD-R
@@ -542,13 +486,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].ADIP = new DumpType
{
Image = outputPrefix + ".adip.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].ADIP.Image, tmpBuf);
mediaTags.Add(MediaTagType.DVD_ADIP, tmpBuf);
}
dumpLog.WriteLine("Reading Disc Control Blocks.");
@@ -558,14 +496,9 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].DCB = new DumpType
{
Image = outputPrefix + ".dcb.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].DCB.Image, tmpBuf);
mediaTags.Add(MediaTagType.DCB, tmpBuf);
}
break;
#endregion All DVD+
@@ -579,14 +512,9 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].CMI = new DumpType
{
Image = outputPrefix + ".cmi.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].CMI.Image, tmpBuf);
mediaTags.Add(MediaTagType.HDDVD_CPI, tmpBuf);
}
break;
#endregion HD DVD-ROM
@@ -604,15 +532,11 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].DI = new DumpType
{
Image = outputPrefix + ".di.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].DI.Image, tmpBuf);
mediaTags.Add(MediaTagType.BD_DI, tmpBuf);
}
// TODO: PAC
/*
dumpLog.WriteLine("Reading PAC.");
sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0,
MmcDiscStructureFormat.Pac, 0, dev.Timeout, out _);
@@ -620,14 +544,8 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].PAC = new DumpType
{
Image = outputPrefix + ".pac.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].PAC.Image, tmpBuf);
}
mediaTags.Add(MediaTagType.PAC, tmpBuf);
}*/
break;
#endregion All Blu-ray
}
@@ -643,15 +561,9 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
alcohol.AddBca(tmpBuf);
sidecar.OpticalDisc[0].BCA = new DumpType
{
Image = outputPrefix + ".bca.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].BCA.Image, tmpBuf);
mediaTags.Add(MediaTagType.BD_BCA, tmpBuf);
}
break;
#endregion BD-ROM only
@@ -667,13 +579,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].DDS = new DumpType
{
Image = outputPrefix + ".dds.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].DDS.Image, tmpBuf);
mediaTags.Add(MediaTagType.BD_DDS, tmpBuf);
}
dumpLog.WriteLine("Reading Spare Area Information.");
@@ -683,27 +589,175 @@ namespace DiscImageChef.Core.Devices.Dumping
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].SAI = new DumpType
{
Image = outputPrefix + ".sai.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].SAI.Image, tmpBuf);
mediaTags.Add(MediaTagType.BD_SpareArea, tmpBuf);
}
break;
#endregion Writable Blu-ray only
}
if(isXbox)
{
Xgd.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError,
ref sidecar, ref dskType, ref resume, ref dumpLog, encoding);
Xgd.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError, mediaTags,
ref dskType, ref resume, ref dumpLog, encoding, outputPrefix, outputPath, formatOptions);
return;
}
Sbc.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError, ref sidecar,
ref dskType, true, ref resume, ref dumpLog, encoding, alcohol);
Sbc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError, mediaTags,
ref dskType, true, ref resume, ref dumpLog, encoding, outputPrefix, outputPath, formatOptions);
}
internal static void AddMediaTagToSidecar(string outputPath,
KeyValuePair<MediaTagType, byte[]> tag,
ref CICMMetadataType sidecar)
{
switch(tag.Key)
{
case MediaTagType.DVD_PFI:
sidecar.OpticalDisc[0].PFI = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.DVD_DMI:
sidecar.OpticalDisc[0].DMI = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.DVD_CMI:
case MediaTagType.HDDVD_CPI:
sidecar.OpticalDisc[0].CMI = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
CSS_CPRM.LeadInCopyright cpy = CSS_CPRM.DecodeLeadInCopyright(tag.Value).Value;
if(cpy.CopyrightType != CopyrightType.NoProtection)
sidecar.OpticalDisc[0].CopyProtection = cpy.CopyrightType.ToString();
break;
case MediaTagType.DVD_BCA:
case MediaTagType.BD_BCA:
sidecar.OpticalDisc[0].BCA = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.BD_DDS:
case MediaTagType.DVDRAM_DDS:
sidecar.OpticalDisc[0].DDS = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.DVDRAM_SpareArea:
case MediaTagType.BD_SpareArea:
sidecar.OpticalDisc[0].SAI = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.DVDR_PreRecordedInfo:
sidecar.OpticalDisc[0].PRI = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.DVD_MediaIdentifier:
sidecar.OpticalDisc[0].MediaID = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.DVDR_PFI:
sidecar.OpticalDisc[0].PFIR = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.DVD_ADIP:
sidecar.OpticalDisc[0].ADIP = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.DCB:
sidecar.OpticalDisc[0].DCB = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.BD_DI:
sidecar.OpticalDisc[0].DI = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.Xbox_SecuritySector:
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
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
}
}
};
break;
case MediaTagType.Xbox_PFI:
if(sidecar.OpticalDisc[0].Xbox == null) sidecar.OpticalDisc[0].Xbox = new XboxType();
sidecar.OpticalDisc[0].Xbox.PFI = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.Xbox_DMI:
if(sidecar.OpticalDisc[0].Xbox == null) sidecar.OpticalDisc[0].Xbox = new XboxType();
sidecar.OpticalDisc[0].Xbox.DMI = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
}
}
}
}

View File

@@ -31,18 +31,24 @@
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using DiscImageChef.Core.Logging;
using DiscImageChef.Devices;
using DiscImageChef.DiscImages;
using DiscImageChef.Metadata;
namespace DiscImageChef.Core.Devices.Dumping
{
public static class NvMe
{
public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force,
bool dumpRaw, bool persistent, bool stopOnError, ref Resume resume, ref DumpLog dumpLog,
Encoding encoding)
public static void Dump(Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses,
bool force, bool dumpRaw, bool persistent, bool stopOnError,
ref Resume resume,
ref
DumpLog dumpLog, Encoding encoding, string outputPrefix, string outputPath,
Dictionary<string, string>
formatOptions)
{
throw new NotImplementedException("NVMe devices not yet supported.");
}

View File

@@ -42,13 +42,11 @@ using DiscImageChef.Core.Logging;
using DiscImageChef.Decoders.SCSI;
using DiscImageChef.Devices;
using DiscImageChef.DiscImages;
using DiscImageChef.Filesystems;
using DiscImageChef.Filters;
using DiscImageChef.Metadata;
using Extents;
using Schemas;
using MediaType = DiscImageChef.CommonTypes.MediaType;
using TrackType = DiscImageChef.DiscImages.TrackType;
namespace DiscImageChef.Core.Devices.Dumping
{
@@ -63,6 +61,7 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="dev">Device</param>
/// <param name="devicePath">Path to the device</param>
/// <param name="outputPrefix">Prefix for output data files</param>
/// <param name="outputPlugin">Plugin for output file</param>
/// <param name="retryPasses">How many times to retry</param>
/// <param name="force">Force to continue dump whenever possible</param>
/// <param name="dumpRaw">Dump long or scrambled sectors</param>
@@ -72,14 +71,21 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="dumpLog">Dump logger</param>
/// <param name="encoding">Encoding to use when analyzing dump</param>
/// <param name="opticalDisc">If device contains an optical disc (e.g. DVD or BD)</param>
/// <param name="sidecar">Partially filled initialized sidecar</param>
/// <param name="mediaTags">Media tags as retrieved in MMC layer</param>
/// <param name="dskType">Disc type as detected in SCSI or MMC layer</param>
/// <param name="alcohol">Alcohol disc image already initialized for optical discs, null otherwise</param>
/// <param name="outputPath">Path to output file</param>
/// <param name="formatOptions">Formats to pass to output file plugin</param>
/// <exception cref="InvalidOperationException">If the resume file is invalid</exception>
internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force,
bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar,
ref MediaType dskType, bool opticalDisc, ref Resume resume, ref DumpLog dumpLog,
Encoding encoding, Alcohol120 alcohol = null)
internal static void Dump(Device dev, string devicePath,
IWritableImage outputPlugin, ushort retryPasses,
bool force, bool dumpRaw,
bool persistent, bool stopOnError,
Dictionary<MediaTagType, byte[]> mediaTags, ref MediaType dskType,
bool opticalDisc,
ref Resume resume,
ref DumpLog dumpLog, Encoding encoding, string outputPrefix,
string outputPath,
Dictionary<string, string> formatOptions)
{
bool sense;
ulong blocks;
@@ -101,6 +107,7 @@ namespace DiscImageChef.Core.Devices.Dumping
uint blocksToRead;
bool aborted = false;
System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;
Modes.DecodedMode? decMode = null;
dumpLog.WriteLine("Initializing reader.");
Reader scsiReader = new Reader(dev, dev.Timeout, null, dumpRaw);
@@ -119,6 +126,7 @@ namespace DiscImageChef.Core.Devices.Dumping
DicConsole.WriteLine("Media has {0} blocks of {1} bytes/each. (for a total of {2} bytes)", blocks,
blockSize, blocks * (ulong)blockSize);
}
// Check how many blocks to read, if error show and return
if(scsiReader.GetBlocksToRead())
{
@@ -140,135 +148,46 @@ namespace DiscImageChef.Core.Devices.Dumping
if(!opticalDisc)
{
sidecar.BlockMedia = new BlockMediaType[1];
sidecar.BlockMedia[0] = new BlockMediaType();
mediaTags = new Dictionary<MediaTagType, byte[]>();
// All USB flash drives report as removable, even if the media is not removable
if(!dev.IsRemovable || dev.IsUsb)
{
if(dev.IsUsb)
{
dumpLog.WriteLine("Reading USB descriptors.");
sidecar.BlockMedia[0].USB = new USBType
{
ProductID = dev.UsbProductId,
VendorID = dev.UsbVendorId,
Descriptors = new DumpType
{
Image = outputPrefix + ".usbdescriptors.bin",
Size = dev.UsbDescriptors.Length,
Checksums = Checksum.GetChecksums(dev.UsbDescriptors).ToArray()
}
};
DataFile.WriteTo("SCSI Dump", sidecar.BlockMedia[0].USB.Descriptors.Image, dev.UsbDescriptors);
}
byte[] cmdBuf;
if(dev.IsUsb && dev.UsbDescriptors != null)
mediaTags.Add(MediaTagType.USB_Descriptors, null);
if(dev.Type == DeviceType.ATAPI)
{
dumpLog.WriteLine("Requesting ATAPI IDENTIFY PACKET DEVICE.");
sense = dev.AtapiIdentify(out cmdBuf, out _);
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)
{
sidecar.BlockMedia[0].ATA = new ATAType
{
Identify = new DumpType
{
Image = outputPrefix + ".identify.bin",
Size = cmdBuf.Length,
Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
}
};
DataFile.WriteTo("SCSI Dump", sidecar.BlockMedia[0].ATA.Identify.Image, cmdBuf);
}
}
sense = dev.ScsiInquiry(out cmdBuf, out _);
if(!sense)
{
dumpLog.WriteLine("Requesting SCSI INQUIRY.");
sidecar.BlockMedia[0].SCSI = new SCSIType
{
Inquiry = new DumpType
{
Image = outputPrefix + ".inquiry.bin",
Size = cmdBuf.Length,
Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
}
};
DataFile.WriteTo("SCSI Dump", sidecar.BlockMedia[0].SCSI.Inquiry.Image, cmdBuf);
dumpLog.WriteLine("Reading SCSI Extended Vendor Page Descriptors.");
sense = dev.ScsiInquiry(out cmdBuf, out _, 0x00);
if(!sense)
{
byte[] pages = EVPD.DecodePage00(cmdBuf);
if(pages != null)
{
List<EVPDType> evpds = new List<EVPDType>();
foreach(byte page in pages)
{
dumpLog.WriteLine("Requesting page {0:X2}h.", page);
sense = dev.ScsiInquiry(out cmdBuf, out _, page);
if(sense) continue;
EVPDType evpd = new EVPDType
{
Image = $"{outputPrefix}.evpd_{page:X2}h.bin",
Checksums = Checksum.GetChecksums(cmdBuf).ToArray(),
Size = cmdBuf.Length
};
evpd.Checksums = Checksum.GetChecksums(cmdBuf).ToArray();
DataFile.WriteTo("SCSI Dump", evpd.Image, cmdBuf);
evpds.Add(evpd);
}
if(evpds.Count > 0) sidecar.BlockMedia[0].SCSI.EVPD = evpds.ToArray();
}
}
dumpLog.WriteLine("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 _);
Modes.DecodedMode? decMode = null;
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);
sidecar.BlockMedia[0].SCSI.ModeSense10 = new DumpType
{
Image = outputPrefix + ".modesense10.bin",
Size = cmdBuf.Length,
Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.BlockMedia[0].SCSI.ModeSense10.Image, cmdBuf);
}
dumpLog.WriteLine("Requesting MODE SENSE (6).");
sense = dev.ModeSense6(out cmdBuf, out _, 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.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);
sidecar.BlockMedia[0].SCSI.ModeSense = new DumpType
{
Image = outputPrefix + ".modesense.bin",
Size = cmdBuf.Length,
Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.BlockMedia[0].SCSI.ModeSense.Image, cmdBuf);
}
if(decMode.HasValue)
@@ -284,7 +203,6 @@ namespace DiscImageChef.Core.Devices.Dumping
}
}
}
}
if(dskType == MediaType.Unknown)
dskType = MediaTypeFromScsi.Get((byte)dev.ScsiType, dev.Manufacturer, dev.Model, scsiMediumType,
@@ -334,23 +252,91 @@ namespace DiscImageChef.Core.Devices.Dumping
blockSize = longBlockSize;
}
bool ret = true;
foreach(MediaTagType tag in mediaTags.Keys)
{
if(outputPlugin.SupportedMediaTags.Contains(tag)) continue;
ret = false;
dumpLog.WriteLine($"Output format does not support {tag}.");
DicConsole.ErrorWriteLine($"Output format does not support {tag}.");
}
if(!ret)
{
dumpLog.WriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
DicConsole.ErrorWriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
if(!force) return;
}
DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead);
string outputExtension = ".bin";
if(opticalDisc && blockSize == 2048) outputExtension = ".iso";
MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", SBC_PROFILE);
DataFile dumpFile = new DataFile(outputPrefix + outputExtension);
ret = outputPlugin.Create(outputPath, dskType, formatOptions, blocks, blockSize);
// Cannot create image
if(!ret)
{
dumpLog.WriteLine("Error creating output image, not continuing.");
dumpLog.WriteLine(outputPlugin.ErrorMessage);
DicConsole.ErrorWriteLine("Error creating output image, not continuing.");
DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage);
return;
}
start = DateTime.UtcNow;
if(alcohol != null && !dumpRaw)
if(opticalDisc)
outputPlugin.SetTracks(new List<Track>
{
alcohol.AddSessions(new[] {new Session {StartTrack = 1, EndTrack = 1, SessionSequence = 1}});
alcohol.AddTrack(20, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1);
alcohol.SetExtension(outputExtension);
alcohol.SetTrackSizes(1, (int)blockSize, 0, 0, (long)blocks);
alcohol.SetTrackTypes(1, TrackType.Data, TrackSubchannelType.None);
new Track
{
TrackBytesPerSector = (int)blockSize,
TrackEndSector = blocks - 1,
TrackSequence = 1,
TrackRawBytesPerSector = (int)blockSize,
TrackSubchannelType = TrackSubchannelType.None,
TrackSession = 1
}
});
else if(decMode.HasValue)
{
bool setGeometry = false;
foreach(Modes.ModePage page in decMode.Value.Pages)
if(page.Page == 0x04 && page.Subpage == 0x00)
{
Modes.ModePage_04? rigidPage = Modes.DecodeModePage_04(page.PageResponse);
if(!rigidPage.HasValue || setGeometry) continue;
dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track",
rigidPage.Value.Cylinders, rigidPage.Value.Heads,
(uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads)));
DicConsole.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)));
outputPlugin.SetGeometry(rigidPage.Value.Cylinders, rigidPage.Value.Heads,
(uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads)));
setGeometry = true;
}
else if(page.Page == 0x05 && page.Subpage == 0x00)
{
Modes.ModePage_05? flexiblePage = Modes.DecodeModePage_05(page.PageResponse);
if(!flexiblePage.HasValue) continue;
dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track",
flexiblePage.Value.Cylinders, flexiblePage.Value.Heads,
flexiblePage.Value.SectorsPerTrack);
DicConsole.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track",
flexiblePage.Value.Cylinders, flexiblePage.Value.Heads,
flexiblePage.Value.SectorsPerTrack);
outputPlugin.SetGeometry(flexiblePage.Value.Cylinders, flexiblePage.Value.Heads,
flexiblePage.Value.SectorsPerTrack);
setGeometry = true;
}
}
DumpHardwareType currentTry = null;
@@ -360,7 +346,6 @@ namespace DiscImageChef.Core.Devices.Dumping
if(currentTry == null || extents == null)
throw new InvalidOperationException("Could not process resume file, not continuing...");
dumpFile.Seek(resume.NextBlock, blockSize);
if(resume.NextBlock > 0) dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock);
for(ulong i = resume.NextBlock; i < blocks; i += blocksToRead)
@@ -374,10 +359,10 @@ namespace DiscImageChef.Core.Devices.Dumping
if(blocks - i < blocksToRead) blocksToRead = (uint)(blocks - i);
#pragma warning disable 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;
if(currentSpeed < minSpeed && currentSpeed != 0) minSpeed = currentSpeed;
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed);
@@ -388,7 +373,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{
mhddLog.Write(i, cmdDuration);
ibgLog.Write(i, currentSpeed * 1024);
dumpFile.Write(readBuffer);
outputPlugin.WriteSectors(readBuffer, i, blocksToRead);
extents.Add(i, blocksToRead, true);
}
else
@@ -397,7 +382,7 @@ namespace DiscImageChef.Core.Devices.Dumping
if(stopOnError) return; // TODO: Return more cleanly
// Write empty data
dumpFile.Write(new byte[blockSize * blocksToRead]);
outputPlugin.WriteSectors(new byte[blockSize * blocksToRead], i, blocksToRead);
for(ulong b = i; b < i + blocksToRead; b++) resume.BadBlocks.Add(b);
@@ -407,7 +392,8 @@ namespace DiscImageChef.Core.Devices.Dumping
dumpLog.WriteLine("Error reading {0} blocks from block {1}.", blocksToRead, i);
}
double newSpeed = (double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000);
double newSpeed =
(double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
resume.NextBlock = i + blocksToRead;
}
@@ -416,8 +402,10 @@ namespace DiscImageChef.Core.Devices.Dumping
DicConsole.WriteLine();
mhddLog.Close();
ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
blockSize * (double)(blocks + 1) /
1024 / (totalDuration / 1000), devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.",
(end - start).TotalSeconds);
dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
@@ -450,10 +438,11 @@ namespace DiscImageChef.Core.Devices.Dumping
{
resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
dumpFile.WriteAt(readBuffer, badSector, blockSize);
outputPlugin.WriteSector(readBuffer, badSector);
dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
}
else if(runningPersistent) dumpFile.WriteAt(readBuffer, badSector, blockSize);
else if(runningPersistent)
outputPlugin.WriteSector(readBuffer, badSector);
}
if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0)
@@ -553,265 +542,286 @@ namespace DiscImageChef.Core.Devices.Dumping
}
#endregion Error handling
resume.BadBlocks.Sort();
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
Checksum dataChk = new Checksum();
dumpFile.Seek(0, SeekOrigin.Begin);
blocksToRead = 500;
dumpLog.WriteLine("Checksum starts.");
for(ulong i = 0; i < blocks; i += blocksToRead)
if(!aborted)
if(opticalDisc)
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags)
{
if(aborted)
{
dumpLog.WriteLine("Aborted!");
break;
}
ret = outputPlugin.WriteMediaTag(tag.Value, tag.Key);
if(blocks - i < blocksToRead) blocksToRead = (uint)(blocks - i);
if(ret || force) continue;
DicConsole.Write("\rChecksumming sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed);
DateTime chkStart = DateTime.UtcNow;
byte[] dataToCheck = new byte[blockSize * blocksToRead];
dumpFile.Read(dataToCheck, 0, (int)(blockSize * blocksToRead));
dataChk.Update(dataToCheck);
DateTime chkEnd = DateTime.UtcNow;
double chkDuration = (chkEnd - chkStart).TotalMilliseconds;
totalChkDuration += chkDuration;
double newSpeed = (double)blockSize * blocksToRead / 1048576 / (chkDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
}
DicConsole.WriteLine();
dumpFile.Close();
end = DateTime.UtcNow;
dumpLog.WriteLine("Checksum finished in {0} seconds.", (end - start).TotalSeconds);
dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));
PluginBase plugins = new PluginBase();
FiltersList filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(outputPrefix + outputExtension);
if(inputFilter == null)
{
DicConsole.ErrorWriteLine("Cannot open file just created, this should not happen.");
return;
}
IMediaImage imageFormat = ImageFormat.Detect(inputFilter);
PartitionType[] xmlFileSysInfo = null;
try { if(!imageFormat.Open(inputFilter)) imageFormat = null; }
catch { imageFormat = null; }
if(imageFormat != null)
{
dumpLog.WriteLine("Getting partitions.");
List<Partition> partitions = Partitions.GetAll(imageFormat);
Partitions.AddSchemesToStats(partitions);
dumpLog.WriteLine("Found {0} partitions.", partitions.Count);
if(partitions.Count > 0)
{
xmlFileSysInfo = new PartitionType[partitions.Count];
for(int i = 0; i < partitions.Count; i++)
{
xmlFileSysInfo[i] = new PartitionType
{
Description = partitions[i].Description,
EndSector = (int)(partitions[i].Start + partitions[i].Length - 1),
Name = partitions[i].Name,
Sequence = (int)partitions[i].Sequence,
StartSector = (int)partitions[i].Start,
Type = partitions[i].Type
};
List<FileSystemType> lstFs = new List<FileSystemType>();
dumpLog.WriteLine("Getting filesystems on partition {0}, starting at {1}, ending at {2}, with type {3}, under scheme {4}.",
i, partitions[i].Start, partitions[i].End, partitions[i].Type,
partitions[i].Scheme);
foreach(IFilesystem plugin in plugins.PluginsList.Values)
try
{
if(!plugin.Identify(imageFormat, partitions[i])) continue;
plugin.GetInformation(imageFormat, partitions[i], out _, encoding);
lstFs.Add(plugin.XmlFsType);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
dumpLog.WriteLine("Filesystem {0} found.", plugin.XmlFsType.Type);
switch(plugin.XmlFsType.Type)
{
case "Opera":
dskType = MediaType.ThreeDO;
break;
case "PC Engine filesystem":
dskType = MediaType.SuperCDROM2;
break;
case "Nintendo Wii filesystem":
dskType = MediaType.WOD;
break;
case "Nintendo Gamecube filesystem":
dskType = MediaType.GOD;
break;
}
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
{
//DicConsole.DebugWriteLine("Dump-media command", "Plugin {0} crashed", _plugin.Name);
}
if(lstFs.Count > 0) xmlFileSysInfo[i].FileSystems = lstFs.ToArray();
}
// Cannot write tag to image
dumpLog.WriteLine($"Cannot write tag {tag.Key}.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
else
{
dumpLog.WriteLine("Getting filesystem for whole device.");
xmlFileSysInfo = new PartitionType[1];
xmlFileSysInfo[0] = new PartitionType {EndSector = (int)(blocks - 1), StartSector = 0};
List<FileSystemType> lstFs = new List<FileSystemType>();
Partition wholePart =
new Partition {Name = "Whole device", Length = blocks, Size = blocks * blockSize};
foreach(IFilesystem plugin in plugins.PluginsList.Values)
try
if(!dev.IsRemovable || dev.IsUsb)
{
if(!plugin.Identify(imageFormat, wholePart)) continue;
plugin.GetInformation(imageFormat, wholePart, out _, encoding);
lstFs.Add(plugin.XmlFsType);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
dumpLog.WriteLine("Filesystem {0} found.", plugin.XmlFsType.Type);
switch(plugin.XmlFsType.Type)
if(dev.IsUsb)
{
case "Opera":
dskType = MediaType.ThreeDO;
break;
case "PC Engine filesystem":
dskType = MediaType.SuperCDROM2;
break;
case "Nintendo Wii filesystem":
dskType = MediaType.WOD;
break;
case "Nintendo Gamecube filesystem":
dskType = MediaType.GOD;
break;
}
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
dumpLog.WriteLine("Reading USB descriptors.");
ret = outputPlugin.WriteMediaTag(dev.UsbDescriptors, MediaTagType.USB_Descriptors);
if(!ret && !force)
{
//DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name);
}
if(lstFs.Count > 0) xmlFileSysInfo[0].FileSystems = lstFs.ToArray();
dumpLog.WriteLine($"Cannot write USB descriptors.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
}
if(alcohol != null && !dumpRaw) alcohol.SetMediaType(dskType);
byte[] cmdBuf;
if(dev.Type == DeviceType.ATAPI)
{
dumpLog.WriteLine("Requesting ATAPI IDENTIFY PACKET DEVICE.");
sense = dev.AtapiIdentify(out cmdBuf, out _);
if(!sense)
{
ret = outputPlugin.WriteMediaTag(cmdBuf, MediaTagType.ATAPI_IDENTIFY);
if(!ret && !force)
{
dumpLog.WriteLine($"Cannot write ATAPI IDENTIFY PACKET DEVICE.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
}
}
sense = dev.ScsiInquiry(out cmdBuf, out _);
if(!sense)
{
dumpLog.WriteLine("Requesting SCSI INQUIRY.");
ret = outputPlugin.WriteMediaTag(cmdBuf, MediaTagType.SCSI_INQUIRY);
if(!ret && !force)
{
dumpLog.WriteLine($"Cannot write SCSI INQUIRY.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
dumpLog.WriteLine("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 _);
decMode = null;
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);
if(!ret && !force)
{
dumpLog.WriteLine($"Cannot write SCSI MODE SENSE (10).");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
}
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)
{
decMode = Modes.DecodeMode6(cmdBuf, dev.ScsiType);
ret = outputPlugin.WriteMediaTag(cmdBuf, MediaTagType.SCSI_MODESENSE_6);
if(!ret && !force)
{
dumpLog.WriteLine($"Cannot write SCSI MODE SENSE (6).");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
}
}
}
}
dumpLog.WriteLine("Closing output file.");
DicConsole.WriteLine("Closing output file.");
outputPlugin.Close();
resume.BadBlocks.Sort();
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
if(aborted)
{
dumpLog.WriteLine("Aborted!");
return;
}
dumpLog.WriteLine("Creating sidecar.");
FiltersList filters = new FiltersList();
IFilter filter = filters.GetFilter(outputPath);
IMediaImage inputPlugin = ImageFormat.Detect(filter);
if(!inputPlugin.Open(filter)) throw new ArgumentException("Could not open created image.");
DateTime chkStart = DateTime.UtcNow;
CICMMetadataType sidecar = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding);
end = DateTime.UtcNow;
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(opticalDisc)
{
sidecar.OpticalDisc[0].Checksums = dataChk.End().ToArray();
sidecar.OpticalDisc[0].DumpHardwareArray = resume.Tries.ToArray();
sidecar.OpticalDisc[0].Image = new ImageType
{
format = "Raw disk image (sector by sector copy)",
Value = outputPrefix + outputExtension
};
// TODO: Implement layers
//sidecar.OpticalDisc[0].Layers = new LayersType();
sidecar.OpticalDisc[0].Sessions = 1;
sidecar.OpticalDisc[0].Tracks = new[] {1};
sidecar.OpticalDisc[0].Track = new Schemas.TrackType[1];
sidecar.OpticalDisc[0].Track[0] = new Schemas.TrackType
{
BytesPerSector = (int)blockSize,
Checksums = sidecar.OpticalDisc[0].Checksums,
EndSector = (long)(blocks - 1),
Image =
new ImageType
{
format = "BINARY",
offset = 0,
offsetSpecified = true,
Value = sidecar.OpticalDisc[0].Image.Value
},
Sequence = new TrackSequenceType {Session = 1, TrackNumber = 1},
Size = (long)(blocks * blockSize),
StartSector = 0
};
if(xmlFileSysInfo != null) sidecar.OpticalDisc[0].Track[0].FileSystemInformation = xmlFileSysInfo;
switch(dskType)
{
case MediaType.DDCD:
case MediaType.DDCDR:
case MediaType.DDCDRW:
sidecar.OpticalDisc[0].Track[0].TrackType1 = TrackTypeTrackType.ddcd;
break;
case MediaType.DVDROM:
case MediaType.DVDR:
case MediaType.DVDRAM:
case MediaType.DVDRW:
case MediaType.DVDRDL:
case MediaType.DVDRWDL:
case MediaType.DVDDownload:
case MediaType.DVDPRW:
case MediaType.DVDPR:
case MediaType.DVDPRWDL:
case MediaType.DVDPRDL:
sidecar.OpticalDisc[0].Track[0].TrackType1 = TrackTypeTrackType.dvd;
break;
case MediaType.HDDVDROM:
case MediaType.HDDVDR:
case MediaType.HDDVDRAM:
case MediaType.HDDVDRW:
case MediaType.HDDVDRDL:
case MediaType.HDDVDRWDL:
sidecar.OpticalDisc[0].Track[0].TrackType1 = TrackTypeTrackType.hddvd;
break;
case MediaType.BDROM:
case MediaType.BDR:
case MediaType.BDRE:
case MediaType.BDREXL:
case MediaType.BDRXL:
sidecar.OpticalDisc[0].Track[0].TrackType1 = TrackTypeTrackType.bluray;
break;
}
sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType);
Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp, out string xmlDskSubTyp);
sidecar.OpticalDisc[0].DiscType = xmlDskTyp;
sidecar.OpticalDisc[0].DiscSubType = xmlDskSubTyp;
sidecar.OpticalDisc[0].DumpHardwareArray = resume.Tries.ToArray();
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags)
if(outputPlugin.SupportedMediaTags.Contains(tag.Key))
Mmc.AddMediaTagToSidecar(outputPath, tag, ref sidecar);
}
else
{
sidecar.BlockMedia[0].Checksums = dataChk.End().ToArray();
// All USB flash drives report as removable, even if the media is not removable
if(!dev.IsRemovable || dev.IsUsb)
{
if(dev.IsUsb)
if(outputPlugin.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors))
sidecar.BlockMedia[0].USB = new USBType
{
ProductID = dev.UsbProductId,
VendorID = dev.UsbVendorId,
Descriptors = new DumpType
{
Image = outputPath,
Size = dev.UsbDescriptors.Length,
Checksums = Checksum.GetChecksums(dev.UsbDescriptors).ToArray()
}
};
byte[] cmdBuf;
if(dev.Type == DeviceType.ATAPI)
{
sense = dev.AtapiIdentify(out cmdBuf, out _);
if(!sense)
if(outputPlugin.SupportedMediaTags.Contains(MediaTagType.ATAPI_IDENTIFY))
sidecar.BlockMedia[0].ATA = new ATAType
{
Identify = new DumpType
{
Image = outputPath,
Size = cmdBuf.Length,
Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
}
};
}
sense = dev.ScsiInquiry(out cmdBuf, out _);
if(!sense)
{
if(outputPlugin.SupportedMediaTags.Contains(MediaTagType.SCSI_INQUIRY))
sidecar.BlockMedia[0].SCSI = new SCSIType
{
Inquiry = new DumpType
{
Image = outputPath,
Size = cmdBuf.Length,
Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
}
};
// TODO: SCSI Extended Vendor Page descriptors
/*
dumpLog.WriteLine("Reading SCSI Extended Vendor Page Descriptors.");
sense = dev.ScsiInquiry(out cmdBuf, out _, 0x00);
if(!sense)
{
byte[] pages = EVPD.DecodePage00(cmdBuf);
if(pages != null)
{
List<EVPDType> evpds = new List<EVPDType>();
foreach(byte page in pages)
{
dumpLog.WriteLine("Requesting page {0:X2}h.", page);
sense = dev.ScsiInquiry(out cmdBuf, out _, page);
if(sense) continue;
EVPDType evpd = new EVPDType
{
Image = $"{outputPrefix}.evpd_{page:X2}h.bin",
Checksums = Checksum.GetChecksums(cmdBuf).ToArray(),
Size = cmdBuf.Length
};
evpd.Checksums = Checksum.GetChecksums(cmdBuf).ToArray();
DataFile.WriteTo("SCSI Dump", evpd.Image, cmdBuf);
evpds.Add(evpd);
}
if(evpds.Count > 0) sidecar.BlockMedia[0].SCSI.EVPD = evpds.ToArray();
}
}
*/
dumpLog.WriteLine("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 _);
decMode = null;
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 = cmdBuf.Length,
Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
};
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))
sidecar.BlockMedia[0].SCSI.ModeSense = new DumpType
{
Image = outputPath,
Size = cmdBuf.Length,
Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
};
}
}
sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType);
Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp, out string xmlDskSubTyp);
sidecar.BlockMedia[0].DiskType = xmlDskTyp;
sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp;
// TODO: Implement device firmware revision
sidecar.BlockMedia[0].Image = new ImageType
{
format = "Raw disk image (sector by sector copy)",
Value = outputPrefix + ".bin"
};
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";
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 = (long)blocks;
sidecar.BlockMedia[0].PhysicalBlockSize = (int)physicalBlockSize;
sidecar.BlockMedia[0].LogicalBlockSize = (int)logicalBlockSize;
@@ -819,7 +829,6 @@ namespace DiscImageChef.Core.Devices.Dumping
sidecar.BlockMedia[0].Model = dev.Model;
sidecar.BlockMedia[0].Serial = dev.Serial;
sidecar.BlockMedia[0].Size = (long)(blocks * blockSize);
if(xmlFileSysInfo != null) sidecar.BlockMedia[0].FileSystemInformation = xmlFileSysInfo;
if(dev.IsRemovable) sidecar.BlockMedia[0].DumpHardwareArray = resume.Tries.ToArray();
}
@@ -844,7 +853,6 @@ namespace DiscImageChef.Core.Devices.Dumping
XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType));
xmlSer.Serialize(xmlFs, sidecar);
xmlFs.Close();
if(alcohol != null && !dumpRaw) alcohol.Close();
}
Statistics.AddMedia(dskType, true);

View File

@@ -31,14 +31,15 @@
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using DiscImageChef.Console;
using DiscImageChef.Core.Logging;
using DiscImageChef.Decoders.SCSI;
using DiscImageChef.Devices;
using DiscImageChef.DiscImages;
using DiscImageChef.Metadata;
using Schemas;
using MediaType = DiscImageChef.CommonTypes.MediaType;
namespace DiscImageChef.Core.Devices.Dumping
@@ -55,6 +56,7 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="dev">Device</param>
/// <param name="devicePath">Path to the device</param>
/// <param name="outputPrefix">Prefix for output data files</param>
/// <param name="outputPlugin">Plugin for output file</param>
/// <param name="retryPasses">How many times to retry</param>
/// <param name="force">Force to continue dump whenever possible</param>
/// <param name="dumpRaw">Dump long or scrambled sectors</param>
@@ -63,12 +65,18 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="resume">Information for dump resuming</param>
/// <param name="dumpLog">Dump logger</param>
/// <param name="encoding">Encoding to use when analyzing dump</param>
/// <param name="separateSubchannel">Write subchannel separate from main channel</param>
/// <param name="dumpLeadIn">Try to read and dump as much Lead-in as possible</param>
/// <param name="outputPath">Path to output file</param>
/// <param name="formatOptions">Formats to pass to output file plugin</param>
/// <exception cref="ArgumentException">If you asked to dump long sectors from a SCSI Streaming device</exception>
public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force,
bool dumpRaw, bool persistent, bool stopOnError, bool separateSubchannel,
ref Resume resume, ref DumpLog dumpLog, bool dumpLeadIn, Encoding encoding)
public static void Dump(Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses,
bool force, bool dumpRaw, bool persistent, bool stopOnError,
ref Resume resume,
ref
DumpLog dumpLog, bool dumpLeadIn, Encoding encoding,
string outputPrefix,
string
outputPath, Dictionary<string, string> formatOptions)
{
MediaType dskType = MediaType.Unknown;
int resets = 0;
@@ -191,23 +199,22 @@ namespace DiscImageChef.Core.Devices.Dumping
}
}
CICMMetadataType sidecar = new CICMMetadataType();
switch(dev.ScsiType)
{
case PeripheralDeviceTypes.SequentialAccess:
if(dumpRaw) throw new ArgumentException("Tapes cannot be dumped raw.");
Ssc.Dump(dev, outputPrefix, devicePath, ref sidecar, ref resume, ref dumpLog);
Ssc.Dump(dev, outputPrefix, devicePath, ref resume, ref dumpLog);
return;
case PeripheralDeviceTypes.MultiMediaDevice:
Mmc.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError,
ref sidecar, ref dskType, separateSubchannel, ref resume, ref dumpLog, dumpLeadIn,
encoding);
Mmc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError,
ref dskType, ref resume, ref dumpLog, dumpLeadIn, encoding, outputPrefix, outputPath,
formatOptions);
return;
default:
Sbc.Dump(dev, devicePath, outputPrefix, retryPasses, force, dumpRaw, persistent, stopOnError,
ref sidecar, ref dskType, false, ref resume, ref dumpLog, encoding);
Sbc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError, null,
ref dskType, false, ref resume, ref dumpLog, encoding, outputPrefix, outputPath,
formatOptions);
break;
}
}

View File

@@ -47,6 +47,7 @@ using Version = DiscImageChef.Metadata.Version;
namespace DiscImageChef.Core.Devices.Dumping
{
// TODO: Add support for images
static class Ssc
{
/// <summary>
@@ -57,9 +58,7 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="outputPrefix">Prefix for output data files</param>
/// <param name="resume">Information for dump resuming</param>
/// <param name="dumpLog">Dump logger</param>
/// <param name="sidecar">Partially filled initialized sidecar</param>
internal static void Dump(Device dev, string outputPrefix, string devicePath, ref CICMMetadataType sidecar,
ref Resume resume,
internal static void Dump(Device dev, string outputPrefix, string devicePath, ref Resume resume,
ref DumpLog dumpLog)
{
FixedSense? fxSense;
@@ -75,6 +74,7 @@ namespace DiscImageChef.Core.Devices.Dumping
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
CICMMetadataType sidecar = new CICMMetadataType();
dev.RequestSense(out byte[] senseBuf, dev.Timeout, out double duration);
fxSense = Sense.DecodeFixed(senseBuf, out string strSense);

View File

@@ -33,20 +33,19 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using DiscImageChef.CommonTypes;
using DiscImageChef.Console;
using DiscImageChef.Core.Logging;
using DiscImageChef.Decoders.MMC;
using DiscImageChef.Devices;
using DiscImageChef.DiscImages;
using DiscImageChef.Filesystems;
using DiscImageChef.Filters;
using DiscImageChef.Metadata;
using Extents;
using Schemas;
using MediaType = DiscImageChef.Metadata.MediaType;
using MediaType = DiscImageChef.CommonTypes.MediaType;
namespace DiscImageChef.Core.Devices.Dumping
{
@@ -61,6 +60,7 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="dev">Device</param>
/// <param name="devicePath">Path to the device</param>
/// <param name="outputPrefix">Prefix for output data files</param>
/// <param name="outputPlugin">Plugin for output file</param>
/// <param name="retryPasses">How many times to retry</param>
/// <param name="force">Force to continue dump whenever possible</param>
/// <param name="dumpRaw">Dump long or scrambled sectors</param>
@@ -69,10 +69,16 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="resume">Information for dump resuming</param>
/// <param name="dumpLog">Dump logger</param>
/// <param name="encoding">Encoding to use when analyzing dump</param>
/// <param name="outputPath">Path to output file</param>
/// <param name="formatOptions">Formats to pass to output file plugin</param>
/// <exception cref="ArgumentException">If you asked to dump long sectors from a SCSI Streaming device</exception>
public static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force,
bool dumpRaw, bool persistent, bool stopOnError, ref Resume resume, ref DumpLog dumpLog,
Encoding encoding)
public static void Dump(Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses,
bool force, bool dumpRaw, bool persistent, bool stopOnError,
ref Resume resume,
ref
DumpLog dumpLog, Encoding encoding, string outputPrefix, string outputPath,
Dictionary<string, string>
formatOptions)
{
bool aborted;
@@ -93,8 +99,6 @@ namespace DiscImageChef.Core.Devices.Dumping
const uint TIMEOUT = 5;
double duration;
CICMMetadataType sidecar = new CICMMetadataType {BlockMedia = new[] {new BlockMediaType()}};
uint blocksToRead = 128;
uint blockSize = 512;
ulong blocks = 0;
@@ -105,6 +109,8 @@ namespace DiscImageChef.Core.Devices.Dumping
int physicalBlockSize = 0;
bool byteAddressed = true;
Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>();
switch(dev.Type)
{
case DeviceType.MMC:
@@ -118,9 +124,11 @@ namespace DiscImageChef.Core.Devices.Dumping
blocks = ecsdDecoded.SectorCount;
blockSize = (uint)(ecsdDecoded.SectorSize == 1 ? 4096 : 512);
if(ecsdDecoded.NativeSectorSize == 0) physicalBlockSize = 512;
else if(ecsdDecoded.NativeSectorSize == 1) physicalBlockSize = 4096;
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;
@@ -131,17 +139,20 @@ namespace DiscImageChef.Core.Devices.Dumping
if(blocks == 0)
{
CSD csdDecoded = Decoders.MMC.Decoders.DecodeCSD(csd);
blocks = (ulong)((csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2));
blocks =
(ulong)((csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2));
blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength);
}
mediaTags.Add(MediaTagType.MMC_CSD, null);
}
else csd = null;
dumpLog.WriteLine("Reading OCR");
sense = dev.ReadOcr(out ocr, out _, TIMEOUT, out duration);
if(sense) ocr = null;
else mediaTags.Add(MediaTagType.MMC_OCR, null);
sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();
break;
}
case DeviceType.SecureDigital:
@@ -152,23 +163,26 @@ namespace DiscImageChef.Core.Devices.Dumping
{
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) *
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;
dumpLog.WriteLine("Reading OCR");
sense = dev.ReadSdocr(out ocr, out _, TIMEOUT, out duration);
if(sense) ocr = null;
else mediaTags.Add(MediaTagType.SD_OCR, null);
dumpLog.WriteLine("Reading SCR");
sense = dev.ReadScr(out scr, out _, TIMEOUT, out duration);
if(sense) scr = null;
else mediaTags.Add(MediaTagType.SD_SCR, null);
sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType();
break;
}
}
@@ -176,80 +190,7 @@ namespace DiscImageChef.Core.Devices.Dumping
dumpLog.WriteLine("Reading CID");
sense = dev.ReadCid(out byte[] cid, out _, TIMEOUT, out duration);
if(sense) cid = null;
DumpType cidDump = null;
DumpType csdDump = null;
DumpType ocrDump = null;
if(cid != null)
{
cidDump = new DumpType
{
Image = outputPrefix + ".cid.bin",
Size = cid.Length,
Checksums = Checksum.GetChecksums(cid).ToArray()
};
DataFile.WriteTo("MMC/SecureDigital Dump", cidDump.Image, cid);
}
if(csd != null)
{
csdDump = new DumpType
{
Image = outputPrefix + ".csd.bin",
Size = csd.Length,
Checksums = Checksum.GetChecksums(csd).ToArray()
};
DataFile.WriteTo("MMC/SecureDigital Dump", csdDump.Image, csd);
}
if(ecsd != null)
{
sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD = new DumpType
{
Image = outputPrefix + ".ecsd.bin",
Size = ecsd.Length,
Checksums = Checksum.GetChecksums(ecsd).ToArray()
};
DataFile.WriteTo("MMC/SecureDigital Dump", sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD.Image,
ecsd);
}
if(ocr != null)
{
ocrDump = new DumpType
{
Image = outputPrefix + ".ocr.bin",
Size = ocr.Length,
Checksums = Checksum.GetChecksums(ocr).ToArray()
};
DataFile.WriteTo("MMC/SecureDigital Dump", ocrDump.Image, ocr);
}
if(scr != null)
{
sidecar.BlockMedia[0].SecureDigital.SCR = new DumpType
{
Image = outputPrefix + ".scr.bin",
Size = scr.Length,
Checksums = Checksum.GetChecksums(scr).ToArray()
};
DataFile.WriteTo("MMC/SecureDigital Dump", sidecar.BlockMedia[0].SecureDigital.SCR.Image, scr);
}
switch(dev.Type)
{
case DeviceType.MMC:
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;
}
else mediaTags.Add(dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID : MediaTagType.MMC_CID, null);
DateTime start;
DateTime end;
@@ -299,12 +240,44 @@ namespace DiscImageChef.Core.Devices.Dumping
if(currentTry == null || extents == null)
throw new InvalidOperationException("Could not process resume file, not continuing...");
bool ret = true;
foreach(MediaTagType tag in mediaTags.Keys)
{
if(outputPlugin.SupportedMediaTags.Contains(tag)) continue;
ret = false;
dumpLog.WriteLine($"Output format does not support {tag}.");
DicConsole.ErrorWriteLine($"Output format does not support {tag}.");
}
if(!ret)
{
dumpLog.WriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
DicConsole.ErrorWriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
if(!force) return;
}
DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead);
MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", SD_PROFILE);
DataFile dumpFile = new DataFile(outputPrefix + ".bin");
dumpFile.Seek(resume.NextBlock, blockSize);
ret = outputPlugin.Create(outputPath,
dev.Type == DeviceType.SecureDigital
? MediaType.SecureDigital
: MediaType.MMC,
formatOptions, blocks, blockSize);
// Cannot create image
if(!ret)
{
dumpLog.WriteLine("Error creating output image, not continuing.");
dumpLog.WriteLine(outputPlugin.ErrorMessage);
DicConsole.ErrorWriteLine("Error creating output image, not continuing.");
DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage);
return;
}
if(resume.NextBlock > 0) dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock);
start = DateTime.UtcNow;
@@ -319,10 +292,10 @@ namespace DiscImageChef.Core.Devices.Dumping
if(blocks - i < blocksToRead) blocksToRead = (byte)(blocks - i);
#pragma warning disable 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;
if(currentSpeed < minSpeed && currentSpeed != 0) minSpeed = currentSpeed;
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed);
@@ -333,7 +306,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{
mhddLog.Write(i, duration);
ibgLog.Write(i, currentSpeed * 1024);
dumpFile.Write(cmdBuf);
outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
extents.Add(i, blocksToRead, true);
}
else
@@ -343,11 +316,12 @@ namespace DiscImageChef.Core.Devices.Dumping
mhddLog.Write(i, duration < 500 ? 65535 : duration);
ibgLog.Write(i, 0);
dumpFile.Write(new byte[blockSize * blocksToRead]);
outputPlugin.WriteSectors(new byte[blockSize * blocksToRead], i, blocksToRead);
dumpLog.WriteLine("Error reading {0} blocks from block {1}.", blocksToRead, i);
}
double newSpeed = (double)blockSize * blocksToRead / 1048576 / (duration / 1000);
double newSpeed =
(double)blockSize * blocksToRead / 1048576 / (duration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
resume.NextBlock = i + blocksToRead;
}
@@ -356,8 +330,10 @@ namespace DiscImageChef.Core.Devices.Dumping
DicConsole.WriteLine();
mhddLog.Close();
ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
blockSize * (double)(blocks + 1) /
1024 / (totalDuration / 1000), devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.",
(end - start).TotalSeconds);
dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
@@ -392,10 +368,11 @@ namespace DiscImageChef.Core.Devices.Dumping
{
resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
dumpFile.WriteAt(cmdBuf, badSector, blockSize);
outputPlugin.WriteSector(cmdBuf, badSector);
dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
}
else if(runningPersistent) dumpFile.WriteAt(cmdBuf, badSector, blockSize);
else if(runningPersistent)
outputPlugin.WriteSector(cmdBuf, badSector);
}
if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0)
@@ -413,171 +390,183 @@ namespace DiscImageChef.Core.Devices.Dumping
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
Checksum dataChk = new Checksum();
dumpFile.Seek(0, SeekOrigin.Begin);
blocksToRead = 500;
dumpLog.WriteLine("Closing output file.");
DicConsole.WriteLine("Closing output file.");
outputPlugin.Close();
dumpLog.WriteLine("Checksum starts.");
for(ulong i = 0; i < blocks; i += blocksToRead)
{
if(aborted)
{
dumpLog.WriteLine("Aborted!");
break;
}
if(blocks - i < blocksToRead) blocksToRead = (byte)(blocks - i);
DicConsole.Write("\rChecksumming sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed);
DateTime chkStart = DateTime.UtcNow;
byte[] dataToCheck = new byte[blockSize * blocksToRead];
dumpFile.Read(dataToCheck, 0, (int)(blockSize * blocksToRead));
dataChk.Update(dataToCheck);
DateTime chkEnd = DateTime.UtcNow;
double chkDuration = (chkEnd - chkStart).TotalMilliseconds;
totalChkDuration += chkDuration;
double newSpeed = (double)blockSize * blocksToRead / 1048576 / (chkDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
}
DicConsole.WriteLine();
dumpFile.Close();
end = DateTime.UtcNow;
dumpLog.WriteLine("Checksum finished in {0} seconds.", (end - start).TotalSeconds);
dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));
PluginBase plugins = new PluginBase();
FiltersList filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(outputPrefix + ".bin");
if(inputFilter == null)
{
DicConsole.ErrorWriteLine("Cannot open file just created, this should not happen.");
return;
}
IMediaImage imageFormat = ImageFormat.Detect(inputFilter);
PartitionType[] xmlFileSysInfo = null;
dumpLog.WriteLine("Creating sidecar.");
FiltersList filters = new FiltersList();
IFilter filter = filters.GetFilter(outputPath);
IMediaImage inputPlugin = ImageFormat.Detect(filter);
if(!inputPlugin.Open(filter)) throw new ArgumentException("Could not open created image.");
try { if(!imageFormat.Open(inputFilter)) imageFormat = null; }
catch { imageFormat = null; }
DateTime chkStart = DateTime.UtcNow;
CICMMetadataType sidecar = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding);
if(imageFormat != null)
switch(dev.Type)
{
dumpLog.WriteLine("Getting partitions.");
List<Partition> partitions = Partitions.GetAll(imageFormat);
Partitions.AddSchemesToStats(partitions);
dumpLog.WriteLine("Found {0} partitions.", partitions.Count);
case DeviceType.MMC:
sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();
break;
case DeviceType.SecureDigital:
sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType();
break;
}
if(partitions.Count > 0)
DumpType cidDump = null;
DumpType csdDump = null;
DumpType ocrDump = null;
if(cid != null)
{
xmlFileSysInfo = new PartitionType[partitions.Count];
for(int i = 0; i < partitions.Count; i++)
cidDump = new DumpType
{
xmlFileSysInfo[i] = new PartitionType
{
Description = partitions[i].Description,
EndSector = (int)(partitions[i].Start + partitions[i].Length - 1),
Name = partitions[i].Name,
Sequence = (int)partitions[i].Sequence,
StartSector = (int)partitions[i].Start,
Type = partitions[i].Type
Image = outputPath,
Size = cid.Length,
Checksums = Checksum.GetChecksums(cid).ToArray()
};
List<FileSystemType> lstFs = new List<FileSystemType>();
dumpLog.WriteLine("Getting filesystems on partition {0}, starting at {1}, ending at {2}, with type {3}, under scheme {4}.",
i, partitions[i].Start, partitions[i].End, partitions[i].Type,
partitions[i].Scheme);
foreach(IFilesystem plugin in plugins.PluginsList.Values)
try
ret =
outputPlugin.WriteMediaTag(cid,
dev.Type == DeviceType.SecureDigital
? MediaTagType.SD_CID
: MediaTagType.MMC_CID);
// Cannot write CID to image
if(!ret && !force)
{
if(!plugin.Identify(imageFormat, partitions[i])) continue;
plugin.GetInformation(imageFormat, partitions[i], out _, encoding);
lstFs.Add(plugin.XmlFsType);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
dumpLog.WriteLine("Filesystem {0} found.", plugin.XmlFsType.Type);
dumpLog.WriteLine("Cannot write CID to output image.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
}
if(csd != null)
{
//DicConsole.DebugWriteLine("Dump-media command", "Plugin {0} crashed", _plugin.Name);
}
if(lstFs.Count > 0) xmlFileSysInfo[i].FileSystems = lstFs.ToArray();
}
}
else
csdDump = new DumpType
{
dumpLog.WriteLine("Getting filesystem for whole device.");
Image = outputPath,
Size = csd.Length,
Checksums = Checksum.GetChecksums(csd).ToArray()
};
xmlFileSysInfo = new PartitionType[1];
xmlFileSysInfo[0] = new PartitionType {EndSector = (int)(blocks - 1), StartSector = 0};
List<FileSystemType> lstFs = new List<FileSystemType>();
ret =
outputPlugin.WriteMediaTag(csd,
dev.Type == DeviceType.SecureDigital
? MediaTagType.SD_CSD
: MediaTagType.MMC_CSD);
Partition wholePart =
new Partition {Name = "Whole device", Length = blocks, Size = blocks * blockSize};
foreach(IFilesystem plugin in plugins.PluginsList.Values)
try
// Cannot write CSD to image
if(!ret && !force)
{
if(!plugin.Identify(imageFormat, wholePart)) continue;
plugin.GetInformation(imageFormat, wholePart, out _, encoding);
lstFs.Add(plugin.XmlFsType);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
dumpLog.WriteLine("Filesystem {0} found.", plugin.XmlFsType.Type);
dumpLog.WriteLine("Cannot write CSD to output image.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
}
if(ecsd != null)
{
//DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name);
}
sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD = new DumpType
{
Image = outputPath,
Size = ecsd.Length,
Checksums = Checksum.GetChecksums(ecsd).ToArray()
};
if(lstFs.Count > 0) xmlFileSysInfo[0].FileSystems = lstFs.ToArray();
ret = outputPlugin.WriteMediaTag(ecsd, MediaTagType.MMC_ExtendedCSD);
// Cannot write Extended CSD to image
if(!ret && !force)
{
dumpLog.WriteLine("Cannot write Extended CSD to output image.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
}
sidecar.BlockMedia[0].Checksums = dataChk.End().ToArray();
if(ocr != null)
{
ocrDump = new DumpType
{
Image = outputPath,
Size = ocr.Length,
Checksums = Checksum.GetChecksums(ocr).ToArray()
};
ret =
outputPlugin.WriteMediaTag(ocr,
dev.Type == DeviceType.SecureDigital
? MediaTagType.SD_OCR
: MediaTagType.MMC_OCR);
// Cannot write OCR to image
if(!ret && !force)
{
dumpLog.WriteLine("Cannot write OCR to output image.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
}
if(scr != null)
{
sidecar.BlockMedia[0].SecureDigital.SCR = new DumpType
{
Image = outputPath,
Size = scr.Length,
Checksums = Checksum.GetChecksums(scr).ToArray()
};
ret = outputPlugin.WriteMediaTag(scr, MediaTagType.SD_SCR);
// Cannot write SCR to image
if(!ret && !force)
{
dumpLog.WriteLine("Cannot write SCR to output image.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
}
switch(dev.Type)
{
case DeviceType.MMC:
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;
}
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));
string xmlDskTyp = null, xmlDskSubTyp = null;
switch(dev.Type)
{
case DeviceType.MMC:
MediaType.MediaTypeToString(CommonTypes.MediaType.MMC, out xmlDskTyp, out xmlDskSubTyp);
sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(CommonTypes.MediaType.MMC);
Metadata.MediaType.MediaTypeToString(MediaType.MMC, out xmlDskTyp, out xmlDskSubTyp);
sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.MMC);
break;
case DeviceType.SecureDigital:
MediaType.MediaTypeToString(CommonTypes.MediaType.SecureDigital, out xmlDskTyp, out xmlDskSubTyp);
sidecar.BlockMedia[0].Dimensions =
Dimensions.DimensionsFromMediaType(CommonTypes.MediaType.SecureDigital);
Metadata.MediaType.MediaTypeToString(MediaType.SecureDigital, out xmlDskTyp, out xmlDskSubTyp);
sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.SecureDigital);
break;
}
sidecar.BlockMedia[0].DiskType = xmlDskTyp;
sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp;
// TODO: Implement device firmware revision
sidecar.BlockMedia[0].Image = new ImageType
{
format = "Raw disk image (sector by sector copy)",
Value = outputPrefix + ".bin"
};
switch(dev.Type)
{
case DeviceType.MMC:
sidecar.BlockMedia[0].Interface = "MultiMediaCard";
break;
case DeviceType.SecureDigital:
sidecar.BlockMedia[0].Interface = "SecureDigital";
break;
}
sidecar.BlockMedia[0].LogicalBlocks = (long)blocks;
sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize > 0 ? physicalBlockSize : (int)blockSize;
sidecar.BlockMedia[0].LogicalBlockSize = (int)blockSize;
@@ -585,7 +574,6 @@ namespace DiscImageChef.Core.Devices.Dumping
sidecar.BlockMedia[0].Model = dev.Model;
sidecar.BlockMedia[0].Serial = dev.Serial;
sidecar.BlockMedia[0].Size = (long)(blocks * blockSize);
if(xmlFileSysInfo != null) sidecar.BlockMedia[0].FileSystemInformation = xmlFileSysInfo;
DicConsole.WriteLine();
@@ -613,10 +601,10 @@ namespace DiscImageChef.Core.Devices.Dumping
switch(dev.Type)
{
case DeviceType.MMC:
Statistics.AddMedia(CommonTypes.MediaType.MMC, true);
Statistics.AddMedia(MediaType.MMC, true);
break;
case DeviceType.SecureDigital:
Statistics.AddMedia(CommonTypes.MediaType.SecureDigital, true);
Statistics.AddMedia(MediaType.SecureDigital, true);
break;
}
}

View File

@@ -33,9 +33,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using DiscImageChef.CommonTypes;
using DiscImageChef.Console;
using DiscImageChef.Core.Logging;
using DiscImageChef.Decoders.DVD;
@@ -43,13 +43,11 @@ using DiscImageChef.Decoders.SCSI;
using DiscImageChef.Decoders.Xbox;
using DiscImageChef.Devices;
using DiscImageChef.DiscImages;
using DiscImageChef.Filesystems;
using DiscImageChef.Filters;
using DiscImageChef.Metadata;
using Extents;
using Schemas;
using MediaType = DiscImageChef.CommonTypes.MediaType;
using TrackType = Schemas.TrackType;
namespace DiscImageChef.Core.Devices.Dumping
{
@@ -64,6 +62,7 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="dev">Device</param>
/// <param name="devicePath">Path to the device</param>
/// <param name="outputPrefix">Prefix for output data files</param>
/// <param name="outputPlugin">Plugin for output file</param>
/// <param name="retryPasses">How many times to retry</param>
/// <param name="force">Force to continue dump whenever possible</param>
/// <param name="dumpRaw">Dump raw/long sectors</param>
@@ -72,15 +71,23 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="resume">Information for dump resuming</param>
/// <param name="dumpLog">Dump logger</param>
/// <param name="encoding">Encoding to use when analyzing dump</param>
/// <param name="sidecar">Partially filled initialized sidecar</param>
/// <param name="mediaTags">Media tags as retrieved in MMC layer</param>
/// <param name="dskType">Disc type as detected in MMC layer</param>
/// <param name="outputPath">Path to output file</param>
/// <param name="formatOptions">Formats to pass to output file plugin</param>
/// <exception cref="InvalidOperationException">
/// If the provided resume does not correspond with the current in progress
/// dump
/// </exception>
internal static void Dump(Device dev, string devicePath, string outputPrefix, ushort retryPasses, bool force,
bool dumpRaw, bool persistent, bool stopOnError, ref CICMMetadataType sidecar,
ref MediaType dskType, ref Resume resume, ref DumpLog dumpLog, Encoding encoding)
internal static void Dump(Device dev, string devicePath,
IWritableImage outputPlugin, ushort retryPasses,
bool force, bool dumpRaw,
bool persistent, bool stopOnError,
Dictionary<MediaTagType, byte[]> mediaTags, ref MediaType dskType,
ref Resume resume,
ref DumpLog dumpLog,
Encoding encoding, string outputPrefix, string outputPath,
Dictionary<string, string> formatOptions)
{
bool sense;
ulong blocks;
@@ -96,6 +103,9 @@ namespace DiscImageChef.Core.Devices.Dumping
bool aborted = false;
System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;
if(mediaTags.ContainsKey(MediaTagType.DVD_PFI)) mediaTags.Remove(MediaTagType.DVD_PFI);
if(mediaTags.ContainsKey(MediaTagType.DVD_DMI)) mediaTags.Remove(MediaTagType.DVD_DMI);
dumpLog.WriteLine("Reading Xbox Security Sector.");
sense = dev.KreonExtractSs(out byte[] ssBuf, out byte[] senseBuf, dev.Timeout, out _);
if(sense)
@@ -116,24 +126,7 @@ namespace DiscImageChef.Core.Devices.Dumping
byte[] tmpBuf = new byte[ssBuf.Length - 4];
Array.Copy(ssBuf, 4, tmpBuf, 0, ssBuf.Length - 4);
sidecar.OpticalDisc[0].Xbox = new XboxType
{
SecuritySectors = new[]
{
new XboxSecuritySectorsType
{
RequestNumber = 0,
RequestVersion = 1,
SecuritySectors = new DumpType
{
Image = outputPrefix + ".ss.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
}
}
}
};
DataFile.WriteTo("SCSI Dump", outputPrefix + ".ss.bin", ssBuf);
mediaTags.Add(MediaTagType.Xbox_SecuritySector, tmpBuf);
ulong l0Video, l1Video, middleZone, gameSize, totalSize, layerBreak;
@@ -170,13 +163,7 @@ namespace DiscImageChef.Core.Devices.Dumping
tmpBuf = new byte[readBuffer.Length - 4];
Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4);
sidecar.OpticalDisc[0].PFI = new DumpType
{
Image = outputPrefix + ".pfi.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].PFI.Image, tmpBuf, "Locked PFI", true);
mediaTags.Add(MediaTagType.DVD_PFI, tmpBuf);
DicConsole.DebugWriteLine("Dump-media command", "Video partition total size: {0} sectors", totalSize);
l0Video = PFI.Decode(readBuffer).Value.Layer0EndPSN - PFI.Decode(readBuffer).Value.DataAreaStartPSN + 1;
l1Video = totalSize - l0Video + 1;
@@ -192,13 +179,7 @@ namespace DiscImageChef.Core.Devices.Dumping
tmpBuf = new byte[readBuffer.Length - 4];
Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4);
sidecar.OpticalDisc[0].DMI = new DumpType
{
Image = outputPrefix + ".dmi.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].DMI.Image, tmpBuf, "Locked DMI", true);
mediaTags.Add(MediaTagType.DVD_DMI, tmpBuf);
// Get game partition size
DicConsole.DebugWriteLine("Dump-media command", "Getting game partition size");
@@ -258,18 +239,13 @@ namespace DiscImageChef.Core.Devices.Dumping
DicConsole.DebugWriteLine("Dump-media command", "Unlocked total size: {0} sectors", totalSize);
blocks = totalSize + 1;
middleZone =
totalSize - (PFI.Decode(readBuffer).Value.Layer0EndPSN - PFI.Decode(readBuffer).Value.DataAreaStartPSN +
totalSize - (PFI.Decode(readBuffer).Value.Layer0EndPSN -
PFI.Decode(readBuffer).Value.DataAreaStartPSN +
1) - gameSize + 1;
tmpBuf = new byte[readBuffer.Length - 4];
Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4);
sidecar.OpticalDisc[0].Xbox.PFI = new DumpType
{
Image = outputPrefix + ".xboxpfi.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].Xbox.PFI.Image, tmpBuf, "Unlocked PFI", true);
mediaTags.Add(MediaTagType.Xbox_PFI, tmpBuf);
dumpLog.WriteLine("Reading Disc Manufacturing Information.");
sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0,
@@ -283,13 +259,7 @@ namespace DiscImageChef.Core.Devices.Dumping
tmpBuf = new byte[readBuffer.Length - 4];
Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4);
sidecar.OpticalDisc[0].Xbox.DMI = new DumpType
{
Image = outputPrefix + ".xboxdmi.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].Xbox.DMI.Image, tmpBuf, "Unlocked DMI", true);
mediaTags.Add(MediaTagType.Xbox_DMI, tmpBuf);
totalSize = l0Video + l1Video + middleZone * 2 + gameSize;
layerBreak = l0Video + middleZone + gameSize / 2;
@@ -340,12 +310,40 @@ namespace DiscImageChef.Core.Devices.Dumping
return;
}
bool ret = true;
foreach(MediaTagType tag in mediaTags.Keys)
{
if(outputPlugin.SupportedMediaTags.Contains(tag)) continue;
ret = false;
dumpLog.WriteLine($"Output format does not support {tag}.");
DicConsole.ErrorWriteLine($"Output format does not support {tag}.");
}
if(!ret)
{
dumpLog.WriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
DicConsole.ErrorWriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
if(!force) return;
}
dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead);
DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead);
MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, BLOCK_SIZE, blocksToRead);
IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", 0x0010);
DataFile dumpFile = new DataFile(outputPrefix + ".iso");
ret = outputPlugin.Create(outputPath, dskType, formatOptions, blocks, BLOCK_SIZE);
// Cannot create image
if(!ret)
{
dumpLog.WriteLine("Error creating output image, not continuing.");
dumpLog.WriteLine(outputPlugin.ErrorMessage);
DicConsole.ErrorWriteLine("Error creating output image, not continuing.");
DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage);
return;
}
start = DateTime.UtcNow;
@@ -359,7 +357,6 @@ namespace DiscImageChef.Core.Devices.Dumping
throw new NotImplementedException("Could not process resume file, not continuing...");
ulong currentSector = resume.NextBlock;
dumpFile.Seek(resume.NextBlock, BLOCK_SIZE);
if(resume.NextBlock > 0) dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock);
dumpLog.WriteLine("Reading game partition.");
@@ -412,10 +409,10 @@ namespace DiscImageChef.Core.Devices.Dumping
if(extentStart - i < blocksToRead) blocksToRead = (uint)(extentStart - i);
#pragma warning disable 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;
if(currentSpeed < minSpeed && currentSpeed != 0) minSpeed = currentSpeed;
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, totalSize, currentSpeed);
@@ -427,7 +424,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{
mhddLog.Write(i, cmdDuration);
ibgLog.Write(i, currentSpeed * 1024);
dumpFile.Write(readBuffer);
outputPlugin.WriteSectors(readBuffer, i, blocksToRead);
extents.Add(i, blocksToRead, true);
}
else
@@ -436,7 +433,7 @@ namespace DiscImageChef.Core.Devices.Dumping
if(stopOnError) return; // TODO: Return more cleanly
// Write empty data
dumpFile.Write(new byte[BLOCK_SIZE * blocksToRead]);
outputPlugin.WriteSectors(new byte[BLOCK_SIZE * blocksToRead], i, blocksToRead);
for(ulong b = i; b < i + blocksToRead; b++) resume.BadBlocks.Add(b);
@@ -452,7 +449,8 @@ namespace DiscImageChef.Core.Devices.Dumping
foreach(string senseLine in senseLines) dumpLog.WriteLine(senseLine);
}
double newSpeed = (double)BLOCK_SIZE * blocksToRead / 1048576 / (cmdDuration / 1000);
double newSpeed =
(double)BLOCK_SIZE * blocksToRead / 1048576 / (cmdDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
blocksToRead = saveBlocksToRead;
currentSector = i + 1;
@@ -473,7 +471,8 @@ namespace DiscImageChef.Core.Devices.Dumping
mhddLog.Write(i, cmdDuration);
ibgLog.Write(i, currentSpeed * 1024);
dumpFile.Write(new byte[blocksToRead * 2048]);
// Write empty data
outputPlugin.WriteSectors(new byte[BLOCK_SIZE * blocksToRead], i, blocksToRead);
blocksToRead = saveBlocksToRead;
extents.Add(i, blocksToRead, true);
currentSector = i + 1;
@@ -501,7 +500,8 @@ namespace DiscImageChef.Core.Devices.Dumping
mhddLog.Write(middle + currentSector, cmdDuration);
ibgLog.Write(middle + currentSector, currentSpeed * 1024);
dumpFile.Write(new byte[BLOCK_SIZE * blocksToRead]);
// Write empty data
outputPlugin.WriteSectors(new byte[BLOCK_SIZE * blocksToRead], middle + currentSector, blocksToRead);
extents.Add(currentSector, blocksToRead, true);
currentSector += blocksToRead;
@@ -539,10 +539,10 @@ namespace DiscImageChef.Core.Devices.Dumping
if(l0Video + l1Video - l1 < blocksToRead) blocksToRead = (uint)(l0Video + l1Video - l1);
#pragma warning disable 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;
if(currentSpeed < minSpeed && currentSpeed != 0) minSpeed = currentSpeed;
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", currentSector, totalSize,
currentSpeed);
@@ -555,7 +555,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{
mhddLog.Write(currentSector, cmdDuration);
ibgLog.Write(currentSector, currentSpeed * 1024);
dumpFile.Write(readBuffer);
outputPlugin.WriteSectors(readBuffer, currentSector, blocksToRead);
extents.Add(currentSector, blocksToRead, true);
}
else
@@ -564,7 +564,7 @@ namespace DiscImageChef.Core.Devices.Dumping
if(stopOnError) return; // TODO: Return more cleanly
// Write empty data
dumpFile.Write(new byte[BLOCK_SIZE * blocksToRead]);
outputPlugin.WriteSectors(new byte[BLOCK_SIZE * blocksToRead], currentSector, blocksToRead);
// TODO: Handle errors in video partition
//errored += blocksToRead;
@@ -579,7 +579,8 @@ namespace DiscImageChef.Core.Devices.Dumping
foreach(string senseLine in senseLines) dumpLog.WriteLine(senseLine);
}
double newSpeed = (double)BLOCK_SIZE * blocksToRead / 1048576 / (cmdDuration / 1000);
double newSpeed =
(double)BLOCK_SIZE * blocksToRead / 1048576 / (cmdDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
currentSector += blocksToRead;
resume.NextBlock = currentSector;
@@ -605,8 +606,10 @@ namespace DiscImageChef.Core.Devices.Dumping
DicConsole.WriteLine();
mhddLog.Close();
ibgLog.Close(dev, blocks, BLOCK_SIZE, (end - start).TotalSeconds, currentSpeed * 1024,
BLOCK_SIZE * (double)(blocks + 1) / 1024 / (totalDuration / 1000), devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
BLOCK_SIZE * (double)(blocks + 1) /
1024 / (totalDuration / 1000), devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.",
(end - start).TotalSeconds);
dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
(double)BLOCK_SIZE * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
@@ -615,7 +618,9 @@ namespace DiscImageChef.Core.Devices.Dumping
{
List<ulong> tmpList = new List<ulong>();
foreach(ulong ur in resume.BadBlocks) for(ulong i = ur; i < ur + blocksToRead; i++) tmpList.Add(i);
foreach(ulong ur in resume.BadBlocks)
for(ulong i = ur; i < ur + blocksToRead; i++)
tmpList.Add(i);
tmpList.Sort();
@@ -648,10 +653,11 @@ namespace DiscImageChef.Core.Devices.Dumping
{
resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
dumpFile.WriteAt(readBuffer, badSector, BLOCK_SIZE);
outputPlugin.WriteSector(readBuffer, badSector);
dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
}
else if(runningPersistent) dumpFile.WriteAt(readBuffer, badSector, BLOCK_SIZE);
else if(runningPersistent)
outputPlugin.WriteSector(readBuffer, badSector);
}
if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0)
@@ -754,177 +760,47 @@ namespace DiscImageChef.Core.Devices.Dumping
resume.BadBlocks.Sort();
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
Checksum dataChk = new Checksum();
dumpFile.Seek(0, SeekOrigin.Begin);
blocksToRead = 500;
blocks = totalSize;
dumpLog.WriteLine("Checksum starts.");
for(ulong i = 0; i < blocks; i += blocksToRead)
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags)
{
ret = outputPlugin.WriteMediaTag(tag.Value, tag.Key);
if(ret || force) continue;
// Cannot write tag to image
dumpLog.WriteLine($"Cannot write tag {tag.Key}.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
dumpLog.WriteLine("Closing output file.");
DicConsole.WriteLine("Closing output file.");
outputPlugin.Close();
resume.BadBlocks.Sort();
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
if(aborted)
{
dumpLog.WriteLine("Aborted!");
break;
}
if(blocks - i < blocksToRead) blocksToRead = (uint)(blocks - i);
DicConsole.Write("\rChecksumming sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed);
DateTime chkStart = DateTime.UtcNow;
byte[] dataToCheck = new byte[BLOCK_SIZE * blocksToRead];
dumpFile.Read(dataToCheck, 0, (int)(BLOCK_SIZE * blocksToRead));
dataChk.Update(dataToCheck);
DateTime chkEnd = DateTime.UtcNow;
double chkDuration = (chkEnd - chkStart).TotalMilliseconds;
totalChkDuration += chkDuration;
double newSpeed = (double)BLOCK_SIZE * blocksToRead / 1048576 / (chkDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
}
DicConsole.WriteLine();
dumpFile.Close();
end = DateTime.UtcNow;
dumpLog.WriteLine("Checksum finished in {0} seconds.", (end - start).TotalSeconds);
dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
(double)BLOCK_SIZE * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));
PluginBase plugins = new PluginBase();
FiltersList filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(outputPrefix + ".iso");
if(inputFilter == null)
{
DicConsole.ErrorWriteLine("Cannot open file just created, this should not happen.");
return;
}
IMediaImage imageFormat = ImageFormat.Detect(inputFilter);
PartitionType[] xmlFileSysInfo = null;
dumpLog.WriteLine("Creating sidecar.");
FiltersList filters = new FiltersList();
IFilter filter = filters.GetFilter(outputPath);
IMediaImage inputPlugin = ImageFormat.Detect(filter);
if(!inputPlugin.Open(filter)) throw new ArgumentException("Could not open created image.");
try { if(!imageFormat.Open(inputFilter)) imageFormat = null; }
catch { imageFormat = null; }
DateTime chkStart = DateTime.UtcNow;
CICMMetadataType sidecar = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding);
end = DateTime.UtcNow;
if(imageFormat != null)
{
dumpLog.WriteLine("Getting partitions.");
List<Partition> partitions = Partitions.GetAll(imageFormat);
Partitions.AddSchemesToStats(partitions);
dumpLog.WriteLine("Found {0} partitions.", partitions.Count);
totalChkDuration = (end - chkStart).TotalMilliseconds;
dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);
dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
(double)BLOCK_SIZE * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));
if(partitions.Count > 0)
{
xmlFileSysInfo = new PartitionType[partitions.Count];
for(int i = 0; i < partitions.Count; i++)
{
xmlFileSysInfo[i] = new PartitionType
{
Description = partitions[i].Description,
EndSector = (int)(partitions[i].Start + partitions[i].Length - 1),
Name = partitions[i].Name,
Sequence = (int)partitions[i].Sequence,
StartSector = (int)partitions[i].Start,
Type = partitions[i].Type
};
List<FileSystemType> lstFs = new List<FileSystemType>();
dumpLog.WriteLine("Getting filesystems on partition {0}, starting at {1}, ending at {2}, with type {3}, under scheme {4}.",
i, partitions[i].Start, partitions[i].End, partitions[i].Type,
partitions[i].Scheme);
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags)
Mmc.AddMediaTagToSidecar(outputPath, tag, ref sidecar);
foreach(IFilesystem plugin in plugins.PluginsList.Values)
try
{
if(!plugin.Identify(imageFormat, partitions[i])) continue;
plugin.GetInformation(imageFormat, partitions[i], out _, encoding);
lstFs.Add(plugin.XmlFsType);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
dumpLog.WriteLine("Filesystem {0} found.", plugin.XmlFsType.Type);
switch(plugin.XmlFsType.Type)
{
case "Opera":
dskType = MediaType.ThreeDO;
break;
case "PC Engine filesystem":
dskType = MediaType.SuperCDROM2;
break;
case "Nintendo Wii filesystem":
dskType = MediaType.WOD;
break;
case "Nintendo Gamecube filesystem":
dskType = MediaType.GOD;
break;
}
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
{
//DicConsole.DebugWriteLine("Dump-media command", "Plugin {0} crashed", _plugin.Name);
}
if(lstFs.Count > 0) xmlFileSysInfo[i].FileSystems = lstFs.ToArray();
}
}
else
{
dumpLog.WriteLine("Getting filesystem for whole device.");
xmlFileSysInfo = new PartitionType[1];
xmlFileSysInfo[0] = new PartitionType {EndSector = (int)(blocks - 1), StartSector = 0};
List<FileSystemType> lstFs = new List<FileSystemType>();
Partition wholePart =
new Partition {Name = "Whole device", Length = blocks, Size = blocks * BLOCK_SIZE};
foreach(IFilesystem plugin in plugins.PluginsList.Values)
try
{
if(!plugin.Identify(imageFormat, wholePart)) continue;
plugin.GetInformation(imageFormat, wholePart, out _, encoding);
lstFs.Add(plugin.XmlFsType);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
dumpLog.WriteLine("Filesystem {0} found.", plugin.XmlFsType.Type);
switch(plugin.XmlFsType.Type)
{
case "Opera":
dskType = MediaType.ThreeDO;
break;
case "PC Engine filesystem":
dskType = MediaType.SuperCDROM2;
break;
case "Nintendo Wii filesystem":
dskType = MediaType.WOD;
break;
case "Nintendo Gamecube filesystem":
dskType = MediaType.GOD;
break;
}
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
{
//DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name);
}
if(lstFs.Count > 0) xmlFileSysInfo[0].FileSystems = lstFs.ToArray();
}
}
sidecar.OpticalDisc[0].Checksums = dataChk.End().ToArray();
sidecar.OpticalDisc[0].DumpHardwareArray = resume.Tries.ToArray();
sidecar.OpticalDisc[0].Image = new ImageType
{
format = "Raw disk image (sector by sector copy)",
Value = outputPrefix + ".iso"
};
sidecar.OpticalDisc[0].Layers = new LayersType
{
type = LayersTypeType.OTP,
@@ -933,32 +809,15 @@ namespace DiscImageChef.Core.Devices.Dumping
};
sidecar.OpticalDisc[0].Layers.Sectors[0] = new SectorsType {Value = (long)layerBreak};
sidecar.OpticalDisc[0].Sessions = 1;
sidecar.OpticalDisc[0].Tracks = new[] {1};
sidecar.OpticalDisc[0].Track = new TrackType[1];
sidecar.OpticalDisc[0].Track[0] = new TrackType
{
BytesPerSector = (int)BLOCK_SIZE,
Checksums = sidecar.OpticalDisc[0].Checksums,
EndSector = (long)(blocks - 1),
Image =
new ImageType
{
format = "BINARY",
offset = 0,
offsetSpecified = true,
Value = sidecar.OpticalDisc[0].Image.Value
},
Sequence = new TrackSequenceType {Session = 1, TrackNumber = 1},
Size = (long)(totalSize * BLOCK_SIZE),
StartSector = 0
};
if(xmlFileSysInfo != null) sidecar.OpticalDisc[0].Track[0].FileSystemInformation = xmlFileSysInfo;
sidecar.OpticalDisc[0].Track[0].TrackType1 = TrackTypeTrackType.dvd;
sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType);
Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp, out string xmlDskSubTyp);
sidecar.OpticalDisc[0].DiscType = xmlDskTyp;
sidecar.OpticalDisc[0].DiscSubType = xmlDskSubTyp;
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags)
if(outputPlugin.SupportedMediaTags.Contains(tag.Key))
Mmc.AddMediaTagToSidecar(outputPath, tag, ref sidecar);
if(!aborted)
{
DicConsole.WriteLine("Writing metadata sidecar");

View File

@@ -88,7 +88,6 @@
<Compile Include="Sidecar\AudioMedia.cs" />
<Compile Include="Sidecar\BlockTape.cs" />
<Compile Include="Logging\DumpLog.cs" />
<Compile Include="Devices\Dumping\Alcohol120.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DiscImageChef.Console\DiscImageChef.Console.csproj">

View File

@@ -31,13 +31,17 @@
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using DiscImageChef.Console;
using DiscImageChef.Core;
using DiscImageChef.Core.Devices.Dumping;
using DiscImageChef.Core.Logging;
using DiscImageChef.Devices;
using DiscImageChef.DiscImages;
using DiscImageChef.Metadata;
namespace DiscImageChef.Commands
@@ -46,19 +50,35 @@ namespace DiscImageChef.Commands
{
internal static void DoDumpMedia(DumpMediaOptions options)
{
// TODO: Be able to cancel hashing
Sidecar.InitProgressEvent += Progress.InitProgress;
Sidecar.UpdateProgressEvent += Progress.UpdateProgress;
Sidecar.EndProgressEvent += Progress.EndProgress;
Sidecar.InitProgressEvent2 += Progress.InitProgress2;
Sidecar.UpdateProgressEvent2 += Progress.UpdateProgress2;
Sidecar.EndProgressEvent2 += Progress.EndProgress2;
Sidecar.UpdateStatusEvent += Progress.UpdateStatus;
DicConsole.DebugWriteLine("Dump-Media command", "--debug={0}", options.Debug);
DicConsole.DebugWriteLine("Dump-Media command", "--verbose={0}", options.Verbose);
DicConsole.DebugWriteLine("Dump-Media command", "--device={0}", options.DevicePath);
DicConsole.DebugWriteLine("Dump-Media command", "--output-prefix={0}", options.OutputPrefix);
DicConsole.DebugWriteLine("Dump-Media command", "--raw={0}", options.Raw);
DicConsole.DebugWriteLine("Dump-Media command", "--stop-on-error={0}", options.StopOnError);
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);
DicConsole.DebugWriteLine("Dump-Media command", "--resume={0}", options.Resume);
DicConsole.DebugWriteLine("Dump-Media command", "--lead-in={0}", options.LeadIn);
DicConsole.DebugWriteLine("Dump-Media command", "--encoding={0}", options.EncodingName);
DicConsole.DebugWriteLine("Dump-Media command", "--output={0}", options.OutputFile);
DicConsole.DebugWriteLine("Dump-Media command", "--format={0}", options.OutputFormat);
DicConsole.DebugWriteLine("Dump-Media command", "--force={0}", options.Force);
DicConsole.DebugWriteLine("Dump-Media command", "--options={0}", options.Options);
Dictionary<string, string> parsedOptions = Options.Parse(options.Options);
DicConsole.DebugWriteLine("Dump-Media command", "Parsed options:");
foreach(KeyValuePair<string, string> parsedOption in parsedOptions)
DicConsole.DebugWriteLine("Dump-Media command", "{0} = {1}", parsedOption.Key, parsedOption.Value);
Encoding encoding = null;
@@ -88,12 +108,15 @@ namespace DiscImageChef.Commands
Core.Statistics.AddDevice(dev);
string outputPrefix = Path.Combine(Path.GetDirectoryName(options.OutputFile),
Path.GetFileNameWithoutExtension(options.OutputFile));
Resume resume = null;
XmlSerializer xs = new XmlSerializer(typeof(Resume));
if(File.Exists(options.OutputPrefix + ".resume.xml") && options.Resume)
if(File.Exists(outputPrefix + ".resume.xml") && options.Resume)
try
{
StreamReader sr = new StreamReader(options.OutputPrefix + ".resume.xml");
StreamReader sr = new StreamReader(outputPrefix + ".resume.xml");
resume = (Resume)xs.Deserialize(sr);
sr.Close();
}
@@ -109,29 +132,74 @@ namespace DiscImageChef.Commands
return;
}
DumpLog dumpLog = new DumpLog(options.OutputPrefix + ".log", dev);
PluginBase plugins = new PluginBase();
List<IWritableImage> candidates = new List<IWritableImage>();
// Try extension
if(string.IsNullOrEmpty(options.OutputFormat))
candidates.AddRange(plugins.WritableImages.Values.Where(t =>
t.KnownExtensions
.Contains(Path.GetExtension(options
.OutputFile))));
// Try Id
else if(Guid.TryParse(options.OutputFormat, out Guid outId))
candidates.AddRange(plugins.WritableImages.Values.Where(t => t.Id.Equals(outId)));
// Try name
else
candidates.AddRange(plugins.WritableImages.Values.Where(t => string.Equals(t.Name, options.OutputFormat,
StringComparison
.InvariantCultureIgnoreCase)));
if(candidates.Count == 0)
{
DicConsole.WriteLine("No plugin supports requested extension.");
return;
}
if(candidates.Count > 1)
{
DicConsole.WriteLine("More than one plugin supports requested extension.");
return;
}
IWritableImage outputFormat = candidates[0];
DumpLog dumpLog = new DumpLog(outputPrefix + ".log", dev);
if(options.Verbose)
{
dumpLog.WriteLine("Output image format: {0} ({1}).", outputFormat.Name, outputFormat.Id);
DicConsole.VerboseWriteLine("Output image format: {0} ({1}).", outputFormat.Name, outputFormat.Id);
}
else
{
dumpLog.WriteLine("Output image format: {0}.", outputFormat.Name);
DicConsole.WriteLine("Output image format: {0}.", outputFormat.Name);
}
switch(dev.Type)
{
case DeviceType.ATA:
Ata.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses, options.Force,
options.Raw, options.Persistent, options.StopOnError, ref resume, ref dumpLog, encoding);
Ata.Dump(dev, options.DevicePath, outputFormat, options.RetryPasses, options.Force, options.Raw,
options.Persistent, options.StopOnError, ref resume, ref dumpLog, encoding, outputPrefix,
options.OutputFile, parsedOptions);
break;
case DeviceType.MMC:
case DeviceType.SecureDigital:
SecureDigital.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses,
options.Force, options.Raw, options.Persistent, options.StopOnError, ref resume,
ref dumpLog, encoding);
SecureDigital.Dump(dev, options.DevicePath, outputFormat, options.RetryPasses, options.Force,
options.Raw, options.Persistent, options.StopOnError, ref resume, ref dumpLog,
encoding, outputPrefix, options.OutputFile, parsedOptions);
break;
case DeviceType.NVMe:
NvMe.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses, options.Force,
options.Raw, options.Persistent, options.StopOnError, ref resume, ref dumpLog, encoding);
NvMe.Dump(dev, options.DevicePath, outputFormat, options.RetryPasses, options.Force, options.Raw,
options.Persistent, options.StopOnError, ref resume, ref dumpLog, encoding, outputPrefix,
options.OutputFile, parsedOptions);
break;
case DeviceType.ATAPI:
case DeviceType.SCSI:
Scsi.Dump(dev, options.DevicePath, options.OutputPrefix, options.RetryPasses, options.Force,
options.Raw, options.Persistent, options.StopOnError, options.SeparateSubchannel,
ref resume, ref dumpLog, options.LeadIn, encoding);
Scsi.Dump(dev, options.DevicePath, outputFormat, options.RetryPasses, options.Force, options.Raw,
options.Persistent, options.StopOnError, ref resume, ref dumpLog, options.LeadIn,
encoding, outputPrefix, options.OutputFile, parsedOptions);
break;
default:
dumpLog.WriteLine("Unknown device type.");
@@ -144,10 +212,9 @@ namespace DiscImageChef.Commands
resume.LastWriteDate = DateTime.UtcNow;
resume.BadBlocks.Sort();
if(File.Exists(options.OutputPrefix + ".resume.xml")) File.Delete(options.OutputPrefix + ".resume.xml");
if(File.Exists(outputPrefix + ".resume.xml")) File.Delete(outputPrefix + ".resume.xml");
FileStream fs = new FileStream(options.OutputPrefix + ".resume.xml", FileMode.Create,
FileAccess.ReadWrite);
FileStream fs = new FileStream(outputPrefix + ".resume.xml", FileMode.Create, FileAccess.ReadWrite);
xs = new XmlSerializer(resume.GetType());
xs.Serialize(fs, resume);
fs.Close();

View File

@@ -269,9 +269,6 @@ namespace DiscImageChef
[Option('i', "device", Required = true, HelpText = "Device path.")]
public string DevicePath { get; set; }
[Option('w', "output-prefix", Required = true, HelpText = "Prefix for media dump.")]
public string OutputPrefix { get; set; }
[Option('r', "raw", Default = false,
HelpText = "Dump sectors with tags included. For optical media, dump scrambled sectors")]
public bool Raw { get; set; }
@@ -288,10 +285,6 @@ namespace DiscImageChef
[Option("persistent", Default = false, 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; }
[Option('m', "resume", Default = true, HelpText = "Create/use resume mapfile.")]
public bool Resume { get; set; }
@@ -300,6 +293,18 @@ namespace DiscImageChef
[Option('e', "encoding", Default = null, HelpText = "Name of character encoding to use.")]
public string EncodingName { get; set; }
[Option('o', "output", Required = true, HelpText = "Output image.")]
public string OutputFile { get; set; }
[Option('t', "format", Default = null,
HelpText =
"Format of the output image, as plugin name or plugin id. If not present, will try to detect it from output image extension.")]
public string OutputFormat { get; set; }
[Option('O', "options", Default = null,
HelpText = "Comma separated name=value pairs of options to pass to output image plugin")]
public string Options { get; set; }
}
[Verb("device-report", HelpText = "Tests the device capabilities and creates an XML report of them.")]