Implement abortion in image convertion process.

This commit is contained in:
2025-11-25 19:47:40 +00:00
parent 37b8eeaf4f
commit 5febb2012d
10 changed files with 106 additions and 39 deletions

View File

@@ -283,5 +283,9 @@ public enum ErrorNumber
/// <summary>Specified sector could not be found</summary>
SectorNotFound = 29,
/// <summary>Image or device has not been opened</summary>
NotOpened = 30
NotOpened = 30,
/// <summary>Canceled</summary>
ECANCELED = Canceled,
/// <summary>Canceled</summary>
Canceled = -125
}

View File

@@ -15,6 +15,8 @@ public partial class Convert
/// <returns>Error if required features not supported and data would be lost</returns>
ErrorNumber ValidateMediaCapabilities()
{
if(_aborted) return ErrorNumber.NoError;
if(!_outputImage.SupportedMediaTypes.Contains(_mediaType))
{
StoppingErrorMessage?.Invoke(UI.Output_format_does_not_support_media_type);
@@ -22,8 +24,11 @@ public partial class Convert
return ErrorNumber.UnsupportedMedia;
}
foreach(MediaTagType mediaTag in _inputImage.Info.ReadableMediaTags.Where(mediaTag =>
!_outputImage.SupportedMediaTags.Contains(mediaTag) && !_force))
foreach(MediaTagType mediaTag in _inputImage.Info.ReadableMediaTags
.Where(mediaTag =>
!_outputImage.SupportedMediaTags.Contains(mediaTag) &&
!_force)
.TakeWhile(_ => !_aborted))
{
StoppingErrorMessage?.Invoke(string.Format(UI.Converting_image_will_lose_media_tag_0 +
Environment.NewLine +
@@ -36,17 +41,23 @@ public partial class Convert
return ErrorNumber.NoError;
}
/// <summary>
/// Validates sector tag compatibility between formats
/// Sets useLong flag based on sector tag support to determine sector size (512 vs 2352 bytes)
/// Some tags like CD flags/ISRC don't require long sectors; subchannel data does
/// In force mode, skips unsupported tags; otherwise reports error if data would be lost
/// </summary>
/// <param name="useLong"></param>
ErrorNumber ValidateSectorTags(out bool useLong)
{
// Validates sector tag compatibility between formats
// Sets useLong flag based on sector tag support to determine sector size (512 vs 2352 bytes)
// Some tags like CD flags/ISRC don't require long sectors; subchannel data does
// In force mode, skips unsupported tags; otherwise reports error if data would be lost
useLong = _inputImage.Info.ReadableSectorTags.Count != 0;
foreach(SectorTagType sectorTag in _inputImage.Info.ReadableSectorTags.Where(sectorTag =>
!_outputImage.SupportedSectorTags.Contains(sectorTag)))
if(_aborted) return ErrorNumber.NoError;
foreach(SectorTagType sectorTag in _inputImage.Info.ReadableSectorTags
.Where(sectorTag =>
!_outputImage.SupportedSectorTags.Contains(sectorTag))
.TakeWhile(_ => !_aborted))
{
if(_force)
{

View File

@@ -227,6 +227,8 @@ public partial class Convert
foreach(TapeFile tapeFile in inputTape.Files)
{
if(_aborted) break;
UpdateProgress?.Invoke(string.Format(UI.Converting_file_0_of_partition_1,
tapeFile.File,
tapeFile.Partition),
@@ -244,6 +246,8 @@ public partial class Convert
foreach(TapePartition tapePartition in inputTape.TapePartitions)
{
if(_aborted) break;
UpdateProgress?.Invoke(string.Format(UI.Converting_tape_partition_0, tapePartition.Number),
currentPartition + 1,
inputTape.TapePartitions.Count);
@@ -270,7 +274,7 @@ public partial class Convert
if(errno != ErrorNumber.NoError) return errno;
}
if(_resume != null || dumpHardware != null)
if((_resume != null || dumpHardware != null) && !_aborted)
{
InitProgress?.Invoke();
@@ -287,7 +291,7 @@ public partial class Convert
ret = false;
if(_sidecar != null || metadata != null)
if((_sidecar != null || metadata != null) && !_aborted)
{
InitProgress?.Invoke();
PulseProgress?.Invoke(UI.Writing_metadata);
@@ -301,6 +305,13 @@ public partial class Convert
EndProgress?.Invoke();
}
if(_aborted)
{
UpdateStatus?.Invoke(UI.Operation_canceled_the_output_file_is_not_correct);
return ErrorNumber.Canceled;
}
var closed = false;
InitProgress?.Invoke();

View File

@@ -13,6 +13,8 @@ public partial class Convert
/// <returns>Error code if creation fails</returns>
private ErrorNumber CreateOutputImage()
{
if(_aborted) return ErrorNumber.NoError;
InitProgress?.Invoke();
PulseProgress?.Invoke(UI.Invoke_Opening_image_file);

View File

@@ -15,6 +15,8 @@ public partial class Convert
/// <returns>Error code if conversion fails in non-force mode</returns>
ErrorNumber ConvertNegativeSectors(bool useLong)
{
if(_aborted) return ErrorNumber.NoError;
ErrorNumber errno = ErrorNumber.NoError;
InitProgress?.Invoke();
@@ -22,6 +24,8 @@ public partial class Convert
// There's no -0
for(uint i = 1; i <= _negativeSectors; i++)
{
if(_aborted) break;
byte[] sector;
UpdateProgress?.Invoke(string.Format(UI.Converting_negative_sector_0_of_1, i, _negativeSectors),
@@ -96,7 +100,8 @@ public partial class Convert
EndProgress?.Invoke();
foreach(SectorTagType tag in _inputImage.Info.ReadableSectorTags.TakeWhile(_ => useLong))
foreach(SectorTagType tag in _inputImage.Info.ReadableSectorTags.TakeWhile(_ => useLong)
.TakeWhile(_ => !_aborted))
{
switch(tag)
{
@@ -125,6 +130,8 @@ public partial class Convert
for(uint i = 1; i <= _negativeSectors; i++)
{
if(_aborted) break;
UpdateProgress?.Invoke(string.Format(UI.Converting_tag_1_for_negative_sector_0, i, tag),
i,
_negativeSectors);
@@ -167,12 +174,15 @@ public partial class Convert
return errno;
}
/// <summary>
/// Converts overflow sectors (lead-out) from input to output image
/// Handles both long and short sector formats with progress indication
/// Also converts associated sector tags if present
/// </summary>
/// <returns>Error code if conversion fails in non-force mode</returns>
ErrorNumber ConvertOverflowSectors(bool useLong)
{
// Converts overflow sectors (lead-out) from input to output image
// Handles both long and short sector formats with progress indication
// Also converts associated sector tags if present
// Returns error code if conversion fails in non-force mode
if(_aborted) return ErrorNumber.NoError;
ErrorNumber errno = ErrorNumber.NoError;
@@ -180,6 +190,8 @@ public partial class Convert
for(uint i = 0; i < _overflowSectors; i++)
{
if(_aborted) break;
byte[] sector;
UpdateProgress?.Invoke(string.Format(UI.Converting_overflow_sector_0_of_1, i, _overflowSectors),
@@ -254,7 +266,8 @@ public partial class Convert
EndProgress?.Invoke();
foreach(SectorTagType tag in _inputImage.Info.ReadableSectorTags.TakeWhile(_ => useLong))
foreach(SectorTagType tag in _inputImage.Info.ReadableSectorTags.TakeWhile(_ => useLong)
.TakeWhile(_ => !_aborted))
{
switch(tag)
{
@@ -283,6 +296,8 @@ public partial class Convert
for(uint i = 1; i <= _overflowSectors; i++)
{
if(_aborted) break;
UpdateProgress?.Invoke(string.Format(UI.Converting_tag_1_for_overflow_sector_0, i, tag),
i,
_overflowSectors);

View File

@@ -6,11 +6,15 @@ namespace Aaru.Core.Image;
public partial class Convert
{
/// <summary>
/// Builds and applies complete ImageInfo metadata to output image
/// Copies input metadata and applies command-line overrides (title, comments, creator, drive info, etc.)
/// Sets Aaru application version and applies all metadata fields to output format
/// </summary>
/// <returns></returns>
ErrorNumber SetImageMetadata()
{
// Builds and applies complete ImageInfo metadata to output image
// Copies input metadata and applies command-line overrides (title, comments, creator, drive info, etc.)
// Sets Aaru application version and applies all metadata fields to output format
if(_aborted) return ErrorNumber.NoError;
var imageInfo = new CommonTypes.Structs.ImageInfo
{

View File

@@ -32,18 +32,18 @@ public partial class Convert
if(errno != ErrorNumber.NoError) return errno;
Dictionary<byte, string> isrcs = new();
Dictionary<byte, byte> trackFlags = new();
Dictionary<byte, string> isrcs = [];
Dictionary<byte, byte> trackFlags = [];
string mcn = null;
HashSet<int> subchannelExtents = [];
Dictionary<byte, int> smallestPregapLbaPerTrack = new();
Dictionary<byte, int> smallestPregapLbaPerTrack = [];
var tracks = new Track[inputOptical.Tracks.Count];
for(var i = 0; i < tracks.Length; i++)
{
tracks[i] = new Track
{
Indexes = new Dictionary<ushort, int>(),
Indexes = [],
Description = inputOptical.Tracks[i].Description,
EndSector = inputOptical.Tracks[i].EndSector,
StartSector = inputOptical.Tracks[i].StartSector,
@@ -147,6 +147,7 @@ public partial class Convert
ErrorNumber ConvertOpticalSectors(IOpticalMediaImage inputOptical, IWritableOpticalImage outputOptical,
bool useLong)
{
if(_aborted) return ErrorNumber.NoError;
InitProgress?.Invoke();
InitProgress2?.Invoke();
byte[] generatedTitleKeys = null;
@@ -154,6 +155,8 @@ public partial class Convert
foreach(Track track in inputOptical.Tracks)
{
if(_aborted) break;
UpdateProgress?.Invoke(string.Format(UI.Converting_sectors_in_track_0_of_1,
currentTrack + 1,
inputOptical.Tracks.Count),
@@ -165,6 +168,7 @@ public partial class Convert
while(doneSectors < trackSectors)
{
if(_aborted) break;
byte[] sector;
uint sectorsToDo;
@@ -340,7 +344,9 @@ public partial class Convert
HashSet<int> subchannelExtents,
Dictionary<byte, int> smallestPregapLbaPerTrack)
{
foreach(SectorTagType tag in inputOptical.Info.ReadableSectorTags.Order().TakeWhile(_ => useLong))
foreach(SectorTagType tag in inputOptical.Info.ReadableSectorTags.Order()
.TakeWhile(_ => useLong)
.TakeWhile(_ => !_aborted))
{
switch(tag)
{
@@ -439,6 +445,7 @@ public partial class Convert
while(doneSectors < trackSectors)
{
if(_aborted) break;
uint sectorsToDo;
if(trackSectors - doneSectors >= _count)
@@ -616,12 +623,15 @@ public partial class Convert
return ErrorNumber.NoError;
}
/// <summary>
/// Decrypts DVD sectors using CSS (Content Scramble System) decryption
/// Retrieves decryption keys from sector tags or generates them from ISO9660 filesystem
/// Only MPEG packets within sectors can be encrypted
/// </summary>
void DecryptDvdSector(ref byte[] sector, IOpticalMediaImage inputOptical, ulong sectorAddress, uint sectorsToDo,
PluginRegister plugins, ref byte[] generatedTitleKeys)
{
// Decrypts DVD sectors using CSS (Content Scramble System) decryption
// Retrieves decryption keys from sector tags or generates them from ISO9660 filesystem
// Only MPEG packets within sectors can be encrypted
if(_aborted) return;
// Only sectors which are MPEG packets can be encrypted.
if(!Mpeg.ContainsMpegPackets(sector, sectorsToDo)) return;
@@ -682,6 +692,8 @@ public partial class Convert
/// </summary>
void GenerateDvdTitleKeys(IOpticalMediaImage inputOptical, PluginRegister plugins, ref byte[] generatedTitleKeys)
{
if(_aborted) return;
List<Partition> partitions = Partitions.GetAll(inputOptical);
partitions = partitions.FindAll(p =>

View File

@@ -9,6 +9,8 @@ public partial class Convert
{
ErrorNumber ConvertSectors(bool useLong, bool isTape)
{
if(_aborted) return ErrorNumber.NoError;
InitProgress?.Invoke();
ErrorNumber errno = ErrorNumber.NoError;
ulong doneSectors = 0;
@@ -133,6 +135,8 @@ public partial class Convert
ErrorNumber ConvertSectorsTags(bool useLong)
{
if(_aborted) return ErrorNumber.NoError;
foreach(SectorTagType tag in _inputImage.Info.ReadableSectorTags.TakeWhile(_ => useLong))
{
switch(tag)

View File

@@ -16,6 +16,8 @@ public partial class Convert
/// <returns>Status code</returns>
ErrorNumber ConvertMediaTags()
{
if(_aborted) return ErrorNumber.NoError;
InitProgress?.Invoke();
ErrorNumber errorNumber = ErrorNumber.NoError;

View File

@@ -7,26 +7,28 @@ namespace Aaru.Core.Image;
public partial class Convert
{
/// <summary>
/// Validates tape image format compatibility
/// Checks if input is tape-based but output format doesn't support tape images
/// </summary>
/// <returns>Error if unsupported media type combination detected</returns>
ErrorNumber ValidateTapeImage(ITapeImage inputTape, IWritableTapeImage outputTape)
{
// Validates tape image format compatibility
// Checks if input is tape-based but output format doesn't support tape images
// Returns error if unsupported media type combination detected
if(inputTape?.IsTape != true || outputTape is not null) return ErrorNumber.NoError;
if(_aborted || inputTape?.IsTape != true || outputTape is not null) return ErrorNumber.NoError;
StoppingErrorMessage?.Invoke(UI.Input_format_contains_a_tape_image_and_is_not_supported_by_output_format);
return ErrorNumber.UnsupportedMedia;
}
/// <summary>
/// Configures output format for tape image handling
/// Calls SetTape() on output to initialize tape mode if both input and output support tapes
/// </summary>
/// <returns>Error if tape mode initialization fails</returns>
ErrorNumber SetupTapeImage(ITapeImage inputTape, IWritableTapeImage outputTape)
{
// Configures output format for tape image handling
// Calls SetTape() on output to initialize tape mode if both input and output support tapes
// Returns error if tape mode initialization fails
if(inputTape?.IsTape != true || outputTape == null) return ErrorNumber.NoError;
if(_aborted || inputTape?.IsTape != true || outputTape == null) return ErrorNumber.NoError;
bool ret = outputTape.SetTape();