Rewrite CompactDisc dumping code. Now supports writable images,

track flags, multisession, correct sidecars when multiple tracks present,
and adjust subchannel to drive capabilities.
This commit is contained in:
2018-01-20 17:12:01 +00:00
parent 28d711200c
commit dfe6a32fc6
2 changed files with 490 additions and 413 deletions

View File

@@ -32,18 +32,33 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using DiscImageChef.Console;
using DiscImageChef.Core.Logging; using DiscImageChef.Core.Logging;
using DiscImageChef.Decoders.CD;
using DiscImageChef.Decoders.SCSI;
using DiscImageChef.Decoders.SCSI.MMC;
using DiscImageChef.Devices; using DiscImageChef.Devices;
using DiscImageChef.DiscImages; using DiscImageChef.DiscImages;
using DiscImageChef.Filters;
using DiscImageChef.Metadata; using DiscImageChef.Metadata;
using Extents;
using Schemas;
using MediaType = DiscImageChef.CommonTypes.MediaType; using MediaType = DiscImageChef.CommonTypes.MediaType;
using PlatformID = DiscImageChef.Interop.PlatformID;
using Session = DiscImageChef.Decoders.CD.Session;
using TrackType = DiscImageChef.DiscImages.TrackType;
namespace DiscImageChef.Core.Devices.Dumping namespace DiscImageChef.Core.Devices.Dumping
{ {
/// <summary> /// <summary>
/// Implement dumping Compact Discs /// Implement dumping Compact Discs
/// </summary> /// </summary>
class CompactDisc // TODO: ISRC, MCN and pregaps
static class CompactDisc
{ {
/// <summary> /// <summary>
/// Dumps a compact disc /// Dumps a compact disc
@@ -63,6 +78,7 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="dumpLeadIn">Try to read and dump as much Lead-in as possible</param> /// <param name="dumpLeadIn">Try to read and dump as much Lead-in as possible</param>
/// <param name="outputPath">Path to output file</param> /// <param name="outputPath">Path to output file</param>
/// <param name="formatOptions">Formats to pass to output file plugin</param> /// <param name="formatOptions">Formats to pass to output file plugin</param>
/// <param name="encoding">Encoding to use when analyzing dump</param>
/// <exception cref="NotImplementedException">If trying to dump scrambled sectors</exception> /// <exception cref="NotImplementedException">If trying to dump scrambled sectors</exception>
/// <exception cref="InvalidOperationException">If the resume file is invalid</exception> /// <exception cref="InvalidOperationException">If the resume file is invalid</exception>
/// <exception cref="ArgumentOutOfRangeException">If the track type is unknown (never)</exception> /// <exception cref="ArgumentOutOfRangeException">If the track type is unknown (never)</exception>
@@ -71,30 +87,32 @@ namespace DiscImageChef.Core.Devices.Dumping
ref MediaType dskType, ref MediaType dskType,
ref ref
Resume resume, ref DumpLog dumpLog, bool dumpLeadIn, Resume resume, ref DumpLog dumpLog, bool dumpLeadIn,
string outputPrefix, Encoding encoding,
string string
outputPath, Dictionary<string, string> formatOptions) outputPrefix, string outputPath, Dictionary<string, string> formatOptions)
{ {
throw new NotImplementedException("Dumping CompactDisc is disable pending rewrite.");
/*bool sense = false;
ulong blocks;
// TODO: Check subchannel support
uint blockSize;
uint subSize; uint subSize;
FullTOC.CDFullTOC? toc = null;
DateTime start; DateTime start;
DateTime end; DateTime end;
bool readcd;
bool sense = false;
const uint SECTOR_SIZE = 2352;
FullTOC.CDFullTOC? toc = null;
double totalDuration = 0; double totalDuration = 0;
double totalChkDuration = 0;
double currentSpeed = 0; double currentSpeed = 0;
double maxSpeed = double.MinValue; double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue; double minSpeed = double.MaxValue;
Checksum dataChk;
bool readcd;
uint blocksToRead = 64; uint blocksToRead = 64;
bool aborted = false; bool aborted = false;
System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true; System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;
Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>();
if(dumpRaw)
{
dumpLog.WriteLine("Raw CD dumping not yet implemented");
DicConsole.ErrorWriteLine("Raw CD dumping not yet implemented");
return;
}
// We discarded all discs that falsify a TOC before requesting a real TOC // We discarded all discs that falsify a TOC before requesting a real TOC
// No TOC, no CD (or an empty one) // No TOC, no CD (or an empty one)
@@ -107,13 +125,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{ {
byte[] tmpBuf = new byte[cmdBuf.Length - 2]; byte[] tmpBuf = new byte[cmdBuf.Length - 2];
Array.Copy(cmdBuf, 2, tmpBuf, 0, cmdBuf.Length - 2); Array.Copy(cmdBuf, 2, tmpBuf, 0, cmdBuf.Length - 2);
sidecar.OpticalDisc[0].TOC = new DumpType mediaTags.Add(MediaTagType.CD_FullTOC, tmpBuf);
{
Image = outputPrefix + ".toc.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].TOC.Image, tmpBuf);
// ATIP exists on blank CDs // ATIP exists on blank CDs
dumpLog.WriteLine("Reading ATIP"); dumpLog.WriteLine("Reading ATIP");
@@ -128,13 +140,7 @@ namespace DiscImageChef.Core.Devices.Dumping
tmpBuf = new byte[cmdBuf.Length - 4]; tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].ATIP = new DumpType mediaTags.Add(MediaTagType.CD_ATIP, tmpBuf);
{
Image = outputPrefix + ".atip.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].TOC.Image, tmpBuf);
} }
} }
@@ -210,13 +216,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{ {
tmpBuf = new byte[cmdBuf.Length - 4]; tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].PMA = new DumpType mediaTags.Add(MediaTagType.CD_PMA, tmpBuf);
{
Image = outputPrefix + ".pma.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].PMA.Image, tmpBuf);
} }
dumpLog.WriteLine("Reading CD-Text from Lead-In"); dumpLog.WriteLine("Reading CD-Text from Lead-In");
@@ -226,75 +226,130 @@ namespace DiscImageChef.Core.Devices.Dumping
{ {
tmpBuf = new byte[cmdBuf.Length - 4]; tmpBuf = new byte[cmdBuf.Length - 4];
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
sidecar.OpticalDisc[0].LeadInCdText = new DumpType mediaTags.Add(MediaTagType.CD_TEXT, tmpBuf);
{
Image = outputPrefix + ".cdtext.bin",
Size = tmpBuf.Length,
Checksums = Checksum.GetChecksums(tmpBuf).ToArray()
};
DataFile.WriteTo("SCSI Dump", sidecar.OpticalDisc[0].LeadInCdText.Image, tmpBuf);
} }
} }
} }
// TODO: Support variable subchannel kinds
blockSize = 2448;
subSize = 96;
int sectorSize;
if(separateSubchannel) sectorSize = (int)(blockSize - subSize);
else sectorSize = (int)blockSize;
if(toc == null) if(toc == null)
{ {
DicConsole.ErrorWriteLine("Error trying to decode TOC..."); DicConsole.ErrorWriteLine("Error trying to decode TOC...");
return; return;
} }
DiscImages.Session[] sessionsForAlcohol = new DiscImages.Session[toc.Value.LastCompleteSession]; MmcSubchannel supportedSubchannel = MmcSubchannel.Raw;
for(int i = 0; i < sessionsForAlcohol.Length; i++) dumpLog.WriteLine("Checking if drive supports full raw subchannel reading...");
DicConsole.WriteLine("Checking if drive supports full raw subchannel reading...");
readcd = !dev.ReadCd(out byte[] readBuffer, out senseBuf, 0, SECTOR_SIZE + 96, 1, MmcSectorTypes.AllTypes,
false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
supportedSubchannel, dev.Timeout, out _);
if(readcd)
{ {
sessionsForAlcohol[i].SessionSequence = (ushort)(i + 1); dumpLog.WriteLine("Full raw subchannel reading supported...");
sessionsForAlcohol[i].StartTrack = ushort.MaxValue; DicConsole.WriteLine("Full raw subchannel reading supported...");
subSize = 96;
} }
foreach(FullTOC.TrackDataDescriptor trk in else
toc.Value.TrackDescriptors.Where(trk => trk.POINT > 0 && trk.POINT < 0xA0 &&
trk.SessionNumber <= sessionsForAlcohol.Length))
{ {
if(trk.POINT < sessionsForAlcohol[trk.SessionNumber - 1].StartTrack) supportedSubchannel = MmcSubchannel.Q16;
sessionsForAlcohol[trk.SessionNumber - 1].StartTrack = trk.POINT; dumpLog.WriteLine("Checking if drive supports PQ subchannel reading...");
if(trk.POINT > sessionsForAlcohol[trk.SessionNumber - 1].EndTrack) readcd = !dev.ReadCd(out readBuffer, out senseBuf, 0, SECTOR_SIZE + 16, 1, MmcSectorTypes.AllTypes,
sessionsForAlcohol[trk.SessionNumber - 1].EndTrack = trk.POINT; false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
supportedSubchannel, dev.Timeout, out _);
if(readcd)
{
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!");
DicConsole.WriteLine("PQ subchannel reading supported...");
DicConsole
.WriteLine("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
subSize = 16;
}
else
{
supportedSubchannel = MmcSubchannel.None;
dumpLog.WriteLine("Checking if drive supports reading without subchannel...");
readcd = !dev.ReadCd(out readBuffer, out senseBuf, 0, SECTOR_SIZE, 1, MmcSectorTypes.AllTypes,
false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
supportedSubchannel, dev.Timeout, out _);
if(!readcd)
{
dumpLog.WriteLine("Cannot read from disc, not continuing...");
DicConsole.ErrorWriteLine("Cannot read from disc, not continuing...");
return;
} }
alcohol.AddSessions(sessionsForAlcohol); dumpLog.WriteLine("Drive can only 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!");
foreach(FullTOC.TrackDataDescriptor trk in toc.Value.TrackDescriptors) DicConsole.WriteLine("Drive can only read without subchannel...");
alcohol.AddTrack((byte)((trk.ADR << 4) & trk.CONTROL), trk.TNO, trk.POINT, trk.Min, trk.Sec, trk.Frame, DicConsole
trk.Zero, trk.PMIN, trk.PSEC, trk.PFRAME, trk.SessionNumber); .WriteLine("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
subSize = 0;
}
}
FullTOC.TrackDataDescriptor[] sortedTracks = FullTOC.TrackDataDescriptor[] sortedTracks =
toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray(); toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray();
List<TrackType> trackList = new List<TrackType>(); List<Track> trackList = new List<Track>();
long lastSector = 0; long lastSector = 0;
string lastMsf = null;
// Check if output format supports subchannels
if(!outputPlugin.SupportedSectorTags.Contains(SectorTagType.CdSectorSubchannel) &&
supportedSubchannel != MmcSubchannel.None)
{
DicConsole.WriteLine("Output format does not support subchannels, {0}continuing...",
force ? "" : "not ");
dumpLog.WriteLine("Output format does not support subchannels, {0}continuing...", force ? "" : "not ");
if(!force) return;
supportedSubchannel = MmcSubchannel.None;
subSize = 0;
}
TrackSubchannelType subType;
switch(supportedSubchannel)
{
case MmcSubchannel.None:
subType = TrackSubchannelType.None;
break;
case MmcSubchannel.Raw:
subType = TrackSubchannelType.Raw;
break;
case MmcSubchannel.Q16:
subType = TrackSubchannelType.Q16;
break;
default:
DicConsole.WriteLine("Handling subchannel type {0} not supported, exiting...", supportedSubchannel);
dumpLog.WriteLine("Handling subchannel type {0} not supported, exiting...", supportedSubchannel);
return;
}
uint blockSize = SECTOR_SIZE + subSize;
DicConsole.WriteLine("Building track map...");
dumpLog.WriteLine("Building track map...");
foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4)) foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4))
if(trk.POINT >= 0x01 && trk.POINT <= 0x63) if(trk.POINT >= 0x01 && trk.POINT <= 0x63)
trackList.Add(new Track
{ {
TrackType track = new TrackType TrackSequence = trk.POINT,
{ TrackSession = trk.SessionNumber,
Sequence = new TrackSequenceType {Session = trk.SessionNumber, TrackNumber = trk.POINT} TrackType =
}; (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
if((TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental) ? TrackType.Data
track.TrackType1 = TrackTypeTrackType.mode1; : TrackType.Audio,
else track.TrackType1 = TrackTypeTrackType.audio; TrackStartSector =
if(trk.PHOUR > 0) (ulong)(trk.PHOUR * 3600 * 75 + trk.PMIN * 60 * 75 + trk.PSEC * 75 + trk.PFRAME - 150),
track.StartMSF = string.Format("{3:D2}:{0:D2}:{1:D2}:{2:D2}", trk.PMIN, trk.PSEC, trk.PFRAME, TrackBytesPerSector = (int)SECTOR_SIZE,
trk.PHOUR); TrackRawBytesPerSector = (int)SECTOR_SIZE,
else track.StartMSF = $"{trk.PMIN:D2}:{trk.PSEC:D2}:{trk.PFRAME:D2}"; TrackSubchannelType = subType
track.StartSector = trk.PHOUR * 3600 * 75 + trk.PMIN * 60 * 75 + trk.PSEC * 75 + trk.PFRAME - 150; });
trackList.Add(track);
}
else if(trk.POINT == 0xA2) else if(trk.POINT == 0xA2)
{ {
int phour, pmin, psec, pframe; int phour, pmin, psec, pframe;
@@ -332,43 +387,14 @@ namespace DiscImageChef.Core.Devices.Dumping
phour = trk.PHOUR; phour = trk.PHOUR;
} }
lastMsf = phour > 0
? $"{phour:D2}:{pmin:D2}:{psec:D2}:{pframe:D2}"
: $"{pmin:D2}:{psec:D2}:{pframe:D2}";
lastSector = phour * 3600 * 75 + pmin * 60 * 75 + psec * 75 + pframe - 150; lastSector = phour * 3600 * 75 + pmin * 60 * 75 + psec * 75 + pframe - 150;
} }
TrackType[] tracks = trackList.ToArray(); Track[] tracks = trackList.ToArray();
for(int t = 1; t < tracks.Length; t++) for(int t = 1; t < tracks.Length; t++) tracks[t - 1].TrackEndSector = tracks[t].TrackStartSector - 1;
{
tracks[t - 1].EndSector = tracks[t].StartSector - 1;
int phour = 0, pmin = 0, psec = 0;
int pframe = (int)(tracks[t - 1].EndSector + 150);
if(pframe > 3600 * 75) tracks[tracks.Length - 1].TrackEndSector = (ulong)lastSector;
{ ulong blocks = (ulong)(lastSector + 1);
phour = pframe / (3600 * 75);
pframe -= phour * 3600 * 75;
}
if(pframe > 60 * 75)
{
pmin = pframe / (60 * 75);
pframe -= pmin * 60 * 75;
}
if(pframe > 75)
{
psec = pframe / 75;
pframe -= psec * 75;
}
tracks[t - 1].EndMSF = phour > 0
? $"{phour:D2}:{pmin:D2}:{psec:D2}:{pframe:D2}"
: $"{pmin:D2}:{psec:D2}:{pframe:D2}";
}
tracks[tracks.Length - 1].EndMSF = lastMsf;
tracks[tracks.Length - 1].EndSector = lastSector;
blocks = (ulong)(lastSector + 1);
if(blocks == 0) if(blocks == 0)
{ {
@@ -376,42 +402,97 @@ namespace DiscImageChef.Core.Devices.Dumping
return; return;
} }
if(dumpRaw) throw new NotImplementedException("Raw CD dumping not yet implemented"); // Check if output format supports all disc tags we have retrieved so far
// TODO: Check subchannel capabilities foreach(MediaTagType tag in mediaTags.Keys)
readcd = !dev.ReadCd(out byte[] readBuffer, out senseBuf, 0, blockSize, 1, MmcSectorTypes.AllTypes, false, {
false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, if(outputPlugin.SupportedMediaTags.Contains(tag)) continue;
MmcSubchannel.Raw, dev.Timeout, out _);
if(readcd) DicConsole.WriteLine("Using MMC READ CD command."); DicConsole.WriteLine("Output format does not support {0}, {1}continuing...", tag, force ? "" : "not ");
dumpLog.WriteLine("Output format does not support {0}, {1}continuing...", tag, force ? "" : "not ");
DumpHardwareType currentTry = null; if(!force) return;
ExtentsULong extents = null; }
ResumeSupport.Process(true, true, blocks, dev.Manufacturer, dev.Model, dev.Serial, dev.PlatformId,
ref resume, ref currentTry, ref extents);
if(currentTry == null || extents == null)
throw new InvalidOperationException("Could not process resume file, not continuing...");
// Check mode for tracks
for(int t = 0; t < tracks.Length; t++)
{
if(tracks[t].TrackType == TrackType.Audio) continue;
dumpLog.WriteLine("Checking mode for track {0}...", tracks[t].TrackSequence);
DicConsole.WriteLine("Checking mode for track {0}...", tracks[t].TrackSequence);
readcd = !dev.ReadCd(out readBuffer, out senseBuf, (uint)tracks[t].TrackStartSector, blockSize, 1,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
MmcErrorField.None, supportedSubchannel, dev.Timeout, out _);
if(!readcd)
{
dumpLog.WriteLine("Unable to guess mode for track {0}, continuing...", tracks[t].TrackSequence);
DicConsole.WriteLine("Unable to guess mode for track {0}, continuing...", tracks[t].TrackSequence);
continue;
}
switch(readBuffer[15])
{
case 1:
DicConsole.WriteLine("Track {0} is MODE1", tracks[t].TrackSequence);
dumpLog.WriteLine("Track {0} is MODE1", tracks[t].TrackSequence);
tracks[t].TrackType = TrackType.CdMode1;
break;
case 2:
DicConsole.WriteLine("Track {0} is MODE2", tracks[t].TrackSequence);
dumpLog.WriteLine("Track {0} is MODE2", tracks[t].TrackSequence);
tracks[t].TrackType = TrackType.CdMode2Formless;
break;
default:
DicConsole.WriteLine("Track {0} is unknown mode {1}", tracks[t].TrackSequence, readBuffer[15]);
dumpLog.WriteLine("Track {0} is unknown mode {1}", tracks[t].TrackSequence, readBuffer[15]);
break;
}
}
// Check if something prevents from dumping the Lead-in
if(dumpLeadIn)
{
if(dev.PlatformId == PlatformID.FreeBSD)
{
dumpLog.WriteLine("FreeBSD panics when reading CD Lead-in, see upstream bug #224253. {0}continuing",
force ? "" : "Not ");
DicConsole
.ErrorWriteLine("FreeBSD panics when reading CD Lead-in, see upstream bug #224253. {0}continuing",
force ? "" : "Not ");
if(!force) return;
dumpLeadIn = false;
}
if(!outputPlugin.SupportedMediaTags.Contains(MediaTagType.CD_LeadIn))
{
DicConsole.WriteLine("Output format does not support CD Lead-in, {0}continuing...",
force ? "" : "not ");
dumpLog.WriteLine("Output format does not support CD Lead-in, {0}continuing...",
force ? "" : "not ");
if(!force) return;
dumpLeadIn = false;
}
}
// Try to read the Lead-in
if(dumpLeadIn) if(dumpLeadIn)
{ {
DicConsole.WriteLine("Trying to read Lead-In..."); DicConsole.WriteLine("Trying to read Lead-In...");
bool gotLeadIn = false; bool gotLeadIn = false;
int leadInSectorsGood = 0, leadInSectorsTotal = 0; int leadInSectorsGood = 0;
MemoryStream leadinMs = new MemoryStream();
dumpFile = new DataFile(outputPrefix + ".leadin.bin");
dataChk = new Checksum();
readBuffer = null; readBuffer = null;
dumpLog.WriteLine("Reading Lead-in"); dumpLog.WriteLine("Reading Lead-in");
for(int leadInBlock = -150; leadInBlock < 0 && resume.NextBlock == 0; leadInBlock++) for(int leadInBlock = -150; leadInBlock < 0 && resume.NextBlock == 0; leadInBlock++)
{ {
if(dev.PlatformId == PlatformID.FreeBSD)
{
DicConsole.DebugWriteLine("Dump-Media",
"FreeBSD panics when reading CD Lead-in, see upstream bug #224253.");
break;
}
if(aborted) if(aborted)
{ {
dumpLog.WriteLine("Aborted!"); dumpLog.WriteLine("Aborted!");
@@ -428,57 +509,42 @@ namespace DiscImageChef.Core.Devices.Dumping
sense = dev.ReadCd(out readBuffer, out senseBuf, (uint)leadInBlock, blockSize, 1, sense = dev.ReadCd(out readBuffer, out senseBuf, (uint)leadInBlock, blockSize, 1,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
true, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout, true, MmcErrorField.None, supportedSubchannel, dev.Timeout,
out double cmdDuration); out double cmdDuration);
if(!sense && !dev.Error) if(!sense && !dev.Error)
{ {
dataChk.Update(readBuffer); leadinMs.Write(readBuffer, 0, (int)blockSize);
dumpFile.Write(readBuffer);
gotLeadIn = true; gotLeadIn = true;
leadInSectorsGood++; leadInSectorsGood++;
leadInSectorsTotal++;
} }
else else
{
if(gotLeadIn)
{ {
// Write empty data // Write empty data
dataChk.Update(new byte[blockSize]); if(gotLeadIn) leadinMs.Write(new byte[blockSize], 0, (int)blockSize);
dumpFile.Write(new byte[blockSize]);
leadInSectorsTotal++;
}
} }
double newSpeed = blockSize / (double)1048576 / (cmdDuration / 1000); double newSpeed = blockSize / (double)1048576 / (cmdDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed; if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
} }
dumpFile.Close(); if(leadInSectorsGood > 0) mediaTags.Add(MediaTagType.CD_LeadIn, leadinMs.ToArray());
if(leadInSectorsGood > 0)
sidecar.OpticalDisc[0].LeadIn = new[]
{
new BorderType
{
Image = outputPrefix + ".leadin.bin",
Checksums = dataChk.End().ToArray(),
Size = leadInSectorsTotal * blockSize
}
};
else File.Delete(outputPrefix + ".leadin.bin");
DicConsole.WriteLine(); DicConsole.WriteLine();
DicConsole.WriteLine("Got {0} lead-in sectors.", leadInSectorsGood); DicConsole.WriteLine("Got {0} lead-in sectors.", leadInSectorsGood);
dumpLog.WriteLine("Got {0} Lead-in sectors.", leadInSectorsGood); dumpLog.WriteLine("Got {0} Lead-in sectors.", leadInSectorsGood);
leadinMs.Close();
} }
// Try how many blocks are readable at once
while(true) while(true)
{ {
if(readcd) if(readcd)
{ {
sense = dev.ReadCd(out readBuffer, out senseBuf, 0, blockSize, blocksToRead, sense = dev.ReadCd(out readBuffer, out senseBuf, 0, blockSize, blocksToRead,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
true, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout, out _); true, MmcErrorField.None, supportedSubchannel, dev.Timeout, out _);
if(dev.Error || sense) blocksToRead /= 2; if(dev.Error || sense) blocksToRead /= 2;
} }
@@ -499,56 +565,106 @@ namespace DiscImageChef.Core.Devices.Dumping
dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize); dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize);
dumpLog.WriteLine("SCSI device type: {0}.", dev.ScsiType); dumpLog.WriteLine("SCSI device type: {0}.", dev.ScsiType);
dumpLog.WriteLine("Media identified as {0}.", dskType); dumpLog.WriteLine("Media identified as {0}.", dskType);
alcohol.SetMediaType(dskType);
dumpFile = new DataFile(outputPrefix + ".bin"); DicConsole.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);
alcohol.SetExtension(".bin"); DicConsole.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
DataFile subFile = null; DicConsole.WriteLine("Device reports {0} bytes per logical block.", blockSize);
if(separateSubchannel) subFile = new DataFile(outputPrefix + ".sub"); DicConsole.WriteLine("SCSI device type: {0}.", dev.ScsiType);
DicConsole.WriteLine("Media identified as {0}.", dskType);
DumpHardwareType currentTry = null;
ExtentsULong extents = null;
ResumeSupport.Process(true, true, blocks, dev.Manufacturer, dev.Model, dev.Serial, dev.PlatformId,
ref resume, ref currentTry, ref extents);
if(currentTry == null || extents == null)
throw new InvalidOperationException("Could not process resume file, not continuing...");
MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", 0x0008); IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", 0x0008);
bool ret = outputPlugin.Create(outputPath, dskType, formatOptions, blocks, SECTOR_SIZE);
dumpFile.Seek(resume.NextBlock, (ulong)sectorSize); // Cannot create image
if(separateSubchannel) subFile.Seek(resume.NextBlock, subSize); if(!ret)
{
dumpLog.WriteLine("Error creating output image, not continuing.");
dumpLog.WriteLine(outputPlugin.ErrorMessage);
DicConsole.ErrorWriteLine("Error creating output image, not continuing.");
DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage);
return;
}
// Send tracklist to output plugin. This may fail if subchannel is set but unsupported.
ret = outputPlugin.SetTracks(tracks.ToList());
if(!ret && supportedSubchannel == MmcSubchannel.None)
{
dumpLog.WriteLine("Error sending tracks to output image, not continuing.");
dumpLog.WriteLine(outputPlugin.ErrorMessage);
DicConsole.ErrorWriteLine("Error sending tracks to output image, not continuing.");
DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage);
return;
}
// If a subchannel is supported, check if output plugin allows us to write it.
if(supportedSubchannel != MmcSubchannel.None)
{
sense = dev.ReadCd(out readBuffer, out senseBuf, 0, blockSize, 64, MmcSectorTypes.AllTypes, false,
false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
supportedSubchannel, dev.Timeout, out _);
byte[] tmpBuf = new byte[SECTOR_SIZE];
Array.Copy(readBuffer, SECTOR_SIZE, tmpBuf, 0, subSize);
ret = outputPlugin.WriteSectorTag(tmpBuf, 0, SectorTagType.CdSectorSubchannel);
if(!ret)
{
DicConsole.WriteLine("Error writing subchannel to output image, {0}continuing...",
force ? "" : "not ");
dumpLog.WriteLine("Error writing subchannel to output image, {0}continuing...",
force ? "" : "not ");
if(!force) return;
supportedSubchannel = MmcSubchannel.None;
subSize = 0;
blockSize = SECTOR_SIZE + subSize;
for(int t = 0; t < tracks.Length; t++) tracks[t].TrackSubchannelType = TrackSubchannelType.None;
ret =
outputPlugin.SetTracks(tracks.ToList());
if(!ret)
{
dumpLog.WriteLine("Error sending tracks to output image, not continuing.");
dumpLog.WriteLine(outputPlugin.ErrorMessage);
DicConsole.ErrorWriteLine("Error sending tracks to output image, not continuing.");
DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage);
return;
}
}
}
// Set track flags
foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => (trk.ADR == 1 || trk.ADR == 4) &&
trk.POINT >= 0x01 &&
trk.POINT <= 0x63))
{
Track track = tracks.FirstOrDefault(t => t.TrackSequence == trk.POINT);
if(track.TrackSequence == 0) continue;
dumpLog.WriteLine("Setting flags for track {0}...", track.TrackSequence);
DicConsole.WriteLine("Setting flags for track {0}...", track.TrackSequence);
outputPlugin.WriteSectorTag(new[] {trk.CONTROL}, track.TrackStartSector, SectorTagType.CdTrackFlags);
}
if(resume.NextBlock > 0) dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock); if(resume.NextBlock > 0) dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock);
// Start reading
start = DateTime.UtcNow; start = DateTime.UtcNow;
for(int t = 0; t < tracks.Length; t++) for(int t = 0; t < tracks.Length; t++)
{ {
dumpLog.WriteLine("Reading track {0}", t); dumpLog.WriteLine("Reading track {0}", t);
tracks[t].BytesPerSector = sectorSize; for(ulong i = resume.NextBlock; i <= tracks[t].TrackEndSector; i += blocksToRead)
tracks[t].Image = new ImageType
{
format = "BINARY",
offset = dumpFile.Position,
offsetSpecified = true,
Value = outputPrefix + ".bin"
};
tracks[t].Size = (tracks[t].EndSector - tracks[t].StartSector + 1) * sectorSize;
tracks[t].SubChannel = new SubChannelType
{
Image = new ImageType {format = "rw_raw", offsetSpecified = true},
Size = (tracks[t].EndSector - tracks[t].StartSector + 1) * subSize
};
if(separateSubchannel)
{
tracks[t].SubChannel.Image.offset = subFile.Position;
tracks[t].SubChannel.Image.Value = outputPrefix + ".sub";
}
else
{
tracks[t].SubChannel.Image.offset = tracks[t].Image.offset;
tracks[t].SubChannel.Image.Value = tracks[t].Image.Value;
}
alcohol.SetTrackSizes((byte)(t + 1), sectorSize, tracks[t].StartSector, dumpFile.Position,
tracks[t].EndSector - tracks[t].StartSector + 1);
bool checkedDataFormat = false;
for(ulong i = resume.NextBlock; i <= (ulong)tracks[t].EndSector; i += blocksToRead)
{ {
if(aborted) if(aborted)
{ {
@@ -559,8 +675,8 @@ namespace DiscImageChef.Core.Devices.Dumping
double cmdDuration = 0; double cmdDuration = 0;
if((ulong)tracks[t].EndSector + 1 - i < blocksToRead) if(tracks[t].TrackEndSector + 1 - i < blocksToRead)
blocksToRead = (uint)((ulong)tracks[t].EndSector + 1 - i); blocksToRead = (uint)(tracks[t].TrackEndSector + 1 - i);
#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
if(currentSpeed > maxSpeed && currentSpeed != 0) maxSpeed = currentSpeed; if(currentSpeed > maxSpeed && currentSpeed != 0) maxSpeed = currentSpeed;
@@ -574,7 +690,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{ {
sense = dev.ReadCd(out readBuffer, out senseBuf, (uint)i, blockSize, blocksToRead, sense = dev.ReadCd(out readBuffer, out senseBuf, (uint)i, blockSize, blocksToRead,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
true, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout, out cmdDuration); true, MmcErrorField.None, supportedSubchannel, dev.Timeout, out cmdDuration);
totalDuration += cmdDuration; totalDuration += cmdDuration;
} }
@@ -583,13 +699,17 @@ namespace DiscImageChef.Core.Devices.Dumping
mhddLog.Write(i, cmdDuration); mhddLog.Write(i, cmdDuration);
ibgLog.Write(i, currentSpeed * 1024); ibgLog.Write(i, currentSpeed * 1024);
extents.Add(i, blocksToRead, true); extents.Add(i, blocksToRead, true);
if(separateSubchannel) if(supportedSubchannel != MmcSubchannel.None)
for(int b = 0; b < blocksToRead; b++) for(int b = 0; b < blocksToRead; b++)
{ {
dumpFile.Write(readBuffer, (int)(0 + b * blockSize), sectorSize); byte[] data = new byte[SECTOR_SIZE];
subFile.Write(readBuffer, (int)(sectorSize + b * blockSize), (int)subSize); byte[] sub = new byte[subSize];
Array.Copy(readBuffer, (int)(0 + b * blockSize), data, 0, SECTOR_SIZE);
Array.Copy(readBuffer, (int)(SECTOR_SIZE + b * blockSize), sub, 0, subSize);
outputPlugin.WriteSectors(data, i, blocksToRead);
outputPlugin.WriteSectorsTag(sub, i, blocksToRead, SectorTagType.CdSectorSubchannel);
} }
else dumpFile.Write(readBuffer); else outputPlugin.WriteSectors(readBuffer, i, blocksToRead);
} }
else else
{ {
@@ -597,12 +717,13 @@ namespace DiscImageChef.Core.Devices.Dumping
if(stopOnError) return; // TODO: Return more cleanly if(stopOnError) return; // TODO: Return more cleanly
// Write empty data // Write empty data
if(separateSubchannel) if(supportedSubchannel != MmcSubchannel.None)
{ {
dumpFile.Write(new byte[sectorSize * blocksToRead]); outputPlugin.WriteSectors(new byte[SECTOR_SIZE * blocksToRead], i, blocksToRead);
subFile.Write(new byte[subSize * blocksToRead]); outputPlugin.WriteSectorsTag(new byte[subSize * blocksToRead], i, blocksToRead,
SectorTagType.CdSectorSubchannel);
} }
else dumpFile.Write(new byte[blockSize * blocksToRead]); else outputPlugin.WriteSectors(new byte[blockSize * blocksToRead], i, blocksToRead);
for(ulong b = i; b < i + blocksToRead; b++) resume.BadBlocks.Add(b); for(ulong b = i; b < i + blocksToRead; b++) resume.BadBlocks.Add(b);
@@ -613,76 +734,21 @@ namespace DiscImageChef.Core.Devices.Dumping
dumpLog.WriteLine("Error reading {0} sectors from sector {1}.", blocksToRead, i); dumpLog.WriteLine("Error reading {0} sectors from sector {1}.", blocksToRead, i);
} }
if(tracks[t].TrackType1 == TrackTypeTrackType.mode1 && !checkedDataFormat) double newSpeed =
{ (double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000);
byte[] sync = new byte[12];
Array.Copy(readBuffer, 0, sync, 0, 12);
if(sync.SequenceEqual(new byte[]
{
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
}))
switch(readBuffer[15])
{
case 0:
tracks[t].TrackType1 = TrackTypeTrackType.mode0;
checkedDataFormat = true;
break;
case 1:
tracks[t].TrackType1 = TrackTypeTrackType.mode1;
checkedDataFormat = true;
break;
case 2:
tracks[t].TrackType1 = TrackTypeTrackType.mode2;
checkedDataFormat = true;
break;
}
}
double newSpeed = (double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed; if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
resume.NextBlock = i + blocksToRead; resume.NextBlock = i + blocksToRead;
} }
DiscImages.TrackType trkType;
switch(tracks[t].TrackType1)
{
case TrackTypeTrackType.audio:
trkType = DiscImages.TrackType.Audio;
break;
case TrackTypeTrackType.mode1:
trkType = DiscImages.TrackType.CdMode1;
break;
case TrackTypeTrackType.mode2:
trkType = DiscImages.TrackType.CdMode2Formless;
break;
case TrackTypeTrackType.m2f1:
trkType = DiscImages.TrackType.CdMode2Form1;
break;
case TrackTypeTrackType.m2f2:
trkType = DiscImages.TrackType.CdMode2Form2;
break;
case TrackTypeTrackType.dvd:
case TrackTypeTrackType.hddvd:
case TrackTypeTrackType.bluray:
case TrackTypeTrackType.ddcd:
case TrackTypeTrackType.mode0:
trkType = DiscImages.TrackType.Data;
break;
default: throw new ArgumentOutOfRangeException();
}
alcohol.SetTrackTypes((byte)(t + 1), trkType,
separateSubchannel
? TrackSubchannelType.None
: TrackSubchannelType.RawInterleaved);
} }
DicConsole.WriteLine(); DicConsole.WriteLine();
end = DateTime.UtcNow; end = DateTime.UtcNow;
mhddLog.Close(); mhddLog.Close();
ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), devicePath); blockSize * (double)(blocks + 1) /
dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); 1024 / (totalDuration / 1000), devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.",
(end - start).TotalSeconds);
dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
@@ -712,7 +778,7 @@ namespace DiscImageChef.Core.Devices.Dumping
{ {
sense = dev.ReadCd(out readBuffer, out senseBuf, (uint)badSector, blockSize, blocksToRead, sense = dev.ReadCd(out readBuffer, out senseBuf, (uint)badSector, blockSize, blocksToRead,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
true, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout, true, MmcErrorField.None, supportedSubchannel, dev.Timeout,
out double cmdDuration); out double cmdDuration);
totalDuration += cmdDuration; totalDuration += cmdDuration;
} }
@@ -726,12 +792,16 @@ namespace DiscImageChef.Core.Devices.Dumping
dumpLog.WriteLine("Correctly retried sector {0} in pass {1}.", badSector, pass); dumpLog.WriteLine("Correctly retried sector {0} in pass {1}.", badSector, pass);
} }
if(separateSubchannel) if(supportedSubchannel != MmcSubchannel.None)
{ {
dumpFile.WriteAt(readBuffer, badSector, (uint)sectorSize, 0, sectorSize); byte[] data = new byte[SECTOR_SIZE];
subFile.WriteAt(readBuffer, badSector, subSize, sectorSize, (int)subSize); byte[] sub = new byte[subSize];
Array.Copy(readBuffer, 0, data, 0, SECTOR_SIZE);
Array.Copy(readBuffer, SECTOR_SIZE, sub, 0, subSize);
outputPlugin.WriteSector(data, badSector);
outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel);
} }
else dumpFile.WriteAt(readBuffer, badSector, blockSize); else outputPlugin.WriteSectors(readBuffer, badSector, blocksToRead);
} }
if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0) if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0)
@@ -797,91 +867,67 @@ namespace DiscImageChef.Core.Devices.Dumping
} }
#endregion Compact Disc Error handling #endregion Compact Disc Error handling
// Write media tags to image
if(!aborted)
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags)
{
ret = outputPlugin.WriteMediaTag(tag.Value, tag.Key);
if(ret || force) continue;
// Cannot write tag to image
dumpLog.WriteLine($"Cannot write tag {tag.Key}.");
throw new ArgumentException(outputPlugin.ErrorMessage);
}
dumpLog.WriteLine("Closing output file.");
DicConsole.WriteLine("Closing output file.");
outputPlugin.Close();
resume.BadBlocks.Sort(); resume.BadBlocks.Sort();
currentTry.Extents = ExtentsConverter.ToMetadata(extents); currentTry.Extents = ExtentsConverter.ToMetadata(extents);
dataChk = new Checksum();
dumpFile.Seek(0, SeekOrigin.Begin);
if(separateSubchannel) subFile.Seek(0, SeekOrigin.Begin);
blocksToRead = 500;
dumpLog.WriteLine("Checksum starts.");
for(int t = 0; t < tracks.Length; t++)
{
Checksum trkChk = new Checksum();
Checksum subChk = new Checksum();
for(ulong i = (ulong)tracks[t].StartSector; i <= (ulong)tracks[t].EndSector; i += blocksToRead)
{
if(aborted) if(aborted)
{ {
dumpLog.WriteLine("Aborted!"); dumpLog.WriteLine("Aborted!");
break; return;
} }
if((ulong)tracks[t].EndSector + 1 - i < blocksToRead) dumpLog.WriteLine("Creating sidecar.");
blocksToRead = (uint)((ulong)tracks[t].EndSector + 1 - i); FiltersList filters = new FiltersList();
IFilter filter = filters.GetFilter(outputPath);
DicConsole.Write("\rChecksumming sector {0} of {1} at track {3} ({2:F3} MiB/sec.)", i, blocks, IMediaImage inputPlugin = ImageFormat.Detect(filter);
currentSpeed, t + 1); if(!inputPlugin.Open(filter)) throw new ArgumentException("Could not open created image.");
DateTime chkStart = DateTime.UtcNow; DateTime chkStart = DateTime.UtcNow;
byte[] dataToCheck = new byte[blockSize * blocksToRead]; CICMMetadataType sidecar = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding);
dumpFile.Read(dataToCheck, 0, (int)(blockSize * blocksToRead));
if(separateSubchannel)
{
byte[] data = new byte[sectorSize];
byte[] sub = new byte[subSize];
for(int b = 0; b < blocksToRead; b++)
{
Array.Copy(dataToCheck, 0, data, 0, sectorSize);
Array.Copy(dataToCheck, sectorSize, sub, 0, subSize);
dataChk.Update(data);
trkChk.Update(data);
subChk.Update(sub);
}
}
else
{
dataChk.Update(dataToCheck);
trkChk.Update(dataToCheck);
}
DateTime chkEnd = DateTime.UtcNow;
double chkDuration = (chkEnd - chkStart).TotalMilliseconds;
totalChkDuration += chkDuration;
double newSpeed = (double)blockSize * blocksToRead / 1048576 / (chkDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
}
tracks[t].Checksums = trkChk.End().ToArray();
tracks[t].SubChannel.Checksums = separateSubchannel ? subChk.End().ToArray() : tracks[t].Checksums;
}
DicConsole.WriteLine();
dumpFile.Close();
end = DateTime.UtcNow; end = DateTime.UtcNow;
dumpLog.WriteLine("Checksum finished in {0} seconds.", (end - start).TotalSeconds);
double totalChkDuration = (end - chkStart).TotalMilliseconds;
dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);
dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));
// TODO: Correct this
sidecar.OpticalDisc[0].Checksums = dataChk.End().ToArray();
sidecar.OpticalDisc[0].DumpHardwareArray = resume.Tries.ToArray();
sidecar.OpticalDisc[0].Image = new ImageType
{
format = "Raw disk image (sector by sector copy)",
Value = outputPrefix + ".bin"
};
sidecar.OpticalDisc[0].Sessions = toc.Value.LastCompleteSession;
sidecar.OpticalDisc[0].Tracks = new[] {tracks.Length};
sidecar.OpticalDisc[0].Track = tracks;
sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType);
Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp, out string xmlDskSubTyp); Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp, out string xmlDskSubTyp);
sidecar.OpticalDisc[0].DiscType = xmlDskTyp; sidecar.OpticalDisc[0].DiscType = xmlDskTyp;
sidecar.OpticalDisc[0].DiscSubType = xmlDskSubTyp; sidecar.OpticalDisc[0].DiscSubType = xmlDskSubTyp;
sidecar.OpticalDisc[0].DumpHardwareArray = resume.Tries.ToArray();
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags)
if(outputPlugin.SupportedMediaTags.Contains(tag.Key))
Mmc.AddMediaTagToSidecar(outputPath, tag, ref sidecar);
DicConsole.WriteLine();
DicConsole.WriteLine("Took a total of {0:F3} seconds ({1:F3} processing commands, {2:F3} checksumming).",
(end - start).TotalSeconds, totalDuration / 1000, totalChkDuration / 1000);
DicConsole.WriteLine("Avegare speed: {0:F3} MiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000));
DicConsole.WriteLine("Fastest speed burst: {0:F3} MiB/sec.", maxSpeed);
DicConsole.WriteLine("Slowest speed burst: {0:F3} MiB/sec.", minSpeed);
DicConsole.WriteLine("{0} sectors could not be read.", resume.BadBlocks.Count);
DicConsole.WriteLine();
if(!aborted) if(!aborted)
{ {
@@ -892,10 +938,9 @@ namespace DiscImageChef.Core.Devices.Dumping
XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType)); XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType));
xmlSer.Serialize(xmlFs, sidecar); xmlSer.Serialize(xmlFs, sidecar);
xmlFs.Close(); xmlFs.Close();
alcohol.Close();
} }
Statistics.AddMedia(dskType, true);*/ Statistics.AddMedia(dskType, true);
} }
} }
} }

View File

@@ -197,7 +197,7 @@ namespace DiscImageChef.Core.Devices.Dumping
if(compactDisc) if(compactDisc)
{ {
CompactDisc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError, CompactDisc.Dump(dev, devicePath, outputPlugin, retryPasses, force, dumpRaw, persistent, stopOnError,
ref dskType, ref resume, ref dumpLog, dumpLeadIn, outputPrefix, outputPath, ref dskType, ref resume, ref dumpLog, dumpLeadIn, encoding, outputPrefix, outputPath,
formatOptions); formatOptions);
return; return;
} }
@@ -757,6 +757,38 @@ namespace DiscImageChef.Core.Devices.Dumping
Checksums = Checksum.GetChecksums(tag.Value).ToArray() Checksums = Checksum.GetChecksums(tag.Value).ToArray()
}; };
break; break;
case MediaTagType.CD_FullTOC:
sidecar.OpticalDisc[0].TOC = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.CD_ATIP:
sidecar.OpticalDisc[0].ATIP = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.CD_PMA:
sidecar.OpticalDisc[0].PMA = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
case MediaTagType.CD_TEXT:
sidecar.OpticalDisc[0].LeadInCdText = new DumpType
{
Image = outputPath,
Size = tag.Value.Length,
Checksums = Checksum.GetChecksums(tag.Value).ToArray()
};
break;
} }
} }
} }