2019-04-20 13:23:58 +01:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
2019-12-26 01:34:24 +00:00
|
|
|
using System.Linq;
|
2019-04-20 13:23:58 +01:00
|
|
|
using System.Text;
|
|
|
|
|
using System.Xml.Serialization;
|
|
|
|
|
using DiscImageChef.CommonTypes.Enums;
|
|
|
|
|
using DiscImageChef.CommonTypes.Interfaces;
|
|
|
|
|
using DiscImageChef.CommonTypes.Metadata;
|
|
|
|
|
using DiscImageChef.Core.Logging;
|
2019-12-26 01:34:24 +00:00
|
|
|
using DiscImageChef.Database;
|
2019-04-20 13:23:58 +01:00
|
|
|
using DiscImageChef.Devices;
|
|
|
|
|
using Schemas;
|
|
|
|
|
|
2019-04-19 17:23:54 +01:00
|
|
|
namespace DiscImageChef.Core.Devices.Dumping
|
|
|
|
|
{
|
|
|
|
|
public partial class Dump
|
|
|
|
|
{
|
2019-12-26 01:34:24 +00:00
|
|
|
readonly bool _debug;
|
2019-12-25 18:07:05 +00:00
|
|
|
readonly Device _dev;
|
|
|
|
|
readonly string _devicePath;
|
|
|
|
|
readonly bool _doResume;
|
|
|
|
|
readonly DumpLog _dumpLog;
|
|
|
|
|
readonly bool _dumpRaw;
|
|
|
|
|
readonly Encoding _encoding;
|
|
|
|
|
readonly bool _force;
|
|
|
|
|
readonly Dictionary<string, string> _formatOptions;
|
|
|
|
|
readonly bool _nometadata;
|
|
|
|
|
readonly bool _notrim;
|
|
|
|
|
readonly string _outputPath;
|
|
|
|
|
readonly IWritableImage _outputPlugin;
|
|
|
|
|
readonly string _outputPrefix;
|
|
|
|
|
readonly bool _persistent;
|
|
|
|
|
readonly CICMMetadataType _preSidecar;
|
|
|
|
|
readonly ushort _retryPasses;
|
|
|
|
|
readonly bool _stopOnError;
|
|
|
|
|
bool _aborted;
|
2019-12-26 01:34:24 +00:00
|
|
|
DicContext _ctx; // Master database context
|
|
|
|
|
Database.Models.Device _dbDev; // Device database entry
|
2019-12-25 18:07:05 +00:00
|
|
|
bool _dumpFirstTrackPregap;
|
2019-12-25 18:25:25 +00:00
|
|
|
bool _fixOffset;
|
2019-12-26 01:34:24 +00:00
|
|
|
uint _maximumReadable; // Maximum number of sectors drive can read at once
|
2019-12-25 18:07:05 +00:00
|
|
|
Resume _resume;
|
|
|
|
|
Sidecar _sidecarClass;
|
|
|
|
|
uint _skip;
|
2019-04-20 13:23:58 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
/// <summary>Initializes dumpers</summary>
|
2019-04-20 16:24:17 +01:00
|
|
|
/// <param name="doResume">Should resume?</param>
|
2019-04-20 13:23:58 +01:00
|
|
|
/// <param name="dev">Device</param>
|
|
|
|
|
/// <param name="devicePath">Path to the device</param>
|
|
|
|
|
/// <param name="outputPrefix">Prefix for output log 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 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="outputPath">Path to output file</param>
|
|
|
|
|
/// <param name="formatOptions">Formats to pass to output file plugin</param>
|
2019-04-20 16:24:17 +01:00
|
|
|
/// <param name="notrim">Do not trim errors from skipped sectors</param>
|
2019-04-20 13:23:58 +01:00
|
|
|
/// <param name="dumpFirstTrackPregap">Try to read and dump as much first track pregap as possible</param>
|
2019-04-20 16:24:17 +01:00
|
|
|
/// <param name="preSidecar">Sidecar to store in dumped image</param>
|
|
|
|
|
/// <param name="skip">How many sectors to skip reading on error</param>
|
|
|
|
|
/// <param name="nometadata">Create metadata sidecar after dump?</param>
|
2019-12-25 18:07:05 +00:00
|
|
|
public Dump(bool doResume, Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses,
|
|
|
|
|
bool force, bool dumpRaw, bool persistent, bool stopOnError, Resume resume, DumpLog dumpLog,
|
|
|
|
|
Encoding encoding, string outputPrefix, string outputPath, Dictionary<string, string> formatOptions,
|
2019-12-25 18:25:25 +00:00
|
|
|
CICMMetadataType preSidecar, uint skip, bool nometadata, bool notrim, bool dumpFirstTrackPregap,
|
2019-12-26 00:48:21 +00:00
|
|
|
bool fixOffset, bool debug)
|
2019-04-20 13:23:58 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_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;
|
2019-12-25 18:25:25 +00:00
|
|
|
_fixOffset = fixOffset;
|
2019-12-26 00:48:21 +00:00
|
|
|
_debug = debug;
|
2019-12-26 01:34:24 +00:00
|
|
|
_maximumReadable = 64;
|
2019-04-20 13:23:58 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
/// <summary>Starts dumping with the stablished fields and autodetecting the device type</summary>
|
2019-04-20 13:23:58 +01:00
|
|
|
public void Start()
|
|
|
|
|
{
|
2019-12-26 01:34:24 +00:00
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_dev.IsUsb &&
|
|
|
|
|
_dev.UsbVendorId == 0x054C &&
|
|
|
|
|
(_dev.UsbProductId == 0x01C8 || _dev.UsbProductId == 0x01C9 || _dev.UsbProductId == 0x02D2))
|
2019-04-20 13:23:58 +01:00
|
|
|
PlayStationPortable();
|
|
|
|
|
else
|
2019-12-25 18:07:05 +00:00
|
|
|
switch(_dev.Type)
|
2019-04-20 13:23:58 +01:00
|
|
|
{
|
|
|
|
|
case DeviceType.ATA:
|
|
|
|
|
Ata();
|
2019-12-25 18:07:05 +00:00
|
|
|
|
2019-04-20 13:23:58 +01:00
|
|
|
break;
|
|
|
|
|
case DeviceType.MMC:
|
|
|
|
|
case DeviceType.SecureDigital:
|
|
|
|
|
SecureDigital();
|
2019-12-25 18:07:05 +00:00
|
|
|
|
2019-04-20 13:23:58 +01:00
|
|
|
break;
|
|
|
|
|
case DeviceType.NVMe:
|
|
|
|
|
NVMe();
|
2019-12-25 18:07:05 +00:00
|
|
|
|
2019-04-20 13:23:58 +01:00
|
|
|
break;
|
|
|
|
|
case DeviceType.ATAPI:
|
|
|
|
|
case DeviceType.SCSI:
|
|
|
|
|
Scsi();
|
2019-12-25 18:07:05 +00:00
|
|
|
|
2019-04-20 13:23:58 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Unknown device type.");
|
|
|
|
|
_dumpLog.Close();
|
2019-04-20 14:02:25 +01:00
|
|
|
StoppingErrorMessage?.Invoke("Unknown device type.");
|
2019-12-25 18:07:05 +00:00
|
|
|
|
2019-04-20 14:02:25 +01:00
|
|
|
return;
|
2019-04-20 13:23:58 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.Close();
|
2019-04-20 13:23:58 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_resume == null ||
|
|
|
|
|
!_doResume)
|
|
|
|
|
return;
|
2019-04-20 13:23:58 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_resume.LastWriteDate = DateTime.UtcNow;
|
|
|
|
|
_resume.BadBlocks.Sort();
|
2019-04-20 13:23:58 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(File.Exists(_outputPrefix + ".resume.xml"))
|
|
|
|
|
File.Delete(_outputPrefix + ".resume.xml");
|
2019-04-20 13:23:58 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
var fs = new FileStream(_outputPrefix + ".resume.xml", FileMode.Create, FileAccess.ReadWrite);
|
|
|
|
|
var xs = new XmlSerializer(_resume.GetType());
|
|
|
|
|
xs.Serialize(fs, _resume);
|
2019-04-20 13:23:58 +01:00
|
|
|
fs.Close();
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-20 16:40:12 +01:00
|
|
|
public void Abort()
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_aborted = true;
|
|
|
|
|
_sidecarClass?.Abort();
|
2019-04-20 16:40:12 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
/// <summary>Event raised when the progress bar is not longer needed</summary>
|
2019-04-20 16:24:17 +01:00
|
|
|
public event EndProgressHandler EndProgress;
|
2019-12-25 18:07:05 +00:00
|
|
|
/// <summary>Event raised when a progress bar is needed</summary>
|
2019-04-20 16:24:17 +01:00
|
|
|
public event InitProgressHandler InitProgress;
|
2019-12-25 18:07:05 +00:00
|
|
|
/// <summary>Event raised to report status updates</summary>
|
2019-04-20 16:24:17 +01:00
|
|
|
public event UpdateStatusHandler UpdateStatus;
|
2019-12-25 18:07:05 +00:00
|
|
|
/// <summary>Event raised to report a non-fatal error</summary>
|
2019-04-20 16:24:17 +01:00
|
|
|
public event ErrorMessageHandler ErrorMessage;
|
2019-12-25 18:07:05 +00:00
|
|
|
/// <summary>Event raised to report a fatal error that stops the dumping operation and should call user's attention</summary>
|
2019-04-20 16:24:17 +01:00
|
|
|
public event ErrorMessageHandler StoppingErrorMessage;
|
2019-12-25 18:07:05 +00:00
|
|
|
/// <summary>Event raised to update the values of a determinate progress bar</summary>
|
2019-04-19 17:45:32 +01:00
|
|
|
public event UpdateProgressHandler UpdateProgress;
|
2019-12-25 18:07:05 +00:00
|
|
|
/// <summary>Event raised to update the status of an undeterminate progress bar</summary>
|
2019-04-20 16:24:17 +01:00
|
|
|
public event PulseProgressHandler PulseProgress;
|
2019-12-25 18:07:05 +00:00
|
|
|
/// <summary>Event raised when the progress bar is not longer needed</summary>
|
2019-04-20 18:11:02 +01:00
|
|
|
public event EndProgressHandler2 EndProgress2;
|
2019-12-25 18:07:05 +00:00
|
|
|
/// <summary>Event raised when a progress bar is needed</summary>
|
2019-04-20 18:11:02 +01:00
|
|
|
public event InitProgressHandler2 InitProgress2;
|
2019-12-25 18:07:05 +00:00
|
|
|
/// <summary>Event raised to update the values of a determinate progress bar</summary>
|
2019-04-20 18:11:02 +01:00
|
|
|
public event UpdateProgressHandler2 UpdateProgress2;
|
2019-04-19 17:23:54 +01:00
|
|
|
}
|
|
|
|
|
}
|