mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
Undo commit 672ccb7e44.
This commit is contained in:
@@ -37,10 +37,12 @@ using System.CommandLine.Invocation;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Xml.Serialization;
|
||||
using Aaru.CommonTypes;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Interop;
|
||||
using Aaru.CommonTypes.Metadata;
|
||||
using Aaru.CommonTypes.Structs.Devices.SCSI;
|
||||
using Aaru.Console;
|
||||
@@ -173,7 +175,10 @@ namespace Aaru.Commands.Media
|
||||
|
||||
AddArgument(new Argument<string>
|
||||
{
|
||||
Arity = ArgumentArity.ExactlyOne, Description = "Output image path", Name = "output-path"
|
||||
Arity = ArgumentArity.ExactlyOne,
|
||||
Description =
|
||||
"Output image path. If filename starts with # and exists, it will be read as a list of output images, its extension will be used to detect the image output format, each media will be ejected and confirmation for the next one will be asked.",
|
||||
Name = "output-path"
|
||||
});
|
||||
|
||||
Add(new Option(new[]
|
||||
@@ -351,103 +356,30 @@ namespace Aaru.Commands.Media
|
||||
break;
|
||||
}
|
||||
|
||||
if(devicePath.Length == 2 &&
|
||||
devicePath[1] == ':' &&
|
||||
devicePath[0] != '/' &&
|
||||
char.IsLetter(devicePath[0]))
|
||||
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
|
||||
string filename = Path.GetFileNameWithoutExtension(outputPath);
|
||||
|
||||
Devices.Device dev;
|
||||
bool isResponse = filename.StartsWith("#", StringComparison.OrdinalIgnoreCase) &&
|
||||
File.Exists(Path.Combine(Path.GetDirectoryName(outputPath),
|
||||
Path.GetFileNameWithoutExtension(outputPath)));
|
||||
|
||||
try
|
||||
{
|
||||
dev = new Devices.Device(devicePath);
|
||||
TextReader resReader;
|
||||
|
||||
if(dev.IsRemote)
|
||||
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem,
|
||||
dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture);
|
||||
if(isResponse)
|
||||
resReader = new StreamReader(Path.Combine(Path.GetDirectoryName(outputPath),
|
||||
Path.GetFileNameWithoutExtension(outputPath)));
|
||||
else
|
||||
resReader = new StringReader(Path.GetFileNameWithoutExtension(outputPath));
|
||||
|
||||
if(dev.Error)
|
||||
{
|
||||
AaruConsole.ErrorWriteLine(Error.Print(dev.LastError));
|
||||
|
||||
return (int)ErrorNumber.CannotOpenDevice;
|
||||
}
|
||||
}
|
||||
catch(DeviceException e)
|
||||
{
|
||||
AaruConsole.ErrorWriteLine(e.Message ?? Error.Print(e.LastError));
|
||||
|
||||
return (int)ErrorNumber.CannotOpenDevice;
|
||||
}
|
||||
|
||||
Statistics.AddDevice(dev);
|
||||
|
||||
string outputPrefix = Path.Combine(Path.GetDirectoryName(outputPath),
|
||||
Path.GetFileNameWithoutExtension(outputPath));
|
||||
|
||||
Resume resumeClass = null;
|
||||
var xs = new XmlSerializer(typeof(Resume));
|
||||
|
||||
if(File.Exists(outputPrefix + ".resume.xml") && resume)
|
||||
try
|
||||
{
|
||||
var sr = new StreamReader(outputPrefix + ".resume.xml");
|
||||
resumeClass = (Resume)xs.Deserialize(sr);
|
||||
sr.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
AaruConsole.ErrorWriteLine("Incorrect resume file, not continuing...");
|
||||
|
||||
return (int)ErrorNumber.InvalidResume;
|
||||
}
|
||||
|
||||
if(resumeClass != null &&
|
||||
resumeClass.NextBlock > resumeClass.LastBlock &&
|
||||
resumeClass.BadBlocks.Count == 0 &&
|
||||
!resumeClass.Tape &&
|
||||
(resumeClass.BadSubchannels is null || resumeClass.BadSubchannels.Count == 0))
|
||||
{
|
||||
AaruConsole.WriteLine("Media already dumped correctly, not continuing...");
|
||||
|
||||
return (int)ErrorNumber.AlreadyDumped;
|
||||
}
|
||||
|
||||
CICMMetadataType sidecar = null;
|
||||
var sidecarXs = new XmlSerializer(typeof(CICMMetadataType));
|
||||
|
||||
if(cicmXml != null)
|
||||
if(File.Exists(cicmXml))
|
||||
{
|
||||
try
|
||||
{
|
||||
var sr = new StreamReader(cicmXml);
|
||||
sidecar = (CICMMetadataType)sidecarXs.Deserialize(sr);
|
||||
sr.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
AaruConsole.ErrorWriteLine("Incorrect metadata sidecar file, not continuing...");
|
||||
|
||||
return (int)ErrorNumber.InvalidSidecar;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AaruConsole.ErrorWriteLine("Could not find metadata sidecar, not continuing...");
|
||||
|
||||
return (int)ErrorNumber.FileNotFound;
|
||||
}
|
||||
if(isResponse)
|
||||
eject = true;
|
||||
|
||||
PluginBase plugins = GetPluginBase.Instance;
|
||||
List<IWritableImage> candidates = new List<IWritableImage>();
|
||||
string extension = Path.GetExtension(outputPath);
|
||||
|
||||
// Try extension
|
||||
if(string.IsNullOrEmpty(format))
|
||||
candidates.AddRange(plugins.WritableImages.Values.Where(t =>
|
||||
t.KnownExtensions.
|
||||
Contains(Path.GetExtension(outputPath))));
|
||||
candidates.AddRange(plugins.WritableImages.Values.Where(t => t.KnownExtensions.Contains(extension)));
|
||||
|
||||
// Try Id
|
||||
else if(Guid.TryParse(format, out Guid outId))
|
||||
@@ -456,8 +388,8 @@ namespace Aaru.Commands.Media
|
||||
// Try name
|
||||
else
|
||||
candidates.AddRange(plugins.WritableImages.Values.Where(t => string.Equals(t.Name, format,
|
||||
StringComparison.
|
||||
InvariantCultureIgnoreCase)));
|
||||
StringComparison.
|
||||
InvariantCultureIgnoreCase)));
|
||||
|
||||
if(candidates.Count == 0)
|
||||
{
|
||||
@@ -473,88 +405,247 @@ namespace Aaru.Commands.Media
|
||||
return (int)ErrorNumber.TooManyFormats;
|
||||
}
|
||||
|
||||
IWritableImage outputFormat = candidates[0];
|
||||
|
||||
var dumpLog = new DumpLog(outputPrefix + ".log", dev, @private);
|
||||
|
||||
if(verbose)
|
||||
while(true)
|
||||
{
|
||||
dumpLog.WriteLine("Output image format: {0} ({1}).", outputFormat.Name, outputFormat.Id);
|
||||
AaruConsole.VerboseWriteLine("Output image format: {0} ({1}).", outputFormat.Name, outputFormat.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
dumpLog.WriteLine("Output image format: {0}.", outputFormat.Name);
|
||||
AaruConsole.WriteLine("Output image format: {0}.", outputFormat.Name);
|
||||
}
|
||||
string responseLine = resReader.ReadLine();
|
||||
|
||||
var errorLog = new ErrorLog(outputPrefix + ".error.log");
|
||||
if(responseLine is null)
|
||||
break;
|
||||
|
||||
var dumper = new Dump(resume, dev, devicePath, outputFormat, retryPasses, force, false, persistent,
|
||||
stopOnError, resumeClass, dumpLog, encodingClass, outputPrefix, outputPath,
|
||||
parsedOptions, sidecar, skip, metadata, trim, firstPregap, fixOffset, debug,
|
||||
wantedSubchannel, speed, @private, fixSubchannelPosition, retrySubchannel,
|
||||
fixSubchannel, fixSubchannelCrc, skipCdireadyHole, errorLog, generateSubchannels);
|
||||
|
||||
dumper.UpdateStatus += Progress.UpdateStatus;
|
||||
dumper.ErrorMessage += Progress.ErrorMessage;
|
||||
dumper.StoppingErrorMessage += Progress.ErrorMessage;
|
||||
dumper.UpdateProgress += Progress.UpdateProgress;
|
||||
dumper.PulseProgress += Progress.PulseProgress;
|
||||
dumper.InitProgress += Progress.InitProgress;
|
||||
dumper.EndProgress += Progress.EndProgress;
|
||||
dumper.InitProgress2 += Progress.InitProgress2;
|
||||
dumper.EndProgress2 += Progress.EndProgress2;
|
||||
dumper.UpdateProgress2 += Progress.UpdateProgress2;
|
||||
|
||||
System.Console.CancelKeyPress += (sender, e) =>
|
||||
{
|
||||
e.Cancel = true;
|
||||
dumper.Abort();
|
||||
};
|
||||
|
||||
dumper.Start();
|
||||
|
||||
if(eject && dev.IsRemovable)
|
||||
{
|
||||
switch(dev.Type)
|
||||
if(responseLine.Any(c => c < 0x20))
|
||||
{
|
||||
case DeviceType.ATA:
|
||||
dev.DoorUnlock(out _, dev.Timeout, out _);
|
||||
dev.MediaEject(out _, dev.Timeout, out _);
|
||||
AaruConsole.ErrorWriteLine("Invalid characters found in list of files, exiting...");
|
||||
|
||||
break;
|
||||
case DeviceType.ATAPI:
|
||||
case DeviceType.SCSI:
|
||||
switch(dev.ScsiType)
|
||||
{
|
||||
case PeripheralDeviceTypes.DirectAccess:
|
||||
case PeripheralDeviceTypes.SimplifiedDevice:
|
||||
case PeripheralDeviceTypes.SCSIZonedBlockDevice:
|
||||
case PeripheralDeviceTypes.WriteOnceDevice:
|
||||
case PeripheralDeviceTypes.OpticalDevice:
|
||||
case PeripheralDeviceTypes.OCRWDevice:
|
||||
dev.SpcAllowMediumRemoval(out _, dev.Timeout, out _);
|
||||
dev.EjectTray(out _, dev.Timeout, out _);
|
||||
|
||||
break;
|
||||
case PeripheralDeviceTypes.MultiMediaDevice:
|
||||
dev.AllowMediumRemoval(out _, dev.Timeout, out _);
|
||||
dev.EjectTray(out _, dev.Timeout, out _);
|
||||
|
||||
break;
|
||||
case PeripheralDeviceTypes.SequentialAccess:
|
||||
dev.SpcAllowMediumRemoval(out _, dev.Timeout, out _);
|
||||
dev.LoadUnload(out _, true, false, false, false, false, dev.Timeout, out _);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
return (int)ErrorNumber.InvalidArgument;
|
||||
}
|
||||
}
|
||||
|
||||
dev.Close();
|
||||
if(isResponse)
|
||||
{
|
||||
AaruConsole.WriteLine("Please insert media with title {0} and press any key to continue...",
|
||||
responseLine);
|
||||
|
||||
System.Console.ReadKey();
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
responseLine = responseLine.Replace('/', '/');
|
||||
|
||||
// Replace Windows forbidden filename characters with Japanese equivalents that are visually the same, but bigger.
|
||||
if(DetectOS.IsWindows)
|
||||
responseLine = responseLine.Replace('<', '\uFF1C').Replace('>', '\uFF1E').Replace(':', '\uFF1A').
|
||||
Replace('"', '\u2033').Replace('\\', '\').Replace('|', '|').
|
||||
Replace('?', '?').Replace('*', '*');
|
||||
|
||||
if(devicePath.Length == 2 &&
|
||||
devicePath[1] == ':' &&
|
||||
devicePath[0] != '/' &&
|
||||
char.IsLetter(devicePath[0]))
|
||||
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
|
||||
|
||||
Devices.Device dev;
|
||||
|
||||
try
|
||||
{
|
||||
dev = new Devices.Device(devicePath);
|
||||
|
||||
if(dev.IsRemote)
|
||||
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem,
|
||||
dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture);
|
||||
|
||||
if(dev.Error)
|
||||
{
|
||||
AaruConsole.ErrorWriteLine(Error.Print(dev.LastError));
|
||||
|
||||
if(isResponse)
|
||||
continue;
|
||||
|
||||
return (int)ErrorNumber.CannotOpenDevice;
|
||||
}
|
||||
}
|
||||
catch(DeviceException e)
|
||||
{
|
||||
AaruConsole.ErrorWriteLine(e.Message ?? Error.Print(e.LastError));
|
||||
|
||||
if(isResponse)
|
||||
continue;
|
||||
|
||||
return (int)ErrorNumber.CannotOpenDevice;
|
||||
}
|
||||
|
||||
Statistics.AddDevice(dev);
|
||||
|
||||
string outputPrefix = Path.Combine(Path.GetDirectoryName(outputPath), responseLine);
|
||||
|
||||
Resume resumeClass = null;
|
||||
var xs = new XmlSerializer(typeof(Resume));
|
||||
|
||||
if(File.Exists(outputPrefix + ".resume.xml") && resume)
|
||||
try
|
||||
{
|
||||
var sr = new StreamReader(outputPrefix + ".resume.xml");
|
||||
resumeClass = (Resume)xs.Deserialize(sr);
|
||||
sr.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
AaruConsole.ErrorWriteLine("Incorrect resume file, not continuing...");
|
||||
|
||||
if(isResponse)
|
||||
continue;
|
||||
|
||||
return (int)ErrorNumber.InvalidResume;
|
||||
}
|
||||
|
||||
if(resumeClass != null &&
|
||||
resumeClass.NextBlock > resumeClass.LastBlock &&
|
||||
resumeClass.BadBlocks.Count == 0 &&
|
||||
!resumeClass.Tape &&
|
||||
(resumeClass.BadSubchannels is null || resumeClass.BadSubchannels.Count == 0))
|
||||
{
|
||||
AaruConsole.WriteLine("Media already dumped correctly, not continuing...");
|
||||
|
||||
if(isResponse)
|
||||
continue;
|
||||
|
||||
return (int)ErrorNumber.AlreadyDumped;
|
||||
}
|
||||
|
||||
CICMMetadataType sidecar = null;
|
||||
var sidecarXs = new XmlSerializer(typeof(CICMMetadataType));
|
||||
|
||||
if(cicmXml != null)
|
||||
if(File.Exists(cicmXml))
|
||||
{
|
||||
try
|
||||
{
|
||||
var sr = new StreamReader(cicmXml);
|
||||
sidecar = (CICMMetadataType)sidecarXs.Deserialize(sr);
|
||||
sr.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
AaruConsole.ErrorWriteLine("Incorrect metadata sidecar file, not continuing...");
|
||||
|
||||
if(isResponse)
|
||||
continue;
|
||||
|
||||
return (int)ErrorNumber.InvalidSidecar;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AaruConsole.ErrorWriteLine("Could not find metadata sidecar, not continuing...");
|
||||
|
||||
if(isResponse)
|
||||
continue;
|
||||
|
||||
return (int)ErrorNumber.FileNotFound;
|
||||
}
|
||||
|
||||
plugins = GetPluginBase.Instance;
|
||||
candidates = new List<IWritableImage>();
|
||||
|
||||
// Try extension
|
||||
if(string.IsNullOrEmpty(format))
|
||||
candidates.AddRange(plugins.WritableImages.Values.Where(t =>
|
||||
t.KnownExtensions.
|
||||
Contains(Path.
|
||||
GetExtension(outputPath))));
|
||||
|
||||
// Try Id
|
||||
else if(Guid.TryParse(format, 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, format,
|
||||
StringComparison.
|
||||
InvariantCultureIgnoreCase)));
|
||||
|
||||
IWritableImage outputFormat = candidates[0];
|
||||
|
||||
var dumpLog = new DumpLog(outputPrefix + ".log", dev, @private);
|
||||
|
||||
if(verbose)
|
||||
{
|
||||
dumpLog.WriteLine("Output image format: {0} ({1}).", outputFormat.Name, outputFormat.Id);
|
||||
AaruConsole.VerboseWriteLine("Output image format: {0} ({1}).", outputFormat.Name, outputFormat.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
dumpLog.WriteLine("Output image format: {0}.", outputFormat.Name);
|
||||
AaruConsole.WriteLine("Output image format: {0}.", outputFormat.Name);
|
||||
}
|
||||
|
||||
var errorLog = new ErrorLog(outputPrefix + ".error.log");
|
||||
|
||||
var dumper = new Dump(resume, dev, devicePath, outputFormat, retryPasses, force, false, persistent,
|
||||
stopOnError, resumeClass, dumpLog, encodingClass, outputPrefix,
|
||||
outputPrefix + extension, parsedOptions, sidecar, skip, metadata, trim,
|
||||
firstPregap, fixOffset, debug, wantedSubchannel, speed, @private,
|
||||
fixSubchannelPosition, retrySubchannel, fixSubchannel, fixSubchannelCrc,
|
||||
skipCdireadyHole, errorLog, generateSubchannels);
|
||||
|
||||
dumper.UpdateStatus += Progress.UpdateStatus;
|
||||
dumper.ErrorMessage += Progress.ErrorMessage;
|
||||
dumper.StoppingErrorMessage += Progress.ErrorMessage;
|
||||
dumper.UpdateProgress += Progress.UpdateProgress;
|
||||
dumper.PulseProgress += Progress.PulseProgress;
|
||||
dumper.InitProgress += Progress.InitProgress;
|
||||
dumper.EndProgress += Progress.EndProgress;
|
||||
dumper.InitProgress2 += Progress.InitProgress2;
|
||||
dumper.EndProgress2 += Progress.EndProgress2;
|
||||
dumper.UpdateProgress2 += Progress.UpdateProgress2;
|
||||
|
||||
System.Console.CancelKeyPress += (sender, e) =>
|
||||
{
|
||||
e.Cancel = true;
|
||||
dumper.Abort();
|
||||
};
|
||||
|
||||
dumper.Start();
|
||||
|
||||
if(eject && dev.IsRemovable)
|
||||
{
|
||||
switch(dev.Type)
|
||||
{
|
||||
case DeviceType.ATA:
|
||||
dev.DoorUnlock(out _, dev.Timeout, out _);
|
||||
dev.MediaEject(out _, dev.Timeout, out _);
|
||||
|
||||
break;
|
||||
case DeviceType.ATAPI:
|
||||
case DeviceType.SCSI:
|
||||
switch(dev.ScsiType)
|
||||
{
|
||||
case PeripheralDeviceTypes.DirectAccess:
|
||||
case PeripheralDeviceTypes.SimplifiedDevice:
|
||||
case PeripheralDeviceTypes.SCSIZonedBlockDevice:
|
||||
case PeripheralDeviceTypes.WriteOnceDevice:
|
||||
case PeripheralDeviceTypes.OpticalDevice:
|
||||
case PeripheralDeviceTypes.OCRWDevice:
|
||||
dev.SpcAllowMediumRemoval(out _, dev.Timeout, out _);
|
||||
dev.EjectTray(out _, dev.Timeout, out _);
|
||||
|
||||
break;
|
||||
case PeripheralDeviceTypes.MultiMediaDevice:
|
||||
dev.AllowMediumRemoval(out _, dev.Timeout, out _);
|
||||
dev.EjectTray(out _, dev.Timeout, out _);
|
||||
|
||||
break;
|
||||
case PeripheralDeviceTypes.SequentialAccess:
|
||||
dev.SpcAllowMediumRemoval(out _, dev.Timeout, out _);
|
||||
dev.LoadUnload(out _, true, false, false, false, false, dev.Timeout, out _);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dev.Close();
|
||||
}
|
||||
|
||||
return (int)ErrorNumber.NoError;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user