Files
Aaru/Aaru.Tests/Issues/OpticalImageConvertIssueTest.cs

410 lines
17 KiB
C#
Raw Normal View History

2021-03-07 18:23:36 +00:00
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Aaru.Checksums;
using Aaru.CommonTypes;
using Aaru.CommonTypes.AaruMetadata;
2021-03-07 18:23:36 +00:00
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Metadata;
using Aaru.Console;
using Aaru.Core;
using Aaru.Core.Media;
using Aaru.Devices;
using NUnit.Framework;
using File = System.IO.File;
2021-03-07 18:23:36 +00:00
using ImageInfo = Aaru.CommonTypes.Structs.ImageInfo;
using Track = Aaru.CommonTypes.Structs.Track;
2021-03-07 18:23:36 +00:00
using Version = Aaru.CommonTypes.Interop.Version;
namespace Aaru.Tests.Issues;
2022-03-06 13:29:38 +00:00
/// <summary>
/// This tests the conversion of an input image (autodetected) to an output image and checks that the resulting
/// image has the same hash as it should
/// </summary>
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
// TODO: The algorithm should be in Core, not copied in 3 places
public abstract class OpticalImageConvertIssueTest
{
public const int SECTORS_TO_READ = 256;
public abstract Dictionary<string, string> ParsedOptions { get; }
public abstract string DataFolder { get; }
public abstract string InputPath { get; }
public abstract string SuggestedOutputFilename { get; }
public abstract IWritableImage OutputFormat { get; }
public abstract string Md5 { get; }
public abstract bool UseLong { get; }
[Test]
public void Convert()
{
Environment.CurrentDirectory = DataFolder;
2021-03-07 18:23:36 +00:00
Resume resume = null;
Metadata sidecar = null;
ErrorNumber errno;
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
var filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(InputPath);
2021-03-07 18:23:36 +00:00
Assert.IsNotNull(inputFilter, Localization.Cannot_open_specified_file);
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
string outputPath = Path.Combine(Path.GetTempPath(), SuggestedOutputFilename);
2021-03-07 18:23:36 +00:00
Assert.IsFalse(File.Exists(outputPath), Localization.Output_file_already_exists_not_continuing);
2021-03-07 18:23:36 +00:00
2022-03-07 07:36:44 +00:00
var inputFormat = ImageFormat.Detect(inputFilter) as IMediaImage;
2021-03-07 18:23:36 +00:00
Assert.IsNotNull(inputFormat, Localization.Input_image_format_not_identified_not_proceeding_with_conversion);
2021-03-07 18:23:36 +00:00
Assert.AreEqual(ErrorNumber.NoError, inputFormat.Open(inputFilter), Localization.Unable_to_open_image_format);
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
Assert.IsTrue(OutputFormat.SupportedMediaTypes.Contains(inputFormat.Info.MediaType),
Localization.Output_format_does_not_support_media_type_cannot_continue);
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
if(inputFormat.Info.ReadableSectorTags.Count == 0)
Assert.IsFalse(UseLong, Localization.Input_image_does_not_support_long_sectors);
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
var inputOptical = inputFormat as IOpticalMediaImage;
var outputOptical = OutputFormat as IWritableOpticalImage;
2021-03-07 18:23:36 +00:00
2023-10-03 23:44:33 +01:00
Assert.IsNotNull(inputOptical, Localization.Could_not_treat_existing_image_as_optical_disc);
Assert.IsNotNull(outputOptical, Localization.Could_not_treat_new_image_as_optical_disc);
Assert.IsNotNull(inputOptical.Tracks, Localization.Existing_image_contains_no_tracks);
2021-03-07 18:23:36 +00:00
Assert.IsTrue(outputOptical.Create(outputPath, inputFormat.Info.MediaType, ParsedOptions, inputFormat.Info.Sectors, inputFormat.Info.SectorSize),
string.Format(Localization.Error_0_creating_output_image, outputOptical.ErrorMessage));
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
var metadata = new ImageInfo
{
Application = "Aaru",
ApplicationVersion = Version.GetVersion(),
Comments = inputFormat.Info.Comments,
Creator = inputFormat.Info.Creator,
DriveFirmwareRevision = inputFormat.Info.DriveFirmwareRevision,
DriveManufacturer = inputFormat.Info.DriveManufacturer,
DriveModel = inputFormat.Info.DriveModel,
DriveSerialNumber = inputFormat.Info.DriveSerialNumber,
LastMediaSequence = inputFormat.Info.LastMediaSequence,
MediaBarcode = inputFormat.Info.MediaBarcode,
MediaManufacturer = inputFormat.Info.MediaManufacturer,
MediaModel = inputFormat.Info.MediaModel,
MediaPartNumber = inputFormat.Info.MediaPartNumber,
MediaSequence = inputFormat.Info.MediaSequence,
MediaSerialNumber = inputFormat.Info.MediaSerialNumber,
MediaTitle = inputFormat.Info.MediaTitle
};
Assert.IsTrue(outputOptical.SetImageInfo(metadata),
2022-12-11 22:20:23 +00:00
string.Format(Localization.Error_0_setting_metadata, outputOptical.ErrorMessage));
2022-03-06 13:29:38 +00:00
Metadata aaruMetadata = inputFormat.AaruMetadata;
List<DumpHardware> dumpHardware = inputFormat.DumpHardware;
2022-03-06 13:29:38 +00:00
foreach(MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags.Where(mediaTag =>
outputOptical.SupportedMediaTags.Contains(mediaTag)))
2022-03-06 13:29:38 +00:00
{
AaruConsole.WriteLine(Localization.Converting_media_tag_0, mediaTag);
2022-03-06 13:29:38 +00:00
errno = inputFormat.ReadMediaTag(mediaTag, out byte[] tag);
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
Assert.AreEqual(ErrorNumber.NoError, errno);
Assert.IsTrue(outputOptical.WriteMediaTag(tag, mediaTag));
}
2021-03-07 18:23:36 +00:00
AaruConsole.WriteLine(Localization._0_sectors_to_convert, inputFormat.Info.Sectors);
2022-03-06 13:29:38 +00:00
ulong doneSectors;
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
Assert.IsTrue(outputOptical.SetTracks(inputOptical.Tracks),
2022-12-11 22:20:23 +00:00
string.Format(Localization.Error_0_sending_tracks_list_to_output_image,
outputOptical.ErrorMessage));
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
foreach(Track track in inputOptical.Tracks)
{
doneSectors = 0;
ulong trackSectors = track.EndSector - track.StartSector + 1;
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
while(doneSectors < trackSectors)
2021-03-07 18:23:36 +00:00
{
2022-03-06 13:29:38 +00:00
byte[] sector;
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
uint sectorsToDo;
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
if(trackSectors - doneSectors >= SECTORS_TO_READ)
sectorsToDo = SECTORS_TO_READ;
else
sectorsToDo = (uint)(trackSectors - doneSectors);
2021-03-07 18:23:36 +00:00
2023-10-03 23:44:33 +01:00
var useNotLong = false;
var result = false;
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
if(UseLong)
{
2023-10-03 23:44:33 +01:00
errno = sectorsToDo == 1
? inputFormat.ReadSectorLong(doneSectors + track.StartSector, out sector)
2022-03-07 07:36:44 +00:00
: inputFormat.ReadSectorsLong(doneSectors + track.StartSector, sectorsToDo, out sector);
2022-03-06 13:29:38 +00:00
if(errno == ErrorNumber.NoError)
2023-10-03 23:44:33 +01:00
{
2022-03-06 13:29:38 +00:00
result = sectorsToDo == 1
? outputOptical.WriteSectorLong(sector, doneSectors + track.StartSector)
: outputOptical.WriteSectorsLong(sector, doneSectors + track.StartSector,
sectorsToDo);
2023-10-03 23:44:33 +01:00
}
2022-03-06 13:29:38 +00:00
else
result = true;
2021-03-07 18:23:36 +00:00
if(!result && sector.Length % 2352 != 0)
2022-03-06 13:29:38 +00:00
useNotLong = true;
}
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
if(!UseLong || useNotLong)
{
2023-10-03 23:44:33 +01:00
errno = sectorsToDo == 1
? inputFormat.ReadSector(doneSectors + track.StartSector, out sector)
2022-03-06 13:29:38 +00:00
: inputFormat.ReadSectors(doneSectors + track.StartSector, sectorsToDo, out sector);
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
Assert.AreEqual(ErrorNumber.NoError, errno);
2023-10-03 23:44:33 +01:00
result = sectorsToDo == 1
? outputOptical.WriteSector(sector, doneSectors + track.StartSector)
2022-03-06 13:29:38 +00:00
: outputOptical.WriteSectors(sector, doneSectors + track.StartSector, sectorsToDo);
}
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
Assert.IsTrue(result,
2022-12-11 22:20:23 +00:00
string.Format(Localization.Error_0_writing_sector_1_not_continuing,
outputOptical.ErrorMessage, doneSectors + track.StartSector));
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
doneSectors += sectorsToDo;
2021-03-07 18:23:36 +00:00
}
2022-03-06 13:29:38 +00:00
}
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
Dictionary<byte, string> isrcs = new();
Dictionary<byte, byte> trackFlags = new();
string mcn = null;
HashSet<int> subchannelExtents = new();
Dictionary<byte, int> smallestPregapLbaPerTrack = new();
2023-10-03 23:44:33 +01:00
var tracks = new Track[inputOptical.Tracks.Count];
2021-03-07 18:23:36 +00:00
2023-10-03 23:44:33 +01:00
for(var i = 0; i < tracks.Length; i++)
2022-03-06 13:29:38 +00:00
{
tracks[i] = new Track
2021-03-07 18:23:36 +00:00
{
2022-03-06 13:29:38 +00:00
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;
}
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags.Where(t => t == SectorTagType.CdTrackIsrc).
OrderBy(t => t))
{
foreach(Track track in tracks)
2021-03-07 18:23:36 +00:00
{
2022-03-06 13:29:38 +00:00
errno = inputFormat.ReadSectorTag(track.Sequence, tag, out byte[] isrc);
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
if(errno != ErrorNumber.NoError)
continue;
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
isrcs[(byte)track.Sequence] = Encoding.UTF8.GetString(isrc);
2021-03-07 18:23:36 +00:00
}
2022-03-06 13:29:38 +00:00
}
2021-03-07 18:23:36 +00:00
2022-03-07 07:36:44 +00:00
foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags.Where(t => t == SectorTagType.CdTrackFlags).
OrderBy(t => t))
2022-03-06 13:29:38 +00:00
{
foreach(Track track in tracks)
2021-03-07 18:23:36 +00:00
{
2022-03-06 13:29:38 +00:00
errno = inputFormat.ReadSectorTag(track.Sequence, tag, out byte[] flags);
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
if(errno != ErrorNumber.NoError)
continue;
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
trackFlags[(byte)track.Sequence] = flags[0];
2021-03-07 18:23:36 +00:00
}
2022-03-06 13:29:38 +00:00
}
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
for(ulong s = 0; s < inputFormat.Info.Sectors; s++)
{
if(s > int.MaxValue)
break;
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
subchannelExtents.Add((int)s);
}
2022-03-16 00:31:33 +00:00
foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags.OrderBy(t => t).TakeWhile(_ => UseLong))
2022-03-06 13:29:38 +00:00
{
switch(tag)
{
case SectorTagType.AppleSectorTag:
case SectorTagType.CdSectorSync:
case SectorTagType.CdSectorHeader:
case SectorTagType.CdSectorSubHeader:
case SectorTagType.CdSectorEdc:
case SectorTagType.CdSectorEccP:
case SectorTagType.CdSectorEccQ:
case SectorTagType.CdSectorEcc:
// This tags are inline in long sector
continue;
2021-03-07 18:23:36 +00:00
}
2022-03-06 13:29:38 +00:00
if(!outputOptical.SupportedSectorTags.Contains(tag))
continue;
foreach(Track track in inputOptical.Tracks)
2021-03-07 18:23:36 +00:00
{
2022-03-06 13:29:38 +00:00
doneSectors = 0;
ulong trackSectors = track.EndSector - track.StartSector + 1;
byte[] sector;
bool result;
2021-03-07 18:23:36 +00:00
switch(tag)
{
2022-03-06 13:29:38 +00:00
case SectorTagType.CdTrackFlags:
case SectorTagType.CdTrackIsrc:
errno = inputFormat.ReadSectorTag(track.Sequence, tag, out sector);
2022-12-11 22:20:23 +00:00
if(errno == ErrorNumber.NoData)
continue;
Assert.AreEqual(ErrorNumber.NoError, errno,
string.Format(Localization.Error_0_reading_tag_not_continuing, errno));
2022-03-06 13:29:38 +00:00
result = outputOptical.WriteSectorTag(sector, track.Sequence, tag);
2022-12-11 22:20:23 +00:00
Assert.IsTrue(result,
string.Format(Localization.Error_0_writing_tag_not_continuing,
outputOptical.ErrorMessage));
2022-03-06 13:29:38 +00:00
2021-03-07 18:23:36 +00:00
continue;
}
2022-03-06 13:29:38 +00:00
while(doneSectors < trackSectors)
2021-03-07 18:23:36 +00:00
{
2022-03-06 13:29:38 +00:00
uint sectorsToDo;
if(trackSectors - doneSectors >= SECTORS_TO_READ)
sectorsToDo = SECTORS_TO_READ;
else
sectorsToDo = (uint)(trackSectors - doneSectors);
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
if(sectorsToDo == 1)
2021-03-07 18:23:36 +00:00
{
2022-03-06 13:29:38 +00:00
errno = inputFormat.ReadSectorTag(doneSectors + track.StartSector, tag, out sector);
2022-12-11 22:20:23 +00:00
Assert.AreEqual(ErrorNumber.NoError, errno,
string.Format(Localization.Error_0_reading_tag_not_continuing, errno));
2022-03-06 13:29:38 +00:00
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, true, outputOptical,
true, true, null, null, smallestPregapLbaPerTrack, false, out _);
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
if(indexesChanged)
outputOptical.SetTracks(tracks.ToList());
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
result = true;
}
else
result = outputOptical.WriteSectorTag(sector, doneSectors + track.StartSector, tag);
2021-03-07 18:23:36 +00:00
}
2022-03-06 13:29:38 +00:00
else
2021-03-07 18:23:36 +00:00
{
2022-03-06 13:29:38 +00:00
errno = inputFormat.ReadSectorsTag(doneSectors + track.StartSector, sectorsToDo, tag,
out sector);
2021-03-07 18:23:36 +00:00
2022-12-11 22:20:23 +00:00
Assert.AreEqual(ErrorNumber.NoError, errno,
string.Format(Localization.Error_0_reading_tag_not_continuing, errno));
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
if(tag == SectorTagType.CdSectorSubchannel)
2021-03-07 18:23:36 +00:00
{
2022-03-06 13:29:38 +00:00
bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw,
MmcSubchannel.Raw, sector, doneSectors + track.StartSector, sectorsToDo, null,
isrcs, (byte)track.Sequence, ref mcn, tracks, subchannelExtents, true,
outputOptical, true, true, null, null, smallestPregapLbaPerTrack, false, out _);
2022-03-06 13:29:38 +00:00
if(indexesChanged)
outputOptical.SetTracks(tracks.ToList());
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
result = true;
2021-03-07 18:23:36 +00:00
}
else
2023-10-03 23:44:33 +01:00
{
2022-03-07 07:36:44 +00:00
result = outputOptical.WriteSectorsTag(sector, doneSectors + track.StartSector, sectorsToDo,
tag);
2023-10-03 23:44:33 +01:00
}
2022-03-06 13:29:38 +00:00
}
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
Assert.IsTrue(result,
2022-12-11 22:20:23 +00:00
string.Format(Localization.Error_0_writing_tag_for_sector_1_not_continuing,
outputOptical.ErrorMessage, doneSectors + track.StartSector));
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
doneSectors += sectorsToDo;
2021-03-07 18:23:36 +00:00
}
}
2022-03-06 13:29:38 +00:00
}
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
if(isrcs.Count > 0)
2023-10-03 23:44:33 +01:00
{
2022-03-06 13:29:38 +00:00
foreach(KeyValuePair<byte, string> isrc in isrcs)
2022-03-07 07:36:44 +00:00
outputOptical.WriteSectorTag(Encoding.UTF8.GetBytes(isrc.Value), isrc.Key, SectorTagType.CdTrackIsrc);
2023-10-03 23:44:33 +01:00
}
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
if(trackFlags.Count > 0)
2023-10-03 23:44:33 +01:00
{
2022-03-06 13:29:38 +00:00
foreach((byte track, byte flags) in trackFlags)
outputOptical.WriteSectorTag(new[]
{
flags
}, track, SectorTagType.CdTrackFlags);
2023-10-03 23:44:33 +01:00
}
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
if(mcn != null)
outputOptical.WriteMediaTag(Encoding.UTF8.GetBytes(mcn), MediaTagType.CD_MCN);
2021-03-07 18:23:36 +00:00
if(resume != null || dumpHardware != null)
2022-03-06 13:29:38 +00:00
{
if(resume != null)
outputOptical.SetDumpHardware(resume.Tries);
else if(dumpHardware != null)
outputOptical.SetDumpHardware(dumpHardware);
}
2021-03-07 18:23:36 +00:00
if(sidecar != null || aaruMetadata != null)
2022-03-06 13:29:38 +00:00
{
if(sidecar != null)
outputOptical.SetMetadata(sidecar);
else if(aaruMetadata != null)
outputOptical.SetMetadata(aaruMetadata);
2022-03-06 13:29:38 +00:00
}
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
Assert.True(outputOptical.Close(),
2022-12-11 22:20:23 +00:00
string.Format(Localization.Error_0_closing_output_image_Contents_are_not_correct,
outputOptical.ErrorMessage));
2021-03-07 18:23:36 +00:00
2022-03-06 13:29:38 +00:00
// Some images will never generate the same
if(Md5 != null)
{
string md5 = Md5Context.File(outputPath, out _);
2021-03-07 18:23:36 +00:00
Assert.AreEqual(Md5, md5, Localization.Hashes_are_different);
2021-03-07 18:23:36 +00:00
}
2022-03-06 13:29:38 +00:00
File.Delete(outputPath);
2021-03-07 18:23:36 +00:00
}
}