mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
Rework image conversion command to separate into parts and move to Aaru.Core.
This commit is contained in:
72
Aaru.Core/Image/Convert/Capabilities.cs
Normal file
72
Aaru.Core/Image/Convert/Capabilities.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
|
using Aaru.Localization;
|
||||||
|
|
||||||
|
namespace Aaru.Core.Image;
|
||||||
|
|
||||||
|
public partial class Convert
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Validates media type and media tag support in output format
|
||||||
|
/// Checks if output format supports the media type being converted
|
||||||
|
/// Validates all readable media tags are supported by output (unless force mode enabled)
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Error if required features not supported and data would be lost</returns>
|
||||||
|
ErrorNumber ValidateMediaCapabilities()
|
||||||
|
{
|
||||||
|
if(!_outputImage.SupportedMediaTypes.Contains(_mediaType))
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(UI.Output_format_does_not_support_media_type);
|
||||||
|
|
||||||
|
return ErrorNumber.UnsupportedMedia;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(MediaTagType mediaTag in _inputImage.Info.ReadableMediaTags.Where(mediaTag =>
|
||||||
|
!_outputImage.SupportedMediaTags.Contains(mediaTag) && !_force))
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Converting_image_will_lose_media_tag_0 +
|
||||||
|
Environment.NewLine +
|
||||||
|
UI.If_you_dont_care_use_force_option,
|
||||||
|
mediaTag));
|
||||||
|
|
||||||
|
return ErrorNumber.DataWillBeLost;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(_force)
|
||||||
|
{
|
||||||
|
if(sectorTag != SectorTagType.CdTrackFlags &&
|
||||||
|
sectorTag != SectorTagType.CdTrackIsrc &&
|
||||||
|
sectorTag != SectorTagType.CdSectorSubchannel)
|
||||||
|
useLong = false;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
StoppingErrorMessage.Invoke(string.Format(UI.Converting_image_will_lose_sector_tag_0 +
|
||||||
|
Environment.NewLine +
|
||||||
|
UI
|
||||||
|
.If_you_dont_care_use_force_option_This_will_skip_all_sector_tags_converting_only_user_data,
|
||||||
|
sectorTag));
|
||||||
|
|
||||||
|
return ErrorNumber.DataWillBeLost;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
}
|
||||||
358
Aaru.Core/Image/Convert/Convert.cs
Normal file
358
Aaru.Core/Image/Convert/Convert.cs
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Aaru.CommonTypes;
|
||||||
|
using Aaru.CommonTypes.AaruMetadata;
|
||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
|
using Aaru.CommonTypes.Interfaces;
|
||||||
|
using Aaru.CommonTypes.Metadata;
|
||||||
|
using Aaru.Localization;
|
||||||
|
using MediaType = Aaru.CommonTypes.MediaType;
|
||||||
|
using TapeFile = Aaru.CommonTypes.Structs.TapeFile;
|
||||||
|
using TapePartition = Aaru.CommonTypes.Structs.TapePartition;
|
||||||
|
|
||||||
|
namespace Aaru.Core.Image;
|
||||||
|
|
||||||
|
public partial class Convert
|
||||||
|
{
|
||||||
|
const string MODULE_NAME = "Image Conversion";
|
||||||
|
readonly string _comments;
|
||||||
|
readonly uint _count;
|
||||||
|
readonly string _creator;
|
||||||
|
readonly bool _decrypt;
|
||||||
|
readonly string _driveFirmwareRevision;
|
||||||
|
readonly string _driveManufacturer;
|
||||||
|
readonly string _driveModel;
|
||||||
|
readonly string _driveSerialNumber;
|
||||||
|
readonly bool _fixSubchannel;
|
||||||
|
readonly bool _fixSubchannelCrc;
|
||||||
|
readonly bool _fixSubchannelPosition;
|
||||||
|
readonly bool _force;
|
||||||
|
readonly bool _generateSubchannels;
|
||||||
|
readonly (uint cylinders, uint heads, uint sectors)? _geometryValues;
|
||||||
|
readonly IMediaImage _inputImage;
|
||||||
|
readonly int _lastMediaSequence;
|
||||||
|
readonly string _mediaBarcode;
|
||||||
|
readonly string _mediaManufacturer;
|
||||||
|
readonly string _mediaModel;
|
||||||
|
readonly string _mediaPartNumber;
|
||||||
|
readonly int _mediaSequence;
|
||||||
|
readonly string _mediaSerialNumber;
|
||||||
|
readonly string _mediaTitle;
|
||||||
|
readonly MediaType _mediaType;
|
||||||
|
readonly uint _negativeSectors;
|
||||||
|
readonly IWritableImage _outputImage;
|
||||||
|
readonly string _outputPath;
|
||||||
|
readonly uint _overflowSectors;
|
||||||
|
readonly Dictionary<string, string> _parsedOptions;
|
||||||
|
readonly PluginRegister _plugins;
|
||||||
|
readonly Resume _resume;
|
||||||
|
readonly Metadata _sidecar;
|
||||||
|
bool _aborted;
|
||||||
|
|
||||||
|
// TODO: Abort
|
||||||
|
public Convert(IMediaImage inputImage, IWritableImage outputImage, MediaType mediaType, bool force,
|
||||||
|
string outputPath, Dictionary<string, string> parsedOptions, uint negativeSectors,
|
||||||
|
uint overflowSectors, string comments, string creator, string driveFirmwareRevision,
|
||||||
|
string driveManufacturer, string driveModel, string driveSerialNumber, int lastMediaSequence,
|
||||||
|
string mediaBarcode, string mediaManufacturer, string mediaModel, string mediaPartNumber,
|
||||||
|
int mediaSequence, string mediaSerialNumber, string mediaTitle, bool decrypt, uint count,
|
||||||
|
PluginRegister plugins, bool fixSubchannelPosition, bool fixSubchannel, bool fixSubchannelCrc,
|
||||||
|
bool generateSubchannels, (uint cylinders, uint heads, uint sectors)? geometryValues, Resume resume,
|
||||||
|
Metadata sidecar)
|
||||||
|
{
|
||||||
|
_inputImage = inputImage;
|
||||||
|
_outputImage = outputImage;
|
||||||
|
_mediaType = mediaType;
|
||||||
|
_force = force;
|
||||||
|
_outputPath = outputPath;
|
||||||
|
_parsedOptions = parsedOptions;
|
||||||
|
_negativeSectors = negativeSectors;
|
||||||
|
_overflowSectors = overflowSectors;
|
||||||
|
_comments = comments;
|
||||||
|
_creator = creator;
|
||||||
|
_driveFirmwareRevision = driveFirmwareRevision;
|
||||||
|
_driveManufacturer = driveManufacturer;
|
||||||
|
_driveModel = driveModel;
|
||||||
|
_driveSerialNumber = driveSerialNumber;
|
||||||
|
_lastMediaSequence = lastMediaSequence;
|
||||||
|
_mediaBarcode = mediaBarcode;
|
||||||
|
_mediaManufacturer = mediaManufacturer;
|
||||||
|
_mediaModel = mediaModel;
|
||||||
|
_mediaPartNumber = mediaPartNumber;
|
||||||
|
_mediaSequence = mediaSequence;
|
||||||
|
_mediaSerialNumber = mediaSerialNumber;
|
||||||
|
_mediaTitle = mediaTitle;
|
||||||
|
_decrypt = decrypt;
|
||||||
|
_count = count;
|
||||||
|
_plugins = plugins;
|
||||||
|
_fixSubchannelPosition = fixSubchannelPosition;
|
||||||
|
_fixSubchannel = fixSubchannel;
|
||||||
|
_fixSubchannelCrc = fixSubchannelCrc;
|
||||||
|
_generateSubchannels = generateSubchannels;
|
||||||
|
_geometryValues = geometryValues;
|
||||||
|
_resume = resume;
|
||||||
|
_sidecar = sidecar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ErrorNumber Start()
|
||||||
|
{
|
||||||
|
// Validate that output format supports the media type and tags
|
||||||
|
ErrorNumber errno = ValidateMediaCapabilities();
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
|
||||||
|
// Validate sector tags compatibility between formats
|
||||||
|
errno = ValidateSectorTags(out bool useLong);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
|
||||||
|
// Check and setup tape image support if needed
|
||||||
|
var inputTape = _inputImage as ITapeImage;
|
||||||
|
var outputTape = _outputImage as IWritableTapeImage;
|
||||||
|
|
||||||
|
errno = ValidateTapeImage(inputTape, outputTape);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
|
||||||
|
var ret = false;
|
||||||
|
|
||||||
|
errno = SetupTapeImage(inputTape, outputTape);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
|
||||||
|
// Validate optical media capabilities (sessions, hidden tracks, etc.)
|
||||||
|
if((_outputImage as IWritableOpticalImage)?.OpticalCapabilities.HasFlag(OpticalImageCapabilities
|
||||||
|
.CanStoreSessions) !=
|
||||||
|
true &&
|
||||||
|
(_inputImage as IOpticalMediaImage)?.Sessions?.Count > 1)
|
||||||
|
{
|
||||||
|
// TODO: Disabled until 6.0
|
||||||
|
/*if(!_force)
|
||||||
|
{*/
|
||||||
|
StoppingErrorMessage?.Invoke(Localization.Core.Output_format_does_not_support_sessions);
|
||||||
|
|
||||||
|
return ErrorNumber.UnsupportedMedia;
|
||||||
|
/*}
|
||||||
|
|
||||||
|
StoppingErrorMessage?.Invoke("Output format does not support sessions, this will end in a loss of data, continuing...");*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for hidden tracks support in optical media
|
||||||
|
if((_outputImage as IWritableOpticalImage)?.OpticalCapabilities.HasFlag(OpticalImageCapabilities
|
||||||
|
.CanStoreHiddenTracks) !=
|
||||||
|
true &&
|
||||||
|
(_inputImage as IOpticalMediaImage)?.Tracks?.Any(static t => t.Sequence == 0) == true)
|
||||||
|
{
|
||||||
|
// TODO: Disabled until 6.0
|
||||||
|
/*if(!_force)
|
||||||
|
{*/
|
||||||
|
StoppingErrorMessage?.Invoke(Localization.Core.Output_format_does_not_support_hidden_tracks);
|
||||||
|
|
||||||
|
return ErrorNumber.UnsupportedMedia;
|
||||||
|
/*}
|
||||||
|
|
||||||
|
StoppingErrorMessage?.Invoke("Output format does not support sessions, this will end in a loss of data, continuing...");*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the output image file with appropriate settings
|
||||||
|
errno = CreateOutputImage();
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
|
||||||
|
// Set image metadata in the output file
|
||||||
|
errno = SetImageMetadata();
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
|
||||||
|
// Prepare metadata and dump hardware information
|
||||||
|
Metadata metadata = _inputImage.AaruMetadata;
|
||||||
|
List<DumpHardware> dumpHardware = _inputImage.DumpHardware;
|
||||||
|
|
||||||
|
// Convert media tags from input to output format
|
||||||
|
errno = ConvertMediaTags();
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError && !_force) return errno;
|
||||||
|
|
||||||
|
UpdateStatus?.Invoke(string.Format(UI._0_sectors_to_convert, _inputImage.Info.Sectors));
|
||||||
|
|
||||||
|
// Perform the actual data conversion from input to output image
|
||||||
|
if(_inputImage is IOpticalMediaImage inputOptical &&
|
||||||
|
_outputImage is IWritableOpticalImage outputOptical &&
|
||||||
|
inputOptical.Tracks != null)
|
||||||
|
{
|
||||||
|
errno = ConvertOptical(inputOptical, outputOptical, useLong);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(inputTape == null || outputTape == null || !inputTape.IsTape)
|
||||||
|
{
|
||||||
|
(uint cylinders, uint heads, uint sectors) chs =
|
||||||
|
_geometryValues != null
|
||||||
|
? (_geometryValues.Value.cylinders, _geometryValues.Value.heads, _geometryValues.Value.sectors)
|
||||||
|
: (_inputImage.Info.Cylinders, _inputImage.Info.Heads, _inputImage.Info.SectorsPerTrack);
|
||||||
|
|
||||||
|
UpdateStatus?.Invoke(string.Format(UI.Setting_geometry_to_0_cylinders_1_heads_and_2_sectors_per_track,
|
||||||
|
chs.cylinders,
|
||||||
|
chs.heads,
|
||||||
|
chs.sectors));
|
||||||
|
|
||||||
|
if(!_outputImage.SetGeometry(chs.cylinders, chs.heads, chs.sectors))
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_setting_geometry_image_may_be_incorrect_continuing,
|
||||||
|
_outputImage.ErrorMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = ConvertSectors(useLong, inputTape?.IsTape == true);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
|
||||||
|
errno = ConvertSectorsTags(useLong);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
|
||||||
|
if(_inputImage is IFluxImage inputFlux && _outputImage is IWritableFluxImage outputFlux)
|
||||||
|
{
|
||||||
|
errno = ConvertFlux(inputFlux, outputFlux);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(inputTape != null && outputTape != null && inputTape.IsTape)
|
||||||
|
{
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
var currentFile = 0;
|
||||||
|
|
||||||
|
foreach(TapeFile tapeFile in inputTape.Files)
|
||||||
|
{
|
||||||
|
UpdateProgress?.Invoke(string.Format(UI.Converting_file_0_of_partition_1,
|
||||||
|
tapeFile.File,
|
||||||
|
tapeFile.Partition),
|
||||||
|
currentFile + 1,
|
||||||
|
inputTape.Files.Count);
|
||||||
|
|
||||||
|
outputTape.AddFile(tapeFile);
|
||||||
|
currentFile++;
|
||||||
|
}
|
||||||
|
|
||||||
|
EndProgress?.Invoke();
|
||||||
|
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
var currentPartition = 0;
|
||||||
|
|
||||||
|
foreach(TapePartition tapePartition in inputTape.TapePartitions)
|
||||||
|
{
|
||||||
|
UpdateProgress?.Invoke(string.Format(UI.Converting_tape_partition_0, tapePartition.Number),
|
||||||
|
currentPartition + 1,
|
||||||
|
inputTape.TapePartitions.Count);
|
||||||
|
|
||||||
|
outputTape.AddPartition(tapePartition);
|
||||||
|
currentPartition++;
|
||||||
|
}
|
||||||
|
|
||||||
|
EndProgress?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_negativeSectors > 0)
|
||||||
|
{
|
||||||
|
errno = ConvertNegativeSectors(useLong);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_overflowSectors > 0)
|
||||||
|
{
|
||||||
|
errno = ConvertOverflowSectors(useLong);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_resume != null || dumpHardware != null)
|
||||||
|
{
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
|
||||||
|
PulseProgress?.Invoke(UI.Writing_dump_hardware_list);
|
||||||
|
|
||||||
|
if(_resume != null)
|
||||||
|
ret = _outputImage.SetDumpHardware(_resume.Tries);
|
||||||
|
else if(dumpHardware != null) ret = _outputImage.SetDumpHardware(dumpHardware);
|
||||||
|
|
||||||
|
if(ret) UpdateStatus?.Invoke(UI.Written_dump_hardware_list_to_output_image);
|
||||||
|
|
||||||
|
EndProgress?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = false;
|
||||||
|
|
||||||
|
if(_sidecar != null || metadata != null)
|
||||||
|
{
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
PulseProgress?.Invoke(UI.Writing_metadata);
|
||||||
|
|
||||||
|
if(_sidecar != null)
|
||||||
|
ret = _outputImage.SetMetadata(_sidecar);
|
||||||
|
else if(metadata != null) ret = _outputImage.SetMetadata(metadata);
|
||||||
|
|
||||||
|
if(ret) UpdateStatus?.Invoke(UI.Written_Aaru_Metadata_to_output_image);
|
||||||
|
|
||||||
|
EndProgress?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
var closed = false;
|
||||||
|
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
PulseProgress?.Invoke(UI.Closing_output_image);
|
||||||
|
closed = _outputImage.Close();
|
||||||
|
EndProgress?.Invoke();
|
||||||
|
|
||||||
|
if(!closed)
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_closing_output_image_Contents_are_not_correct,
|
||||||
|
_outputImage.ErrorMessage));
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateStatus?.Invoke(UI.Conversion_done);
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Event raised when the progress bar is no longer needed</summary>
|
||||||
|
public event EndProgressHandler EndProgress;
|
||||||
|
|
||||||
|
/// <summary>Event raised when a progress bar is needed</summary>
|
||||||
|
public event InitProgressHandler InitProgress;
|
||||||
|
|
||||||
|
/// <summary>Event raised to report status updates</summary>
|
||||||
|
public event UpdateStatusHandler UpdateStatus;
|
||||||
|
|
||||||
|
/// <summary>Event raised to report a non-fatal error</summary>
|
||||||
|
public event ErrorMessageHandler ErrorMessage;
|
||||||
|
|
||||||
|
/// <summary>Event raised to report a fatal error that stops the dumping operation and should call user's attention</summary>
|
||||||
|
public event ErrorMessageHandler StoppingErrorMessage;
|
||||||
|
|
||||||
|
/// <summary>Event raised to update the values of a determinate progress bar</summary>
|
||||||
|
public event UpdateProgressHandler UpdateProgress;
|
||||||
|
|
||||||
|
/// <summary>Event raised to update the status of an indeterminate progress bar</summary>
|
||||||
|
public event PulseProgressHandler PulseProgress;
|
||||||
|
|
||||||
|
/// <summary>Event raised when the progress bar is no longer needed</summary>
|
||||||
|
public event EndProgressHandler2 EndProgress2;
|
||||||
|
|
||||||
|
/// <summary>Event raised when a progress bar is needed</summary>
|
||||||
|
public event InitProgressHandler2 InitProgress2;
|
||||||
|
|
||||||
|
/// <summary>Event raised to update the values of a determinate progress bar</summary>
|
||||||
|
public event UpdateProgressHandler2 UpdateProgress2;
|
||||||
|
|
||||||
|
public void Abort()
|
||||||
|
{
|
||||||
|
_aborted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
35
Aaru.Core/Image/Convert/Create.cs
Normal file
35
Aaru.Core/Image/Convert/Create.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
|
using Aaru.Localization;
|
||||||
|
|
||||||
|
namespace Aaru.Core.Image;
|
||||||
|
|
||||||
|
public partial class Convert
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates output image file with specified parameters
|
||||||
|
/// Calls the output format plugin's Create() method with sector count and format options
|
||||||
|
/// Shows progress indicator during file creation
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Error code if creation fails</returns>
|
||||||
|
private ErrorNumber CreateOutputImage()
|
||||||
|
{
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
PulseProgress?.Invoke(UI.Invoke_Opening_image_file);
|
||||||
|
|
||||||
|
bool created = _outputImage.Create(_outputPath,
|
||||||
|
_mediaType,
|
||||||
|
_parsedOptions,
|
||||||
|
_inputImage.Info.Sectors,
|
||||||
|
_negativeSectors,
|
||||||
|
_overflowSectors,
|
||||||
|
_inputImage.Info.SectorSize);
|
||||||
|
|
||||||
|
EndProgress?.Invoke();
|
||||||
|
|
||||||
|
if(created) return ErrorNumber.NoError;
|
||||||
|
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_creating_output_image, _outputImage.ErrorMessage));
|
||||||
|
|
||||||
|
return ErrorNumber.CannotCreateFormat;
|
||||||
|
}
|
||||||
|
}
|
||||||
339
Aaru.Core/Image/Convert/Edge.cs
Normal file
339
Aaru.Core/Image/Convert/Edge.cs
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
|
using Aaru.Localization;
|
||||||
|
using Aaru.Logging;
|
||||||
|
|
||||||
|
namespace Aaru.Core.Image;
|
||||||
|
|
||||||
|
public partial class Convert
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts negative sectors (pre-gap) 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 ConvertNegativeSectors(bool useLong)
|
||||||
|
{
|
||||||
|
ErrorNumber errno = ErrorNumber.NoError;
|
||||||
|
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
|
||||||
|
// There's no -0
|
||||||
|
for(uint i = 1; i <= _negativeSectors; i++)
|
||||||
|
{
|
||||||
|
byte[] sector;
|
||||||
|
|
||||||
|
UpdateProgress?.Invoke(string.Format(UI.Converting_negative_sector_0_of_1, i, _negativeSectors),
|
||||||
|
i,
|
||||||
|
_negativeSectors);
|
||||||
|
|
||||||
|
bool result;
|
||||||
|
SectorStatus sectorStatus;
|
||||||
|
|
||||||
|
if(useLong)
|
||||||
|
{
|
||||||
|
errno = _inputImage.ReadSectorLong(i, true, out sector, out sectorStatus);
|
||||||
|
|
||||||
|
if(errno == ErrorNumber.NoError)
|
||||||
|
result = _outputImage.WriteSectorLong(sector, i, true, sectorStatus);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_reading_negative_sector_1_continuing, errno, i));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_reading_negative_sector_1_not_continuing,
|
||||||
|
errno,
|
||||||
|
i));
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errno = _inputImage.ReadSector(i, true, out sector, out sectorStatus);
|
||||||
|
|
||||||
|
if(errno == ErrorNumber.NoError)
|
||||||
|
result = _outputImage.WriteSector(sector, i, true, sectorStatus);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_reading_negative_sector_1_continuing, errno, i));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_reading_negative_sector_1_not_continuing,
|
||||||
|
errno,
|
||||||
|
i));
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result) continue;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_writing_negative_sector_1_continuing,
|
||||||
|
_outputImage.ErrorMessage,
|
||||||
|
i));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_writing_negative_sector_1_not_continuing,
|
||||||
|
_outputImage.ErrorMessage,
|
||||||
|
i));
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EndProgress?.Invoke();
|
||||||
|
|
||||||
|
foreach(SectorTagType tag in _inputImage.Info.ReadableSectorTags.TakeWhile(_ => useLong))
|
||||||
|
{
|
||||||
|
switch(tag)
|
||||||
|
{
|
||||||
|
case SectorTagType.AppleSonyTag:
|
||||||
|
case SectorTagType.AppleProfileTag:
|
||||||
|
case SectorTagType.PriamDataTowerTag:
|
||||||
|
case SectorTagType.CdSectorSync:
|
||||||
|
case SectorTagType.CdSectorHeader:
|
||||||
|
case SectorTagType.CdSectorSubHeader:
|
||||||
|
case SectorTagType.CdSectorEdc:
|
||||||
|
case SectorTagType.CdSectorEccP:
|
||||||
|
case SectorTagType.CdSectorEccQ:
|
||||||
|
case SectorTagType.CdSectorEcc:
|
||||||
|
// These tags are inline in long sector
|
||||||
|
continue;
|
||||||
|
case SectorTagType.CdTrackFlags:
|
||||||
|
case SectorTagType.CdTrackIsrc:
|
||||||
|
case SectorTagType.CdTrackText:
|
||||||
|
// These tags are track tags
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_force && !_outputImage.SupportedSectorTags.Contains(tag)) continue;
|
||||||
|
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
|
||||||
|
for(uint i = 1; i <= _negativeSectors; i++)
|
||||||
|
{
|
||||||
|
UpdateProgress?.Invoke(string.Format(UI.Converting_tag_1_for_negative_sector_0, i, tag),
|
||||||
|
i,
|
||||||
|
_negativeSectors);
|
||||||
|
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
errno = _inputImage.ReadSectorTag(i, true, tag, out byte[] sector);
|
||||||
|
|
||||||
|
if(errno == ErrorNumber.NoError)
|
||||||
|
result = _outputImage.WriteSectorTag(sector, i, true, tag);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
AaruLogging.Error(UI.Error_0_reading_negative_sector_1_continuing, errno, i);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AaruLogging.Error(UI.Error_0_reading_negative_sector_1_not_continuing, errno, i);
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result) continue;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
AaruLogging.Error(UI.Error_0_writing_negative_sector_1_continuing, _outputImage.ErrorMessage, i);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AaruLogging.Error(UI.Error_0_writing_negative_sector_1_not_continuing,
|
||||||
|
_outputImage.ErrorMessage,
|
||||||
|
i);
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
ErrorNumber errno = ErrorNumber.NoError;
|
||||||
|
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
|
||||||
|
for(uint i = 0; i < _overflowSectors; i++)
|
||||||
|
{
|
||||||
|
byte[] sector;
|
||||||
|
|
||||||
|
UpdateProgress?.Invoke(string.Format(UI.Converting_overflow_sector_0_of_1, i, _overflowSectors),
|
||||||
|
i,
|
||||||
|
_overflowSectors);
|
||||||
|
|
||||||
|
bool result;
|
||||||
|
SectorStatus sectorStatus;
|
||||||
|
|
||||||
|
if(useLong)
|
||||||
|
{
|
||||||
|
errno = _inputImage.ReadSectorLong(_inputImage.Info.Sectors + i, false, out sector, out sectorStatus);
|
||||||
|
|
||||||
|
if(errno == ErrorNumber.NoError)
|
||||||
|
result = _outputImage.WriteSectorLong(sector, _inputImage.Info.Sectors + i, false, sectorStatus);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_reading_overflow_sector_1_continuing, errno, i));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_reading_overflow_sector_1_not_continuing,
|
||||||
|
errno,
|
||||||
|
i));
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errno = _inputImage.ReadSector(_inputImage.Info.Sectors + i, false, out sector, out sectorStatus);
|
||||||
|
|
||||||
|
if(errno == ErrorNumber.NoError)
|
||||||
|
result = _outputImage.WriteSector(sector, _inputImage.Info.Sectors + i, false, sectorStatus);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_reading_overflow_sector_1_continuing, errno, i));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_reading_overflow_sector_1_not_continuing,
|
||||||
|
errno,
|
||||||
|
i));
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result) continue;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_writing_overflow_sector_1_continuing,
|
||||||
|
_outputImage.ErrorMessage,
|
||||||
|
i));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_writing_overflow_sector_1_not_continuing,
|
||||||
|
_outputImage.ErrorMessage,
|
||||||
|
i));
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EndProgress?.Invoke();
|
||||||
|
|
||||||
|
foreach(SectorTagType tag in _inputImage.Info.ReadableSectorTags.TakeWhile(_ => useLong))
|
||||||
|
{
|
||||||
|
switch(tag)
|
||||||
|
{
|
||||||
|
case SectorTagType.AppleSonyTag:
|
||||||
|
case SectorTagType.AppleProfileTag:
|
||||||
|
case SectorTagType.PriamDataTowerTag:
|
||||||
|
case SectorTagType.CdSectorSync:
|
||||||
|
case SectorTagType.CdSectorHeader:
|
||||||
|
case SectorTagType.CdSectorSubHeader:
|
||||||
|
case SectorTagType.CdSectorEdc:
|
||||||
|
case SectorTagType.CdSectorEccP:
|
||||||
|
case SectorTagType.CdSectorEccQ:
|
||||||
|
case SectorTagType.CdSectorEcc:
|
||||||
|
// These tags are inline in long sector
|
||||||
|
continue;
|
||||||
|
case SectorTagType.CdTrackFlags:
|
||||||
|
case SectorTagType.CdTrackIsrc:
|
||||||
|
case SectorTagType.CdTrackText:
|
||||||
|
// These tags are track tags
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_force && !_outputImage.SupportedSectorTags.Contains(tag)) continue;
|
||||||
|
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
|
||||||
|
for(uint i = 1; i <= _overflowSectors; i++)
|
||||||
|
{
|
||||||
|
UpdateProgress?.Invoke(string.Format(UI.Converting_tag_1_for_overflow_sector_0, i, tag),
|
||||||
|
i,
|
||||||
|
_overflowSectors);
|
||||||
|
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
errno = _inputImage.ReadSectorTag(_inputImage.Info.Sectors + i, false, tag, out byte[] sector);
|
||||||
|
|
||||||
|
if(errno == ErrorNumber.NoError)
|
||||||
|
result = _outputImage.WriteSectorTag(sector, _inputImage.Info.Sectors + i, false, tag);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_reading_overflow_sector_1_continuing,
|
||||||
|
errno,
|
||||||
|
_inputImage.Info.Sectors + i));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_reading_overflow_sector_1_not_continuing,
|
||||||
|
errno,
|
||||||
|
_inputImage.Info.Sectors + i));
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result) continue;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_writing_overflow_sector_1_continuing,
|
||||||
|
_outputImage.ErrorMessage,
|
||||||
|
_inputImage.Info.Sectors + i));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_writing_overflow_sector_1_not_continuing,
|
||||||
|
_outputImage.ErrorMessage,
|
||||||
|
_inputImage.Info.Sectors + i));
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EndProgress?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
52
Aaru.Core/Image/Convert/Flux.cs
Normal file
52
Aaru.Core/Image/Convert/Flux.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
|
using Aaru.CommonTypes.Interfaces;
|
||||||
|
|
||||||
|
namespace Aaru.Core.Image;
|
||||||
|
|
||||||
|
public partial class Convert
|
||||||
|
{
|
||||||
|
// TODO: Should we return error any time?
|
||||||
|
// TODO: Add progress reporting
|
||||||
|
ErrorNumber ConvertFlux(IFluxImage inputFlux, IWritableFluxImage outputFlux)
|
||||||
|
{
|
||||||
|
for(ushort track = 0; track < inputFlux.Info.Cylinders; track++)
|
||||||
|
{
|
||||||
|
for(uint head = 0; head < inputFlux.Info.Heads; head++)
|
||||||
|
{
|
||||||
|
ErrorNumber error = inputFlux.SubTrackLength(head, track, out byte subTrackLen);
|
||||||
|
|
||||||
|
if(error != ErrorNumber.NoError) continue;
|
||||||
|
|
||||||
|
for(byte subTrackIndex = 0; subTrackIndex < subTrackLen; subTrackIndex++)
|
||||||
|
{
|
||||||
|
error = inputFlux.CapturesLength(head, track, subTrackIndex, out uint capturesLen);
|
||||||
|
|
||||||
|
if(error != ErrorNumber.NoError) continue;
|
||||||
|
|
||||||
|
for(uint captureIndex = 0; captureIndex < capturesLen; captureIndex++)
|
||||||
|
{
|
||||||
|
inputFlux.ReadFluxCapture(head,
|
||||||
|
track,
|
||||||
|
subTrackIndex,
|
||||||
|
captureIndex,
|
||||||
|
out ulong indexResolution,
|
||||||
|
out ulong dataResolution,
|
||||||
|
out byte[] indexBuffer,
|
||||||
|
out byte[] dataBuffer);
|
||||||
|
|
||||||
|
outputFlux.WriteFluxCapture(indexResolution,
|
||||||
|
dataResolution,
|
||||||
|
indexBuffer,
|
||||||
|
dataBuffer,
|
||||||
|
head,
|
||||||
|
track,
|
||||||
|
subTrackIndex,
|
||||||
|
captureIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
}
|
||||||
49
Aaru.Core/Image/Convert/Metadata.cs
Normal file
49
Aaru.Core/Image/Convert/Metadata.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
|
using Aaru.CommonTypes.Interop;
|
||||||
|
using Aaru.Localization;
|
||||||
|
|
||||||
|
namespace Aaru.Core.Image;
|
||||||
|
|
||||||
|
public partial class Convert
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
var imageInfo = new CommonTypes.Structs.ImageInfo
|
||||||
|
{
|
||||||
|
Application = "Aaru",
|
||||||
|
ApplicationVersion = Version.GetInformationalVersion(),
|
||||||
|
Comments = _comments ?? _inputImage.Info.Comments,
|
||||||
|
Creator = _creator ?? _inputImage.Info.Creator,
|
||||||
|
DriveFirmwareRevision = _driveFirmwareRevision ?? _inputImage.Info.DriveFirmwareRevision,
|
||||||
|
DriveManufacturer = _driveManufacturer ?? _inputImage.Info.DriveManufacturer,
|
||||||
|
DriveModel = _driveModel ?? _inputImage.Info.DriveModel,
|
||||||
|
DriveSerialNumber = _driveSerialNumber ?? _inputImage.Info.DriveSerialNumber,
|
||||||
|
LastMediaSequence = _lastMediaSequence != 0 ? _lastMediaSequence : _inputImage.Info.LastMediaSequence,
|
||||||
|
MediaBarcode = _mediaBarcode ?? _inputImage.Info.MediaBarcode,
|
||||||
|
MediaManufacturer = _mediaManufacturer ?? _inputImage.Info.MediaManufacturer,
|
||||||
|
MediaModel = _mediaModel ?? _inputImage.Info.MediaModel,
|
||||||
|
MediaPartNumber = _mediaPartNumber ?? _inputImage.Info.MediaPartNumber,
|
||||||
|
MediaSequence = _mediaSequence != 0 ? _mediaSequence : _inputImage.Info.MediaSequence,
|
||||||
|
MediaSerialNumber = _mediaSerialNumber ?? _inputImage.Info.MediaSerialNumber,
|
||||||
|
MediaTitle = _mediaTitle ?? _inputImage.Info.MediaTitle
|
||||||
|
};
|
||||||
|
|
||||||
|
if(_outputImage.SetImageInfo(imageInfo)) return ErrorNumber.NoError;
|
||||||
|
|
||||||
|
if(!_force)
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_setting_metadata_not_continuing,
|
||||||
|
_outputImage.ErrorMessage));
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorMessage?.Invoke(string.Format(Localization.Core.Error_0_setting_metadata, _outputImage.ErrorMessage));
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
}
|
||||||
751
Aaru.Core/Image/Convert/Optical.cs
Normal file
751
Aaru.Core/Image/Convert/Optical.cs
Normal file
@@ -0,0 +1,751 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Aaru.CommonTypes;
|
||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
|
using Aaru.CommonTypes.Interfaces;
|
||||||
|
using Aaru.CommonTypes.Structs;
|
||||||
|
using Aaru.Core.Media;
|
||||||
|
using Aaru.Decryption.DVD;
|
||||||
|
using Aaru.Devices;
|
||||||
|
using Aaru.Localization;
|
||||||
|
using Aaru.Logging;
|
||||||
|
|
||||||
|
namespace Aaru.Core.Image;
|
||||||
|
|
||||||
|
public partial class Convert
|
||||||
|
{
|
||||||
|
ErrorNumber ConvertOptical(IOpticalMediaImage inputOptical, IWritableOpticalImage outputOptical, bool useLong)
|
||||||
|
{
|
||||||
|
if(!outputOptical.SetTracks(inputOptical.Tracks))
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_sending_tracks_list_to_output_image,
|
||||||
|
outputOptical.ErrorMessage));
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_decrypt) UpdateStatus?.Invoke("Decrypting encrypted sectors.");
|
||||||
|
|
||||||
|
// Convert all sectors track by track
|
||||||
|
ErrorNumber errno = ConvertOpticalSectors(inputOptical, outputOptical, useLong);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
|
||||||
|
Dictionary<byte, string> isrcs = new();
|
||||||
|
Dictionary<byte, byte> trackFlags = new();
|
||||||
|
string mcn = null;
|
||||||
|
HashSet<int> subchannelExtents = [];
|
||||||
|
Dictionary<byte, int> smallestPregapLbaPerTrack = new();
|
||||||
|
var tracks = new Track[inputOptical.Tracks.Count];
|
||||||
|
|
||||||
|
for(var i = 0; i < tracks.Length; i++)
|
||||||
|
{
|
||||||
|
tracks[i] = new Track
|
||||||
|
{
|
||||||
|
Indexes = new Dictionary<ushort, int>(),
|
||||||
|
Description = inputOptical.Tracks[i].Description,
|
||||||
|
EndSector = inputOptical.Tracks[i].EndSector,
|
||||||
|
StartSector = inputOptical.Tracks[i].StartSector,
|
||||||
|
Pregap = inputOptical.Tracks[i].Pregap,
|
||||||
|
Sequence = inputOptical.Tracks[i].Sequence,
|
||||||
|
Session = inputOptical.Tracks[i].Session,
|
||||||
|
BytesPerSector = inputOptical.Tracks[i].BytesPerSector,
|
||||||
|
RawBytesPerSector = inputOptical.Tracks[i].RawBytesPerSector,
|
||||||
|
Type = inputOptical.Tracks[i].Type,
|
||||||
|
SubchannelType = inputOptical.Tracks[i].SubchannelType
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach(KeyValuePair<ushort, int> idx in inputOptical.Tracks[i].Indexes)
|
||||||
|
tracks[i].Indexes[idx.Key] = idx.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets tracks ISRCs
|
||||||
|
foreach(SectorTagType tag in inputOptical.Info.ReadableSectorTags
|
||||||
|
.Where(static t => t == SectorTagType.CdTrackIsrc)
|
||||||
|
.Order())
|
||||||
|
{
|
||||||
|
foreach(Track track in tracks)
|
||||||
|
{
|
||||||
|
errno = inputOptical.ReadSectorTag(track.Sequence, false, tag, out byte[] isrc);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) continue;
|
||||||
|
|
||||||
|
isrcs[(byte)track.Sequence] = Encoding.UTF8.GetString(isrc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets tracks flags
|
||||||
|
foreach(SectorTagType tag in inputOptical.Info.ReadableSectorTags
|
||||||
|
.Where(static t => t == SectorTagType.CdTrackFlags)
|
||||||
|
.Order())
|
||||||
|
{
|
||||||
|
foreach(Track track in tracks)
|
||||||
|
{
|
||||||
|
errno = inputOptical.ReadSectorTag(track.Sequence, false, tag, out byte[] flags);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) continue;
|
||||||
|
|
||||||
|
trackFlags[(byte)track.Sequence] = flags[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets subchannel extents
|
||||||
|
for(ulong s = 0; s < inputOptical.Info.Sectors; s++)
|
||||||
|
{
|
||||||
|
if(s > int.MaxValue) break;
|
||||||
|
|
||||||
|
subchannelExtents.Add((int)s);
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = ConvertOpticalSectorsTags(inputOptical,
|
||||||
|
outputOptical,
|
||||||
|
useLong,
|
||||||
|
isrcs,
|
||||||
|
ref mcn,
|
||||||
|
tracks,
|
||||||
|
subchannelExtents,
|
||||||
|
smallestPregapLbaPerTrack);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError) return errno;
|
||||||
|
|
||||||
|
// Write ISRCs
|
||||||
|
foreach(KeyValuePair<byte, string> isrc in isrcs)
|
||||||
|
{
|
||||||
|
outputOptical.WriteSectorTag(Encoding.UTF8.GetBytes(isrc.Value),
|
||||||
|
isrc.Key,
|
||||||
|
false,
|
||||||
|
SectorTagType.CdTrackIsrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write track flags
|
||||||
|
if(trackFlags.Count > 0)
|
||||||
|
{
|
||||||
|
foreach((byte track, byte flags) in trackFlags)
|
||||||
|
outputOptical.WriteSectorTag([flags], track, false, SectorTagType.CdTrackFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write MCN
|
||||||
|
if(mcn != null) outputOptical.WriteMediaTag(Encoding.UTF8.GetBytes(mcn), MediaTagType.CD_MCN);
|
||||||
|
|
||||||
|
if(!IsCompactDiscMedia(inputOptical.Info.MediaType) || !_generateSubchannels) return ErrorNumber.NoError;
|
||||||
|
|
||||||
|
// Generate subchannel data
|
||||||
|
CompactDisc.GenerateSubchannels(subchannelExtents,
|
||||||
|
tracks,
|
||||||
|
trackFlags,
|
||||||
|
inputOptical.Info.Sectors,
|
||||||
|
null,
|
||||||
|
InitProgress,
|
||||||
|
UpdateProgress,
|
||||||
|
EndProgress,
|
||||||
|
outputOptical);
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorNumber ConvertOpticalSectors(IOpticalMediaImage inputOptical, IWritableOpticalImage outputOptical,
|
||||||
|
bool useLong)
|
||||||
|
{
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
InitProgress2?.Invoke();
|
||||||
|
byte[] generatedTitleKeys = null;
|
||||||
|
var currentTrack = 0;
|
||||||
|
|
||||||
|
foreach(Track track in inputOptical.Tracks)
|
||||||
|
{
|
||||||
|
UpdateProgress?.Invoke(string.Format(UI.Converting_sectors_in_track_0_of_1,
|
||||||
|
currentTrack + 1,
|
||||||
|
inputOptical.Tracks.Count),
|
||||||
|
currentTrack,
|
||||||
|
inputOptical.Tracks.Count);
|
||||||
|
|
||||||
|
ulong doneSectors = 0;
|
||||||
|
ulong trackSectors = track.EndSector - track.StartSector + 1;
|
||||||
|
|
||||||
|
while(doneSectors < trackSectors)
|
||||||
|
{
|
||||||
|
byte[] sector;
|
||||||
|
|
||||||
|
uint sectorsToDo;
|
||||||
|
|
||||||
|
if(trackSectors - doneSectors >= _count)
|
||||||
|
sectorsToDo = _count;
|
||||||
|
else
|
||||||
|
sectorsToDo = (uint)(trackSectors - doneSectors);
|
||||||
|
|
||||||
|
UpdateProgress2?.Invoke(string.Format(UI.Converting_sectors_0_to_1_in_track_2,
|
||||||
|
doneSectors + track.StartSector,
|
||||||
|
doneSectors + sectorsToDo + track.StartSector,
|
||||||
|
track.Sequence),
|
||||||
|
(long)doneSectors,
|
||||||
|
(long)trackSectors);
|
||||||
|
|
||||||
|
var useNotLong = false;
|
||||||
|
var result = false;
|
||||||
|
SectorStatus sectorStatus = SectorStatus.NotDumped;
|
||||||
|
var sectorStatusArray = new SectorStatus[1];
|
||||||
|
ErrorNumber errno;
|
||||||
|
|
||||||
|
if(useLong)
|
||||||
|
{
|
||||||
|
errno = sectorsToDo == 1
|
||||||
|
? inputOptical.ReadSectorLong(doneSectors + track.StartSector,
|
||||||
|
false,
|
||||||
|
out sector,
|
||||||
|
out sectorStatus)
|
||||||
|
: inputOptical.ReadSectorsLong(doneSectors + track.StartSector,
|
||||||
|
false,
|
||||||
|
sectorsToDo,
|
||||||
|
out sector,
|
||||||
|
out sectorStatusArray);
|
||||||
|
|
||||||
|
if(errno == ErrorNumber.NoError)
|
||||||
|
{
|
||||||
|
result = sectorsToDo == 1
|
||||||
|
? outputOptical.WriteSectorLong(sector,
|
||||||
|
doneSectors + track.StartSector,
|
||||||
|
false,
|
||||||
|
sectorStatus)
|
||||||
|
: outputOptical.WriteSectorsLong(sector,
|
||||||
|
doneSectors + track.StartSector,
|
||||||
|
false,
|
||||||
|
sectorsToDo,
|
||||||
|
sectorStatusArray);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_reading_sector_1_continuing,
|
||||||
|
errno,
|
||||||
|
doneSectors + track.StartSector));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_reading_sector_1_not_continuing,
|
||||||
|
errno,
|
||||||
|
doneSectors + track.StartSector));
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!result && sector.Length % 2352 != 0)
|
||||||
|
{
|
||||||
|
if(!_force)
|
||||||
|
{
|
||||||
|
StoppingErrorMessage
|
||||||
|
?.Invoke(UI.Input_image_is_not_returning_raw_sectors_use_force_if_you_want_to_continue);
|
||||||
|
|
||||||
|
return ErrorNumber.InOutError;
|
||||||
|
}
|
||||||
|
|
||||||
|
useNotLong = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!useLong || useNotLong)
|
||||||
|
{
|
||||||
|
errno = sectorsToDo == 1
|
||||||
|
? inputOptical.ReadSector(doneSectors + track.StartSector,
|
||||||
|
false,
|
||||||
|
out sector,
|
||||||
|
out sectorStatus)
|
||||||
|
: inputOptical.ReadSectors(doneSectors + track.StartSector,
|
||||||
|
false,
|
||||||
|
sectorsToDo,
|
||||||
|
out sector,
|
||||||
|
out sectorStatusArray);
|
||||||
|
|
||||||
|
// TODO: Move to generic place when anything but CSS DVDs can be decrypted
|
||||||
|
if(IsDvdMedia(inputOptical.Info.MediaType) && _decrypt)
|
||||||
|
{
|
||||||
|
DecryptDvdSector(ref sector,
|
||||||
|
inputOptical,
|
||||||
|
doneSectors + track.StartSector,
|
||||||
|
sectorsToDo,
|
||||||
|
_plugins,
|
||||||
|
ref generatedTitleKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(errno == ErrorNumber.NoError)
|
||||||
|
{
|
||||||
|
result = sectorsToDo == 1
|
||||||
|
? outputOptical.WriteSector(sector,
|
||||||
|
doneSectors + track.StartSector,
|
||||||
|
false,
|
||||||
|
sectorStatus)
|
||||||
|
: outputOptical.WriteSectors(sector,
|
||||||
|
doneSectors + track.StartSector,
|
||||||
|
false,
|
||||||
|
sectorsToDo,
|
||||||
|
sectorStatusArray);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_reading_sector_1_continuing,
|
||||||
|
errno,
|
||||||
|
doneSectors + track.StartSector));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_reading_sector_1_not_continuing,
|
||||||
|
errno,
|
||||||
|
doneSectors + track.StartSector));
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!result)
|
||||||
|
{
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_writing_sector_1_continuing,
|
||||||
|
outputOptical.ErrorMessage,
|
||||||
|
doneSectors + track.StartSector));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_writing_sector_1_not_continuing,
|
||||||
|
outputOptical.ErrorMessage,
|
||||||
|
doneSectors + track.StartSector));
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doneSectors += sectorsToDo;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTrack++;
|
||||||
|
}
|
||||||
|
|
||||||
|
EndProgress2?.Invoke();
|
||||||
|
EndProgress?.Invoke();
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorNumber ConvertOpticalSectorsTags(IOpticalMediaImage inputOptical, IWritableOpticalImage outputOptical,
|
||||||
|
bool useLong, Dictionary<byte, string> isrcs, ref string mcn, Track[] tracks,
|
||||||
|
HashSet<int> subchannelExtents,
|
||||||
|
Dictionary<byte, int> smallestPregapLbaPerTrack)
|
||||||
|
{
|
||||||
|
foreach(SectorTagType tag in inputOptical.Info.ReadableSectorTags.Order().TakeWhile(_ => useLong))
|
||||||
|
{
|
||||||
|
switch(tag)
|
||||||
|
{
|
||||||
|
case SectorTagType.AppleSonyTag:
|
||||||
|
case SectorTagType.AppleProfileTag:
|
||||||
|
case SectorTagType.PriamDataTowerTag:
|
||||||
|
case SectorTagType.CdSectorSync:
|
||||||
|
case SectorTagType.CdSectorHeader:
|
||||||
|
case SectorTagType.CdSectorSubHeader:
|
||||||
|
case SectorTagType.CdSectorEdc:
|
||||||
|
case SectorTagType.CdSectorEccP:
|
||||||
|
case SectorTagType.CdSectorEccQ:
|
||||||
|
case SectorTagType.CdSectorEcc:
|
||||||
|
case SectorTagType.DvdSectorCmi:
|
||||||
|
case SectorTagType.DvdSectorTitleKey:
|
||||||
|
case SectorTagType.DvdSectorEdc:
|
||||||
|
case SectorTagType.DvdSectorIed:
|
||||||
|
case SectorTagType.DvdSectorInformation:
|
||||||
|
case SectorTagType.DvdSectorNumber:
|
||||||
|
// This tags are inline in long sector
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_force && !outputOptical.SupportedSectorTags.Contains(tag)) continue;
|
||||||
|
|
||||||
|
ErrorNumber errno = ErrorNumber.NoError;
|
||||||
|
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
InitProgress2?.Invoke();
|
||||||
|
var currentTrack = 0;
|
||||||
|
|
||||||
|
foreach(Track track in inputOptical.Tracks)
|
||||||
|
{
|
||||||
|
UpdateProgress?.Invoke(string.Format(UI.Converting_tags_in_track_0_of_1,
|
||||||
|
currentTrack + 1,
|
||||||
|
inputOptical.Tracks.Count),
|
||||||
|
currentTrack,
|
||||||
|
inputOptical.Tracks.Count);
|
||||||
|
|
||||||
|
ulong doneSectors = 0;
|
||||||
|
ulong trackSectors = track.EndSector - track.StartSector + 1;
|
||||||
|
byte[] sector;
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
switch(tag)
|
||||||
|
{
|
||||||
|
case SectorTagType.CdTrackFlags:
|
||||||
|
case SectorTagType.CdTrackIsrc:
|
||||||
|
errno = inputOptical.ReadSectorTag(track.Sequence, false, tag, out sector);
|
||||||
|
|
||||||
|
switch(errno)
|
||||||
|
{
|
||||||
|
case ErrorNumber.NoData:
|
||||||
|
errno = ErrorNumber.NoError;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
case ErrorNumber.NoError:
|
||||||
|
result = outputOptical.WriteSectorTag(sector, track.Sequence, false, tag);
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_writing_tag_continuing,
|
||||||
|
outputOptical.ErrorMessage));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_writing_tag_not_continuing,
|
||||||
|
outputOptical.ErrorMessage));
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!result)
|
||||||
|
{
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_writing_tag_continuing,
|
||||||
|
outputOptical.ErrorMessage));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_writing_tag_not_continuing,
|
||||||
|
outputOptical.ErrorMessage));
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(doneSectors < trackSectors)
|
||||||
|
{
|
||||||
|
uint sectorsToDo;
|
||||||
|
|
||||||
|
if(trackSectors - doneSectors >= _count)
|
||||||
|
sectorsToDo = _count;
|
||||||
|
else
|
||||||
|
sectorsToDo = (uint)(trackSectors - doneSectors);
|
||||||
|
|
||||||
|
UpdateProgress2?.Invoke(string.Format(UI.Converting_tag_3_for_sectors_0_to_1_in_track_2,
|
||||||
|
doneSectors + track.StartSector,
|
||||||
|
doneSectors + sectorsToDo + track.StartSector,
|
||||||
|
track.Sequence,
|
||||||
|
tag),
|
||||||
|
(long)(doneSectors + track.StartSector),
|
||||||
|
(long)(doneSectors + sectorsToDo + track.StartSector));
|
||||||
|
|
||||||
|
if(sectorsToDo == 1)
|
||||||
|
{
|
||||||
|
errno = inputOptical.ReadSectorTag(doneSectors + track.StartSector, false, tag, out sector);
|
||||||
|
|
||||||
|
if(errno == ErrorNumber.NoError)
|
||||||
|
{
|
||||||
|
if(tag == SectorTagType.CdSectorSubchannel)
|
||||||
|
{
|
||||||
|
bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw,
|
||||||
|
MmcSubchannel.Raw,
|
||||||
|
sector,
|
||||||
|
doneSectors + track.StartSector,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
isrcs,
|
||||||
|
(byte)track.Sequence,
|
||||||
|
ref mcn,
|
||||||
|
tracks,
|
||||||
|
subchannelExtents,
|
||||||
|
_fixSubchannelPosition,
|
||||||
|
outputOptical,
|
||||||
|
_fixSubchannel,
|
||||||
|
_fixSubchannelCrc,
|
||||||
|
null,
|
||||||
|
smallestPregapLbaPerTrack,
|
||||||
|
false,
|
||||||
|
out _);
|
||||||
|
|
||||||
|
if(indexesChanged) outputOptical.SetTracks(tracks.ToList());
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = outputOptical.WriteSectorTag(sector,
|
||||||
|
doneSectors + track.StartSector,
|
||||||
|
false,
|
||||||
|
tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_reading_tag_for_sector_1_continuing,
|
||||||
|
errno,
|
||||||
|
doneSectors + track.StartSector));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage
|
||||||
|
?.Invoke(string.Format(UI.Error_0_reading_tag_for_sector_1_not_continuing,
|
||||||
|
errno,
|
||||||
|
doneSectors + track.StartSector));
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errno = inputOptical.ReadSectorsTag(doneSectors + track.StartSector,
|
||||||
|
false,
|
||||||
|
sectorsToDo,
|
||||||
|
tag,
|
||||||
|
out sector);
|
||||||
|
|
||||||
|
if(errno == ErrorNumber.NoError)
|
||||||
|
{
|
||||||
|
if(tag == SectorTagType.CdSectorSubchannel)
|
||||||
|
{
|
||||||
|
bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw,
|
||||||
|
MmcSubchannel.Raw,
|
||||||
|
sector,
|
||||||
|
doneSectors + track.StartSector,
|
||||||
|
sectorsToDo,
|
||||||
|
null,
|
||||||
|
isrcs,
|
||||||
|
(byte)track.Sequence,
|
||||||
|
ref mcn,
|
||||||
|
tracks,
|
||||||
|
subchannelExtents,
|
||||||
|
_fixSubchannelPosition,
|
||||||
|
outputOptical,
|
||||||
|
_fixSubchannel,
|
||||||
|
_fixSubchannelCrc,
|
||||||
|
null,
|
||||||
|
smallestPregapLbaPerTrack,
|
||||||
|
false,
|
||||||
|
out _);
|
||||||
|
|
||||||
|
if(indexesChanged) outputOptical.SetTracks(tracks.ToList());
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = outputOptical.WriteSectorsTag(sector,
|
||||||
|
doneSectors + track.StartSector,
|
||||||
|
false,
|
||||||
|
sectorsToDo,
|
||||||
|
tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_reading_tag_for_sector_1_continuing,
|
||||||
|
errno,
|
||||||
|
doneSectors + track.StartSector));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage
|
||||||
|
?.Invoke(string.Format(UI.Error_0_reading_tag_for_sector_1_not_continuing,
|
||||||
|
errno,
|
||||||
|
doneSectors + track.StartSector));
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!result)
|
||||||
|
{
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_writing_tag_for_sector_1_continuing,
|
||||||
|
outputOptical.ErrorMessage,
|
||||||
|
doneSectors + track.StartSector));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage
|
||||||
|
?.Invoke(string.Format(UI.Error_0_writing_tag_for_sector_1_not_continuing,
|
||||||
|
outputOptical.ErrorMessage,
|
||||||
|
doneSectors + track.StartSector));
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doneSectors += sectorsToDo;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTrack++;
|
||||||
|
}
|
||||||
|
|
||||||
|
EndProgress?.Invoke();
|
||||||
|
EndProgress2?.Invoke();
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError && !_force) return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Only sectors which are MPEG packets can be encrypted.
|
||||||
|
if(!Mpeg.ContainsMpegPackets(sector, sectorsToDo)) return;
|
||||||
|
|
||||||
|
byte[] cmi, titleKey;
|
||||||
|
|
||||||
|
if(sectorsToDo == 1)
|
||||||
|
{
|
||||||
|
if(inputOptical.ReadSectorTag(sectorAddress, false, SectorTagType.DvdSectorCmi, out cmi) ==
|
||||||
|
ErrorNumber.NoError &&
|
||||||
|
inputOptical.ReadSectorTag(sectorAddress, false, SectorTagType.DvdTitleKeyDecrypted, out titleKey) ==
|
||||||
|
ErrorNumber.NoError)
|
||||||
|
sector = CSS.DecryptSector(sector, titleKey, cmi);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(generatedTitleKeys == null) GenerateDvdTitleKeys(inputOptical, plugins, ref generatedTitleKeys);
|
||||||
|
|
||||||
|
if(generatedTitleKeys != null)
|
||||||
|
{
|
||||||
|
sector = CSS.DecryptSector(sector,
|
||||||
|
generatedTitleKeys.Skip((int)(5 * sectorAddress)).Take(5).ToArray(),
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(inputOptical.ReadSectorsTag(sectorAddress, false, sectorsToDo, SectorTagType.DvdSectorCmi, out cmi) ==
|
||||||
|
ErrorNumber.NoError &&
|
||||||
|
inputOptical.ReadSectorsTag(sectorAddress,
|
||||||
|
false,
|
||||||
|
sectorsToDo,
|
||||||
|
SectorTagType.DvdTitleKeyDecrypted,
|
||||||
|
out titleKey) ==
|
||||||
|
ErrorNumber.NoError)
|
||||||
|
sector = CSS.DecryptSector(sector, titleKey, cmi, sectorsToDo);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(generatedTitleKeys == null) GenerateDvdTitleKeys(inputOptical, plugins, ref generatedTitleKeys);
|
||||||
|
|
||||||
|
if(generatedTitleKeys != null)
|
||||||
|
{
|
||||||
|
sector = CSS.DecryptSector(sector,
|
||||||
|
generatedTitleKeys.Skip((int)(5 * sectorAddress))
|
||||||
|
.Take((int)(5 * sectorsToDo))
|
||||||
|
.ToArray(),
|
||||||
|
null,
|
||||||
|
sectorsToDo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates DVD CSS title keys from ISO9660 filesystem
|
||||||
|
/// Used when explicit title keys are not available in sector tags
|
||||||
|
/// Searches for ISO9660 partitions to derive decryption keys
|
||||||
|
/// </summary>
|
||||||
|
void GenerateDvdTitleKeys(IOpticalMediaImage inputOptical, PluginRegister plugins, ref byte[] generatedTitleKeys)
|
||||||
|
{
|
||||||
|
List<Partition> partitions = Partitions.GetAll(inputOptical);
|
||||||
|
|
||||||
|
partitions = partitions.FindAll(p =>
|
||||||
|
{
|
||||||
|
Filesystems.Identify(inputOptical, out List<string> idPlugins, p);
|
||||||
|
|
||||||
|
return idPlugins.Contains("iso9660 filesystem");
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!plugins.ReadOnlyFilesystems.TryGetValue("iso9660 filesystem", out IReadOnlyFilesystem rofs)) return;
|
||||||
|
|
||||||
|
AaruLogging.Debug(MODULE_NAME, UI.Generating_decryption_keys);
|
||||||
|
|
||||||
|
generatedTitleKeys = CSS.GenerateTitleKeys(inputOptical, partitions, inputOptical.Info.Sectors, rofs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDvdMedia(MediaType mediaType) =>
|
||||||
|
|
||||||
|
// Checks if media type is any variant of DVD (ROM, R, RDL, PR, PRDL)
|
||||||
|
// Consolidates media type checking logic used throughout conversion process
|
||||||
|
mediaType is MediaType.DVDROM or MediaType.DVDR or MediaType.DVDRDL or MediaType.DVDPR or MediaType.DVDPRDL;
|
||||||
|
|
||||||
|
private bool IsCompactDiscMedia(MediaType mediaType) =>
|
||||||
|
|
||||||
|
// Checks if media type is any variant of compact disc (CD, CDDA, CDR, CDRW, etc.)
|
||||||
|
// Covers all 45+ CD-based media types including gaming and specialty formats
|
||||||
|
mediaType is MediaType.CD
|
||||||
|
or MediaType.CDDA
|
||||||
|
or MediaType.CDG
|
||||||
|
or MediaType.CDEG
|
||||||
|
or MediaType.CDI
|
||||||
|
or MediaType.CDROM
|
||||||
|
or MediaType.CDROMXA
|
||||||
|
or MediaType.CDPLUS
|
||||||
|
or MediaType.CDMO
|
||||||
|
or MediaType.CDR
|
||||||
|
or MediaType.CDRW
|
||||||
|
or MediaType.CDMRW
|
||||||
|
or MediaType.VCD
|
||||||
|
or MediaType.SVCD
|
||||||
|
or MediaType.PCD
|
||||||
|
or MediaType.DTSCD
|
||||||
|
or MediaType.CDMIDI
|
||||||
|
or MediaType.CDV
|
||||||
|
or MediaType.CDIREADY
|
||||||
|
or MediaType.FMTOWNS
|
||||||
|
or MediaType.PS1CD
|
||||||
|
or MediaType.PS2CD
|
||||||
|
or MediaType.MEGACD
|
||||||
|
or MediaType.SATURNCD
|
||||||
|
or MediaType.GDROM
|
||||||
|
or MediaType.GDR
|
||||||
|
or MediaType.MilCD
|
||||||
|
or MediaType.SuperCDROM2
|
||||||
|
or MediaType.JaguarCD
|
||||||
|
or MediaType.ThreeDO
|
||||||
|
or MediaType.PCFX
|
||||||
|
or MediaType.NeoGeoCD
|
||||||
|
or MediaType.CDTV
|
||||||
|
or MediaType.CD32
|
||||||
|
or MediaType.Playdia
|
||||||
|
or MediaType.Pippin
|
||||||
|
or MediaType.VideoNow
|
||||||
|
or MediaType.VideoNowColor
|
||||||
|
or MediaType.VideoNowXp
|
||||||
|
or MediaType.CVD;
|
||||||
|
}
|
||||||
229
Aaru.Core/Image/Convert/Sectors.cs
Normal file
229
Aaru.Core/Image/Convert/Sectors.cs
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
|
using Aaru.Localization;
|
||||||
|
using Aaru.Logging;
|
||||||
|
|
||||||
|
namespace Aaru.Core.Image;
|
||||||
|
|
||||||
|
public partial class Convert
|
||||||
|
{
|
||||||
|
ErrorNumber ConvertSectors(bool useLong, bool isTape)
|
||||||
|
{
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
ErrorNumber errno = ErrorNumber.NoError;
|
||||||
|
ulong doneSectors = 0;
|
||||||
|
|
||||||
|
while(doneSectors < _inputImage.Info.Sectors)
|
||||||
|
{
|
||||||
|
byte[] sector;
|
||||||
|
|
||||||
|
uint sectorsToDo;
|
||||||
|
|
||||||
|
if(isTape)
|
||||||
|
sectorsToDo = 1;
|
||||||
|
else if(_inputImage.Info.Sectors - doneSectors >= _count)
|
||||||
|
sectorsToDo = _count;
|
||||||
|
else
|
||||||
|
sectorsToDo = (uint)(_inputImage.Info.Sectors - doneSectors);
|
||||||
|
|
||||||
|
UpdateProgress?.Invoke(string.Format(UI.Converting_sectors_0_to_1, doneSectors, doneSectors + sectorsToDo),
|
||||||
|
(long)doneSectors,
|
||||||
|
(long)_inputImage.Info.Sectors);
|
||||||
|
|
||||||
|
bool result;
|
||||||
|
SectorStatus sectorStatus = SectorStatus.NotDumped;
|
||||||
|
var sectorStatusArray = new SectorStatus[1];
|
||||||
|
|
||||||
|
if(useLong)
|
||||||
|
{
|
||||||
|
errno = sectorsToDo == 1
|
||||||
|
? _inputImage.ReadSectorLong(doneSectors, false, out sector, out sectorStatus)
|
||||||
|
: _inputImage.ReadSectorsLong(doneSectors,
|
||||||
|
false,
|
||||||
|
sectorsToDo,
|
||||||
|
out sector,
|
||||||
|
out sectorStatusArray);
|
||||||
|
|
||||||
|
if(errno == ErrorNumber.NoError)
|
||||||
|
{
|
||||||
|
result = sectorsToDo == 1
|
||||||
|
? _outputImage.WriteSectorLong(sector, doneSectors, false, sectorStatus)
|
||||||
|
: _outputImage.WriteSectorsLong(sector,
|
||||||
|
doneSectors,
|
||||||
|
false,
|
||||||
|
sectorsToDo,
|
||||||
|
sectorStatusArray);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_reading_sector_1_continuing, errno, doneSectors));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_reading_sector_1_not_continuing,
|
||||||
|
errno,
|
||||||
|
doneSectors));
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errno = sectorsToDo == 1
|
||||||
|
? _inputImage.ReadSector(doneSectors, false, out sector, out sectorStatus)
|
||||||
|
: _inputImage.ReadSectors(doneSectors,
|
||||||
|
false,
|
||||||
|
sectorsToDo,
|
||||||
|
out sector,
|
||||||
|
out sectorStatusArray);
|
||||||
|
|
||||||
|
if(errno == ErrorNumber.NoError)
|
||||||
|
{
|
||||||
|
result = sectorsToDo == 1
|
||||||
|
? _outputImage.WriteSector(sector, doneSectors, false, sectorStatus)
|
||||||
|
: _outputImage.WriteSectors(sector,
|
||||||
|
doneSectors,
|
||||||
|
false,
|
||||||
|
sectorsToDo,
|
||||||
|
sectorStatusArray);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_reading_sector_1_continuing, errno, doneSectors));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_reading_sector_1_not_continuing,
|
||||||
|
errno,
|
||||||
|
doneSectors));
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!result)
|
||||||
|
{
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_writing_sector_1_continuing,
|
||||||
|
_outputImage.ErrorMessage,
|
||||||
|
doneSectors));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_writing_sector_1_not_continuing,
|
||||||
|
_outputImage.ErrorMessage,
|
||||||
|
doneSectors));
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doneSectors += sectorsToDo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorNumber ConvertSectorsTags(bool useLong)
|
||||||
|
{
|
||||||
|
foreach(SectorTagType tag in _inputImage.Info.ReadableSectorTags.TakeWhile(_ => useLong))
|
||||||
|
{
|
||||||
|
switch(tag)
|
||||||
|
{
|
||||||
|
case SectorTagType.AppleSonyTag:
|
||||||
|
case SectorTagType.AppleProfileTag:
|
||||||
|
case SectorTagType.PriamDataTowerTag:
|
||||||
|
case SectorTagType.CdSectorSync:
|
||||||
|
case SectorTagType.CdSectorHeader:
|
||||||
|
case SectorTagType.CdSectorSubHeader:
|
||||||
|
case SectorTagType.CdSectorEdc:
|
||||||
|
case SectorTagType.CdSectorEccP:
|
||||||
|
case SectorTagType.CdSectorEccQ:
|
||||||
|
case SectorTagType.CdSectorEcc:
|
||||||
|
// These tags are inline in long sector
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_force && !_outputImage.SupportedSectorTags.Contains(tag)) continue;
|
||||||
|
|
||||||
|
ulong doneSectors = 0;
|
||||||
|
ErrorNumber errno;
|
||||||
|
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
|
||||||
|
while(doneSectors < _inputImage.Info.Sectors)
|
||||||
|
{
|
||||||
|
uint sectorsToDo;
|
||||||
|
|
||||||
|
if(_inputImage.Info.Sectors - doneSectors >= _count)
|
||||||
|
sectorsToDo = _count;
|
||||||
|
else
|
||||||
|
sectorsToDo = (uint)(_inputImage.Info.Sectors - doneSectors);
|
||||||
|
|
||||||
|
UpdateProgress?.Invoke(string.Format(UI.Converting_tag_2_for_sectors_0_to_1,
|
||||||
|
doneSectors,
|
||||||
|
doneSectors + sectorsToDo,
|
||||||
|
tag),
|
||||||
|
(long)doneSectors,
|
||||||
|
(long)_inputImage.Info.Sectors);
|
||||||
|
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
errno = sectorsToDo == 1
|
||||||
|
? _inputImage.ReadSectorTag(doneSectors, false, tag, out byte[] sector)
|
||||||
|
: _inputImage.ReadSectorsTag(doneSectors, false, sectorsToDo, tag, out sector);
|
||||||
|
|
||||||
|
if(errno == ErrorNumber.NoError)
|
||||||
|
{
|
||||||
|
result = sectorsToDo == 1
|
||||||
|
? _outputImage.WriteSectorTag(sector, doneSectors, false, tag)
|
||||||
|
: _outputImage.WriteSectorsTag(sector, doneSectors, false, sectorsToDo, tag);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
AaruLogging.Error(UI.Error_0_reading_sector_1_continuing, errno, doneSectors);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AaruLogging.Error(UI.Error_0_reading_sector_1_not_continuing, errno, doneSectors);
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!result)
|
||||||
|
{
|
||||||
|
if(_force)
|
||||||
|
{
|
||||||
|
AaruLogging.Error(UI.Error_0_writing_sector_1_continuing,
|
||||||
|
_outputImage.ErrorMessage,
|
||||||
|
doneSectors);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AaruLogging.Error(UI.Error_0_writing_sector_1_not_continuing,
|
||||||
|
_outputImage.ErrorMessage,
|
||||||
|
doneSectors);
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doneSectors += sectorsToDo;
|
||||||
|
}
|
||||||
|
|
||||||
|
EndProgress?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorNumber.NoError;
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Aaru.Core/Image/Convert/Tags.cs
Normal file
63
Aaru.Core/Image/Convert/Tags.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
|
using Aaru.Localization;
|
||||||
|
using Aaru.Logging;
|
||||||
|
using Humanizer;
|
||||||
|
|
||||||
|
namespace Aaru.Core.Image;
|
||||||
|
|
||||||
|
public partial class Convert
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts media tags (TOC, lead-in, etc.) from input to output format
|
||||||
|
/// Handles force mode to skip unsupported tags or fail on data loss
|
||||||
|
/// Shows progress for each tag being converted
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Status code</returns>
|
||||||
|
ErrorNumber ConvertMediaTags()
|
||||||
|
{
|
||||||
|
InitProgress?.Invoke();
|
||||||
|
ErrorNumber errorNumber = ErrorNumber.NoError;
|
||||||
|
|
||||||
|
foreach(MediaTagType mediaTag in _inputImage.Info.ReadableMediaTags.Where(mediaTag => !_force ||
|
||||||
|
_outputImage.SupportedMediaTags.Contains(mediaTag)))
|
||||||
|
{
|
||||||
|
PulseProgress?.Invoke(string.Format(UI.Converting_media_tag_0, mediaTag.Humanize()));
|
||||||
|
ErrorNumber errno = _inputImage.ReadMediaTag(mediaTag, out byte[] tag);
|
||||||
|
|
||||||
|
if(errno != ErrorNumber.NoError)
|
||||||
|
{
|
||||||
|
if(_force)
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_reading_media_tag, errno));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AaruLogging.Error(UI.Error_0_reading_media_tag_not_continuing, errno);
|
||||||
|
|
||||||
|
errorNumber = errno;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_outputImage?.WriteMediaTag(tag, mediaTag) == true) continue;
|
||||||
|
|
||||||
|
if(_force)
|
||||||
|
ErrorMessage?.Invoke(string.Format(UI.Error_0_writing_media_tag, _outputImage?.ErrorMessage));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StoppingErrorMessage?.Invoke(string.Format(UI.Error_0_writing_media_tag_not_continuing,
|
||||||
|
_outputImage?.ErrorMessage));
|
||||||
|
|
||||||
|
errorNumber = ErrorNumber.WriteError;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EndProgress?.Invoke();
|
||||||
|
|
||||||
|
return errorNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Aaru.Core/Image/Convert/Tape.cs
Normal file
42
Aaru.Core/Image/Convert/Tape.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using Aaru.CommonTypes.Enums;
|
||||||
|
using Aaru.CommonTypes.Interfaces;
|
||||||
|
using Aaru.Localization;
|
||||||
|
|
||||||
|
namespace Aaru.Core.Image;
|
||||||
|
|
||||||
|
public partial class Convert
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
StoppingErrorMessage?.Invoke(UI.Input_format_contains_a_tape_image_and_is_not_supported_by_output_format);
|
||||||
|
|
||||||
|
return ErrorNumber.UnsupportedMedia;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
bool ret = outputTape.SetTape();
|
||||||
|
|
||||||
|
// Cannot set image to tape mode
|
||||||
|
if(ret) return ErrorNumber.NoError;
|
||||||
|
|
||||||
|
StoppingErrorMessage?.Invoke(UI.Error_setting_output_image_in_tape_mode +
|
||||||
|
Environment.NewLine +
|
||||||
|
_outputImage.ErrorMessage);
|
||||||
|
|
||||||
|
return ErrorNumber.WriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,7 +63,7 @@ public sealed partial class Sidecar
|
|||||||
new AudioMedia
|
new AudioMedia
|
||||||
{
|
{
|
||||||
Checksums = imgChecksums,
|
Checksums = imgChecksums,
|
||||||
Image = new Image
|
Image = new CommonTypes.AaruMetadata.Image
|
||||||
{
|
{
|
||||||
Format = image.Format,
|
Format = image.Format,
|
||||||
Offset = 0,
|
Offset = 0,
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ public sealed partial class Sidecar
|
|||||||
new BlockMedia
|
new BlockMedia
|
||||||
{
|
{
|
||||||
Checksums = imgChecksums,
|
Checksums = imgChecksums,
|
||||||
Image = new Image
|
Image = new CommonTypes.AaruMetadata.Image
|
||||||
{
|
{
|
||||||
Format = image.Format,
|
Format = image.Format,
|
||||||
Offset = 0,
|
Offset = 0,
|
||||||
@@ -1013,7 +1013,7 @@ public sealed partial class Sidecar
|
|||||||
{
|
{
|
||||||
Cylinder = t / image.Info.Heads,
|
Cylinder = t / image.Info.Heads,
|
||||||
Head = (ushort)(t % image.Info.Heads),
|
Head = (ushort)(t % image.Info.Heads),
|
||||||
Image = new Image
|
Image = new CommonTypes.AaruMetadata.Image
|
||||||
{
|
{
|
||||||
Format = scpImage.Format,
|
Format = scpImage.Format,
|
||||||
Value = Path.GetFileName(scpFilePath),
|
Value = Path.GetFileName(scpFilePath),
|
||||||
@@ -1126,7 +1126,7 @@ public sealed partial class Sidecar
|
|||||||
{
|
{
|
||||||
Cylinder = kvp.Key / image.Info.Heads,
|
Cylinder = kvp.Key / image.Info.Heads,
|
||||||
Head = (ushort)(kvp.Key % image.Info.Heads),
|
Head = (ushort)(kvp.Key % image.Info.Heads),
|
||||||
Image = new Image
|
Image = new CommonTypes.AaruMetadata.Image
|
||||||
{
|
{
|
||||||
Format = kfImage.Format,
|
Format = kfImage.Format,
|
||||||
Value = kfDir
|
Value = kfDir
|
||||||
@@ -1220,7 +1220,7 @@ public sealed partial class Sidecar
|
|||||||
{
|
{
|
||||||
Cylinder = (uint)(t / image.Info.Heads),
|
Cylinder = (uint)(t / image.Info.Heads),
|
||||||
Head = (ushort)(t % image.Info.Heads),
|
Head = (ushort)(t % image.Info.Heads),
|
||||||
Image = new Image
|
Image = new CommonTypes.AaruMetadata.Image
|
||||||
{
|
{
|
||||||
Format = dfiImage.Format,
|
Format = dfiImage.Format,
|
||||||
Value = Path.GetFileName(dfiFilePath)
|
Value = Path.GetFileName(dfiFilePath)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public sealed partial class Sidecar
|
|||||||
[
|
[
|
||||||
new BlockMedia
|
new BlockMedia
|
||||||
{
|
{
|
||||||
Image = new Image
|
Image = new CommonTypes.AaruMetadata.Image
|
||||||
{
|
{
|
||||||
Format = "Directory",
|
Format = "Directory",
|
||||||
Value = folderName
|
Value = folderName
|
||||||
@@ -69,7 +69,7 @@ public sealed partial class Sidecar
|
|||||||
[
|
[
|
||||||
new TapePartition
|
new TapePartition
|
||||||
{
|
{
|
||||||
Image = new Image
|
Image = new CommonTypes.AaruMetadata.Image
|
||||||
{
|
{
|
||||||
Format = "Directory",
|
Format = "Directory",
|
||||||
Value = folderName
|
Value = folderName
|
||||||
@@ -98,7 +98,7 @@ public sealed partial class Sidecar
|
|||||||
|
|
||||||
var tapeFile = new TapeFile
|
var tapeFile = new TapeFile
|
||||||
{
|
{
|
||||||
Image = new Image
|
Image = new CommonTypes.AaruMetadata.Image
|
||||||
{
|
{
|
||||||
Format = "Raw disk image (sector by sector copy)",
|
Format = "Raw disk image (sector by sector copy)",
|
||||||
Offset = 0,
|
Offset = 0,
|
||||||
|
|||||||
@@ -61,12 +61,14 @@ public sealed partial class Sidecar
|
|||||||
new LinearMedia
|
new LinearMedia
|
||||||
{
|
{
|
||||||
Checksums = imgChecksums,
|
Checksums = imgChecksums,
|
||||||
Image = new Image
|
Image =
|
||||||
{
|
new CommonTypes.AaruMetadata.Image
|
||||||
Format = image.Format,
|
{
|
||||||
Offset = 0,
|
Format = image.Format,
|
||||||
Value = Path.GetFileName(imagePath)
|
Offset = 0,
|
||||||
},
|
Value =
|
||||||
|
Path.GetFileName(imagePath)
|
||||||
|
},
|
||||||
Size = image.Info.Sectors
|
Size = image.Info.Sectors
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public sealed partial class Sidecar
|
|||||||
new OpticalDisc
|
new OpticalDisc
|
||||||
{
|
{
|
||||||
Checksums = imgChecksums,
|
Checksums = imgChecksums,
|
||||||
Image = new Image
|
Image = new CommonTypes.AaruMetadata.Image
|
||||||
{
|
{
|
||||||
Format = image.Format,
|
Format = image.Format,
|
||||||
Offset = 0,
|
Offset = 0,
|
||||||
@@ -308,7 +308,7 @@ public sealed partial class Sidecar
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
xmlTrk.Image = new Image
|
xmlTrk.Image = new CommonTypes.AaruMetadata.Image
|
||||||
{
|
{
|
||||||
Value = Path.GetFileName(trk.File),
|
Value = Path.GetFileName(trk.File),
|
||||||
Format = trk.FileType
|
Format = trk.FileType
|
||||||
@@ -413,7 +413,7 @@ public sealed partial class Sidecar
|
|||||||
{
|
{
|
||||||
xmlTrk.SubChannel = new SubChannel
|
xmlTrk.SubChannel = new SubChannel
|
||||||
{
|
{
|
||||||
Image = new Image
|
Image = new CommonTypes.AaruMetadata.Image
|
||||||
{
|
{
|
||||||
Value = trk.SubchannelFile
|
Value = trk.SubchannelFile
|
||||||
},
|
},
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user