Move commands to families.

This commit is contained in:
2020-01-03 17:41:19 +00:00
parent ba599df172
commit 684122e94c
30 changed files with 373 additions and 111 deletions

View File

@@ -0,0 +1,316 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Analyze.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Verbs.
//
// --[ Description ] ----------------------------------------------------------
//
// Implements the 'analyze' verb.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
using DiscImageChef.Core;
namespace DiscImageChef.Commands.Image
{
internal class AnalyzeCommand : Command
{
public AnalyzeCommand() : base("analyze",
"Analyzes a disc image and searches for partitions and/or filesystems.")
{
Add(new Option(new[]
{
"--encoding", "-e"
}, "Name of character encoding to use.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option(new[]
{
"--filesystems", "-f"
}, "Searches and analyzes filesystems.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--partitions", "-p"
}, "Searches and interprets partitions.")
{
Argument = new Argument<bool>(() => true), Required = false
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne, Description = "Disc image path", Name = "image-path"
});
Handler = CommandHandler.Create(typeof(AnalyzeCommand).GetMethod(nameof(Invoke)));
}
public static int Invoke(bool verbose, bool debug, string encoding, bool filesystems, bool partitions,
string imagePath)
{
MainClass.PrintCopyright();
if(debug)
DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
if(verbose)
DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
Statistics.AddCommand("analyze");
DicConsole.DebugWriteLine("Analyze command", "--debug={0}", debug);
DicConsole.DebugWriteLine("Analyze command", "--encoding={0}", encoding);
DicConsole.DebugWriteLine("Analyze command", "--filesystems={0}", filesystems);
DicConsole.DebugWriteLine("Analyze command", "--input={0}", imagePath);
DicConsole.DebugWriteLine("Analyze command", "--partitions={0}", partitions);
DicConsole.DebugWriteLine("Analyze command", "--verbose={0}", verbose);
var filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(imagePath);
if(inputFilter == null)
{
DicConsole.ErrorWriteLine("Cannot open specified file.");
return(int)ErrorNumber.CannotOpenFile;
}
Encoding encodingClass = null;
if(encoding != null)
try
{
encodingClass = Claunia.Encoding.Encoding.GetEncoding(encoding);
if(verbose)
DicConsole.VerboseWriteLine("Using encoding for {0}.", encodingClass.EncodingName);
}
catch(ArgumentException)
{
DicConsole.ErrorWriteLine("Specified encoding is not supported.");
return(int)ErrorNumber.EncodingUnknown;
}
PluginBase plugins = GetPluginBase.Instance;
bool checkRaw = false;
try
{
IMediaImage imageFormat = ImageFormat.Detect(inputFilter);
if(imageFormat == null)
{
DicConsole.WriteLine("Image format not identified, not proceeding with analysis.");
return(int)ErrorNumber.UnrecognizedFormat;
}
if(verbose)
DicConsole.VerboseWriteLine("Image format identified by {0} ({1}).", imageFormat.Name,
imageFormat.Id);
else
DicConsole.WriteLine("Image format identified by {0}.", imageFormat.Name);
DicConsole.WriteLine();
try
{
if(!imageFormat.Open(inputFilter))
{
DicConsole.WriteLine("Unable to open image format");
DicConsole.WriteLine("No error given");
return(int)ErrorNumber.CannotOpenFormat;
}
if(verbose)
{
ImageInfo.PrintImageInfo(imageFormat);
DicConsole.WriteLine();
}
Statistics.AddMediaFormat(imageFormat.Format);
Statistics.AddMedia(imageFormat.Info.MediaType, false);
Statistics.AddFilter(inputFilter.Name);
}
catch(Exception ex)
{
DicConsole.ErrorWriteLine("Unable to open image format");
DicConsole.ErrorWriteLine("Error: {0}", ex.Message);
DicConsole.DebugWriteLine("Analyze command", "Stack trace: {0}", ex.StackTrace);
return(int)ErrorNumber.CannotOpenFormat;
}
List<string> idPlugins;
IFilesystem plugin;
string information;
if(partitions)
{
List<Partition> partitionsList = Core.Partitions.GetAll(imageFormat);
Core.Partitions.AddSchemesToStats(partitionsList);
if(partitionsList.Count == 0)
{
DicConsole.DebugWriteLine("Analyze command", "No partitions found");
if(!filesystems)
{
DicConsole.WriteLine("No partitions founds, not searching for filesystems");
return(int)ErrorNumber.NothingFound;
}
checkRaw = true;
}
else
{
DicConsole.WriteLine("{0} partitions found.", partitionsList.Count);
for(int i = 0; i < partitionsList.Count; i++)
{
DicConsole.WriteLine();
DicConsole.WriteLine("Partition {0}:", partitionsList[i].Sequence);
DicConsole.WriteLine("Partition name: {0}", partitionsList[i].Name);
DicConsole.WriteLine("Partition type: {0}", partitionsList[i].Type);
DicConsole.WriteLine("Partition start: sector {0}, byte {1}", partitionsList[i].Start,
partitionsList[i].Offset);
DicConsole.WriteLine("Partition length: {0} sectors, {1} bytes", partitionsList[i].Length,
partitionsList[i].Size);
DicConsole.WriteLine("Partition scheme: {0}", partitionsList[i].Scheme);
DicConsole.WriteLine("Partition description:");
DicConsole.WriteLine(partitionsList[i].Description);
if(!filesystems)
continue;
DicConsole.WriteLine("Identifying filesystem on partition");
Core.Filesystems.Identify(imageFormat, out idPlugins, partitionsList[i]);
if(idPlugins.Count == 0)
DicConsole.WriteLine("Filesystem not identified");
else if(idPlugins.Count > 1)
{
DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins");
foreach(string pluginName in idPlugins)
if(plugins.PluginsList.TryGetValue(pluginName, out plugin))
{
DicConsole.WriteLine($"As identified by {plugin.Name}.");
plugin.GetInformation(imageFormat, partitionsList[i], out information,
encodingClass);
DicConsole.Write(information);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
}
}
else
{
plugins.PluginsList.TryGetValue(idPlugins[0], out plugin);
if(plugin == null)
continue;
DicConsole.WriteLine($"Identified by {plugin.Name}.");
plugin.GetInformation(imageFormat, partitionsList[i], out information, encodingClass);
DicConsole.Write("{0}", information);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
}
}
}
}
if(checkRaw)
{
var wholePart = new Partition
{
Name = "Whole device", Length = imageFormat.Info.Sectors,
Size = imageFormat.Info.Sectors * imageFormat.Info.SectorSize
};
Core.Filesystems.Identify(imageFormat, out idPlugins, wholePart);
if(idPlugins.Count == 0)
DicConsole.WriteLine("Filesystem not identified");
else if(idPlugins.Count > 1)
{
DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins");
foreach(string pluginName in idPlugins)
if(plugins.PluginsList.TryGetValue(pluginName, out plugin))
{
DicConsole.WriteLine($"As identified by {plugin.Name}.");
plugin.GetInformation(imageFormat, wholePart, out information, encodingClass);
DicConsole.Write(information);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
}
}
else
{
plugins.PluginsList.TryGetValue(idPlugins[0], out plugin);
if(plugin != null)
{
DicConsole.WriteLine($"Identified by {plugin.Name}.");
plugin.GetInformation(imageFormat, wholePart, out information, encodingClass);
DicConsole.Write(information);
Statistics.AddFilesystem(plugin.XmlFsType.Type);
}
}
}
}
catch(Exception ex)
{
DicConsole.ErrorWriteLine($"Error reading file: {ex.Message}");
DicConsole.DebugWriteLine("Analyze command", ex.StackTrace);
return(int)ErrorNumber.UnexpectedException;
}
return(int)ErrorNumber.NoError;
}
}
}

View File

@@ -0,0 +1,489 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Checksum.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Verbs.
//
// --[ Description ] ----------------------------------------------------------
//
// Implements the 'checksum' verb.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using DiscImageChef.Core;
using Schemas;
namespace DiscImageChef.Commands.Image
{
internal class ChecksumCommand : Command
{
// How many sectors to read at once
const uint SECTORS_TO_READ = 256;
public ChecksumCommand() : base("checksum", "Checksums an image.")
{
Add(new Option(new[]
{
"--adler32", "-a"
}, "Calculates Adler32.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option("--crc16", "Calculates CRC16.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--crc32", "-c"
}, "Calculates CRC32.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option("--crc64", "Calculates CRC64.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option("--fletcher16", "Calculates Fletcher-16.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option("--fletcher32", "Calculates Fletcher-32.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--md5", "-m"
}, "Calculates MD5.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--separated-tracks", "-t"
}, "Checksums each track separately.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--sha1", "-s"
}, "Calculates SHA1.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option("--sha256", "Calculates SHA256.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option("--sha384", "Calculates SHA384.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option("--sha512", "Calculates SHA512.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--spamsum", "-f"
}, "Calculates SpamSum fuzzy hash.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--whole-disc", "-w"
}, "Checksums the whole disc.")
{
Argument = new Argument<bool>(() => true), Required = false
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne, Description = "Media image path", Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)));
}
public static int Invoke(bool debug, bool verbose, bool adler32, bool crc16, bool crc32, bool crc64,
bool fletcher16, bool fletcher32, bool md5, bool sha1, bool sha256, bool sha384,
bool sha512, bool spamSum, string imagePath, bool separatedTracks, bool wholeDisc)
{
MainClass.PrintCopyright();
if(debug)
DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
if(verbose)
DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
Statistics.AddCommand("checksum");
DicConsole.DebugWriteLine("Checksum command", "--adler32={0}", adler32);
DicConsole.DebugWriteLine("Checksum command", "--crc16={0}", crc16);
DicConsole.DebugWriteLine("Checksum command", "--crc32={0}", crc32);
DicConsole.DebugWriteLine("Checksum command", "--crc64={0}", crc64);
DicConsole.DebugWriteLine("Checksum command", "--debug={0}", debug);
DicConsole.DebugWriteLine("Checksum command", "--fletcher16={0}", fletcher16);
DicConsole.DebugWriteLine("Checksum command", "--fletcher32={0}", fletcher32);
DicConsole.DebugWriteLine("Checksum command", "--input={0}", imagePath);
DicConsole.DebugWriteLine("Checksum command", "--md5={0}", md5);
DicConsole.DebugWriteLine("Checksum command", "--separated-tracks={0}", separatedTracks);
DicConsole.DebugWriteLine("Checksum command", "--sha1={0}", sha1);
DicConsole.DebugWriteLine("Checksum command", "--sha256={0}", sha256);
DicConsole.DebugWriteLine("Checksum command", "--sha384={0}", sha384);
DicConsole.DebugWriteLine("Checksum command", "--sha512={0}", sha512);
DicConsole.DebugWriteLine("Checksum command", "--spamsum={0}", spamSum);
DicConsole.DebugWriteLine("Checksum command", "--verbose={0}", verbose);
DicConsole.DebugWriteLine("Checksum command", "--whole-disc={0}", wholeDisc);
var filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(imagePath);
if(inputFilter == null)
{
DicConsole.ErrorWriteLine("Cannot open specified file.");
return(int)ErrorNumber.CannotOpenFile;
}
IMediaImage inputFormat = ImageFormat.Detect(inputFilter);
if(inputFormat == null)
{
DicConsole.ErrorWriteLine("Unable to recognize image format, not checksumming");
return(int)ErrorNumber.UnrecognizedFormat;
}
inputFormat.Open(inputFilter);
Statistics.AddMediaFormat(inputFormat.Format);
Statistics.AddMedia(inputFormat.Info.MediaType, false);
Statistics.AddFilter(inputFilter.Name);
var enabledChecksums = new EnableChecksum();
if(adler32)
enabledChecksums |= EnableChecksum.Adler32;
if(crc16)
enabledChecksums |= EnableChecksum.Crc16;
if(crc32)
enabledChecksums |= EnableChecksum.Crc32;
if(crc64)
enabledChecksums |= EnableChecksum.Crc64;
if(md5)
enabledChecksums |= EnableChecksum.Md5;
if(sha1)
enabledChecksums |= EnableChecksum.Sha1;
if(sha256)
enabledChecksums |= EnableChecksum.Sha256;
if(sha384)
enabledChecksums |= EnableChecksum.Sha384;
if(sha512)
enabledChecksums |= EnableChecksum.Sha512;
if(spamSum)
enabledChecksums |= EnableChecksum.SpamSum;
if(fletcher16)
enabledChecksums |= EnableChecksum.Fletcher16;
if(fletcher32)
enabledChecksums |= EnableChecksum.Fletcher32;
Checksum mediaChecksum = null;
switch(inputFormat)
{
case IOpticalMediaImage opticalInput when opticalInput.Tracks != null:
try
{
Checksum trackChecksum = null;
if(wholeDisc)
mediaChecksum = new Checksum(enabledChecksums);
ulong previousTrackEnd = 0;
List<Track> inputTracks = opticalInput.Tracks;
foreach(Track currentTrack in inputTracks)
{
if(currentTrack.TrackStartSector - previousTrackEnd != 0 && wholeDisc)
for(ulong i = previousTrackEnd + 1; i < currentTrack.TrackStartSector; i++)
{
DicConsole.Write("\rHashing track-less sector {0}", i);
byte[] hiddenSector = inputFormat.ReadSector(i);
mediaChecksum?.Update(hiddenSector);
}
DicConsole.DebugWriteLine("Checksum command",
"Track {0} starts at sector {1} and ends at sector {2}",
currentTrack.TrackSequence, currentTrack.TrackStartSector,
currentTrack.TrackEndSector);
if(separatedTracks)
trackChecksum = new Checksum(enabledChecksums);
ulong sectors = (currentTrack.TrackEndSector - currentTrack.TrackStartSector) + 1;
ulong doneSectors = 0;
DicConsole.WriteLine("Track {0} has {1} sectors", currentTrack.TrackSequence, sectors);
while(doneSectors < sectors)
{
byte[] sector;
if(sectors - doneSectors >= SECTORS_TO_READ)
{
sector = opticalInput.ReadSectors(doneSectors, SECTORS_TO_READ,
currentTrack.TrackSequence);
DicConsole.Write("\rHashing sectors {0} to {2} of track {1}", doneSectors,
currentTrack.TrackSequence, doneSectors + SECTORS_TO_READ);
doneSectors += SECTORS_TO_READ;
}
else
{
sector = opticalInput.ReadSectors(doneSectors, (uint)(sectors - doneSectors),
currentTrack.TrackSequence);
DicConsole.Write("\rHashing sectors {0} to {2} of track {1}", doneSectors,
currentTrack.TrackSequence, doneSectors + (sectors - doneSectors));
doneSectors += sectors - doneSectors;
}
if(wholeDisc)
mediaChecksum?.Update(sector);
if(separatedTracks)
trackChecksum?.Update(sector);
}
DicConsole.WriteLine();
if(separatedTracks)
if(trackChecksum != null)
foreach(ChecksumType chk in trackChecksum.End())
DicConsole.WriteLine("Track {0}'s {1}: {2}", currentTrack.TrackSequence,
chk.type, chk.Value);
previousTrackEnd = currentTrack.TrackEndSector;
}
if(opticalInput.Info.Sectors - previousTrackEnd != 0 && wholeDisc)
for(ulong i = previousTrackEnd + 1; i < opticalInput.Info.Sectors; i++)
{
DicConsole.Write("\rHashing track-less sector {0}", i);
byte[] hiddenSector = inputFormat.ReadSector(i);
mediaChecksum?.Update(hiddenSector);
}
if(wholeDisc)
if(mediaChecksum != null)
foreach(ChecksumType chk in mediaChecksum.End())
DicConsole.WriteLine("Disk's {0}: {1}", chk.type, chk.Value);
}
catch(Exception ex)
{
if(debug)
DicConsole.DebugWriteLine("Could not get tracks because {0}", ex.Message);
else
DicConsole.WriteLine("Unable to get separate tracks, not checksumming them");
}
break;
case ITapeImage tapeImage when tapeImage.IsTape && tapeImage.Files?.Count > 0:
{
Checksum trackChecksum = null;
if(wholeDisc)
mediaChecksum = new Checksum(enabledChecksums);
ulong previousTrackEnd = 0;
foreach(TapeFile currentFile in tapeImage.Files)
{
if(currentFile.FirstBlock - previousTrackEnd != 0 && wholeDisc)
for(ulong i = previousTrackEnd + 1; i < currentFile.FirstBlock; i++)
{
DicConsole.Write("\rHashing file-less block {0}", i);
byte[] hiddenSector = inputFormat.ReadSector(i);
mediaChecksum?.Update(hiddenSector);
}
DicConsole.DebugWriteLine("Checksum command",
"Track {0} starts at sector {1} and ends at block {2}",
currentFile.File, currentFile.FirstBlock, currentFile.LastBlock);
if(separatedTracks)
trackChecksum = new Checksum(enabledChecksums);
ulong sectors = (currentFile.LastBlock - currentFile.FirstBlock) + 1;
ulong doneSectors = 0;
DicConsole.WriteLine("File {0} has {1} sectors", currentFile.File, sectors);
while(doneSectors < sectors)
{
byte[] sector;
if(sectors - doneSectors >= SECTORS_TO_READ)
{
sector = tapeImage.ReadSectors(doneSectors + currentFile.FirstBlock, SECTORS_TO_READ);
DicConsole.Write("\rHashing blocks {0} to {2} of file {1}", doneSectors,
currentFile.File, doneSectors + SECTORS_TO_READ);
doneSectors += SECTORS_TO_READ;
}
else
{
sector = tapeImage.ReadSectors(doneSectors + currentFile.FirstBlock,
(uint)(sectors - doneSectors));
DicConsole.Write("\rHashing blocks {0} to {2} of file {1}", doneSectors,
currentFile.File, doneSectors + (sectors - doneSectors));
doneSectors += sectors - doneSectors;
}
if(wholeDisc)
mediaChecksum?.Update(sector);
if(separatedTracks)
trackChecksum?.Update(sector);
}
DicConsole.WriteLine();
if(separatedTracks)
if(trackChecksum != null)
foreach(ChecksumType chk in trackChecksum.End())
DicConsole.WriteLine("File {0}'s {1}: {2}", currentFile.File, chk.type, chk.Value);
previousTrackEnd = currentFile.LastBlock;
}
if(tapeImage.Info.Sectors - previousTrackEnd != 0 && wholeDisc)
for(ulong i = previousTrackEnd + 1; i < tapeImage.Info.Sectors; i++)
{
DicConsole.Write("\rHashing file-less sector {0}", i);
byte[] hiddenSector = inputFormat.ReadSector(i);
mediaChecksum?.Update(hiddenSector);
}
if(wholeDisc)
if(mediaChecksum != null)
foreach(ChecksumType chk in mediaChecksum.End())
DicConsole.WriteLine("Tape's {0}: {1}", chk.type, chk.Value);
break;
}
default:
{
mediaChecksum = new Checksum(enabledChecksums);
ulong sectors = inputFormat.Info.Sectors;
DicConsole.WriteLine("Sectors {0}", sectors);
ulong doneSectors = 0;
while(doneSectors < sectors)
{
byte[] sector;
if(sectors - doneSectors >= SECTORS_TO_READ)
{
sector = inputFormat.ReadSectors(doneSectors, SECTORS_TO_READ);
DicConsole.Write("\rHashing sectors {0} to {1}", doneSectors,
doneSectors + SECTORS_TO_READ);
doneSectors += SECTORS_TO_READ;
}
else
{
sector = inputFormat.ReadSectors(doneSectors, (uint)(sectors - doneSectors));
DicConsole.Write("\rHashing sectors {0} to {1}", doneSectors,
doneSectors + (sectors - doneSectors));
doneSectors += sectors - doneSectors;
}
mediaChecksum.Update(sector);
}
DicConsole.WriteLine();
foreach(ChecksumType chk in mediaChecksum.End())
DicConsole.WriteLine("Disk's {0}: {1}", chk.type, chk.Value);
break;
}
}
return(int)ErrorNumber.NoError;
}
}
}

View File

@@ -0,0 +1,365 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Compare.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Verbs.
//
// --[ Description ] ----------------------------------------------------------
//
// Implements the 'compare' verb.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Text;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using DiscImageChef.Core;
using ImageInfo = DiscImageChef.CommonTypes.Structs.ImageInfo;
namespace DiscImageChef.Commands.Image
{
internal class CompareCommand : Command
{
public CompareCommand() : base("compare", "Compares two disc images.")
{
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne, Description = "First media image path", Name = "image-path1"
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne, Description = "Second media image path", Name = "image-path2"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)));
}
public static int Invoke(bool debug, bool verbose, string imagePath1, string imagePath2)
{
MainClass.PrintCopyright();
if(debug)
DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
if(verbose)
DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
Statistics.AddCommand("compare");
DicConsole.DebugWriteLine("Compare command", "--debug={0}", debug);
DicConsole.DebugWriteLine("Compare command", "--input1={0}", imagePath1);
DicConsole.DebugWriteLine("Compare command", "--input2={0}", imagePath2);
DicConsole.DebugWriteLine("Compare command", "--verbose={0}", verbose);
var filtersList = new FiltersList();
IFilter inputFilter1 = filtersList.GetFilter(imagePath1);
filtersList = new FiltersList();
IFilter inputFilter2 = filtersList.GetFilter(imagePath2);
if(inputFilter1 == null)
{
DicConsole.ErrorWriteLine("Cannot open input file 1");
return(int)ErrorNumber.CannotOpenFile;
}
if(inputFilter2 == null)
{
DicConsole.ErrorWriteLine("Cannot open input file 2");
return(int)ErrorNumber.CannotOpenFile;
}
IMediaImage input1Format = ImageFormat.Detect(inputFilter1);
IMediaImage input2Format = ImageFormat.Detect(inputFilter2);
if(input1Format == null)
{
DicConsole.ErrorWriteLine("Input file 1 format not identified, not proceeding with comparison.");
return(int)ErrorNumber.UnrecognizedFormat;
}
if(verbose)
DicConsole.VerboseWriteLine("Input file 1 format identified by {0} ({1}).", input1Format.Name,
input1Format.Id);
else
DicConsole.WriteLine("Input file 1 format identified by {0}.", input1Format.Name);
if(input2Format == null)
{
DicConsole.ErrorWriteLine("Input file 2 format not identified, not proceeding with comparison.");
return(int)ErrorNumber.UnrecognizedFormat;
}
if(verbose)
DicConsole.VerboseWriteLine("Input file 2 format identified by {0} ({1}).", input2Format.Name,
input2Format.Id);
else
DicConsole.WriteLine("Input file 2 format identified by {0}.", input2Format.Name);
input1Format.Open(inputFilter1);
input2Format.Open(inputFilter2);
Statistics.AddMediaFormat(input1Format.Format);
Statistics.AddMediaFormat(input2Format.Format);
Statistics.AddMedia(input1Format.Info.MediaType, false);
Statistics.AddMedia(input2Format.Info.MediaType, false);
Statistics.AddFilter(inputFilter1.Name);
Statistics.AddFilter(inputFilter2.Name);
var sb = new StringBuilder();
if(verbose)
{
sb.AppendLine("\tDisc image 1\tDisc image 2");
sb.AppendLine("================================");
sb.AppendFormat("File\t{0}\t{1}", imagePath1, imagePath2).AppendLine();
sb.AppendFormat("Disc image format\t{0}\t{1}", input1Format.Name, input2Format.Name).AppendLine();
}
else
{
sb.AppendFormat("Disc image 1: {0}", imagePath1).AppendLine();
sb.AppendFormat("Disc image 2: {0}", imagePath2).AppendLine();
}
bool imagesDiffer = false;
ImageInfo image1Info = input1Format.Info;
ImageInfo image2Info = input2Format.Info;
List<Session> image1Sessions = new List<Session>();
List<Session> image2Sessions = new List<Session>();
Dictionary<MediaTagType, byte[]> image1DiskTags = new Dictionary<MediaTagType, byte[]>();
Dictionary<MediaTagType, byte[]> image2DiskTags = new Dictionary<MediaTagType, byte[]>();
foreach(MediaTagType disktag in Enum.GetValues(typeof(MediaTagType)))
{
try
{
byte[] temparray = input1Format.ReadDiskTag(disktag);
image1DiskTags.Add(disktag, temparray);
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
{
// ignored
}
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
}
foreach(MediaTagType disktag in Enum.GetValues(typeof(MediaTagType)))
{
try
{
byte[] temparray = input2Format.ReadDiskTag(disktag);
image2DiskTags.Add(disktag, temparray);
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
{
// ignored
}
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
}
if(verbose)
{
sb.AppendFormat("Has partitions?\t{0}\t{1}", image1Info.HasPartitions, image2Info.HasPartitions).
AppendLine();
sb.AppendFormat("Has sessions?\t{0}\t{1}", image1Info.HasSessions, image2Info.HasSessions).AppendLine();
sb.AppendFormat("Image size\t{0}\t{1}", image1Info.ImageSize, image2Info.ImageSize).AppendLine();
sb.AppendFormat("Sectors\t{0}\t{1}", image1Info.Sectors, image2Info.Sectors).AppendLine();
sb.AppendFormat("Sector size\t{0}\t{1}", image1Info.SectorSize, image2Info.SectorSize).AppendLine();
sb.AppendFormat("Creation time\t{0}\t{1}", image1Info.CreationTime, image2Info.CreationTime).
AppendLine();
sb.AppendFormat("Last modification time\t{0}\t{1}", image1Info.LastModificationTime,
image2Info.LastModificationTime).AppendLine();
sb.AppendFormat("Disk type\t{0}\t{1}", image1Info.MediaType, image2Info.MediaType).AppendLine();
sb.AppendFormat("Image version\t{0}\t{1}", image1Info.Version, image2Info.Version).AppendLine();
sb.AppendFormat("Image application\t{0}\t{1}", image1Info.Application, image2Info.Application).
AppendLine();
sb.AppendFormat("Image application version\t{0}\t{1}", image1Info.ApplicationVersion,
image2Info.ApplicationVersion).AppendLine();
sb.AppendFormat("Image creator\t{0}\t{1}", image1Info.Creator, image2Info.Creator).AppendLine();
sb.AppendFormat("Image name\t{0}\t{1}", image1Info.MediaTitle, image2Info.MediaTitle).AppendLine();
sb.AppendFormat("Image comments\t{0}\t{1}", image1Info.Comments, image2Info.Comments).AppendLine();
sb.AppendFormat("Disk manufacturer\t{0}\t{1}", image1Info.MediaManufacturer,
image2Info.MediaManufacturer).AppendLine();
sb.AppendFormat("Disk model\t{0}\t{1}", image1Info.MediaModel, image2Info.MediaModel).AppendLine();
sb.AppendFormat("Disk serial number\t{0}\t{1}", image1Info.MediaSerialNumber,
image2Info.MediaSerialNumber).AppendLine();
sb.AppendFormat("Disk barcode\t{0}\t{1}", image1Info.MediaBarcode, image2Info.MediaBarcode).
AppendLine();
sb.AppendFormat("Disk part no.\t{0}\t{1}", image1Info.MediaPartNumber, image2Info.MediaPartNumber).
AppendLine();
sb.AppendFormat("Disk sequence\t{0}\t{1}", image1Info.MediaSequence, image2Info.MediaSequence).
AppendLine();
sb.AppendFormat("Last disk on sequence\t{0}\t{1}", image1Info.LastMediaSequence,
image2Info.LastMediaSequence).AppendLine();
sb.AppendFormat("Drive manufacturer\t{0}\t{1}", image1Info.DriveManufacturer,
image2Info.DriveManufacturer).AppendLine();
sb.AppendFormat("Drive firmware revision\t{0}\t{1}", image1Info.DriveFirmwareRevision,
image2Info.DriveFirmwareRevision).AppendLine();
sb.AppendFormat("Drive model\t{0}\t{1}", image1Info.DriveModel, image2Info.DriveModel).AppendLine();
sb.AppendFormat("Drive serial number\t{0}\t{1}", image1Info.DriveSerialNumber,
image2Info.DriveSerialNumber).AppendLine();
foreach(MediaTagType disktag in Enum.GetValues(typeof(MediaTagType)))
sb.AppendFormat("Has {0}?\t{1}\t{2}", disktag, image1DiskTags.ContainsKey(disktag),
image2DiskTags.ContainsKey(disktag)).AppendLine();
}
DicConsole.WriteLine("Comparing disk image characteristics");
if(image1Info.HasPartitions != image2Info.HasPartitions)
{
imagesDiffer = true;
if(!verbose)
sb.AppendLine("Image partitioned status differ");
}
if(image1Info.HasSessions != image2Info.HasSessions)
{
imagesDiffer = true;
if(!verbose)
sb.AppendLine("Image session status differ");
}
if(image1Info.Sectors != image2Info.Sectors)
{
imagesDiffer = true;
if(!verbose)
sb.AppendLine("Image sectors differ");
}
if(image1Info.SectorSize != image2Info.SectorSize)
{
imagesDiffer = true;
if(!verbose)
sb.AppendLine("Image sector size differ");
}
if(image1Info.MediaType != image2Info.MediaType)
{
imagesDiffer = true;
if(!verbose)
sb.AppendLine("Disk type differ");
}
ulong leastSectors;
if(image1Info.Sectors < image2Info.Sectors)
{
imagesDiffer = true;
leastSectors = image1Info.Sectors;
if(!verbose)
sb.AppendLine("Image 2 has more sectors");
}
else if(image1Info.Sectors > image2Info.Sectors)
{
imagesDiffer = true;
leastSectors = image2Info.Sectors;
if(!verbose)
sb.AppendLine("Image 1 has more sectors");
}
else
leastSectors = image1Info.Sectors;
DicConsole.WriteLine("Comparing sectors...");
for(ulong sector = 0; sector < leastSectors; sector++)
{
DicConsole.Write("\rComparing sector {0} of {1}...", sector + 1, leastSectors);
try
{
byte[] image1Sector = input1Format.ReadSector(sector);
byte[] image2Sector = input2Format.ReadSector(sector);
ArrayHelpers.CompareBytes(out bool different, out bool sameSize, image1Sector, image2Sector);
if(different)
{
imagesDiffer = true;
sb.AppendFormat("Sector {0} is different", sector).AppendLine();
}
else if(!sameSize)
{
imagesDiffer = true;
sb.
AppendFormat("Sector {0} has different sizes ({1} bytes in image 1, {2} in image 2) but are otherwise identical",
sector, image1Sector.LongLength, image2Sector.LongLength).AppendLine();
}
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
{
// ignored
}
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
}
DicConsole.WriteLine();
sb.AppendLine(imagesDiffer ? "Images differ" : "Images do not differ");
DicConsole.WriteLine(sb.ToString());
return(int)ErrorNumber.NoError;
}
}
}

View File

@@ -0,0 +1,906 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : ConvertImage.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Verbs.
//
// --[ Description ] ----------------------------------------------------------
//
// Converts from one media image to another.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Metadata;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using DiscImageChef.Core;
using Schemas;
using ImageInfo = DiscImageChef.CommonTypes.Structs.ImageInfo;
using Version = DiscImageChef.CommonTypes.Interop.Version;
namespace DiscImageChef.Commands.Image
{
internal class ConvertImageCommand : Command
{
public ConvertImageCommand() : base("convert-image", "Converts one image to another format.")
{
Add(new Option(new[]
{
"--cicm-xml", "-x"
}, "Take metadata from existing CICM XML sidecar.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option("--comments", "Image comments.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option(new[]
{
"--count", "-c"
}, "How many sectors to convert at once.")
{
Argument = new Argument<int>(() => 64), Required = false
});
Add(new Option("--creator", "Who (person) created the image?.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option("--drive-manufacturer",
"Manufacturer of the drive used to read the media represented by the image.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option("--drive-model", "Model of the drive used to read the media represented by the image.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option("--drive-revision",
"Firmware revision of the drive used to read the media represented by the image.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option("--drive-serial",
"Serial number of the drive used to read the media represented by the image.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option(new[]
{
"--force", "-f"
}, "Continue conversion even if sector or media tags will be lost in the process.")
{
Argument = new Argument<bool>(() => false), Required = false
});
Add(new Option(new[]
{
"--format", "-p"
},
"Format of the output image, as plugin name or plugin id. If not present, will try to detect it from output image extension.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option("--media-barcode", "Barcode of the media represented by the image.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option("--media-lastsequence",
"Last media of the sequence the media represented by the image corresponds to.")
{
Argument = new Argument<int>(() => 0), Required = false
});
Add(new Option("--media-manufacturer", "Manufacturer of the media represented by the image.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option("--media-model", "Model of the media represented by the image.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option("--media-partnumber", "Part number of the media represented by the image.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option("--media-sequence", "Number in sequence for the media represented by the image.")
{
Argument = new Argument<int>(() => 0), Required = false
});
Add(new Option("--media-serial", "Serial number of the media represented by the image.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option("--media-title", "Title of the media represented by the image.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option(new[]
{
"--options", "-O"
}, "Comma separated name=value pairs of options to pass to output image plugin.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option(new[]
{
"--resume-file", "-r"
}, "Take list of dump hardware from existing resume file.")
{
Argument = new Argument<string>(() => null), Required = false
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne, Description = "Input image path", Name = "input-path"
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne, Description = "Output image path", Name = "output-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)));
}
public static int Invoke(bool verbose, bool debug, string cicmXml, string comments, int count, string creator,
string driveFirmwareRevision, string driveManufacturer, string driveModel,
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 outputOptions, string resumeFile, string format)
{
MainClass.PrintCopyright();
if(debug)
DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
if(verbose)
DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
Statistics.AddCommand("convert-image");
DicConsole.DebugWriteLine("Analyze command", "--cicm-xml={0}", cicmXml);
DicConsole.DebugWriteLine("Analyze command", "--comments={0}", comments);
DicConsole.DebugWriteLine("Analyze command", "--count={0}", count);
DicConsole.DebugWriteLine("Analyze command", "--creator={0}", creator);
DicConsole.DebugWriteLine("Analyze command", "--debug={0}", debug);
DicConsole.DebugWriteLine("Analyze command", "--drive-manufacturer={0}", driveManufacturer);
DicConsole.DebugWriteLine("Analyze command", "--drive-model={0}", driveModel);
DicConsole.DebugWriteLine("Analyze command", "--drive-revision={0}", driveFirmwareRevision);
DicConsole.DebugWriteLine("Analyze command", "--drive-serial={0}", driveSerialNumber);
DicConsole.DebugWriteLine("Analyze command", "--force={0}", force);
DicConsole.DebugWriteLine("Analyze command", "--format={0}", format);
DicConsole.DebugWriteLine("Analyze command", "--input={0}", inputPath);
DicConsole.DebugWriteLine("Analyze command", "--media-barcode={0}", mediaBarcode);
DicConsole.DebugWriteLine("Analyze command", "--media-lastsequence={0}", lastMediaSequence);
DicConsole.DebugWriteLine("Analyze command", "--media-manufacturer={0}", mediaManufacturer);
DicConsole.DebugWriteLine("Analyze command", "--media-model={0}", mediaModel);
DicConsole.DebugWriteLine("Analyze command", "--media-partnumber={0}", mediaPartNumber);
DicConsole.DebugWriteLine("Analyze command", "--media-sequence={0}", mediaSequence);
DicConsole.DebugWriteLine("Analyze command", "--media-serial={0}", mediaSerialNumber);
DicConsole.DebugWriteLine("Analyze command", "--media-title={0}", mediaTitle);
DicConsole.DebugWriteLine("Analyze command", "--options={0}", outputOptions);
DicConsole.DebugWriteLine("Analyze command", "--output={0}", outputPath);
DicConsole.DebugWriteLine("Analyze command", "--resume-file={0}", resumeFile);
DicConsole.DebugWriteLine("Analyze command", "--verbose={0}", verbose);
Dictionary<string, string> parsedOptions = Core.Options.Parse(outputOptions);
DicConsole.DebugWriteLine("Analyze command", "Parsed options:");
foreach(KeyValuePair<string, string> parsedOption in parsedOptions)
DicConsole.DebugWriteLine("Analyze command", "{0} = {1}", parsedOption.Key, parsedOption.Value);
if(count == 0)
{
DicConsole.ErrorWriteLine("Need to specify more than 0 sectors to copy at once");
return(int)ErrorNumber.InvalidArgument;
}
Resume resume = null;
CICMMetadataType sidecar = null;
var xs = new XmlSerializer(typeof(CICMMetadataType));
if(cicmXml != null)
if(File.Exists(cicmXml))
try
{
var sr = new StreamReader(cicmXml);
sidecar = (CICMMetadataType)xs.Deserialize(sr);
sr.Close();
}
catch
{
DicConsole.ErrorWriteLine("Incorrect metadata sidecar file, not continuing...");
return(int)ErrorNumber.InvalidSidecar;
}
else
{
DicConsole.ErrorWriteLine("Could not find metadata sidecar, not continuing...");
return(int)ErrorNumber.FileNotFound;
}
xs = new XmlSerializer(typeof(Resume));
if(resumeFile != null)
if(File.Exists(resumeFile))
try
{
var sr = new StreamReader(resumeFile);
resume = (Resume)xs.Deserialize(sr);
sr.Close();
}
catch
{
DicConsole.ErrorWriteLine("Incorrect resume file, not continuing...");
return(int)ErrorNumber.InvalidResume;
}
else
{
DicConsole.ErrorWriteLine("Could not find resume file, not continuing...");
return(int)ErrorNumber.FileNotFound;
}
var filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(inputPath);
if(inputFilter == null)
{
DicConsole.ErrorWriteLine("Cannot open specified file.");
return(int)ErrorNumber.CannotOpenFile;
}
if(File.Exists(outputPath))
{
DicConsole.ErrorWriteLine("Output file already exists, not continuing.");
return(int)ErrorNumber.DestinationExists;
}
PluginBase plugins = GetPluginBase.Instance;
IMediaImage inputFormat = ImageFormat.Detect(inputFilter);
if(inputFormat == null)
{
DicConsole.WriteLine("Input image format not identified, not proceeding with conversion.");
return(int)ErrorNumber.UnrecognizedFormat;
}
if(verbose)
DicConsole.VerboseWriteLine("Input image format identified by {0} ({1}).", inputFormat.Name,
inputFormat.Id);
else
DicConsole.WriteLine("Input image format identified by {0}.", inputFormat.Name);
try
{
if(!inputFormat.Open(inputFilter))
{
DicConsole.WriteLine("Unable to open image format");
DicConsole.WriteLine("No error given");
return(int)ErrorNumber.CannotOpenFormat;
}
DicConsole.DebugWriteLine("Convert-image command", "Correctly opened image file.");
DicConsole.DebugWriteLine("Convert-image command", "Image without headers is {0} bytes.",
inputFormat.Info.ImageSize);
DicConsole.DebugWriteLine("Convert-image command", "Image has {0} sectors.", inputFormat.Info.Sectors);
DicConsole.DebugWriteLine("Convert-image command", "Image identifies media type as {0}.",
inputFormat.Info.MediaType);
Statistics.AddMediaFormat(inputFormat.Format);
Statistics.AddMedia(inputFormat.Info.MediaType, false);
Statistics.AddFilter(inputFilter.Name);
}
catch(Exception ex)
{
DicConsole.ErrorWriteLine("Unable to open image format");
DicConsole.ErrorWriteLine("Error: {0}", ex.Message);
DicConsole.DebugWriteLine("Convert-image command", "Stack trace: {0}", ex.StackTrace);
return(int)ErrorNumber.CannotOpenFormat;
}
List<IWritableImage> candidates = new List<IWritableImage>();
// Try extension
if(string.IsNullOrEmpty(format))
candidates.AddRange(plugins.WritableImages.Values.Where(t =>
t.KnownExtensions.
Contains(Path.GetExtension(outputPath))));
// Try Id
else if(Guid.TryParse(format, out Guid outId))
candidates.AddRange(plugins.WritableImages.Values.Where(t => t.Id.Equals(outId)));
// Try name
else
candidates.AddRange(plugins.WritableImages.Values.Where(t => string.Equals(t.Name, format,
StringComparison.
InvariantCultureIgnoreCase)));
if(candidates.Count == 0)
{
DicConsole.WriteLine("No plugin supports requested extension.");
return(int)ErrorNumber.FormatNotFound;
}
if(candidates.Count > 1)
{
DicConsole.WriteLine("More than one plugin supports requested extension.");
return(int)ErrorNumber.TooManyFormats;
}
IWritableImage outputFormat = candidates[0];
if(verbose)
DicConsole.VerboseWriteLine("Output image format: {0} ({1}).", outputFormat.Name, outputFormat.Id);
else
DicConsole.WriteLine("Output image format: {0}.", outputFormat.Name);
if(!outputFormat.SupportedMediaTypes.Contains(inputFormat.Info.MediaType))
{
DicConsole.ErrorWriteLine("Output format does not support media type, cannot continue...");
return(int)ErrorNumber.UnsupportedMedia;
}
foreach(MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags)
{
if(outputFormat.SupportedMediaTags.Contains(mediaTag) || force)
continue;
DicConsole.ErrorWriteLine("Converting image will lose media tag {0}, not continuing...", mediaTag);
DicConsole.ErrorWriteLine("If you don't care, use force option.");
return(int)ErrorNumber.DataWillBeLost;
}
bool useLong = inputFormat.Info.ReadableSectorTags.Count != 0;
foreach(SectorTagType sectorTag in inputFormat.Info.ReadableSectorTags)
{
if(outputFormat.SupportedSectorTags.Contains(sectorTag))
continue;
if(force)
{
if(sectorTag != SectorTagType.CdTrackFlags &&
sectorTag != SectorTagType.CdTrackIsrc &&
sectorTag != SectorTagType.CdSectorSubchannel)
useLong = false;
continue;
}
DicConsole.ErrorWriteLine("Converting image will lose sector tag {0}, not continuing...", sectorTag);
DicConsole.
ErrorWriteLine("If you don't care, use force option. This will skip all sector tags converting only user data.");
return(int)ErrorNumber.DataWillBeLost;
}
if(!outputFormat.Create(outputPath, inputFormat.Info.MediaType, parsedOptions, inputFormat.Info.Sectors,
inputFormat.Info.SectorSize))
{
DicConsole.ErrorWriteLine("Error {0} creating output image.", outputFormat.ErrorMessage);
return(int)ErrorNumber.CannotCreateFormat;
}
var metadata = new ImageInfo
{
Application = "DiscImageChef",
ApplicationVersion = Version.GetVersion(),
Comments = comments ?? inputFormat.Info.Comments,
Creator = creator ?? inputFormat.Info.Creator,
DriveFirmwareRevision = driveFirmwareRevision ?? inputFormat.Info.DriveFirmwareRevision,
DriveManufacturer = driveManufacturer ?? inputFormat.Info.DriveManufacturer,
DriveModel = driveModel ?? inputFormat.Info.DriveModel,
DriveSerialNumber = driveSerialNumber ?? inputFormat.Info.DriveSerialNumber,
LastMediaSequence = lastMediaSequence != 0 ? lastMediaSequence : inputFormat.Info.LastMediaSequence,
MediaBarcode = mediaBarcode ?? inputFormat.Info.MediaBarcode,
MediaManufacturer = mediaManufacturer ?? inputFormat.Info.MediaManufacturer,
MediaModel = mediaModel ?? inputFormat.Info.MediaModel,
MediaPartNumber = mediaPartNumber ?? inputFormat.Info.MediaPartNumber,
MediaSequence = mediaSequence != 0 ? mediaSequence : inputFormat.Info.MediaSequence,
MediaSerialNumber = mediaSerialNumber ?? inputFormat.Info.MediaSerialNumber,
MediaTitle = mediaTitle ?? inputFormat.Info.MediaTitle
};
if(!outputFormat.SetMetadata(metadata))
{
DicConsole.ErrorWrite("Error {0} setting metadata, ", outputFormat.ErrorMessage);
if(!force)
{
DicConsole.ErrorWriteLine("not continuing...");
return(int)ErrorNumber.WriteError;
}
DicConsole.ErrorWriteLine("continuing...");
}
CICMMetadataType cicmMetadata = inputFormat.CicmMetadata;
List<DumpHardwareType> dumpHardware = inputFormat.DumpHardware;
foreach(MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags)
{
if(force && !outputFormat.SupportedMediaTags.Contains(mediaTag))
continue;
DicConsole.WriteLine("Converting media tag {0}", mediaTag);
byte[] tag = inputFormat.ReadDiskTag(mediaTag);
if(outputFormat.WriteMediaTag(tag, mediaTag))
continue;
if(force)
DicConsole.ErrorWriteLine("Error {0} writing media tag, continuing...", outputFormat.ErrorMessage);
else
{
DicConsole.ErrorWriteLine("Error {0} writing media tag, not continuing...",
outputFormat.ErrorMessage);
return(int)ErrorNumber.WriteError;
}
}
DicConsole.WriteLine("{0} sectors to convert", inputFormat.Info.Sectors);
ulong doneSectors = 0;
if(inputFormat is IOpticalMediaImage inputOptical &&
outputFormat is IWritableOpticalImage outputOptical &&
inputOptical.Tracks != null)
{
if(!outputOptical.SetTracks(inputOptical.Tracks))
{
DicConsole.ErrorWriteLine("Error {0} sending tracks list to output image.",
outputFormat.ErrorMessage);
return(int)ErrorNumber.WriteError;
}
foreach(Track track in inputOptical.Tracks)
{
doneSectors = 0;
ulong trackSectors = (track.TrackEndSector - track.TrackStartSector) + 1;
while(doneSectors < trackSectors)
{
byte[] sector;
uint sectorsToDo;
if(trackSectors - doneSectors >= (ulong)count)
sectorsToDo = (uint)count;
else
sectorsToDo = (uint)(trackSectors - doneSectors);
DicConsole.Write("\rConverting sectors {0} to {1} in track {3} ({2:P2} done)",
doneSectors + track.TrackStartSector,
doneSectors + sectorsToDo + track.TrackStartSector,
(doneSectors + track.TrackStartSector) / (double)inputFormat.Info.Sectors,
track.TrackSequence);
bool result;
if(useLong)
if(sectorsToDo == 1)
{
sector = inputFormat.ReadSectorLong(doneSectors + track.TrackStartSector);
result = outputFormat.WriteSectorLong(sector, doneSectors + track.TrackStartSector);
}
else
{
sector = inputFormat.ReadSectorsLong(doneSectors + track.TrackStartSector, sectorsToDo);
result = outputFormat.WriteSectorsLong(sector, doneSectors + track.TrackStartSector,
sectorsToDo);
}
else
{
if(sectorsToDo == 1)
{
sector = inputFormat.ReadSector(doneSectors + track.TrackStartSector);
result = outputFormat.WriteSector(sector, doneSectors + track.TrackStartSector);
}
else
{
sector = inputFormat.ReadSectors(doneSectors + track.TrackStartSector, sectorsToDo);
result = outputFormat.WriteSectors(sector, doneSectors + track.TrackStartSector,
sectorsToDo);
}
}
if(!result)
if(force)
DicConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...",
outputFormat.ErrorMessage, doneSectors);
else
{
DicConsole.ErrorWriteLine("Error {0} writing sector {1}, not continuing...",
outputFormat.ErrorMessage, doneSectors);
return(int)ErrorNumber.WriteError;
}
doneSectors += sectorsToDo;
}
}
DicConsole.Write("\rConverting sectors {0} to {1} in track {3} ({2:P2} done)", inputFormat.Info.Sectors,
inputFormat.Info.Sectors, 1.0, inputOptical.Tracks.Count);
DicConsole.WriteLine();
foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags.OrderBy(t => t))
{
if(!useLong)
break;
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;
}
if(force && !outputFormat.SupportedSectorTags.Contains(tag))
continue;
foreach(Track track in inputOptical.Tracks)
{
doneSectors = 0;
ulong trackSectors = (track.TrackEndSector - track.TrackStartSector) + 1;
byte[] sector;
bool result;
switch(tag)
{
case SectorTagType.CdTrackFlags:
case SectorTagType.CdTrackIsrc:
DicConsole.Write("\rConverting tag {0} in track {1} ({2:P2} done).", tag,
track.TrackSequence,
track.TrackSequence / (double)inputOptical.Tracks.Count);
sector = inputFormat.ReadSectorTag(track.TrackStartSector, tag);
result = outputFormat.WriteSectorTag(sector, track.TrackStartSector, tag);
if(!result)
if(force)
DicConsole.ErrorWriteLine("Error {0} writing tag, continuing...",
outputFormat.ErrorMessage);
else
{
DicConsole.ErrorWriteLine("Error {0} writing tag, not continuing...",
outputFormat.ErrorMessage);
return(int)ErrorNumber.WriteError;
}
continue;
}
while(doneSectors < trackSectors)
{
uint sectorsToDo;
if(trackSectors - doneSectors >= (ulong)count)
sectorsToDo = (uint)count;
else
sectorsToDo = (uint)(trackSectors - doneSectors);
DicConsole.Write("\rConverting tag {4} for sectors {0} to {1} in track {3} ({2:P2} done)",
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);
}
else
{
sector = inputFormat.ReadSectorsTag(doneSectors + track.TrackStartSector, sectorsToDo,
tag);
result = outputFormat.WriteSectorsTag(sector, doneSectors + track.TrackStartSector,
sectorsToDo, tag);
}
if(!result)
if(force)
DicConsole.ErrorWriteLine("Error {0} writing tag for sector {1}, continuing...",
outputFormat.ErrorMessage, doneSectors);
else
{
DicConsole.ErrorWriteLine("Error {0} writing tag for sector {1}, not continuing...",
outputFormat.ErrorMessage, doneSectors);
return(int)ErrorNumber.WriteError;
}
doneSectors += sectorsToDo;
}
}
switch(tag)
{
case SectorTagType.CdTrackFlags:
case SectorTagType.CdTrackIsrc:
DicConsole.Write("\rConverting tag {0} in track {1} ({2:P2} done).", tag,
inputOptical.Tracks.Count, 1.0);
break;
default:
DicConsole.Write("\rConverting tag {4} for sectors {0} to {1} in track {3} ({2:P2} done)",
inputFormat.Info.Sectors, inputFormat.Info.Sectors, 1.0,
inputOptical.Tracks.Count, tag);
break;
}
DicConsole.WriteLine();
}
}
else
{
DicConsole.WriteLine("Setting geometry to {0} cylinders, {1} heads and {2} sectors per track",
inputFormat.Info.Cylinders, inputFormat.Info.Heads,
inputFormat.Info.SectorsPerTrack);
if(!outputFormat.SetGeometry(inputFormat.Info.Cylinders, inputFormat.Info.Heads,
inputFormat.Info.SectorsPerTrack))
DicConsole.ErrorWriteLine("Error {0} setting geometry, image may be incorrect, continuing...",
outputFormat.ErrorMessage);
while(doneSectors < inputFormat.Info.Sectors)
{
byte[] sector;
uint sectorsToDo;
if(inputFormat.Info.Sectors - doneSectors >= (ulong)count)
sectorsToDo = (uint)count;
else
sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors);
DicConsole.Write("\rConverting sectors {0} to {1} ({2:P2} done)", doneSectors,
doneSectors + sectorsToDo, doneSectors / (double)inputFormat.Info.Sectors);
bool result;
if(useLong)
if(sectorsToDo == 1)
{
sector = inputFormat.ReadSectorLong(doneSectors);
result = outputFormat.WriteSectorLong(sector, doneSectors);
}
else
{
sector = inputFormat.ReadSectorsLong(doneSectors, sectorsToDo);
result = outputFormat.WriteSectorsLong(sector, doneSectors, sectorsToDo);
}
else
{
if(sectorsToDo == 1)
{
sector = inputFormat.ReadSector(doneSectors);
result = outputFormat.WriteSector(sector, doneSectors);
}
else
{
sector = inputFormat.ReadSectors(doneSectors, sectorsToDo);
result = outputFormat.WriteSectors(sector, doneSectors, sectorsToDo);
}
}
if(!result)
if(force)
DicConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...",
outputFormat.ErrorMessage, doneSectors);
else
{
DicConsole.ErrorWriteLine("Error {0} writing sector {1}, not continuing...",
outputFormat.ErrorMessage, doneSectors);
return(int)ErrorNumber.WriteError;
}
doneSectors += sectorsToDo;
}
DicConsole.Write("\rConverting sectors {0} to {1} ({2:P2} done)", inputFormat.Info.Sectors,
inputFormat.Info.Sectors, 1.0);
DicConsole.WriteLine();
foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags)
{
if(!useLong)
break;
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;
}
if(force && !outputFormat.SupportedSectorTags.Contains(tag))
continue;
doneSectors = 0;
while(doneSectors < inputFormat.Info.Sectors)
{
byte[] sector;
uint sectorsToDo;
if(inputFormat.Info.Sectors - doneSectors >= (ulong)count)
sectorsToDo = (uint)count;
else
sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors);
DicConsole.Write("\rConverting tag {2} for sectors {0} to {1} ({2:P2} done)", doneSectors,
doneSectors + sectorsToDo, doneSectors / (double)inputFormat.Info.Sectors,
tag);
bool result;
if(sectorsToDo == 1)
{
sector = inputFormat.ReadSectorTag(doneSectors, tag);
result = outputFormat.WriteSectorTag(sector, doneSectors, tag);
}
else
{
sector = inputFormat.ReadSectorsTag(doneSectors, sectorsToDo, tag);
result = outputFormat.WriteSectorsTag(sector, doneSectors, sectorsToDo, tag);
}
if(!result)
if(force)
DicConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...",
outputFormat.ErrorMessage, doneSectors);
else
{
DicConsole.ErrorWriteLine("Error {0} writing sector {1}, not continuing...",
outputFormat.ErrorMessage, doneSectors);
return(int)ErrorNumber.WriteError;
}
doneSectors += sectorsToDo;
}
DicConsole.Write("\rConverting tag {2} for sectors {0} to {1} ({2:P2} done)",
inputFormat.Info.Sectors, inputFormat.Info.Sectors, 1.0, tag);
DicConsole.WriteLine();
}
}
bool ret = false;
if(resume != null ||
dumpHardware != null)
{
if(resume != null)
ret = outputFormat.SetDumpHardware(resume.Tries);
else if(dumpHardware != null)
ret = outputFormat.SetDumpHardware(dumpHardware);
if(ret)
DicConsole.WriteLine("Written dump hardware list to output image.");
}
ret = false;
if(sidecar != null ||
cicmMetadata != null)
{
if(sidecar != null)
ret = outputFormat.SetCicmMetadata(sidecar);
else if(cicmMetadata != null)
ret = outputFormat.SetCicmMetadata(cicmMetadata);
if(ret)
DicConsole.WriteLine("Written CICM XML metadata to output image.");
}
DicConsole.WriteLine("Closing output image.");
if(!outputFormat.Close())
DicConsole.ErrorWriteLine("Error {0} closing output image... Contents are not correct.",
outputFormat.ErrorMessage);
DicConsole.WriteLine();
DicConsole.WriteLine("Conversion done.");
return(int)ErrorNumber.NoError;
}
}
}

View File

@@ -0,0 +1,261 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CreateSidecar.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Verbs.
//
// --[ Description ] ----------------------------------------------------------
//
// Implements the 'create-sidecar' verb.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
using DiscImageChef.Core;
using Schemas;
namespace DiscImageChef.Commands.Image
{
internal class CreateSidecarCommand : Command
{
public CreateSidecarCommand() : base("create-sidecar", "Creates CICM Metadata XML sidecar.")
{
Add(new Option(new[]
{
"--block-size", "-b"
},
"Only used for tapes, indicates block size. Files in the folder whose size is not a multiple of this value will simply be ignored.")
{
Argument = new Argument<int>(() => 512), Required = false
});
Add(new Option(new[]
{
"--encoding", "-e"
}, "Name of character encoding to use.")
{
Argument = new Argument<string>(() => null), Required = false
});
Add(new Option(new[]
{
"--tape", "-t"
},
"When used indicates that input is a folder containing alphabetically sorted files extracted from a linear block-based tape with fixed block size (e.g. a SCSI tape device).")
{
Argument = new Argument<bool>(() => false), Required = false
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne, Description = "Media image path", Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)));
}
public static int Invoke(bool debug, bool verbose, uint blockSize, string encodingName, string imagePath,
bool tape)
{
MainClass.PrintCopyright();
if(debug)
DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
if(verbose)
DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
Statistics.AddCommand("create-sidecar");
DicConsole.DebugWriteLine("Create sidecar command", "--block-size={0}", blockSize);
DicConsole.DebugWriteLine("Create sidecar command", "--debug={0}", debug);
DicConsole.DebugWriteLine("Create sidecar command", "--encoding={0}", encodingName);
DicConsole.DebugWriteLine("Create sidecar command", "--input={0}", imagePath);
DicConsole.DebugWriteLine("Create sidecar command", "--tape={0}", tape);
DicConsole.DebugWriteLine("Create sidecar command", "--verbose={0}", verbose);
Encoding encodingClass = null;
if(encodingName != null)
try
{
encodingClass = Claunia.Encoding.Encoding.GetEncoding(encodingName);
if(verbose)
DicConsole.VerboseWriteLine("Using encoding for {0}.", encodingClass.EncodingName);
}
catch(ArgumentException)
{
DicConsole.ErrorWriteLine("Specified encoding is not supported.");
return(int)ErrorNumber.EncodingUnknown;
}
if(File.Exists(imagePath))
{
if(tape)
{
DicConsole.ErrorWriteLine("You cannot use --tape option when input is a file.");
return(int)ErrorNumber.ExpectedDirectory;
}
var filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(imagePath);
if(inputFilter == null)
{
DicConsole.ErrorWriteLine("Cannot open specified file.");
return(int)ErrorNumber.CannotOpenFile;
}
try
{
IMediaImage imageFormat = ImageFormat.Detect(inputFilter);
if(imageFormat == null)
{
DicConsole.WriteLine("Image format not identified, not proceeding with analysis.");
return(int)ErrorNumber.UnrecognizedFormat;
}
if(verbose)
DicConsole.VerboseWriteLine("Image format identified by {0} ({1}).", imageFormat.Name,
imageFormat.Id);
else
DicConsole.WriteLine("Image format identified by {0}.", imageFormat.Name);
try
{
if(!imageFormat.Open(inputFilter))
{
DicConsole.WriteLine("Unable to open image format");
DicConsole.WriteLine("No error given");
return(int)ErrorNumber.CannotOpenFormat;
}
DicConsole.DebugWriteLine("Analyze command", "Correctly opened image file.");
}
catch(Exception ex)
{
DicConsole.ErrorWriteLine("Unable to open image format");
DicConsole.ErrorWriteLine("Error: {0}", ex.Message);
return(int)ErrorNumber.CannotOpenFormat;
}
Statistics.AddMediaFormat(imageFormat.Format);
Statistics.AddFilter(inputFilter.Name);
var sidecarClass = new Sidecar(imageFormat, imagePath, inputFilter.Id, encodingClass);
sidecarClass.InitProgressEvent += Progress.InitProgress;
sidecarClass.UpdateProgressEvent += Progress.UpdateProgress;
sidecarClass.EndProgressEvent += Progress.EndProgress;
sidecarClass.InitProgressEvent2 += Progress.InitProgress2;
sidecarClass.UpdateProgressEvent2 += Progress.UpdateProgress2;
sidecarClass.EndProgressEvent2 += Progress.EndProgress2;
sidecarClass.UpdateStatusEvent += Progress.UpdateStatus;
System.Console.CancelKeyPress += (sender, e) =>
{
e.Cancel = true;
sidecarClass.Abort();
};
CICMMetadataType sidecar = sidecarClass.Create();
DicConsole.WriteLine("Writing metadata sidecar");
var xmlFs =
new
FileStream(Path.Combine(Path.GetDirectoryName(imagePath) ?? throw new InvalidOperationException(), Path.GetFileNameWithoutExtension(imagePath) + ".cicm.xml"),
FileMode.CreateNew);
var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
xmlSer.Serialize(xmlFs, sidecar);
xmlFs.Close();
}
catch(Exception ex)
{
DicConsole.ErrorWriteLine($"Error reading file: {ex.Message}");
DicConsole.DebugWriteLine("Analyze command", ex.StackTrace);
return(int)ErrorNumber.UnexpectedException;
}
}
else if(Directory.Exists(imagePath))
{
if(!tape)
{
DicConsole.ErrorWriteLine("Cannot create a sidecar from a directory.");
return(int)ErrorNumber.ExpectedFile;
}
string[] contents = Directory.GetFiles(imagePath, "*", SearchOption.TopDirectoryOnly);
List<string> files = contents.Where(file => new FileInfo(file).Length % blockSize == 0).ToList();
files.Sort(StringComparer.CurrentCultureIgnoreCase);
var sidecarClass = new Sidecar();
sidecarClass.InitProgressEvent += Progress.InitProgress;
sidecarClass.UpdateProgressEvent += Progress.UpdateProgress;
sidecarClass.EndProgressEvent += Progress.EndProgress;
sidecarClass.InitProgressEvent2 += Progress.InitProgress2;
sidecarClass.UpdateProgressEvent2 += Progress.UpdateProgress2;
sidecarClass.EndProgressEvent2 += Progress.EndProgress2;
sidecarClass.UpdateStatusEvent += Progress.UpdateStatus;
CICMMetadataType sidecar = sidecarClass.BlockTape(Path.GetFileName(imagePath), files, blockSize);
DicConsole.WriteLine("Writing metadata sidecar");
var xmlFs =
new
FileStream(Path.Combine(Path.GetDirectoryName(imagePath) ?? throw new InvalidOperationException(), Path.GetFileNameWithoutExtension(imagePath) + ".cicm.xml"),
FileMode.CreateNew);
var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
xmlSer.Serialize(xmlFs, sidecar);
xmlFs.Close();
}
else
DicConsole.ErrorWriteLine("The specified input file cannot be found.");
return(int)ErrorNumber.NoError;
}
}
}

View File

@@ -0,0 +1,372 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Decode.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Verbs.
//
// --[ Description ] ----------------------------------------------------------
//
// Implements the 'decode' verb.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2019 Natalia Portillo
// ****************************************************************************/
using System.CommandLine;
using System.CommandLine.Invocation;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
using DiscImageChef.Core;
using DiscImageChef.Decoders.ATA;
using DiscImageChef.Decoders.CD;
using DiscImageChef.Decoders.SCSI;
namespace DiscImageChef.Commands.Image
{
internal class DecodeCommand : Command
{
public DecodeCommand() : base("decode", "Decodes and pretty prints disk and/or sector tags.")
{
Add(new Option(new[]
{
"--disk-tags", "-f"
}, "Decode disk tags.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--length", "-l"
}, "How many sectors to decode, or \"all\".")
{
Argument = new Argument<string>(() => "all"), Required = false
});
Add(new Option(new[]
{
"--sector-tags", "-p"
}, "Decode sector tags.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--start", "-s"
}, "Sector to start decoding from.")
{
Argument = new Argument<ulong>(() => 0), Required = false
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne, Description = "Media image path", Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)));
}
public static int Invoke(bool verbose, bool debug, bool diskTags, string imagePath, string length,
bool sectorTags, ulong startSector)
{
MainClass.PrintCopyright();
if(debug)
DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
if(verbose)
DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
Statistics.AddCommand("decode");
DicConsole.DebugWriteLine("Decode command", "--debug={0}", debug);
DicConsole.DebugWriteLine("Decode command", "--disk-tags={0}", diskTags);
DicConsole.DebugWriteLine("Decode command", "--input={0}", imagePath);
DicConsole.DebugWriteLine("Decode command", "--length={0}", length);
DicConsole.DebugWriteLine("Decode command", "--sector-tags={0}", sectorTags);
DicConsole.DebugWriteLine("Decode command", "--start={0}", startSector);
DicConsole.DebugWriteLine("Decode command", "--verbose={0}", verbose);
var filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(imagePath);
if(inputFilter == null)
{
DicConsole.ErrorWriteLine("Cannot open specified file.");
return(int)ErrorNumber.CannotOpenFile;
}
IMediaImage inputFormat = ImageFormat.Detect(inputFilter);
if(inputFormat == null)
{
DicConsole.ErrorWriteLine("Unable to recognize image format, not decoding");
return(int)ErrorNumber.UnrecognizedFormat;
}
inputFormat.Open(inputFilter);
Statistics.AddMediaFormat(inputFormat.Format);
Statistics.AddMedia(inputFormat.Info.MediaType, false);
Statistics.AddFilter(inputFilter.Name);
if(diskTags)
if(inputFormat.Info.ReadableMediaTags.Count == 0)
DicConsole.WriteLine("There are no disk tags in chosen disc image.");
else
foreach(MediaTagType tag in inputFormat.Info.ReadableMediaTags)
switch(tag)
{
case MediaTagType.SCSI_INQUIRY:
{
byte[] inquiry = inputFormat.ReadDiskTag(MediaTagType.SCSI_INQUIRY);
if(inquiry == null)
DicConsole.WriteLine("Error reading SCSI INQUIRY response from disc image");
else
{
DicConsole.WriteLine("SCSI INQUIRY command response:");
DicConsole.
WriteLine("================================================================================");
DicConsole.WriteLine(Inquiry.Prettify(inquiry));
DicConsole.
WriteLine("================================================================================");
}
break;
}
case MediaTagType.ATA_IDENTIFY:
{
byte[] identify = inputFormat.ReadDiskTag(MediaTagType.ATA_IDENTIFY);
if(identify == null)
DicConsole.WriteLine("Error reading ATA IDENTIFY DEVICE response from disc image");
else
{
DicConsole.WriteLine("ATA IDENTIFY DEVICE command response:");
DicConsole.
WriteLine("================================================================================");
DicConsole.WriteLine(Identify.Prettify(identify));
DicConsole.
WriteLine("================================================================================");
}
break;
}
case MediaTagType.ATAPI_IDENTIFY:
{
byte[] identify = inputFormat.ReadDiskTag(MediaTagType.ATAPI_IDENTIFY);
if(identify == null)
DicConsole.
WriteLine("Error reading ATA IDENTIFY PACKET DEVICE response from disc image");
else
{
DicConsole.WriteLine("ATA IDENTIFY PACKET DEVICE command response:");
DicConsole.
WriteLine("================================================================================");
DicConsole.WriteLine(Identify.Prettify(identify));
DicConsole.
WriteLine("================================================================================");
}
break;
}
case MediaTagType.CD_ATIP:
{
byte[] atip = inputFormat.ReadDiskTag(MediaTagType.CD_ATIP);
if(atip == null)
DicConsole.WriteLine("Error reading CD ATIP from disc image");
else
{
DicConsole.WriteLine("CD ATIP:");
DicConsole.
WriteLine("================================================================================");
DicConsole.WriteLine(ATIP.Prettify(atip));
DicConsole.
WriteLine("================================================================================");
}
break;
}
case MediaTagType.CD_FullTOC:
{
byte[] fullToc = inputFormat.ReadDiskTag(MediaTagType.CD_FullTOC);
if(fullToc == null)
DicConsole.WriteLine("Error reading CD full TOC from disc image");
else
{
DicConsole.WriteLine("CD full TOC:");
DicConsole.
WriteLine("================================================================================");
DicConsole.WriteLine(FullTOC.Prettify(fullToc));
DicConsole.
WriteLine("================================================================================");
}
break;
}
case MediaTagType.CD_PMA:
{
byte[] pma = inputFormat.ReadDiskTag(MediaTagType.CD_PMA);
if(pma == null)
DicConsole.WriteLine("Error reading CD PMA from disc image");
else
{
DicConsole.WriteLine("CD PMA:");
DicConsole.
WriteLine("================================================================================");
DicConsole.WriteLine(PMA.Prettify(pma));
DicConsole.
WriteLine("================================================================================");
}
break;
}
case MediaTagType.CD_SessionInfo:
{
byte[] sessionInfo = inputFormat.ReadDiskTag(MediaTagType.CD_SessionInfo);
if(sessionInfo == null)
DicConsole.WriteLine("Error reading CD session information from disc image");
else
{
DicConsole.WriteLine("CD session information:");
DicConsole.
WriteLine("================================================================================");
DicConsole.WriteLine(Session.Prettify(sessionInfo));
DicConsole.
WriteLine("================================================================================");
}
break;
}
case MediaTagType.CD_TEXT:
{
byte[] cdText = inputFormat.ReadDiskTag(MediaTagType.CD_TEXT);
if(cdText == null)
DicConsole.WriteLine("Error reading CD-TEXT from disc image");
else
{
DicConsole.WriteLine("CD-TEXT:");
DicConsole.
WriteLine("================================================================================");
DicConsole.WriteLine(CDTextOnLeadIn.Prettify(cdText));
DicConsole.
WriteLine("================================================================================");
}
break;
}
case MediaTagType.CD_TOC:
{
byte[] toc = inputFormat.ReadDiskTag(MediaTagType.CD_TOC);
if(toc == null)
DicConsole.WriteLine("Error reading CD TOC from disc image");
else
{
DicConsole.WriteLine("CD TOC:");
DicConsole.
WriteLine("================================================================================");
DicConsole.WriteLine(TOC.Prettify(toc));
DicConsole.
WriteLine("================================================================================");
}
break;
}
default:
DicConsole.WriteLine("Decoder for disk tag type \"{0}\" not yet implemented, sorry.",
tag);
break;
}
if(sectorTags)
{
if(length.ToLowerInvariant() == "all") { }
else
{
if(!ulong.TryParse(length, out ulong _))
{
DicConsole.WriteLine("Value \"{0}\" is not a valid number for length.", length);
DicConsole.WriteLine("Not decoding sectors tags");
return 3;
}
}
if(inputFormat.Info.ReadableSectorTags.Count == 0)
DicConsole.WriteLine("There are no sector tags in chosen disc image.");
else
foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags)
switch(tag)
{
default:
DicConsole.WriteLine("Decoder for disk tag type \"{0}\" not yet implemented, sorry.",
tag);
break;
}
// TODO: Not implemented
}
return(int)ErrorNumber.NoError;
}
}
}

View File

@@ -0,0 +1,160 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Entropy.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Verbs.
//
// --[ Description ] ----------------------------------------------------------
//
// Implements the 'entropy' verb.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2019 Natalia Portillo
// ****************************************************************************/
using System.CommandLine;
using System.CommandLine.Invocation;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
using DiscImageChef.Core;
namespace DiscImageChef.Commands.Image
{
internal class EntropyCommand : Command
{
public EntropyCommand() : base("entropy", "Calculates entropy and/or duplicated sectors of an image.")
{
Add(new Option(new[]
{
"--duplicated-sectors", "-p"
}, "Calculates how many sectors are duplicated (have same exact data in user area).")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--separated-tracks", "-t"
}, "Calculates entropy for each track separately.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--whole-disc", "-w"
}, "Calculates entropy for the whole disc.")
{
Argument = new Argument<bool>(() => true), Required = false
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne, Description = "Media image path", Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)));
}
public static int Invoke(bool debug, bool verbose, bool duplicatedSectors, string imagePath,
bool separatedTracks, bool wholeDisc)
{
MainClass.PrintCopyright();
if(debug)
DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
if(verbose)
DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
Statistics.AddCommand("entropy");
DicConsole.DebugWriteLine("Entropy command", "--debug={0}", debug);
DicConsole.DebugWriteLine("Entropy command", "--duplicated-sectors={0}", duplicatedSectors);
DicConsole.DebugWriteLine("Entropy command", "--input={0}", imagePath);
DicConsole.DebugWriteLine("Entropy command", "--separated-tracks={0}", separatedTracks);
DicConsole.DebugWriteLine("Entropy command", "--verbose={0}", verbose);
DicConsole.DebugWriteLine("Entropy command", "--whole-disc={0}", wholeDisc);
var filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(imagePath);
if(inputFilter == null)
{
DicConsole.ErrorWriteLine("Cannot open specified file.");
return(int)ErrorNumber.CannotOpenFile;
}
IMediaImage inputFormat = ImageFormat.Detect(inputFilter);
if(inputFormat == null)
{
DicConsole.ErrorWriteLine("Unable to recognize image format, not checksumming");
return(int)ErrorNumber.UnrecognizedFormat;
}
inputFormat.Open(inputFilter);
Statistics.AddMediaFormat(inputFormat.Format);
Statistics.AddMedia(inputFormat.Info.MediaType, false);
Statistics.AddFilter(inputFilter.Name);
var entropyCalculator = new Entropy(debug, verbose, inputFormat);
entropyCalculator.InitProgressEvent += Progress.InitProgress;
entropyCalculator.InitProgress2Event += Progress.InitProgress2;
entropyCalculator.UpdateProgressEvent += Progress.UpdateProgress;
entropyCalculator.UpdateProgress2Event += Progress.UpdateProgress2;
entropyCalculator.EndProgressEvent += Progress.EndProgress;
entropyCalculator.EndProgress2Event += Progress.EndProgress2;
if(separatedTracks)
{
EntropyResults[] tracksEntropy = entropyCalculator.CalculateTracksEntropy(duplicatedSectors);
foreach(EntropyResults trackEntropy in tracksEntropy)
{
DicConsole.WriteLine("Entropy for track {0} is {1:F4}.", trackEntropy.Track, trackEntropy.Entropy);
if(trackEntropy.UniqueSectors != null)
DicConsole.WriteLine("Track {0} has {1} unique sectors ({2:P3})", trackEntropy.Track,
trackEntropy.UniqueSectors,
(double)trackEntropy.UniqueSectors / (double)trackEntropy.Sectors);
}
}
if(!wholeDisc)
return(int)ErrorNumber.NoError;
EntropyResults entropy = entropyCalculator.CalculateMediaEntropy(duplicatedSectors);
DicConsole.WriteLine("Entropy for disk is {0:F4}.", entropy.Entropy);
if(entropy.UniqueSectors != null)
DicConsole.WriteLine("Disk has {0} unique sectors ({1:P3})", entropy.UniqueSectors,
(double)entropy.UniqueSectors / (double)entropy.Sectors);
return(int)ErrorNumber.NoError;
}
}
}

View File

@@ -0,0 +1,55 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : ImageFamily.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Verbs.
//
// --[ Description ] ----------------------------------------------------------
//
// Implements the 'image' verb.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2019 Natalia Portillo
// ****************************************************************************/
using System.CommandLine;
namespace DiscImageChef.Commands.Image
{
public class ImageFamily : Command
{
public ImageFamily() : base("image", "Commands to manage images")
{
AddAlias("i");
AddCommand(new AnalyzeCommand());
AddCommand(new ChecksumCommand());
AddCommand(new CompareCommand());
AddCommand(new ConvertImageCommand());
AddCommand(new CreateSidecarCommand());
AddCommand(new DecodeCommand());
AddCommand(new EntropyCommand());
AddCommand(new ImageInfoCommand());
AddCommand(new PrintHexCommand());
AddCommand(new VerifyCommand());
}
}
}

View File

@@ -0,0 +1,133 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : ImageInfo.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Verbs.
//
// --[ Description ] ----------------------------------------------------------
//
// Implements the 'image-info' verb.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.CommandLine;
using System.CommandLine.Invocation;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
using DiscImageChef.Core;
namespace DiscImageChef.Commands.Image
{
internal class ImageInfoCommand : Command
{
public ImageInfoCommand() : base("image-info",
"Opens a media image and shows information about the media it represents and metadata.")
{
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne, Description = "Media image path", Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)));
}
public static int Invoke(bool debug, bool verbose, string imagePath)
{
MainClass.PrintCopyright();
if(debug)
DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
if(verbose)
DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
Statistics.AddCommand("image-info");
DicConsole.DebugWriteLine("Analyze command", "--debug={0}", debug);
DicConsole.DebugWriteLine("Analyze command", "--input={0}", imagePath);
DicConsole.DebugWriteLine("Analyze command", "--verbose={0}", verbose);
var filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(imagePath);
if(inputFilter == null)
{
DicConsole.ErrorWriteLine("Cannot open specified file.");
return(int)ErrorNumber.CannotOpenFile;
}
try
{
IMediaImage imageFormat = ImageFormat.Detect(inputFilter);
if(imageFormat == null)
{
DicConsole.WriteLine("Image format not identified.");
return(int)ErrorNumber.UnrecognizedFormat;
}
DicConsole.WriteLine("Image format identified by {0} ({1}).", imageFormat.Name, imageFormat.Id);
DicConsole.WriteLine();
try
{
if(!imageFormat.Open(inputFilter))
{
DicConsole.WriteLine("Unable to open image format");
DicConsole.WriteLine("No error given");
return(int)ErrorNumber.CannotOpenFormat;
}
ImageInfo.PrintImageInfo(imageFormat);
Statistics.AddMediaFormat(imageFormat.Format);
Statistics.AddMedia(imageFormat.Info.MediaType, false);
Statistics.AddFilter(inputFilter.Name);
}
catch(Exception ex)
{
DicConsole.ErrorWriteLine("Unable to open image format");
DicConsole.ErrorWriteLine("Error: {0}", ex.Message);
DicConsole.DebugWriteLine("Image-info command", "Stack trace: {0}", ex.StackTrace);
return(int)ErrorNumber.CannotOpenFormat;
}
}
catch(Exception ex)
{
DicConsole.ErrorWriteLine($"Error reading file: {ex.Message}");
DicConsole.DebugWriteLine("Image-info command", ex.StackTrace);
return(int)ErrorNumber.UnexpectedException;
}
return(int)ErrorNumber.NoError;
}
}
}

View File

@@ -0,0 +1,160 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : PrintHex.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Verbs.
//
// --[ Description ] ----------------------------------------------------------
//
// Implements the 'printhex' verb.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2019 Natalia Portillo
// ****************************************************************************/
using System.CommandLine;
using System.CommandLine.Invocation;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
using DiscImageChef.Core;
namespace DiscImageChef.Commands.Image
{
internal class PrintHexCommand : Command
{
public PrintHexCommand() : base("printhex", "Prints a sector, in hexadecimal values, to the console.")
{
Add(new Option(new[]
{
"--length", "-l"
}, "How many sectors to print.")
{
Argument = new Argument<ulong>(() => 1), Required = false
});
Add(new Option(new[]
{
"--long-sectors", "-r"
}, "Print sectors with tags included.")
{
Argument = new Argument<bool>(() => false), Required = false
});
Add(new Option(new[]
{
"--start", "-s"
}, "Starting sector.")
{
Argument = new Argument<ulong>(), Required = true
});
Add(new Option(new[]
{
"--width", "-w"
}, "How many bytes to print per line.")
{
Argument = new Argument<ushort>(() => 32), Required = false
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne, Description = "Media image path", Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)));
}
public static int Invoke(bool debug, bool verbose, string imagePath, ulong length, bool longSectors,
ulong startSector, ushort widthBytes)
{
MainClass.PrintCopyright();
if(debug)
DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
if(verbose)
DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
Statistics.AddCommand("print-hex");
DicConsole.DebugWriteLine("PrintHex command", "--debug={0}", debug);
DicConsole.DebugWriteLine("PrintHex command", "--input={0}", imagePath);
DicConsole.DebugWriteLine("PrintHex command", "--length={0}", length);
DicConsole.DebugWriteLine("PrintHex command", "--long-sectors={0}", longSectors);
DicConsole.DebugWriteLine("PrintHex command", "--start={0}", startSector);
DicConsole.DebugWriteLine("PrintHex command", "--verbose={0}", verbose);
DicConsole.DebugWriteLine("PrintHex command", "--WidthBytes={0}", widthBytes);
var filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(imagePath);
if(inputFilter == null)
{
DicConsole.ErrorWriteLine("Cannot open specified file.");
return(int)ErrorNumber.CannotOpenFile;
}
IMediaImage inputFormat = ImageFormat.Detect(inputFilter);
if(inputFormat == null)
{
DicConsole.ErrorWriteLine("Unable to recognize image format, not verifying");
return(int)ErrorNumber.UnrecognizedFormat;
}
inputFormat.Open(inputFilter);
for(ulong i = 0; i < length; i++)
{
DicConsole.WriteLine("Sector {0}", startSector + i);
if(inputFormat.Info.ReadableSectorTags == null)
{
DicConsole.
WriteLine("Requested sectors with tags, unsupported by underlying image format, printing only user data.");
longSectors = false;
}
else
{
if(inputFormat.Info.ReadableSectorTags.Count == 0)
{
DicConsole.
WriteLine("Requested sectors with tags, unsupported by underlying image format, printing only user data.");
longSectors = false;
}
}
byte[] sector = longSectors ? inputFormat.ReadSectorLong(startSector + i)
: inputFormat.ReadSector(startSector + i);
PrintHex.PrintHexArray(sector, widthBytes);
}
return(int)ErrorNumber.NoError;
}
}
}

View File

@@ -0,0 +1,319 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Verify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Verbs.
//
// --[ Description ] ----------------------------------------------------------
//
// Implements the 'verify' verb.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Console;
using DiscImageChef.Core;
namespace DiscImageChef.Commands.Image
{
internal class VerifyCommand : Command
{
public VerifyCommand() : base("verify", "Verifies a disc image integrity, and if supported, sector integrity.")
{
Add(new Option(new[]
{
"--verify-disc", "-w"
}, "Verify disc image if supported.")
{
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--verify-sectors", "-s"
}, "Verify all sectors if supported.")
{
Argument = new Argument<bool>(() => true), Required = false
});
AddArgument(new Argument<string>
{
Arity = ArgumentArity.ExactlyOne, Description = "Disc image path", Name = "image-path"
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)));
}
public static int Invoke(bool debug, bool verbose, string imagePath, bool verifyDisc = true,
bool verifySectors = true)
{
MainClass.PrintCopyright();
if(debug)
DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
if(verbose)
DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
Statistics.AddCommand("verify");
DicConsole.DebugWriteLine("Verify command", "--debug={0}", debug);
DicConsole.DebugWriteLine("Verify command", "--input={0}", imagePath);
DicConsole.DebugWriteLine("Verify command", "--verbose={0}", verbose);
DicConsole.DebugWriteLine("Verify command", "--verify-disc={0}", verifyDisc);
DicConsole.DebugWriteLine("Verify command", "--verify-sectors={0}", verifySectors);
var filtersList = new FiltersList();
IFilter inputFilter = filtersList.GetFilter(imagePath);
if(inputFilter == null)
{
DicConsole.ErrorWriteLine("Cannot open specified file.");
return(int)ErrorNumber.CannotOpenFile;
}
IMediaImage inputFormat = ImageFormat.Detect(inputFilter);
if(inputFormat == null)
{
DicConsole.ErrorWriteLine("Unable to recognize image format, not verifying");
return(int)ErrorNumber.FormatNotFound;
}
inputFormat.Open(inputFilter);
Statistics.AddMediaFormat(inputFormat.Format);
Statistics.AddMedia(inputFormat.Info.MediaType, false);
Statistics.AddFilter(inputFilter.Name);
bool? correctImage = null;
long errorSectors = 0;
bool? correctSectors = null;
long unknownSectors = 0;
var verifiableImage = inputFormat as IVerifiableImage;
var verifiableSectorsImage = inputFormat as IVerifiableSectorsImage;
if(verifiableImage is null &&
verifiableSectorsImage is null)
{
DicConsole.ErrorWriteLine("The specified image does not support any kind of verification");
return(int)ErrorNumber.NotVerificable;
}
if(verifyDisc && verifiableImage != null)
{
DateTime startCheck = DateTime.UtcNow;
bool? discCheckStatus = verifiableImage.VerifyMediaImage();
DateTime endCheck = DateTime.UtcNow;
TimeSpan checkTime = endCheck - startCheck;
switch(discCheckStatus)
{
case true:
DicConsole.WriteLine("Disc image checksums are correct");
break;
case false:
DicConsole.WriteLine("Disc image checksums are incorrect");
break;
case null:
DicConsole.WriteLine("Disc image does not contain checksums");
break;
}
correctImage = discCheckStatus;
DicConsole.VerboseWriteLine("Checking disc image checksums took {0} seconds", checkTime.TotalSeconds);
}
if(verifySectors)
{
DateTime startCheck = DateTime.Now;
DateTime endCheck = startCheck;
List<ulong> failingLbas = new List<ulong>();
List<ulong> unknownLbas = new List<ulong>();
if(verifiableSectorsImage is IOpticalMediaImage opticalMediaImage)
{
List<Track> inputTracks = opticalMediaImage.Tracks;
ulong currentSectorAll = 0;
startCheck = DateTime.UtcNow;
foreach(Track currentTrack in inputTracks)
{
ulong remainingSectors = currentTrack.TrackEndSector - currentTrack.TrackStartSector;
ulong currentSector = 0;
while(remainingSectors > 0)
{
DicConsole.Write("\rChecking sector {0} of {1}, on track {2}", currentSectorAll,
inputFormat.Info.Sectors, currentTrack.TrackSequence);
List<ulong> tempFailingLbas;
List<ulong> tempUnknownLbas;
if(remainingSectors < 512)
opticalMediaImage.VerifySectors(currentSector, (uint)remainingSectors,
currentTrack.TrackSequence, out tempFailingLbas,
out tempUnknownLbas);
else
opticalMediaImage.VerifySectors(currentSector, 512, currentTrack.TrackSequence,
out tempFailingLbas, out tempUnknownLbas);
failingLbas.AddRange(tempFailingLbas);
unknownLbas.AddRange(tempUnknownLbas);
if(remainingSectors < 512)
{
currentSector += remainingSectors;
currentSectorAll += remainingSectors;
remainingSectors = 0;
}
else
{
currentSector += 512;
currentSectorAll += 512;
remainingSectors -= 512;
}
}
}
endCheck = DateTime.UtcNow;
}
else if(verifiableSectorsImage != null)
{
ulong remainingSectors = inputFormat.Info.Sectors;
ulong currentSector = 0;
startCheck = DateTime.UtcNow;
while(remainingSectors > 0)
{
DicConsole.Write("\rChecking sector {0} of {1}", currentSector, inputFormat.Info.Sectors);
List<ulong> tempFailingLbas;
List<ulong> tempUnknownLbas;
if(remainingSectors < 512)
verifiableSectorsImage.VerifySectors(currentSector, (uint)remainingSectors,
out tempFailingLbas, out tempUnknownLbas);
else
verifiableSectorsImage.VerifySectors(currentSector, 512, out tempFailingLbas,
out tempUnknownLbas);
failingLbas.AddRange(tempFailingLbas);
unknownLbas.AddRange(tempUnknownLbas);
if(remainingSectors < 512)
{
currentSector += remainingSectors;
remainingSectors = 0;
}
else
{
currentSector += 512;
remainingSectors -= 512;
}
}
endCheck = DateTime.UtcNow;
}
TimeSpan checkTime = endCheck - startCheck;
DicConsole.Write("\r" + new string(' ', System.Console.WindowWidth - 1) + "\r");
if(unknownSectors > 0)
DicConsole.WriteLine("There is at least one sector that does not contain a checksum");
if(errorSectors > 0)
DicConsole.WriteLine("There is at least one sector with incorrect checksum or errors");
if(unknownSectors == 0 &&
errorSectors == 0)
DicConsole.WriteLine("All sector checksums are correct");
DicConsole.VerboseWriteLine("Checking sector checksums took {0} seconds", checkTime.TotalSeconds);
if(verbose)
{
DicConsole.VerboseWriteLine("LBAs with error:");
if(failingLbas.Count == (int)inputFormat.Info.Sectors)
DicConsole.VerboseWriteLine("\tall sectors.");
else
foreach(ulong t in failingLbas)
DicConsole.VerboseWriteLine("\t{0}", t);
DicConsole.WriteLine("LBAs without checksum:");
if(unknownLbas.Count == (int)inputFormat.Info.Sectors)
DicConsole.VerboseWriteLine("\tall sectors.");
else
foreach(ulong t in unknownLbas)
DicConsole.VerboseWriteLine("\t{0}", t);
}
DicConsole.WriteLine("Total sectors........... {0}", inputFormat.Info.Sectors);
DicConsole.WriteLine("Total errors............ {0}", failingLbas.Count);
DicConsole.WriteLine("Total unknowns.......... {0}", unknownLbas.Count);
DicConsole.WriteLine("Total errors+unknowns... {0}", failingLbas.Count + unknownLbas.Count);
if(failingLbas.Count > 0)
correctSectors = false;
else if((ulong)unknownLbas.Count < inputFormat.Info.Sectors)
correctSectors = true;
}
switch(correctImage)
{
case null when correctSectors is null: return(int)ErrorNumber.NotVerificable;
case null when correctSectors == false: return(int)ErrorNumber.BadSectorsImageNotVerified;
case null when correctSectors == true: return(int)ErrorNumber.CorrectSectorsImageNotVerified;
case false when correctSectors is null: return(int)ErrorNumber.BadImageSectorsNotVerified;
case false when correctSectors == false: return(int)ErrorNumber.BadImageBadSectors;
case false when correctSectors == true: return(int)ErrorNumber.CorrectSectorsBadImage;
case true when correctSectors is null: return(int)ErrorNumber.CorrectImageSectorsNotVerified;
case true when correctSectors == false: return(int)ErrorNumber.CorrectImageBadSectors;
case true when correctSectors == true: return(int)ErrorNumber.NoError;
}
return(int)ErrorNumber.NoError;
}
}
}