Merge pull request #261 from discimagechef/cd_dump_rewrite

Compact Disc dump rewrite
This commit is contained in:
2020-01-02 01:03:37 +00:00
committed by GitHub
34 changed files with 8285 additions and 8456 deletions

View File

@@ -233,9 +233,20 @@
<e p="Devices" t="Include">
<e p="Dumping" t="Include">
<e p="ATA.cs" t="Include" />
<e p="CompactDisc.cs" t="Include" />
<e p="CompactDisc" t="Include">
<e p="Data.cs" t="Include" />
<e p="Dump.cs" t="Include" />
<e p="Error.cs" t="Include" />
<e p="LeadOuts.cs" t="Include" />
<e p="Pregap.cs" t="Include" />
<e p="Subchannel.cs" t="Include" />
<e p="Tags.cs" t="Include" />
<e p="Tracks.cs" t="Include" />
<e p="Trim.cs" t="Include" />
</e>
<e p="Dump.cs" t="Include" />
<e p="MMC.cs" t="Include" />
<e p="Metadata.cs" t="Include" />
<e p="NVMe.cs" t="Include" />
<e p="PlayStationPortable.cs" t="Include" />
<e p="ResumeSupport.cs" t="Include" />
@@ -291,6 +302,7 @@
<e p="MMC.cs" t="Include" />
</e>
<e p="Info" t="Include">
<e p="CompactDisc.cs" t="Include" />
<e p="ScsiInfo.cs" t="Include" />
<e p="XgdInfo.cs" t="Include" />
</e>
@@ -1265,19 +1277,6 @@
</e>
</e>
</e>
<e p="DiscImageChef.EntityFramework" t="IncludeRecursive">
<e p="DiscImageChef.EntityFramework.csproj" t="IncludeRecursive" />
<e p="Program.cs" t="Include" />
<e p="bin" t="ExcludeRecursive" />
<e p="discimagechef.db" t="Include" />
<e p="obj" t="ExcludeRecursive">
<e p="Debug" t="Include">
<e p="netcoreapp2.0" t="Include">
<e p="DiscImageChef.EntityFramework.AssemblyInfo.cs" t="Include" />
</e>
</e>
</e>
</e>
<e p="DiscImageChef.Filesystems" t="IncludeRecursive">
<e p="AODOS.cs" t="Include" />
<e p="APFS.cs" t="Include" />

View File

@@ -53,9 +53,9 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <summary>Dumps an ATA device</summary>
public void Ata()
{
if(dumpRaw)
if(_dumpRaw)
{
if(force)
if(_force)
ErrorMessage?.Invoke("Raw dumping not yet supported in ATA devices, continuing...");
else
{
@@ -70,8 +70,8 @@ namespace DiscImageChef.Core.Devices.Dumping
double imageWriteDuration = 0;
UpdateStatus?.Invoke("Requesting ATA IDENTIFY DEVICE.");
dumpLog.WriteLine("Requesting ATA IDENTIFY DEVICE.");
bool sense = dev.AtaIdentify(out byte[] cmdBuf, out _);
_dumpLog.WriteLine("Requesting ATA IDENTIFY DEVICE.");
bool sense = _dev.AtaIdentify(out byte[] cmdBuf, out _);
if(!sense &&
Identify.Decode(cmdBuf).HasValue)
@@ -93,8 +93,8 @@ namespace DiscImageChef.Core.Devices.Dumping
// Initializate reader
UpdateStatus?.Invoke("Initializing reader.");
dumpLog.WriteLine("Initializing reader.");
var ataReader = new Reader(dev, TIMEOUT, ataIdentify);
_dumpLog.WriteLine("Initializing reader.");
var ataReader = new Reader(_dev, TIMEOUT, ataIdentify);
// Fill reader blocks
ulong blocks = ataReader.GetDeviceBlocks();
@@ -102,7 +102,7 @@ namespace DiscImageChef.Core.Devices.Dumping
// Check block sizes
if(ataReader.GetBlockSize())
{
dumpLog.WriteLine("ERROR: Cannot get block size: {0}.", ataReader.ErrorMessage);
_dumpLog.WriteLine("ERROR: Cannot get block size: {0}.", ataReader.ErrorMessage);
ErrorMessage(ataReader.ErrorMessage);
return;
@@ -113,16 +113,16 @@ namespace DiscImageChef.Core.Devices.Dumping
if(ataReader.FindReadCommand())
{
dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", ataReader.ErrorMessage);
_dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", ataReader.ErrorMessage);
ErrorMessage(ataReader.ErrorMessage);
return;
}
// Check how many blocks to read, if error show and return
if(ataReader.GetBlocksToRead())
if(ataReader.GetBlocksToRead(_maximumReadable))
{
dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", ataReader.ErrorMessage);
_dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", ataReader.ErrorMessage);
ErrorMessage(ataReader.ErrorMessage);
return;
@@ -141,23 +141,23 @@ namespace DiscImageChef.Core.Devices.Dumping
UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time.");
UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block.");
UpdateStatus?.Invoke($"Device reports {physicalsectorsize} bytes per physical block.");
dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);
_dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);
dumpLog.WriteLine("Device reports {0} cylinders {1} heads {2} sectors per track.", cylinders, heads,
sectors);
_dumpLog.WriteLine("Device reports {0} cylinders {1} heads {2} sectors per track.", cylinders,
heads, sectors);
dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize);
dumpLog.WriteLine("Device reports {0} bytes per physical block.", physicalsectorsize);
_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 &&
bool removable = !_dev.IsCompactFlash &&
ataId.GeneralConfiguration.HasFlag(Identify.GeneralConfigurationBit.Removable);
DumpHardwareType currentTry = null;
ExtentsULong extents = null;
ResumeSupport.Process(ataReader.IsLba, removable, blocks, dev.Manufacturer, dev.Model, dev.Serial,
dev.PlatformId, ref resume, ref currentTry, ref extents);
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)
@@ -173,36 +173,36 @@ namespace DiscImageChef.Core.Devices.Dumping
bool ret = true;
if(dev.IsUsb &&
dev.UsbDescriptors != null &&
!outputPlugin.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors))
if(_dev.IsUsb &&
_dev.UsbDescriptors != null &&
!_outputPlugin.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors))
{
ret = false;
dumpLog.WriteLine("Output format does not support USB descriptors.");
_dumpLog.WriteLine("Output format does not support USB descriptors.");
ErrorMessage("Output format does not support USB descriptors.");
}
if(dev.IsPcmcia &&
dev.Cis != null &&
!outputPlugin.SupportedMediaTags.Contains(MediaTagType.PCMCIA_CIS))
if(_dev.IsPcmcia &&
_dev.Cis != null &&
!_outputPlugin.SupportedMediaTags.Contains(MediaTagType.PCMCIA_CIS))
{
ret = false;
dumpLog.WriteLine("Output format does not support PCMCIA CIS descriptors.");
_dumpLog.WriteLine("Output format does not support PCMCIA CIS descriptors.");
ErrorMessage("Output format does not support PCMCIA CIS descriptors.");
}
if(!outputPlugin.SupportedMediaTags.Contains(MediaTagType.ATA_IDENTIFY))
if(!_outputPlugin.SupportedMediaTags.Contains(MediaTagType.ATA_IDENTIFY))
{
ret = false;
dumpLog.WriteLine("Output format does not support ATA IDENTIFY.");
_dumpLog.WriteLine("Output format does not support ATA IDENTIFY.");
ErrorMessage("Output format does not support ATA IDENTIFY.");
}
if(!ret)
{
dumpLog.WriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
_dumpLog.WriteLine("Several media tags not supported, {0}continuing...", _force ? "" : "not ");
if(force)
if(_force)
ErrorMessage("Several media tags not supported, continuing...");
else
{
@@ -212,40 +212,40 @@ namespace DiscImageChef.Core.Devices.Dumping
}
}
ret = outputPlugin.Create(outputPath,
dev.IsCompactFlash ? MediaType.CompactFlash : MediaType.GENERIC_HDD,
formatOptions, blocks, blockSize);
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);
_dumpLog.WriteLine("Error creating output image, not continuing.");
_dumpLog.WriteLine(_outputPlugin.ErrorMessage);
StoppingErrorMessage?.Invoke("Error creating output image, not continuing." +
Environment.NewLine +
outputPlugin.ErrorMessage);
_outputPlugin.ErrorMessage);
return;
}
// Setting geometry
outputPlugin.SetGeometry(cylinders, heads, sectors);
_outputPlugin.SetGeometry(cylinders, heads, sectors);
if(ataReader.IsLba)
{
UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time.");
if(skip < blocksToRead)
skip = blocksToRead;
if(_skip < blocksToRead)
_skip = blocksToRead;
mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
ibgLog = new IbgLog(outputPrefix + ".ibg", ATA_PROFILE);
mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead);
ibgLog = new IbgLog(_outputPrefix + ".ibg", ATA_PROFILE);
if(resume.NextBlock > 0)
if(_resume.NextBlock > 0)
{
UpdateStatus?.Invoke($"Resuming from block {resume.NextBlock}.");
dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock);
UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}.");
_dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
}
bool newTrim = false;
@@ -255,13 +255,13 @@ namespace DiscImageChef.Core.Devices.Dumping
ulong sectorSpeedStart = 0;
InitProgress?.Invoke();
for(ulong i = resume.NextBlock; i < blocks; i += blocksToRead)
for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
{
if(aborted)
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
UpdateStatus?.Invoke("Aborted!");
dumpLog.WriteLine("Aborted!");
_dumpLog.WriteLine("Aborted!");
break;
}
@@ -289,38 +289,38 @@ namespace DiscImageChef.Core.Devices.Dumping
mhddLog.Write(i, duration);
ibgLog.Write(i, currentSpeed * 1024);
DateTime writeStart = DateTime.Now;
outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
_outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
extents.Add(i, blocksToRead, true);
}
else
{
if(i + skip > blocks)
skip = (uint)(blocks - i);
if(i + _skip > blocks)
_skip = (uint)(blocks - i);
for(ulong b = i; b < i + skip; b++)
resume.BadBlocks.Add(b);
for(ulong b = i; b < i + _skip; b++)
_resume.BadBlocks.Add(b);
mhddLog.Write(i, duration < 500 ? 65535 : duration);
ibgLog.Write(i, 0);
DateTime writeStart = DateTime.Now;
outputPlugin.WriteSectors(new byte[blockSize * skip], i, skip);
_outputPlugin.WriteSectors(new byte[blockSize * _skip], i, _skip);
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", skip, i);
i += skip - blocksToRead;
_dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
i += _skip - blocksToRead;
newTrim = true;
}
sectorSpeedStart += blocksToRead;
resume.NextBlock = i + blocksToRead;
sectorSpeedStart += blocksToRead;
_resume.NextBlock = i + blocksToRead;
double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
if(elapsed < 1)
continue;
currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed);
currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
sectorSpeedStart = 0;
timeSpeedStart = DateTime.UtcNow;
}
@@ -329,46 +329,46 @@ namespace DiscImageChef.Core.Devices.Dumping
EndProgress?.Invoke();
mhddLog.Close();
ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
blockSize * (double)(blocks + 1) / 1024 /
(totalDuration / 1000), devicePath);
ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
(blockSize * (double)(blocks + 1)) / 1024 /
(totalDuration / 1000), _devicePath);
UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");
UpdateStatus?.
Invoke($"Average dump speed {(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec.");
Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");
UpdateStatus?.
Invoke($"Average write speed {(double)blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec.");
Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec.");
dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
_dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
_dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000));
dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration);
_dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration);
#region Trimming
if(resume.BadBlocks.Count > 0 &&
!aborted &&
!notrim &&
if(_resume.BadBlocks.Count > 0 &&
!_aborted &&
!_notrim &&
newTrim)
{
start = DateTime.UtcNow;
UpdateStatus?.Invoke("Trimming bad sectors");
dumpLog.WriteLine("Trimming bad sectors");
_dumpLog.WriteLine("Trimming bad sectors");
ulong[] tmpArray = resume.BadBlocks.ToArray();
ulong[] tmpArray = _resume.BadBlocks.ToArray();
InitProgress?.Invoke();
foreach(ulong badSector in tmpArray)
{
if(aborted)
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
UpdateStatus?.Invoke("Aborted!");
dumpLog.WriteLine("Aborted!");
_dumpLog.WriteLine("Aborted!");
break;
}
@@ -382,44 +382,44 @@ namespace DiscImageChef.Core.Devices.Dumping
if(error)
continue;
resume.BadBlocks.Remove(badSector);
_resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
outputPlugin.WriteSector(cmdBuf, badSector);
_outputPlugin.WriteSector(cmdBuf, badSector);
}
EndProgress?.Invoke();
end = DateTime.UtcNow;
UpdateStatus?.Invoke($"Trimmming finished in {(end - start).TotalSeconds} seconds.");
dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds);
_dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds);
}
#endregion Trimming
#region Error handling
if(resume.BadBlocks.Count > 0 &&
!aborted &&
retryPasses > 0)
if(_resume.BadBlocks.Count > 0 &&
!_aborted &&
_retryPasses > 0)
{
int pass = 1;
bool forward = true;
InitProgress?.Invoke();
repeatRetryLba:
ulong[] tmpArray = resume.BadBlocks.ToArray();
ulong[] tmpArray = _resume.BadBlocks.ToArray();
foreach(ulong badSector in tmpArray)
{
if(aborted)
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
UpdateStatus?.Invoke("Aborted!");
dumpLog.WriteLine("Aborted!");
_dumpLog.WriteLine("Aborted!");
break;
}
PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector,
pass, forward ? "forward" : "reverse",
persistent ? "recovering partial data, " : ""));
_persistent ? "recovering partial data, " : ""));
bool error = ataReader.ReadBlock(out cmdBuf, badSector, out duration);
@@ -427,24 +427,24 @@ namespace DiscImageChef.Core.Devices.Dumping
if(!error)
{
resume.BadBlocks.Remove(badSector);
_resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
outputPlugin.WriteSector(cmdBuf, badSector);
_outputPlugin.WriteSector(cmdBuf, badSector);
UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}.");
dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
_dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
}
else if(persistent)
outputPlugin.WriteSector(cmdBuf, badSector);
else if(_persistent)
_outputPlugin.WriteSector(cmdBuf, badSector);
}
if(pass < retryPasses &&
!aborted &&
resume.BadBlocks.Count > 0)
if(pass < _retryPasses &&
!_aborted &&
_resume.BadBlocks.Count > 0)
{
pass++;
forward = !forward;
resume.BadBlocks.Sort();
resume.BadBlocks.Reverse();
_resume.BadBlocks.Sort();
_resume.BadBlocks.Reverse();
goto repeatRetryLba;
}
@@ -457,8 +457,8 @@ namespace DiscImageChef.Core.Devices.Dumping
}
else
{
mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
ibgLog = new IbgLog(outputPrefix + ".ibg", ATA_PROFILE);
mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead);
ibgLog = new IbgLog(_outputPrefix + ".ibg", ATA_PROFILE);
ulong currentBlock = 0;
blocks = (ulong)(cylinders * heads * sectors);
@@ -473,11 +473,11 @@ namespace DiscImageChef.Core.Devices.Dumping
{
for(byte sc = 1; sc < sectors; sc++)
{
if(aborted)
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
UpdateStatus?.Invoke("Aborted!");
dumpLog.WriteLine("Aborted!");
_dumpLog.WriteLine("Aborted!");
break;
}
@@ -505,25 +505,25 @@ namespace DiscImageChef.Core.Devices.Dumping
ibgLog.Write(currentBlock, currentSpeed * 1024);
DateTime writeStart = DateTime.Now;
outputPlugin.WriteSector(cmdBuf,
(ulong)((cy * heads + hd) * sectors + (sc - 1)));
_outputPlugin.WriteSector(cmdBuf,
(ulong)((((cy * heads) + hd) * sectors) + (sc - 1)));
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
extents.Add(currentBlock);
dumpLog.WriteLine("Error reading cylinder {0} head {1} sector {2}.", cy, hd,
sc);
_dumpLog.WriteLine("Error reading cylinder {0} head {1} sector {2}.", cy, hd,
sc);
}
else
{
resume.BadBlocks.Add(currentBlock);
_resume.BadBlocks.Add(currentBlock);
mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration);
ibgLog.Write(currentBlock, 0);
DateTime writeStart = DateTime.Now;
outputPlugin.WriteSector(new byte[blockSize],
(ulong)((cy * heads + hd) * sectors + (sc - 1)));
_outputPlugin.WriteSector(new byte[blockSize],
(ulong)((((cy * heads) + hd) * sectors) + (sc - 1)));
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
}
@@ -536,7 +536,7 @@ namespace DiscImageChef.Core.Devices.Dumping
if(elapsed < 1)
continue;
currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed);
currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
sectorSpeedStart = 0;
timeSpeedStart = DateTime.UtcNow;
}
@@ -547,47 +547,47 @@ namespace DiscImageChef.Core.Devices.Dumping
EndProgress?.Invoke();
mhddLog.Close();
ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
blockSize * (double)(blocks + 1) / 1024 /
(totalDuration / 1000), devicePath);
ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
(blockSize * (double)(blocks + 1)) / 1024 /
(totalDuration / 1000), _devicePath);
UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");
UpdateStatus?.
Invoke($"Average dump speed {(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec.");
Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");
UpdateStatus?.
Invoke($"Average write speed {(double)blockSize * (double)(blocks + 1) / 1024 / (imageWriteDuration / 1000):F3} KiB/sec.");
Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (imageWriteDuration / 1000):F3} KiB/sec.");
dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
_dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
_dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000));
dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 /
(imageWriteDuration / 1000));
_dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
((double)blockSize * (double)(blocks + 1)) / 1024 /
(imageWriteDuration / 1000));
}
foreach(ulong bad in resume.BadBlocks)
dumpLog.WriteLine("Sector {0} could not be read.", bad);
foreach(ulong bad in _resume.BadBlocks)
_dumpLog.WriteLine("Sector {0} could not be read.", bad);
outputPlugin.SetDumpHardware(resume.Tries);
_outputPlugin.SetDumpHardware(_resume.Tries);
if(preSidecar != null)
outputPlugin.SetCicmMetadata(preSidecar);
if(_preSidecar != null)
_outputPlugin.SetCicmMetadata(_preSidecar);
dumpLog.WriteLine("Closing output file.");
_dumpLog.WriteLine("Closing output file.");
UpdateStatus?.Invoke("Closing output file.");
DateTime closeStart = DateTime.Now;
outputPlugin.Close();
_outputPlugin.Close();
DateTime closeEnd = DateTime.Now;
UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds.");
dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);
_dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);
if(aborted)
if(_aborted)
{
dumpLog.WriteLine("Aborted!");
_dumpLog.WriteLine("Aborted!");
UpdateStatus?.Invoke("Aborted!");
return;
@@ -595,12 +595,12 @@ namespace DiscImageChef.Core.Devices.Dumping
double totalChkDuration = 0;
if(!nometadata)
if(!_nometadata)
{
dumpLog.WriteLine("Creating sidecar.");
_dumpLog.WriteLine("Creating sidecar.");
UpdateStatus?.Invoke("Creating sidecar.");
var filters = new FiltersList();
IFilter filter = filters.GetFilter(outputPath);
IFilter filter = filters.GetFilter(_outputPath);
IMediaImage inputPlugin = ImageFormat.Detect(filter);
if(!inputPlugin.Open(filter))
@@ -611,60 +611,63 @@ namespace DiscImageChef.Core.Devices.Dumping
}
DateTime chkStart = DateTime.UtcNow;
sidecarClass = new Sidecar(inputPlugin, outputPath, filter.Id, encoding);
sidecarClass.InitProgressEvent += InitProgress;
sidecarClass.UpdateProgressEvent += UpdateProgress;
sidecarClass.EndProgressEvent += EndProgress;
sidecarClass.InitProgressEvent2 += InitProgress2;
sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
sidecarClass.EndProgressEvent2 += EndProgress2;
sidecarClass.UpdateStatusEvent += UpdateStatus;
CICMMetadataType sidecar = sidecarClass.Create();
if(preSidecar != null)
_sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding);
_sidecarClass.InitProgressEvent += InitProgress;
_sidecarClass.UpdateProgressEvent += UpdateProgress;
_sidecarClass.EndProgressEvent += EndProgress;
_sidecarClass.InitProgressEvent2 += InitProgress2;
_sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
_sidecarClass.EndProgressEvent2 += EndProgress2;
_sidecarClass.UpdateStatusEvent += UpdateStatus;
CICMMetadataType sidecar = _sidecarClass.Create();
if(_preSidecar != null)
{
preSidecar.BlockMedia = sidecar.BlockMedia;
sidecar = preSidecar;
_preSidecar.BlockMedia = sidecar.BlockMedia;
sidecar = _preSidecar;
}
if(dev.IsUsb &&
dev.UsbDescriptors != null)
if(_dev.IsUsb &&
_dev.UsbDescriptors != null)
{
dumpLog.WriteLine("Reading USB descriptors.");
_dumpLog.WriteLine("Reading USB descriptors.");
UpdateStatus?.Invoke("Reading USB descriptors.");
ret = outputPlugin.WriteMediaTag(dev.UsbDescriptors, MediaTagType.USB_Descriptors);
ret = _outputPlugin.WriteMediaTag(_dev.UsbDescriptors, MediaTagType.USB_Descriptors);
if(ret)
sidecar.BlockMedia[0].USB = new USBType
{
ProductID = dev.UsbProductId, VendorID = dev.UsbVendorId, Descriptors = new DumpType
{
Image = outputPath, Size = (ulong)dev.UsbDescriptors.Length,
Checksums = Checksum.GetChecksums(dev.UsbDescriptors).ToArray()
}
ProductID = _dev.UsbProductId, VendorID = _dev.UsbVendorId, Descriptors =
new DumpType
{
Image = _outputPath, Size = (ulong)_dev.UsbDescriptors.Length,
Checksums = Checksum.GetChecksums(_dev.UsbDescriptors).ToArray()
}
};
}
if(dev.IsPcmcia &&
dev.Cis != null)
if(_dev.IsPcmcia &&
_dev.Cis != null)
{
dumpLog.WriteLine("Reading PCMCIA CIS.");
_dumpLog.WriteLine("Reading PCMCIA CIS.");
UpdateStatus?.Invoke("Reading PCMCIA CIS.");
ret = outputPlugin.WriteMediaTag(dev.Cis, MediaTagType.PCMCIA_CIS);
ret = _outputPlugin.WriteMediaTag(_dev.Cis, MediaTagType.PCMCIA_CIS);
if(ret)
sidecar.BlockMedia[0].PCMCIA = new PCMCIAType
{
CIS = new DumpType
{
Image = outputPath, Size = (ulong)dev.Cis.Length,
Checksums = Checksum.GetChecksums(dev.Cis).ToArray()
Image = _outputPath, Size = (ulong)_dev.Cis.Length,
Checksums = Checksum.GetChecksums(_dev.Cis).ToArray()
}
};
dumpLog.WriteLine("Decoding PCMCIA CIS.");
_dumpLog.WriteLine("Decoding PCMCIA CIS.");
UpdateStatus?.Invoke("Decoding PCMCIA CIS.");
Tuple[] tuples = CIS.GetTuples(dev.Cis);
Tuple[] tuples = CIS.GetTuples(_dev.Cis);
if(tuples != null)
foreach(Tuple tuple in tuples)
@@ -676,8 +679,7 @@ namespace DiscImageChef.Core.Devices.Dumping
if(manfid != null)
{
sidecar.BlockMedia[0].PCMCIA.ManufacturerCode =
manfid.ManufacturerID;
sidecar.BlockMedia[0].PCMCIA.ManufacturerCode = manfid.ManufacturerID;
sidecar.BlockMedia[0].PCMCIA.CardCode = manfid.CardID;
sidecar.BlockMedia[0].PCMCIA.ManufacturerCodeSpecified = true;
@@ -704,14 +706,14 @@ namespace DiscImageChef.Core.Devices.Dumping
}
}
ret = outputPlugin.WriteMediaTag(ataIdentify, MediaTagType.ATA_IDENTIFY);
ret = _outputPlugin.WriteMediaTag(ataIdentify, MediaTagType.ATA_IDENTIFY);
if(ret)
sidecar.BlockMedia[0].ATA = new ATAType
{
Identify = new DumpType
{
Image = outputPath, Size = (ulong)cmdBuf.Length,
Image = _outputPath, Size = (ulong)cmdBuf.Length,
Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
}
};
@@ -722,12 +724,13 @@ namespace DiscImageChef.Core.Devices.Dumping
UpdateStatus?.Invoke($"Sidecar created in {(chkEnd - chkStart).TotalSeconds} seconds.");
UpdateStatus?.
Invoke($"Average checksum speed {(double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec.");
Invoke($"Average checksum speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000):F3} KiB/sec.");
dumpLog.WriteLine("Sidecar created in {0} seconds.", (chkEnd - chkStart).TotalSeconds);
_dumpLog.WriteLine("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));
_dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
((double)blockSize * (double)(blocks + 1)) / 1024 /
(totalChkDuration / 1000));
List<(ulong start, string type)> filesystems = new List<(ulong start, string type)>();
@@ -746,15 +749,15 @@ namespace DiscImageChef.Core.Devices.Dumping
UpdateStatus?.
Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}");
dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type,
filesystem.start);
_dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type,
filesystem.start);
}
(string type, string subType) xmlType;
if(dev.IsCompactFlash)
if(_dev.IsCompactFlash)
xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.CompactFlash);
else if(dev.IsPcmcia)
else if(_dev.IsPcmcia)
xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.PCCardTypeI);
else
xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.GENERIC_HDD);
@@ -765,9 +768,9 @@ namespace DiscImageChef.Core.Devices.Dumping
sidecar.BlockMedia[0].LogicalBlocks = blocks;
sidecar.BlockMedia[0].PhysicalBlockSize = physicalsectorsize;
sidecar.BlockMedia[0].LogicalBlockSize = blockSize;
sidecar.BlockMedia[0].Manufacturer = dev.Manufacturer;
sidecar.BlockMedia[0].Model = dev.Model;
sidecar.BlockMedia[0].Serial = dev.Serial;
sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer;
sidecar.BlockMedia[0].Model = _dev.Model;
sidecar.BlockMedia[0].Serial = _dev.Serial;
sidecar.BlockMedia[0].Size = blocks * blockSize;
if(cylinders > 0 &&
@@ -784,7 +787,7 @@ namespace DiscImageChef.Core.Devices.Dumping
UpdateStatus?.Invoke("Writing metadata sidecar");
var xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create);
var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);
var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
xmlSer.Serialize(xmlFs, sidecar);
@@ -797,21 +800,21 @@ namespace DiscImageChef.Core.Devices.Dumping
Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");
UpdateStatus?.
Invoke($"Average speed: {(double)blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");
Invoke($"Average speed: {((double)blockSize * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");
UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
UpdateStatus?.Invoke($"{resume.BadBlocks.Count} sectors could not be read.");
UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");
if(resume.BadBlocks.Count > 0)
resume.BadBlocks.Sort();
if(_resume.BadBlocks.Count > 0)
_resume.BadBlocks.Sort();
UpdateStatus?.Invoke("");
}
if(dev.IsCompactFlash)
if(_dev.IsCompactFlash)
Statistics.AddMedia(MediaType.CompactFlash, true);
else if(dev.IsPcmcia)
else if(_dev.IsPcmcia)
Statistics.AddMedia(MediaType.PCCardTypeI, true);
else
Statistics.AddMedia(MediaType.GENERIC_HDD, true);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,382 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CompactDisc.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ Description ] ----------------------------------------------------------
//
// Dumps CDs and DDCDs.
//
// --[ 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-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Linq;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Extents;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using DiscImageChef.Core.Logging;
using DiscImageChef.Decoders.SCSI;
using DiscImageChef.Devices;
using Schemas;
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
namespace DiscImageChef.Core.Devices.Dumping
{
partial class Dump
{
/// <summary>Reads all CD user data</summary>
/// <param name="audioExtents">Extents with audio sectors</param>
/// <param name="blocks">Total number of positive sectors</param>
/// <param name="blockSize">Size of the read sector in bytes</param>
/// <param name="currentSpeed">Current read speed</param>
/// <param name="currentTry">Current dump hardware try</param>
/// <param name="extents">Extents</param>
/// <param name="ibgLog">IMGBurn log</param>
/// <param name="imageWriteDuration">Duration of image write</param>
/// <param name="lastSector">Last sector number</param>
/// <param name="leadOutExtents">Lead-out extents</param>
/// <param name="maxSpeed">Maximum speed</param>
/// <param name="mhddLog">MHDD log</param>
/// <param name="minSpeed">Minimum speed</param>
/// <param name="newTrim">Is trim a new one?</param>
/// <param name="nextData">Next cluster of sectors is all data</param>
/// <param name="offsetBytes">Read offset</param>
/// <param name="read6">Device supports READ(6)</param>
/// <param name="read10">Device supports READ(10)</param>
/// <param name="read12">Device supports READ(12)</param>
/// <param name="read16">Device supports READ(16)</param>
/// <param name="readcd">Device supports READ CD</param>
/// <param name="sectorsForOffset">Sectors needed to fix offset</param>
/// <param name="subSize">Subchannel size in bytes</param>
/// <param name="supportedSubchannel">Drive's maximum supported subchannel</param>
/// <param name="supportsLongSectors">Supports reading EDC and ECC</param>
/// <param name="totalDuration">Total commands duration</param>
void ReadCdData(ExtentsULong audioExtents, ulong blocks, uint blockSize, ref double currentSpeed,
DumpHardwareType currentTry, ExtentsULong extents, IbgLog ibgLog, ref double imageWriteDuration,
long lastSector, ExtentsULong leadOutExtents, ref double maxSpeed, MhddLog mhddLog,
ref double minSpeed, out bool newTrim, bool nextData, int offsetBytes, bool read6, bool read10,
bool read12, bool read16, bool readcd, int sectorsForOffset, uint subSize,
MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration,
Track[] tracks)
{
ulong sectorSpeedStart = 0; // Used to calculate correct speed
DateTime timeSpeedStart = DateTime.UtcNow; // Time of start for speed calculation
uint blocksToRead = 0; // How many sectors to read at once
bool sense = true; // Sense indicator
byte[] cmdBuf = null; // Data buffer
byte[] senseBuf = null; // Sense buffer
double cmdDuration = 0; // Command execution time
const uint sectorSize = 2352; // Full sector size
byte[] tmpBuf; // Temporary buffer
newTrim = false;
InitProgress?.Invoke();
for(ulong i = _resume.NextBlock; (long)i <= lastSector; i += blocksToRead)
{
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
UpdateStatus?.Invoke("Aborted!");
_dumpLog.WriteLine("Aborted!");
break;
}
while(leadOutExtents.Contains(i))
{
i++;
}
if((long)i > lastSector)
break;
uint firstSectorToRead = (uint)i;
Track track =
tracks.OrderBy(t => t.TrackStartSector).LastOrDefault(t => i >= t.TrackStartSector);
blocksToRead = 0;
bool inData = nextData;
for(ulong j = i; j < i + _maximumReadable; j++)
{
if(j > (ulong)lastSector)
{
blocksToRead += (uint)sectorsForOffset;
break;
}
if(nextData)
{
if(audioExtents.Contains(j))
{
nextData = false;
break;
}
blocksToRead++;
}
else
{
if(!audioExtents.Contains(j))
{
nextData = true;
break;
}
blocksToRead++;
}
}
if(track.TrackSequence != 0 &&
(i + blocksToRead) - (ulong)sectorsForOffset > track.TrackEndSector + 1)
blocksToRead = (uint)(((track.TrackEndSector + 1) - i) + (ulong)sectorsForOffset);
if(blocksToRead == 1)
blocksToRead += (uint)sectorsForOffset;
if(_fixOffset && !inData)
{
// TODO: FreeBSD bug
if(offsetBytes < 0)
{
if(i == 0)
firstSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1
else
firstSectorToRead -= (uint)sectorsForOffset;
}
}
#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
// ReSharper disable CompareOfFloatsByEqualityOperator
if(currentSpeed > maxSpeed &&
currentSpeed != 0)
maxSpeed = currentSpeed;
if(currentSpeed < minSpeed &&
currentSpeed != 0)
minSpeed = currentSpeed;
// ReSharper restore CompareOfFloatsByEqualityOperator
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i,
(long)blocks);
if(readcd)
{
sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
totalDuration += cmdDuration;
}
else if(read16)
{
sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, firstSectorToRead, blockSize,
0, blocksToRead, false, _dev.Timeout, out cmdDuration);
}
else if(read12)
{
sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead,
blockSize, 0, blocksToRead, false, _dev.Timeout, out cmdDuration);
}
else if(read10)
{
sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead,
blockSize, 0, (ushort)blocksToRead, _dev.Timeout, out cmdDuration);
}
else if(read6)
{
sense = _dev.Read6(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, (byte)blocksToRead,
_dev.Timeout, out cmdDuration);
}
// Because one block has been partially used to fix the offset
if(_fixOffset &&
!inData &&
offsetBytes != 0)
{
int offsetFix = offsetBytes < 0 ? (int)(sectorSize - (offsetBytes * -1)) : offsetBytes;
if(supportedSubchannel != MmcSubchannel.None)
{
// De-interleave subchannel
byte[] data = new byte[sectorSize * blocksToRead];
byte[] sub = new byte[subSize * blocksToRead];
for(int b = 0; b < blocksToRead; b++)
{
Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);
Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
}
tmpBuf = new byte[sectorSize * (blocksToRead - sectorsForOffset)];
Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length);
data = tmpBuf;
blocksToRead -= (uint)sectorsForOffset;
// Re-interleave subchannel
cmdBuf = new byte[blockSize * blocksToRead];
for(int b = 0; b < blocksToRead; b++)
{
Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize);
Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize);
}
}
else
{
tmpBuf = new byte[blockSize * (blocksToRead - sectorsForOffset)];
Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length);
cmdBuf = tmpBuf;
blocksToRead -= (uint)sectorsForOffset;
}
}
if(!sense &&
!_dev.Error)
{
mhddLog.Write(i, cmdDuration);
ibgLog.Write(i, currentSpeed * 1024);
extents.Add(i, blocksToRead, true);
DateTime writeStart = DateTime.Now;
if(supportedSubchannel != MmcSubchannel.None)
{
byte[] data = new byte[sectorSize * blocksToRead];
byte[] sub = new byte[subSize * blocksToRead];
for(int b = 0; b < blocksToRead; b++)
{
Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);
Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
}
_outputPlugin.WriteSectorsLong(data, i, blocksToRead);
_outputPlugin.WriteSectorsTag(sub, i, blocksToRead, SectorTagType.CdSectorSubchannel);
}
else
{
if(supportsLongSectors)
{
_outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead);
}
else
{
if(cmdBuf.Length % sectorSize == 0)
{
byte[] data = new byte[2048 * blocksToRead];
for(int b = 0; b < blocksToRead; b++)
Array.Copy(cmdBuf, (int)(16 + (b * blockSize)), data, 2048 * b, 2048);
_outputPlugin.WriteSectors(data, i, blocksToRead);
}
else
{
_outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead);
}
}
}
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
}
else
{
// TODO: Reset device after X errors
if(_stopOnError)
return; // TODO: Return more cleanly
if(i + _skip > blocks)
_skip = (uint)(blocks - i);
// Write empty data
DateTime writeStart = DateTime.Now;
if(supportedSubchannel != MmcSubchannel.None)
{
_outputPlugin.WriteSectorsLong(new byte[sectorSize * _skip], i, _skip);
_outputPlugin.WriteSectorsTag(new byte[subSize * _skip], i, _skip,
SectorTagType.CdSectorSubchannel);
}
else
{
if(supportsLongSectors)
{
_outputPlugin.WriteSectorsLong(new byte[blockSize * _skip], i, _skip);
}
else
{
if(cmdBuf.Length % sectorSize == 0)
_outputPlugin.WriteSectors(new byte[2048 * _skip], i, _skip);
else
_outputPlugin.WriteSectorsLong(new byte[blockSize * _skip], i, _skip);
}
}
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
for(ulong b = i; b < i + _skip; b++)
_resume.BadBlocks.Add(b);
DicConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf));
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
ibgLog.Write(i, 0);
_dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
i += _skip - blocksToRead;
newTrim = true;
}
sectorSpeedStart += blocksToRead;
_resume.NextBlock = i + blocksToRead;
double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
if(elapsed < 1)
continue;
currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
sectorSpeedStart = 0;
timeSpeedStart = DateTime.UtcNow;
}
EndProgress?.Invoke();
}
}
}

View File

@@ -0,0 +1,985 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CompactDisc.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ Description ] ----------------------------------------------------------
//
// Dumps CDs and DDCDs.
//
// --[ 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-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Extents;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using DiscImageChef.Core.Logging;
using DiscImageChef.Core.Media.Detection;
using DiscImageChef.Database.Models;
using DiscImageChef.Decoders.CD;
using DiscImageChef.Devices;
using Schemas;
using PlatformID = DiscImageChef.CommonTypes.Interop.PlatformID;
using TrackType = DiscImageChef.CommonTypes.Enums.TrackType;
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
namespace DiscImageChef.Core.Devices.Dumping
{
/// <summary>Implement dumping Compact Discs</summary>
// TODO: Barcode
partial class Dump
{
/// <summary>Dumps a compact disc</summary>
/// <param name="dskType">Disc type as detected in MMC layer</param>
void CompactDisc(out MediaType dskType)
{
ExtentsULong audioExtents; // Extents with audio sectors
ulong blocks; // Total number of positive sectors
uint blockSize; // Size of the read sector in bytes
CdOffset cdOffset; // Read offset from database
byte[] cmdBuf; // Data buffer
DumpHardwareType currentTry = null; // Current dump hardware try
double currentSpeed = 0; // Current read speed
DateTime dumpStart = DateTime.UtcNow; // Time of dump start
DateTime end; // Time of operation end
ExtentsULong extents = null; // Extents
bool hiddenData; // Hidden track is data
IbgLog ibgLog; // IMGBurn log
double imageWriteDuration = 0; // Duration of image write
long lastSector; // Last sector number
var leadOutExtents = new ExtentsULong(); // Lead-out extents
Dictionary<int, long> leadOutStarts = new Dictionary<int, long>(); // Lead-out starts
double maxSpeed = double.MinValue; // Maximum speed
MhddLog mhddLog; // MHDD log
double minSpeed = double.MaxValue; // Minimum speed
bool newTrim; // Is trim a new one?
int offsetBytes = 0; // Read offset
bool read6 = false; // Device supports READ(6)
bool read10 = false; // Device supports READ(10)
bool read12 = false; // Device supports READ(12)
bool read16 = false; // Device supports READ(16)
bool readcd; // Device supports READ CD
bool ret; // Image writing return status
const uint sectorSize = 2352; // Full sector size
int sectorsForOffset = 0; // Sectors needed to fix offset
bool sense = true; // Sense indicator
int sessions; // Number of sessions in disc
DateTime start; // Start of operation
uint subSize; // Subchannel size in bytes
TrackSubchannelType subType; // Track subchannel type
bool supportsLongSectors = true; // Supports reading EDC and ECC
bool supportsPqSubchannel; // Supports reading PQ subchannel
bool supportsRwSubchannel; // Supports reading RW subchannel
byte[] tmpBuf; // Temporary buffer
FullTOC.CDFullTOC? toc; // Full CD TOC
double totalDuration = 0; // Total commands duration
Dictionary<byte, byte> trackFlags = new Dictionary<byte, byte>(); // Track flags
Track[] tracks; // Tracks in disc
int firstTrackLastSession; // Number of first track in last session
bool hiddenTrack; // Disc has a hidden track before track 1
MmcSubchannel supportedSubchannel; // Drive's maximum supported subchannel
Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>(); // Media tags
dskType = MediaType.CD;
if(_dumpRaw)
{
_dumpLog.WriteLine("Raw CD dumping not yet implemented");
StoppingErrorMessage?.Invoke("Raw CD dumping not yet implemented");
return;
}
// Search for read offset in master database
cdOffset = _ctx.CdOffsets.FirstOrDefault(d => d.Manufacturer == _dev.Manufacturer && d.Model == _dev.Model);
if(cdOffset is null)
{
_dumpLog.WriteLine("CD reading offset not found in database.");
UpdateStatus?.Invoke("CD reading offset not found in database.");
}
else
{
_dumpLog.WriteLine($"CD reading offset is {cdOffset.Offset} samples.");
UpdateStatus?.Invoke($"CD reading offset is {cdOffset.Offset} samples.");
}
// Check subchannels support
supportsPqSubchannel = SupportsPqSubchannel();
supportsRwSubchannel = SupportsRwSubchannel();
switch(_subchannel)
{
case DumpSubchannel.Any:
if(supportsRwSubchannel)
supportedSubchannel = MmcSubchannel.Raw;
else if(supportsPqSubchannel)
supportedSubchannel = MmcSubchannel.Q16;
else
supportedSubchannel = MmcSubchannel.None;
break;
case DumpSubchannel.Rw:
if(supportsRwSubchannel)
supportedSubchannel = MmcSubchannel.Raw;
else
{
_dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing...");
StoppingErrorMessage?.
Invoke("Drive does not support the requested subchannel format, not continuing...");
return;
}
break;
case DumpSubchannel.RwOrPq:
if(supportsRwSubchannel)
supportedSubchannel = MmcSubchannel.Raw;
else if(supportsPqSubchannel)
supportedSubchannel = MmcSubchannel.Q16;
else
{
_dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing...");
StoppingErrorMessage?.
Invoke("Drive does not support the requested subchannel format, not continuing...");
return;
}
break;
case DumpSubchannel.Pq:
if(supportsPqSubchannel)
supportedSubchannel = MmcSubchannel.Q16;
else
{
_dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing...");
StoppingErrorMessage?.
Invoke("Drive does not support the requested subchannel format, not continuing...");
return;
}
break;
case DumpSubchannel.None:
supportedSubchannel = MmcSubchannel.None;
break;
default: throw new ArgumentOutOfRangeException();
}
// Check if output format supports subchannels
if(!_outputPlugin.SupportedSectorTags.Contains(SectorTagType.CdSectorSubchannel) &&
supportedSubchannel != MmcSubchannel.None)
{
if(_force ||
_subchannel == DumpSubchannel.None)
{
_dumpLog.WriteLine("Output format does not support subchannels, continuing...");
UpdateStatus?.Invoke("Output format does not support subchannels, continuing...");
}
else
{
_dumpLog.WriteLine("Output format does not support subchannels, not continuing...");
StoppingErrorMessage?.Invoke("Output format does not support subchannels, not continuing...");
return;
}
supportedSubchannel = MmcSubchannel.None;
}
switch(supportedSubchannel)
{
case MmcSubchannel.None:
_dumpLog.WriteLine("Checking if drive supports reading without subchannel...");
UpdateStatus?.Invoke("Checking if drive supports reading without subchannel...");
readcd = !_dev.ReadCd(out cmdBuf, out _, 0, sectorSize, 1, MmcSectorTypes.AllTypes, false, false,
true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
supportedSubchannel, _dev.Timeout, out _);
if(!readcd)
{
_dumpLog.WriteLine("Drive does not support READ CD, trying SCSI READ commands...");
ErrorMessage?.Invoke("Drive does not support READ CD, trying SCSI READ commands...");
_dumpLog.WriteLine("Checking if drive supports READ(6)...");
UpdateStatus?.Invoke("Checking if drive supports READ(6)...");
read6 = !_dev.Read6(out cmdBuf, out _, 0, 2048, 1, _dev.Timeout, out _);
_dumpLog.WriteLine("Checking if drive supports READ(10)...");
UpdateStatus?.Invoke("Checking if drive supports READ(10)...");
read10 = !_dev.Read10(out cmdBuf, out _, 0, false, true, false, false, 0, 2048, 0, 1,
_dev.Timeout, out _);
_dumpLog.WriteLine("Checking if drive supports READ(12)...");
UpdateStatus?.Invoke("Checking if drive supports READ(12)...");
read12 = !_dev.Read12(out cmdBuf, out _, 0, false, true, false, false, 0, 2048, 0, 1, false,
_dev.Timeout, out _);
_dumpLog.WriteLine("Checking if drive supports READ(16)...");
UpdateStatus?.Invoke("Checking if drive supports READ(16)...");
read16 = !_dev.Read16(out cmdBuf, out _, 0, false, true, false, 0, 2048, 0, 1, false,
_dev.Timeout, out _);
if(!read6 &&
!read10 &&
!read12 &&
!read16)
{
_dumpLog.WriteLine("Cannot read from disc, not continuing...");
StoppingErrorMessage?.Invoke("Cannot read from disc, not continuing...");
return;
}
if(read6)
{
_dumpLog.WriteLine("Drive supports READ(6)...");
UpdateStatus?.Invoke("Drive supports READ(6)...");
}
if(read10)
{
_dumpLog.WriteLine("Drive supports READ(10)...");
UpdateStatus?.Invoke("Drive supports READ(10)...");
}
if(read12)
{
_dumpLog.WriteLine("Drive supports READ(12)...");
UpdateStatus?.Invoke("Drive supports READ(12)...");
}
if(read16)
{
_dumpLog.WriteLine("Drive supports READ(16)...");
UpdateStatus?.Invoke("Drive supports READ(16)...");
}
}
_dumpLog.WriteLine("Drive can read without subchannel...");
_dumpLog.WriteLine("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
UpdateStatus?.Invoke("Drive can read without subchannel...");
UpdateStatus?.
Invoke("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
subSize = 0;
subType = TrackSubchannelType.None;
break;
case MmcSubchannel.Raw:
_dumpLog.WriteLine("Full raw subchannel reading supported...");
UpdateStatus?.Invoke("Full raw subchannel reading supported...");
subType = TrackSubchannelType.Raw;
subSize = 96;
readcd = true;
break;
case MmcSubchannel.Q16:
_dumpLog.WriteLine("PQ subchannel reading supported...");
_dumpLog.WriteLine("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
UpdateStatus?.Invoke("PQ subchannel reading supported...");
UpdateStatus?.
Invoke("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
subType = TrackSubchannelType.Q16;
subSize = 16;
readcd = true;
break;
default:
_dumpLog.WriteLine("Handling subchannel type {0} not supported, exiting...", supportedSubchannel);
StoppingErrorMessage?.
Invoke($"Handling subchannel type {supportedSubchannel} not supported, exiting...");
return;
}
blockSize = sectorSize + subSize;
tracks = GetCdTracks(ref blockSize, dskType, out lastSector, leadOutStarts, mediaTags, out toc, trackFlags,
subType);
if(tracks is null)
return;
SolveTrackPregaps(tracks, supportsPqSubchannel, supportsRwSubchannel);
for(int t = 1; t < tracks.Length; t++)
tracks[t - 1].TrackEndSector = tracks[t].TrackStartSector - 1;
tracks[tracks.Length - 1].TrackEndSector = (ulong)lastSector;
blocks = (ulong)(lastSector + 1);
if(blocks == 0)
{
StoppingErrorMessage?.Invoke("Cannot dump blank media.");
return;
}
ResumeSupport.Process(true, true, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId,
ref _resume, ref currentTry, ref extents);
if(currentTry == null ||
extents == null)
{
StoppingErrorMessage?.Invoke("Could not process resume file, not continuing...");
return;
}
// Read media tags
ReadCdTags(ref dskType, mediaTags, out sessions, out firstTrackLastSession);
// Check if output format supports all disc tags we have retrieved so far
foreach(MediaTagType tag in mediaTags.Keys)
{
if(_outputPlugin.SupportedMediaTags.Contains(tag))
continue;
if(_force)
{
_dumpLog.WriteLine("Output format does not support {0}, continuing...", tag);
ErrorMessage?.Invoke($"Output format does not support {tag}, continuing...");
}
else
{
_dumpLog.WriteLine("Output format does not support {0}, not continuing...", tag);
StoppingErrorMessage?.Invoke($"Output format does not support {tag}, not continuing...");
return;
}
}
if(leadOutStarts.Any())
{
UpdateStatus?.Invoke("Solving lead-outs...");
foreach(KeyValuePair<int, long> leadOuts in leadOutStarts)
for(int i = 0; i < tracks.Length; i++)
{
if(tracks[i].TrackSession != leadOuts.Key)
continue;
if(tracks[i].TrackEndSector >= (ulong)leadOuts.Value)
tracks[i].TrackEndSector = (ulong)leadOuts.Value - 1;
}
var dataExtents = new ExtentsULong();
foreach(Track trk in tracks)
dataExtents.Add(trk.TrackStartSector, trk.TrackEndSector);
Tuple<ulong, ulong>[] dataExtentsArray = dataExtents.ToArray();
for(int i = 0; i < dataExtentsArray.Length - 1; i++)
leadOutExtents.Add(dataExtentsArray[i].Item2 + 1, dataExtentsArray[i + 1].Item1 - 1);
}
_dumpLog.WriteLine("Detecting disc type...");
UpdateStatus?.Invoke("Detecting disc type...");
MMC.DetectDiscType(ref dskType, sessions, toc, _dev, out hiddenTrack, out hiddenData,
firstTrackLastSession);
if(hiddenTrack)
{
_dumpLog.WriteLine("Disc contains a hidden track...");
UpdateStatus?.Invoke("Disc contains a hidden track...");
List<Track> trkList = new List<Track>
{
new Track
{
TrackSequence = 0, TrackSession = 1,
TrackType = hiddenData ? TrackType.Data : TrackType.Audio,
TrackStartSector = 0, TrackBytesPerSector = (int)sectorSize,
TrackRawBytesPerSector = (int)sectorSize, TrackSubchannelType = subType,
TrackEndSector = tracks.First(t => t.TrackSequence == 1).TrackStartSector - 1
}
};
trkList.AddRange(tracks);
tracks = trkList.ToArray();
}
// Check mode for tracks
for(int t = 0; t < tracks.Length; t++)
{
if(!readcd)
{
tracks[t].TrackType = TrackType.CdMode1;
continue;
}
if(tracks[t].TrackType == TrackType.Audio)
continue;
_dumpLog.WriteLine("Checking mode for track {0}...", tracks[t].TrackSequence);
UpdateStatus?.Invoke($"Checking mode for track {tracks[t].TrackSequence}...");
sense = !_dev.ReadCd(out cmdBuf, out _, (uint)tracks[t].TrackStartSector, blockSize, 1,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
MmcErrorField.None, supportedSubchannel, _dev.Timeout, out _);
if(!sense)
{
_dumpLog.WriteLine("Unable to guess mode for track {0}, continuing...", tracks[t].TrackSequence);
UpdateStatus?.Invoke($"Unable to guess mode for track {tracks[t].TrackSequence}, continuing...");
continue;
}
switch(cmdBuf[15])
{
case 1:
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE1");
_dumpLog.WriteLine("Track {0} is MODE1", tracks[t].TrackSequence);
tracks[t].TrackType = TrackType.CdMode1;
break;
case 2:
if(dskType == MediaType.CDI ||
dskType == MediaType.CDIREADY)
{
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE2");
_dumpLog.WriteLine("Track {0} is MODE2", tracks[t].TrackSequence);
tracks[t].TrackType = TrackType.CdMode2Formless;
break;
}
if((cmdBuf[0x012] & 0x20) == 0x20) // mode 2 form 2
{
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE2 FORM 2");
_dumpLog.WriteLine("Track {0} is MODE2 FORM 2", tracks[t].TrackSequence);
tracks[t].TrackType = TrackType.CdMode2Form2;
break;
}
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE2 FORM 1");
_dumpLog.WriteLine("Track {0} is MODE2 FORM 1", tracks[t].TrackSequence);
tracks[t].TrackType = TrackType.CdMode2Form1;
// These media type specifications do not legally allow mode 2 tracks to be present
if(dskType == MediaType.CDROM ||
dskType == MediaType.CDPLUS ||
dskType == MediaType.CDV)
dskType = MediaType.CD;
break;
default:
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is unknown mode {cmdBuf[15]}");
_dumpLog.WriteLine("Track {0} is unknown mode {1}", tracks[t].TrackSequence, cmdBuf[15]);
break;
}
}
if(_outputPlugin.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000"))
{
if(tracks.Length > 1)
{
StoppingErrorMessage?.Invoke("Output format does not support more than 1 track, not continuing...");
_dumpLog.WriteLine("Output format does not support more than 1 track, not continuing...");
return;
}
if(tracks.Any(t => t.TrackType == TrackType.Audio))
{
StoppingErrorMessage?.Invoke("Output format does not support audio tracks, not continuing...");
_dumpLog.WriteLine("Output format does not support audio tracks, not continuing...");
return;
}
if(tracks.Any(t => t.TrackType != TrackType.CdMode1))
{
StoppingErrorMessage?.Invoke("Output format only supports MODE 1 tracks, not continuing...");
_dumpLog.WriteLine("Output format only supports MODE 1 tracks, not continuing...");
return;
}
supportsLongSectors = false;
}
// Check if something prevents from dumping the first track pregap
if(_dumpFirstTrackPregap && readcd)
{
if(_dev.PlatformId == PlatformID.FreeBSD &&
!_dev.IsRemote)
{
if(_force)
{
_dumpLog.
WriteLine("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. continuing");
ErrorMessage?.
Invoke("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. continuing");
}
else
{
_dumpLog.
WriteLine("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. Not continuing");
StoppingErrorMessage?.
Invoke("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. Not continuing");
return;
}
_dumpFirstTrackPregap = false;
}
if(!_outputPlugin.SupportedMediaTags.Contains(MediaTagType.CD_FirstTrackPregap))
{
if(_force)
{
_dumpLog.WriteLine("Output format does not support CD first track pregap, continuing...");
ErrorMessage?.Invoke("Output format does not support CD first track pregap, continuing...");
}
else
{
_dumpLog.WriteLine("Output format does not support CD first track pregap, not continuing...");
StoppingErrorMessage?.
Invoke("Output format does not support CD first track pregap, not continuing...");
return;
}
_dumpFirstTrackPregap = false;
}
}
// Try to read the first track pregap
if(_dumpFirstTrackPregap && readcd)
ReadCdFirstTrackPregap(blockSize, ref currentSpeed, mediaTags, supportedSubchannel, ref totalDuration);
// Try how many blocks are readable at once
while(true)
{
if(readcd)
{
sense = _dev.ReadCd(out cmdBuf, out _, 0, blockSize, _maximumReadable, MmcSectorTypes.AllTypes,
false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
supportedSubchannel, _dev.Timeout, out _);
if(_dev.Error || sense)
_maximumReadable /= 2;
}
else if(read16)
{
sense = _dev.Read16(out cmdBuf, out _, 0, false, true, false, 0, blockSize, 0, _maximumReadable,
false, _dev.Timeout, out _);
if(_dev.Error || sense)
_maximumReadable /= 2;
}
else if(read12)
{
sense = _dev.Read12(out cmdBuf, out _, 0, false, true, false, false, 0, blockSize, 0,
_maximumReadable, false, _dev.Timeout, out _);
if(_dev.Error || sense)
_maximumReadable /= 2;
}
else if(read10)
{
sense = _dev.Read10(out cmdBuf, out _, 0, false, true, false, false, 0, blockSize, 0,
(ushort)_maximumReadable, _dev.Timeout, out _);
if(_dev.Error || sense)
_maximumReadable /= 2;
}
else if(read6)
{
sense = _dev.Read6(out cmdBuf, out _, 0, blockSize, (byte)_maximumReadable, _dev.Timeout, out _);
if(_dev.Error || sense)
_maximumReadable /= 2;
}
if(!_dev.Error ||
_maximumReadable == 1)
break;
}
if(_dev.Error || sense)
{
_dumpLog.WriteLine("Device error {0} trying to guess ideal transfer length.", _dev.LastError);
StoppingErrorMessage?.Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length.");
}
_dumpLog.WriteLine("Reading {0} sectors at a time.", _maximumReadable);
_dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);
_dumpLog.WriteLine("Device can read {0} blocks at a time.", _maximumReadable);
_dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize);
_dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType);
_dumpLog.WriteLine("Media identified as {0}.", dskType);
UpdateStatus?.Invoke($"Reading {_maximumReadable} sectors at a time.");
UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes).");
UpdateStatus?.Invoke($"Device can read {_maximumReadable} blocks at a time.");
UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block.");
UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}.");
UpdateStatus?.Invoke($"Media identified as {dskType}.");
ret = _outputPlugin.Create(_outputPath, dskType, _formatOptions, blocks,
supportsLongSectors ? blockSize : 2048);
// Cannot create image
if(!ret)
{
_dumpLog.WriteLine("Error creating output image, not continuing.");
_dumpLog.WriteLine(_outputPlugin.ErrorMessage);
StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine +
_outputPlugin.ErrorMessage);
}
// Send track list to output plugin. This may fail if subchannel is set but unsupported.
ret = (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList());
if(!ret &&
supportedSubchannel == MmcSubchannel.None)
{
_dumpLog.WriteLine("Error sending tracks to output image, not continuing.");
_dumpLog.WriteLine(_outputPlugin.ErrorMessage);
StoppingErrorMessage?.Invoke("Error sending tracks to output image, not continuing." +
Environment.NewLine +
_outputPlugin.ErrorMessage);
return;
}
// If a subchannel is supported, check if output plugin allows us to write it.
if(supportedSubchannel != MmcSubchannel.None)
{
_dev.ReadCd(out cmdBuf, out _, 0, blockSize, 1, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, supportedSubchannel,
_dev.Timeout, out _);
tmpBuf = new byte[subSize];
Array.Copy(cmdBuf, sectorSize, tmpBuf, 0, subSize);
ret = _outputPlugin.WriteSectorTag(tmpBuf, 0, SectorTagType.CdSectorSubchannel);
if(!ret)
{
if(_force)
{
_dumpLog.WriteLine("Error writing subchannel to output image, {0}continuing...",
_force ? "" : "not ");
_dumpLog.WriteLine(_outputPlugin.ErrorMessage);
ErrorMessage?.Invoke("Error writing subchannel to output image, continuing..." +
Environment.NewLine +
_outputPlugin.ErrorMessage);
}
else
{
StoppingErrorMessage?.Invoke("Error writing subchannel to output image, not continuing..." +
Environment.NewLine +
_outputPlugin.ErrorMessage);
return;
}
supportedSubchannel = MmcSubchannel.None;
subSize = 0;
blockSize = sectorSize + subSize;
for(int t = 0; t < tracks.Length; t++)
tracks[t].TrackSubchannelType = TrackSubchannelType.None;
ret = (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList());
if(!ret)
{
_dumpLog.WriteLine("Error sending tracks to output image, not continuing.");
_dumpLog.WriteLine(_outputPlugin.ErrorMessage);
StoppingErrorMessage?.Invoke("Error sending tracks to output image, not continuing..." +
Environment.NewLine +
_outputPlugin.ErrorMessage);
return;
}
}
}
// Set track flags
foreach(KeyValuePair<byte, byte> kvp in trackFlags)
{
Track track = tracks.FirstOrDefault(t => t.TrackSequence == kvp.Key);
if(track.TrackSequence == 0)
continue;
_dumpLog.WriteLine("Setting flags for track {0}...", track.TrackSequence);
UpdateStatus?.Invoke($"Setting flags for track {track.TrackSequence}...");
_outputPlugin.WriteSectorTag(new[]
{
kvp.Value
}, track.TrackStartSector, SectorTagType.CdTrackFlags);
}
// Set MCN
sense = _dev.ReadMcn(out string mcn, out _, out _, _dev.Timeout, out _);
if(!sense &&
mcn != null &&
mcn != "0000000000000" &&
_outputPlugin.WriteMediaTag(Encoding.ASCII.GetBytes(mcn), MediaTagType.CD_MCN))
{
UpdateStatus?.Invoke($"Setting disc Media Catalogue Number to {mcn}");
_dumpLog.WriteLine("Setting disc Media Catalogue Number to {0}", mcn);
}
// Set ISRCs
foreach(Track trk in tracks)
{
sense = _dev.ReadIsrc((byte)trk.TrackSequence, out string isrc, out _, out _, _dev.Timeout, out _);
if(sense ||
isrc == null ||
isrc == "000000000000")
continue;
if(!_outputPlugin.WriteSectorTag(Encoding.ASCII.GetBytes(isrc), trk.TrackStartSector,
SectorTagType.CdTrackIsrc))
continue;
UpdateStatus?.Invoke($"Setting ISRC for track {trk.TrackSequence} to {isrc}");
_dumpLog.WriteLine("Setting ISRC for track {0} to {1}", trk.TrackSequence, isrc);
}
if(_resume.NextBlock > 0)
{
UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}.");
_dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
}
if(_skip < _maximumReadable)
_skip = _maximumReadable;
#if DEBUG
foreach(Track trk in tracks)
UpdateStatus?.
Invoke($"Track {trk.TrackSequence} starts at LBA {trk.TrackStartSector} and ends at LBA {trk.TrackEndSector}");
#endif
if(dskType == MediaType.CDIREADY)
{
_dumpLog.WriteLine("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them.");
UpdateStatus?.
Invoke("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them.");
}
// Check offset
if(_fixOffset)
{
_fixOffset = Media.Info.CompactDisc.GetOffset(cdOffset, _dbDev, _debug, _dev, dskType, _dumpLog,
out offsetBytes, readcd, out sectorsForOffset, tracks,
UpdateStatus);
}
else if(tracks.Any(t => t.TrackType == TrackType.Audio))
{
_dumpLog.WriteLine("There are audio tracks and offset fixing is disabled, dump may not be correct.");
UpdateStatus?.Invoke("There are audio tracks and offset fixing is disabled, dump may not be correct.");
}
mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, _maximumReadable);
ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0008);
audioExtents = new ExtentsULong();
foreach(Track audioTrack in tracks.Where(t => t.TrackType == TrackType.Audio))
{
audioExtents.Add(audioTrack.TrackStartSector, audioTrack.TrackEndSector);
}
// Set speed
if(_speedMultiplier >= 0)
{
_dumpLog.WriteLine($"Setting speed to {(_speed == 0 ? "MAX" : $"{_speed}x")}.");
UpdateStatus?.Invoke($"Setting speed to {(_speed == 0 ? "MAX" : $"{_speed}x")}.");
_speed *= _speedMultiplier;
if(_speed == 0 ||
_speed > 0xFFFF)
_speed = 0xFFFF;
_dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, (ushort)_speed, 0, _dev.Timeout, out _);
}
// Start reading
start = DateTime.UtcNow;
ReadCdData(audioExtents, blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog,
ref imageWriteDuration, lastSector, leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed,
out newTrim, tracks[0].TrackType != TrackType.Audio, offsetBytes, read6, read10, read12, read16,
readcd, sectorsForOffset, subSize, supportedSubchannel, supportsLongSectors, ref totalDuration, tracks);
// TODO: Enable when underlying images support lead-outs
/*
DumpCdLeadOuts(blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration,
leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, read6, read10, read12, read16, readcd,
supportedSubchannel, subSize, ref totalDuration);
*/
end = DateTime.UtcNow;
mhddLog.Close();
ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
(blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000),
_devicePath);
UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");
UpdateStatus?.
Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");
UpdateStatus?.
Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec.");
_dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
_dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000));
_dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration);
TrimCdUserData(audioExtents, blockSize, currentTry, extents, newTrim, offsetBytes, read6, read10, read12,
read16, readcd, sectorsForOffset, subSize, supportedSubchannel, supportsLongSectors,
ref totalDuration);
RetryCdUserData(audioExtents, blockSize, currentTry, extents, offsetBytes, readcd, sectorsForOffset,
subSize, supportedSubchannel, ref totalDuration);
// Write media tags to image
if(!_aborted)
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags)
{
if(tag.Value is null)
{
DicConsole.ErrorWriteLine("Error: Tag type {0} is null, skipping...", tag.Key);
continue;
}
ret = _outputPlugin.WriteMediaTag(tag.Value, tag.Key);
if(ret || _force)
continue;
// Cannot write tag to image
_dumpLog.WriteLine($"Cannot write tag {tag.Key}.");
StoppingErrorMessage?.Invoke(_outputPlugin.ErrorMessage);
return;
}
_resume.BadBlocks.Sort();
foreach(ulong bad in _resume.BadBlocks)
_dumpLog.WriteLine("Sector {0} could not be read.", bad);
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
_outputPlugin.SetDumpHardware(_resume.Tries);
if(_preSidecar != null)
_outputPlugin.SetCicmMetadata(_preSidecar);
_dumpLog.WriteLine("Closing output file.");
UpdateStatus?.Invoke("Closing output file.");
DateTime closeStart = DateTime.Now;
_outputPlugin.Close();
DateTime closeEnd = DateTime.Now;
UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds.");
if(_aborted)
{
_dumpLog.WriteLine("Aborted!");
return;
}
double totalChkDuration = 0;
if(!_nometadata)
WriteOpticalSidecar(blockSize, blocks, dskType, null, mediaTags, sessions, out totalChkDuration);
end = DateTime.UtcNow;
UpdateStatus?.Invoke("");
UpdateStatus?.
Invoke($"Took a total of {(end - dumpStart).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");
UpdateStatus?.
Invoke($"Average speed: {((double)blockSize * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");
UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");
UpdateStatus?.Invoke("");
Statistics.AddMedia(dskType, true);
}
}
}

View File

@@ -0,0 +1,411 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CompactDisc.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ Description ] ----------------------------------------------------------
//
// Dumps CDs and DDCDs.
//
// --[ 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-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Extents;
using DiscImageChef.Console;
using DiscImageChef.Decoders.SCSI;
using DiscImageChef.Devices;
using Schemas;
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
namespace DiscImageChef.Core.Devices.Dumping
{
partial class Dump
{
void RetryCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry,
ExtentsULong extents, int offsetBytes, bool readcd, int sectorsForOffset, uint subSize,
MmcSubchannel supportedSubchannel, ref double totalDuration)
{
bool sense = true; // Sense indicator
byte[] cmdBuf = null; // Data buffer
double cmdDuration; // Command execution time
const uint sectorSize = 2352; // Full sector size
byte[] tmpBuf; // Temporary buffer
byte[] senseBuf = null; // Sense buffer
if(_resume.BadBlocks.Count <= 0 ||
_aborted ||
_retryPasses <= 0)
return;
int pass = 1;
bool forward = true;
bool runningPersistent = false;
Modes.ModePage? currentModePage = null;
byte[] md6;
byte[] md10;
if(_persistent)
{
Modes.ModePage_01_MMC pgMmc;
sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout,
out _);
if(sense)
{
sense = _dev.ModeSense10(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01,
_dev.Timeout, out _);
if(!sense)
{
Modes.DecodedMode? dcMode10 =
Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);
if(dcMode10?.Pages != null)
foreach(Modes.ModePage modePage in dcMode10.Value.Pages)
if(modePage.Page == 0x01 &&
modePage.Subpage == 0x00)
currentModePage = modePage;
}
}
else
{
Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);
if(dcMode6?.Pages != null)
foreach(Modes.ModePage modePage in dcMode6.Value.Pages)
if(modePage.Page == 0x01 &&
modePage.Subpage == 0x00)
currentModePage = modePage;
}
if(currentModePage == null)
{
pgMmc = new Modes.ModePage_01_MMC
{
PS = false, ReadRetryCount = 32, Parameter = 0x00
};
currentModePage = new Modes.ModePage
{
Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc)
};
}
pgMmc = new Modes.ModePage_01_MMC
{
PS = false, ReadRetryCount = 255, Parameter = 0x20
};
var md = new Modes.DecodedMode
{
Header = new Modes.ModeHeader(), Pages = new[]
{
new Modes.ModePage
{
Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc)
}
}
};
md6 = Modes.EncodeMode6(md, _dev.ScsiType);
md10 = Modes.EncodeMode10(md, _dev.ScsiType);
UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks).");
_dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks).");
sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _);
if(sense)
sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _);
if(sense)
{
UpdateStatus?.
Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");
DicConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf));
_dumpLog.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");
}
else
{
runningPersistent = true;
}
}
InitProgress?.Invoke();
cdRepeatRetry:
ulong[] tmpArray = _resume.BadBlocks.ToArray();
List<ulong> sectorsNotEvenPartial = new List<ulong>();
foreach(ulong badSector in tmpArray)
{
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
_dumpLog.WriteLine("Aborted!");
break;
}
PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass,
forward ? "forward" : "reverse",
runningPersistent ? "recovering partial data, " : ""));
byte sectorsToReRead = 1;
uint badSectorToReRead = (uint)badSector;
if(_fixOffset &&
audioExtents.Contains(badSector) &&
offsetBytes != 0)
{
if(offsetBytes > 0)
{
badSectorToReRead -= (uint)sectorsForOffset;
}
sectorsToReRead = (byte)(sectorsForOffset + 1);
}
if(readcd)
{
sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, sectorsToReRead,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
totalDuration += cmdDuration;
}
if(sense || _dev.Error)
{
if(!runningPersistent)
continue;
FixedSense? decSense = Sense.DecodeFixed(senseBuf);
// MEDIUM ERROR, retry with ignore error below
if(decSense.HasValue &&
decSense.Value.ASC == 0x11)
if(!sectorsNotEvenPartial.Contains(badSector))
sectorsNotEvenPartial.Add(badSector);
}
// Because one block has been partially used to fix the offset
if(_fixOffset &&
audioExtents.Contains(badSector) &&
offsetBytes != 0)
{
int offsetFix = offsetBytes > 0 ? (int)(sectorSize - (offsetBytes * -1)) : offsetBytes;
if(supportedSubchannel != MmcSubchannel.None)
{
// De-interleave subchannel
byte[] data = new byte[sectorSize * sectorsToReRead];
byte[] sub = new byte[subSize * sectorsToReRead];
for(int b = 0; b < sectorsToReRead; b++)
{
Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);
Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
}
tmpBuf = new byte[sectorSize * (sectorsToReRead - sectorsForOffset)];
Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length);
data = tmpBuf;
// Re-interleave subchannel
cmdBuf = new byte[blockSize * sectorsToReRead];
for(int b = 0; b < sectorsToReRead; b++)
{
Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize);
Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize);
}
}
else
{
tmpBuf = new byte[blockSize * (sectorsToReRead - sectorsForOffset)];
Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length);
cmdBuf = tmpBuf;
}
}
if(!sense &&
!_dev.Error)
{
_resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
UpdateStatus?.Invoke($"Correctly retried sector {badSector} in pass {pass}.");
_dumpLog.WriteLine("Correctly retried sector {0} in pass {1}.", badSector, pass);
sectorsNotEvenPartial.Remove(badSector);
}
if(supportedSubchannel != MmcSubchannel.None)
{
byte[] data = new byte[sectorSize];
byte[] sub = new byte[subSize];
Array.Copy(cmdBuf, 0, data, 0, sectorSize);
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
_outputPlugin.WriteSectorLong(data, badSector);
_outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel);
}
else
{
_outputPlugin.WriteSectorLong(cmdBuf, badSector);
}
}
if(pass < _retryPasses &&
!_aborted &&
_resume.BadBlocks.Count > 0)
{
pass++;
forward = !forward;
_resume.BadBlocks.Sort();
_resume.BadBlocks.Reverse();
goto cdRepeatRetry;
}
EndProgress?.Invoke();
// TODO: Enable when underlying images support lead-outs
/*
RetryCdLeadOuts(blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration,
leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, read6, read10, read12, read16, readcd,
supportedSubchannel, subSize, ref totalDuration);
*/
// Try to ignore read errors, on some drives this allows to recover partial even if damaged data
if(_persistent && sectorsNotEvenPartial.Count > 0)
{
var pgMmc = new Modes.ModePage_01_MMC
{
PS = false, ReadRetryCount = 255, Parameter = 0x01
};
var md = new Modes.DecodedMode
{
Header = new Modes.ModeHeader(), Pages = new[]
{
new Modes.ModePage
{
Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc)
}
}
};
md6 = Modes.EncodeMode6(md, _dev.ScsiType);
md10 = Modes.EncodeMode10(md, _dev.ScsiType);
_dumpLog.WriteLine("Sending MODE SELECT to drive (ignore error correction).");
sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _);
if(sense)
sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _);
if(!sense)
{
runningPersistent = true;
InitProgress?.Invoke();
foreach(ulong badSector in sectorsNotEvenPartial)
{
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
_dumpLog.WriteLine("Aborted!");
break;
}
PulseProgress?.Invoke($"Trying to get partial data for sector {badSector}");
if(readcd)
{
sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)badSector, blockSize, 1,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout,
out cmdDuration);
totalDuration += cmdDuration;
}
if(sense || _dev.Error)
continue;
_dumpLog.WriteLine("Got partial data for sector {0} in pass {1}.", badSector, pass);
if(supportedSubchannel != MmcSubchannel.None)
{
byte[] data = new byte[sectorSize];
byte[] sub = new byte[subSize];
Array.Copy(cmdBuf, 0, data, 0, sectorSize);
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
_outputPlugin.WriteSectorLong(data, badSector);
_outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel);
}
else
{
_outputPlugin.WriteSectorLong(cmdBuf, badSector);
}
}
EndProgress?.Invoke();
}
}
if(runningPersistent && currentModePage.HasValue)
{
var md = new Modes.DecodedMode
{
Header = new Modes.ModeHeader(), Pages = new[]
{
currentModePage.Value
}
};
md6 = Modes.EncodeMode6(md, _dev.ScsiType);
md10 = Modes.EncodeMode10(md, _dev.ScsiType);
_dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status).");
sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _);
if(sense)
_dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _);
}
EndProgress?.Invoke();
}
}
}

View File

@@ -0,0 +1,347 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CompactDisc.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ Description ] ----------------------------------------------------------
//
// Dumps CDs and DDCDs.
//
// --[ 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-2019 Natalia Portillo
// ****************************************************************************/
using System;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Extents;
using DiscImageChef.Core.Logging;
using DiscImageChef.Devices;
using Schemas;
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
namespace DiscImageChef.Core.Devices.Dumping
{
partial class Dump
{
/// <summary>Dumps inter-session lead-outs</summary>
/// <param name="blocks">Total number of positive sectors</param>
/// <param name="blockSize">Size of the read sector in bytes</param>
/// <param name="currentSpeed">Current read speed</param>
/// <param name="currentTry">Current dump hardware try</param>
/// <param name="extents">Extents</param>
/// <param name="ibgLog">IMGBurn log</param>
/// <param name="imageWriteDuration">Duration of image write</param>
/// <param name="leadOutExtents">Lead-out extents</param>
/// <param name="maxSpeed">Maximum speed</param>
/// <param name="mhddLog">MHDD log</param>
/// <param name="minSpeed">Minimum speed</param>
/// <param name="read6">Device supports READ(6)</param>
/// <param name="read10">Device supports READ(10)</param>
/// <param name="read12">Device supports READ(12)</param>
/// <param name="read16">Device supports READ(16)</param>
/// <param name="readcd">Device supports READ CD</param>
/// <param name="supportedSubchannel">Drive's maximum supported subchannel</param>
/// <param name="subSize">Subchannel size in bytes</param>
/// <param name="totalDuration">Total commands duration</param>
void DumpCdLeadOuts(ulong blocks, uint blockSize, ref double currentSpeed, DumpHardwareType currentTry,
ExtentsULong extents, IbgLog ibgLog, ref double imageWriteDuration,
ExtentsULong leadOutExtents, ref double maxSpeed, MhddLog mhddLog, ref double minSpeed,
bool read6, bool read10, bool read12, bool read16, bool readcd,
MmcSubchannel supportedSubchannel, uint subSize, ref double totalDuration)
{
byte[] cmdBuf = null; // Data buffer
const uint sectorSize = 2352; // Full sector size
bool sense = true; // Sense indicator
UpdateStatus?.Invoke("Reading lead-outs");
_dumpLog.WriteLine("Reading lead-outs");
InitProgress?.Invoke();
foreach((ulong item1, ulong item2) in leadOutExtents.ToArray())
for(ulong i = item1; i <= item2; i++)
{
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
_dumpLog.WriteLine("Aborted!");
break;
}
double cmdDuration = 0;
#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
// ReSharper disable CompareOfFloatsByEqualityOperator
if(currentSpeed > maxSpeed &&
currentSpeed != 0)
maxSpeed = currentSpeed;
if(currentSpeed < minSpeed &&
currentSpeed != 0)
minSpeed = currentSpeed;
// ReSharper restore CompareOfFloatsByEqualityOperator
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
PulseProgress?.Invoke($"Reading sector {i} at lead-out ({currentSpeed:F3} MiB/sec.)");
if(readcd)
{
sense = _dev.ReadCd(out cmdBuf, out _, (uint)i, blockSize, 1, MmcSectorTypes.AllTypes, false,
false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
supportedSubchannel, _dev.Timeout, out cmdDuration);
totalDuration += cmdDuration;
}
else if(read16)
sense = _dev.Read16(out cmdBuf, out _, 0, false, true, false, i, blockSize, 0, 1, false,
_dev.Timeout, out cmdDuration);
else if(read12)
sense = _dev.Read12(out cmdBuf, out _, 0, false, true, false, false, (uint)i, blockSize, 0, 1,
false, _dev.Timeout, out cmdDuration);
else if(read10)
sense = _dev.Read10(out cmdBuf, out _, 0, false, true, false, false, (uint)i, blockSize, 0, 1,
_dev.Timeout, out cmdDuration);
else if(read6)
sense = _dev.Read6(out cmdBuf, out _, (uint)i, blockSize, 1, _dev.Timeout, out cmdDuration);
if(!sense &&
!_dev.Error)
{
mhddLog.Write(i, cmdDuration);
ibgLog.Write(i, currentSpeed * 1024);
extents.Add(i, _maximumReadable, true);
leadOutExtents.Remove(i);
DateTime writeStart = DateTime.Now;
if(supportedSubchannel != MmcSubchannel.None)
{
byte[] data = new byte[sectorSize * _maximumReadable];
byte[] sub = new byte[subSize * _maximumReadable];
for(int b = 0; b < _maximumReadable; b++)
{
Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);
Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
}
_outputPlugin.WriteSectorsLong(data, i, _maximumReadable);
_outputPlugin.WriteSectorsTag(sub, i, _maximumReadable, SectorTagType.CdSectorSubchannel);
}
else
_outputPlugin.WriteSectors(cmdBuf, i, _maximumReadable);
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
}
else
{
// TODO: Reset device after X errors
if(_stopOnError)
return; // TODO: Return more cleanly
// Write empty data
DateTime writeStart = DateTime.Now;
if(supportedSubchannel != MmcSubchannel.None)
{
_outputPlugin.WriteSectorsLong(new byte[sectorSize * _skip], i, 1);
_outputPlugin.WriteSectorsTag(new byte[subSize * _skip], i, 1,
SectorTagType.CdSectorSubchannel);
}
else
_outputPlugin.WriteSectors(new byte[blockSize * _skip], i, 1);
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
ibgLog.Write(i, 0);
}
double newSpeed = ((double)blockSize * _maximumReadable) / 1048576 / (cmdDuration / 1000);
if(!double.IsInfinity(newSpeed))
currentSpeed = newSpeed;
_resume.NextBlock = i + 1;
}
EndProgress?.Invoke();
}
/// <summary>Retries inter-session lead-outs</summary>
/// <param name="blocks">Total number of positive sectors</param>
/// <param name="blockSize">Size of the read sector in bytes</param>
/// <param name="currentSpeed">Current read speed</param>
/// <param name="currentTry">Current dump hardware try</param>
/// <param name="extents">Extents</param>
/// <param name="ibgLog">IMGBurn log</param>
/// <param name="imageWriteDuration">Duration of image write</param>
/// <param name="leadOutExtents">Lead-out extents</param>
/// <param name="maxSpeed">Maximum speed</param>
/// <param name="mhddLog">MHDD log</param>
/// <param name="minSpeed">Minimum speed</param>
/// <param name="read6">Device supports READ(6)</param>
/// <param name="read10">Device supports READ(10)</param>
/// <param name="read12">Device supports READ(12)</param>
/// <param name="read16">Device supports READ(16)</param>
/// <param name="readcd">Device supports READ CD</param>
/// <param name="supportedSubchannel">Drive's maximum supported subchannel</param>
/// <param name="subSize">Subchannel size in bytes</param>
/// <param name="totalDuration">Total commands duration</param>
void RetryCdLeadOuts(ulong blocks, uint blockSize, ref double currentSpeed, DumpHardwareType currentTry,
ExtentsULong extents, IbgLog ibgLog, ref double imageWriteDuration,
ExtentsULong leadOutExtents, ref double maxSpeed, MhddLog mhddLog, ref double minSpeed,
bool read6, bool read10, bool read12, bool read16, bool readcd,
MmcSubchannel supportedSubchannel, uint subSize, ref double totalDuration)
{
byte[] cmdBuf = null; // Data buffer
const uint sectorSize = 2352; // Full sector size
bool sense = true; // Sense indicator
_dumpLog.WriteLine("Retrying lead-outs");
InitProgress?.Invoke();
foreach((ulong item1, ulong item2) in leadOutExtents.ToArray())
for(ulong i = item1; i <= item2; i++)
{
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
_dumpLog.WriteLine("Aborted!");
break;
}
double cmdDuration = 0;
#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
// ReSharper disable CompareOfFloatsByEqualityOperator
if(currentSpeed > maxSpeed &&
currentSpeed != 0)
maxSpeed = currentSpeed;
if(currentSpeed < minSpeed &&
currentSpeed != 0)
minSpeed = currentSpeed;
// ReSharper restore CompareOfFloatsByEqualityOperator
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
PulseProgress?.Invoke(string.Format("Reading sector {0} at lead-out ({1:F3} MiB/sec.)", i, blocks,
currentSpeed));
if(readcd)
{
sense = _dev.ReadCd(out cmdBuf, out _, (uint)i, blockSize, 1, MmcSectorTypes.AllTypes, false,
false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
supportedSubchannel, _dev.Timeout, out cmdDuration);
totalDuration += cmdDuration;
}
else if(read16)
sense = _dev.Read16(out cmdBuf, out _, 0, false, true, false, i, blockSize, 0, 1, false,
_dev.Timeout, out cmdDuration);
else if(read12)
sense = _dev.Read12(out cmdBuf, out _, 0, false, true, false, false, (uint)i, blockSize, 0, 1,
false, _dev.Timeout, out cmdDuration);
else if(read10)
sense = _dev.Read10(out cmdBuf, out _, 0, false, true, false, false, (uint)i, blockSize, 0, 1,
_dev.Timeout, out cmdDuration);
else if(read6)
sense = _dev.Read6(out cmdBuf, out _, (uint)i, blockSize, 1, _dev.Timeout, out cmdDuration);
if(!sense &&
!_dev.Error)
{
mhddLog.Write(i, cmdDuration);
ibgLog.Write(i, currentSpeed * 1024);
extents.Add(i, _maximumReadable, true);
leadOutExtents.Remove(i);
DateTime writeStart = DateTime.Now;
if(supportedSubchannel != MmcSubchannel.None)
{
byte[] data = new byte[sectorSize * _maximumReadable];
byte[] sub = new byte[subSize * _maximumReadable];
for(int b = 0; b < _maximumReadable; b++)
{
Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b,
sectorSize);
Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
}
_outputPlugin.WriteSectorsLong(data, i, _maximumReadable);
_outputPlugin.WriteSectorsTag(sub, i, _maximumReadable, SectorTagType.CdSectorSubchannel);
}
else
_outputPlugin.WriteSectors(cmdBuf, i, _maximumReadable);
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
}
else
{
// TODO: Reset device after X errors
if(_stopOnError)
return; // TODO: Return more cleanly
// Write empty data
DateTime writeStart = DateTime.Now;
if(supportedSubchannel != MmcSubchannel.None)
{
_outputPlugin.WriteSectorsLong(new byte[sectorSize * _skip], i, 1);
_outputPlugin.WriteSectorsTag(new byte[subSize * _skip], i, 1,
SectorTagType.CdSectorSubchannel);
}
else
_outputPlugin.WriteSectors(new byte[blockSize * _skip], i, 1);
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
ibgLog.Write(i, 0);
}
double newSpeed = ((double)blockSize * _maximumReadable) / 1048576 / (cmdDuration / 1000);
if(!double.IsInfinity(newSpeed))
currentSpeed = newSpeed;
}
EndProgress?.Invoke();
}
}
}

View File

@@ -0,0 +1,232 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CompactDisc.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ Description ] ----------------------------------------------------------
//
// Dumps CDs and DDCDs.
//
// --[ 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-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Devices;
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
namespace DiscImageChef.Core.Devices.Dumping
{
partial class Dump
{
// TODO: Fix offset
void ReadCdFirstTrackPregap(uint blockSize, ref double currentSpeed, Dictionary<MediaTagType, byte[]> mediaTags,
MmcSubchannel supportedSubchannel, ref double totalDuration)
{
bool sense; // Sense indicator
byte[] cmdBuf; // Data buffer
double cmdDuration; // Command execution time
DateTime timeSpeedStart; // Time of start for speed calculation
ulong sectorSpeedStart = 0; // Used to calculate correct speed
bool gotFirstTrackPregap = false;
int firstTrackPregapSectorsGood = 0;
var firstTrackPregapMs = new MemoryStream();
_dumpLog.WriteLine("Reading first track pregap");
UpdateStatus?.Invoke("Reading first track pregap");
InitProgress?.Invoke();
timeSpeedStart = DateTime.UtcNow;
for(int firstTrackPregapBlock = -150; firstTrackPregapBlock < 0 && _resume.NextBlock == 0;
firstTrackPregapBlock++)
{
if(_aborted)
{
_dumpLog.WriteLine("Aborted!");
UpdateStatus?.Invoke("Aborted!");
break;
}
PulseProgress?.
Invoke($"Trying to read first track pregap sector {firstTrackPregapBlock} ({currentSpeed:F3} MiB/sec.)");
sense = _dev.ReadCd(out cmdBuf, out _, (uint)firstTrackPregapBlock, blockSize, 1,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
if(!sense &&
!_dev.Error)
{
firstTrackPregapMs.Write(cmdBuf, 0, (int)blockSize);
gotFirstTrackPregap = true;
firstTrackPregapSectorsGood++;
totalDuration += cmdDuration;
}
else
{
// Write empty data
if(gotFirstTrackPregap)
firstTrackPregapMs.Write(new byte[blockSize], 0, (int)blockSize);
}
sectorSpeedStart++;
double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
if(elapsed < 1)
continue;
currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
sectorSpeedStart = 0;
timeSpeedStart = DateTime.UtcNow;
}
if(firstTrackPregapSectorsGood > 0)
mediaTags.Add(MediaTagType.CD_FirstTrackPregap, firstTrackPregapMs.ToArray());
EndProgress?.Invoke();
UpdateStatus?.Invoke($"Got {firstTrackPregapSectorsGood} first track pregap sectors.");
_dumpLog.WriteLine("Got {0} first track pregap sectors.", firstTrackPregapSectorsGood);
firstTrackPregapMs.Close();
}
void SolveTrackPregaps(Track[] tracks, bool supportsPqSubchannel, bool supportsRwSubchannel)
{
bool sense; // Sense indicator
byte[] cmdBuf; // Data buffer
if(!supportsPqSubchannel &&
!supportsRwSubchannel)
return;
for(int i = 1; i < tracks.Length; i++)
{
uint lba = (uint)tracks[i].TrackStartSector - 150;
int trackPregap = 0;
bool previousSense = false;
while(lba > tracks[i - 1].TrackStartSector)
{
if(supportsPqSubchannel)
sense = _dev.ReadCd(out cmdBuf, out _, lba, 16, 1, MmcSectorTypes.AllTypes, false, false, false,
MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Q16,
_dev.Timeout, out _);
else
{
sense = _dev.ReadCd(out cmdBuf, out _, lba, 16, 1, MmcSectorTypes.AllTypes, false, false, false,
MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Q16,
_dev.Timeout, out _);
sense = _dev.ReadCd(out cmdBuf, out _, lba, 96, 1, MmcSectorTypes.AllTypes, false, false, false,
MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Raw,
_dev.Timeout, out _);
if(!sense)
{
int[] q = new int[cmdBuf.Length / 8];
// De-interlace Q subchannel
for(int iq = 0; iq < cmdBuf.Length; iq += 8)
{
q[iq / 8] = (cmdBuf[iq] & 0x40) << 1;
q[iq / 8] += cmdBuf[iq + 1] & 0x40;
q[iq / 8] += (cmdBuf[iq + 2] & 0x40) >> 1;
q[iq / 8] += (cmdBuf[iq + 3] & 0x40) >> 2;
q[iq / 8] += (cmdBuf[iq + 4] & 0x40) >> 3;
q[iq / 8] += (cmdBuf[iq + 5] & 0x40) >> 4;
q[iq / 8] += (cmdBuf[iq + 6] & 0x40) >> 5;
q[iq / 8] += (cmdBuf[iq + 7] & 0x40) >> 6;
}
cmdBuf = new byte[q.Length];
for(int iq = 0; iq < cmdBuf.Length; iq++)
{
cmdBuf[iq] = (byte)q[iq];
}
cmdBuf[1] = (byte)(((cmdBuf[1] / 16) * 10) + (cmdBuf[1] & 0x0F));
cmdBuf[2] = (byte)(((cmdBuf[2] / 16) * 10) + (cmdBuf[2] & 0x0F));
cmdBuf[3] = (byte)(((cmdBuf[3] / 16) * 10) + (cmdBuf[3] & 0x0F));
cmdBuf[4] = (byte)(((cmdBuf[4] / 16) * 10) + (cmdBuf[4] & 0x0F));
cmdBuf[5] = (byte)(((cmdBuf[5] / 16) * 10) + (cmdBuf[5] & 0x0F));
cmdBuf[6] = (byte)(((cmdBuf[6] / 16) * 10) + (cmdBuf[6] & 0x0F));
cmdBuf[7] = (byte)(((cmdBuf[7] / 16) * 10) + (cmdBuf[7] & 0x0F));
cmdBuf[8] = (byte)(((cmdBuf[8] / 16) * 10) + (cmdBuf[8] & 0x0F));
cmdBuf[9] = (byte)(((cmdBuf[9] / 16) * 10) + (cmdBuf[9] & 0x0F));
}
}
if(!sense)
{
// Q position
if((cmdBuf[0] & 0xF) != 1)
continue;
if(cmdBuf[1] != tracks[i].TrackSequence ||
cmdBuf[2] != 0)
{
lba++;
trackPregap = (int)(tracks[i].TrackStartSector - lba);
if(previousSense)
break;
continue;
}
int pregapQ = (cmdBuf[3] * 60 * 75) + (cmdBuf[4] * 75) + cmdBuf[5];
if(pregapQ > trackPregap)
trackPregap = pregapQ;
else
break;
lba--;
}
else
{
previousSense = true;
lba--;
}
}
#if DEBUG
_dumpLog.WriteLine($"Track {tracks[i].TrackSequence} pregap is {trackPregap} sectors");
UpdateStatus?.Invoke($"Track {tracks[i].TrackSequence} pregap is {trackPregap} sectors");
#endif
tracks[i].TrackPregap = (ulong)trackPregap;
tracks[i].TrackStartSector -= tracks[i].TrackPregap;
}
}
}
}

View File

@@ -0,0 +1,87 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CompactDisc.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ Description ] ----------------------------------------------------------
//
// Dumps CDs and DDCDs.
//
// --[ 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-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Extents;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Metadata;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using DiscImageChef.Core.Logging;
using DiscImageChef.Core.Media.Detection;
using DiscImageChef.Decoders.CD;
using DiscImageChef.Decoders.SCSI;
using DiscImageChef.Decoders.SCSI.MMC;
using DiscImageChef.Devices;
using Schemas;
using CdOffset = DiscImageChef.Database.Models.CdOffset;
using MediaType = DiscImageChef.CommonTypes.MediaType;
using PlatformID = DiscImageChef.CommonTypes.Interop.PlatformID;
using Session = DiscImageChef.Decoders.CD.Session;
using TrackType = DiscImageChef.CommonTypes.Enums.TrackType;
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
namespace DiscImageChef.Core.Devices.Dumping
{
partial class Dump
{
bool SupportsRwSubchannel()
{
_dumpLog.WriteLine("Checking if drive supports full raw subchannel reading...");
UpdateStatus?.Invoke("Checking if drive supports full raw subchannel reading...");
return!_dev.ReadCd(out _, out _, 0, 2352 + 96, 1, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Raw,
_dev.Timeout, out _);
}
bool SupportsPqSubchannel()
{
_dumpLog.WriteLine("Checking if drive supports PQ subchannel reading...");
UpdateStatus?.Invoke("Checking if drive supports PQ subchannel reading...");
return!_dev.ReadCd(out _, out _, 0, 2352 + 16, 1, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16,
_dev.Timeout, out _);
}
}
}

View File

@@ -0,0 +1,147 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CompactDisc.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ Description ] ----------------------------------------------------------
//
// Dumps CDs and DDCDs.
//
// --[ 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-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.Decoders.CD;
using DiscImageChef.Decoders.SCSI.MMC;
using DiscImageChef.Devices;
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
namespace DiscImageChef.Core.Devices.Dumping
{
partial class Dump
{
/// <summary>Reads media tags from Compact Disc media</summary>
/// <param name="mediaType">Media type</param>
/// <param name="mediaTags">Media tags dictionary</param>
/// <param name="sessions">Sessions</param>
/// <param name="firstTrackLastSession">First track in last session</param>
void ReadCdTags(ref MediaType mediaType, Dictionary<MediaTagType, byte[]> mediaTags, out int sessions,
out int firstTrackLastSession)
{
byte[] cmdBuf; // Data buffer
bool sense; // Sense indicator
byte[] tmpBuf; // Temporary buffer
sessions = 1;
firstTrackLastSession = 1;
// ATIP exists on blank CDs
_dumpLog.WriteLine("Reading ATIP");
UpdateStatus?.Invoke("Reading ATIP");
sense = _dev.ReadAtip(out cmdBuf, out _, _dev.Timeout, out _);
if(!sense)
{
ATIP.CDATIP? atip = ATIP.Decode(cmdBuf);
if(atip.HasValue)
{
// Only CD-R and CD-RW have ATIP
mediaType = atip.Value.DiscType ? MediaType.CDRW : MediaType.CDR;
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
mediaTags.Add(MediaTagType.CD_ATIP, tmpBuf);
}
}
_dumpLog.WriteLine("Reading Disc Information");
UpdateStatus?.Invoke("Reading Disc Information");
sense = _dev.ReadDiscInformation(out cmdBuf, out _, MmcDiscInformationDataTypes.DiscInformation,
_dev.Timeout, out _);
if(!sense)
{
DiscInformation.StandardDiscInformation? discInfo = DiscInformation.Decode000b(cmdBuf);
if(discInfo.HasValue &&
mediaType == MediaType.CD)
switch(discInfo.Value.DiscType)
{
case 0x10:
mediaType = MediaType.CDI;
break;
case 0x20:
mediaType = MediaType.CDROMXA;
break;
}
}
_dumpLog.WriteLine("Reading PMA");
UpdateStatus?.Invoke("Reading PMA");
sense = _dev.ReadPma(out cmdBuf, out _, _dev.Timeout, out _);
if(!sense &&
PMA.Decode(cmdBuf).HasValue)
{
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
mediaTags.Add(MediaTagType.CD_PMA, tmpBuf);
}
_dumpLog.WriteLine("Reading Session Information");
UpdateStatus?.Invoke("Reading Session Information");
sense = _dev.ReadSessionInfo(out cmdBuf, out _, _dev.Timeout, out _);
if(!sense)
{
Session.CDSessionInfo? session = Session.Decode(cmdBuf);
if(session.HasValue)
{
sessions = session.Value.LastCompleteSession;
firstTrackLastSession = session.Value.TrackDescriptors[0].TrackNumber;
}
}
_dumpLog.WriteLine("Reading CD-Text from Lead-In");
UpdateStatus?.Invoke("Reading CD-Text from Lead-In");
sense = _dev.ReadCdText(out cmdBuf, out _, _dev.Timeout, out _);
if(sense || !CDTextOnLeadIn.Decode(cmdBuf).HasValue)
return;
tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
mediaTags.Add(MediaTagType.CD_TEXT, tmpBuf);
}
}
}

View File

@@ -0,0 +1,294 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CompactDisc.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ Description ] ----------------------------------------------------------
//
// Dumps CDs and DDCDs.
//
// --[ 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-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Decoders.CD;
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
namespace DiscImageChef.Core.Devices.Dumping
{
partial class Dump
{
/// <summary>Reads the TOC, processes it, returns the track list and last sector</summary>
/// <param name="blockSize">Size of the read sector in bytes</param>
/// <param name="dskType">Disc type</param>
/// <param name="lastSector">Last sector number</param>
/// <param name="leadOutStarts">Lead-out starts</param>
/// <param name="mediaTags">Media tags</param>
/// <param name="toc">Full CD TOC</param>
/// <param name="trackFlags">Track flags</param>
/// <param name="subType">Track subchannel type</param>
/// <returns>List of tracks</returns>
Track[] GetCdTracks(ref uint blockSize, MediaType dskType, out long lastSector,
Dictionary<int, long> leadOutStarts, Dictionary<MediaTagType, byte[]> mediaTags,
out FullTOC.CDFullTOC? toc, Dictionary<byte, byte> trackFlags, TrackSubchannelType subType)
{
byte[] cmdBuf = null; // Data buffer
const uint sectorSize = 2352; // Full sector size
bool sense = true; // Sense indicator
List<Track> trackList = new List<Track>(); // Tracks in disc
byte[] tmpBuf; // Temporary buffer
toc = null;
lastSector = 0;
TrackType leadoutTrackType = TrackType.Audio;
// We discarded all discs that falsify a TOC before requesting a real TOC
// No TOC, no CD (or an empty one)
_dumpLog.WriteLine("Reading full TOC");
UpdateStatus?.Invoke("Reading full TOC");
sense = _dev.ReadRawToc(out cmdBuf, out _, 0, _dev.Timeout, out _);
if(!sense)
{
toc = FullTOC.Decode(cmdBuf);
if(toc.HasValue)
{
tmpBuf = new byte[cmdBuf.Length - 2];
Array.Copy(cmdBuf, 2, tmpBuf, 0, cmdBuf.Length - 2);
mediaTags.Add(MediaTagType.CD_FullTOC, tmpBuf);
}
}
UpdateStatus?.Invoke("Building track map...");
_dumpLog.WriteLine("Building track map...");
if(toc.HasValue)
{
FullTOC.TrackDataDescriptor[] sortedTracks =
toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray();
foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4))
if(trk.POINT >= 0x01 &&
trk.POINT <= 0x63)
{
trackList.Add(new Track
{
TrackSequence = trk.POINT, TrackSession = trk.SessionNumber,
TrackType = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental
? TrackType.Data : TrackType.Audio,
TrackStartSector =
(ulong)(((trk.PHOUR * 3600 * 75) + (trk.PMIN * 60 * 75) + (trk.PSEC * 75) +
trk.PFRAME) - 150),
TrackBytesPerSector = (int)sectorSize,
TrackRawBytesPerSector = (int)sectorSize,
TrackSubchannelType = subType
});
trackFlags.Add(trk.POINT, trk.CONTROL);
}
else if(trk.POINT == 0xA2)
{
int phour, pmin, psec, pframe;
if(trk.PFRAME == 0)
{
pframe = 74;
if(trk.PSEC == 0)
{
psec = 59;
if(trk.PMIN == 0)
{
pmin = 59;
phour = trk.PHOUR - 1;
}
else
{
pmin = trk.PMIN - 1;
phour = trk.PHOUR;
}
}
else
{
psec = trk.PSEC - 1;
pmin = trk.PMIN;
phour = trk.PHOUR;
}
}
else
{
pframe = trk.PFRAME - 1;
psec = trk.PSEC;
pmin = trk.PMIN;
phour = trk.PHOUR;
}
lastSector = ((phour * 3600 * 75) + (pmin * 60 * 75) + (psec * 75) + pframe) - 150;
leadOutStarts.Add(trk.SessionNumber, lastSector + 1);
}
else if(trk.POINT == 0xA0 &&
trk.ADR == 1)
{
switch(trk.PSEC)
{
case 0x10:
dskType = MediaType.CDI;
break;
case 0x20:
if(dskType == MediaType.CD ||
dskType == MediaType.CDROM)
dskType = MediaType.CDROMXA;
break;
}
leadoutTrackType =
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data
: TrackType.Audio;
}
}
else
{
UpdateStatus?.Invoke("Cannot read RAW TOC, requesting processed one...");
_dumpLog.WriteLine("Cannot read RAW TOC, requesting processed one...");
sense = _dev.ReadToc(out cmdBuf, out _, false, 0, _dev.Timeout, out _);
TOC.CDTOC? oldToc = TOC.Decode(cmdBuf);
if((sense || !oldToc.HasValue) &&
!_force)
{
_dumpLog.WriteLine("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000...");
StoppingErrorMessage?.
Invoke("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000...");
return null;
}
foreach(TOC.CDTOCTrackDataDescriptor trk in oldToc.
Value.TrackDescriptors.OrderBy(t => t.TrackNumber).
Where(trk => trk.ADR == 1 || trk.ADR == 4))
if(trk.TrackNumber >= 0x01 &&
trk.TrackNumber <= 0x63)
{
trackList.Add(new Track
{
TrackSequence = trk.TrackNumber, TrackSession = 1,
TrackType = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental
? TrackType.Data : TrackType.Audio,
TrackStartSector = trk.TrackStartAddress, TrackBytesPerSector = (int)sectorSize,
TrackRawBytesPerSector = (int)sectorSize, TrackSubchannelType = subType
});
trackFlags.Add(trk.TrackNumber, trk.CONTROL);
}
else if(trk.TrackNumber == 0xAA)
{
leadoutTrackType =
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data
: TrackType.Audio;
lastSector = trk.TrackStartAddress - 1;
}
}
if(trackList.Count == 0)
{
UpdateStatus?.Invoke("No tracks found, adding a single track from 0 to Lead-Out");
_dumpLog.WriteLine("No tracks found, adding a single track from 0 to Lead-Out");
trackList.Add(new Track
{
TrackSequence = 1, TrackSession = 1, TrackType = leadoutTrackType,
TrackStartSector = 0,
TrackBytesPerSector = (int)sectorSize, TrackRawBytesPerSector = (int)sectorSize,
TrackSubchannelType = subType
});
trackFlags.Add(1, (byte)(leadoutTrackType == TrackType.Audio ? 0 : 4));
}
if(lastSector == 0)
{
sense = _dev.ReadCapacity16(out cmdBuf, out _, _dev.Timeout, out _);
if(!sense)
{
byte[] temp = new byte[8];
Array.Copy(cmdBuf, 0, temp, 0, 8);
Array.Reverse(temp);
lastSector = (long)BitConverter.ToUInt64(temp, 0);
blockSize = (uint)((cmdBuf[5] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]);
}
else
{
sense = _dev.ReadCapacity(out cmdBuf, out _, _dev.Timeout, out _);
if(!sense)
{
lastSector = (cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3];
blockSize = (uint)((cmdBuf[5] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]);
}
}
if(lastSector <= 0)
{
if(!_force)
{
StoppingErrorMessage?.
Invoke("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors...");
_dumpLog.
WriteLine("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors...");
return null;
}
UpdateStatus?.
Invoke("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before...");
_dumpLog.WriteLine("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before...");
lastSector = 360000;
}
}
return trackList.ToArray();
}
}
}

View File

@@ -0,0 +1,210 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CompactDisc.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ Description ] ----------------------------------------------------------
//
// Dumps CDs and DDCDs.
//
// --[ 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-2019 Natalia Portillo
// ****************************************************************************/
using System;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Extents;
using DiscImageChef.Devices;
using Schemas;
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
namespace DiscImageChef.Core.Devices.Dumping
{
partial class Dump
{
void TrimCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry,
ExtentsULong extents, bool newTrim, int offsetBytes, bool read6, bool read10, bool read12,
bool read16, bool readcd, int sectorsForOffset, uint subSize,
MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration)
{
DateTime start;
DateTime end;
bool sense = true; // Sense indicator
byte[] cmdBuf = null; // Data buffer
double cmdDuration = 0; // Command execution time
const uint sectorSize = 2352; // Full sector size
byte[] tmpBuf; // Temporary buffer
if(_resume.BadBlocks.Count <= 0 ||
_aborted ||
_notrim ||
!newTrim)
return;
start = DateTime.UtcNow;
UpdateStatus?.Invoke("Trimming bad sectors");
_dumpLog.WriteLine("Trimming bad sectors");
ulong[] tmpArray = _resume.BadBlocks.ToArray();
InitProgress?.Invoke();
foreach(ulong badSector in tmpArray)
{
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
UpdateStatus?.Invoke("Aborted!");
_dumpLog.WriteLine("Aborted!");
break;
}
PulseProgress?.Invoke($"Trimming sector {badSector}");
byte sectorsToTrim = 1;
uint badSectorToRead = (uint)badSector;
if(_fixOffset &&
audioExtents.Contains(badSector) &&
offsetBytes != 0)
{
if(offsetBytes > 0)
{
badSectorToRead -= (uint)sectorsForOffset;
}
sectorsToTrim = (byte)(sectorsForOffset + 1);
}
if(readcd)
sense = _dev.ReadCd(out cmdBuf, out _, badSectorToRead, blockSize, sectorsToTrim,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
else if(read16)
sense = _dev.Read16(out cmdBuf, out _, 0, false, true, false, badSectorToRead, blockSize, 0,
sectorsToTrim, false, _dev.Timeout, out cmdDuration);
else if(read12)
sense = _dev.Read12(out cmdBuf, out _, 0, false, true, false, false, badSectorToRead, blockSize, 0,
sectorsToTrim, false, _dev.Timeout, out cmdDuration);
else if(read10)
sense = _dev.Read10(out cmdBuf, out _, 0, false, true, false, false, badSectorToRead, blockSize, 0,
sectorsToTrim, _dev.Timeout, out cmdDuration);
else if(read6)
sense = _dev.Read6(out cmdBuf, out _, badSectorToRead, blockSize, sectorsToTrim, _dev.Timeout,
out cmdDuration);
totalDuration += cmdDuration;
if(sense || _dev.Error)
continue;
if(!sense &&
!_dev.Error)
{
_resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
}
// Because one block has been partially used to fix the offset
if(_fixOffset &&
audioExtents.Contains(badSector) &&
offsetBytes != 0)
{
int offsetFix = offsetBytes < 0 ? (int)(sectorSize - (offsetBytes * -1)) : offsetBytes;
if(supportedSubchannel != MmcSubchannel.None)
{
// De-interleave subchannel
byte[] data = new byte[sectorSize * sectorsToTrim];
byte[] sub = new byte[subSize * sectorsToTrim];
for(int b = 0; b < sectorsToTrim; b++)
{
Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);
Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
}
tmpBuf = new byte[sectorSize * (sectorsToTrim - sectorsForOffset)];
Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length);
data = tmpBuf;
// Re-interleave subchannel
cmdBuf = new byte[blockSize * sectorsToTrim];
for(int b = 0; b < sectorsToTrim; b++)
{
Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize);
Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize);
}
}
else
{
tmpBuf = new byte[blockSize * (sectorsToTrim - sectorsForOffset)];
Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length);
cmdBuf = tmpBuf;
}
}
if(supportedSubchannel != MmcSubchannel.None)
{
byte[] data = new byte[sectorSize];
byte[] sub = new byte[subSize];
Array.Copy(cmdBuf, 0, data, 0, sectorSize);
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
_outputPlugin.WriteSectorLong(data, badSector);
_outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel);
}
else
{
if(supportsLongSectors)
{
_outputPlugin.WriteSectorLong(cmdBuf, badSector);
}
else
{
if(cmdBuf.Length % sectorSize == 0)
{
byte[] data = new byte[2048];
Array.Copy(cmdBuf, 16, data, 0, 2048);
_outputPlugin.WriteSector(data, badSector);
}
else
{
_outputPlugin.WriteSectorLong(cmdBuf, badSector);
}
}
}
}
EndProgress?.Invoke();
end = DateTime.UtcNow;
UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds.");
_dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds);
}
}
}

View File

@@ -1,45 +1,59 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Metadata;
using DiscImageChef.Core.Logging;
using DiscImageChef.Database;
using DiscImageChef.Devices;
using Schemas;
namespace DiscImageChef.Core.Devices.Dumping
{
public enum DumpSubchannel
{
Any, Rw, RwOrPq,
Pq, None
}
public partial class Dump
{
readonly Device dev;
readonly string devicePath;
readonly bool doResume;
readonly DumpLog dumpLog;
readonly bool dumpRaw;
readonly Encoding encoding;
readonly bool force;
readonly Dictionary<string, string> formatOptions;
readonly bool nometadata;
readonly bool notrim;
readonly string outputPath;
readonly IWritableImage outputPlugin;
readonly string outputPrefix;
readonly bool persistent;
readonly CICMMetadataType preSidecar;
readonly ushort retryPasses;
readonly bool stopOnError;
bool aborted;
bool dumpFirstTrackPregap;
Resume resume;
Sidecar sidecarClass;
uint skip;
readonly bool _debug;
readonly Device _dev;
readonly string _devicePath;
readonly bool _doResume;
readonly DumpLog _dumpLog;
readonly bool _dumpRaw;
readonly Encoding _encoding;
readonly bool _force;
readonly Dictionary<string, string> _formatOptions;
readonly bool _nometadata;
readonly bool _notrim;
readonly string _outputPath;
readonly IWritableImage _outputPlugin;
readonly string _outputPrefix;
readonly bool _persistent;
readonly CICMMetadataType _preSidecar;
readonly ushort _retryPasses;
readonly bool _stopOnError;
readonly DumpSubchannel _subchannel;
bool _aborted;
DicContext _ctx; // Master database context
Database.Models.Device _dbDev; // Device database entry
bool _dumpFirstTrackPregap;
bool _fixOffset;
uint _maximumReadable; // Maximum number of sectors drive can read at once
Resume _resume;
Sidecar _sidecarClass;
uint _skip;
int _speed;
int _speedMultiplier;
/// <summary>
/// Initializes dumpers
/// </summary>
/// <summary>Initializes dumpers</summary>
/// <param name="doResume">Should resume?</param>
/// <param name="dev">Device</param>
/// <param name="devicePath">Path to the device</param>
@@ -60,130 +74,143 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="preSidecar">Sidecar to store in dumped image</param>
/// <param name="skip">How many sectors to skip reading on error</param>
/// <param name="nometadata">Create metadata sidecar after dump?</param>
public Dump(bool doResume, Device dev, string devicePath,
IWritableImage outputPlugin, ushort retryPasses,
bool force, bool dumpRaw, bool persistent,
bool stopOnError, Resume resume, DumpLog dumpLog,
Encoding encoding, string outputPrefix, string outputPath,
Dictionary<string, string> formatOptions,
CICMMetadataType preSidecar, uint skip, bool nometadata,
bool notrim, bool dumpFirstTrackPregap)
public Dump(bool doResume, Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses,
bool force, bool dumpRaw, bool persistent, bool stopOnError, Resume resume, DumpLog dumpLog,
Encoding encoding, string outputPrefix, string outputPath, Dictionary<string, string> formatOptions,
CICMMetadataType preSidecar, uint skip, bool nometadata, bool notrim, bool dumpFirstTrackPregap,
bool fixOffset, bool debug, DumpSubchannel subchannel, int speed)
{
this.doResume = doResume;
this.dev = dev;
this.devicePath = devicePath;
this.outputPlugin = outputPlugin;
this.retryPasses = retryPasses;
this.force = force;
this.dumpRaw = dumpRaw;
this.persistent = persistent;
this.stopOnError = stopOnError;
this.resume = resume;
this.dumpLog = dumpLog;
this.encoding = encoding;
this.outputPrefix = outputPrefix;
this.outputPath = outputPath;
this.formatOptions = formatOptions;
this.preSidecar = preSidecar;
this.skip = skip;
this.nometadata = nometadata;
this.notrim = notrim;
this.dumpFirstTrackPregap = dumpFirstTrackPregap;
aborted = false;
_doResume = doResume;
_dev = dev;
_devicePath = devicePath;
_outputPlugin = outputPlugin;
_retryPasses = retryPasses;
_force = force;
_dumpRaw = dumpRaw;
_persistent = persistent;
_stopOnError = stopOnError;
_resume = resume;
_dumpLog = dumpLog;
_encoding = encoding;
_outputPrefix = outputPrefix;
_outputPath = outputPath;
_formatOptions = formatOptions;
_preSidecar = preSidecar;
_skip = skip;
_nometadata = nometadata;
_notrim = notrim;
_dumpFirstTrackPregap = dumpFirstTrackPregap;
_aborted = false;
_fixOffset = fixOffset;
_debug = debug;
_maximumReadable = 64;
_subchannel = subchannel;
_speedMultiplier = -1;
_speed = speed;
}
/// <summary>
/// Starts dumping with the stablished fields and autodetecting the device type
/// </summary>
/// <summary>Starts dumping with the stablished fields and autodetecting the device type</summary>
public void Start()
{
if(dev.IsUsb && dev.UsbVendorId == 0x054C &&
(dev.UsbProductId == 0x01C8 || dev.UsbProductId == 0x01C9 || dev.UsbProductId == 0x02D2))
// Open master database
_ctx = DicContext.Create(Settings.Settings.MasterDbPath);
// Search for device in master database
_dbDev = _ctx.Devices.FirstOrDefault(d => d.Manufacturer == _dev.Manufacturer && d.Model == _dev.Model &&
d.Revision == _dev.Revision);
if(_dbDev is null)
{
_dumpLog.WriteLine("Device not in database, please create a device report and attach it to a Github issue.");
UpdateStatus?.
Invoke("Device not in database, please create a device report and attach it to a Github issue.");
}
else
{
_dumpLog.WriteLine($"Device in database since {_dbDev.LastSynchronized}.");
UpdateStatus?.Invoke($"Device in database since {_dbDev.LastSynchronized}.");
if(_dbDev.OptimalMultipleSectorsRead > 0)
_maximumReadable = (uint)_dbDev.OptimalMultipleSectorsRead;
}
if(_dev.IsUsb &&
_dev.UsbVendorId == 0x054C &&
(_dev.UsbProductId == 0x01C8 || _dev.UsbProductId == 0x01C9 || _dev.UsbProductId == 0x02D2))
PlayStationPortable();
else
switch(dev.Type)
switch(_dev.Type)
{
case DeviceType.ATA:
Ata();
break;
case DeviceType.MMC:
case DeviceType.SecureDigital:
SecureDigital();
break;
case DeviceType.NVMe:
NVMe();
break;
case DeviceType.ATAPI:
case DeviceType.SCSI:
Scsi();
break;
default:
dumpLog.WriteLine("Unknown device type.");
dumpLog.Close();
_dumpLog.WriteLine("Unknown device type.");
_dumpLog.Close();
StoppingErrorMessage?.Invoke("Unknown device type.");
return;
}
dumpLog.Close();
_dumpLog.Close();
if(resume == null || !doResume) return;
if(_resume == null ||
!_doResume)
return;
resume.LastWriteDate = DateTime.UtcNow;
resume.BadBlocks.Sort();
_resume.LastWriteDate = DateTime.UtcNow;
_resume.BadBlocks.Sort();
if(File.Exists(outputPrefix + ".resume.xml")) File.Delete(outputPrefix + ".resume.xml");
if(File.Exists(_outputPrefix + ".resume.xml"))
File.Delete(_outputPrefix + ".resume.xml");
FileStream fs = new FileStream(outputPrefix + ".resume.xml", FileMode.Create, FileAccess.ReadWrite);
XmlSerializer xs = new XmlSerializer(resume.GetType());
xs.Serialize(fs, resume);
var fs = new FileStream(_outputPrefix + ".resume.xml", FileMode.Create, FileAccess.ReadWrite);
var xs = new XmlSerializer(_resume.GetType());
xs.Serialize(fs, _resume);
fs.Close();
}
public void Abort()
{
aborted = true;
sidecarClass?.Abort();
_aborted = true;
_sidecarClass?.Abort();
}
/// <summary>
/// Event raised when the progress bar is not longer needed
/// </summary>
/// <summary>Event raised when the progress bar is not longer needed</summary>
public event EndProgressHandler EndProgress;
/// <summary>
/// Event raised when a progress bar is needed
/// </summary>
/// <summary>Event raised when a progress bar is needed</summary>
public event InitProgressHandler InitProgress;
/// <summary>
/// Event raised to report status updates
/// </summary>
/// <summary>Event raised to report status updates</summary>
public event UpdateStatusHandler UpdateStatus;
/// <summary>
/// Event raised to report a non-fatal error
/// </summary>
/// <summary>Event raised to report a non-fatal error</summary>
public event ErrorMessageHandler ErrorMessage;
/// <summary>
/// Event raised to report a fatal error that stops the dumping operation and should call user's attention
/// </summary>
/// <summary>Event raised to report a fatal error that stops the dumping operation and should call user's attention</summary>
public event ErrorMessageHandler StoppingErrorMessage;
/// <summary>
/// Event raised to update the values of a determinate progress bar
/// </summary>
/// <summary>Event raised to update the values of a determinate progress bar</summary>
public event UpdateProgressHandler UpdateProgress;
/// <summary>
/// Event raised to update the status of an undeterminate progress bar
/// </summary>
/// <summary>Event raised to update the status of an undeterminate progress bar</summary>
public event PulseProgressHandler PulseProgress;
/// <summary>
/// Event raised when the progress bar is not longer needed
/// </summary>
/// <summary>Event raised when the progress bar is not longer needed</summary>
public event EndProgressHandler2 EndProgress2;
/// <summary>
/// Event raised when a progress bar is needed
/// </summary>
/// <summary>Event raised when a progress bar is needed</summary>
public event InitProgressHandler2 InitProgress2;
/// <summary>
/// Event raised to update the values of a determinate progress bar
/// </summary>
/// <summary>Event raised to update the values of a determinate progress bar</summary>
public event UpdateProgressHandler2 UpdateProgress2;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,138 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : SBC.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ Description ] ----------------------------------------------------------
//
// Dumps SCSI Block devices.
//
// --[ 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-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Metadata;
using Schemas;
using MediaType = DiscImageChef.CommonTypes.MediaType;
namespace DiscImageChef.Core.Devices.Dumping
{
partial class Dump
{
/// <summary>Creates optical metadata sidecar</summary>
/// <param name="blockSize">Size of the read sector in bytes</param>
/// <param name="blocks">Total number of positive sectors</param>
/// <param name="mediaType">Disc type</param>
/// <param name="layers">Disc layers</param>
/// <param name="mediaTags">Media tags</param>
/// <param name="sessions">Disc sessions</param>
/// <param name="totalChkDuration">Total time spent doing checksums</param>
void WriteOpticalSidecar(uint blockSize, ulong blocks, MediaType mediaType, LayersType layers,
Dictionary<MediaTagType, byte[]> mediaTags, int sessions, out double totalChkDuration)
{
_dumpLog.WriteLine("Creating sidecar.");
var filters = new FiltersList();
IFilter filter = filters.GetFilter(_outputPath);
IMediaImage inputPlugin = ImageFormat.Detect(filter);
totalChkDuration = 0;
if(!inputPlugin.Open(filter))
{
StoppingErrorMessage?.Invoke("Could not open created image.");
return;
}
DateTime chkStart = DateTime.UtcNow;
// ReSharper disable once UseObjectOrCollectionInitializer
_sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding);
_sidecarClass.InitProgressEvent += InitProgress;
_sidecarClass.UpdateProgressEvent += UpdateProgress;
_sidecarClass.EndProgressEvent += EndProgress;
_sidecarClass.InitProgressEvent2 += InitProgress2;
_sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
_sidecarClass.EndProgressEvent2 += EndProgress2;
_sidecarClass.UpdateStatusEvent += UpdateStatus;
CICMMetadataType sidecar = _sidecarClass.Create();
DateTime end = DateTime.UtcNow;
totalChkDuration = (end - chkStart).TotalMilliseconds;
_dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);
_dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000));
if(_preSidecar != null)
{
_preSidecar.OpticalDisc = sidecar.OpticalDisc;
sidecar = _preSidecar;
}
List<(ulong start, string type)> filesystems = new List<(ulong start, string type)>();
if(sidecar.OpticalDisc[0].Track != null)
filesystems.AddRange(from xmlTrack in sidecar.OpticalDisc[0].Track
where xmlTrack.FileSystemInformation != null
from partition in xmlTrack.FileSystemInformation
where partition.FileSystems != null from fileSystem in partition.FileSystems
select (partition.StartSector, fileSystem.Type));
if(filesystems.Count > 0)
foreach(var filesystem in filesystems.Select(o => new
{
o.start, o.type
}).Distinct())
_dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start);
sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(mediaType);
(string type, string subType) discType = CommonTypes.Metadata.MediaType.MediaTypeToString(mediaType);
sidecar.OpticalDisc[0].DiscType = discType.type;
sidecar.OpticalDisc[0].DiscSubType = discType.subType;
sidecar.OpticalDisc[0].DumpHardwareArray = _resume.Tries.ToArray();
sidecar.OpticalDisc[0].Sessions = (uint)sessions;
sidecar.OpticalDisc[0].Layers = layers;
if(mediaTags != null)
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags.Where(tag => _outputPlugin.
SupportedMediaTags.
Contains(tag.Key)))
AddMediaTagToSidecar(_outputPath, tag, ref sidecar);
UpdateStatus?.Invoke("Writing metadata sidecar");
var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);
var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
xmlSer.Serialize(xmlFs, sidecar);
xmlFs.Close();
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -37,59 +37,66 @@ using DiscImageChef.Decoders.SCSI;
namespace DiscImageChef.Core.Devices.Dumping
{
/// <summary>
/// Implements dumping SCSI and ATAPI devices
/// </summary>
/// <summary>Implements dumping SCSI and ATAPI devices</summary>
public partial class Dump
{
// TODO: Get cartridge serial number from Certance vendor EVPD
/// <summary>
/// Dumps a SCSI Block Commands device or a Reduced Block Commands devices
/// </summary>
/// <summary>Dumps a SCSI Block Commands device or a Reduced Block Commands devices</summary>
public void Scsi()
{
MediaType dskType = MediaType.Unknown;
int resets = 0;
if(dev.IsRemovable)
if(_dev.IsRemovable)
{
InitProgress?.Invoke();
deviceGotReset:
bool sense = dev.ScsiTestUnitReady(out byte[] senseBuf, dev.Timeout, out _);
bool sense = _dev.ScsiTestUnitReady(out byte[] senseBuf, _dev.Timeout, out _);
if(sense)
{
FixedSense? decSense = Sense.DecodeFixed(senseBuf);
if(decSense.HasValue)
{
ErrorMessage
?.Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");
dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h",
decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
ErrorMessage?.
Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");
_dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h",
decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
// Just retry, for 5 times
if(decSense.Value.ASC == 0x29)
{
resets++;
if(resets < 5) goto deviceGotReset;
if(resets < 5)
goto deviceGotReset;
}
if(decSense.Value.ASC == 0x3A)
{
int leftRetries = 5;
while(leftRetries > 0)
{
PulseProgress?.Invoke("Waiting for drive to become ready");
Thread.Sleep(2000);
sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
if(!sense) break;
sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _);
if(!sense)
break;
decSense = Sense.DecodeFixed(senseBuf);
if(decSense.HasValue)
{
ErrorMessage
?.Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");
dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h",
decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
ErrorMessage?.
Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");
_dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h",
decSense.Value.SenseKey, decSense.Value.ASC,
decSense.Value.ASCQ);
}
leftRetries--;
@@ -98,26 +105,34 @@ namespace DiscImageChef.Core.Devices.Dumping
if(sense)
{
StoppingErrorMessage?.Invoke("Please insert media in drive");
return;
}
}
else if(decSense.Value.ASC == 0x04 && decSense.Value.ASCQ == 0x01)
else if(decSense.Value.ASC == 0x04 &&
decSense.Value.ASCQ == 0x01)
{
int leftRetries = 50;
while(leftRetries > 0)
{
PulseProgress?.Invoke("Waiting for drive to become ready");
Thread.Sleep(2000);
sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
if(!sense) break;
sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _);
if(!sense)
break;
decSense = Sense.DecodeFixed(senseBuf);
if(decSense.HasValue)
{
ErrorMessage
?.Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");
dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
ErrorMessage?.
Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");
_dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
decSense.Value.SenseKey, decSense.Value.ASC,
decSense.Value.ASCQ);
}
leftRetries--;
@@ -125,8 +140,9 @@ namespace DiscImageChef.Core.Devices.Dumping
if(sense)
{
StoppingErrorMessage
?.Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");
StoppingErrorMessage?.
Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");
return;
}
}
@@ -147,20 +163,26 @@ namespace DiscImageChef.Core.Devices.Dumping
else if(decSense.Value.ASC == 0x28)
{
int leftRetries = 10;
while(leftRetries > 0)
{
PulseProgress?.Invoke("Waiting for drive to become ready");
Thread.Sleep(2000);
sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
if(!sense) break;
sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _);
if(!sense)
break;
decSense = Sense.DecodeFixed(senseBuf);
if(decSense.HasValue)
{
ErrorMessage
?.Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");
dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
ErrorMessage?.
Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");
_dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
decSense.Value.SenseKey, decSense.Value.ASC,
decSense.Value.ASCQ);
}
leftRetries--;
@@ -168,21 +190,24 @@ namespace DiscImageChef.Core.Devices.Dumping
if(sense)
{
StoppingErrorMessage
?.Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");
StoppingErrorMessage?.
Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");
return;
}
}
else
{
StoppingErrorMessage
?.Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");
StoppingErrorMessage?.
Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");
return;
}
}
else
{
StoppingErrorMessage?.Invoke("Unknown testing unit was ready.");
return;
}
}
@@ -190,28 +215,34 @@ namespace DiscImageChef.Core.Devices.Dumping
EndProgress?.Invoke();
}
switch(dev.ScsiType)
switch(_dev.ScsiType)
{
case PeripheralDeviceTypes.SequentialAccess:
if(dumpRaw)
if(_dumpRaw)
{
StoppingErrorMessage?.Invoke("Tapes cannot be dumped raw.");
return;
}
if(outputPlugin is IWritableTapeImage) Ssc();
if(_outputPlugin is IWritableTapeImage)
Ssc();
else
StoppingErrorMessage
?.Invoke("The specified plugin does not support storing streaming tape images.");
StoppingErrorMessage?.
Invoke("The specified plugin does not support storing streaming tape images.");
return;
case PeripheralDeviceTypes.MultiMediaDevice:
if(outputPlugin is IWritableOpticalImage) Mmc(ref dskType);
if(_outputPlugin is IWritableOpticalImage)
Mmc(ref dskType);
else
StoppingErrorMessage
?.Invoke("The specified plugin does not support storing optical disc images.");
StoppingErrorMessage?.
Invoke("The specified plugin does not support storing optical disc images.");
return;
default:
Sbc(null, ref dskType, false);
break;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -53,9 +53,9 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <summary>Dumps a MultiMediaCard or SecureDigital flash card</summary>
public void SecureDigital()
{
if(dumpRaw)
if(_dumpRaw)
{
if(force)
if(_force)
ErrorMessage?.
Invoke("Raw dumping is not supported in MultiMediaCard or SecureDigital devices. Continuing...");
else
@@ -84,13 +84,13 @@ namespace DiscImageChef.Core.Devices.Dumping
Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>();
switch(dev.Type)
switch(_dev.Type)
{
case DeviceType.MMC:
{
UpdateStatus?.Invoke("Reading Extended CSD");
dumpLog.WriteLine("Reading Extended CSD");
sense = dev.ReadExtendedCsd(out ecsd, out _, TIMEOUT, out duration);
_dumpLog.WriteLine("Reading Extended CSD");
sense = _dev.ReadExtendedCsd(out ecsd, out _, TIMEOUT, out duration);
if(!sense)
{
@@ -112,8 +112,8 @@ namespace DiscImageChef.Core.Devices.Dumping
ecsd = null;
UpdateStatus?.Invoke("Reading CSD");
dumpLog.WriteLine("Reading CSD");
sense = dev.ReadCsd(out csd, out _, TIMEOUT, out duration);
_dumpLog.WriteLine("Reading CSD");
sense = _dev.ReadCsd(out csd, out _, TIMEOUT, out duration);
if(!sense)
{
@@ -130,8 +130,8 @@ namespace DiscImageChef.Core.Devices.Dumping
csd = null;
UpdateStatus?.Invoke("Reading OCR");
dumpLog.WriteLine("Reading OCR");
sense = dev.ReadOcr(out ocr, out _, TIMEOUT, out duration);
_dumpLog.WriteLine("Reading OCR");
sense = _dev.ReadOcr(out ocr, out _, TIMEOUT, out duration);
if(sense)
ocr = null;
@@ -144,8 +144,8 @@ namespace DiscImageChef.Core.Devices.Dumping
case DeviceType.SecureDigital:
{
UpdateStatus?.Invoke("Reading CSD");
dumpLog.WriteLine("Reading CSD");
sense = dev.ReadCsd(out csd, out _, TIMEOUT, out duration);
_dumpLog.WriteLine("Reading CSD");
sense = _dev.ReadCsd(out csd, out _, TIMEOUT, out duration);
if(!sense)
{
@@ -165,8 +165,8 @@ namespace DiscImageChef.Core.Devices.Dumping
csd = null;
UpdateStatus?.Invoke("Reading OCR");
dumpLog.WriteLine("Reading OCR");
sense = dev.ReadSdocr(out ocr, out _, TIMEOUT, out duration);
_dumpLog.WriteLine("Reading OCR");
sense = _dev.ReadSdocr(out ocr, out _, TIMEOUT, out duration);
if(sense)
ocr = null;
@@ -174,8 +174,8 @@ namespace DiscImageChef.Core.Devices.Dumping
mediaTags.Add(MediaTagType.SD_OCR, null);
UpdateStatus?.Invoke("Reading SCR");
dumpLog.WriteLine("Reading SCR");
sense = dev.ReadScr(out scr, out _, TIMEOUT, out duration);
_dumpLog.WriteLine("Reading SCR");
sense = _dev.ReadScr(out scr, out _, TIMEOUT, out duration);
if(sense)
scr = null;
@@ -187,13 +187,13 @@ namespace DiscImageChef.Core.Devices.Dumping
}
UpdateStatus?.Invoke("Reading CID");
dumpLog.WriteLine("Reading CID");
sense = dev.ReadCid(out byte[] cid, out _, TIMEOUT, out duration);
_dumpLog.WriteLine("Reading CID");
sense = _dev.ReadCid(out byte[] cid, out _, TIMEOUT, out duration);
if(sense)
cid = null;
else
mediaTags.Add(dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID : MediaTagType.MMC_CID, null);
mediaTags.Add(_dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID : MediaTagType.MMC_CID, null);
DateTime start;
DateTime end;
@@ -204,21 +204,21 @@ namespace DiscImageChef.Core.Devices.Dumping
if(blocks == 0)
{
dumpLog.WriteLine("Unable to get device size.");
_dumpLog.WriteLine("Unable to get device size.");
StoppingErrorMessage?.Invoke("Unable to get device size.");
return;
}
UpdateStatus?.Invoke($"Device reports {blocks} blocks.");
dumpLog.WriteLine("Device reports {0} blocks.", blocks);
_dumpLog.WriteLine("Device reports {0} blocks.", blocks);
byte[] cmdBuf;
bool error;
while(true)
{
error = dev.Read(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, TIMEOUT, out duration);
error = _dev.Read(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, TIMEOUT, out duration);
if(error)
blocksToRead /= 2;
@@ -230,23 +230,23 @@ namespace DiscImageChef.Core.Devices.Dumping
if(error)
{
dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", dev.LastError);
StoppingErrorMessage?.Invoke($"Device error {dev.LastError} trying to guess ideal transfer length.");
_dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", _dev.LastError);
StoppingErrorMessage?.Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length.");
return;
}
UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time.");
dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
_dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
if(skip < blocksToRead)
skip = blocksToRead;
if(_skip < blocksToRead)
_skip = blocksToRead;
DumpHardwareType currentTry = null;
ExtentsULong extents = null;
ResumeSupport.Process(true, false, blocks, dev.Manufacturer, dev.Model, dev.Serial, dev.PlatformId,
ref resume, ref currentTry, ref extents);
ResumeSupport.Process(true, false, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId,
ref _resume, ref currentTry, ref extents);
if(currentTry == null ||
extents == null)
@@ -260,53 +260,53 @@ namespace DiscImageChef.Core.Devices.Dumping
foreach(MediaTagType tag in mediaTags.Keys)
{
if(outputPlugin.SupportedMediaTags.Contains(tag))
if(_outputPlugin.SupportedMediaTags.Contains(tag))
continue;
ret = false;
dumpLog.WriteLine($"Output format does not support {tag}.");
_dumpLog.WriteLine($"Output format does not support {tag}.");
ErrorMessage?.Invoke($"Output format does not support {tag}.");
}
if(!ret)
{
if(force)
if(_force)
{
dumpLog.WriteLine("Several media tags not supported, continuing...");
_dumpLog.WriteLine("Several media tags not supported, continuing...");
ErrorMessage?.Invoke("Several media tags not supported, continuing...");
}
else
{
dumpLog.WriteLine("Several media tags not supported, not continuing...");
_dumpLog.WriteLine("Several media tags not supported, not continuing...");
StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing...");
return;
}
}
var mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
var ibgLog = new IbgLog(outputPrefix + ".ibg", SD_PROFILE);
var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead);
var ibgLog = new IbgLog(_outputPrefix + ".ibg", SD_PROFILE);
ret = outputPlugin.Create(outputPath,
dev.Type == DeviceType.SecureDigital ? MediaType.SecureDigital : MediaType.MMC,
formatOptions, blocks, blockSize);
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);
_dumpLog.WriteLine("Error creating output image, not continuing.");
_dumpLog.WriteLine(_outputPlugin.ErrorMessage);
StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine +
outputPlugin.ErrorMessage);
_outputPlugin.ErrorMessage);
return;
}
if(resume.NextBlock > 0)
if(_resume.NextBlock > 0)
{
UpdateStatus?.Invoke($"Resuming from block {resume.NextBlock}.");
dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock);
UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}.");
_dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
}
start = DateTime.UtcNow;
@@ -317,13 +317,13 @@ namespace DiscImageChef.Core.Devices.Dumping
InitProgress?.Invoke();
for(ulong i = resume.NextBlock; i < blocks; i += blocksToRead)
for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
{
if(aborted)
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
UpdateStatus?.Invoke("Aborted!");
dumpLog.WriteLine("Aborted!");
_dumpLog.WriteLine("Aborted!");
break;
}
@@ -344,46 +344,46 @@ namespace DiscImageChef.Core.Devices.Dumping
UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i,
(long)blocks);
error = dev.Read(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed, TIMEOUT,
out duration);
error = _dev.Read(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed, TIMEOUT,
out duration);
if(!error)
{
mhddLog.Write(i, duration);
ibgLog.Write(i, currentSpeed * 1024);
DateTime writeStart = DateTime.Now;
outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
_outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
extents.Add(i, blocksToRead, true);
}
else
{
if(i + skip > blocks)
skip = (uint)(blocks - i);
if(i + _skip > blocks)
_skip = (uint)(blocks - i);
for(ulong b = i; b < i + skip; b++)
resume.BadBlocks.Add(b);
for(ulong b = i; b < i + _skip; b++)
_resume.BadBlocks.Add(b);
mhddLog.Write(i, duration < 500 ? 65535 : duration);
ibgLog.Write(i, 0);
DateTime writeStart = DateTime.Now;
outputPlugin.WriteSectors(new byte[blockSize * skip], i, skip);
_outputPlugin.WriteSectors(new byte[blockSize * _skip], i, _skip);
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", skip, i);
i += skip - blocksToRead;
_dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
i += _skip - blocksToRead;
newTrim = true;
}
sectorSpeedStart += blocksToRead;
resume.NextBlock = i + blocksToRead;
sectorSpeedStart += blocksToRead;
_resume.NextBlock = i + blocksToRead;
double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
if(elapsed < 1)
continue;
currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed);
currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
sectorSpeedStart = 0;
timeSpeedStart = DateTime.UtcNow;
}
@@ -392,76 +392,76 @@ namespace DiscImageChef.Core.Devices.Dumping
EndProgress?.Invoke();
mhddLog.Close();
ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000),
devicePath);
ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
(blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000),
_devicePath);
UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");
UpdateStatus?.
Invoke($"Average dump speed {(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec.");
Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");
UpdateStatus?.
Invoke($"Average write speed {(double)blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec.");
Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec.");
dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
_dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
_dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000));
dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration);
_dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration);
#region Trimming
if(resume.BadBlocks.Count > 0 &&
!aborted &&
!notrim &&
if(_resume.BadBlocks.Count > 0 &&
!_aborted &&
!_notrim &&
newTrim)
{
start = DateTime.UtcNow;
UpdateStatus?.Invoke("Trimming bad sectors");
dumpLog.WriteLine("Trimming bad sectors");
_dumpLog.WriteLine("Trimming bad sectors");
ulong[] tmpArray = resume.BadBlocks.ToArray();
ulong[] tmpArray = _resume.BadBlocks.ToArray();
InitProgress?.Invoke();
foreach(ulong badSector in tmpArray)
{
if(aborted)
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
UpdateStatus?.Invoke("Aborted!");
dumpLog.WriteLine("Aborted!");
_dumpLog.WriteLine("Aborted!");
break;
}
PulseProgress?.Invoke($"Trimming sector {badSector}");
error = dev.Read(out cmdBuf, out _, (uint)badSector, blockSize, 1, byteAddressed, TIMEOUT,
out duration);
error = _dev.Read(out cmdBuf, out _, (uint)badSector, blockSize, 1, byteAddressed, TIMEOUT,
out duration);
totalDuration += duration;
if(error)
continue;
resume.BadBlocks.Remove(badSector);
_resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
outputPlugin.WriteSector(cmdBuf, badSector);
_outputPlugin.WriteSector(cmdBuf, badSector);
}
EndProgress?.Invoke();
end = DateTime.UtcNow;
UpdateStatus?.Invoke($"Trimmming finished in {(end - start).TotalSeconds} seconds.");
dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds);
_dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds);
}
#endregion Trimming
#region Error handling
if(resume.BadBlocks.Count > 0 &&
!aborted &&
retryPasses > 0)
if(_resume.BadBlocks.Count > 0 &&
!_aborted &&
_retryPasses > 0)
{
int pass = 1;
bool forward = true;
@@ -469,15 +469,15 @@ namespace DiscImageChef.Core.Devices.Dumping
InitProgress?.Invoke();
repeatRetryLba:
ulong[] tmpArray = resume.BadBlocks.ToArray();
ulong[] tmpArray = _resume.BadBlocks.ToArray();
foreach(ulong badSector in tmpArray)
{
if(aborted)
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
UpdateStatus?.Invoke("Aborted!");
dumpLog.WriteLine("Aborted!");
_dumpLog.WriteLine("Aborted!");
break;
}
@@ -486,31 +486,31 @@ namespace DiscImageChef.Core.Devices.Dumping
forward ? "forward" : "reverse",
runningPersistent ? "recovering partial data, " : ""));
error = dev.Read(out cmdBuf, out _, (uint)badSector, blockSize, 1, byteAddressed, TIMEOUT,
out duration);
error = _dev.Read(out cmdBuf, out _, (uint)badSector, blockSize, 1, byteAddressed, TIMEOUT,
out duration);
totalDuration += duration;
if(!error)
{
resume.BadBlocks.Remove(badSector);
_resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
outputPlugin.WriteSector(cmdBuf, badSector);
_outputPlugin.WriteSector(cmdBuf, badSector);
UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}.");
dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
_dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
}
else if(runningPersistent)
outputPlugin.WriteSector(cmdBuf, badSector);
_outputPlugin.WriteSector(cmdBuf, badSector);
}
if(pass < retryPasses &&
!aborted &&
resume.BadBlocks.Count > 0)
if(pass < _retryPasses &&
!_aborted &&
_resume.BadBlocks.Count > 0)
{
pass++;
forward = !forward;
resume.BadBlocks.Sort();
resume.BadBlocks.Reverse();
_resume.BadBlocks.Sort();
_resume.BadBlocks.Reverse();
goto repeatRetryLba;
}
@@ -521,58 +521,58 @@ namespace DiscImageChef.Core.Devices.Dumping
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
outputPlugin.SetDumpHardware(resume.Tries);
_outputPlugin.SetDumpHardware(_resume.Tries);
if(preSidecar != null)
outputPlugin.SetCicmMetadata(preSidecar);
if(_preSidecar != null)
_outputPlugin.SetCicmMetadata(_preSidecar);
dumpLog.WriteLine("Closing output file.");
_dumpLog.WriteLine("Closing output file.");
UpdateStatus?.Invoke("Closing output file.");
DateTime closeStart = DateTime.Now;
outputPlugin.Close();
_outputPlugin.Close();
DateTime closeEnd = DateTime.Now;
UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds.");
dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);
_dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);
if(aborted)
if(_aborted)
{
UpdateStatus?.Invoke("Aborted!");
dumpLog.WriteLine("Aborted!");
_dumpLog.WriteLine("Aborted!");
return;
}
double totalChkDuration = 0;
if(!nometadata)
if(!_nometadata)
{
UpdateStatus?.Invoke("Creating sidecar.");
dumpLog.WriteLine("Creating sidecar.");
_dumpLog.WriteLine("Creating sidecar.");
var filters = new FiltersList();
IFilter filter = filters.GetFilter(outputPath);
IFilter filter = filters.GetFilter(_outputPath);
IMediaImage inputPlugin = ImageFormat.Detect(filter);
if(!inputPlugin.Open(filter))
StoppingErrorMessage?.Invoke("Could not open created image.");
DateTime chkStart = DateTime.UtcNow;
sidecarClass = new Sidecar(inputPlugin, outputPath, filter.Id, encoding);
sidecarClass.InitProgressEvent += InitProgress;
sidecarClass.UpdateProgressEvent += UpdateProgress;
sidecarClass.EndProgressEvent += EndProgress;
sidecarClass.InitProgressEvent2 += InitProgress2;
sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
sidecarClass.EndProgressEvent2 += EndProgress2;
sidecarClass.UpdateStatusEvent += UpdateStatus;
CICMMetadataType sidecar = sidecarClass.Create();
_sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding);
_sidecarClass.InitProgressEvent += InitProgress;
_sidecarClass.UpdateProgressEvent += UpdateProgress;
_sidecarClass.EndProgressEvent += EndProgress;
_sidecarClass.InitProgressEvent2 += InitProgress2;
_sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
_sidecarClass.EndProgressEvent2 += EndProgress2;
_sidecarClass.UpdateStatusEvent += UpdateStatus;
CICMMetadataType sidecar = _sidecarClass.Create();
if(preSidecar != null)
if(_preSidecar != null)
{
preSidecar.BlockMedia = sidecar.BlockMedia;
sidecar = preSidecar;
_preSidecar.BlockMedia = sidecar.BlockMedia;
sidecar = _preSidecar;
}
switch(dev.Type)
switch(_dev.Type)
{
case DeviceType.MMC:
sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();
@@ -592,22 +592,22 @@ namespace DiscImageChef.Core.Devices.Dumping
{
cidDump = new DumpType
{
Image = outputPath, Size = (ulong)cid.Length, Checksums = Checksum.GetChecksums(cid).ToArray()
Image = _outputPath, Size = (ulong)cid.Length, Checksums = Checksum.GetChecksums(cid).ToArray()
};
ret =
outputPlugin.WriteMediaTag(cid,
dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID
: MediaTagType.MMC_CID);
_outputPlugin.WriteMediaTag(cid,
_dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID
: MediaTagType.MMC_CID);
// Cannot write CID to image
if(!ret &&
!force)
!_force)
{
dumpLog.WriteLine("Cannot write CID to output image.");
_dumpLog.WriteLine("Cannot write CID to output image.");
StoppingErrorMessage?.Invoke("Cannot write CID to output image." + Environment.NewLine +
outputPlugin.ErrorMessage);
_outputPlugin.ErrorMessage);
return;
}
@@ -617,22 +617,22 @@ namespace DiscImageChef.Core.Devices.Dumping
{
csdDump = new DumpType
{
Image = outputPath, Size = (ulong)csd.Length, Checksums = Checksum.GetChecksums(csd).ToArray()
Image = _outputPath, Size = (ulong)csd.Length, Checksums = Checksum.GetChecksums(csd).ToArray()
};
ret =
outputPlugin.WriteMediaTag(csd,
dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CSD
: MediaTagType.MMC_CSD);
_outputPlugin.WriteMediaTag(csd,
_dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CSD
: MediaTagType.MMC_CSD);
// Cannot write CSD to image
if(!ret &&
!force)
!_force)
{
dumpLog.WriteLine("Cannot write CSD to output image.");
_dumpLog.WriteLine("Cannot write CSD to output image.");
StoppingErrorMessage?.Invoke("Cannot write CSD to output image." + Environment.NewLine +
outputPlugin.ErrorMessage);
_outputPlugin.ErrorMessage);
return;
}
@@ -642,20 +642,21 @@ namespace DiscImageChef.Core.Devices.Dumping
{
sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD = new DumpType
{
Image = outputPath, Size = (ulong)ecsd.Length, Checksums = Checksum.GetChecksums(ecsd).ToArray()
Image = _outputPath, Size = (ulong)ecsd.Length,
Checksums = Checksum.GetChecksums(ecsd).ToArray()
};
ret = outputPlugin.WriteMediaTag(ecsd, MediaTagType.MMC_ExtendedCSD);
ret = _outputPlugin.WriteMediaTag(ecsd, MediaTagType.MMC_ExtendedCSD);
// Cannot write Extended CSD to image
if(!ret &&
!force)
!_force)
{
dumpLog.WriteLine("Cannot write Extended CSD to output image.");
_dumpLog.WriteLine("Cannot write Extended CSD to output image.");
StoppingErrorMessage?.Invoke("Cannot write Extended CSD to output image." +
Environment.NewLine +
outputPlugin.ErrorMessage);
_outputPlugin.ErrorMessage);
return;
}
@@ -665,22 +666,22 @@ namespace DiscImageChef.Core.Devices.Dumping
{
ocrDump = new DumpType
{
Image = outputPath, Size = (ulong)ocr.Length, Checksums = Checksum.GetChecksums(ocr).ToArray()
Image = _outputPath, Size = (ulong)ocr.Length, Checksums = Checksum.GetChecksums(ocr).ToArray()
};
ret =
outputPlugin.WriteMediaTag(ocr,
dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_OCR
: MediaTagType.MMC_OCR);
_outputPlugin.WriteMediaTag(ocr,
_dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_OCR
: MediaTagType.MMC_OCR);
// Cannot write OCR to image
if(!ret &&
!force)
!_force)
{
dumpLog.WriteLine("Cannot write OCR to output image.");
_dumpLog.WriteLine("Cannot write OCR to output image.");
StoppingErrorMessage?.Invoke("Cannot write OCR to output image." + Environment.NewLine +
outputPlugin.ErrorMessage);
_outputPlugin.ErrorMessage);
return;
}
@@ -690,25 +691,25 @@ namespace DiscImageChef.Core.Devices.Dumping
{
sidecar.BlockMedia[0].SecureDigital.SCR = new DumpType
{
Image = outputPath, Size = (ulong)scr.Length, Checksums = Checksum.GetChecksums(scr).ToArray()
Image = _outputPath, Size = (ulong)scr.Length, Checksums = Checksum.GetChecksums(scr).ToArray()
};
ret = outputPlugin.WriteMediaTag(scr, MediaTagType.SD_SCR);
ret = _outputPlugin.WriteMediaTag(scr, MediaTagType.SD_SCR);
// Cannot write SCR to image
if(!ret &&
!force)
!_force)
{
dumpLog.WriteLine("Cannot write SCR to output image.");
_dumpLog.WriteLine("Cannot write SCR to output image.");
StoppingErrorMessage?.Invoke("Cannot write SCR to output image." + Environment.NewLine +
outputPlugin.ErrorMessage);
_outputPlugin.ErrorMessage);
return;
}
}
switch(dev.Type)
switch(_dev.Type)
{
case DeviceType.MMC:
sidecar.BlockMedia[0].MultiMediaCard.CID = cidDump;
@@ -730,20 +731,19 @@ namespace DiscImageChef.Core.Devices.Dumping
UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds.");
UpdateStatus?.
Invoke($"Average checksum speed {(double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec.");
Invoke($"Average checksum speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000):F3} KiB/sec.");
dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);
_dumpLog.WriteLine("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));
_dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000));
(string type, string subType) xmlType = (null, null);
switch(dev.Type)
switch(_dev.Type)
{
case DeviceType.MMC:
xmlType =
CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.MMC);
xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.MMC);
sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.MMC);
@@ -762,14 +762,14 @@ namespace DiscImageChef.Core.Devices.Dumping
sidecar.BlockMedia[0].LogicalBlocks = blocks;
sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize > 0 ? physicalBlockSize : blockSize;
sidecar.BlockMedia[0].LogicalBlockSize = blockSize;
sidecar.BlockMedia[0].Manufacturer = dev.Manufacturer;
sidecar.BlockMedia[0].Model = dev.Model;
sidecar.BlockMedia[0].Serial = dev.Serial;
sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer;
sidecar.BlockMedia[0].Model = _dev.Model;
sidecar.BlockMedia[0].Serial = _dev.Serial;
sidecar.BlockMedia[0].Size = blocks * blockSize;
UpdateStatus?.Invoke("Writing metadata sidecar");
var xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create);
var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);
var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
xmlSer.Serialize(xmlFs, sidecar);
@@ -782,17 +782,17 @@ namespace DiscImageChef.Core.Devices.Dumping
Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");
UpdateStatus?.
Invoke($"Average speed: {(double)blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");
Invoke($"Average speed: {((double)blockSize * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");
UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
UpdateStatus?.Invoke($"{resume.BadBlocks.Count} sectors could not be read.");
UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");
UpdateStatus?.Invoke("");
if(resume.BadBlocks.Count > 0)
resume.BadBlocks.Sort();
if(_resume.BadBlocks.Count > 0)
_resume.BadBlocks.Sort();
switch(dev.Type)
switch(_dev.Type)
{
case DeviceType.MMC:
Statistics.AddMedia(MediaType.MMC, true);

File diff suppressed because it is too large Load Diff

View File

@@ -37,13 +37,11 @@ using DiscImageChef.Devices;
namespace DiscImageChef.Core.Devices
{
/// <summary>
/// Reduces common code used for scanning and dumping
/// </summary>
partial class Reader
/// <summary>Reduces common code used for scanning and dumping</summary>
internal partial class Reader
{
Device dev;
uint timeout;
readonly Device dev;
readonly uint timeout;
internal Reader(Device dev, uint timeout, byte[] identification, bool raw = false)
{
@@ -56,7 +54,10 @@ namespace DiscImageChef.Core.Devices
{
case DeviceType.ATA:
Identify.IdentifyDevice? ataIdNullable = Identify.Decode(identification);
if(ataIdNullable.HasValue) ataId = ataIdNullable.Value;
if(ataIdNullable.HasValue)
ataId = ataIdNullable.Value;
break;
case DeviceType.NVMe: throw new NotImplementedException("NVMe devices not yet supported.");
}
@@ -81,6 +82,7 @@ namespace DiscImageChef.Core.Devices
case DeviceType.SCSI: return ScsiGetBlocks();
default:
ErrorMessage = $"Unknown device type {dev.Type}.";
return 0;
}
}
@@ -94,6 +96,7 @@ namespace DiscImageChef.Core.Devices
case DeviceType.SCSI: return ScsiFindReadCommand();
default:
ErrorMessage = $"Unknown device type {dev.Type}.";
return true;
}
}
@@ -107,6 +110,7 @@ namespace DiscImageChef.Core.Devices
case DeviceType.SCSI: return ScsiGetBlockSize();
default:
ErrorMessage = $"Unknown device type {dev.Type}.";
return true;
}
}
@@ -120,6 +124,7 @@ namespace DiscImageChef.Core.Devices
case DeviceType.SCSI: return ScsiGetBlocksToRead(startWithBlocks);
default:
ErrorMessage = $"Unknown device type {dev.Type}.";
return true;
}
}
@@ -140,6 +145,7 @@ namespace DiscImageChef.Core.Devices
default:
buffer = null;
duration = 0d;
return true;
}
}
@@ -152,6 +158,7 @@ namespace DiscImageChef.Core.Devices
default:
buffer = null;
duration = 0d;
return true;
}
}
@@ -165,6 +172,7 @@ namespace DiscImageChef.Core.Devices
case DeviceType.SCSI: return ScsiSeek(block, out duration);
default:
duration = 0d;
return true;
}
}
@@ -176,6 +184,7 @@ namespace DiscImageChef.Core.Devices
case DeviceType.ATA: return AtaSeekChs(cylinder, head, sector, out duration);
default:
duration = 0;
return true;
}
}

View File

@@ -47,7 +47,17 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Devices\Dumping\CompactDisc\Data.cs" />
<Compile Include="Devices\Dumping\CompactDisc\Dump.cs" />
<Compile Include="Devices\Dumping\CompactDisc\Error.cs" />
<Compile Include="Devices\Dumping\CompactDisc\LeadOuts.cs" />
<Compile Include="Devices\Dumping\CompactDisc\Pregap.cs" />
<Compile Include="Devices\Dumping\CompactDisc\Subchannel.cs" />
<Compile Include="Devices\Dumping\CompactDisc\Tags.cs" />
<Compile Include="Devices\Dumping\CompactDisc\Tracks.cs" />
<Compile Include="Devices\Dumping\CompactDisc\Trim.cs" />
<Compile Include="Devices\Dumping\Dump.cs" />
<Compile Include="Devices\Dumping\Metadata.cs" />
<Compile Include="Devices\Dumping\PlayStationPortable.cs" />
<Compile Include="Devices\Info\DeviceInfo.cs" />
<Compile Include="Devices\Info\Plextor.cs" />
@@ -61,6 +71,7 @@
<Compile Include="GetPluginBase.cs" />
<Compile Include="ImageInfo.cs" />
<Compile Include="Media\Detection\MMC.cs" />
<Compile Include="Media\Info\CompactDisc.cs" />
<Compile Include="Media\Info\ScsiInfo.cs" />
<Compile Include="Media\Info\XgdInfo.cs" />
<Compile Include="Options.cs" />
@@ -96,7 +107,6 @@
<Compile Include="Devices\ReaderSCSI.cs" />
<Compile Include="Devices\Dumping\SSC.cs" />
<Compile Include="Devices\Dumping\MMC.cs" />
<Compile Include="Devices\Dumping\CompactDisc.cs" />
<Compile Include="Devices\Dumping\SBC.cs" />
<Compile Include="Devices\Dumping\XGD.cs" />
<Compile Include="Devices\Dumping\ResumeSupport.cs" />

View File

@@ -31,85 +31,949 @@
// ****************************************************************************/
using System;
using System.IO;
using System.Linq;
using DiscImageChef.Checksums;
using DiscImageChef.CommonTypes;
using DiscImageChef.Console;
using DiscImageChef.Decoders.CD;
using DiscImageChef.Decoders.Sega;
using DiscImageChef.Devices;
// ReSharper disable JoinDeclarationAndInitializer
namespace DiscImageChef.Core.Media.Detection
{
public static class MMC
{
/// <summary>
/// This is some kind of header. Every 10 bytes there's an audio byte.
/// </summary>
static readonly byte[] VideoNowColorFrameMarker =
/// <summary>SHA256 of PlayStation 2 boot sectors, seen in PAL discs</summary>
const string PS2_PAL_HASH = "5d04ff236613e1d8adcf9c201874acd6f6deed1e04306558b86f91cfb626f39d";
/// <summary>SHA256 of PlayStation 2 boot sectors, seen in Japanese, American, Malaysian and Korean discs</summary>
const string PS2_NTSC_HASH = "0bada1426e2c0351b872ef2a9ad2e5a0ac3918f4c53aa53329cb2911a8e16c23";
/// <summary>SHA256 of PlayStation 2 boot sectors, seen in Japanese discs</summary>
const string PS2_JAPANESE_HASH = "b82bffb809070d61fe050b7e1545df53d8f3cc648257cdff7502bc0ba6b38870";
static readonly byte[] _ps3Id =
{
0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81,
0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7,
0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81,
0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3,
0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7,
0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3,
0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7,
0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81,
0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3,
0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00,
0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81,
0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7,
0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81,
0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3,
0xC7, 0x00, 0x00, 0x00, 0x02, 0x01, 0x04, 0x02, 0x06, 0x03, 0xFF, 0x00, 0x08, 0x04, 0x0A, 0x05, 0x0C,
0x06, 0x0E, 0x07, 0xFF, 0x00, 0x11, 0x08, 0x13, 0x09, 0x15, 0x0A, 0x17, 0x0B, 0xFF, 0x00, 0x19, 0x0C,
0x1B, 0x0D, 0x1D, 0x0E, 0x1F, 0x0F, 0xFF, 0x00, 0x00, 0x28, 0x02, 0x29, 0x04, 0x2A, 0x06, 0x2B, 0xFF,
0x00, 0x08, 0x2C, 0x0A, 0x2D, 0x0C, 0x2E, 0x0E, 0x2F, 0xFF, 0x00, 0x11, 0x30, 0x13, 0x31, 0x15, 0x32,
0x17, 0x33, 0xFF, 0x00, 0x19, 0x34, 0x1B, 0x35, 0x1D, 0x36, 0x1F, 0x37, 0xFF, 0x00, 0x00, 0x38, 0x02,
0x39, 0x04, 0x3A, 0x06, 0x3B, 0xFF, 0x00, 0x08, 0x3C, 0x0A, 0x3D, 0x0C, 0x3E, 0x0E, 0x3F, 0xFF, 0x00,
0x11, 0x40, 0x13, 0x41, 0x15, 0x42, 0x17, 0x43, 0xFF, 0x00, 0x19, 0x44, 0x1B, 0x45, 0x1D, 0x46, 0x1F,
0x47, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
0x50, 0x6C, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x33, 0x00, 0x00, 0x00, 0x00
};
/// <summary>
/// Checks if the media corresponds to CD-i.
/// </summary>
static readonly byte[] _ps4Id =
{
0x50, 0x6C, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x34, 0x00, 0x00, 0x00, 0x00
};
static readonly byte[] _operaId =
{
0x01, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x01
};
// Only present on bootable CDs, but those make more than 99% of all available
static readonly byte[] _fmTownsBootId =
{
0x49, 0x50, 0x4C, 0x34, 0xEB, 0x55, 0x06
};
/// <summary>Present on first two seconds of second track, says "COPYRIGHT BANDAI"</summary>
static readonly byte[] _playdiaCopyright =
{
0x43, 0x4F, 0x50, 0x59, 0x52, 0x49, 0x47, 0x48, 0x54, 0x20, 0x42, 0x41, 0x4E, 0x44, 0x41, 0x49
};
static readonly byte[] _pcEngineSignature =
{
0x50, 0x43, 0x20, 0x45, 0x6E, 0x67, 0x69, 0x6E, 0x65, 0x20, 0x43, 0x44, 0x2D, 0x52, 0x4F, 0x4D, 0x20, 0x53,
0x59, 0x53, 0x54, 0x45, 0x4D
};
static readonly byte[] _pcFxSignature =
{
0x50, 0x43, 0x2D, 0x46, 0x58, 0x3A, 0x48, 0x75, 0x5F, 0x43, 0x44, 0x2D, 0x52, 0x4F, 0x4D
};
static readonly byte[] _atariSignature =
{
0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41,
0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52,
0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41,
0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x52, 0x41, 0x20, 0x49, 0x50, 0x41,
0x52, 0x50, 0x56, 0x4F, 0x44, 0x45, 0x44, 0x20, 0x54, 0x41, 0x20, 0x41, 0x45, 0x48, 0x44, 0x41, 0x52, 0x45,
0x41, 0x20, 0x52, 0x54
};
/// <summary>This is some kind of header. Every 10 bytes there's an audio byte.</summary>
static readonly byte[] _videoNowColorFrameMarker =
{
0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3,
0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81,
0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7,
0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3,
0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00,
0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3,
0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81,
0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7,
0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3,
0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00,
0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3,
0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81,
0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7,
0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x00, 0x00, 0x02, 0x01, 0x04, 0x02, 0x06, 0x03, 0xFF, 0x00, 0x08, 0x04,
0x0A, 0x05, 0x0C, 0x06, 0x0E, 0x07, 0xFF, 0x00, 0x11, 0x08, 0x13, 0x09, 0x15, 0x0A, 0x17, 0x0B, 0xFF, 0x00,
0x19, 0x0C, 0x1B, 0x0D, 0x1D, 0x0E, 0x1F, 0x0F, 0xFF, 0x00, 0x00, 0x28, 0x02, 0x29, 0x04, 0x2A, 0x06, 0x2B,
0xFF, 0x00, 0x08, 0x2C, 0x0A, 0x2D, 0x0C, 0x2E, 0x0E, 0x2F, 0xFF, 0x00, 0x11, 0x30, 0x13, 0x31, 0x15, 0x32,
0x17, 0x33, 0xFF, 0x00, 0x19, 0x34, 0x1B, 0x35, 0x1D, 0x36, 0x1F, 0x37, 0xFF, 0x00, 0x00, 0x38, 0x02, 0x39,
0x04, 0x3A, 0x06, 0x3B, 0xFF, 0x00, 0x08, 0x3C, 0x0A, 0x3D, 0x0C, 0x3E, 0x0E, 0x3F, 0xFF, 0x00, 0x11, 0x40,
0x13, 0x41, 0x15, 0x42, 0x17, 0x43, 0xFF, 0x00, 0x19, 0x44, 0x1B, 0x45, 0x1D, 0x46, 0x1F, 0x47, 0xFF, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x00
};
static bool IsData(byte[] sector)
{
if(sector?.Length != 2352)
return false;
byte[] syncMark =
{
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
};
byte[] testMark = new byte[12];
Array.Copy(sector, 0, testMark, 0, 12);
return syncMark.SequenceEqual(testMark) && (sector[0xF] == 0 || sector[0xF] == 1 || sector[0xF] == 2);
}
/// <summary>Checks if the media corresponds to CD-i.</summary>
/// <param name="sector0">Contents of LBA 0, with all headers.</param>
/// <param name="sector16">Contents of LBA 0, with all headers.</param>
/// <returns><c>true</c> if it corresponds to a CD-i, <c>false</c>otherwise.</returns>
public static bool IsCdi(byte[] sector0, byte[] sector16)
static bool IsCdi(byte[] sector0, byte[] sector16)
{
if(sector0?.Length != 2352 || sector16?.Length != 2352) return false;
if(sector16?.Length != 2352)
return false;
byte[] syncMark = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
byte[] cdiMark = {0x01, 0x43, 0x44, 0x2D};
byte[] testMark = new byte[12];
Array.Copy(sector0, 0, testMark, 0, 12);
byte[] cdiMark =
{
0x01, 0x43, 0x44, 0x2D
};
bool hiddenData = syncMark.SequenceEqual(testMark) &&
(sector0[0xF] == 0 || sector0[0xF] == 1 || sector0[0xF] == 2);
bool isData = IsData(sector0);
if(!hiddenData || sector0[0xF] != 2) return false;
if(!isData ||
sector0[0xF] != 2)
return false;
testMark = new byte[4];
byte[] testMark = new byte[4];
Array.Copy(sector16, 24, testMark, 0, 4);
return cdiMark.SequenceEqual(testMark);
}
public static bool IsVideoNowColor(byte[] videoFrame)
static bool IsVideoNowColor(byte[] videoFrame)
{
if(videoFrame is null || videoFrame.Length < VideoNowColorFrameMarker.Length) return false;
if(videoFrame is null ||
videoFrame.Length < _videoNowColorFrameMarker.Length)
return false;
byte[] buffer = new byte[_videoNowColorFrameMarker.Length];
byte[] buffer = new byte[VideoNowColorFrameMarker.Length];
for(int framePosition = 0; framePosition + buffer.Length < videoFrame.Length; framePosition++)
{
Array.Copy(videoFrame, framePosition, buffer, 0, buffer.Length);
for(int ab = 9; ab < buffer.Length; ab += 10) buffer[ab] = 0;
for(int ab = 9; ab < buffer.Length; ab += 10)
buffer[ab] = 0;
if(!VideoNowColorFrameMarker.SequenceEqual(buffer)) continue;
if(!_videoNowColorFrameMarker.SequenceEqual(buffer))
continue;
return true;
}
return false;
}
public static int GetVideoNowColorOffset(byte[] data)
{
byte[] buffer = new byte[_videoNowColorFrameMarker.Length];
for(int framePosition = 0; framePosition + buffer.Length < data.Length; framePosition++)
{
Array.Copy(data, framePosition, buffer, 0, buffer.Length);
for(int ab = 9; ab < buffer.Length; ab += 10)
buffer[ab] = 0;
if(!_videoNowColorFrameMarker.SequenceEqual(buffer))
continue;
return 18032 - framePosition;
}
return 0;
}
public static void DetectDiscType(ref MediaType mediaType, int sessions, FullTOC.CDFullTOC? decodedToc,
Device dev, out bool hiddenTrack, out bool hiddenData,
int firstTrackLastSession)
{
uint startOfFirstDataTrack = uint.MaxValue;
byte[] cmdBuf;
bool sense;
byte secondSessionFirstTrack = 0;
byte[] sector0;
byte[] sector1 = null;
byte[] ps2BootSectors = null;
byte[] playdia1 = null;
byte[] playdia2 = null;
byte[] firstDataSectorNotZero = null;
byte[] secondDataSectorNotZero = null;
byte[] firstTrackSecondSession = null;
byte[] firstTrackSecondSessionAudio = null;
byte[] videoNowColorFrame;
hiddenTrack = false;
hiddenData = false;
if(decodedToc.HasValue)
if(decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == 2))
secondSessionFirstTrack =
decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == 2).Min(t => t.POINT);
if(mediaType == MediaType.CD ||
mediaType == MediaType.CDROMXA)
{
bool hasDataTrack = false;
bool hasAudioTrack = false;
bool allFirstSessionTracksAreAudio = true;
bool hasVideoTrack = false;
if(decodedToc.HasValue)
foreach(FullTOC.TrackDataDescriptor track in decodedToc.Value.TrackDescriptors)
{
if(track.TNO == 1 &&
((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack ||
(TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental))
allFirstSessionTracksAreAudio &= firstTrackLastSession != 1;
if((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack ||
(TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental)
{
uint startAddress =
(uint)(((track.PHOUR * 3600 * 75) + (track.PMIN * 60 * 75) + (track.PSEC * 75) +
track.PFRAME) - 150);
if(startAddress < startOfFirstDataTrack)
startOfFirstDataTrack = startAddress;
hasDataTrack = true;
allFirstSessionTracksAreAudio &= track.POINT >= firstTrackLastSession;
}
else
{
hasAudioTrack = true;
}
hasVideoTrack |= track.ADR == 4;
}
if(hasDataTrack &&
hasAudioTrack &&
allFirstSessionTracksAreAudio &&
sessions == 2)
mediaType = MediaType.CDPLUS;
if(!hasDataTrack &&
hasAudioTrack &&
sessions == 1)
mediaType = MediaType.CDDA;
if(hasDataTrack &&
!hasAudioTrack &&
sessions == 1)
mediaType = MediaType.CDROM;
if(hasVideoTrack &&
!hasDataTrack &&
sessions == 1)
mediaType = MediaType.CDV;
}
if(secondSessionFirstTrack != 0 &&
decodedToc.HasValue &&
decodedToc.Value.TrackDescriptors.Any(t => t.POINT == secondSessionFirstTrack))
{
FullTOC.TrackDataDescriptor secondSessionFirstTrackTrack =
decodedToc.Value.TrackDescriptors.First(t => t.POINT == secondSessionFirstTrack);
uint firstSectorSecondSessionFirstTrack =
(uint)(((secondSessionFirstTrackTrack.PHOUR * 3600 * 75) +
(secondSessionFirstTrackTrack.PMIN * 60 * 75) +
(secondSessionFirstTrackTrack.PSEC * 75) +
secondSessionFirstTrackTrack.PFRAME) - 150);
sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack, 2352, 1,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
firstTrackSecondSession = cmdBuf;
}
else
{
sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack, 2352, 1,
MmcSectorTypes.Cdda, false, false, true, MmcHeaderCodes.None, true, true,
MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
firstTrackSecondSession = cmdBuf;
}
sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack - 1, 2352, 3,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
firstTrackSecondSessionAudio = cmdBuf;
}
else
{
sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack - 1, 2352, 3,
MmcSectorTypes.Cdda, false, false, true, MmcHeaderCodes.None, true, true,
MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
firstTrackSecondSessionAudio = cmdBuf;
}
}
videoNowColorFrame = new byte[9 * 2352];
for(int i = 0; i < 9; i++)
{
sense = dev.ReadCd(out cmdBuf, out _, (uint)i, 2352, 1, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(sense || dev.Error)
{
sense = dev.ReadCd(out cmdBuf, out _, (uint)i, 2352, 1, MmcSectorTypes.Cdda, false, false, true,
MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(sense || !dev.Error)
{
videoNowColorFrame = null;
break;
}
}
Array.Copy(cmdBuf, 0, videoNowColorFrame, i * 2352, 2352);
}
if(decodedToc.HasValue)
{
FullTOC.TrackDataDescriptor firstTrack = decodedToc.Value.TrackDescriptors.First(t => t.POINT == 1);
uint firstTrackSector = (uint)(((firstTrack.PHOUR * 3600 * 75) + (firstTrack.PMIN * 60 * 75) +
(firstTrack.PSEC * 75) + firstTrack.PFRAME) - 150);
// Check for hidden data before start of track 1
if(firstTrackSector > 0)
{
sense = dev.ReadCd(out sector0, out _, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(!dev.Error &&
!sense)
{
hiddenTrack = true;
hiddenData = IsData(sector0);
if(hiddenData)
{
sense = dev.ReadCd(out byte[] sector16, out _, 16, 2352, 1, MmcSectorTypes.AllTypes, false, false,
true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(IsCdi(sector0, sector16))
{
mediaType = MediaType.CDIREADY;
}
}
}
}
}
sector0 = null;
switch(mediaType)
{
case MediaType.CD:
case MediaType.CDDA:
case MediaType.CDPLUS:
case MediaType.CDROM:
case MediaType.CDROMXA:
{
sense = dev.ReadCd(out cmdBuf, out _, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector0 = new byte[2048];
Array.Copy(cmdBuf, 16, sector0, 0, 2048);
sense = dev.ReadCd(out cmdBuf, out _, 1, 2352, 1, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector1 = new byte[2048];
Array.Copy(cmdBuf, 16, sector1, 0, 2048);
}
sense = dev.ReadCd(out cmdBuf, out _, 4200, 2352, 1, MmcSectorTypes.AllTypes, false, false,
true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
playdia1 = new byte[2048];
Array.Copy(cmdBuf, 24, playdia1, 0, 2048);
}
sense = dev.ReadCd(out cmdBuf, out _, 4201, 2352, 1, MmcSectorTypes.AllTypes, false, false,
true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
playdia2 = new byte[2048];
Array.Copy(cmdBuf, 24, playdia2, 0, 2048);
}
if(startOfFirstDataTrack != uint.MaxValue)
{
sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack, 2352, 1,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
firstDataSectorNotZero = new byte[2048];
Array.Copy(cmdBuf, 16, firstDataSectorNotZero, 0, 2048);
}
sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack + 1, 2352, 1,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
secondDataSectorNotZero = new byte[2048];
Array.Copy(cmdBuf, 16, secondDataSectorNotZero, 0, 2048);
}
}
var ps2Ms = new MemoryStream();
for(uint p = 0; p < 12; p++)
{
sense = dev.ReadCd(out cmdBuf, out _, p, 2352, 1, MmcSectorTypes.AllTypes, false, false,
true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(sense || dev.Error)
break;
ps2Ms.Write(cmdBuf, cmdBuf[0x0F] == 0x02 ? 24 : 16, 2048);
}
if(ps2Ms.Length == 0x6000)
ps2BootSectors = ps2Ms.ToArray();
}
else
{
sense = dev.ReadCd(out cmdBuf, out _, 0, 2324, 1, MmcSectorTypes.Mode2, false, false, true,
MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector0 = new byte[2048];
Array.Copy(cmdBuf, 0, sector0, 0, 2048);
sense = dev.ReadCd(out cmdBuf, out _, 1, 2324, 1, MmcSectorTypes.Mode2, false, false, true,
MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector1 = new byte[2048];
Array.Copy(cmdBuf, 1, sector0, 0, 2048);
}
sense = dev.ReadCd(out cmdBuf, out _, 4200, 2324, 1, MmcSectorTypes.Mode2, false, false,
true, MmcHeaderCodes.None, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
playdia1 = new byte[2048];
Array.Copy(cmdBuf, 0, playdia1, 0, 2048);
}
sense = dev.ReadCd(out cmdBuf, out _, 4201, 2324, 1, MmcSectorTypes.Mode2, false, false,
true, MmcHeaderCodes.None, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
playdia2 = new byte[2048];
Array.Copy(cmdBuf, 0, playdia2, 0, 2048);
}
if(startOfFirstDataTrack != uint.MaxValue)
{
sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack, 2324, 1,
MmcSectorTypes.Mode2, false, false, true, MmcHeaderCodes.None, true,
true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
firstDataSectorNotZero = new byte[2048];
Array.Copy(cmdBuf, 0, firstDataSectorNotZero, 0, 2048);
}
sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack + 1, 2324, 1,
MmcSectorTypes.Mode2, false, false, true, MmcHeaderCodes.None, true,
true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
secondDataSectorNotZero = new byte[2048];
Array.Copy(cmdBuf, 0, secondDataSectorNotZero, 0, 2048);
}
}
var ps2Ms = new MemoryStream();
for(uint p = 0; p < 12; p++)
{
sense = dev.ReadCd(out cmdBuf, out _, p, 2324, 1, MmcSectorTypes.Mode2, false, false,
true, MmcHeaderCodes.None, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(sense || dev.Error)
break;
ps2Ms.Write(cmdBuf, 0, 2048);
}
if(ps2Ms.Length == 0x6000)
ps2BootSectors = ps2Ms.ToArray();
}
else
{
sense = dev.ReadCd(out cmdBuf, out _, 0, 2048, 1, MmcSectorTypes.Mode1, false, false, true,
MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector0 = cmdBuf;
sense = dev.ReadCd(out cmdBuf, out _, 0, 2048, 1, MmcSectorTypes.Mode1, false, false,
true, MmcHeaderCodes.None, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
sector1 = cmdBuf;
sense = dev.ReadCd(out cmdBuf, out _, 0, 2048, 12, MmcSectorTypes.Mode1, false, false,
true, MmcHeaderCodes.None, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
ps2BootSectors = cmdBuf;
if(startOfFirstDataTrack != uint.MaxValue)
{
sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack, 2048, 1,
MmcSectorTypes.Mode1, false, false, true, MmcHeaderCodes.None,
true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout,
out _);
if(!sense &&
!dev.Error)
firstDataSectorNotZero = cmdBuf;
sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack + 1, 2048, 1,
MmcSectorTypes.Mode1, false, false, true, MmcHeaderCodes.None,
true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout,
out _);
if(!sense &&
!dev.Error)
secondDataSectorNotZero = cmdBuf;
}
}
else
{
goto case MediaType.DVDROM;
}
}
}
break;
}
// TODO: Check for CD-i Ready
case MediaType.CDI: break;
case MediaType.DVDROM:
case MediaType.HDDVDROM:
case MediaType.BDROM:
case MediaType.Unknown:
sense = dev.Read16(out cmdBuf, out _, 0, false, true, false, 0, 2048, 0, 1, false, dev.Timeout,
out _);
if(!sense &&
!dev.Error)
{
sector0 = cmdBuf;
sense = dev.Read16(out cmdBuf, out _, 0, false, true, false, 1, 2048, 0, 1, false, dev.Timeout,
out _);
if(!sense &&
!dev.Error)
sector1 = cmdBuf;
sense = dev.Read16(out cmdBuf, out _, 0, false, true, false, 0, 2048, 0, 12, false, dev.Timeout,
out _);
if(!sense &&
!dev.Error &&
cmdBuf.Length == 0x6000)
ps2BootSectors = cmdBuf;
}
else
{
sense = dev.Read12(out cmdBuf, out _, 0, false, true, false, false, 0, 2048, 0, 1, false,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector0 = cmdBuf;
sense = dev.Read12(out cmdBuf, out _, 0, false, true, false, false, 1, 2048, 0, 1, false,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
sector1 = cmdBuf;
sense = dev.Read12(out cmdBuf, out _, 0, false, true, false, false, 0, 2048, 0, 12, false,
dev.Timeout, out _);
if(!sense &&
!dev.Error &&
cmdBuf.Length == 0x6000)
ps2BootSectors = cmdBuf;
}
else
{
sense = dev.Read10(out cmdBuf, out _, 0, false, true, false, false, 0, 2048, 0, 1,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector0 = cmdBuf;
sense = dev.Read10(out cmdBuf, out _, 0, false, true, false, false, 1, 2048, 0, 1,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
sector1 = cmdBuf;
sense = dev.Read10(out cmdBuf, out _, 0, false, true, false, false, 0, 2048, 0, 12,
dev.Timeout, out _);
if(!sense &&
!dev.Error &&
cmdBuf.Length == 0x6000)
ps2BootSectors = cmdBuf;
}
else
{
sense = dev.Read6(out cmdBuf, out _, 0, 2048, 1, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector0 = cmdBuf;
sense = dev.Read6(out cmdBuf, out _, 1, 2048, 1, dev.Timeout, out _);
if(!sense &&
!dev.Error)
sector1 = cmdBuf;
sense = dev.Read6(out cmdBuf, out _, 0, 2048, 12, dev.Timeout, out _);
if(!sense &&
!dev.Error &&
cmdBuf.Length == 0x6000)
ps2BootSectors = cmdBuf;
}
}
}
}
break;
// Recordables will not be checked
case MediaType.CDR:
case MediaType.CDRW:
case MediaType.CDMRW:
case MediaType.DDCDR:
case MediaType.DDCDRW:
case MediaType.DVDR:
case MediaType.DVDRW:
case MediaType.DVDPR:
case MediaType.DVDPRW:
case MediaType.DVDPRWDL:
case MediaType.DVDRDL:
case MediaType.DVDPRDL:
case MediaType.DVDRAM:
case MediaType.DVDRWDL:
case MediaType.DVDDownload:
case MediaType.HDDVDRAM:
case MediaType.HDDVDR:
case MediaType.HDDVDRW:
case MediaType.HDDVDRDL:
case MediaType.HDDVDRWDL:
case MediaType.BDR:
case MediaType.BDRE:
case MediaType.BDRXL:
case MediaType.BDREXL: return;
}
if(sector0 == null)
return;
switch(mediaType)
{
case MediaType.CD:
case MediaType.CDDA:
case MediaType.CDPLUS:
case MediaType.CDROM:
case MediaType.CDROMXA:
// TODO: CDTV requires reading the filesystem, searching for a file called "/CDTV.TM"
// TODO: CD32 requires reading the filesystem, searching for a file called "/CD32.TM"
// TODO: Neo-Geo CD requires reading the filesystem and checking that the file "/IPL.TXT" is correct
// TODO: Pippin requires interpreting Apple Partition Map, reading HFS and checking for Pippin signatures
{
if(CD.DecodeIPBin(sector0).HasValue)
{
mediaType = MediaType.MEGACD;
return;
}
if(Saturn.DecodeIPBin(sector0).HasValue)
mediaType = MediaType.SATURNCD;
// Are GDR detectable ???
if(Dreamcast.DecodeIPBin(sector0).HasValue)
mediaType = MediaType.GDROM;
if(ps2BootSectors != null &&
ps2BootSectors.Length == 0x6000)
{
// The decryption key is applied as XOR. As first byte is originally always NULL, it gives us the key :)
byte decryptByte = ps2BootSectors[0];
for(int i = 0; i < 0x6000; i++)
ps2BootSectors[i] ^= decryptByte;
string ps2BootSectorsHash = Sha256Context.Data(ps2BootSectors, out _);
DicConsole.DebugWriteLine("Media-info Command", "PlayStation 2 boot sectors SHA256: {0}",
ps2BootSectorsHash);
if(ps2BootSectorsHash == PS2_PAL_HASH ||
ps2BootSectorsHash == PS2_NTSC_HASH ||
ps2BootSectorsHash == PS2_JAPANESE_HASH)
mediaType = MediaType.PS2CD;
}
if(sector0 != null)
{
byte[] syncBytes = new byte[7];
Array.Copy(sector0, 0, syncBytes, 0, 7);
if(_operaId.SequenceEqual(syncBytes))
mediaType = MediaType.ThreeDO;
if(_fmTownsBootId.SequenceEqual(syncBytes))
mediaType = MediaType.FMTOWNS;
}
if(playdia1 != null &&
playdia2 != null)
{
byte[] pd1 = new byte[_playdiaCopyright.Length];
byte[] pd2 = new byte[_playdiaCopyright.Length];
Array.Copy(playdia1, 38, pd1, 0, pd1.Length);
Array.Copy(playdia2, 0, pd2, 0, pd1.Length);
if(_playdiaCopyright.SequenceEqual(pd1) &&
_playdiaCopyright.SequenceEqual(pd2))
mediaType = MediaType.Playdia;
}
if(secondDataSectorNotZero != null)
{
byte[] pce = new byte[_pcEngineSignature.Length];
Array.Copy(secondDataSectorNotZero, 32, pce, 0, pce.Length);
if(_pcEngineSignature.SequenceEqual(pce))
mediaType = MediaType.SuperCDROM2;
}
if(firstDataSectorNotZero != null)
{
byte[] pcfx = new byte[_pcFxSignature.Length];
Array.Copy(firstDataSectorNotZero, 0, pcfx, 0, pcfx.Length);
if(_pcFxSignature.SequenceEqual(pcfx))
mediaType = MediaType.PCFX;
}
if(firstTrackSecondSessionAudio != null)
{
byte[] jaguar = new byte[_atariSignature.Length];
for(int i = 0; i + jaguar.Length <= firstTrackSecondSessionAudio.Length; i += 2)
{
Array.Copy(firstTrackSecondSessionAudio, i, jaguar, 0, jaguar.Length);
if(!_atariSignature.SequenceEqual(jaguar))
continue;
mediaType = MediaType.JaguarCD;
break;
}
}
if(firstTrackSecondSession != null)
if(firstTrackSecondSession.Length >= 2336)
{
byte[] milcd = new byte[2048];
Array.Copy(firstTrackSecondSession, 24, milcd, 0, 2048);
if(Dreamcast.DecodeIPBin(milcd).HasValue)
mediaType = MediaType.MilCD;
}
// TODO: Detect black and white VideoNow
// TODO: Detect VideoNow XP
if(IsVideoNowColor(videoNowColorFrame))
mediaType = MediaType.VideoNowColor;
break;
}
// TODO: Check for CD-i Ready
case MediaType.CDI: break;
case MediaType.DVDROM:
case MediaType.HDDVDROM:
case MediaType.BDROM:
case MediaType.Unknown:
// TODO: Nuon requires reading the filesystem, searching for a file called "/NUON/NUON.RUN"
if(ps2BootSectors != null &&
ps2BootSectors.Length == 0x6000)
{
// The decryption key is applied as XOR. As first byte is originally always NULL, it gives us the key :)
byte decryptByte = ps2BootSectors[0];
for(int i = 0; i < 0x6000; i++)
ps2BootSectors[i] ^= decryptByte;
string ps2BootSectorsHash = Sha256Context.Data(ps2BootSectors, out _);
DicConsole.DebugWriteLine("Media-info Command", "PlayStation 2 boot sectors SHA256: {0}",
ps2BootSectorsHash);
if(ps2BootSectorsHash == PS2_PAL_HASH ||
ps2BootSectorsHash == PS2_NTSC_HASH ||
ps2BootSectorsHash == PS2_JAPANESE_HASH)
mediaType = MediaType.PS2DVD;
}
if(sector1 != null)
{
byte[] tmp = new byte[_ps3Id.Length];
Array.Copy(sector1, 0, tmp, 0, tmp.Length);
if(tmp.SequenceEqual(_ps3Id))
switch(mediaType)
{
case MediaType.BDROM:
mediaType = MediaType.PS3BD;
break;
case MediaType.DVDROM:
mediaType = MediaType.PS3DVD;
break;
}
tmp = new byte[_ps4Id.Length];
Array.Copy(sector1, 512, tmp, 0, tmp.Length);
if(tmp.SequenceEqual(_ps4Id) &&
mediaType == MediaType.BDROM)
mediaType = MediaType.PS4BD;
}
// TODO: Identify discs that require reading tracks (PC-FX, PlayStation, Sega, etc)
break;
}
}
}
}

View File

@@ -0,0 +1,236 @@
using System;
using System.Linq;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Core.Logging;
using DiscImageChef.Core.Media.Detection;
using DiscImageChef.Database.Models;
using DiscImageChef.Devices;
using Device = DiscImageChef.Database.Models.Device;
namespace DiscImageChef.Core.Media.Info
{
public static class CompactDisc
{
/// <summary>Gets the offset bytes from a Compact Disc</summary>
/// <param name="cdOffset">Offset entry from database</param>
/// <param name="dbDev">Device entry from database</param>
/// <param name="debug">Debug</param>
/// <param name="dev">Opened device</param>
/// <param name="dskType">Detected disk type</param>
/// <param name="dumpLog">Dump log if applicable</param>
/// <param name="offsetBytes">Set to combined offset, in bytes</param>
/// <param name="readcd">If device supports READ CD command</param>
/// <param name="sectorsForOffset">Sectors needed to fix offset</param>
/// <param name="tracks">Disc track list</param>
/// <param name="updateStatus">UpdateStatus event</param>
/// <returns><c>true</c> if offset could be found, <c>false</c> otherwise</returns>
public static bool GetOffset(CdOffset cdOffset, Device dbDev, bool debug, DiscImageChef.Devices.Device dev,
MediaType dskType, DumpLog dumpLog, out int offsetBytes, bool readcd,
out int sectorsForOffset, Track[] tracks, UpdateStatusHandler updateStatus)
{
byte[] cmdBuf;
bool sense;
bool offsetFound = false;
const uint sectorSize = 2352;
offsetBytes = 0;
sectorsForOffset = 0;
if(dskType != MediaType.VideoNowColor)
{
if(tracks.All(t => t.TrackType != TrackType.Audio))
{
// No audio tracks so no need to fix offset
dumpLog?.WriteLine("No audio tracks, disabling offset fix.");
updateStatus?.Invoke("No audio tracks, disabling offset fix.");
return false;
}
if(!readcd)
{
dumpLog.WriteLine("READ CD command is not supported, disabling offset fix. Dump may not be correct.");
updateStatus?.
Invoke("READ CD command is not supported, disabling offset fix. Dump may not be correct.");
return false;
}
if(tracks.Any(t => t.TrackType != TrackType.Audio))
{
Track dataTrack = tracks.FirstOrDefault(t => t.TrackType != TrackType.Audio);
if(dataTrack.TrackSequence != 0)
{
dataTrack.TrackStartSector += 151;
// Calculate MSF
ulong minute = dataTrack.TrackStartSector / 4500;
ulong second = (dataTrack.TrackStartSector - (minute * 4500)) / 75;
ulong frame = dataTrack.TrackStartSector - (minute * 4500) - (second * 75);
dataTrack.TrackStartSector -= 151;
// Convert to BCD
ulong remainder = minute % 10;
minute = ((minute / 10) * 16) + remainder;
remainder = second % 10;
second = ((second / 10) * 16) + remainder;
remainder = frame % 10;
frame = ((frame / 10) * 16) + remainder;
// Scramble M and S
minute ^= 0x01;
second ^= 0x80;
// Build sync
byte[] sectorSync =
{
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, (byte)minute,
(byte)second, (byte)frame
};
byte[] tmpBuf = new byte[sectorSync.Length];
// Plextor READ CDDA
if(dbDev?.ATAPI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true ||
dbDev?.SCSI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true ||
dev.Manufacturer.ToLowerInvariant() == "plextor")
{
sense = dev.PlextorReadCdDa(out cmdBuf, out _, (uint)dataTrack.TrackStartSector, sectorSize,
3, PlextorSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
for(int i = 0; i < cmdBuf.Length - sectorSync.Length; i++)
{
Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length);
if(!tmpBuf.SequenceEqual(sectorSync))
continue;
offsetBytes = i - 2352;
offsetFound = true;
break;
}
}
}
if(debug ||
dbDev?.ATAPI?.RemovableMedias?.Any(d => d.CanReadCdScrambled == true) == true ||
dbDev?.SCSI?.RemovableMedias?.Any(d => d.CanReadCdScrambled == true) == true ||
dev.Manufacturer.ToLowerInvariant() == "hl-dt-st")
{
sense = dev.ReadCd(out cmdBuf, out _, (uint)dataTrack.TrackStartSector, sectorSize, 3,
MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true,
false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
for(int i = 0; i < cmdBuf.Length - sectorSync.Length; i++)
{
Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length);
if(!tmpBuf.SequenceEqual(sectorSync))
continue;
offsetBytes = i - 2352;
offsetFound = true;
break;
}
}
}
}
}
if(cdOffset is null)
{
if(offsetFound)
{
dumpLog?.WriteLine($"Combined disc and drive offsets are {offsetBytes} bytes");
updateStatus?.Invoke($"Combined disc and drive offsets are {offsetBytes} bytes");
}
else
{
dumpLog.
WriteLine("Drive read offset is unknown, disabling offset fix. Dump may not be correct.");
updateStatus?.
Invoke("Drive read offset is unknown, disabling offset fix. Dump may not be correct.");
return false;
}
}
else
{
if(offsetFound)
{
dumpLog.
WriteLine($"Disc offsets is {offsetBytes - (cdOffset.Offset * 4)} bytes ({(offsetBytes / 4) - cdOffset.Offset} samples)");
updateStatus?.
Invoke($"Disc offsets is {offsetBytes - (cdOffset.Offset * 4)} bytes ({(offsetBytes / 4) - cdOffset.Offset} samples)");
}
else
{
dumpLog?.WriteLine("Disc write offset is unknown, dump may not be correct.");
updateStatus?.Invoke("Disc write offset is unknown, dump may not be correct.");
offsetBytes = cdOffset.Offset * 4;
}
dumpLog?.WriteLine($"Offset is {offsetBytes} bytes.");
updateStatus?.Invoke($"Offset is {offsetBytes} bytes.");
}
}
else
{
byte[] videoNowColorFrame = new byte[9 * sectorSize];
sense = dev.ReadCd(out cmdBuf, out _, 0, sectorSize, 9, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(sense || dev.Error)
{
sense = dev.ReadCd(out cmdBuf, out _, 0, sectorSize, 9, MmcSectorTypes.Cdda, false, false, true,
MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(sense || dev.Error)
{
videoNowColorFrame = null;
}
}
if(videoNowColorFrame is null)
{
dumpLog?.WriteLine("Could not find VideoNow Color frame offset, dump may not be correct.");
updateStatus?.Invoke("Could not find VideoNow Color frame offset, dump may not be correct.");
}
else
{
offsetBytes = MMC.GetVideoNowColorOffset(videoNowColorFrame);
dumpLog?.WriteLine($"VideoNow Color frame is offset {offsetBytes} bytes.");
updateStatus?.Invoke($"VideoNow Color frame is offset {offsetBytes} bytes.");
}
}
sectorsForOffset = offsetBytes / (int)sectorSize;
if(sectorsForOffset < 0)
sectorsForOffset *= -1;
if(offsetBytes % sectorSize != 0)
sectorsForOffset++;
return offsetFound;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -62,7 +62,7 @@ namespace DiscImageChef.Database
public static DicContext Create(string dbPath)
{
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlite($"Data Source={dbPath}");
optionsBuilder.UseLazyLoadingProxies().UseSqlite($"Data Source={dbPath}");
return new DicContext(optionsBuilder.Options);
}

View File

@@ -52,6 +52,7 @@
<ItemGroup>
<PackageReference Include="EntityFramework" Version="6.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.6" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.3.0" />

View File

@@ -38,9 +38,7 @@ namespace DiscImageChef.Devices
{
public partial class Device
{
/// <summary>
/// Sends the MMC GET CONFIGURATION command for all Features
/// </summary>
/// <summary>Sends the MMC GET CONFIGURATION command for all Features</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI GET CONFIGURATION response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -49,23 +47,19 @@ namespace DiscImageChef.Devices
public bool GetConfiguration(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) =>
GetConfiguration(out buffer, out senseBuffer, 0x0000, MmcGetConfigurationRt.All, timeout, out duration);
/// <summary>
/// Sends the MMC GET CONFIGURATION command for all Features starting with specified one
/// </summary>
/// <summary>Sends the MMC GET CONFIGURATION command for all Features starting with specified one</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI GET CONFIGURATION response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
/// <param name="startingFeatureNumber">Feature number where the feature list should start from</param>
/// <param name="timeout">Timeout in seconds.</param>
/// <param name="duration">Duration in milliseconds it took for the device to execute the command.</param>
public bool GetConfiguration(out byte[] buffer, out byte[] senseBuffer, ushort startingFeatureNumber,
uint timeout, out double duration) =>
public bool GetConfiguration(out byte[] buffer, out byte[] senseBuffer, ushort startingFeatureNumber,
uint timeout, out double duration) =>
GetConfiguration(out buffer, out senseBuffer, startingFeatureNumber, MmcGetConfigurationRt.All, timeout,
out duration);
/// <summary>
/// Sends the MMC GET CONFIGURATION command
/// </summary>
/// <summary>Sends the MMC GET CONFIGURATION command</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI GET CONFIGURATION response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -73,8 +67,7 @@ namespace DiscImageChef.Devices
/// <param name="duration">Duration in milliseconds it took for the device to execute the command.</param>
/// <param name="startingFeatureNumber">Starting Feature number.</param>
/// <param name="rt">Return type, <see cref="MmcGetConfigurationRt" />.</param>
public bool GetConfiguration(out byte[] buffer, out byte[] senseBuffer,
ushort startingFeatureNumber,
public bool GetConfiguration(out byte[] buffer, out byte[] senseBuffer, ushort startingFeatureNumber,
MmcGetConfigurationRt rt, uint timeout, out double duration)
{
senseBuffer = new byte[32];
@@ -91,9 +84,11 @@ namespace DiscImageChef.Devices
LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration,
out bool sense);
Error = LastError != 0;
if(sense) return true;
if(sense)
return true;
ushort confLength = (ushort)((buffer[2] << 8) + buffer[3] + 4);
buffer = new byte[confLength];
@@ -103,6 +98,7 @@ namespace DiscImageChef.Devices
LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration,
out sense);
Error = LastError != 0;
DicConsole.DebugWriteLine("SCSI Device", "GET CONFIGURATION took {0} ms.", duration);
@@ -110,9 +106,7 @@ namespace DiscImageChef.Devices
return sense;
}
/// <summary>
/// Sends the MMC READ DISC STRUCTURE command
/// </summary>
/// <summary>Sends the MMC READ DISC STRUCTURE command</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI READ DISC STRUCTURE response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -123,10 +117,9 @@ namespace DiscImageChef.Devices
/// <param name="format">Which disc structure are we requesting</param>
/// <param name="agid">AGID used in medium copy protection</param>
/// <param name="duration">Duration in milliseconds it took for the device to execute the command.</param>
public bool ReadDiscStructure(out byte[] buffer, out byte[] senseBuffer, MmcDiscStructureMediaType mediaType,
uint address, byte layerNumber, MmcDiscStructureFormat format,
byte agid,
uint timeout, out double duration)
public bool ReadDiscStructure(out byte[] buffer, out byte[] senseBuffer, MmcDiscStructureMediaType mediaType,
uint address, byte layerNumber, MmcDiscStructureFormat format, byte agid,
uint timeout, out double duration)
{
senseBuffer = new byte[32];
byte[] cdb = new byte[12];
@@ -146,9 +139,11 @@ namespace DiscImageChef.Devices
LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration,
out bool sense);
Error = LastError != 0;
if(sense) return true;
if(sense)
return true;
ushort strctLength = (ushort)((buffer[0] << 8) + buffer[1] + 2);
@@ -158,30 +153,39 @@ namespace DiscImageChef.Devices
{
case MmcDiscStructureFormat.DiscInformation:
buffer = new byte[4100];
break;
case MmcDiscStructureFormat.BdBurstCuttingArea:
buffer = new byte[68];
break;
case MmcDiscStructureFormat.BdDds:
buffer = new byte[strctLength < 100 ? 100 : strctLength];
break;
case MmcDiscStructureFormat.CartridgeStatus:
buffer = new byte[8];
break;
case MmcDiscStructureFormat.BdSpareAreaInformation:
buffer = new byte[16];
break;
default:
buffer = new byte[strctLength];
break;
}
else buffer = new byte[strctLength];
else
buffer = new byte[strctLength];
cdb[8] = (byte)((buffer.Length & 0xFF00) >> 8);
cdb[9] = (byte)(buffer.Length & 0xFF);
senseBuffer = new byte[32];
LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration,
out sense);
Error = LastError != 0;
DicConsole.DebugWriteLine("SCSI Device", "READ DISC STRUCTURE took {0} ms.", duration);
@@ -189,9 +193,7 @@ namespace DiscImageChef.Devices
return sense;
}
/// <summary>
/// Sends the MMC READ TOC/PMA/ATIP command to get formatted TOC from disc, in MM:SS:FF format
/// </summary>
/// <summary>Sends the MMC READ TOC/PMA/ATIP command to get formatted TOC from disc, in MM:SS:FF format</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI READ TOC/PMA/ATIP response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -201,9 +203,7 @@ namespace DiscImageChef.Devices
public bool ReadToc(out byte[] buffer, out byte[] senseBuffer, byte track, uint timeout, out double duration) =>
ReadTocPmaAtip(out buffer, out senseBuffer, true, 0, track, timeout, out duration);
/// <summary>
/// Sends the MMC READ TOC/PMA/ATIP command to get formatted TOC from disc
/// </summary>
/// <summary>Sends the MMC READ TOC/PMA/ATIP command to get formatted TOC from disc</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI READ TOC/PMA/ATIP response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -215,9 +215,7 @@ namespace DiscImageChef.Devices
out double duration) =>
ReadTocPmaAtip(out buffer, out senseBuffer, msf, 0, track, timeout, out duration);
/// <summary>
/// Sends the MMC READ TOC/PMA/ATIP command to get multi-session information, in MM:SS:FF format
/// </summary>
/// <summary>Sends the MMC READ TOC/PMA/ATIP command to get multi-session information, in MM:SS:FF format</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI READ TOC/PMA/ATIP response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -226,9 +224,7 @@ namespace DiscImageChef.Devices
public bool ReadSessionInfo(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) =>
ReadTocPmaAtip(out buffer, out senseBuffer, true, 1, 0, timeout, out duration);
/// <summary>
/// Sends the MMC READ TOC/PMA/ATIP command to get multi-session information
/// </summary>
/// <summary>Sends the MMC READ TOC/PMA/ATIP command to get multi-session information</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI READ TOC/PMA/ATIP response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -239,9 +235,7 @@ namespace DiscImageChef.Devices
out double duration) =>
ReadTocPmaAtip(out buffer, out senseBuffer, msf, 1, 0, timeout, out duration);
/// <summary>
/// Sends the MMC READ TOC/PMA/ATIP command to get raw TOC subchannels
/// </summary>
/// <summary>Sends the MMC READ TOC/PMA/ATIP command to get raw TOC subchannels</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI READ TOC/PMA/ATIP response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -252,9 +246,7 @@ namespace DiscImageChef.Devices
out double duration) =>
ReadTocPmaAtip(out buffer, out senseBuffer, true, 2, sessionNumber, timeout, out duration);
/// <summary>
/// Sends the MMC READ TOC/PMA/ATIP command to get PMA
/// </summary>
/// <summary>Sends the MMC READ TOC/PMA/ATIP command to get PMA</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI READ TOC/PMA/ATIP response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -263,9 +255,7 @@ namespace DiscImageChef.Devices
public bool ReadPma(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) =>
ReadTocPmaAtip(out buffer, out senseBuffer, true, 3, 0, timeout, out duration);
/// <summary>
/// Sends the MMC READ TOC/PMA/ATIP command to get ATIP
/// </summary>
/// <summary>Sends the MMC READ TOC/PMA/ATIP command to get ATIP</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI READ TOC/PMA/ATIP response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -274,9 +264,7 @@ namespace DiscImageChef.Devices
public bool ReadAtip(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) =>
ReadTocPmaAtip(out buffer, out senseBuffer, true, 4, 0, timeout, out duration);
/// <summary>
/// Sends the MMC READ TOC/PMA/ATIP command to get Lead-In CD-TEXT
/// </summary>
/// <summary>Sends the MMC READ TOC/PMA/ATIP command to get Lead-In CD-TEXT</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI READ TOC/PMA/ATIP response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -285,9 +273,7 @@ namespace DiscImageChef.Devices
public bool ReadCdText(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) =>
ReadTocPmaAtip(out buffer, out senseBuffer, true, 5, 0, timeout, out duration);
/// <summary>
/// Sends the MMC READ TOC/PMA/ATIP command
/// </summary>
/// <summary>Sends the MMC READ TOC/PMA/ATIP command</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI READ TOC/PMA/ATIP response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -296,8 +282,8 @@ namespace DiscImageChef.Devices
/// <param name="trackSessionNumber">Track/Session number</param>
/// <param name="timeout">Timeout in seconds.</param>
/// <param name="duration">Duration in milliseconds it took for the device to execute the command.</param>
public bool ReadTocPmaAtip(out byte[] buffer, out byte[] senseBuffer, bool msf, byte format,
byte trackSessionNumber, uint timeout, out double duration)
public bool ReadTocPmaAtip(out byte[] buffer, out byte[] senseBuffer, bool msf, byte format,
byte trackSessionNumber, uint timeout, out double duration)
{
senseBuffer = new byte[32];
byte[] cdb = new byte[10];
@@ -305,7 +291,10 @@ namespace DiscImageChef.Devices
byte[] tmpBuffer = (format & 0x0F) == 5 ? new byte[32768] : new byte[1024];
cdb[0] = (byte)ScsiCommands.ReadTocPmaAtip;
if(msf) cdb[1] = 0x02;
if(msf)
cdb[1] = 0x02;
cdb[2] = (byte)(format & 0x0F);
cdb[6] = trackSessionNumber;
cdb[7] = (byte)((tmpBuffer.Length & 0xFF00) >> 8);
@@ -313,6 +302,7 @@ namespace DiscImageChef.Devices
LastError = SendScsiCommand(cdb, ref tmpBuffer, out senseBuffer, timeout, ScsiDirection.In, out duration,
out bool sense);
Error = LastError != 0;
uint strctLength = (uint)((tmpBuffer[0] << 8) + tmpBuffer[1] + 2);
@@ -322,6 +312,7 @@ namespace DiscImageChef.Devices
{
Array.Copy(tmpBuffer, 0, buffer, 0, buffer.Length);
DicConsole.DebugWriteLine("SCSI Device", "READ TOC/PMA/ATIP took {0} ms.", duration);
return sense;
}
@@ -329,16 +320,16 @@ namespace DiscImageChef.Devices
LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration,
out sense);
Error = LastError != 0;
duration += tmpDuration;
DicConsole.DebugWriteLine("SCSI Device", "READ TOC/PMA/ATIP took {0} ms.", duration);
return sense;
}
/// <summary>
/// Sends the MMC READ DISC INFORMATION command
/// </summary>
/// <summary>Sends the MMC READ DISC INFORMATION command</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI READ DISC INFORMATION response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -348,18 +339,15 @@ namespace DiscImageChef.Devices
ReadDiscInformation(out buffer, out senseBuffer, MmcDiscInformationDataTypes.DiscInformation, timeout,
out duration);
/// <summary>
/// Sends the MMC READ DISC INFORMATION command
/// </summary>
/// <summary>Sends the MMC READ DISC INFORMATION command</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the SCSI READ DISC INFORMATION response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
/// <param name="dataType">Which disc information to read</param>
/// <param name="timeout">Timeout in seconds.</param>
/// <param name="duration">Duration in milliseconds it took for the device to execute the command.</param>
public bool ReadDiscInformation(out byte[] buffer, out byte[] senseBuffer,
MmcDiscInformationDataTypes dataType,
uint timeout, out double duration)
public bool ReadDiscInformation(out byte[] buffer, out byte[] senseBuffer, MmcDiscInformationDataTypes dataType,
uint timeout, out double duration)
{
senseBuffer = new byte[32];
byte[] cdb = new byte[10];
@@ -372,10 +360,13 @@ namespace DiscImageChef.Devices
LastError = SendScsiCommand(cdb, ref tmpBuffer, out senseBuffer, timeout, ScsiDirection.In, out duration,
out bool sense);
Error = LastError != 0;
uint strctLength = (uint)((tmpBuffer[0] << 8) + tmpBuffer[1] + 2);
if(strctLength > tmpBuffer.Length) strctLength = (uint)tmpBuffer.Length;
uint strctLength = (uint)((tmpBuffer[0] << 8) + tmpBuffer[1] + 2);
if(strctLength > tmpBuffer.Length)
strctLength = (uint)tmpBuffer.Length;
buffer = new byte[strctLength];
Array.Copy(tmpBuffer, 0, buffer, 0, buffer.Length);
@@ -385,9 +376,7 @@ namespace DiscImageChef.Devices
return sense;
}
/// <summary>
/// Sends the MMC READ CD command
/// </summary>
/// <summary>Sends the MMC READ CD command</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the MMC READ CD response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -405,21 +394,23 @@ namespace DiscImageChef.Devices
/// <param name="edcEcc">If set to <c>true</c> we request the EDC/ECC fields for data sectors.</param>
/// <param name="c2Error">C2 error options.</param>
/// <param name="subchannel">Subchannel selection.</param>
public bool ReadCd(out byte[] buffer, out byte[] senseBuffer, uint lba,
uint blockSize, uint transferLength,
MmcSectorTypes expectedSectorType, bool dap, bool relAddr,
bool sync,
MmcHeaderCodes headerCodes, bool userData, bool edcEcc,
MmcErrorField c2Error,
MmcSubchannel subchannel, uint timeout, out double duration)
public bool ReadCd(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, uint transferLength,
MmcSectorTypes expectedSectorType, bool dap, bool relAddr, bool sync,
MmcHeaderCodes headerCodes, bool userData, bool edcEcc, MmcErrorField c2Error,
MmcSubchannel subchannel, uint timeout, out double duration)
{
senseBuffer = new byte[32];
byte[] cdb = new byte[12];
cdb[0] = (byte)ScsiCommands.ReadCd;
cdb[1] = (byte)((byte)expectedSectorType << 2);
if(dap) cdb[1] += 0x02;
if(relAddr) cdb[1] += 0x01;
if(dap)
cdb[1] += 0x02;
if(relAddr)
cdb[1] += 0x01;
cdb[2] = (byte)((lba & 0xFF000000) >> 24);
cdb[3] = (byte)((lba & 0xFF0000) >> 16);
cdb[4] = (byte)((lba & 0xFF00) >> 8);
@@ -429,15 +420,23 @@ namespace DiscImageChef.Devices
cdb[8] = (byte)(transferLength & 0xFF);
cdb[9] = (byte)((byte)c2Error << 1);
cdb[9] += (byte)((byte)headerCodes << 5);
if(sync) cdb[9] += 0x80;
if(userData) cdb[9] += 0x10;
if(edcEcc) cdb[9] += 0x08;
if(sync)
cdb[9] += 0x80;
if(userData)
cdb[9] += 0x10;
if(edcEcc)
cdb[9] += 0x08;
cdb[10] = (byte)subchannel;
buffer = new byte[blockSize * transferLength];
LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration,
out bool sense);
Error = LastError != 0;
DicConsole.DebugWriteLine("SCSI Device", "READ CD took {0} ms.", duration);
@@ -445,9 +444,7 @@ namespace DiscImageChef.Devices
return sense;
}
/// <summary>
/// Sends the MMC READ CD MSF command
/// </summary>
/// <summary>Sends the MMC READ CD MSF command</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the MMC READ CD MSF response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
@@ -464,20 +461,20 @@ namespace DiscImageChef.Devices
/// <param name="edcEcc">If set to <c>true</c> we request the EDC/ECC fields for data sectors.</param>
/// <param name="c2Error">C2 error options.</param>
/// <param name="subchannel">Subchannel selection.</param>
public bool ReadCdMsf(out byte[] buffer, out byte[] senseBuffer, uint startMsf,
uint endMsf, uint blockSize,
MmcSectorTypes expectedSectorType, bool dap, bool sync,
MmcHeaderCodes headerCodes,
bool userData, bool edcEcc, MmcErrorField c2Error,
MmcSubchannel subchannel, uint timeout,
out double duration)
public bool ReadCdMsf(out byte[] buffer, out byte[] senseBuffer, uint startMsf, uint endMsf, uint blockSize,
MmcSectorTypes expectedSectorType, bool dap, bool sync, MmcHeaderCodes headerCodes,
bool userData, bool edcEcc, MmcErrorField c2Error, MmcSubchannel subchannel, uint timeout,
out double duration)
{
senseBuffer = new byte[32];
byte[] cdb = new byte[12];
cdb[0] = (byte)ScsiCommands.ReadCdMsf;
cdb[1] = (byte)((byte)expectedSectorType << 2);
if(dap) cdb[1] += 0x02;
if(dap)
cdb[1] += 0x02;
cdb[3] = (byte)((startMsf & 0xFF0000) >> 16);
cdb[4] = (byte)((startMsf & 0xFF00) >> 8);
cdb[5] = (byte)(startMsf & 0xFF);
@@ -486,17 +483,25 @@ namespace DiscImageChef.Devices
cdb[8] = (byte)(endMsf & 0xFF);
cdb[9] = (byte)((byte)c2Error << 1);
cdb[9] += (byte)((byte)headerCodes << 5);
if(sync) cdb[9] += 0x80;
if(userData) cdb[9] += 0x10;
if(edcEcc) cdb[9] += 0x08;
if(sync)
cdb[9] += 0x80;
if(userData)
cdb[9] += 0x10;
if(edcEcc)
cdb[9] += 0x08;
cdb[10] = (byte)subchannel;
uint transferLength = (uint)((cdb[6] - cdb[3]) * 60 * 75 + (cdb[7] - cdb[4]) * 75 + (cdb[8] - cdb[5]));
uint transferLength = (uint)(((cdb[6] - cdb[3]) * 60 * 75) + ((cdb[7] - cdb[4]) * 75) + (cdb[8] - cdb[5]));
buffer = new byte[blockSize * transferLength];
LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration,
out bool sense);
Error = LastError != 0;
DicConsole.DebugWriteLine("SCSI Device", "READ CD MSF took {0} ms.", duration);
@@ -518,11 +523,16 @@ namespace DiscImageChef.Devices
byte[] buffer = new byte[0];
cdb[0] = (byte)ScsiCommands.PreventAllowMediumRemoval;
if(prevent) cdb[4] += 0x01;
if(persistent) cdb[4] += 0x02;
if(prevent)
cdb[4] += 0x01;
if(persistent)
cdb[4] += 0x02;
LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration,
out bool sense);
Error = LastError != 0;
DicConsole.DebugWriteLine("SCSI Device", "PREVENT ALLOW MEDIUM REMOVAL took {0} ms.", duration);
@@ -542,16 +552,18 @@ namespace DiscImageChef.Devices
public bool StopUnit(out byte[] senseBuffer, uint timeout, out double duration) =>
StartStopUnit(out senseBuffer, false, 0, 0, false, false, false, timeout, out duration);
public bool StartStopUnit(out byte[] senseBuffer, bool immediate, byte formatLayer, byte powerConditions,
bool changeFormatLayer, bool loadEject, bool start, uint timeout,
out double duration)
public bool StartStopUnit(out byte[] senseBuffer, bool immediate, byte formatLayer, byte powerConditions,
bool changeFormatLayer, bool loadEject, bool start, uint timeout, out double duration)
{
senseBuffer = new byte[32];
byte[] cdb = new byte[6];
byte[] buffer = new byte[0];
cdb[0] = (byte)ScsiCommands.StartStopUnit;
if(immediate) cdb[1] += 0x01;
if(immediate)
cdb[1] += 0x01;
if(changeFormatLayer)
{
cdb[3] = (byte)(formatLayer & 0x03);
@@ -559,14 +571,18 @@ namespace DiscImageChef.Devices
}
else
{
if(loadEject) cdb[4] += 0x02;
if(start) cdb[4] += 0x01;
if(loadEject)
cdb[4] += 0x02;
if(start)
cdb[4] += 0x01;
}
cdb[4] += (byte)((powerConditions & 0x0F) << 4);
LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration,
out bool sense);
Error = LastError != 0;
DicConsole.DebugWriteLine("SCSI Device", "START STOP UNIT took {0} ms.", duration);
@@ -592,17 +608,19 @@ namespace DiscImageChef.Devices
LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration,
out bool sense);
Error = LastError != 0;
DicConsole.DebugWriteLine("SCSI Device", "READ READ SUB-CHANNEL (MCN) took {0} ms.", duration);
if(!sense && (buffer[8] & 0x80) == 0x80) mcn = Encoding.ASCII.GetString(buffer, 9, 13);
if(!sense &&
(buffer[8] & 0x80) == 0x80)
mcn = Encoding.ASCII.GetString(buffer, 9, 13);
return sense;
}
public bool ReadIsrc(byte trackNumber, out string isrc, out byte[] buffer, out byte[] senseBuffer,
uint timeout,
public bool ReadIsrc(byte trackNumber, out string isrc, out byte[] buffer, out byte[] senseBuffer, uint timeout,
out double duration)
{
senseBuffer = new byte[32];
@@ -621,11 +639,38 @@ namespace DiscImageChef.Devices
LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration,
out bool sense);
Error = LastError != 0;
DicConsole.DebugWriteLine("SCSI Device", "READ READ SUB-CHANNEL (ISRC) took {0} ms.", duration);
if(!sense && (buffer[8] & 0x80) == 0x80) isrc = Encoding.ASCII.GetString(buffer, 9, 12);
if(!sense &&
(buffer[8] & 0x80) == 0x80)
isrc = Encoding.ASCII.GetString(buffer, 9, 12);
return sense;
}
public bool SetCdSpeed(out byte[] senseBuffer, RotationalControl rotationalControl, ushort readSpeed,
ushort writeSpeed, uint timeout, out double duration)
{
senseBuffer = new byte[32];
byte[] cdb = new byte[12];
byte[] buffer = new byte[0];
cdb[0] = (byte)ScsiCommands.SetCdSpeed;
cdb[1] = (byte)((byte)rotationalControl & 0x03);
cdb[2] = (byte)((readSpeed & 0xFF00) >> 8);
cdb[3] = (byte)(readSpeed & 0xFF);
cdb[4] = (byte)((writeSpeed & 0xFF00) >> 8);
cdb[5] = (byte)(writeSpeed & 0xFF);
LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration,
out bool sense);
Error = LastError != 0;
DicConsole.DebugWriteLine("SCSI Device", "SET CD SPEED took {0} ms.", duration);
return sense;
}

File diff suppressed because it is too large Load Diff

View File

@@ -54,16 +54,18 @@ using Schemas;
using DeviceInfo = DiscImageChef.Core.Devices.Info.DeviceInfo;
using MediaType = DiscImageChef.CommonTypes.MediaType;
// ReSharper disable UnusedMember.Local
namespace DiscImageChef.Gui.Forms
{
public class frmDump : Form
{
readonly string devicePath;
Device dev;
Dump dumper;
string outputPrefix;
Resume resume;
CICMMetadataType sidecar;
readonly string _devicePath;
Device _dev;
Dump _dumper;
string _outputPrefix;
Resume _resume;
CICMMetadataType _sidecar;
public frmDump(string devicePath, DeviceInfo deviceInfo, ScsiInfo scsiInfo = null)
{
@@ -189,7 +191,7 @@ namespace DiscImageChef.Gui.Forms
break;
}
this.devicePath = devicePath;
_devicePath = devicePath;
}
void OnCmbFormatSelectedIndexChanged(object sender, EventArgs e)
@@ -318,7 +320,7 @@ namespace DiscImageChef.Gui.Forms
if(result != DialogResult.Ok)
{
txtDestination.Text = "";
outputPrefix = null;
_outputPrefix = null;
return;
}
@@ -328,8 +330,8 @@ namespace DiscImageChef.Gui.Forms
txtDestination.Text = dlgDestination.FileName;
outputPrefix = Path.Combine(Path.GetDirectoryName(dlgDestination.FileName),
Path.GetFileNameWithoutExtension(dlgDestination.FileName));
_outputPrefix = Path.Combine(Path.GetDirectoryName(dlgDestination.FileName),
Path.GetFileNameWithoutExtension(dlgDestination.FileName));
chkResume.Checked = true;
}
@@ -344,7 +346,7 @@ namespace DiscImageChef.Gui.Forms
{
if(chkExistingMetadata.Checked == false)
{
sidecar = null;
_sidecar = null;
return;
}
@@ -370,7 +372,7 @@ namespace DiscImageChef.Gui.Forms
try
{
var sr = new StreamReader(dlgMetadata.FileName);
sidecar = (CICMMetadataType)sidecarXs.Deserialize(sr);
_sidecar = (CICMMetadataType)sidecarXs.Deserialize(sr);
sr.Close();
}
catch
@@ -385,19 +387,19 @@ namespace DiscImageChef.Gui.Forms
if(chkResume.Checked == false)
return;
if(outputPrefix != null)
if(_outputPrefix != null)
CheckResumeFile();
}
void CheckResumeFile()
{
resume = null;
_resume = null;
var xs = new XmlSerializer(typeof(Resume));
try
{
var sr = new StreamReader(outputPrefix + ".resume.xml");
resume = (Resume)xs.Deserialize(sr);
var sr = new StreamReader(_outputPrefix + ".resume.xml");
_resume = (Resume)xs.Deserialize(sr);
sr.Close();
}
catch
@@ -408,9 +410,9 @@ namespace DiscImageChef.Gui.Forms
return;
}
if(resume == null ||
resume.NextBlock <= resume.LastBlock ||
(resume.BadBlocks.Count != 0 && !resume.Tape))
if(_resume == null ||
_resume.NextBlock <= _resume.LastBlock ||
(_resume.BadBlocks.Count != 0 && !_resume.Tape))
return;
MessageBox.Show("Media already dumped correctly, please choose another destination...",
@@ -424,7 +426,7 @@ namespace DiscImageChef.Gui.Forms
void OnBtnStopClick(object sender, EventArgs e)
{
btnStop.Enabled = false;
dumper.Abort();
_dumper.Abort();
}
void OnBtnDumpClick(object sender, EventArgs e)
@@ -442,15 +444,15 @@ namespace DiscImageChef.Gui.Forms
try
{
dev = new Device(devicePath);
_dev = new Device(_devicePath);
if(dev.IsRemote)
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem,
dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture);
if(_dev.IsRemote)
Statistics.AddRemote(_dev.RemoteApplication, _dev.RemoteVersion, _dev.RemoteOperatingSystem,
_dev.RemoteOperatingSystemVersion, _dev.RemoteArchitecture);
if(dev.Error)
if(_dev.Error)
{
StoppingErrorMessage($"Error {dev.LastError} opening device.");
StoppingErrorMessage($"Error {_dev.LastError} opening device.");
return;
}
@@ -462,7 +464,7 @@ namespace DiscImageChef.Gui.Forms
return;
}
Statistics.AddDevice(dev);
Statistics.AddDevice(_dev);
Statistics.AddCommand("dump-media");
if(!(cmbFormat.SelectedValue is IWritableImage outputFormat))
@@ -515,36 +517,37 @@ namespace DiscImageChef.Gui.Forms
parsedOptions.Add(key, value);
}
var dumpLog = new DumpLog(outputPrefix + ".log", dev);
var dumpLog = new DumpLog(_outputPrefix + ".log", _dev);
dumpLog.WriteLine("Output image format: {0}.", outputFormat.Name);
dumper = new Dump(chkResume.Checked == true, dev, devicePath, outputFormat, (ushort)stpRetries.Value,
chkForce.Checked == true, false, chkPersistent.Checked == true,
chkStopOnError.Checked == true, resume, dumpLog, encoding, outputPrefix,
txtDestination.Text, parsedOptions, sidecar, (uint)stpSkipped.Value,
chkExistingMetadata.Checked == false, chkTrim.Checked == false,
chkTrack1Pregap.Checked == true);
_dumper = new Dump(chkResume.Checked == true, _dev, _devicePath, outputFormat,
(ushort)stpRetries.Value,
chkForce.Checked == true, false, chkPersistent.Checked == true,
chkStopOnError.Checked == true, _resume, dumpLog, encoding, _outputPrefix,
txtDestination.Text, parsedOptions, _sidecar, (uint)stpSkipped.Value,
chkExistingMetadata.Checked == false, chkTrim.Checked == false,
chkTrack1Pregap.Checked == true, true, false, DumpSubchannel.Any, 0);
new Thread(DoWork).Start();
}
void DoWork()
{
dumper.UpdateStatus += UpdateStatus;
dumper.ErrorMessage += ErrorMessage;
dumper.StoppingErrorMessage += StoppingErrorMessage;
dumper.PulseProgress += PulseProgress;
dumper.InitProgress += InitProgress;
dumper.UpdateProgress += UpdateProgress;
dumper.EndProgress += EndProgress;
dumper.InitProgress2 += InitProgress2;
dumper.UpdateProgress2 += UpdateProgress2;
dumper.EndProgress2 += EndProgress2;
_dumper.UpdateStatus += UpdateStatus;
_dumper.ErrorMessage += ErrorMessage;
_dumper.StoppingErrorMessage += StoppingErrorMessage;
_dumper.PulseProgress += PulseProgress;
_dumper.InitProgress += InitProgress;
_dumper.UpdateProgress += UpdateProgress;
_dumper.EndProgress += EndProgress;
_dumper.InitProgress2 += InitProgress2;
_dumper.UpdateProgress2 += UpdateProgress2;
_dumper.EndProgress2 += EndProgress2;
dumper.Start();
_dumper.Start();
dev.Close();
_dev.Close();
WorkFinished();
}
@@ -643,6 +646,7 @@ namespace DiscImageChef.Gui.Forms
}
#region XAML IDs
// ReSharper disable InconsistentNaming
ComboBox cmbFormat;
TextBox txtDestination;
Button btnDestination;
@@ -673,6 +677,8 @@ namespace DiscImageChef.Gui.Forms
Label lblProgress2;
ProgressBar prgProgress2;
StackLayout stkOptions;
// ReSharper restore InconsistentNaming
#endregion
}
}

View File

@@ -35,8 +35,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscImageChef.Database", "D
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscImageChef.Gui", "DiscImageChef.Gui\DiscImageChef.Gui.csproj", "{CF61AD81-3F98-4E7B-8F00-9957A6CDE5FA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscImageChef.EntityFramework", "DiscImageChef.EntityFramework\DiscImageChef.EntityFramework.csproj", "{F73EE2BD-6009-4590-9F5A-68198CA5C0DA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscImageChef.Dto", "DiscImageChef.Dto\DiscImageChef.Dto.csproj", "{F4399FF5-9BD0-475A-9EA7-3DAE45291FE2}"
EndProject
Global
@@ -175,14 +173,6 @@ Global
{CF61AD81-3F98-4E7B-8F00-9957A6CDE5FA}.Release|Any CPU.Build.0 = Release|Any CPU
{CF61AD81-3F98-4E7B-8F00-9957A6CDE5FA}.Release|x86.ActiveCfg = Release|Any CPU
{CF61AD81-3F98-4E7B-8F00-9957A6CDE5FA}.Release|x86.Build.0 = Release|Any CPU
{F73EE2BD-6009-4590-9F5A-68198CA5C0DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F73EE2BD-6009-4590-9F5A-68198CA5C0DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F73EE2BD-6009-4590-9F5A-68198CA5C0DA}.Debug|x86.ActiveCfg = Debug|Any CPU
{F73EE2BD-6009-4590-9F5A-68198CA5C0DA}.Debug|x86.Build.0 = Debug|Any CPU
{F73EE2BD-6009-4590-9F5A-68198CA5C0DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F73EE2BD-6009-4590-9F5A-68198CA5C0DA}.Release|Any CPU.Build.0 = Release|Any CPU
{F73EE2BD-6009-4590-9F5A-68198CA5C0DA}.Release|x86.ActiveCfg = Release|Any CPU
{F73EE2BD-6009-4590-9F5A-68198CA5C0DA}.Release|x86.Build.0 = Release|Any CPU
{F4399FF5-9BD0-475A-9EA7-3DAE45291FE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F4399FF5-9BD0-475A-9EA7-3DAE45291FE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4399FF5-9BD0-475A-9EA7-3DAE45291FE2}.Debug|x86.ActiveCfg = Debug|Any CPU

View File

@@ -185,24 +185,52 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=ASCQ/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ATAPI/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ATIP/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=BANDAI/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Bluray/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=CDDA/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=CDTV/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=cdrom/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=certance/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=checksumming/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=checksums/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=cicm/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Claunia/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=DDCD/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Drive_0027s/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=EVPD/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Hldtst/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=iomega/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=isrc/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Kreon/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=leadout/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=mhdd/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=mhddlog/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=milcd/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=MINIX/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=NTFS/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=NTSC/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nuon/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=opticals/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PCMCIA/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Plextor/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pframe/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=phour/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=playdia/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Plextor/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pmin/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Portillo/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pregap/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pregaps/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=psec/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=readcd/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Recordables/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Reiser/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=SDHCI/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=subchannels/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=subpages/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=subchannel/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=subchannels/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=umounting/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=xeto/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Wxripper/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=xeto/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Xtreme/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -52,81 +52,90 @@ using PlatformID = DiscImageChef.CommonTypes.Interop.PlatformID;
namespace DiscImageChef.Commands
{
// TODO: Add raw dumping
internal class DumpMediaCommand : Command
{
string cicmXml;
string devicePath;
bool doResume = true;
string encodingName;
bool firstTrackPregap;
bool force;
bool noMetadata;
bool noTrim;
string outputFile;
string outputOptions;
bool persistent;
// TODO: Add raw dumping
ushort retryPasses = 5;
bool showHelp;
int skip = 512;
bool stopOnError;
string wantedOutputFormat;
string _cicmXml;
string _devicePath;
bool _doResume = true;
string _encodingName;
bool _firstTrackPregap;
bool _fixOffset = true;
bool _force;
bool _noMetadata;
bool _noTrim;
string _outputFile;
string _outputOptions;
bool _persistent;
ushort _retryPasses = 5;
bool _showHelp;
int _skip = 512;
int _speed;
bool _stopOnError;
string _wantedOutputFormat;
string _wantedSubchannel;
public DumpMediaCommand() : base("dump-media", "Dumps the media inserted on a device to a media image.")
{
Options = new OptionSet
{
$"{MainClass.AssemblyTitle} {MainClass.AssemblyVersion?.InformationalVersion}",
$"{MainClass.AssemblyCopyright}", "", $"usage: DiscImageChef {Name} [OPTIONS] devicepath outputimage",
$"{MainClass.AssemblyCopyright}", "", $"usage: DiscImageChef {Name} [OPTIONS] device_path output_image",
"", Help,
{
"cicm-xml|x=", "Take metadata from existing CICM XML sidecar.", s => cicmXml = s
"cicm-xml|x=", "Take metadata from existing CICM XML sidecar.", s => _cicmXml = s
},
{
"encoding|e=", "Name of character encoding to use.", s => encodingName = s
"encoding|e=", "Name of character encoding to use.", s => _encodingName = s
}
};
if(DetectOS.GetRealPlatformID() != PlatformID.FreeBSD)
Options.Add("first-pregap", "Try to read first track pregap. Only applicable to CD/DDCD/GD.",
b => firstTrackPregap = b != null);
b => _firstTrackPregap = b != null);
Options.Add("force|f", "Continue dump whatever happens.", b => force = b != null);
Options.Add("fix-offset", "Fix audio tracks offset. Only applicable to CD/GD.",
b => _fixOffset = b != null);
Options.Add("force|f", "Continue dump whatever happens.", b => _force = b != null);
Options.Add("format|t=",
"Format of the output image, as plugin name or plugin id. If not present, will try to detect it from output image extension.",
s => wantedOutputFormat = s);
s => _wantedOutputFormat = s);
Options.Add("no-metadata", "Disables creating CICM XML sidecar.", b => noMetadata = b != null);
Options.Add("no-trim", "Disables trimming errored from skipped sectors.", b => noTrim = b != null);
Options.Add("no-metadata", "Disables creating CICM XML sidecar.", b => _noMetadata = b != null);
Options.Add("no-trim", "Disables trimming errored from skipped sectors.", b => _noTrim = b != null);
Options.Add("options|O=", "Comma separated name=value pairs of options to pass to output image plugin.",
s => outputOptions = s);
s => _outputOptions = s);
Options.Add("persistent", "Try to recover partial or incorrect data.", b => persistent = b != null);
Options.Add("persistent", "Try to recover partial or incorrect data.", b => _persistent = b != null);
Options.Add("resume|r", "Create/use resume mapfile.", b => _doResume = b != null);
Options.Add("retry-passes|p=", "How many retry passes to do.", (ushort us) => _retryPasses = us);
Options.Add("skip|k=", "When an unreadable sector is found skip this many sectors.", (int i) => _skip = i);
Options.Add("stop-on-error|s", "Stop media dump on first error.", b => _stopOnError = b != null);
Options.Add("help|h|?", "Show this message and exit.", v => _showHelp = v != null);
Options.Add("subchannel",
"Subchannel to dump. Only applicable to CD/GD. Values: any, rw, rw-or-pq, pq, none",
s => _wantedSubchannel = s);
Options.Add("speed", "Speed to dump. Only applicable to optical drives, 0 for maximum",
(int i) => _speed = i);
/* TODO: Disabled temporarily
Options.Add("raw|r", "Dump sectors with tags included. For optical media, dump scrambled sectors.", (b) => raw = b != null);*/
Options.Add("resume|r", "Create/use resume mapfile.",
b => doResume = b != null);
Options.Add("retry-passes|p=", "How many retry passes to do.", (ushort us) => retryPasses = us);
Options.Add("skip|k=", "When an unreadable sector is found skip this many sectors.", (int i) => skip = i);
Options.Add("stop-on-error|s", "Stop media dump on first error.",
b => stopOnError = b != null);
Options.Add("help|h|?", "Show this message and exit.",
v => showHelp = v != null);
}
public override int Invoke(IEnumerable<string> arguments)
{
List<string> extra = Options.Parse(arguments);
if(showHelp)
if(_showHelp)
{
Options.WriteOptionDescriptions(CommandSet.Out);
@@ -157,31 +166,32 @@ namespace DiscImageChef.Commands
return(int)ErrorNumber.MissingArgument;
}
devicePath = extra[0];
outputFile = extra[1];
_devicePath = extra[0];
_outputFile = extra[1];
DicConsole.DebugWriteLine("Dump-Media command", "--cicm-xml={0}", cicmXml);
DicConsole.DebugWriteLine("Dump-Media command", "--cicm-xml={0}", _cicmXml);
DicConsole.DebugWriteLine("Dump-Media command", "--debug={0}", MainClass.Debug);
DicConsole.DebugWriteLine("Dump-Media command", "--device={0}", devicePath);
DicConsole.DebugWriteLine("Dump-Media command", "--encoding={0}", encodingName);
DicConsole.DebugWriteLine("Dump-Media command", "--first-pregap={0}", firstTrackPregap);
DicConsole.DebugWriteLine("Dump-Media command", "--force={0}", force);
DicConsole.DebugWriteLine("Dump-Media command", "--force={0}", force);
DicConsole.DebugWriteLine("Dump-Media command", "--format={0}", wantedOutputFormat);
DicConsole.DebugWriteLine("Dump-Media command", "--no-metadata={0}", noMetadata);
DicConsole.DebugWriteLine("Dump-Media command", "--device={0}", _devicePath);
DicConsole.DebugWriteLine("Dump-Media command", "--encoding={0}", _encodingName);
DicConsole.DebugWriteLine("Dump-Media command", "--first-pregap={0}", _firstTrackPregap);
DicConsole.DebugWriteLine("Dump-Media command", "--force={0}", _force);
DicConsole.DebugWriteLine("Dump-Media command", "--force={0}", _force);
DicConsole.DebugWriteLine("Dump-Media command", "--format={0}", _wantedOutputFormat);
DicConsole.DebugWriteLine("Dump-Media command", "--no-metadata={0}", _noMetadata);
DicConsole.DebugWriteLine("Dump-Media command", "--options={0}", Options);
DicConsole.DebugWriteLine("Dump-Media command", "--output={0}", outputFile);
DicConsole.DebugWriteLine("Dump-Media command", "--persistent={0}", persistent);
DicConsole.DebugWriteLine("Dump-Media command", "--output={0}", _outputFile);
DicConsole.DebugWriteLine("Dump-Media command", "--persistent={0}", _persistent);
DicConsole.DebugWriteLine("Dump-Media command", "--resume={0}", _doResume);
DicConsole.DebugWriteLine("Dump-Media command", "--retry-passes={0}", _retryPasses);
DicConsole.DebugWriteLine("Dump-Media command", "--skip={0}", _skip);
DicConsole.DebugWriteLine("Dump-Media command", "--stop-on-error={0}", _stopOnError);
DicConsole.DebugWriteLine("Dump-Media command", "--verbose={0}", MainClass.Verbose);
DicConsole.DebugWriteLine("Dump-Media command", "--subchannel={0}", _wantedSubchannel);
// TODO: Disabled temporarily
//DicConsole.DebugWriteLine("Dump-Media command", "--raw={0}", raw);
DicConsole.DebugWriteLine("Dump-Media command", "--resume={0}", doResume);
DicConsole.DebugWriteLine("Dump-Media command", "--retry-passes={0}", retryPasses);
DicConsole.DebugWriteLine("Dump-Media command", "--skip={0}", skip);
DicConsole.DebugWriteLine("Dump-Media command", "--stop-on-error={0}", stopOnError);
DicConsole.DebugWriteLine("Dump-Media command", "--verbose={0}", MainClass.Verbose);
Dictionary<string, string> parsedOptions = Core.Options.Parse(outputOptions);
Dictionary<string, string> parsedOptions = Core.Options.Parse(_outputOptions);
DicConsole.DebugWriteLine("Dump-Media command", "Parsed options:");
foreach(KeyValuePair<string, string> parsedOption in parsedOptions)
@@ -189,10 +199,10 @@ namespace DiscImageChef.Commands
Encoding encoding = null;
if(encodingName != null)
if(_encodingName != null)
try
{
encoding = Claunia.Encoding.Encoding.GetEncoding(encodingName);
encoding = Claunia.Encoding.Encoding.GetEncoding(_encodingName);
if(MainClass.Verbose)
DicConsole.VerboseWriteLine("Using encoding for {0}.", encoding.EncodingName);
@@ -204,17 +214,48 @@ namespace DiscImageChef.Commands
return(int)ErrorNumber.EncodingUnknown;
}
if(devicePath.Length == 2 &&
devicePath[1] == ':' &&
devicePath[0] != '/' &&
char.IsLetter(devicePath[0]))
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
DumpSubchannel subchannel = DumpSubchannel.Any;
switch(_wantedSubchannel?.ToLowerInvariant())
{
case"any":
case null:
subchannel = DumpSubchannel.Any;
break;
case"rw":
subchannel = DumpSubchannel.Rw;
break;
case"rw-or-pq":
subchannel = DumpSubchannel.RwOrPq;
break;
case"pq":
subchannel = DumpSubchannel.Pq;
break;
case"none":
subchannel = DumpSubchannel.None;
break;
default:
DicConsole.WriteLine("Incorrect subchannel type \"{0}\" requested.", _wantedSubchannel);
break;
}
if(_devicePath.Length == 2 &&
_devicePath[1] == ':' &&
_devicePath[0] != '/' &&
char.IsLetter(_devicePath[0]))
_devicePath = "\\\\.\\" + char.ToUpper(_devicePath[0]) + ':';
Device dev;
try
{
dev = new Device(devicePath);
dev = new Device(_devicePath);
if(dev.IsRemote)
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem,
@@ -236,13 +277,13 @@ namespace DiscImageChef.Commands
Statistics.AddDevice(dev);
string outputPrefix = Path.Combine(Path.GetDirectoryName(outputFile),
Path.GetFileNameWithoutExtension(outputFile));
string outputPrefix = Path.Combine(Path.GetDirectoryName(_outputFile),
Path.GetFileNameWithoutExtension(_outputFile));
Resume resume = null;
var xs = new XmlSerializer(typeof(Resume));
if(File.Exists(outputPrefix + ".resume.xml") && doResume)
if(File.Exists(outputPrefix + ".resume.xml") && _doResume)
try
{
var sr = new StreamReader(outputPrefix + ".resume.xml");
@@ -269,12 +310,12 @@ namespace DiscImageChef.Commands
CICMMetadataType sidecar = null;
var sidecarXs = new XmlSerializer(typeof(CICMMetadataType));
if(cicmXml != null)
if(File.Exists(cicmXml))
if(_cicmXml != null)
if(File.Exists(_cicmXml))
{
try
{
var sr = new StreamReader(cicmXml);
var sr = new StreamReader(_cicmXml);
sidecar = (CICMMetadataType)sidecarXs.Deserialize(sr);
sr.Close();
}
@@ -296,18 +337,19 @@ namespace DiscImageChef.Commands
List<IWritableImage> candidates = new List<IWritableImage>();
// Try extension
if(string.IsNullOrEmpty(wantedOutputFormat))
if(string.IsNullOrEmpty(_wantedOutputFormat))
candidates.AddRange(plugins.WritableImages.Values.Where(t =>
t.KnownExtensions.
Contains(Path.GetExtension(outputFile))));
Contains(Path.
GetExtension(_outputFile))));
// Try Id
else if(Guid.TryParse(wantedOutputFormat, out Guid outId))
else if(Guid.TryParse(_wantedOutputFormat, 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, wantedOutputFormat,
candidates.AddRange(plugins.WritableImages.Values.Where(t => string.Equals(t.Name, _wantedOutputFormat,
StringComparison.
InvariantCultureIgnoreCase)));
@@ -340,9 +382,10 @@ namespace DiscImageChef.Commands
DicConsole.WriteLine("Output image format: {0}.", outputFormat.Name);
}
var dumper = new Dump(doResume, dev, devicePath, outputFormat, retryPasses, force, false, persistent,
stopOnError, resume, dumpLog, encoding, outputPrefix, outputFile, parsedOptions,
sidecar, (uint)skip, noMetadata, noTrim, firstTrackPregap);
var dumper = new Dump(_doResume, dev, _devicePath, outputFormat, _retryPasses, _force, false, _persistent,
_stopOnError, resume, dumpLog, encoding, outputPrefix, _outputFile, parsedOptions,
sidecar, (uint)_skip, _noMetadata, _noTrim, _firstTrackPregap, _fixOffset,
MainClass.Debug, subchannel, _speed);
dumper.UpdateStatus += Progress.UpdateStatus;
dumper.ErrorMessage += Progress.ErrorMessage;