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;
@@ -90,9 +96,9 @@ namespace DiscImageChef.Core.Devices.Dumping
}
}
bool sense;
bool sense;
const ushort ATA_PROFILE = 0x0001;
const uint TIMEOUT = 5;
const uint TIMEOUT = 5;
dumpLog.WriteLine("Requesting ATA IDENTIFY DEVICE.");
sense = dev.AtaIdentify(out byte[] cmdBuf, out _);
@@ -101,101 +107,24 @@ namespace DiscImageChef.Core.Devices.Dumping
Identify.IdentifyDevice? ataIdNullable = Identify.Decode(cmdBuf);
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);
Identify.IdentifyDevice ataId = ataIdNullable.Value;
byte[] ataIdentify = cmdBuf;
cmdBuf = new byte[0];
DateTime start;
DateTime end;
double totalDuration = 0;
double totalChkDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
double totalDuration = 0;
double totalChkDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
aborted = false;
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
@@ -206,7 +135,7 @@ namespace DiscImageChef.Core.Devices.Dumping
return;
}
uint blockSize = ataReader.LogicalBlockSize;
uint blockSize = ataReader.LogicalBlockSize;
uint physicalsectorsize = ataReader.PhysicalBlockSize;
if(ataReader.FindReadCommand())
{
@@ -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())
{
@@ -222,40 +152,89 @@ namespace DiscImageChef.Core.Devices.Dumping
return;
}
uint blocksToRead = ataReader.BlocksToRead;
ushort cylinders = ataReader.Cylinders;
byte heads = ataReader.Heads;
byte sectors = ataReader.Sectors;
uint blocksToRead = ataReader.BlocksToRead;
ushort cylinders = ataReader.Cylinders;
byte heads = ataReader.Heads;
byte sectors = ataReader.Sectors;
dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);
dumpLog.WriteLine("Device reports {0} cylinders {1} heads {2} sectors per track.", cylinders, heads,
sectors);
dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize);
dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize);
dumpLog.WriteLine("Device reports {0} bytes per physical block.", physicalsectorsize);
bool removable = !dev.IsCompactFlash &&
ataId.GeneralConfiguration.HasFlag(Identify.GeneralConfigurationBit.Removable);
DumpHardwareType currentTry = null;
ExtentsULong extents = null;
ExtentsULong extents = null;
ResumeSupport.Process(ataReader.IsLba, removable, blocks, dev.Manufacturer, dev.Model, dev.Serial,
dev.PlatformId, ref resume, ref currentTry, ref extents);
if(currentTry == null || extents == null)
throw new InvalidOperationException("Could not process resume file, not continuing...");
MhddLog mhddLog;
IbgLog ibgLog;
double duration;
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);
ibgLog = new IbgLog(outputPrefix + ".ibg", ATA_PROFILE);
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,30 +271,32 @@ 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;
resume.NextBlock = i + blocksToRead;
}
end = DateTime.Now;
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));
#region Error handling
if(resume.BadBlocks.Count > 0 && !aborted)
{
int pass = 0;
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)
@@ -364,12 +346,11 @@ namespace DiscImageChef.Core.Devices.Dumping
else
{
mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
ibgLog = new IbgLog(outputPrefix + ".ibg", ATA_PROFILE);
dumpFile = new DataFile(outputPrefix + ".bin");
ibgLog = new IbgLog(outputPrefix + ".ibg", ATA_PROFILE);
ulong currentBlock = 0;
blocks = (ulong)(cylinders * heads * sectors);
start = DateTime.UtcNow;
blocks = (ulong)(cylinders * heads * sectors);
start = DateTime.UtcNow;
for(ushort cy = 0; cy < cylinders; cy++)
{
for(byte hd = 0; hd < heads; hd++)
@@ -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,195 +409,163 @@ 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)
{
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.");
dumpLog.WriteLine("Aborted!");
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)
{
xmlFileSysInfo = new PartitionType[partitions.Count];
for(int i = 0; i < partitions.Count; i++)
if(ret)
sidecar.BlockMedia[0].USB = new USBType
{
xmlFileSysInfo[i] = new PartitionType
ProductID = dev.UsbProductId,
VendorID = dev.UsbVendorId,
Descriptors = new DumpType
{
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);
}
#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 * blockSize
Image = outputPath,
Size = dev.UsbDescriptors.Length,
Checksums = Checksum.GetChecksums(dev.UsbDescriptors).ToArray()
}
};
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);
}
#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.BlockMedia[0].Checksums = dataChk.End().ToArray();
if(dev.IsPcmcia)
{
dumpLog.WriteLine("Reading PCMCIA CIS.");
ret = outputPlugin.WriteMediaTag(dev.Cis, MediaTagType.PCMCIA_CIS);
if(ret)
sidecar.BlockMedia[0].PCMCIA = new PCMCIAType
{
CIS = new DumpType
{
Image = outputPath,
Size = dev.Cis.Length,
Checksums = Checksum.GetChecksums(dev.Cis).ToArray()
}
};
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;
}
}
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);
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;
Metadata.MediaType.MediaTypeToString(MediaType.GENERIC_HDD, out xmlDskTyp, out xmlDskSubTyp);
sidecar.BlockMedia[0].DiskType = xmlDskTyp;
sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp;
sidecar.BlockMedia[0].Interface = "ATA";
sidecar.BlockMedia[0].LogicalBlocks = (long)blocks;
sidecar.BlockMedia[0].PhysicalBlockSize = (int)physicalsectorsize;
sidecar.BlockMedia[0].LogicalBlockSize = (int)blockSize;
sidecar.BlockMedia[0].Manufacturer = dev.Manufacturer;
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;
sidecar.BlockMedia[0].LogicalBlockSize = (int)blockSize;
sidecar.BlockMedia[0].Manufacturer = dev.Manufacturer;
sidecar.BlockMedia[0].Model = dev.Model;
sidecar.BlockMedia[0].Serial = dev.Serial;
sidecar.BlockMedia[0].Size = (long)(blocks * blockSize);
if(cylinders > 0 && heads > 0 && sectors > 0)
{
sidecar.BlockMedia[0].Cylinders = cylinders;
sidecar.BlockMedia[0].CylindersSpecified = true;
sidecar.BlockMedia[0].Heads = heads;
sidecar.BlockMedia[0].HeadsSpecified = true;
sidecar.BlockMedia[0].SectorsPerTrack = sectors;
sidecar.BlockMedia[0].Cylinders = cylinders;
sidecar.BlockMedia[0].CylindersSpecified = true;
sidecar.BlockMedia[0].Heads = heads;
sidecar.BlockMedia[0].HeadsSpecified = true;
sidecar.BlockMedia[0].SectorsPerTrack = sectors;
sidecar.BlockMedia[0].SectorsPerTrackSpecified = true;
}
DicConsole.WriteLine();
DicConsole
.WriteLine("Took a total of {0:F3} seconds ({1:F3} processing commands, {2:F3} checksumming).",
(end - start).TotalSeconds, totalDuration / 1000, totalChkDuration / 1000);
.WriteLine("Took a total of {0:F3} seconds ({1:F3} processing commands, {2:F3} checksumming).",
(end - start).TotalSeconds, totalDuration / 1000, totalChkDuration / 1000);
DicConsole.WriteLine("Avegare speed: {0:F3} MiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000));
DicConsole.WriteLine("Fastest speed burst: {0:F3} MiB/sec.", maxSpeed);
DicConsole.WriteLine("Slowest speed burst: {0:F3} MiB/sec.", minSpeed);
DicConsole.WriteLine("{0} sectors could not be read.", resume.BadBlocks.Count);
DicConsole.WriteLine("{0} sectors could not be read.", resume.BadBlocks.Count);
if(resume.BadBlocks.Count > 0) resume.BadBlocks.Sort();
DicConsole.WriteLine();
@@ -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;
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();
bool compactDisc = true;
bool isXbox = false;
// 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();
blocks = scsiReader.GetDeviceBlocks();
dumpLog.WriteLine("Device reports disc has {0} blocks", blocks);
Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>();
#region Nintendo
switch(dskType)
@@ -213,9 +217,9 @@ namespace DiscImageChef.Core.Devices.Dumping
if(!sense)
{
PFI.PhysicalFormatInformation? nintendoPfi = PFI.Decode(cmdBuf);
if(nintendoPfi != null)
if(nintendoPfi != null)
if(nintendoPfi.Value.DiskCategory == DiskCategory.Nintendo &&
nintendoPfi.Value.PartVersion == 15)
nintendoPfi.Value.PartVersion == 15)
{
dumpLog.WriteLine("Dumping Nintendo GameCube or Wii discs is not yet implemented.");
throw new
@@ -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];
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,
@@ -326,9 +321,9 @@ namespace DiscImageChef.Core.Devices.Dumping
dskType = MediaType.XGD2;
// All XGD3 all have the same number of blocks
if(blocks == 25063 || // Locked (or non compatible drive)
if(blocks == 25063 || // Locked (or non compatible drive)
blocks == 4229664 || // Xtreme unlock
blocks == 4246304) // Wxripper unlock
blocks == 4246304) // Wxripper unlock
dskType = MediaType.XGD3;
}
@@ -345,7 +340,7 @@ namespace DiscImageChef.Core.Devices.Dumping
if(dumpRaw && !force)
{
DicConsole
.ErrorWriteLine("Not continuing. If you want to continue reading cooked data when raw is not available use the force option.");
.ErrorWriteLine("Not continuing. If you want to continue reading cooked data when raw is not available use the force option.");
// TODO: Exit more gracefully
return;
}
@@ -353,19 +348,11 @@ namespace DiscImageChef.Core.Devices.Dumping
isXbox = true;
}
alcohol.AddDmi(cmdBuf);
if(cmdBuf.Length == 2052)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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);
}
}
@@ -385,19 +372,9 @@ namespace DiscImageChef.Core.Devices.Dumping
if(!sense)
if(CSS_CPRM.DecodeLeadInCopyright(cmdBuf).HasValue)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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
@@ -413,17 +390,11 @@ namespace DiscImageChef.Core.Devices.Dumping
MmcDiscStructureFormat.BurstCuttingArea, 0, dev.Timeout, out _);
if(!sense)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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
@@ -436,15 +407,9 @@ namespace DiscImageChef.Core.Devices.Dumping
if(!sense)
if(DDS.Decode(cmdBuf).HasValue)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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.");
@@ -454,16 +419,11 @@ namespace DiscImageChef.Core.Devices.Dumping
if(!sense)
if(Spare.Decode(cmdBuf).HasValue)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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
@@ -475,16 +435,11 @@ namespace DiscImageChef.Core.Devices.Dumping
MmcDiscStructureFormat.PreRecordedInfo, 0, dev.Timeout, out _);
if(!sense)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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
}
@@ -500,15 +455,9 @@ namespace DiscImageChef.Core.Devices.Dumping
MmcDiscStructureFormat.DvdrMediaIdentifier, 0, dev.Timeout, out _);
if(!sense)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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.");
@@ -517,16 +466,11 @@ namespace DiscImageChef.Core.Devices.Dumping
out _);
if(!sense)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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
@@ -540,15 +484,9 @@ namespace DiscImageChef.Core.Devices.Dumping
MmcDiscStructureFormat.Adip, 0, dev.Timeout, out _);
if(!sense)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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.");
@@ -556,16 +494,11 @@ namespace DiscImageChef.Core.Devices.Dumping
MmcDiscStructureFormat.Dcb, 0, dev.Timeout, out _);
if(!sense)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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+
@@ -577,16 +510,11 @@ namespace DiscImageChef.Core.Devices.Dumping
out _);
if(!sense)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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
@@ -602,17 +530,13 @@ namespace DiscImageChef.Core.Devices.Dumping
if(!sense)
if(DI.Decode(cmdBuf).HasValue)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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
}
@@ -641,17 +559,11 @@ namespace DiscImageChef.Core.Devices.Dumping
MmcDiscStructureFormat.BdBurstCuttingArea, 0, dev.Timeout, out _);
if(!sense)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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
@@ -665,15 +577,9 @@ namespace DiscImageChef.Core.Devices.Dumping
MmcDiscStructureFormat.BdDds, 0, dev.Timeout, out _);
if(!sense)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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.");
@@ -681,29 +587,177 @@ namespace DiscImageChef.Core.Devices.Dumping
MmcDiscStructureFormat.BdSpareAreaInformation, 0, dev.Timeout, out _);
if(!sense)
{
tmpBuf = new byte[cmdBuf.Length - 4];
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.");
}

File diff suppressed because it is too large Load Diff

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,15 +65,21 @@ 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;
int resets = 0;
if(dev.IsRemovable)
{
@@ -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,24 +58,23 @@ 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,
ref DumpLog dumpLog)
internal static void Dump(Device dev, string outputPrefix, string devicePath, ref Resume resume,
ref DumpLog dumpLog)
{
FixedSense? fxSense;
bool aborted;
bool sense;
ulong blocks = 0;
uint blockSize;
MediaType dskType = MediaType.Unknown;
DateTime start;
DateTime end;
double totalDuration = 0;
double totalChkDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
FixedSense? fxSense;
bool aborted;
bool sense;
ulong blocks = 0;
uint blockSize;
MediaType dskType = MediaType.Unknown;
DateTime start;
DateTime end;
double totalDuration = 0;
double totalChkDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
CICMMetadataType sidecar = 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;
@@ -88,22 +94,22 @@ namespace DiscImageChef.Core.Devices.Dumping
}
}
bool sense;
bool sense;
const ushort SD_PROFILE = 0x0001;
const uint TIMEOUT = 5;
double duration;
const uint TIMEOUT = 5;
double duration;
CICMMetadataType sidecar = new CICMMetadataType {BlockMedia = new[] {new BlockMediaType()}};
uint blocksToRead = 128;
uint blockSize = 512;
ulong blocks = 0;
byte[] csd = null;
byte[] ocr = null;
byte[] ecsd = null;
byte[] scr = null;
int physicalBlockSize = 0;
bool byteAddressed = true;
uint blocksToRead = 128;
uint blockSize = 512;
ulong blocks = 0;
byte[] csd = null;
byte[] ocr = null;
byte[] ecsd = null;
byte[] scr = null;
int physicalBlockSize = 0;
bool byteAddressed = true;
Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>();
switch(dev.Type)
{
@@ -114,13 +120,15 @@ namespace DiscImageChef.Core.Devices.Dumping
if(!sense)
{
ExtendedCSD ecsdDecoded = Decoders.MMC.Decoders.DecodeExtendedCSD(ecsd);
blocksToRead = ecsdDecoded.OptimalReadSize;
blocks = ecsdDecoded.SectorCount;
blockSize = (uint)(ecsdDecoded.SectorSize == 1 ? 4096 : 512);
if(ecsdDecoded.NativeSectorSize == 0) physicalBlockSize = 512;
else if(ecsdDecoded.NativeSectorSize == 1) physicalBlockSize = 4096;
blocksToRead = ecsdDecoded.OptimalReadSize;
blocks = ecsdDecoded.SectorCount;
blockSize = (uint)(ecsdDecoded.SectorSize == 1 ? 4096 : 512);
if(ecsdDecoded.NativeSectorSize == 0) physicalBlockSize = 512;
else if(ecsdDecoded.NativeSectorSize == 1)
physicalBlockSize = 4096;
// 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));
blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength);
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);
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:
@@ -151,115 +162,45 @@ namespace DiscImageChef.Core.Devices.Dumping
if(!sense)
{
Decoders.SecureDigital.CSD csdDecoded = Decoders.SecureDigital.Decoders.DecodeCSD(csd);
blocks = (ulong)(csdDecoded.Structure == 0
? (csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2)
: (csdDecoded.Size + 1) * 1024);
blocks = (ulong)(csdDecoded.Structure == 0
? (csdDecoded.Size + 1) *
Math.Pow(2, csdDecoded.SizeMultiplier + 2)
: (csdDecoded.Size + 1) * 1024);
blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength);
// Structure >=1 for SDHC/SDXC, so that's block addressed
byteAddressed = csdDecoded.Structure == 0;
mediaTags.Add(MediaTagType.SD_CSD, null);
}
else csd = null;
dumpLog.WriteLine("Reading OCR");
sense = dev.ReadSdocr(out ocr, out _, TIMEOUT, out duration);
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);
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;
}
}
dumpLog.WriteLine("Reading CID");
sense = dev.ReadCid(out byte[] cid, out _, TIMEOUT, out duration);
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;
double totalDuration = 0;
double totalChkDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
double totalDuration = 0;
double totalChkDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
aborted = false;
aborted = false;
System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;
if(blocks == 0)
@@ -272,7 +213,7 @@ namespace DiscImageChef.Core.Devices.Dumping
dumpLog.WriteLine("Device reports {0} blocks.", blocks);
byte[] cmdBuf;
bool error;
bool error;
while(true)
{
@@ -293,18 +234,50 @@ namespace DiscImageChef.Core.Devices.Dumping
dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
DumpHardwareType currentTry = null;
ExtentsULong extents = null;
ExtentsULong extents = null;
ResumeSupport.Process(true, false, blocks, dev.Manufacturer, dev.Model, dev.Serial, dev.PlatformId,
ref resume, ref currentTry, ref extents);
if(currentTry == null || extents == null)
throw new 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);
IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", SD_PROFILE);
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,29 +316,32 @@ 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;
resume.NextBlock = i + blocksToRead;
}
end = DateTime.Now;
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));
#region Error handling
if(resume.BadBlocks.Count > 0 && !aborted)
{
int pass = 0;
bool forward = true;
int pass = 0;
bool forward = true;
bool runningPersistent = false;
repeatRetryLba:
@@ -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,179 +390,190 @@ 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)
{
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.");
dumpLog.WriteLine("Aborted!");
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)
{
cidDump = new DumpType
{
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);
Image = outputPath,
Size = cid.Length,
Checksums = Checksum.GetChecksums(cid).ToArray()
};
foreach(IFilesystem plugin in plugins.PluginsList.Values)
try
{
if(!plugin.Identify(imageFormat, partitions[i])) continue;
ret =
outputPlugin.WriteMediaTag(cid,
dev.Type == DeviceType.SecureDigital
? MediaTagType.SD_CID
: MediaTagType.MMC_CID);
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
// Cannot write CID to image
if(!ret && !force)
{
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(!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);
}
#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();
dumpLog.WriteLine("Cannot write CID to output image.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
}
sidecar.BlockMedia[0].Checksums = dataChk.End().ToArray();
if(csd != null)
{
csdDump = new DumpType
{
Image = outputPath,
Size = csd.Length,
Checksums = Checksum.GetChecksums(csd).ToArray()
};
ret =
outputPlugin.WriteMediaTag(csd,
dev.Type == DeviceType.SecureDigital
? MediaTagType.SD_CSD
: MediaTagType.MMC_CSD);
// Cannot write CSD to image
if(!ret && !force)
{
dumpLog.WriteLine("Cannot write CSD to output image.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
}
if(ecsd != null)
{
sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD = new DumpType
{
Image = outputPath,
Size = ecsd.Length,
Checksums = Checksum.GetChecksums(ecsd).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);
}
}
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].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].LogicalBlocks = (long)blocks;
sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize > 0 ? physicalBlockSize : (int)blockSize;
sidecar.BlockMedia[0].LogicalBlockSize = (int)blockSize;
sidecar.BlockMedia[0].Manufacturer = dev.Manufacturer;
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;
sidecar.BlockMedia[0].LogicalBlockSize = (int)blockSize;
sidecar.BlockMedia[0].Manufacturer = dev.Manufacturer;
sidecar.BlockMedia[0].Model = dev.Model;
sidecar.BlockMedia[0].Serial = dev.Serial;
sidecar.BlockMedia[0].Size = (long)(blocks * blockSize);
DicConsole.WriteLine();
@@ -595,7 +583,7 @@ namespace DiscImageChef.Core.Devices.Dumping
(double)blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000));
DicConsole.WriteLine("Fastest speed burst: {0:F3} MiB/sec.", maxSpeed);
DicConsole.WriteLine("Slowest speed burst: {0:F3} MiB/sec.", minSpeed);
DicConsole.WriteLine("{0} sectors could not be read.", resume.BadBlocks.Count);
DicConsole.WriteLine("{0} sectors could not be read.", resume.BadBlocks.Count);
if(resume.BadBlocks.Count > 0) resume.BadBlocks.Sort();
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,30 +71,41 @@ 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;
const uint BLOCK_SIZE = 2048;
uint blocksToRead = 64;
DateTime start;
DateTime end;
double totalDuration = 0;
double totalChkDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
bool aborted = false;
bool sense;
ulong blocks;
const uint BLOCK_SIZE = 2048;
uint blocksToRead = 64;
DateTime start;
DateTime end;
double totalDuration = 0;
double totalChkDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
bool aborted = false;
System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;
if(mediaTags.ContainsKey(MediaTagType.DVD_PFI)) mediaTags.Remove(MediaTagType.DVD_PFI);
if(mediaTags.ContainsKey(MediaTagType.DVD_DMI)) mediaTags.Remove(MediaTagType.DVD_DMI);
dumpLog.WriteLine("Reading Xbox Security Sector.");
sense = dev.KreonExtractSs(out byte[] ssBuf, out byte[] senseBuf, dev.Timeout, out _);
if(sense)
@@ -114,26 +124,9 @@ namespace DiscImageChef.Core.Devices.Dumping
return;
}
byte[] tmpBuf = new byte[ssBuf.Length - 4];
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;
@@ -168,18 +161,12 @@ namespace DiscImageChef.Core.Devices.Dumping
return;
}
tmpBuf = new byte[readBuffer.Length - 4];
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;
l1Video = totalSize - l0Video + 1;
dumpLog.WriteLine("Reading Disc Manufacturing Information.");
sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0,
MmcDiscStructureFormat.DiscManufacturingInformation, 0, 0, out _);
@@ -190,15 +177,9 @@ namespace DiscImageChef.Core.Devices.Dumping
return;
}
tmpBuf = new byte[readBuffer.Length - 4];
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");
@@ -256,20 +237,15 @@ namespace DiscImageChef.Core.Devices.Dumping
}
DicConsole.DebugWriteLine("Dump-media command", "Unlocked total size: {0} sectors", totalSize);
blocks = totalSize + 1;
blocks = totalSize + 1;
middleZone =
totalSize - (PFI.Decode(readBuffer).Value.Layer0EndPSN - PFI.Decode(readBuffer).Value.DataAreaStartPSN +
1) - gameSize + 1;
totalSize - (PFI.Decode(readBuffer).Value.Layer0EndPSN -
PFI.Decode(readBuffer).Value.DataAreaStartPSN +
1) - gameSize + 1;
tmpBuf = new byte[readBuffer.Length - 4];
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,
@@ -281,33 +257,27 @@ namespace DiscImageChef.Core.Devices.Dumping
return;
}
tmpBuf = new byte[readBuffer.Length - 4];
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;
totalSize = l0Video + l1Video + middleZone * 2 + gameSize;
layerBreak = l0Video + middleZone + gameSize / 2;
DicConsole.WriteLine("Video layer 0 size: {0} sectors", l0Video);
DicConsole.WriteLine("Video layer 1 size: {0} sectors", l1Video);
DicConsole.WriteLine("Middle zone size: {0} sectors", middleZone);
DicConsole.WriteLine("Game data size: {0} sectors", gameSize);
DicConsole.WriteLine("Total size: {0} sectors", totalSize);
DicConsole.WriteLine("Real layer break: {0}", layerBreak);
DicConsole.WriteLine("Middle zone size: {0} sectors", middleZone);
DicConsole.WriteLine("Game data size: {0} sectors", gameSize);
DicConsole.WriteLine("Total size: {0} sectors", totalSize);
DicConsole.WriteLine("Real layer break: {0}", layerBreak);
DicConsole.WriteLine();
dumpLog.WriteLine("Video layer 0 size: {0} sectors", l0Video);
dumpLog.WriteLine("Video layer 1 size: {0} sectors", l1Video);
dumpLog.WriteLine("Middle zone 0 size: {0} sectors", middleZone);
dumpLog.WriteLine("Game data 0 size: {0} sectors", gameSize);
dumpLog.WriteLine("Total 0 size: {0} sectors", totalSize);
dumpLog.WriteLine("Real layer break: {0}", layerBreak);
dumpLog.WriteLine("Game data 0 size: {0} sectors", gameSize);
dumpLog.WriteLine("Total 0 size: {0} sectors", totalSize);
dumpLog.WriteLine("Real layer break: {0}", layerBreak);
bool read12 = !dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, 0, BLOCK_SIZE, 0, 1,
false, dev.Timeout, out _);
@@ -340,26 +310,53 @@ 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");
IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", 0x0010);
ret = outputPlugin.Create(outputPath, dskType, formatOptions, blocks, BLOCK_SIZE);
// Cannot create image
if(!ret)
{
dumpLog.WriteLine("Error creating output image, not continuing.");
dumpLog.WriteLine(outputPlugin.ErrorMessage);
DicConsole.ErrorWriteLine("Error creating output image, not continuing.");
DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage);
return;
}
start = DateTime.UtcNow;
double cmdDuration = 0;
uint saveBlocksToRead = blocksToRead;
DumpHardwareType currentTry = null;
ExtentsULong extents = null;
double cmdDuration = 0;
uint saveBlocksToRead = blocksToRead;
DumpHardwareType currentTry = null;
ExtentsULong extents = null;
ResumeSupport.Process(true, true, totalSize, dev.Manufacturer, dev.Model, dev.Serial, dev.PlatformId,
ref resume, ref currentTry, ref extents);
if(currentTry == null || extents == null)
throw new NotImplementedException("Could not process resume file, not continuing...");
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.");
@@ -367,7 +364,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{
if(aborted)
{
resume.NextBlock = currentSector;
resume.NextBlock = currentSector;
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
dumpLog.WriteLine("Aborted!");
break;
@@ -382,19 +379,19 @@ namespace DiscImageChef.Core.Devices.Dumping
if(xboxSs.Value.Extents[e].StartPSN <= xboxSs.Value.Layer0EndPSN)
extentStart = xboxSs.Value.Extents[e].StartPSN - 0x30000;
else
extentStart = (xboxSs.Value.Layer0EndPSN + 1) * 2 -
((xboxSs.Value.Extents[e].StartPSN ^ 0xFFFFFF) + 1) - 0x30000;
extentStart = (xboxSs.Value.Layer0EndPSN + 1) * 2 -
((xboxSs.Value.Extents[e].StartPSN ^ 0xFFFFFF) + 1) - 0x30000;
if(xboxSs.Value.Extents[e].EndPSN <= xboxSs.Value.Layer0EndPSN)
extentEnd = xboxSs.Value.Extents[e].EndPSN - 0x30000;
else
extentEnd = (xboxSs.Value.Layer0EndPSN + 1) * 2 -
((xboxSs.Value.Extents[e].EndPSN ^ 0xFFFFFF) + 1) - 0x30000;
extentEnd = (xboxSs.Value.Layer0EndPSN + 1) * 2 -
((xboxSs.Value.Extents[e].EndPSN ^ 0xFFFFFF) + 1) - 0x30000;
}
// After last extent
else
{
extentStart = blocks;
extentEnd = blocks;
extentEnd = blocks;
}
if(currentSector > extentEnd) continue;
@@ -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);
@@ -448,15 +445,16 @@ namespace DiscImageChef.Core.Devices.Dumping
dumpLog.WriteLine("Error reading {0} blocks from block {1}.", blocksToRead, i);
string[] senseLines = Sense.PrettifySense(senseBuf).Split(new[] {Environment.NewLine},
StringSplitOptions
.RemoveEmptyEntries);
.RemoveEmptyEntries);
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;
resume.NextBlock = currentSector;
blocksToRead = saveBlocksToRead;
currentSector = i + 1;
resume.NextBlock = currentSector;
}
for(ulong i = extentStart; i <= extentEnd; i += blocksToRead)
@@ -473,10 +471,11 @@ 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;
currentSector = i + 1;
resume.NextBlock = currentSector;
}
@@ -500,12 +499,13 @@ namespace DiscImageChef.Core.Devices.Dumping
currentSpeed);
mhddLog.Write(middle + currentSector, cmdDuration);
ibgLog.Write(middle + currentSector, currentSpeed * 1024);
dumpFile.Write(new byte[BLOCK_SIZE * blocksToRead]);
ibgLog.Write(middle + currentSector, currentSpeed * 1024);
// Write empty data
outputPlugin.WriteSectors(new byte[BLOCK_SIZE * blocksToRead], middle + currentSector, blocksToRead);
extents.Add(currentSector, blocksToRead, true);
currentSector += blocksToRead;
resume.NextBlock = currentSector;
currentSector += blocksToRead;
resume.NextBlock = currentSector;
}
blocksToRead = saveBlocksToRead;
@@ -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,10 +579,11 @@ namespace DiscImageChef.Core.Devices.Dumping
foreach(string senseLine in senseLines) dumpLog.WriteLine(senseLine);
}
double newSpeed = (double)BLOCK_SIZE * blocksToRead / 1048576 / (cmdDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
currentSector += blocksToRead;
resume.NextBlock = currentSector;
double newSpeed =
(double)BLOCK_SIZE * blocksToRead / 1048576 / (cmdDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
currentSector += blocksToRead;
resume.NextBlock = currentSector;
}
dumpLog.WriteLine("Unlocking drive (Wxripper).");
@@ -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,12 +618,14 @@ 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();
int pass = 0;
bool forward = true;
int pass = 0;
bool forward = true;
bool runningPersistent = false;
resume.BadBlocks = tmpList;
@@ -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)
@@ -664,65 +670,65 @@ namespace DiscImageChef.Core.Devices.Dumping
}
Modes.ModePage? currentModePage = null;
byte[] md6;
byte[] md10;
byte[] md6;
byte[] md10;
if(!runningPersistent && persistent)
{
if(dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice)
{
Modes.ModePage_01_MMC pgMmc =
Modes.ModePage_01_MMC pgMmc =
new Modes.ModePage_01_MMC {PS = false, ReadRetryCount = 255, Parameter = 0x20};
Modes.DecodedMode md = new Modes.DecodedMode
Modes.DecodedMode md = new Modes.DecodedMode
{
Header = new Modes.ModeHeader(),
Pages = new[]
Pages = new[]
{
new Modes.ModePage
{
Page = 0x01,
Subpage = 0x00,
Page = 0x01,
Subpage = 0x00,
PageResponse = Modes.EncodeModePage_01_MMC(pgMmc)
}
}
};
md6 = Modes.EncodeMode6(md, dev.ScsiType);
md6 = Modes.EncodeMode6(md, dev.ScsiType);
md10 = Modes.EncodeMode10(md, dev.ScsiType);
}
else
{
Modes.ModePage_01 pg = new Modes.ModePage_01
{
PS = false,
AWRE = false,
ARRE = false,
TB = true,
RC = false,
EER = true,
PER = false,
DTE = false,
DCR = false,
PS = false,
AWRE = false,
ARRE = false,
TB = true,
RC = false,
EER = true,
PER = false,
DTE = false,
DCR = false,
ReadRetryCount = 255
};
Modes.DecodedMode md = new Modes.DecodedMode
{
Header = new Modes.ModeHeader(),
Pages = new[]
Pages = new[]
{
new Modes.ModePage
{
Page = 0x01,
Subpage = 0x00,
Page = 0x01,
Subpage = 0x00,
PageResponse = Modes.EncodeModePage_01(pg)
}
}
};
md6 = Modes.EncodeMode6(md, dev.ScsiType);
md6 = Modes.EncodeMode6(md, dev.ScsiType);
md10 = Modes.EncodeMode10(md, dev.ScsiType);
}
dumpLog.WriteLine("Sending MODE SELECT to drive.");
sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out _);
sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out _);
if(sense) sense = dev.ModeSelect10(md10, out senseBuf, true, false, dev.Timeout, out _);
runningPersistent = true;
@@ -737,9 +743,9 @@ namespace DiscImageChef.Core.Devices.Dumping
Modes.DecodedMode md = new Modes.DecodedMode
{
Header = new Modes.ModeHeader(),
Pages = new[] {currentModePage.Value}
Pages = new[] {currentModePage.Value}
};
md6 = Modes.EncodeMode6(md, dev.ScsiType);
md6 = Modes.EncodeMode6(md, dev.ScsiType);
md10 = Modes.EncodeMode10(md, dev.ScsiType);
dumpLog.WriteLine("Sending MODE SELECT to drive.");
@@ -754,211 +760,64 @@ 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)
{
if(aborted)
{
dumpLog.WriteLine("Aborted!");
break;
}
ret = outputPlugin.WriteMediaTag(tag.Value, tag.Key);
if(ret || force) continue;
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;
// Cannot write tag to image
dumpLog.WriteLine($"Cannot write tag {tag.Key}.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
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));
dumpLog.WriteLine("Closing output file.");
DicConsole.WriteLine("Closing output file.");
outputPlugin.Close();
PluginBase plugins = new PluginBase();
FiltersList filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(outputPrefix + ".iso");
resume.BadBlocks.Sort();
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
if(inputFilter == null)
if(aborted)
{
DicConsole.ErrorWriteLine("Cannot open file just created, this should not happen.");
dumpLog.WriteLine("Aborted!");
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,
type = LayersTypeType.OTP,
typeSpecified = true,
Sectors = new SectorsType[1]
Sectors = new SectorsType[1]
};
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);
sidecar.OpticalDisc[0].Sessions = 1;
sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType);
Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp, out string xmlDskSubTyp);
sidecar.OpticalDisc[0].DiscType = xmlDskTyp;
sidecar.OpticalDisc[0].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)
{
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);
// 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", "--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", "--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,13 +108,16 @@ namespace DiscImageChef.Commands
Core.Statistics.AddDevice(dev);
Resume resume = null;
XmlSerializer xs = new XmlSerializer(typeof(Resume));
if(File.Exists(options.OutputPrefix + ".resume.xml") && options.Resume)
string outputPrefix = Path.Combine(Path.GetDirectoryName(options.OutputFile),
Path.GetFileNameWithoutExtension(options.OutputFile));
Resume resume = null;
XmlSerializer xs = new XmlSerializer(typeof(Resume));
if(File.Exists(outputPrefix + ".resume.xml") && options.Resume)
try
{
StreamReader sr = new StreamReader(options.OutputPrefix + ".resume.xml");
resume = (Resume)xs.Deserialize(sr);
StreamReader sr = new StreamReader(outputPrefix + ".resume.xml");
resume = (Resume)xs.Deserialize(sr);
sr.Close();
}
catch
@@ -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,11 +212,10 @@ 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);
xs = new XmlSerializer(resume.GetType());
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.")]