public static void DoConvert(ConvertImageOptions options) { DicConsole.DebugWriteLine("Analyze command", "--debug={0}", options.Debug); DicConsole.DebugWriteLine("Analyze command", "--verbose={0}", options.Verbose); DicConsole.DebugWriteLine("Analyze command", "--input={0}", options.InputFile); DicConsole.DebugWriteLine("Analyze command", "--output={0}", options.OutputFile); DicConsole.DebugWriteLine("Analyze command", "--format={0}", options.OutputFormat); DicConsole.DebugWriteLine("Analyze command", "--count={0}", options.Count); DicConsole.DebugWriteLine("Analyze command", "--force={0}", options.Force); DicConsole.DebugWriteLine("Analyze command", "--creator={0}", options.Creator); DicConsole.DebugWriteLine("Analyze command", "--media-title={0}", options.MediaTitle); DicConsole.DebugWriteLine("Analyze command", "--comments={0}", options.Comments); DicConsole.DebugWriteLine("Analyze command", "--media-manufacturer={0}", options.MediaManufacturer); DicConsole.DebugWriteLine("Analyze command", "--media-model={0}", options.MediaModel); DicConsole.DebugWriteLine("Analyze command", "--media-serial={0}", options.MediaSerialNumber); DicConsole.DebugWriteLine("Analyze command", "--media-barcode={0}", options.MediaBarcode); DicConsole.DebugWriteLine("Analyze command", "--media-partnumber={0}", options.MediaPartNumber); DicConsole.DebugWriteLine("Analyze command", "--media-sequence={0}", options.MediaSequence); DicConsole.DebugWriteLine("Analyze command", "--media-lastsequence={0}", options.LastMediaSequence); DicConsole.DebugWriteLine("Analyze command", "--drive-manufacturer={0}", options.DriveManufacturer); DicConsole.DebugWriteLine("Analyze command", "--drive-model={0}", options.DriveModel); DicConsole.DebugWriteLine("Analyze command", "--drive-serial={0}", options.DriveSerialNumber); DicConsole.DebugWriteLine("Analyze command", "--drive-revision={0}", options.DriveFirmwareRevision); DicConsole.DebugWriteLine("Analyze command", "--cicm-xml={0}", options.CicmXml); DicConsole.DebugWriteLine("Analyze command", "--resume-file={0}", options.ResumeFile); DicConsole.DebugWriteLine("Analyze command", "--options={0}", options.Options); Dictionary <string, string> parsedOptions = Options.Parse(options.Options); DicConsole.DebugWriteLine("Analyze command", "Parsed options:"); foreach (KeyValuePair <string, string> parsedOption in parsedOptions) { DicConsole.DebugWriteLine("Analyze command", "{0} = {1}", parsedOption.Key, parsedOption.Value); } if (options.Count == 0) { DicConsole.ErrorWriteLine("Need to specify more than 0 sectors to copy at once"); return; } Resume resume = null; CICMMetadataType sidecar = null; XmlSerializer xs = new XmlSerializer(typeof(CICMMetadataType)); if (options.CicmXml != null) { if (File.Exists(options.CicmXml)) { try { StreamReader sr = new StreamReader(options.CicmXml); sidecar = (CICMMetadataType)xs.Deserialize(sr); sr.Close(); } catch { DicConsole.ErrorWriteLine("Incorrect metadata sidecar file, not continuing..."); return; } } else { DicConsole.ErrorWriteLine("Could not find metadata sidecar, not continuing..."); return; } } xs = new XmlSerializer(typeof(Resume)); if (options.ResumeFile != null) { if (File.Exists(options.ResumeFile)) { try { StreamReader sr = new StreamReader(options.ResumeFile); resume = (Resume)xs.Deserialize(sr); sr.Close(); } catch { DicConsole.ErrorWriteLine("Incorrect resume file, not continuing..."); return; } } else { DicConsole.ErrorWriteLine("Could not find resume file, not continuing..."); return; } } FiltersList filtersList = new FiltersList(); IFilter inputFilter = filtersList.GetFilter(options.InputFile); if (inputFilter == null) { DicConsole.ErrorWriteLine("Cannot open specified file."); return; } if (File.Exists(options.OutputFile)) { DicConsole.ErrorWriteLine("Output file already exists, not continuing."); return; } PluginBase plugins = GetPluginBase.Instance; IMediaImage inputFormat = ImageFormat.Detect(inputFilter); if (inputFormat == null) { DicConsole.WriteLine("Input image format not identified, not proceeding with conversion."); return; } if (options.Verbose) { DicConsole.VerboseWriteLine("Input image format identified by {0} ({1}).", inputFormat.Name, inputFormat.Id); } else { DicConsole.WriteLine("Input image format identified by {0}.", inputFormat.Name); } try { if (!inputFormat.Open(inputFilter)) { DicConsole.WriteLine("Unable to open image format"); DicConsole.WriteLine("No error given"); return; } DicConsole.DebugWriteLine("Convert-image command", "Correctly opened image file."); DicConsole.DebugWriteLine("Convert-image command", "Image without headers is {0} bytes.", inputFormat.Info.ImageSize); DicConsole.DebugWriteLine("Convert-image command", "Image has {0} sectors.", inputFormat.Info.Sectors); DicConsole.DebugWriteLine("Convert-image command", "Image identifies media type as {0}.", inputFormat.Info.MediaType); Core.Statistics.AddMediaFormat(inputFormat.Format); Core.Statistics.AddMedia(inputFormat.Info.MediaType, false); Core.Statistics.AddFilter(inputFilter.Name); } catch (Exception ex) { DicConsole.ErrorWriteLine("Unable to open image format"); DicConsole.ErrorWriteLine("Error: {0}", ex.Message); DicConsole.DebugWriteLine("Convert-image command", "Stack trace: {0}", ex.StackTrace); return; } List <IWritableImage> candidates = new List <IWritableImage>(); // Try extension if (string.IsNullOrEmpty(options.OutputFormat)) { candidates.AddRange(plugins.WritableImages.Values.Where(t => t.KnownExtensions .Contains(Path.GetExtension(options .OutputFile)))); } // Try Id else if (Guid.TryParse(options.OutputFormat, out Guid outId)) { candidates.AddRange(plugins.WritableImages.Values.Where(t => t.Id.Equals(outId))); } // Try name else { candidates.AddRange(plugins.WritableImages.Values.Where(t => string.Equals(t.Name, options.OutputFormat, StringComparison .InvariantCultureIgnoreCase))); } if (candidates.Count == 0) { DicConsole.WriteLine("No plugin supports requested extension."); return; } if (candidates.Count > 1) { DicConsole.WriteLine("More than one plugin supports requested extension."); return; } IWritableImage outputFormat = candidates[0]; if (options.Verbose) { DicConsole.VerboseWriteLine("Output image format: {0} ({1}).", outputFormat.Name, outputFormat.Id); } else { DicConsole.WriteLine("Output image format: {0}.", outputFormat.Name); } if (!outputFormat.SupportedMediaTypes.Contains(inputFormat.Info.MediaType)) { DicConsole.ErrorWriteLine("Output format does not support media type, cannot continue..."); return; } foreach (MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags) { if (outputFormat.SupportedMediaTags.Contains(mediaTag) || options.Force) { continue; } DicConsole.ErrorWriteLine("Converting image will lose media tag {0}, not continuing...", mediaTag); DicConsole.ErrorWriteLine("If you don't care, use force option."); return; } bool useLong = inputFormat.Info.ReadableSectorTags.Count != 0; foreach (SectorTagType sectorTag in inputFormat.Info.ReadableSectorTags) { if (outputFormat.SupportedSectorTags.Contains(sectorTag)) { continue; } if (options.Force) { if (sectorTag != SectorTagType.CdTrackFlags && sectorTag != SectorTagType.CdTrackIsrc && sectorTag != SectorTagType.CdSectorSubchannel) { useLong = false; } continue; } DicConsole.ErrorWriteLine("Converting image will lose sector tag {0}, not continuing...", sectorTag); DicConsole .ErrorWriteLine("If you don't care, use force option. This will skip all sector tags converting only user data."); return; } if (!outputFormat.Create(options.OutputFile, inputFormat.Info.MediaType, parsedOptions, inputFormat.Info.Sectors, inputFormat.Info.SectorSize)) { DicConsole.ErrorWriteLine("Error {0} creating output image.", outputFormat.ErrorMessage); return; } CommonTypes.Structs.ImageInfo metadata = new CommonTypes.Structs.ImageInfo { Application = "DiscImageChef", ApplicationVersion = Version.GetVersion(), Comments = options.Comments ?? inputFormat.Info.Comments, Creator = options.Creator ?? inputFormat.Info.Creator, DriveFirmwareRevision = options.DriveFirmwareRevision ?? inputFormat.Info.DriveFirmwareRevision, DriveManufacturer = options.DriveManufacturer ?? inputFormat.Info.DriveManufacturer, DriveModel = options.DriveModel ?? inputFormat.Info.DriveModel, DriveSerialNumber = options.DriveSerialNumber ?? inputFormat.Info.DriveSerialNumber, LastMediaSequence = options.LastMediaSequence != 0 ? options.LastMediaSequence : inputFormat.Info.LastMediaSequence, MediaBarcode = options.MediaBarcode ?? inputFormat.Info.MediaBarcode, MediaManufacturer = options.MediaManufacturer ?? inputFormat.Info.MediaManufacturer, MediaModel = options.MediaModel ?? inputFormat.Info.MediaModel, MediaPartNumber = options.MediaPartNumber ?? inputFormat.Info.MediaPartNumber, MediaSequence = options.MediaSequence != 0 ? options.MediaSequence : inputFormat.Info.MediaSequence, MediaSerialNumber = options.MediaSerialNumber ?? inputFormat.Info.MediaSerialNumber, MediaTitle = options.MediaTitle ?? inputFormat.Info.MediaTitle }; if (!outputFormat.SetMetadata(metadata)) { DicConsole.ErrorWrite("Error {0} setting metadata, ", outputFormat.ErrorMessage); if (!options.Force) { DicConsole.ErrorWriteLine("not continuing..."); return; } DicConsole.ErrorWriteLine("continuing..."); } List <Track> tracks; try { tracks = inputFormat.Tracks; } catch (Exception) { tracks = null; } CICMMetadataType cicmMetadata = inputFormat.CicmMetadata; List <DumpHardwareType> dumpHardware = inputFormat.DumpHardware; if (tracks != null) { if (!outputFormat.SetTracks(tracks)) { DicConsole.ErrorWriteLine("Error {0} sending tracks list to output image.", outputFormat.ErrorMessage); return; } } foreach (MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags) { if (options.Force && !outputFormat.SupportedMediaTags.Contains(mediaTag)) { continue; } DicConsole.WriteLine("Converting media tag {0}", mediaTag); byte[] tag = inputFormat.ReadDiskTag(mediaTag); if (outputFormat.WriteMediaTag(tag, mediaTag)) { continue; } if (options.Force) { DicConsole.ErrorWriteLine("Error {0} writing media tag, continuing...", outputFormat.ErrorMessage); } else { DicConsole.ErrorWriteLine("Error {0} writing media tag, not continuing...", outputFormat.ErrorMessage); return; } } DicConsole.WriteLine("{0} sectors to convert", inputFormat.Info.Sectors); ulong doneSectors = 0; if (tracks == null) { DicConsole.WriteLine("Setting geometry to {0} cylinders, {1} heads and {2} sectors per track", inputFormat.Info.Cylinders, inputFormat.Info.Heads, inputFormat.Info.SectorsPerTrack); if (!outputFormat.SetGeometry(inputFormat.Info.Cylinders, inputFormat.Info.Heads, inputFormat.Info.SectorsPerTrack)) { DicConsole.ErrorWriteLine("Error {0} setting geometry, image may be incorrect, continuing...", outputFormat.ErrorMessage); } while (doneSectors < inputFormat.Info.Sectors) { byte[] sector; uint sectorsToDo; if (inputFormat.Info.Sectors - doneSectors >= (ulong)options.Count) { sectorsToDo = (uint)options.Count; } else { sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors); } DicConsole.Write("\rConverting sectors {0} to {1} ({2:P2} done)", doneSectors, doneSectors + sectorsToDo, doneSectors / (double)inputFormat.Info.Sectors); bool result; if (useLong) { if (sectorsToDo == 1) { sector = inputFormat.ReadSectorLong(doneSectors); result = outputFormat.WriteSectorLong(sector, doneSectors); } else { sector = inputFormat.ReadSectorsLong(doneSectors, sectorsToDo); result = outputFormat.WriteSectorsLong(sector, doneSectors, sectorsToDo); } } else { if (sectorsToDo == 1) { sector = inputFormat.ReadSector(doneSectors); result = outputFormat.WriteSector(sector, doneSectors); } else { sector = inputFormat.ReadSectors(doneSectors, sectorsToDo); result = outputFormat.WriteSectors(sector, doneSectors, sectorsToDo); } } if (!result) { if (options.Force) { DicConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...", outputFormat.ErrorMessage, doneSectors); } else { DicConsole.ErrorWriteLine("Error {0} writing sector {1}, not continuing...", outputFormat.ErrorMessage, doneSectors); return; } } doneSectors += sectorsToDo; } DicConsole.Write("\rConverting sectors {0} to {1} ({2:P2} done)", inputFormat.Info.Sectors, inputFormat.Info.Sectors, 1.0); DicConsole.WriteLine(); foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags) { if (!useLong) { break; } switch (tag) { case SectorTagType.AppleSectorTag: case SectorTagType.CdSectorSync: case SectorTagType.CdSectorHeader: case SectorTagType.CdSectorSubHeader: case SectorTagType.CdSectorEdc: case SectorTagType.CdSectorEccP: case SectorTagType.CdSectorEccQ: case SectorTagType.CdSectorEcc: // This tags are inline in long sector continue; } if (options.Force && !outputFormat.SupportedSectorTags.Contains(tag)) { continue; } doneSectors = 0; while (doneSectors < inputFormat.Info.Sectors) { byte[] sector; uint sectorsToDo; if (inputFormat.Info.Sectors - doneSectors >= (ulong)options.Count) { sectorsToDo = (uint)options.Count; } else { sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors); } DicConsole.Write("\rConverting tag {2} for sectors {0} to {1} ({2:P2} done)", doneSectors, doneSectors + sectorsToDo, doneSectors / (double)inputFormat.Info.Sectors, tag); bool result; if (sectorsToDo == 1) { sector = inputFormat.ReadSectorTag(doneSectors, tag); result = outputFormat.WriteSectorTag(sector, doneSectors, tag); } else { sector = inputFormat.ReadSectorsTag(doneSectors, sectorsToDo, tag); result = outputFormat.WriteSectorsTag(sector, doneSectors, sectorsToDo, tag); } if (!result) { if (options.Force) { DicConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...", outputFormat.ErrorMessage, doneSectors); } else { DicConsole.ErrorWriteLine("Error {0} writing sector {1}, not continuing...", outputFormat.ErrorMessage, doneSectors); return; } } doneSectors += sectorsToDo; } DicConsole.Write("\rConverting tag {2} for sectors {0} to {1} ({2:P2} done)", inputFormat.Info.Sectors, inputFormat.Info.Sectors, 1.0, tag); DicConsole.WriteLine(); } } else { foreach (Track track in tracks) { doneSectors = 0; ulong trackSectors = track.TrackEndSector - track.TrackStartSector + 1; while (doneSectors < trackSectors) { byte[] sector; uint sectorsToDo; if (trackSectors - doneSectors >= (ulong)options.Count) { sectorsToDo = (uint)options.Count; } else { sectorsToDo = (uint)(trackSectors - doneSectors); } DicConsole.Write("\rConverting sectors {0} to {1} in track {3} ({2:P2} done)", doneSectors + track.TrackStartSector, doneSectors + sectorsToDo + track.TrackStartSector, (doneSectors + track.TrackStartSector) / (double)inputFormat.Info.Sectors, track.TrackSequence); bool result; if (useLong) { if (sectorsToDo == 1) { sector = inputFormat.ReadSectorLong(doneSectors + track.TrackStartSector); result = outputFormat.WriteSectorLong(sector, doneSectors + track.TrackStartSector); } else { sector = inputFormat.ReadSectorsLong(doneSectors + track.TrackStartSector, sectorsToDo); result = outputFormat.WriteSectorsLong(sector, doneSectors + track.TrackStartSector, sectorsToDo); } } else { if (sectorsToDo == 1) { sector = inputFormat.ReadSector(doneSectors + track.TrackStartSector); result = outputFormat.WriteSector(sector, doneSectors + track.TrackStartSector); } else { sector = inputFormat.ReadSectors(doneSectors + track.TrackStartSector, sectorsToDo); result = outputFormat.WriteSectors(sector, doneSectors + track.TrackStartSector, sectorsToDo); } } if (!result) { if (options.Force) { DicConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...", outputFormat.ErrorMessage, doneSectors); } else { DicConsole.ErrorWriteLine("Error {0} writing sector {1}, not continuing...", outputFormat.ErrorMessage, doneSectors); return; } } doneSectors += sectorsToDo; } } DicConsole.Write("\rConverting sectors {0} to {1} in track {3} ({2:P2} done)", inputFormat.Info.Sectors, inputFormat.Info.Sectors, 1.0, tracks.Count); DicConsole.WriteLine(); foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags.OrderBy(t => t)) { if (!useLong) { break; } switch (tag) { case SectorTagType.AppleSectorTag: case SectorTagType.CdSectorSync: case SectorTagType.CdSectorHeader: case SectorTagType.CdSectorSubHeader: case SectorTagType.CdSectorEdc: case SectorTagType.CdSectorEccP: case SectorTagType.CdSectorEccQ: case SectorTagType.CdSectorEcc: // This tags are inline in long sector continue; } if (options.Force && !outputFormat.SupportedSectorTags.Contains(tag)) { continue; } foreach (Track track in tracks) { doneSectors = 0; ulong trackSectors = track.TrackEndSector - track.TrackStartSector + 1; byte[] sector; bool result; switch (tag) { case SectorTagType.CdTrackFlags: case SectorTagType.CdTrackIsrc: DicConsole.Write("\rConverting tag {0} in track {1} ({2:P2} done).", tag, track.TrackSequence, track.TrackSequence / (double)tracks.Count); sector = inputFormat.ReadSectorTag(track.TrackStartSector, tag); result = outputFormat.WriteSectorTag(sector, track.TrackStartSector, tag); if (!result) { if (options.Force) { DicConsole.ErrorWriteLine("Error {0} writing tag, continuing...", outputFormat.ErrorMessage); } else { DicConsole.ErrorWriteLine("Error {0} writing tag, not continuing...", outputFormat.ErrorMessage); return; } } continue; } while (doneSectors < trackSectors) { uint sectorsToDo; if (trackSectors - doneSectors >= (ulong)options.Count) { sectorsToDo = (uint)options.Count; } else { sectorsToDo = (uint)(trackSectors - doneSectors); } DicConsole.Write("\rConverting tag {4} for sectors {0} to {1} in track {3} ({2:P2} done)", doneSectors + track.TrackStartSector, doneSectors + sectorsToDo + track.TrackStartSector, (doneSectors + track.TrackStartSector) / (double)inputFormat.Info.Sectors, track.TrackSequence, tag); if (sectorsToDo == 1) { sector = inputFormat.ReadSectorTag(doneSectors + track.TrackStartSector, tag); result = outputFormat.WriteSectorTag(sector, doneSectors + track.TrackStartSector, tag); } else { sector = inputFormat.ReadSectorsTag(doneSectors + track.TrackStartSector, sectorsToDo, tag); result = outputFormat.WriteSectorsTag(sector, doneSectors + track.TrackStartSector, sectorsToDo, tag); } if (!result) { if (options.Force) { DicConsole.ErrorWriteLine("Error {0} writing tag for sector {1}, continuing...", outputFormat.ErrorMessage, doneSectors); } else { DicConsole.ErrorWriteLine("Error {0} writing tag for sector {1}, not continuing...", outputFormat.ErrorMessage, doneSectors); return; } } doneSectors += sectorsToDo; } } switch (tag) { case SectorTagType.CdTrackFlags: case SectorTagType.CdTrackIsrc: DicConsole.Write("\rConverting tag {0} in track {1} ({2:P2} done).", tag, tracks.Count, 1.0); break; default: DicConsole.Write("\rConverting tag {4} for sectors {0} to {1} in track {3} ({2:P2} done)", inputFormat.Info.Sectors, inputFormat.Info.Sectors, 1.0, tracks.Count, tag); break; } DicConsole.WriteLine(); } } bool ret = false; if (resume != null || dumpHardware != null) { if (resume != null) { ret = outputFormat.SetDumpHardware(resume.Tries); } else if (dumpHardware != null) { ret = outputFormat.SetDumpHardware(dumpHardware); } if (ret) { DicConsole.WriteLine("Written dump hardware list to output image."); } } ret = false; if (sidecar != null || cicmMetadata != null) { if (sidecar != null) { ret = outputFormat.SetCicmMetadata(sidecar); } else if (cicmMetadata != null) { ret = outputFormat.SetCicmMetadata(cicmMetadata); } if (ret) { DicConsole.WriteLine("Written CICM XML metadata to output image."); } } DicConsole.WriteLine("Closing output image."); if (!outputFormat.Close()) { DicConsole.ErrorWriteLine("Error {0} closing output image... Contents are not correct.", outputFormat.ErrorMessage); } DicConsole.WriteLine(); DicConsole.WriteLine("Conversion done."); Core.Statistics.AddCommand("convert-image"); }
/// <summary> /// Dumps an Xbox Game Disc using a Kreon drive /// </summary> /// <param name="dev">Device</param> /// <param name="devicePath">Path to the device</param> /// <param name="outputPrefix">Prefix for output data files</param> /// <param name="outputPlugin">Plugin for output file</param> /// <param name="retryPasses">How many times to retry</param> /// <param name="force">Force to continue dump whenever possible</param> /// <param name="dumpRaw">Dump raw/long sectors</param> /// <param name="persistent">Store whatever data the drive returned on error</param> /// <param name="stopOnError">Stop dump on first error</param> /// <param name="resume">Information for dump resuming</param> /// <param name="dumpLog">Dump logger</param> /// <param name="encoding">Encoding to use when analyzing dump</param> /// <param name="mediaTags">Media tags as retrieved in MMC layer</param> /// <param name="dskType">Disc type as detected in MMC layer</param> /// <param name="outputPath">Path to output file</param> /// <param name="formatOptions">Formats to pass to output file plugin</param> /// <exception cref="InvalidOperationException"> /// If the provided resume does not correspond with the current in progress /// dump /// </exception> internal static void Dump(Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses, bool force, bool dumpRaw, bool persistent, bool stopOnError, Dictionary <MediaTagType, byte[]> mediaTags, ref MediaType dskType, ref Resume resume, ref DumpLog dumpLog, Encoding encoding, string outputPrefix, string outputPath, Dictionary <string, string> formatOptions, CICMMetadataType preSidecar, uint skip, bool nometadata, bool notrim) { bool sense; ulong blocks; const uint BLOCK_SIZE = 2048; uint blocksToRead = 64; DateTime start; DateTime end; double totalDuration = 0; double currentSpeed = 0; double maxSpeed = double.MinValue; double minSpeed = double.MaxValue; bool aborted = false; System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true; if (mediaTags.ContainsKey(MediaTagType.DVD_PFI)) { mediaTags.Remove(MediaTagType.DVD_PFI); } if (mediaTags.ContainsKey(MediaTagType.DVD_DMI)) { mediaTags.Remove(MediaTagType.DVD_DMI); } dumpLog.WriteLine("Reading Xbox Security Sector."); sense = dev.KreonExtractSs(out byte[] ssBuf, out byte[] senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot get Xbox Security Sector, not continuing."); DicConsole.ErrorWriteLine("Cannot get Xbox Security Sector, not continuing."); return; } dumpLog.WriteLine("Decoding Xbox Security Sector."); SS.SecuritySector?xboxSs = SS.Decode(ssBuf); if (!xboxSs.HasValue) { dumpLog.WriteLine("Cannot decode Xbox Security Sector, not continuing."); DicConsole.ErrorWriteLine("Cannot decode Xbox Security Sector, not continuing."); return; } byte[] tmpBuf = new byte[ssBuf.Length - 4]; Array.Copy(ssBuf, 4, tmpBuf, 0, ssBuf.Length - 4); mediaTags.Add(MediaTagType.Xbox_SecuritySector, tmpBuf); ulong l0Video, l1Video, middleZone, gameSize, totalSize, layerBreak; // Get video partition size DicConsole.DebugWriteLine("Dump-media command", "Getting video partition size"); dumpLog.WriteLine("Locking drive."); sense = dev.KreonLock(out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot lock drive, not continuing."); DicConsole.ErrorWriteLine("Cannot lock drive, not continuing."); return; } dumpLog.WriteLine("Getting video partition size."); sense = dev.ReadCapacity(out byte[] readBuffer, out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot get disc capacity."); DicConsole.ErrorWriteLine("Cannot get disc capacity."); return; } totalSize = (ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]); dumpLog.WriteLine("Reading Physical Format Information."); sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); if (sense) { dumpLog.WriteLine("Cannot get PFI."); DicConsole.ErrorWriteLine("Cannot get PFI."); return; } tmpBuf = new byte[readBuffer.Length - 4]; Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); mediaTags.Add(MediaTagType.DVD_PFI, tmpBuf); DicConsole.DebugWriteLine("Dump-media command", "Video partition total size: {0} sectors", totalSize); l0Video = PFI.Decode(readBuffer).Value.Layer0EndPSN - PFI.Decode(readBuffer).Value.DataAreaStartPSN + 1; l1Video = totalSize - l0Video + 1; dumpLog.WriteLine("Reading Disc Manufacturing Information."); sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, MmcDiscStructureFormat.DiscManufacturingInformation, 0, 0, out _); if (sense) { dumpLog.WriteLine("Cannot get DMI."); DicConsole.ErrorWriteLine("Cannot get DMI."); return; } tmpBuf = new byte[readBuffer.Length - 4]; Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); mediaTags.Add(MediaTagType.DVD_DMI, tmpBuf); // Get game partition size DicConsole.DebugWriteLine("Dump-media command", "Getting game partition size"); dumpLog.WriteLine("Unlocking drive (Xtreme)."); sense = dev.KreonUnlockXtreme(out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot unlock drive, not continuing."); DicConsole.ErrorWriteLine("Cannot unlock drive, not continuing."); return; } dumpLog.WriteLine("Getting game partition size."); sense = dev.ReadCapacity(out readBuffer, out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot get disc capacity."); DicConsole.ErrorWriteLine("Cannot get disc capacity."); return; } gameSize = (ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]) + 1; DicConsole.DebugWriteLine("Dump-media command", "Game partition total size: {0} sectors", gameSize); // Get middle zone size DicConsole.DebugWriteLine("Dump-media command", "Getting middle zone size"); dumpLog.WriteLine("Unlocking drive (Wxripper)."); sense = dev.KreonUnlockWxripper(out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot unlock drive, not continuing."); DicConsole.ErrorWriteLine("Cannot unlock drive, not continuing."); return; } dumpLog.WriteLine("Getting disc size."); sense = dev.ReadCapacity(out readBuffer, out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot get disc capacity."); DicConsole.ErrorWriteLine("Cannot get disc capacity."); return; } totalSize = (ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]); dumpLog.WriteLine("Reading Physical Format Information."); sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); if (sense) { dumpLog.WriteLine("Cannot get PFI."); DicConsole.ErrorWriteLine("Cannot get PFI."); return; } DicConsole.DebugWriteLine("Dump-media command", "Unlocked total size: {0} sectors", totalSize); blocks = totalSize + 1; middleZone = totalSize - (PFI.Decode(readBuffer).Value.Layer0EndPSN - PFI.Decode(readBuffer).Value.DataAreaStartPSN + 1) - gameSize + 1; tmpBuf = new byte[readBuffer.Length - 4]; Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); mediaTags.Add(MediaTagType.Xbox_PFI, tmpBuf); dumpLog.WriteLine("Reading Disc Manufacturing Information."); sense = dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, MmcDiscStructureFormat.DiscManufacturingInformation, 0, 0, out _); if (sense) { dumpLog.WriteLine("Cannot get DMI."); DicConsole.ErrorWriteLine("Cannot get DMI."); return; } tmpBuf = new byte[readBuffer.Length - 4]; Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); mediaTags.Add(MediaTagType.Xbox_DMI, tmpBuf); totalSize = l0Video + l1Video + middleZone * 2 + gameSize; layerBreak = l0Video + middleZone + gameSize / 2; DicConsole.WriteLine("Video layer 0 size: {0} sectors", l0Video); DicConsole.WriteLine("Video layer 1 size: {0} sectors", l1Video); DicConsole.WriteLine("Middle zone size: {0} sectors", middleZone); DicConsole.WriteLine("Game data size: {0} sectors", gameSize); DicConsole.WriteLine("Total size: {0} sectors", totalSize); DicConsole.WriteLine("Real layer break: {0}", layerBreak); DicConsole.WriteLine(); dumpLog.WriteLine("Video layer 0 size: {0} sectors", l0Video); dumpLog.WriteLine("Video layer 1 size: {0} sectors", l1Video); dumpLog.WriteLine("Middle zone 0 size: {0} sectors", middleZone); dumpLog.WriteLine("Game data 0 size: {0} sectors", gameSize); dumpLog.WriteLine("Total 0 size: {0} sectors", totalSize); dumpLog.WriteLine("Real layer break: {0}", layerBreak); bool read12 = !dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, 0, BLOCK_SIZE, 0, 1, false, dev.Timeout, out _); if (!read12) { dumpLog.WriteLine("Cannot read medium, aborting scan..."); DicConsole.ErrorWriteLine("Cannot read medium, aborting scan..."); return; } dumpLog.WriteLine("Using SCSI READ (12) command."); DicConsole.WriteLine("Using SCSI READ (12) command."); while (true) { if (read12) { sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, 0, BLOCK_SIZE, 0, blocksToRead, false, dev.Timeout, out _); if (sense || dev.Error) { blocksToRead /= 2; } } if (!dev.Error || blocksToRead == 1) { break; } } if (dev.Error) { dumpLog.WriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError); DicConsole.ErrorWriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError); return; } if (skip < blocksToRead) { skip = blocksToRead; } bool ret = true; foreach (MediaTagType tag in mediaTags.Keys) { if (outputPlugin.SupportedMediaTags.Contains(tag)) { continue; } ret = false; dumpLog.WriteLine($"Output format does not support {tag}."); DicConsole.ErrorWriteLine($"Output format does not support {tag}."); } if (!ret) { dumpLog.WriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not "); DicConsole.ErrorWriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not "); if (!force) { return; } } dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead); DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead); MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, BLOCK_SIZE, blocksToRead); IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", 0x0010); ret = outputPlugin.Create(outputPath, dskType, formatOptions, blocks, BLOCK_SIZE); // Cannot create image if (!ret) { dumpLog.WriteLine("Error creating output image, not continuing."); dumpLog.WriteLine(outputPlugin.ErrorMessage); DicConsole.ErrorWriteLine("Error creating output image, not continuing."); DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage); return; } start = DateTime.UtcNow; double imageWriteDuration = 0; double cmdDuration = 0; uint saveBlocksToRead = blocksToRead; DumpHardwareType currentTry = null; ExtentsULong extents = null; ResumeSupport.Process(true, true, totalSize, dev.Manufacturer, dev.Model, dev.Serial, dev.PlatformId, ref resume, ref currentTry, ref extents); if (currentTry == null || extents == null) { throw new NotImplementedException("Could not process resume file, not continuing..."); } outputPlugin.SetTracks(new List <Track> { new Track { TrackBytesPerSector = (int)BLOCK_SIZE, TrackEndSector = blocks - 1, TrackSequence = 1, TrackRawBytesPerSector = (int)BLOCK_SIZE, TrackSubchannelType = TrackSubchannelType.None, TrackSession = 1, TrackType = TrackType.Data } }); ulong currentSector = resume.NextBlock; if (resume.NextBlock > 0) { dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock); } bool newTrim = false; dumpLog.WriteLine("Reading game partition."); for (int e = 0; e <= 16; e++) { if (aborted) { resume.NextBlock = currentSector; currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } if (currentSector >= blocks) { break; } ulong extentStart, extentEnd; // Extents if (e < 16) { if (xboxSs.Value.Extents[e].StartPSN <= xboxSs.Value.Layer0EndPSN) { extentStart = xboxSs.Value.Extents[e].StartPSN - 0x30000; } else { extentStart = (xboxSs.Value.Layer0EndPSN + 1) * 2 - ((xboxSs.Value.Extents[e].StartPSN ^ 0xFFFFFF) + 1) - 0x30000; } if (xboxSs.Value.Extents[e].EndPSN <= xboxSs.Value.Layer0EndPSN) { extentEnd = xboxSs.Value.Extents[e].EndPSN - 0x30000; } else { extentEnd = (xboxSs.Value.Layer0EndPSN + 1) * 2 - ((xboxSs.Value.Extents[e].EndPSN ^ 0xFFFFFF) + 1) - 0x30000; } } // After last extent else { extentStart = blocks; extentEnd = blocks; } if (currentSector > extentEnd) { continue; } for (ulong i = currentSector; i < extentStart; i += blocksToRead) { saveBlocksToRead = blocksToRead; if (aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } if (extentStart - i < blocksToRead) { blocksToRead = (uint)(extentStart - i); } #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator if (currentSpeed > maxSpeed && currentSpeed != 0) { maxSpeed = currentSpeed; } if (currentSpeed < minSpeed && currentSpeed != 0) { minSpeed = currentSpeed; } #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, totalSize, currentSpeed); sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)i, BLOCK_SIZE, 0, blocksToRead, false, dev.Timeout, out cmdDuration); totalDuration += cmdDuration; if (!sense && !dev.Error) { mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); DateTime writeStart = DateTime.Now; outputPlugin.WriteSectors(readBuffer, i, blocksToRead); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; extents.Add(i, blocksToRead, true); } 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; outputPlugin.WriteSectors(new byte[BLOCK_SIZE * 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; string[] senseLines = Sense.PrettifySense(senseBuf).Split(new[] { Environment.NewLine }, StringSplitOptions .RemoveEmptyEntries); foreach (string senseLine in senseLines) { dumpLog.WriteLine(senseLine); } newTrim = true; } double newSpeed = (double)BLOCK_SIZE * blocksToRead / 1048576 / (cmdDuration / 1000); if (!double.IsInfinity(newSpeed)) { currentSpeed = newSpeed; } blocksToRead = saveBlocksToRead; currentSector = i + 1; resume.NextBlock = currentSector; } for (ulong i = extentStart; i <= extentEnd; i += blocksToRead) { saveBlocksToRead = blocksToRead; if (aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } if (extentEnd - i < blocksToRead) { blocksToRead = (uint)(extentEnd - i) + 1; } mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); // Write empty data DateTime writeStart = DateTime.Now; outputPlugin.WriteSectors(new byte[BLOCK_SIZE * blocksToRead], i, blocksToRead); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; blocksToRead = saveBlocksToRead; extents.Add(i, blocksToRead, true); currentSector = i + 1; resume.NextBlock = currentSector; } if (!aborted) { currentSector = extentEnd + 1; } } // Middle Zone D dumpLog.WriteLine("Writing Middle Zone D (empty)."); for (ulong middle = currentSector - blocks - 1; middle < middleZone - 1; middle += blocksToRead) { if (aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } if (middleZone - 1 - middle < blocksToRead) { blocksToRead = (uint)(middleZone - 1 - middle); } DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", middle + currentSector, totalSize, currentSpeed); mhddLog.Write(middle + currentSector, cmdDuration); ibgLog.Write(middle + currentSector, currentSpeed * 1024); // Write empty data DateTime writeStart = DateTime.Now; outputPlugin.WriteSectors(new byte[BLOCK_SIZE * blocksToRead], middle + currentSector, blocksToRead); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; extents.Add(currentSector, blocksToRead, true); currentSector += blocksToRead; resume.NextBlock = currentSector; } blocksToRead = saveBlocksToRead; dumpLog.WriteLine("Locking drive."); sense = dev.KreonLock(out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot lock drive, not continuing."); DicConsole.ErrorWriteLine("Cannot lock drive, not continuing."); return; } sense = dev.ReadCapacity(out readBuffer, out senseBuf, dev.Timeout, out _); if (sense) { DicConsole.ErrorWriteLine("Cannot get disc capacity."); return; } // Video Layer 1 dumpLog.WriteLine("Reading Video Layer 1."); for (ulong l1 = currentSector - blocks - middleZone + l0Video; l1 < l0Video + l1Video; l1 += blocksToRead) { if (aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } if (l0Video + l1Video - l1 < blocksToRead) { blocksToRead = (uint)(l0Video + l1Video - l1); } #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator if (currentSpeed > maxSpeed && currentSpeed != 0) { maxSpeed = currentSpeed; } if (currentSpeed < minSpeed && currentSpeed != 0) { minSpeed = currentSpeed; } #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", currentSector, totalSize, currentSpeed); sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)l1, BLOCK_SIZE, 0, blocksToRead, false, dev.Timeout, out cmdDuration); totalDuration += cmdDuration; if (!sense && !dev.Error) { mhddLog.Write(currentSector, cmdDuration); ibgLog.Write(currentSector, currentSpeed * 1024); DateTime writeStart = DateTime.Now; outputPlugin.WriteSectors(readBuffer, currentSector, blocksToRead); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; extents.Add(currentSector, blocksToRead, true); } else { // TODO: Reset device after X errors if (stopOnError) { return; // TODO: Return more cleanly } // Write empty data DateTime writeStart = DateTime.Now; outputPlugin.WriteSectors(new byte[BLOCK_SIZE * skip], currentSector, skip); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; // TODO: Handle errors in video partition //errored += blocksToRead; //resume.BadBlocks.Add(l1); DicConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); mhddLog.Write(l1, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(l1, 0); dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", skip, l1); l1 += skip - blocksToRead; string[] senseLines = Sense.PrettifySense(senseBuf).Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); foreach (string senseLine in senseLines) { dumpLog.WriteLine(senseLine); } } double newSpeed = (double)BLOCK_SIZE * blocksToRead / 1048576 / (cmdDuration / 1000); if (!double.IsInfinity(newSpeed)) { currentSpeed = newSpeed; } currentSector += blocksToRead; resume.NextBlock = currentSector; } dumpLog.WriteLine("Unlocking drive (Wxripper)."); sense = dev.KreonUnlockWxripper(out senseBuf, dev.Timeout, out _); if (sense) { dumpLog.WriteLine("Cannot unlock drive, not continuing."); DicConsole.ErrorWriteLine("Cannot unlock drive, not continuing."); return; } sense = dev.ReadCapacity(out readBuffer, out senseBuf, dev.Timeout, out _); if (sense) { DicConsole.ErrorWriteLine("Cannot get disc capacity."); return; } end = DateTime.UtcNow; DicConsole.WriteLine(); mhddLog.Close(); ibgLog.Close(dev, blocks, BLOCK_SIZE, (end - start).TotalSeconds, currentSpeed * 1024, BLOCK_SIZE * (double)(blocks + 1) / 1024 / (totalDuration / 1000), devicePath); dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", (double)BLOCK_SIZE * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", (double)BLOCK_SIZE * (double)(blocks + 1) / 1024 / imageWriteDuration); #region Trimming if (resume.BadBlocks.Count > 0 && !aborted && !notrim && newTrim) { start = DateTime.UtcNow; dumpLog.WriteLine("Trimming bad sectors"); ulong[] tmpArray = resume.BadBlocks.ToArray(); foreach (ulong badSector in tmpArray) { if (aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } DicConsole.Write("\rTrimming sector {0}", badSector); sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)badSector, BLOCK_SIZE, 0, 1, false, dev.Timeout, out cmdDuration); totalDuration += cmdDuration; if (sense || dev.Error) { continue; } resume.BadBlocks.Remove(badSector); extents.Add(badSector); outputPlugin.WriteSector(readBuffer, badSector); } DicConsole.WriteLine(); end = DateTime.UtcNow; dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds); } #endregion Trimming #region Error handling if (resume.BadBlocks.Count > 0 && !aborted && retryPasses > 0) { List <ulong> tmpList = new List <ulong>(); foreach (ulong ur in resume.BadBlocks) { for (ulong i = ur; i < ur + blocksToRead; i++) { tmpList.Add(i); } } tmpList.Sort(); int pass = 1; bool forward = true; bool runningPersistent = false; resume.BadBlocks = tmpList; Modes.ModePage?currentModePage = null; byte[] md6; byte[] md10; if (persistent) { Modes.ModePage_01_MMC pgMmc; sense = dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, dev.Timeout, out _); if (sense) { sense = dev.ModeSense10(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, dev.Timeout, out _); if (!sense) { Modes.DecodedMode?dcMode10 = Modes.DecodeMode10(readBuffer, PeripheralDeviceTypes.MultiMediaDevice); if (dcMode10.HasValue) { foreach (Modes.ModePage modePage in dcMode10.Value.Pages) { if (modePage.Page == 0x01 && modePage.Subpage == 0x00) { currentModePage = modePage; } } } } } else { Modes.DecodedMode?dcMode6 = Modes.DecodeMode6(readBuffer, PeripheralDeviceTypes.MultiMediaDevice); if (dcMode6.HasValue) { 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 = 0x20, 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 }; Modes.DecodedMode 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 (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) { DicConsole .WriteLine("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; } } repeatRetry: ulong[] tmpArray = resume.BadBlocks.ToArray(); foreach (ulong badSector in tmpArray) { if (aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); dumpLog.WriteLine("Aborted!"); break; } DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass, forward ? "forward" : "reverse", runningPersistent ? "recovering partial data, " : ""); sense = dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)badSector, BLOCK_SIZE, 0, 1, false, dev.Timeout, out cmdDuration); totalDuration += cmdDuration; if (!sense && !dev.Error) { resume.BadBlocks.Remove(badSector); extents.Add(badSector); outputPlugin.WriteSector(readBuffer, badSector); dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } else if (runningPersistent) { outputPlugin.WriteSector(readBuffer, badSector); } } if (pass < retryPasses && !aborted && resume.BadBlocks.Count > 0) { pass++; forward = !forward; resume.BadBlocks.Sort(); resume.BadBlocks.Reverse(); goto repeatRetry; } if (runningPersistent && currentModePage.HasValue) { Modes.DecodedMode 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 _); } } DicConsole.WriteLine(); } #endregion Error handling resume.BadBlocks.Sort(); currentTry.Extents = ExtentsConverter.ToMetadata(extents); 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); } 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."); DicConsole.WriteLine("Closing output file."); DateTime closeStart = DateTime.Now; outputPlugin.Close(); DateTime closeEnd = DateTime.Now; dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); if (aborted) { dumpLog.WriteLine("Aborted!"); return; } double totalChkDuration = 0; if (!nometadata) { dumpLog.WriteLine("Creating sidecar."); FiltersList filters = new FiltersList(); IFilter filter = filters.GetFilter(outputPath); IMediaImage inputPlugin = ImageFormat.Detect(filter); if (!inputPlugin.Open(filter)) { throw new ArgumentException("Could not open created image."); } DateTime chkStart = DateTime.UtcNow; CICMMetadataType sidecar = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding); end = DateTime.UtcNow; if (preSidecar != null) { preSidecar.OpticalDisc = sidecar.OpticalDisc; sidecar = preSidecar; } totalChkDuration = (end - chkStart).TotalMilliseconds; dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", (double)BLOCK_SIZE * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); foreach (KeyValuePair <MediaTagType, byte[]> tag in mediaTags) { Mmc.AddMediaTagToSidecar(outputPath, tag, ref sidecar); } 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((ulong)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].Layers = new LayersType { type = LayersTypeType.OTP, typeSpecified = true, Sectors = new SectorsType[1] }; sidecar.OpticalDisc[0].Layers.Sectors[0] = new SectorsType { Value = (long)layerBreak }; sidecar.OpticalDisc[0].Sessions = 1; sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp, out string xmlDskSubTyp); sidecar.OpticalDisc[0].DiscType = xmlDskTyp; sidecar.OpticalDisc[0].DiscSubType = xmlDskSubTyp; foreach (KeyValuePair <MediaTagType, byte[]> tag in mediaTags) { if (outputPlugin.SupportedMediaTags.Contains(tag.Key)) { Mmc.AddMediaTagToSidecar(outputPath, tag, ref sidecar); } } DicConsole.WriteLine("Writing metadata sidecar"); FileStream xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create); XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType)); xmlSer.Serialize(xmlFs, sidecar); xmlFs.Close(); } DicConsole.WriteLine(); DicConsole.WriteLine("Took a total of {0:F3} seconds ({1:F3} processing commands, {2:F3} checksumming, {3:F3} writing, {4:F3} closing).", (end - start).TotalSeconds, totalDuration / 1000, totalChkDuration / 1000, imageWriteDuration, (closeEnd - closeStart).TotalSeconds); DicConsole.WriteLine("Avegare speed: {0:F3} MiB/sec.", (double)BLOCK_SIZE * (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(); Statistics.AddMedia(dskType, true); }