Add options to fix subchannels on image conversion.

This commit is contained in:
2020-07-14 02:16:53 +01:00
parent f1244d543c
commit 4759a73679
2 changed files with 282 additions and 73 deletions

View File

@@ -36,6 +36,7 @@ using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reactive;
using System.Text;
using System.Threading;
using System.Xml.Serialization;
using Aaru.CommonTypes;
@@ -45,6 +46,8 @@ using Aaru.CommonTypes.Metadata;
using Aaru.CommonTypes.Structs;
using Aaru.Console;
using Aaru.Core;
using Aaru.Core.Media;
using Aaru.Devices;
using Aaru.Gui.Models;
using Avalonia.Controls;
using Avalonia.Threading;
@@ -59,30 +62,27 @@ namespace Aaru.Gui.ViewModels.Windows
{
public class ImageConvertViewModel : ViewModelBase
{
readonly Window _view;
readonly IMediaImage inputFormat;
bool _cicmXmlFromImageVisible;
string _cicmXmlText;
bool _closeVisible;
string _commentsText;
bool _commentsVisible;
string _creatorText;
bool _creatorVisible;
bool _destinationEnabled;
string _destinationText;
bool _destinationVisible;
string _driveFirmwareRevisionText;
bool _driveFirmwareRevisionVisible;
string _driveManufacturerText;
bool _driveManufacturerVisible;
string _driveModelText;
bool _driveModelVisible;
string _driveSerialNumberText;
bool _driveSerialNumberVisible;
bool _forceChecked;
readonly Window _view;
readonly IMediaImage inputFormat;
bool _cicmXmlFromImageVisible;
string _cicmXmlText;
bool _closeVisible;
string _commentsText;
bool _commentsVisible;
string _creatorText;
bool _creatorVisible;
bool _destinationEnabled;
string _destinationText;
bool _destinationVisible;
string _driveFirmwareRevisionText;
bool _driveFirmwareRevisionVisible;
string _driveManufacturerText;
bool _driveManufacturerVisible;
string _driveModelText;
bool _driveModelVisible;
string _driveSerialNumberText;
bool _driveSerialNumberVisible;
bool _forceChecked;
bool _formatReadOnly;
double _lastMediaSequenceValue;
bool _lastMediaSequenceVisible;
@@ -127,49 +127,45 @@ namespace Aaru.Gui.ViewModels.Windows
public ImageConvertViewModel(IMediaImage inputFormat, string imageSource, Window view)
{
_view = view;
this.inputFormat = inputFormat;
cancel = false;
DestinationCommand = ReactiveCommand.Create(ExecuteDestinationCommand);
CreatorCommand = ReactiveCommand.Create(ExecuteCreatorCommand);
MediaTitleCommand = ReactiveCommand.Create(ExecuteMediaTitleCommand);
MediaManufacturerCommand = ReactiveCommand.Create(ExecuteMediaManufacturerCommand);
MediaModelCommand = ReactiveCommand.Create(ExecuteMediaModelCommand);
MediaSerialNumberCommand = ReactiveCommand.Create(ExecuteMediaSerialNumberCommand);
MediaBarcodeCommand = ReactiveCommand.Create(ExecuteMediaBarcodeCommand);
MediaPartNumberCommand = ReactiveCommand.Create(ExecuteMediaPartNumberCommand);
MediaSequenceCommand = ReactiveCommand.Create(ExecuteMediaSequenceCommand);
LastMediaSequenceCommand = ReactiveCommand.Create(ExecuteLastMediaSequenceCommand);
DriveManufacturerCommand = ReactiveCommand.Create(ExecuteDriveManufacturerCommand);
DriveModelCommand = ReactiveCommand.Create(ExecuteDriveModelCommand);
DriveSerialNumberCommand = ReactiveCommand.Create(ExecuteDriveSerialNumberCommand);
_view = view;
this.inputFormat = inputFormat;
cancel = false;
DestinationCommand = ReactiveCommand.Create(ExecuteDestinationCommand);
CreatorCommand = ReactiveCommand.Create(ExecuteCreatorCommand);
MediaTitleCommand = ReactiveCommand.Create(ExecuteMediaTitleCommand);
MediaManufacturerCommand = ReactiveCommand.Create(ExecuteMediaManufacturerCommand);
MediaModelCommand = ReactiveCommand.Create(ExecuteMediaModelCommand);
MediaSerialNumberCommand = ReactiveCommand.Create(ExecuteMediaSerialNumberCommand);
MediaBarcodeCommand = ReactiveCommand.Create(ExecuteMediaBarcodeCommand);
MediaPartNumberCommand = ReactiveCommand.Create(ExecuteMediaPartNumberCommand);
MediaSequenceCommand = ReactiveCommand.Create(ExecuteMediaSequenceCommand);
LastMediaSequenceCommand = ReactiveCommand.Create(ExecuteLastMediaSequenceCommand);
DriveManufacturerCommand = ReactiveCommand.Create(ExecuteDriveManufacturerCommand);
DriveModelCommand = ReactiveCommand.Create(ExecuteDriveModelCommand);
DriveSerialNumberCommand = ReactiveCommand.Create(ExecuteDriveSerialNumberCommand);
DriveFirmwareRevisionCommand = ReactiveCommand.Create(ExecuteDriveFirmwareRevisionCommand);
CommentsCommand = ReactiveCommand.Create(ExecuteCommentsCommand);
CicmXmlFromImageCommand = ReactiveCommand.Create(ExecuteCicmXmlFromImageCommand);
CicmXmlCommand = ReactiveCommand.Create(ExecuteCicmXmlCommand);
ResumeFileFromImageCommand = ReactiveCommand.Create(ExecuteResumeFileFromImageCommand);
ResumeFileCommand = ReactiveCommand.Create(ExecuteResumeFileCommand);
StartCommand = ReactiveCommand.Create(ExecuteStartCommand);
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
StopCommand = ReactiveCommand.Create(ExecuteStopCommand);
SourceText = imageSource;
CreatorVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.Creator);
MediaTitleVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaTitle);
CommentsVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.Comments);
CommentsCommand = ReactiveCommand.Create(ExecuteCommentsCommand);
CicmXmlFromImageCommand = ReactiveCommand.Create(ExecuteCicmXmlFromImageCommand);
CicmXmlCommand = ReactiveCommand.Create(ExecuteCicmXmlCommand);
ResumeFileFromImageCommand = ReactiveCommand.Create(ExecuteResumeFileFromImageCommand);
ResumeFileCommand = ReactiveCommand.Create(ExecuteResumeFileCommand);
StartCommand = ReactiveCommand.Create(ExecuteStartCommand);
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
StopCommand = ReactiveCommand.Create(ExecuteStopCommand);
SourceText = imageSource;
CreatorVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.Creator);
MediaTitleVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaTitle);
CommentsVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.Comments);
MediaManufacturerVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaManufacturer);
MediaModelVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaModel);
MediaModelVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaModel);
MediaSerialNumberVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaSerialNumber);
MediaBarcodeVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaBarcode);
MediaPartNumberVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaPartNumber);
MediaBarcodeVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaBarcode);
MediaPartNumberVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaPartNumber);
MediaSequenceVisible = inputFormat.Info.MediaSequence != 0 && inputFormat.Info.LastMediaSequence != 0;
LastMediaSequenceVisible = inputFormat.Info.MediaSequence != 0 && inputFormat.Info.LastMediaSequence != 0;
DriveManufacturerVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveManufacturer);
DriveModelVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveModel);
DriveSerialNumberVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveSerialNumber);
DriveManufacturerVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveManufacturer);
DriveModelVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveModel);
DriveSerialNumberVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveSerialNumber);
DriveFirmwareRevisionVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveFirmwareRevision);
PluginBase plugins = GetPluginBase.Instance;
@@ -1014,6 +1010,47 @@ namespace Aaru.Gui.ViewModels.Windows
Progress2Value = Progress2MaxValue;
});
Dictionary<byte, string> isrcs = new Dictionary<byte, string>();
Dictionary<byte, byte> trackFlags = new Dictionary<byte, byte>();
string mcn = null;
HashSet<int> subchannelExtents = new HashSet<int>();
foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags.
Where(t => t == SectorTagType.CdTrackIsrc).OrderBy(t => t))
{
foreach(Track track in inputOptical.Tracks)
{
byte[] isrc = inputFormat.ReadSectorTag(track.TrackSequence, tag);
if(isrc is null)
continue;
isrcs[(byte)track.TrackSequence] = Encoding.UTF8.GetString(isrc);
}
}
foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags.
Where(t => t == SectorTagType.CdTrackFlags).OrderBy(t => t))
{
foreach(Track track in inputOptical.Tracks)
{
byte[] flags = inputFormat.ReadSectorTag(track.TrackSequence, tag);
if(flags is null)
continue;
trackFlags[(byte)track.TrackSequence] = flags[0];
}
}
for(ulong s = 0; s < inputFormat.Info.Sectors; s++)
{
if(s > int.MaxValue)
break;
subchannelExtents.Add((int)s);
}
foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags)
{
if(!useLong || cancel)
@@ -1076,12 +1113,45 @@ namespace Aaru.Gui.ViewModels.Windows
if(sectorsToDo == 1)
{
sector = inputFormat.ReadSectorTag(doneSectors, tag);
result = outputFormat.WriteSectorTag(sector, doneSectors, tag);
Track track = tracks.LastOrDefault(t => t.TrackStartSector >= doneSectors);
if(tag == SectorTagType.CdSectorSubchannel &&
track != null)
{
bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw,
MmcSubchannel.Raw, sector, doneSectors, 1, null, isrcs,
(byte)track.TrackSequence, ref mcn, tracks.ToArray(), subchannelExtents, false,
outputFormat, false, false, null, null);
if(indexesChanged)
outputOptical.SetTracks(tracks.ToList());
result = true;
}
else
result = outputFormat.WriteSectorTag(sector, doneSectors, tag);
}
else
{
sector = inputFormat.ReadSectorsTag(doneSectors, sectorsToDo, tag);
result = outputFormat.WriteSectorsTag(sector, doneSectors, sectorsToDo, tag);
Track track = tracks.LastOrDefault(t => t.TrackStartSector >= doneSectors);
if(tag == SectorTagType.CdSectorSubchannel &&
track != null)
{
bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw,
MmcSubchannel.Raw, sector, doneSectors, sectorsToDo, null, isrcs,
(byte)track.TrackSequence, ref mcn, tracks.ToArray(), subchannelExtents, false,
outputFormat, false, false, null, null);
if(indexesChanged)
outputOptical.SetTracks(tracks.ToList());
result = true;
}
else
result = outputFormat.WriteSectorsTag(sector, doneSectors, sectorsToDo, tag);
}
if(!result)
@@ -1118,6 +1188,21 @@ namespace Aaru.Gui.ViewModels.Windows
Progress2Value = Progress2MaxValue;
});
if(isrcs.Count > 0)
foreach(KeyValuePair<byte, string> isrc in isrcs)
outputOptical.WriteSectorTag(Encoding.UTF8.GetBytes(isrc.Value), isrc.Key,
SectorTagType.CdTrackIsrc);
if(trackFlags.Count > 0)
foreach(KeyValuePair<byte, byte> flags in trackFlags)
outputOptical.WriteSectorTag(new[]
{
flags.Value
}, flags.Key, SectorTagType.CdTrackFlags);
if(mcn != null)
outputOptical.WriteMediaTag(Encoding.UTF8.GetBytes(mcn), MediaTagType.CD_MCN);
}
}
else

View File

@@ -36,6 +36,7 @@ using System.CommandLine;
using System.CommandLine.Invocation;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
@@ -44,6 +45,8 @@ using Aaru.CommonTypes.Metadata;
using Aaru.CommonTypes.Structs;
using Aaru.Console;
using Aaru.Core;
using Aaru.Core.Media;
using Aaru.Devices;
using Schemas;
using ImageInfo = Aaru.CommonTypes.Structs.ImageInfo;
using MediaType = Aaru.CommonTypes.MediaType;
@@ -186,6 +189,30 @@ namespace Aaru.Commands.Image
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option(new[]
{
"--fix-subchannel-position"
}, "Store subchannel according to the sector they describe.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--fix-subchannel"
}, "Try to fix subchannel. Implies fixing subchannel position.")
{
Argument = new Argument<bool>(() => false), Required = false
});
Add(new Option(new[]
{
"--fix-subchannel-crc"
}, "If subchannel looks OK but CRC fails, rewrite it. Implies fixing subchannel.")
{
Argument = new Argument<bool>(() => false), Required = false
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne, Description = "Input image path", Name = "input-path"
@@ -204,7 +231,8 @@ namespace Aaru.Commands.Image
string driveSerialNumber, bool force, string inputPath, int lastMediaSequence,
string mediaBarcode, string mediaManufacturer, string mediaModel,
string mediaPartNumber, int mediaSequence, string mediaSerialNumber, string mediaTitle,
string outputPath, string options, string resumeFile, string format, string geometry)
string outputPath, string options, string resumeFile, string format, string geometry,
bool fixSubchannelPosition, bool fixSubchannel, bool fixSubchannelCrc)
{
MainClass.PrintCopyright();
@@ -214,6 +242,12 @@ namespace Aaru.Commands.Image
if(verbose)
AaruConsole.VerboseWriteLineEvent += System.Console.WriteLine;
if(fixSubchannelCrc)
fixSubchannel = true;
if(fixSubchannel)
fixSubchannelPosition = true;
Statistics.AddCommand("convert-image");
AaruConsole.DebugWriteLine("Analyze command", "--cicm-xml={0}", cicmXml);
@@ -241,6 +275,9 @@ namespace Aaru.Commands.Image
AaruConsole.DebugWriteLine("Analyze command", "--output={0}", outputPath);
AaruConsole.DebugWriteLine("Analyze command", "--resume-file={0}", resumeFile);
AaruConsole.DebugWriteLine("Analyze command", "--verbose={0}", verbose);
AaruConsole.DebugWriteLine("Analyze command", "--fix-subchannel-position={0}", fixSubchannelPosition);
AaruConsole.DebugWriteLine("Analyze command", "--fix-subchannel={0}", fixSubchannel);
AaruConsole.DebugWriteLine("Analyze command", "--fix-subchannel-crc={0}", fixSubchannelCrc);
Dictionary<string, string> parsedOptions = Core.Options.Parse(options);
AaruConsole.DebugWriteLine("Analyze command", "Parsed options:");
@@ -449,8 +486,8 @@ namespace Aaru.Commands.Image
// Try name
else
candidates.AddRange(plugins.WritableImages.Values.Where(t => string.Equals(t.Name, format,
StringComparison.
InvariantCultureIgnoreCase)));
StringComparison.
InvariantCultureIgnoreCase)));
if(candidates.Count == 0)
{
@@ -613,7 +650,7 @@ namespace Aaru.Commands.Image
sectorsToDo = (uint)(trackSectors - doneSectors);
AaruConsole.Write("\rConverting sectors {0} to {1} in track {3} ({2:P2} done)",
doneSectors + track.TrackStartSector,
doneSectors + track.TrackStartSector,
doneSectors + sectorsToDo + track.TrackStartSector,
(doneSectors + track.TrackStartSector) / (double)inputFormat.Info.Sectors,
track.TrackSequence);
@@ -690,6 +727,48 @@ namespace Aaru.Commands.Image
AaruConsole.WriteLine();
Dictionary<byte, string> isrcs = new Dictionary<byte, string>();
Dictionary<byte, byte> trackFlags = new Dictionary<byte, byte>();
string mcn = null;
HashSet<int> subchannelExtents = new HashSet<int>();
Track[] tracks = inputOptical.Tracks.ToArray();
foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags.
Where(t => t == SectorTagType.CdTrackIsrc).OrderBy(t => t))
{
foreach(Track track in inputOptical.Tracks)
{
byte[] isrc = inputFormat.ReadSectorTag(track.TrackSequence, tag);
if(isrc is null)
continue;
isrcs[(byte)track.TrackSequence] = Encoding.UTF8.GetString(isrc);
}
}
foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags.
Where(t => t == SectorTagType.CdTrackFlags).OrderBy(t => t))
{
foreach(Track track in inputOptical.Tracks)
{
byte[] flags = inputFormat.ReadSectorTag(track.TrackSequence, tag);
if(flags is null)
continue;
trackFlags[(byte)track.TrackSequence] = flags[0];
}
}
for(ulong s = 0; s < inputFormat.Info.Sectors; s++)
{
if(s > int.MaxValue)
break;
subchannelExtents.Add((int)s);
}
foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags.OrderBy(t => t))
{
if(!useLong)
@@ -755,23 +834,53 @@ namespace Aaru.Commands.Image
sectorsToDo = (uint)(trackSectors - doneSectors);
AaruConsole.Write("\rConverting tag {4} for sectors {0} to {1} in track {3} ({2:P2} done)",
doneSectors + track.TrackStartSector,
doneSectors + track.TrackStartSector,
doneSectors + sectorsToDo + track.TrackStartSector,
(doneSectors + track.TrackStartSector) / (double)inputFormat.Info.Sectors,
track.TrackSequence, tag);
if(sectorsToDo == 1)
{
sector = inputFormat.ReadSectorTag(doneSectors + track.TrackStartSector, tag);
result = outputFormat.WriteSectorTag(sector, doneSectors + track.TrackStartSector, tag);
sector = inputFormat.ReadSectorTag(doneSectors + track.TrackStartSector, tag);
if(tag == SectorTagType.CdSectorSubchannel)
{
bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw,
MmcSubchannel.Raw, sector, doneSectors + track.TrackStartSector, 1, null,
isrcs, (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents,
fixSubchannelPosition, outputFormat, fixSubchannel, fixSubchannelCrc, null,
null);
if(indexesChanged)
outputOptical.SetTracks(tracks.ToList());
result = true;
}
else
result = outputFormat.WriteSectorTag(sector, doneSectors + track.TrackStartSector,
tag);
}
else
{
sector = inputFormat.ReadSectorsTag(doneSectors + track.TrackStartSector, sectorsToDo,
tag);
result = outputFormat.WriteSectorsTag(sector, doneSectors + track.TrackStartSector,
sectorsToDo, tag);
if(tag == SectorTagType.CdSectorSubchannel)
{
bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw,
MmcSubchannel.Raw, sector, doneSectors + track.TrackStartSector,
sectorsToDo, null, isrcs, (byte)track.TrackSequence, ref mcn, tracks,
subchannelExtents, fixSubchannelPosition, outputFormat, fixSubchannel,
fixSubchannelCrc, null, null);
if(indexesChanged)
outputOptical.SetTracks(tracks.ToList());
result = true;
}
else
result = outputFormat.WriteSectorsTag(sector, doneSectors + track.TrackStartSector,
sectorsToDo, tag);
}
if(!result)
@@ -810,6 +919,21 @@ namespace Aaru.Commands.Image
AaruConsole.WriteLine();
}
if(isrcs.Count > 0)
foreach(KeyValuePair<byte, string> isrc in isrcs)
outputOptical.WriteSectorTag(Encoding.UTF8.GetBytes(isrc.Value), isrc.Key,
SectorTagType.CdTrackIsrc);
if(trackFlags.Count > 0)
foreach(KeyValuePair<byte, byte> flags in trackFlags)
outputOptical.WriteSectorTag(new[]
{
flags.Value
}, flags.Key, SectorTagType.CdTrackFlags);
if(mcn != null)
outputOptical.WriteMediaTag(Encoding.UTF8.GetBytes(mcn), MediaTagType.CD_MCN);
}
else
{