Add error recovery support and related logging

This commit is contained in:
2026-04-12 16:23:34 +01:00
parent 23631ae564
commit 7b74de7db4
22 changed files with 219 additions and 67 deletions

View File

@@ -45,6 +45,7 @@ using Aaru.Core.Graphics;
using Aaru.Core.Logging;
using Aaru.Decoders.ATA;
using Aaru.Decoders.PCMCIA;
using Aaru.Images;
using Aaru.Logging;
using Humanizer;
using Identify = Aaru.CommonTypes.Structs.Devices.ATA.Identify;
@@ -250,6 +251,8 @@ public partial class Dump
return;
}
if(outputFormat is AaruFormat aif && _errorRecovery > 0) aif.SetErasureCodingAuto((byte)_errorRecovery);
// Setting geometry
outputFormat.SetGeometry(cylinders, heads, sectors);

View File

@@ -49,6 +49,7 @@ using Aaru.Core.Media.Detection;
using Aaru.Database.Models;
using Aaru.Decoders.CD;
using Aaru.Devices;
using Aaru.Images;
using Aaru.Logging;
using Humanizer;
using Spectre.Console;
@@ -971,6 +972,8 @@ sealed partial class Dump
return;
}
if(outputOptical is AaruFormat aif && _errorRecovery > 0) aif.SetErasureCodingAuto((byte)_errorRecovery);
ErrorNumber errno = outputOptical.ReadMediaTag(MediaTagType.CD_MCN, out byte[] mcnBytes);
if(errno == ErrorNumber.NoError) mcn = Encoding.ASCII.GetString(mcnBytes);
@@ -1083,9 +1086,8 @@ sealed partial class Dump
foreach(int sub in _resume.BadSubchannels) subchannelExtents.Add(sub);
if(_resume.NextBlock < blocks)
{
for(ulong i = _resume.NextBlock; i < blocks; i++) subchannelExtents.Add((int)i);
}
for(ulong i = _resume.NextBlock; i < blocks; i++)
subchannelExtents.Add((int)i);
}
if(_resume.NextBlock > 0)
@@ -1511,9 +1513,8 @@ sealed partial class Dump
supportsLongSectors);
foreach(Tuple<ulong, ulong> leadoutExtent in leadOutExtents.ToArray())
{
for(ulong e = leadoutExtent.Item1; e <= leadoutExtent.Item2; e++) subchannelExtents.Remove((int)e);
}
for(ulong e = leadoutExtent.Item1; e <= leadoutExtent.Item2; e++)
subchannelExtents.Remove((int)e);
if(subchannelExtents.Count > 0 && _retryPasses > 0 && _retrySubchannel)
{

View File

@@ -83,6 +83,7 @@ public partial class Dump
readonly Stopwatch _dumpStopwatch;
readonly Encoding _encoding;
readonly ErrorLog _errorLog;
readonly int _errorRecovery;
readonly bool _fixSubchannel;
readonly bool _fixSubchannelCrc;
readonly bool _fixSubchannelPosition;
@@ -103,6 +104,7 @@ public partial class Dump
readonly bool _retrySubchannel;
readonly Stopwatch _sidecarStopwatch;
readonly Stopwatch _speedStopwatch;
readonly bool _startReverse;
readonly bool _stopOnError;
readonly bool _storeEncrypted;
readonly DumpSubchannel _subchannel;
@@ -125,7 +127,6 @@ public partial class Dump
bool _skipCdireadyHole;
int _speed;
int _speedMultiplier;
readonly bool _startReverse;
bool _supportsPlextorD8;
bool _useBufferedReads;
@@ -183,7 +184,7 @@ public partial class Dump
bool fixSubchannel, bool fixSubchannelCrc, bool skipCdireadyHole, ErrorLog errorLog,
bool generateSubchannels, uint maximumReadable, bool useBufferedReads, bool storeEncrypted,
bool titleKeys, uint ignoreCdrRunOuts, bool createGraph, uint dimensions, bool paranoia,
bool cureParanoia, bool bypassWiiDecryption, bool startReverse)
bool cureParanoia, bool bypassWiiDecryption, bool startReverse, int errorRecovery)
{
_doResume = doResume;
_dev = dev;
@@ -229,6 +230,7 @@ public partial class Dump
_cureParanoia = cureParanoia;
_bypassWiiDecryption = bypassWiiDecryption;
_startReverse = startReverse;
_errorRecovery = errorRecovery;
_dumpStopwatch = new Stopwatch();
_sidecarStopwatch = new Stopwatch();
_speedStopwatch = new Stopwatch();

View File

@@ -46,6 +46,7 @@ using Aaru.Core.Graphics;
using Aaru.Core.Logging;
using Aaru.Decoders.SCSI;
using Aaru.Devices;
using Aaru.Images;
using Aaru.Logging;
using Humanizer;
using Version = Aaru.CommonTypes.Interop.Version;
@@ -223,6 +224,8 @@ partial class Dump
return;
}
if(outputFormat is AaruFormat aif && _errorRecovery > 0) aif.SetErasureCodingAuto((byte)_errorRecovery);
_dumpStopwatch.Restart();
double imageWriteDuration = 0;

View File

@@ -46,6 +46,7 @@ using Aaru.Core.Graphics;
using Aaru.Core.Logging;
using Aaru.Decoders.SCSI;
using Aaru.Devices;
using Aaru.Images;
using Aaru.Logging;
using Humanizer;
using Version = Aaru.CommonTypes.Interop.Version;
@@ -142,6 +143,8 @@ public partial class Dump
return;
}
if(outputFormat is AaruFormat aif && _errorRecovery > 0) aif.SetErasureCodingAuto((byte)_errorRecovery);
_dumpStopwatch.Restart();
double imageWriteDuration = 0;

View File

@@ -43,6 +43,7 @@ using Aaru.Core.Graphics;
using Aaru.Core.Logging;
using Aaru.Decoders.SCSI;
using Aaru.Devices;
using Aaru.Images;
using Aaru.Logging;
using Humanizer;
using Track = Aaru.CommonTypes.Structs.Track;
@@ -169,6 +170,8 @@ public partial class Dump
return;
}
if(outputOptical is AaruFormat aif && _errorRecovery > 0) aif.SetErasureCodingAuto((byte)_errorRecovery);
_dumpStopwatch.Restart();
double imageWriteDuration = 0;

View File

@@ -48,6 +48,7 @@ using Aaru.Decoders.SCSI;
using Aaru.Decoders.SCSI.SSC;
using Aaru.Devices;
using Aaru.Helpers;
using Aaru.Images;
using Aaru.Logging;
using Humanizer;
using TapeFile = Aaru.CommonTypes.Structs.TapeFile;
@@ -283,8 +284,9 @@ partial class Dump
Modes.DecodedMode? decMode = null;
if(!sense && !_dev.Error)
if(Modes.DecodeMode10(cmdBuf, _dev.ScsiType).HasValue)
decMode = Modes.DecodeMode10(cmdBuf, _dev.ScsiType);
{
if(Modes.DecodeMode10(cmdBuf, _dev.ScsiType).HasValue) decMode = Modes.DecodeMode10(cmdBuf, _dev.ScsiType);
}
UpdateStatus?.Invoke(Localization.Core.Requesting_MODE_SENSE_6);
@@ -312,8 +314,9 @@ partial class Dump
if(sense || _dev.Error) sense = _dev.ModeSense(out cmdBuf, out senseBuf, 5, out duration);
if(!sense && !_dev.Error)
if(Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue)
decMode = Modes.DecodeMode6(cmdBuf, _dev.ScsiType);
{
if(Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue) decMode = Modes.DecodeMode6(cmdBuf, _dev.ScsiType);
}
// TODO: Check partitions page
if(decMode.HasValue)
@@ -834,6 +837,8 @@ partial class Dump
return;
}
if(outputTape is AaruFormat aif && _errorRecovery > 0) aif.SetErasureCodingAuto((byte)_errorRecovery);
_dumpStopwatch.Restart();
var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, 1, _private);
var ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0008);

View File

@@ -35,7 +35,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
using System.Text.Json;
using Aaru.CommonTypes;
using Aaru.CommonTypes.AaruMetadata;
@@ -76,16 +75,16 @@ partial class Dump
DVDDecryption dvdDecrypt = null)
{
bool sense;
byte scsiMediumType = 0;
byte scsiDensityCode = 0;
byte scsiMediumType = 0;
byte scsiDensityCode = 0;
var containsFloppyPage = false;
const ushort sbcProfile = 0x0001;
const uint DvdLeadinSectors = 4096; // Usable Lead-in before LBA 0 per DVD spec (PSN 0x02F0000x02FFFF)
double totalDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
Modes.DecodedMode? decMode = null;
const ushort sbcProfile = 0x0001;
const uint DvdLeadinSectors = 4096; // Usable Lead-in before LBA 0 per DVD spec (PSN 0x02F0000x02FFFF)
double totalDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
Modes.DecodedMode? decMode = null;
bool ret;
ExtentsULong blankExtents = null;
var outputFormat = _outputPlugin as IWritableImage;
@@ -325,9 +324,9 @@ partial class Dump
scsiReader.layerbreak = decodedPfi?.Layer0EndPSN ?? 0;
if(scsiReader.layerbreak == 0) scsiReader.layerbreak = decodedPfi?.DataAreaEndPSN ?? 0;
scsiReader.otp = decodedPfi?.TrackPath ?? false;
scsiReader.otp = decodedPfi?.TrackPath ?? false;
if(scsiReader.HldtstReadRaw) blocksToRead = 1;
if(scsiReader.HldtstReadRaw) blocksToRead = 1;
if(scsiReader.OmniDriveReadRaw) blocksToRead = 31;
UpdateStatus?.Invoke(string.Format(Localization.Core.Reading_0_raw_bytes_1_cooked_bytes_per_sector,
@@ -345,7 +344,8 @@ partial class Dump
{
if(!scsiReader.OmniDriveReadRaw)
{
StoppingErrorMessage?.Invoke(Localization.Core.Dumping_Nintendo_GameCube_or_Wii_discs_is_not_yet_implemented);
StoppingErrorMessage?.Invoke(Localization.Core
.Dumping_Nintendo_GameCube_or_Wii_discs_is_not_yet_implemented);
return;
}
@@ -410,6 +410,8 @@ partial class Dump
return;
}
if(outputFormat is AaruFormat aif && _errorRecovery > 0) aif.SetErasureCodingAuto((byte)_errorRecovery);
imageCreated = true;
}
@@ -428,13 +430,13 @@ partial class Dump
if(decodedPfi.HasValue) nominalNegativeSectors = decodedPfi.Value.DataAreaStartPSN;
if(scsiReader.OmniDriveReadRaw &&
decodedPfi.HasValue &&
((dskType >= MediaType.DVDROM && dskType <= MediaType.DVDDownload)
|| dskType == MediaType.PS2DVD
|| dskType == MediaType.PS3DVD
|| dskType == MediaType.Nuon
|| dskType == MediaType.GOD
|| dskType == MediaType.WOD))
decodedPfi.HasValue &&
(dskType >= MediaType.DVDROM && dskType <= MediaType.DVDDownload ||
dskType == MediaType.PS2DVD ||
dskType == MediaType.PS3DVD ||
dskType == MediaType.Nuon ||
dskType == MediaType.GOD ||
dskType == MediaType.WOD))
nominalNegativeSectors = Math.Min(nominalNegativeSectors, DvdLeadinSectors);
mediaTags.TryGetValue(MediaTagType.BD_DI, out byte[] di);
@@ -587,6 +589,9 @@ partial class Dump
return;
}
if(outputFormat is AaruFormat aif && _errorRecovery > 0)
aif.SetErasureCodingAuto((byte)_errorRecovery);
imageCreated = true;
#if DEBUG
@@ -722,6 +727,8 @@ partial class Dump
return;
}
if(outputFormat is AaruFormat aif && _errorRecovery > 0) aif.SetErasureCodingAuto((byte)_errorRecovery);
if(writeSingleOpticalTrack)
{
UpdateStatus?.Invoke(Localization.Core
@@ -811,8 +818,7 @@ partial class Dump
var newTrim = false;
if(ngcwMode && !InitializeNgcwContext(dskType, scsiReader, outputFormat))
return;
if(ngcwMode && !InitializeNgcwContext(dskType, scsiReader, outputFormat)) return;
if(mediaTags.TryGetValue(MediaTagType.DVD_CMI, out byte[] cmi) &&
Settings.Settings.Current.EnableDecryption &&
@@ -849,33 +855,35 @@ partial class Dump
if(scsiReader.HldtstReadRaw || scsiReader.ReadBuffer3CReadRaw || scsiReader.OmniDriveReadRaw)
{
uint nominalForRawDvd = 0;
uint nominalForRawDvd = 0;
uint overflowForRawDvd = 0;
if(scsiReader.OmniDriveReadRaw && outputFormat is IWritableOpticalImage optImg)
{
if(optImg.OpticalCapabilities.HasFlag(OpticalImageCapabilities.CanStoreNegativeSectors))
nominalForRawDvd = nominalNegativeSectors;
if(optImg.OpticalCapabilities.HasFlag(OpticalImageCapabilities.CanStoreOverflowSectors))
overflowForRawDvd = 100u;
}
ReadRawDvdData(blocks,
blocksToRead,
blockSize,
currentTry,
extents,
ref currentSpeed,
ref minSpeed,
ref maxSpeed,
ref totalDuration,
scsiReader,
mhddLog,
ibgLog,
ref imageWriteDuration,
ref newTrim,
discKey ?? null,
nominalForRawDvd,
overflowForRawDvd);
blocksToRead,
blockSize,
currentTry,
extents,
ref currentSpeed,
ref minSpeed,
ref maxSpeed,
ref totalDuration,
scsiReader,
mhddLog,
ibgLog,
ref imageWriteDuration,
ref newTrim,
discKey ?? null,
nominalForRawDvd,
overflowForRawDvd);
}
else
{
@@ -962,8 +970,8 @@ partial class Dump
// Unnecessary since keys are already in raw data
!scsiReader.ReadBuffer3CReadRaw &&
!scsiReader.OmniDriveReadRaw &&
!scsiReader.HldtstReadRaw &&
!scsiReader.OmniDriveReadRaw &&
!scsiReader.HldtstReadRaw &&
mediaTag is not null)
RetryTitleKeys(dvdDecrypt, mediaTag, ref totalDuration);

View File

@@ -46,6 +46,7 @@ using Aaru.Core.Graphics;
using Aaru.Core.Logging;
using Aaru.Decoders.MMC;
using Aaru.Decoders.SecureDigital;
using Aaru.Images;
using Aaru.Logging;
using Humanizer;
using CSD = Aaru.Decoders.MMC.CSD;
@@ -457,6 +458,8 @@ public partial class Dump
return;
}
if(outputFormat is AaruFormat aif && _errorRecovery > 0) aif.SetErasureCodingAuto((byte)_errorRecovery);
if(cid != null)
{
switch(_dev.Type)

View File

@@ -48,6 +48,7 @@ using Aaru.Decoders.DVD;
using Aaru.Decoders.SCSI;
using Aaru.Decoders.Xbox;
using Aaru.Devices;
using Aaru.Images;
using Aaru.Logging;
using Humanizer;
using Device = Aaru.Devices.Remote.Device;
@@ -515,6 +516,8 @@ partial class Dump
return;
}
if(outputFormat is AaruFormat aif && _errorRecovery > 0) aif.SetErasureCodingAuto((byte)_errorRecovery);
_dumpStopwatch.Restart();
double imageWriteDuration = 0;
@@ -1088,9 +1091,8 @@ partial class Dump
List<ulong> tmpList = [];
foreach(ulong ur in _resume.BadBlocks)
{
for(ulong i = ur; i < ur + blocksToRead; i++) tmpList.Add(i);
}
for(ulong i = ur; i < ur + blocksToRead; i++)
tmpList.Add(i);
tmpList.Sort();

View File

@@ -27,6 +27,7 @@ public partial class Convert
readonly string _driveManufacturer;
readonly string _driveModel;
readonly string _driveSerialNumber;
readonly int _errorRecovery;
readonly bool _fixSubchannel;
readonly bool _fixSubchannelCrc;
readonly bool _fixSubchannelPosition;
@@ -64,7 +65,7 @@ public partial class Convert
PluginRegister plugins, bool fixSubchannelPosition, bool fixSubchannel, bool fixSubchannelCrc,
bool generateSubchannels, (uint cylinders, uint heads, uint sectors)? geometryValues, Resume resume,
Metadata sidecar, bool bypassPs3Decryption, bool bypassWiiuDecryption, bool bypassWiiDecryption,
string inputPath = null)
string inputPath, int errorRecovery)
{
_inputImage = inputImage;
_outputImage = outputImage;
@@ -102,6 +103,7 @@ public partial class Convert
_bypassWiiuDecryption = bypassWiiuDecryption;
_bypassWiiDecryption = bypassWiiDecryption;
_inputPath = inputPath;
_errorRecovery = errorRecovery;
}
public ErrorNumber Start()

View File

@@ -1,4 +1,5 @@
using Aaru.CommonTypes.Enums;
using Aaru.Images;
using Aaru.Localization;
namespace Aaru.Core.Image;
@@ -26,6 +27,8 @@ public partial class Convert
_overflowSectors,
_inputImage.Info.SectorSize);
if(_outputImage is AaruFormat aif && _errorRecovery > 0) aif.SetErasureCodingAuto((byte)_errorRecovery);
EndProgress?.Invoke();
if(created) return ErrorNumber.NoError;

View File

@@ -1,6 +1,7 @@
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.Images;
using Aaru.Localization;
namespace Aaru.Core.Image;
@@ -23,6 +24,8 @@ public sealed partial class Merger
overflowSectors,
primaryImage.Info.SectorSize);
if(outputImage is AaruFormat aif && errorRecovery > 0) aif.SetErasureCodingAuto((byte)errorRecovery);
EndProgress?.Invoke();
if(created) return ErrorNumber.NoError;

View File

@@ -6,6 +6,7 @@ using Aaru.CommonTypes.AaruMetadata;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Metadata;
using Aaru.Images;
using Aaru.Localization;
using File = System.IO.File;
using MediaType = Aaru.CommonTypes.MediaType;
@@ -49,7 +50,8 @@ public sealed partial class Merger
bool generateSubchannels,
bool decrypt,
bool ignoreNegativeSectors,
bool ignoreOverflowSectors
bool ignoreOverflowSectors,
int errorRecovery
)
{
const string MODULE_NAME = "Image merger";
@@ -144,6 +146,23 @@ public sealed partial class Merger
UpdateStatus?.Invoke(string.Format(UI.Output_image_format_0, outputFormat.Name));
if(errorRecovery > 0)
{
if(outputFormat is not AaruFormat)
{
StoppingErrorMessage?.Invoke(UI.Error_recovery_is_only_supported_in_AaruFormat);
return ErrorNumber.NotSupported;
}
if(errorRecovery > 100)
{
StoppingErrorMessage?.Invoke(UI.Maximum_error_recovery_is_100);
return ErrorNumber.InvalidArgument;
}
}
if(primaryImage.Info.Sectors != secondaryImage.Info.Sectors)
{
StoppingErrorMessage?.Invoke(UI.Images_have_different_number_of_sectors_cannot_merge);
@@ -220,13 +239,13 @@ public sealed partial class Merger
InitProgress?.Invoke();
PulseProgress?.Invoke(UI.Calculating_sectors_to_merge);
List<ulong> sectorsToCopyFromSecondImage =
CalculateSectorsToCopy(primaryImage, secondaryImage, primaryResume, secondaryResume, overrideSectorsList);
List<ulong> sectorsToCopyFromSecondImage =
CalculateSectorsToCopy(primaryImage, secondaryImage, primaryResume, secondaryResume, overrideSectorsList);
EndProgress?.Invoke();
// Flux images might contain no decoded data, which results in a sector count of 0. We allow this if the image contains flux.
var containsFlux = primaryImage is IFluxImage || secondaryImage is IFluxImage;
bool containsFlux = primaryImage is IFluxImage || secondaryImage is IFluxImage;
if(sectorsToCopyFromSecondImage.Count == 0 && !containsFlux)
{
@@ -349,7 +368,7 @@ public sealed partial class Merger
if(primaryImage is IFluxImage primaryFlux && outputFormat is IWritableFluxImage outputFlux)
{
IFluxImage secondaryFlux = secondaryImage as IFluxImage;
var secondaryFlux = secondaryImage as IFluxImage;
UpdateStatus?.Invoke(secondaryFlux != null
? UI.Flux_merge_primary_then_secondary_appended

View File

@@ -352,7 +352,8 @@ public sealed partial class ImageConvertViewModel : ViewModelBase
false,
false,
false,
SourceText);
SourceText,
0);
// Prepare UI
await Dispatcher.UIThread.InvokeAsync(() =>

View File

@@ -762,7 +762,8 @@ public sealed partial class MediaDumpViewModel : ViewModelBase
Paranoia,
CureParanoia,
BypassWiiDecryption,
false);
false,
0);
new Thread(DoWork).Start();
}

View File

@@ -11231,5 +11231,23 @@ namespace Aaru.Localization {
return ResourceManager.GetString("Start_reverse_error_retry", resourceCulture);
}
}
public static string Add_error_recovery {
get {
return ResourceManager.GetString("Add_error_recovery", resourceCulture);
}
}
public static string Error_recovery_is_only_supported_in_AaruFormat {
get {
return ResourceManager.GetString("Error_recovery_is_only_supported_in_AaruFormat", resourceCulture);
}
}
public static string Maximum_error_recovery_is_100 {
get {
return ResourceManager.GetString("Maximum_error_recovery_is_100", resourceCulture);
}
}
}
}

View File

@@ -5611,4 +5611,13 @@ Probadores:
<data name="Start_reverse_error_retry" xml:space="preserve">
<value>Comenzar reintentos de errores al revés.</value>
</data>
<data name="Add_error_recovery" xml:space="preserve">
<value>Añade el porcentaje especificado de recuperación de errores (sólo AaruFormat).</value>
</data>
<data name="Error_recovery_is_only_supported_in_AaruFormat" xml:space="preserve">
<value>La recuperación de errores solo está soportada en AaruFormat</value>
</data>
<data name="Maximum_error_recovery_is_100" xml:space="preserve">
<value>La recuperación de errores máxima es 100%</value>
</data>
</root>

View File

@@ -5695,4 +5695,13 @@ Do you want to continue?</value>
<data name="Start_reverse_error_retry" xml:space="preserve">
<value>Start error retrying in reverse.</value>
</data>
<data name="Add_error_recovery" xml:space="preserve">
<value>Add the specified percentage of error recovery (only AaruFormat).</value>
</data>
<data name="Error_recovery_is_only_supported_in_AaruFormat" xml:space="preserve">
<value>Error recovery is only supported in AaruFormat</value>
</data>
<data name="Maximum_error_recovery_is_100" xml:space="preserve">
<value>Maximum error recovery is 100%</value>
</data>
</root>

View File

@@ -44,6 +44,7 @@ using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Metadata;
using Aaru.Core;
using Aaru.Images;
using Aaru.Localization;
using Aaru.Logging;
using Schemas;
@@ -234,6 +235,23 @@ sealed class ConvertImageCommand : Command<ConvertImageCommand.Settings>
if(outputFormat == null) return (int)ErrorNumber.FormatNotFound;
if(settings.ErrorRecovery > 0)
{
if(outputFormat is not AaruFormat)
{
AaruLogging.Error(UI.Error_recovery_is_only_supported_in_AaruFormat);
return (int)ErrorNumber.NotSupported;
}
if(settings.ErrorRecovery > 100)
{
AaruLogging.Error(UI.Maximum_error_recovery_is_100);
return (int)ErrorNumber.InvalidArgument;
}
}
if(settings.Verbose)
AaruLogging.Verbose(UI.Output_image_format_0_1, outputFormat.Name, outputFormat.Id);
else
@@ -274,7 +292,8 @@ sealed class ConvertImageCommand : Command<ConvertImageCommand.Settings>
settings.BypassPs3Decryption,
settings.BypassWiiuDecryption,
settings.BypassWiiDecryption,
settings.InputPath);
settings.InputPath,
settings.ErrorRecovery);
ErrorNumber errno = ErrorNumber.NoError;
@@ -560,6 +579,7 @@ sealed class ConvertImageCommand : Command<ConvertImageCommand.Settings>
AaruLogging.Debug(MODULE_NAME, "--aaru-metadata={0}", Markup.Escape(settings.AaruMetadata ?? ""));
AaruLogging.Debug(MODULE_NAME, "--ignore-negative-sectors={0}", settings.IgnoreNegativeSectors);
AaruLogging.Debug(MODULE_NAME, "--ignore-overflow-sectors={0}", settings.IgnoreOverflowSectors);
AaruLogging.Debug(MODULE_NAME, "--error-recovery={0}", settings.ErrorRecovery);
AaruLogging.Debug(MODULE_NAME, UI.Parsed_options);
@@ -758,5 +778,9 @@ sealed class ConvertImageCommand : Command<ConvertImageCommand.Settings>
[DefaultValue(false)]
[CommandOption("--ignore-overflow-sectors")]
public bool IgnoreOverflowSectors { get; init; }
[LocalizedDescription(nameof(UI.Add_error_recovery))]
[DefaultValue(0)]
[CommandOption("--error-recovery")]
public int ErrorRecovery { get; set; }
}
}

View File

@@ -72,7 +72,8 @@ class MergeCommand : AsyncCommand<MergeCommand.Settings>
settings.GenerateSubchannels,
settings.Decrypt,
settings.IgnoreNegativeSectors,
settings.IgnoreOverflowSectors);
settings.IgnoreOverflowSectors,
settings.ErrorRecovery);
ErrorNumber errno = ErrorNumber.NoError;
@@ -188,6 +189,7 @@ class MergeCommand : AsyncCommand<MergeCommand.Settings>
AaruLogging.Debug(MODULE_NAME, "--sectors-file={0}", Markup.Escape(settings.SectorsFile ?? ""));
AaruLogging.Debug(MODULE_NAME, "--ignore-negative-sectors={0}", settings.IgnoreNegativeSectors);
AaruLogging.Debug(MODULE_NAME, "--ignore-overflow-sectors={0}", settings.IgnoreOverflowSectors);
AaruLogging.Debug(MODULE_NAME, "--error-recovery={0}", settings.ErrorRecovery);
AaruLogging.Debug(MODULE_NAME, UI.Parsed_options);
@@ -326,5 +328,9 @@ class MergeCommand : AsyncCommand<MergeCommand.Settings>
[DefaultValue(false)]
[CommandOption("--ignore-overflow-sectors")]
public bool IgnoreOverflowSectors { get; init; }
[LocalizedDescription(nameof(UI.Add_error_recovery))]
[DefaultValue(0)]
[CommandOption("--error-recovery")]
public int ErrorRecovery { get; set; }
}
}

View File

@@ -51,6 +51,7 @@ using Aaru.CommonTypes.Structs.Devices.SCSI;
using Aaru.Core;
using Aaru.Core.Devices.Dumping;
using Aaru.Core.Logging;
using Aaru.Images;
using Aaru.Localization;
using Aaru.Logging;
using Schemas;
@@ -126,6 +127,7 @@ sealed class DumpMediaCommand : Command<DumpMediaCommand.Settings>
AaruLogging.Debug(MODULE_NAME, "--cure-paranoia={0}", settings.CureParanoia);
AaruLogging.Debug(MODULE_NAME, "--raw={0}", settings.Raw);
AaruLogging.Debug(MODULE_NAME, "--start-reverse={0}", settings.StartReverse);
AaruLogging.Debug(MODULE_NAME, "--error-recovery={0}", settings.ErrorRecovery);
Dictionary<string, string> parsedOptions = Options.Parse(settings.Options);
AaruLogging.Debug(MODULE_NAME, UI.Parsed_options);
@@ -480,6 +482,23 @@ sealed class DumpMediaCommand : Command<DumpMediaCommand.Settings>
IBaseWritableImage outputFormat = candidates[0];
if(settings.ErrorRecovery > 0)
{
if(outputFormat is not AaruFormat)
{
AaruLogging.Error(UI.Error_recovery_is_only_supported_in_AaruFormat);
return (int)ErrorNumber.NotSupported;
}
if(settings.ErrorRecovery > 100)
{
AaruLogging.Error(UI.Maximum_error_recovery_is_100);
return (int)ErrorNumber.InvalidArgument;
}
}
DeviceLog.StartLog(dev, settings.Private);
if(settings.Verbose)
@@ -530,7 +549,8 @@ sealed class DumpMediaCommand : Command<DumpMediaCommand.Settings>
settings.Paranoia,
settings.CureParanoia,
settings.BypassWiiDecryption,
settings.StartReverse);
settings.StartReverse,
settings.ErrorRecovery);
AnsiConsole.Progress()
.AutoClear(true)
@@ -806,6 +826,10 @@ sealed class DumpMediaCommand : Command<DumpMediaCommand.Settings>
[LocalizedDescription(nameof(UI.Start_reverse_error_retry))]
[CommandOption("--start-reverse")]
public bool StartReverse { get; init; }
[LocalizedDescription(nameof(UI.Add_error_recovery))]
[DefaultValue(0)]
[CommandOption("--error-recovery")]
public int ErrorRecovery { get; set; }
}
#endregion