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 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 _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; /// Initializes dumpers /// Should resume? /// Device /// Path to the device /// Prefix for output log files /// Plugin for output file /// How many times to retry /// Force to continue dump whenever possible /// Dump long sectors /// Store whatever data the drive returned on error /// Stop dump on first error /// Information for dump resuming /// Dump logger /// Encoding to use when analyzing dump /// Path to output file /// Formats to pass to output file plugin /// Do not trim errors from skipped sectors /// Try to read and dump as much first track pregap as possible /// Sidecar to store in dumped image /// How many sectors to skip reading on error /// Create metadata sidecar after dump? 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 formatOptions, CICMMetadataType preSidecar, uint skip, bool nometadata, bool notrim, bool dumpFirstTrackPregap, bool fixOffset, bool debug, DumpSubchannel subchannel, int speed) { _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; } /// Starts dumping with the stablished fields and autodetecting the device type public void Start() { // 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) { 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(); StoppingErrorMessage?.Invoke("Unknown device type."); return; } _dumpLog.Close(); if(_resume == null || !_doResume) return; _resume.LastWriteDate = DateTime.UtcNow; _resume.BadBlocks.Sort(); if(File.Exists(_outputPrefix + ".resume.xml")) File.Delete(_outputPrefix + ".resume.xml"); 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(); } /// Event raised when the progress bar is not longer needed public event EndProgressHandler EndProgress; /// Event raised when a progress bar is needed public event InitProgressHandler InitProgress; /// Event raised to report status updates public event UpdateStatusHandler UpdateStatus; /// Event raised to report a non-fatal error public event ErrorMessageHandler ErrorMessage; /// Event raised to report a fatal error that stops the dumping operation and should call user's attention public event ErrorMessageHandler StoppingErrorMessage; /// Event raised to update the values of a determinate progress bar public event UpdateProgressHandler UpdateProgress; /// Event raised to update the status of an undeterminate progress bar public event PulseProgressHandler PulseProgress; /// Event raised when the progress bar is not longer needed public event EndProgressHandler2 EndProgress2; /// Event raised when a progress bar is needed public event InitProgressHandler2 InitProgress2; /// Event raised to update the values of a determinate progress bar public event UpdateProgressHandler2 UpdateProgress2; } }