mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
Merge pull request #261 from discimagechef/cd_dump_rewrite
Compact Disc dump rewrite
This commit is contained in:
27
.idea/.idea.DiscImageChef/.idea/contentModel.xml
generated
27
.idea/.idea.DiscImageChef/.idea/contentModel.xml
generated
@@ -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" />
|
||||
|
||||
@@ -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
382
DiscImageChef.Core/Devices/Dumping/CompactDisc/Data.cs
Normal file
382
DiscImageChef.Core/Devices/Dumping/CompactDisc/Data.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
985
DiscImageChef.Core/Devices/Dumping/CompactDisc/Dump.cs
Normal file
985
DiscImageChef.Core/Devices/Dumping/CompactDisc/Dump.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
411
DiscImageChef.Core/Devices/Dumping/CompactDisc/Error.cs
Normal file
411
DiscImageChef.Core/Devices/Dumping/CompactDisc/Error.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
347
DiscImageChef.Core/Devices/Dumping/CompactDisc/LeadOuts.cs
Normal file
347
DiscImageChef.Core/Devices/Dumping/CompactDisc/LeadOuts.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
232
DiscImageChef.Core/Devices/Dumping/CompactDisc/Pregap.cs
Normal file
232
DiscImageChef.Core/Devices/Dumping/CompactDisc/Pregap.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
87
DiscImageChef.Core/Devices/Dumping/CompactDisc/Subchannel.cs
Normal file
87
DiscImageChef.Core/Devices/Dumping/CompactDisc/Subchannel.cs
Normal 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 _);
|
||||
}
|
||||
}
|
||||
}
|
||||
147
DiscImageChef.Core/Devices/Dumping/CompactDisc/Tags.cs
Normal file
147
DiscImageChef.Core/Devices/Dumping/CompactDisc/Tags.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
294
DiscImageChef.Core/Devices/Dumping/CompactDisc/Tracks.cs
Normal file
294
DiscImageChef.Core/Devices/Dumping/CompactDisc/Tracks.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
210
DiscImageChef.Core/Devices/Dumping/CompactDisc/Trim.cs
Normal file
210
DiscImageChef.Core/Devices/Dumping/CompactDisc/Trim.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
138
DiscImageChef.Core/Devices/Dumping/Metadata.cs
Normal file
138
DiscImageChef.Core/Devices/Dumping/Metadata.cs
Normal 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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
236
DiscImageChef.Core/Media/Info/CompactDisc.cs
Normal file
236
DiscImageChef.Core/Media/Info/CompactDisc.cs
Normal 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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user