using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using BurnOutSharp.External.psxt001z;
using MPF.CueSheets;
using MPF.Data;
using MPF.Utilities;
namespace MPF.DiscImageCreator
{
///
/// Represents a generic set of DiscImageCreator parameters
///
public class Parameters : BaseParameters
{
#region Generic Dumping Information
///
public override string InputPath => DriveLetter;
///
public override string OutputPath => Filename;
///
///
public override int? Speed
{
get { return DriveSpeed; }
set { DriveSpeed = (sbyte?)value; }
}
#endregion
#region Metadata
///
/// Base command to run
///
public Command BaseCommand { get; set; }
///
public override InternalProgram InternalProgram => InternalProgram.DiscImageCreator;
#endregion
///
/// Set of flags to pass to the executable
///
protected Dictionary _flags = new Dictionary();
public bool? this[Flag key]
{
get
{
if (_flags.ContainsKey(key))
return _flags[key];
return null;
}
set
{
_flags[key] = value;
}
}
protected internal IEnumerable Keys => _flags.Keys;
#region Common Input Values
///
/// Drive letter or path to pass to DiscImageCreator
///
public string DriveLetter { get; set; }
///
/// Drive speed to set, if applicable
///
public int? DriveSpeed { get; set; }
///
/// Destination filename for DiscImageCreator output
///
public string Filename { get; set; }
///
/// Optiarc drive output filename for merging
///
public string OptiarcFilename { get; set; }
///
/// Start LBA value for dumping specific sectors
///
public int? StartLBAValue { get; set; }
///
/// End LBA value for dumping specific sectors
///
public int? EndLBAValue { get; set; }
#endregion
#region Flag Values
///
/// Manual offset for Audio CD (default 0)
///
public int? AddOffsetValue { get; set; }
///
/// 0xbe opcode value for dumping
/// Possible values: raw (default), pack
///
/// TODO: Make this an enum
public string BEOpcodeValue { get; set; }
///
/// C2 reread options for dumping
/// [0] - Reread value (default 4000)
/// [1] - 0 reread issue sector (default), 1 reread all
/// [2] - First LBA to reread (default 0)
/// [3] - Last LBA to reread (default EOS)
///
public int?[] C2OpcodeValue { get; set; } = new int?[4];
///
/// End LBA for fixing
///
public int? FixValue { get; set; }
///
/// Set the force unit access flag value (default 1)
///
public int? ForceUnitAccessValue { get; set; }
///
/// Set the no skip security sector flag value (default 100)
///
public int? NoSkipSecuritySectorValue { get; set; }
///
/// Set the pad sector flag value (default 0)
///
public int? PadSectorValue { get; set; }
///
/// Set the reverse End LBA value (required for DVD)
///
public int? ReverseEndLBAValue { get; set; }
///
/// Set the reverse Start LBA value (required for DVD)
///
public int? ReverseStartLBAValue { get; set; }
///
/// Set scan file timeout value (default 60)
///
public int? ScanFileProtectValue { get; set; }
///
/// Beginning and ending sectors to skip for physical protection (both default 0)
///
public int?[] SkipSectorValue { get; set; } = new int?[2];
///
/// Set the subchanel read level
/// Possible values: 0 no next sub, 1 next sub (default), 2 next and next next
///
public int? SubchannelReadLevelValue { get; set; }
///
/// Set number of empty bytes to insert at the head of first track for VideoNow (default 0)
///
public int? VideoNowValue { get; set; }
#endregion
///
public Parameters(string parameters) : base(parameters) { }
///
public Parameters(KnownSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
{
}
#region BaseParameters Implementations
///
public override (bool, List) CheckAllOutputFilesExist(string basePath)
{
/*
If there are no external programs, such as error checking, etc., DIC outputs
a slightly different set of files. This reduced set needs to be documented in
order for special use cases, such as self-built versions of DIC or removed
helper programs, can be detected to the best of our ability. Below is the list
of files that are generated in that case:
.bin
.c2
.ccd
.cue
.img/.imgtmp
.scm/.scmtmp
.sub/.subtmp
_cmd.txt (formerly)
_img.cue
This list needs to be translated into the minimum viable set of information
such that things like error checking can be passed back as a flag, or some
similar method.
Here are some notes about the various output files and what they represent:
- bin - Final split output disc image (CD/GD only)
- c2 - Represents each byte per sector as one bit; 0 means no error, 1 means error
- c2Error - Human-readable version of `c2`; only errors are printed
- ccd - CloneCD control file referencing the `img` file
- cmd - Represents the commandline that was run
- cue - CDRWIN cuesheet referencing the `bin` file(s)
- dat - Logiqx datfile referencing the `bin` file(s)
- disc - Disc metadata and information
- drive - Drive metadata and information
- img - CloneCD output disc image (CD/GD only)
- img.cue - CDRWIN cuesheet referencing the `img` file
- img_EdcEcc - ECC check output as run on the `img` file
- iso - Final output disc image (DVD/BD only)
- mainError - Read, drive, or system errors
- mainInfo - ISOBuster-formatted sector information
- scm - Scrambled disc image
- sub - Binary subchannel data as read from the disc
- subError - Subchannel read errors
- subInfo - Subchannel informational messages
- subIntention - Subchannel intentional error information
- subReadable - Human-readable version of `sub`
- volDesc - Volume descriptor information
*/
List missingFiles = new List();
switch (this.Type)
{
case MediaType.CDROM:
case MediaType.GDROM: // TODO: Verify GD-ROM outputs this
if (!File.Exists($"{basePath}.ccd"))
missingFiles.Add($"{basePath}.ccd");
if (!File.Exists($"{basePath}.cue"))
missingFiles.Add($"{basePath}.cue");
if (!File.Exists($"{basePath}.dat"))
missingFiles.Add($"{basePath}.dat");
if (!File.Exists($"{basePath}.img") && !File.Exists($"{basePath}.imgtmp"))
missingFiles.Add($"{basePath}.img");
if (!File.Exists($"{basePath}.sub") && !File.Exists($"{basePath}.subtmp"))
missingFiles.Add($"{basePath}.sub");
if (!File.Exists($"{basePath}_disc.txt"))
missingFiles.Add($"{basePath}_disc.txt");
if (!File.Exists($"{basePath}_drive.txt"))
missingFiles.Add($"{basePath}_drive.txt");
if (!File.Exists($"{basePath}_img.cue"))
missingFiles.Add($"{basePath}_img.cue");
if (!File.Exists($"{basePath}_mainError.txt"))
missingFiles.Add($"{basePath}_mainError.txt");
if (!File.Exists($"{basePath}_mainInfo.txt"))
missingFiles.Add($"{basePath}_mainInfo.txt");
if (!File.Exists($"{basePath}_subError.txt"))
missingFiles.Add($"{basePath}_subError.txt");
if (!File.Exists($"{basePath}_subInfo.txt"))
missingFiles.Add($"{basePath}_subInfo.txt");
if (!File.Exists($"{basePath}_subReadable.txt") && !File.Exists($"{basePath}_sub.txt"))
missingFiles.Add($"{basePath}_subReadable.txt");
if (!File.Exists($"{basePath}_volDesc.txt"))
missingFiles.Add($"{basePath}_volDesc.txt");
// Audio-only discs don't output these files
if (!this.System.IsAudio())
{
if (!File.Exists($"{basePath}.img_EdcEcc.txt") && !File.Exists($"{basePath}.img_EccEdc.txt"))
missingFiles.Add($"{basePath}.img_EdcEcc.txt");
if (!File.Exists($"{basePath}.scm") && !File.Exists($"{basePath}.scmtmp"))
missingFiles.Add($"{basePath}.scm");
}
// Removed or inconsistent files
if (false)
{
// Doesn't output on Linux
if (!File.Exists($"{basePath}.c2"))
missingFiles.Add($"{basePath}.c2");
// Doesn't output on Linux
if (!File.Exists($"{basePath}_c2Error.txt"))
missingFiles.Add($"{basePath}_c2Error.txt");
// Replaced by timestamp-named file
if (!File.Exists($"{basePath}_cmd.txt"))
missingFiles.Add($"{basePath}_cmd.txt");
// Not guaranteed output
if (!File.Exists($"{basePath}_subIntention.txt"))
missingFiles.Add($"{basePath}_subIntention.txt");
}
break;
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (!File.Exists($"{basePath}.dat"))
missingFiles.Add($"{basePath}.dat");
if (!File.Exists($"{basePath}_disc.txt"))
missingFiles.Add($"{basePath}_disc.txt");
if (!File.Exists($"{basePath}_drive.txt"))
missingFiles.Add($"{basePath}_drive.txt");
if (!File.Exists($"{basePath}_mainError.txt"))
missingFiles.Add($"{basePath}_mainError.txt");
if (!File.Exists($"{basePath}_mainInfo.txt"))
missingFiles.Add($"{basePath}_mainInfo.txt");
if (!File.Exists($"{basePath}_volDesc.txt"))
missingFiles.Add($"{basePath}_volDesc.txt");
// Removed or inconsistent files
if (false)
{
// Replaced by timestamp-named file
if (!File.Exists($"{basePath}_cmd.txt"))
missingFiles.Add($"{basePath}_cmd.txt");
}
break;
case MediaType.FloppyDisk:
case MediaType.HardDisk:
// TODO: Determine what outputs come out from a HDD, SD, etc.
if (!File.Exists($"{basePath}.dat"))
missingFiles.Add($"{basePath}.dat");
if (!File.Exists($"{basePath}_disc.txt"))
missingFiles.Add($"{basePath}_disc.txt");
// Removed or inconsistent files
if (false)
{
// Replaced by timestamp-named file
if (!File.Exists($"{basePath}_cmd.txt"))
missingFiles.Add($"{basePath}_cmd.txt");
}
break;
default:
return (false, missingFiles);
}
return (!missingFiles.Any(), missingFiles);
}
///
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive)
{
string outputDirectory = Path.GetDirectoryName(basePath);
// Fill in the hash data
info.TracksAndWriteOffsets.ClrMameProData = GetDatfile(basePath + ".dat");
// Extract info based generically on MediaType
switch (this.Type)
{
case MediaType.CDROM:
case MediaType.GDROM: // TODO: Verify GD-ROM outputs this
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? "Disc has no PVD"; ;
// Audio-only discs will fail if there are any C2 errors, so they would never get here
if (this.System.IsAudio())
{
info.CommonDiscInfo.ErrorsCount = "0";
}
else
{
long errorCount = -1;
if (File.Exists(basePath + ".img_EdcEcc.txt"))
errorCount = GetErrorCount(basePath + ".img_EdcEcc.txt");
else if (File.Exists(basePath + ".img_EccEdc.txt"))
errorCount = GetErrorCount(basePath + ".img_EccEdc.txt");
info.CommonDiscInfo.ErrorsCount = (errorCount == -1 ? "Error retrieving error count" : errorCount.ToString());
}
info.TracksAndWriteOffsets.Cuesheet = GetFullFile(basePath + ".cue") ?? "";
var cueSheet = new CueSheet(basePath + ".cue"); // TODO: Do something with this
string cdWriteOffset = GetWriteOffset(basePath + "_disc.txt") ?? "";
info.CommonDiscInfo.RingWriteOffset = cdWriteOffset;
info.TracksAndWriteOffsets.OtherWriteOffsets = cdWriteOffset;
break;
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
// Get the individual hash data, as per internal
if (GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out string crc32, out string md5, out string sha1))
{
info.SizeAndChecksums.Size = size;
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
}
// Deal with the layerbreaks
if (this.Type == MediaType.DVD)
{
string layerbreak = GetLayerbreak(basePath + "_disc.txt", System.IsXGD()) ?? "";
info.SizeAndChecksums.Layerbreak = !string.IsNullOrEmpty(layerbreak) ? Int64.Parse(layerbreak) : default;
}
else if (this.Type == MediaType.BluRay)
{
if (GetLayerbreak(Path.Combine(outputDirectory, "PIC.bin"), out long? layerbreak1, out long? layerbreak2, out long? layerbreak3))
{
if (layerbreak1 != null && layerbreak1 * 2048 < info.SizeAndChecksums.Size)
info.SizeAndChecksums.Layerbreak = layerbreak1.Value;
if (layerbreak2 != null && layerbreak2 * 2048 < info.SizeAndChecksums.Size)
info.SizeAndChecksums.Layerbreak2 = layerbreak2.Value;
if (layerbreak3 != null && layerbreak3 * 2048 < info.SizeAndChecksums.Size)
info.SizeAndChecksums.Layerbreak3 = layerbreak3.Value;
}
}
// Read the PVD
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? "";
// Bluray-specific options
if (this.Type == MediaType.BluRay)
info.Extras.PIC = GetPIC(Path.Combine(outputDirectory, "PIC.bin")) ?? "";
break;
}
// Extract info based specifically on KnownSystem
switch (this.System)
{
case KnownSystem.AppleMacintosh:
case KnownSystem.EnhancedCD:
case KnownSystem.IBMPCCompatible:
case KnownSystem.RainbowDisc:
if (File.Exists(basePath + "_subIntention.txt"))
{
FileInfo fi = new FileInfo(basePath + "_subIntention.txt");
if (fi.Length > 0)
info.CopyProtection.SecuROMData = GetFullFile(basePath + "_subIntention.txt") ?? "";
}
break;
case KnownSystem.DVDAudio:
case KnownSystem.DVDVideo:
info.CopyProtection.Protection = GetDVDProtection(basePath + "_CSSKey.txt", basePath + "_disc.txt") ?? "";
break;
case KnownSystem.KonamiPython2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out RedumpRegion? pythonTwoRegion, out string pythonTwoDate))
{
info.CommonDiscInfo.Comments += $"Internal Disc Serial: {pythonTwoSerial}\n";
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
}
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
break;
case KnownSystem.MicrosoftXBOX:
if (GetXgdAuxInfo(basePath + "_disc.txt", out string dmihash, out string pfihash, out string sshash, out string ss, out string ssver))
{
info.CommonDiscInfo.Comments += $"{Template.XBOXDMIHash}: {dmihash ?? ""}\n" +
$"{Template.XBOXPFIHash}: {pfihash ?? ""}\n" +
$"{Template.XBOXSSHash}: {sshash ?? ""}\n" +
$"{Template.XBOXSSVersion}: {ssver ?? ""}\n";
info.Extras.SecuritySectorRanges = ss ?? "";
}
if (GetXboxDMIInfo(Path.Combine(outputDirectory, "DMI.bin"), out string serial, out string version, out RedumpRegion? region))
{
info.CommonDiscInfo.Serial = serial ?? "";
info.VersionAndEditions.Version = version ?? "";
info.CommonDiscInfo.Region = region;
}
break;
case KnownSystem.MicrosoftXBOX360:
if (GetXgdAuxInfo(basePath + "_disc.txt", out string dmi360hash, out string pfi360hash, out string ss360hash, out string ss360, out string ssver360))
{
info.CommonDiscInfo.Comments += $"{Template.XBOXDMIHash}: {dmi360hash ?? ""}\n" +
$"{Template.XBOXPFIHash}: {pfi360hash ?? ""}\n" +
$"{Template.XBOXSSHash}: {ss360hash ?? ""}\n" +
$"{Template.XBOXSSVersion}: {ssver360 ?? ""}\n";
info.Extras.SecuritySectorRanges = ss360 ?? "";
}
if (GetXbox360DMIInfo(Path.Combine(outputDirectory, "DMI.bin"), out string serial360, out string version360, out RedumpRegion? region360))
{
info.CommonDiscInfo.Serial = serial360 ?? "";
info.VersionAndEditions.Version = version360 ?? "";
info.CommonDiscInfo.Region = region360;
}
break;
case KnownSystem.NamcoSegaNintendoTriforce:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
// Take only the first 16 lines for GD-ROM
if (!string.IsNullOrEmpty(info.Extras.Header))
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
info.VersionAndEditions.Version = gdVersion ?? "";
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
}
}
break;
case KnownSystem.SegaCDMegaCD:
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
// Take only the last 16 lines for Sega CD
if (!string.IsNullOrEmpty(info.Extras.Header))
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Skip(16));
if (GetSegaCDBuildInfo(info.Extras.Header, out string scdSerial, out string fixedDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {scdSerial ?? ""}";
info.CommonDiscInfo.EXEDateBuildDate = fixedDate ?? "";
}
break;
case KnownSystem.SegaChihiro:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
// Take only the first 16 lines for GD-ROM
if (!string.IsNullOrEmpty(info.Extras.Header))
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
info.VersionAndEditions.Version = gdVersion ?? "";
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
}
}
break;
case KnownSystem.SegaDreamcast:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
// Take only the first 16 lines for GD-ROM
if (!string.IsNullOrEmpty(info.Extras.Header))
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
info.VersionAndEditions.Version = gdVersion ?? "";
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
}
}
break;
case KnownSystem.SegaNaomi:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
// Take only the first 16 lines for GD-ROM
if (!string.IsNullOrEmpty(info.Extras.Header))
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
info.VersionAndEditions.Version = gdVersion ?? "";
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
}
}
break;
case KnownSystem.SegaNaomi2:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
// Take only the first 16 lines for GD-ROM
if (!string.IsNullOrEmpty(info.Extras.Header))
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
info.VersionAndEditions.Version = gdVersion ?? "";
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
}
}
break;
case KnownSystem.SegaSaturn:
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
// Take only the first 16 lines for Saturn
if (!string.IsNullOrEmpty(info.Extras.Header))
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
if (GetSaturnBuildInfo(info.Extras.Header, out string saturnSerial, out string saturnVersion, out string buildDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {saturnSerial ?? ""}";
info.VersionAndEditions.Version = saturnVersion ?? "";
info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? "";
}
break;
case KnownSystem.SonyPlayStation:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out RedumpRegion? playstationRegion, out string playstationDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {playstationSerial ?? ""}\n";
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
}
bool? psEdcStatus = null;
if (File.Exists(basePath + ".img_EdcEcc.txt"))
psEdcStatus = GetPlayStationEDCStatus(basePath + ".img_EdcEcc.txt");
else if (File.Exists(basePath + ".img_EccEdc.txt"))
psEdcStatus = GetPlayStationEDCStatus(basePath + ".img_EccEdc.txt");
if (psEdcStatus == true)
info.EDC.EDC = YesNo.Yes;
else if (psEdcStatus == false)
info.EDC.EDC = YesNo.No;
else
info.EDC.EDC = YesNo.NULL;
info.CopyProtection.AntiModchip = GetPlayStationAntiModchipDetected(basePath + "_disc.txt") ? YesNo.Yes : YesNo.No;
bool? psLibCryptStatus = GetLibCryptDetected(basePath + ".sub");
if (psLibCryptStatus == true)
{
// Guard against false positives
if (File.Exists(basePath + "_subIntention.txt"))
{
string libCryptData = GetFullFile(basePath + "_subIntention.txt") ?? "";
if (string.IsNullOrEmpty(libCryptData))
{
info.CopyProtection.LibCrypt = YesNo.No;
}
else
{
info.CopyProtection.LibCrypt = YesNo.Yes;
info.CopyProtection.LibCryptData = libCryptData;
}
}
else
{
info.CopyProtection.LibCrypt = YesNo.No;
}
}
else if (psLibCryptStatus == false)
{
info.CopyProtection.LibCrypt = YesNo.No;
}
else
{
info.CopyProtection.LibCrypt = YesNo.NULL;
info.CopyProtection.LibCryptData = "LibCrypt could not be detected because subchannel file is missing";
}
break;
case KnownSystem.SonyPlayStation2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out RedumpRegion? playstationTwoRegion, out string playstationTwoDate))
{
info.CommonDiscInfo.Comments += $"Internal Disc Serial: {playstationTwoSerial}\n";
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
}
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
break;
case KnownSystem.SonyPlayStation4:
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
break;
case KnownSystem.SonyPlayStation5:
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
break;
}
// Fill in any artifacts that exist, Base64-encoded
//if (File.Exists(basePath + ".c2"))
// info.Artifacts["c2"] = Convert.ToBase64String(File.ReadAllBytes(basePath + ".c2"));
if (File.Exists(basePath + "_c2Error.txt"))
info.Artifacts["c2Error"] = GetBase64(GetFullFile(basePath + "_c2Error.txt"));
if (File.Exists(basePath + ".ccd"))
info.Artifacts["ccd"] = GetBase64(GetFullFile(basePath + ".ccd"));
if (File.Exists(basePath + "_cmd.txt")) // TODO: Figure out how to read in the timestamp-named file
info.Artifacts["cmd"] = GetBase64(GetFullFile(basePath + "_cmd.txt"));
if (File.Exists(basePath + ".cue"))
info.Artifacts["cue"] = GetBase64(GetFullFile(basePath + ".cue"));
if (File.Exists(basePath + ".dat"))
info.Artifacts["dat"] = GetBase64(GetFullFile(basePath + ".dat"));
if (File.Exists(basePath + "_disc.txt"))
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt"));
//if (File.Exists(Path.Combine(outputDirectory, "DMI.bin")))
// info.Artifacts["dmi"] = Convert.ToBase64String(File.ReadAllBytes(Path.Combine(outputDirectory, "DMI.bin")));
if (File.Exists(basePath + "_drive.txt"))
info.Artifacts["drive"] = GetBase64(GetFullFile(basePath + "_drive.txt"));
if (File.Exists(basePath + "_img.cue"))
info.Artifacts["img_cue"] = GetBase64(GetFullFile(basePath + "_img.cue"));
if (File.Exists(basePath + ".img_EdcEcc.txt"))
info.Artifacts["img_EdcEcc"] = GetBase64(GetFullFile(basePath + ".img_EdcEcc.txt"));
if (File.Exists(basePath + ".img_EccEdc.txt"))
info.Artifacts["img_EdcEcc"] = GetBase64(GetFullFile(basePath + ".img_EccEdc.txt"));
if (File.Exists(basePath + "_mainError.txt"))
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt"));
if (File.Exists(basePath + "_mainInfo.txt"))
info.Artifacts["mainInfo"] = GetBase64(GetFullFile(basePath + "_mainInfo.txt"));
//if (File.Exists(Path.Combine(outputDirectory, "PFI.bin")))
// info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes(Path.Combine(outputDirectory, "PFI.bin")));
//if (File.Exists(Path.Combine(outputDirectory, "SS.bin")))
// info.Artifacts["ss"] = Convert.ToBase64String(File.ReadAllBytes(Path.Combine(outputDirectory, "SS.bin")));
if (File.Exists(basePath + ".sub"))
info.Artifacts["sub"] = Convert.ToBase64String(File.ReadAllBytes(basePath + ".sub"));
if (File.Exists(basePath + "_subError.txt"))
info.Artifacts["subError"] = GetBase64(GetFullFile(basePath + "_subError.txt"));
if (File.Exists(basePath + "_subInfo.txt"))
info.Artifacts["subInfo"] = GetBase64(GetFullFile(basePath + "_subInfo.txt"));
if (File.Exists(basePath + "_subIntention.txt"))
info.Artifacts["subIntention"] = GetBase64(GetFullFile(basePath + "_subIntention.txt"));
//if (File.Exists(basePath + "_sub.txt"))
// info.Artifacts["subReadable"] = GetBase64(GetFullFile(basePath + "_sub.txt"));
//if (File.Exists(basePath + "_subReadable.txt"))
// info.Artifacts["subReadable"] = GetBase64(GetFullFile(basePath + "_subReadable.txt"));
if (File.Exists(basePath + "_volDesc.txt"))
info.Artifacts["volDesc"] = GetBase64(GetFullFile(basePath + "_volDesc.txt"));
}
///
public override string GenerateParameters()
{
List parameters = new List();
if (BaseCommand != Command.NONE)
parameters.Add(Converters.LongName(BaseCommand));
else
return null;
// Drive Letter
if (BaseCommand == Command.Audio
|| BaseCommand == Command.BluRay
|| BaseCommand == Command.Close
|| BaseCommand == Command.CompactDisc
|| BaseCommand == Command.Data
|| BaseCommand == Command.DigitalVideoDisc
|| BaseCommand == Command.Disk
|| BaseCommand == Command.DriveSpeed
|| BaseCommand == Command.Eject
|| BaseCommand == Command.Floppy
|| BaseCommand == Command.GDROM
|| BaseCommand == Command.Reset
|| BaseCommand == Command.SACD
|| BaseCommand == Command.Start
|| BaseCommand == Command.Stop
|| BaseCommand == Command.Swap
|| BaseCommand == Command.XBOX
|| BaseCommand == Command.XBOXSwap
|| BaseCommand == Command.XGD2Swap
|| BaseCommand == Command.XGD3Swap)
{
if (DriveLetter != null)
parameters.Add(DriveLetter);
else
return null;
}
// Filename
if (BaseCommand == Command.Audio
|| BaseCommand == Command.BluRay
|| BaseCommand == Command.CompactDisc
|| BaseCommand == Command.Data
|| BaseCommand == Command.DigitalVideoDisc
|| BaseCommand == Command.Disk
|| BaseCommand == Command.Floppy
|| BaseCommand == Command.GDROM
|| BaseCommand == Command.MDS
|| BaseCommand == Command.Merge
|| BaseCommand == Command.SACD
|| BaseCommand == Command.Swap
|| BaseCommand == Command.Sub
|| BaseCommand == Command.Tape
|| BaseCommand == Command.XBOX
|| BaseCommand == Command.XBOXSwap
|| BaseCommand == Command.XGD2Swap
|| BaseCommand == Command.XGD3Swap)
{
if (Filename != null)
parameters.Add("\"" + Filename.Trim('"') + "\"");
else
return null;
}
// Optiarc Filename
if (BaseCommand == Command.Merge)
{
if (OptiarcFilename != null)
parameters.Add("\"" + OptiarcFilename.Trim('"') + "\"");
else
return null;
}
// Drive Speed
if (BaseCommand == Command.Audio
|| BaseCommand == Command.BluRay
|| BaseCommand == Command.CompactDisc
|| BaseCommand == Command.Data
|| BaseCommand == Command.DigitalVideoDisc
|| BaseCommand == Command.GDROM
|| BaseCommand == Command.SACD
|| BaseCommand == Command.Swap
|| BaseCommand == Command.XBOX
|| BaseCommand == Command.XBOXSwap
|| BaseCommand == Command.XGD2Swap
|| BaseCommand == Command.XGD3Swap)
{
if (DriveSpeed != null)
parameters.Add(DriveSpeed.ToString());
else
return null;
}
// LBA Markers
if (BaseCommand == Command.Audio
|| BaseCommand == Command.Data)
{
if (StartLBAValue != null && StartLBAValue > 0
&& EndLBAValue != null && EndLBAValue > 0)
{
parameters.Add(StartLBAValue.ToString());
parameters.Add(EndLBAValue.ToString());
}
else
return null;
}
// Add Offset
if (GetSupportedCommands(Flag.AddOffset).Contains(BaseCommand))
{
if (this[Flag.AddOffset] == true)
{
parameters.Add(Converters.LongName(Flag.AddOffset));
if (AddOffsetValue != null)
parameters.Add(AddOffsetValue.ToString());
}
}
// AMSF Dumping
if (GetSupportedCommands(Flag.AMSF).Contains(BaseCommand))
{
if (this[Flag.AMSF] == true)
parameters.Add(Converters.LongName(Flag.AMSF));
}
// Atari Jaguar CD
if (GetSupportedCommands(Flag.AtariJaguar).Contains(BaseCommand))
{
if (this[Flag.AtariJaguar] == true)
parameters.Add(Converters.LongName(Flag.AtariJaguar));
}
// BE Opcode
if (GetSupportedCommands(Flag.BEOpcode).Contains(BaseCommand))
{
if (this[Flag.BEOpcode] == true && this[Flag.D8Opcode] != true)
{
parameters.Add(Converters.LongName(Flag.BEOpcode));
if (BEOpcodeValue != null
&& (BEOpcodeValue == "raw" || BEOpcodeValue == "pack"))
parameters.Add(BEOpcodeValue);
}
}
// C2 Opcode
if (GetSupportedCommands(Flag.C2Opcode).Contains(BaseCommand))
{
if (this[Flag.C2Opcode] == true)
{
parameters.Add(Converters.LongName(Flag.C2Opcode));
if (C2OpcodeValue[0] != null)
{
if (C2OpcodeValue[0] > 0)
parameters.Add(C2OpcodeValue[0].ToString());
else
return null;
}
if (C2OpcodeValue[1] != null)
{
if (C2OpcodeValue[1] == 0)
{
parameters.Add(C2OpcodeValue[1].ToString());
}
else if (C2OpcodeValue[1] == 1)
{
parameters.Add(C2OpcodeValue[1].ToString());
if (C2OpcodeValue[2] != null && C2OpcodeValue[3] != null)
{
if (C2OpcodeValue[2] > 0 && C2OpcodeValue[3] > 0)
{
parameters.Add(C2OpcodeValue[2].ToString());
parameters.Add(C2OpcodeValue[3].ToString());
}
else
{
return null;
}
}
}
else
{
return null;
}
}
}
}
// Copyright Management Information
if (GetSupportedCommands(Flag.CopyrightManagementInformation).Contains(BaseCommand))
{
if (this[Flag.CopyrightManagementInformation] == true)
parameters.Add(Converters.LongName(Flag.CopyrightManagementInformation));
}
// D8 Opcode
if (GetSupportedCommands(Flag.D8Opcode).Contains(BaseCommand))
{
if (this[Flag.D8Opcode] == true)
parameters.Add(Converters.LongName(Flag.D8Opcode));
}
// Disable Beep
if (GetSupportedCommands(Flag.DisableBeep).Contains(BaseCommand))
{
if (this[Flag.DisableBeep] == true)
parameters.Add(Converters.LongName(Flag.DisableBeep));
}
// Extract MicroSoftCabFile
if (GetSupportedCommands(Flag.ExtractMicroSoftCabFile).Contains(BaseCommand))
{
if (this[Flag.ExtractMicroSoftCabFile] == true)
parameters.Add(Converters.LongName(Flag.ExtractMicroSoftCabFile));
}
// Fix
if (GetSupportedCommands(Flag.Fix).Contains(BaseCommand))
{
if (this[Flag.Fix] == true)
{
parameters.Add(Converters.LongName(Flag.Fix));
if (FixValue != null)
parameters.Add(FixValue.ToString());
else
return null;
}
}
// Force Unit Access
if (GetSupportedCommands(Flag.ForceUnitAccess).Contains(BaseCommand))
{
if (this[Flag.ForceUnitAccess] == true)
{
parameters.Add(Converters.LongName(Flag.ForceUnitAccess));
if (ForceUnitAccessValue != null)
parameters.Add(ForceUnitAccessValue.ToString());
}
}
// Multi-Sector Read
if (GetSupportedCommands(Flag.MultiSectorRead).Contains(BaseCommand))
{
if (this[Flag.MultiSectorRead] == true)
parameters.Add(Converters.LongName(Flag.MultiSectorRead));
}
// Multi-Session
if (GetSupportedCommands(Flag.MultiSession).Contains(BaseCommand))
{
if (this[Flag.MultiSession] == true)
parameters.Add(Converters.LongName(Flag.MultiSession));
}
// Not fix SubP
if (GetSupportedCommands(Flag.NoFixSubP).Contains(BaseCommand))
{
if (this[Flag.NoFixSubP] == true)
parameters.Add(Converters.LongName(Flag.NoFixSubP));
}
// Not fix SubQ
if (GetSupportedCommands(Flag.NoFixSubQ).Contains(BaseCommand))
{
if (this[Flag.NoFixSubQ] == true)
parameters.Add(Converters.LongName(Flag.NoFixSubQ));
}
// Not fix SubQ (PlayStation LibCrypt)
if (GetSupportedCommands(Flag.NoFixSubQLibCrypt).Contains(BaseCommand))
{
if (this[Flag.NoFixSubQLibCrypt] == true)
parameters.Add(Converters.LongName(Flag.NoFixSubQLibCrypt));
}
// Not fix SubQ (SecuROM)
if (GetSupportedCommands(Flag.NoFixSubQSecuROM).Contains(BaseCommand))
{
if (this[Flag.NoFixSubQSecuROM] == true)
parameters.Add(Converters.LongName(Flag.NoFixSubQSecuROM));
}
// Not fix SubRtoW
if (GetSupportedCommands(Flag.NoFixSubRtoW).Contains(BaseCommand))
{
if (this[Flag.NoFixSubRtoW] == true)
parameters.Add(Converters.LongName(Flag.NoFixSubRtoW));
}
// Not skip security sectors
if (GetSupportedCommands(Flag.NoSkipSS).Contains(BaseCommand))
{
if (this[Flag.NoSkipSS] == true)
{
parameters.Add(Converters.LongName(Flag.NoSkipSS));
if (NoSkipSecuritySectorValue != null)
parameters.Add(NoSkipSecuritySectorValue.ToString());
}
}
// Pad sectors
if (GetSupportedCommands(Flag.PadSector).Contains(BaseCommand))
{
if (this[Flag.PadSector] == true)
{
parameters.Add(Converters.LongName(Flag.PadSector));
if (PadSectorValue != null)
parameters.Add(PadSectorValue.ToString());
}
}
// Raw read (2064 byte/sector)
if (GetSupportedCommands(Flag.Raw).Contains(BaseCommand))
{
if (this[Flag.Raw] == true)
parameters.Add(Converters.LongName(Flag.Raw));
}
// Resume
if (GetSupportedCommands(Flag.Resume).Contains(BaseCommand))
{
if (this[Flag.Resume] == true)
parameters.Add(Converters.LongName(Flag.Resume));
}
// Reverse read
if (GetSupportedCommands(Flag.Reverse).Contains(BaseCommand))
{
if (this[Flag.Reverse] == true)
{
parameters.Add(Converters.LongName(Flag.Reverse));
if (BaseCommand == Command.DigitalVideoDisc)
{
if (ReverseStartLBAValue == null || ReverseEndLBAValue == null)
return null;
parameters.Add(ReverseStartLBAValue.ToString());
parameters.Add(ReverseEndLBAValue.ToString());
}
}
}
// Scan PlayStation anti-mod strings
if (GetSupportedCommands(Flag.ScanAntiMod).Contains(BaseCommand))
{
if (this[Flag.ScanAntiMod] == true)
parameters.Add(Converters.LongName(Flag.ScanAntiMod));
}
// Scan file to detect protect
if (GetSupportedCommands(Flag.ScanFileProtect).Contains(BaseCommand))
{
if (this[Flag.ScanFileProtect] == true)
{
parameters.Add(Converters.LongName(Flag.ScanFileProtect));
if (ScanFileProtectValue != null)
{
if (ScanFileProtectValue > 0)
parameters.Add(ScanFileProtectValue.ToString());
else
return null;
}
}
}
// Scan file to detect protect
if (GetSupportedCommands(Flag.ScanSectorProtect).Contains(BaseCommand))
{
if (this[Flag.ScanSectorProtect] == true)
parameters.Add(Converters.LongName(Flag.ScanSectorProtect));
}
// Scan 74:00:00 (Saturn)
if (GetSupportedCommands(Flag.SeventyFour).Contains(BaseCommand))
{
if (this[Flag.SeventyFour] == true)
parameters.Add(Converters.LongName(Flag.SeventyFour));
}
// Skip sectors
if (GetSupportedCommands(Flag.SkipSector).Contains(BaseCommand))
{
if (this[Flag.SkipSector] == true)
{
parameters.Add(Converters.LongName(Flag.SkipSector));
if (SkipSectorValue[0] != null)
{
if (SkipSectorValue[0] > 0)
parameters.Add(SkipSectorValue[0].ToString());
else
return null;
}
if (SkipSectorValue[1] != null)
{
if (SkipSectorValue[1] == 0)
parameters.Add(SkipSectorValue[1].ToString());
}
}
}
// Set Subchannel read level
if (GetSupportedCommands(Flag.SubchannelReadLevel).Contains(BaseCommand))
{
if (this[Flag.SubchannelReadLevel] == true)
{
parameters.Add(Converters.LongName(Flag.SubchannelReadLevel));
if (SubchannelReadLevelValue != null)
{
if (SubchannelReadLevelValue >= 0 && SubchannelReadLevelValue <= 2)
parameters.Add(SubchannelReadLevelValue.ToString());
else
return null;
}
}
}
// Use Anchor Volume Descriptor Pointer
if (GetSupportedCommands(Flag.UseAnchorVolumeDescriptorPointer).Contains(BaseCommand))
{
if (this[Flag.UseAnchorVolumeDescriptorPointer] == true)
parameters.Add(Converters.LongName(Flag.UseAnchorVolumeDescriptorPointer));
}
// VideoNow
if (GetSupportedCommands(Flag.VideoNow).Contains(BaseCommand))
{
if (this[Flag.VideoNow] == true)
{
parameters.Add(Converters.LongName(Flag.VideoNow));
if (VideoNowValue != null)
{
if (VideoNowValue >= 0)
parameters.Add(VideoNowValue.ToString());
else
return null;
}
}
}
// VideoNow Color
if (GetSupportedCommands(Flag.VideoNowColor).Contains(BaseCommand))
{
if (this[Flag.VideoNowColor] == true)
parameters.Add(Converters.LongName(Flag.VideoNowColor));
}
// VideoNowXP
if (GetSupportedCommands(Flag.VideoNowXP).Contains(BaseCommand))
{
if (this[Flag.VideoNowXP] == true)
parameters.Add(Converters.LongName(Flag.VideoNowXP));
}
return string.Join(" ", parameters);
}
///
public override string GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType);
///
public override MediaType? GetMediaType() => Converters.ToMediaType(BaseCommand);
///
public override bool IsDumpingCommand()
{
switch (BaseCommand)
{
case Command.Audio:
case Command.BluRay:
case Command.CompactDisc:
case Command.Data:
case Command.DigitalVideoDisc:
case Command.Disk:
case Command.Floppy:
case Command.GDROM:
case Command.SACD:
case Command.Swap:
case Command.Tape:
case Command.XBOX:
case Command.XBOXSwap:
case Command.XGD2Swap:
case Command.XGD3Swap:
return true;
default:
return false;
}
}
///
protected override void ResetValues()
{
BaseCommand = Command.NONE;
DriveLetter = null;
DriveSpeed = null;
Filename = null;
StartLBAValue = null;
EndLBAValue = null;
_flags = new Dictionary();
AddOffsetValue = null;
BEOpcodeValue = null;
C2OpcodeValue = new int?[4];
FixValue = null;
ForceUnitAccessValue = null;
NoSkipSecuritySectorValue = null;
ScanFileProtectValue = null;
SubchannelReadLevelValue = null;
VideoNowValue = null;
}
///
protected override void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options)
{
SetBaseCommand(this.System, this.Type);
DriveLetter = driveLetter.ToString();
DriveSpeed = driveSpeed;
Filename = filename;
// First check to see if the combination of system and MediaType is valid
var validTypes = Validators.GetValidMediaTypes(this.System);
if (!validTypes.Contains(this.Type))
return;
// Set disable beep flag, if needed
if (options.DICQuietMode)
this[Flag.DisableBeep] = true;
// Set the C2 reread count
switch (options.DICRereadCount)
{
case -1:
C2OpcodeValue[0] = null;
break;
case 0:
C2OpcodeValue[0] = 20;
break;
default:
C2OpcodeValue[0] = options.DICRereadCount;
break;
}
// Now sort based on disc type
switch (this.Type)
{
case MediaType.CDROM:
this[Flag.C2Opcode] = true;
switch (this.System)
{
case KnownSystem.AppleMacintosh:
case KnownSystem.IBMPCCompatible:
this[Flag.NoFixSubQSecuROM] = true;
this[Flag.ScanFileProtect] = true;
this[Flag.ScanSectorProtect] = options.DICParanoidMode;
this[Flag.SubchannelReadLevel] = options.DICParanoidMode;
if (this[Flag.SubchannelReadLevel] == true)
SubchannelReadLevelValue = 2;
break;
case KnownSystem.AtariJaguarCD:
this[Flag.AtariJaguar] = true;
break;
case KnownSystem.HasbroVideoNow:
case KnownSystem.HasbroVideoNowJr:
this[Flag.VideoNow] = true;
this.VideoNowValue = 18032;
break;
case KnownSystem.HasbroVideoNowColor:
this[Flag.VideoNowColor] = true;
break;
case KnownSystem.HasbroVideoNowXP:
this[Flag.VideoNowXP] = true;
break;
case KnownSystem.SonyPlayStation:
this[Flag.ScanAntiMod] = true;
this[Flag.NoFixSubQLibCrypt] = true;
break;
}
break;
case MediaType.DVD:
this[Flag.CopyrightManagementInformation] = options.DICUseCMIFlag;
this[Flag.ScanFileProtect] = options.DICParanoidMode;
break;
case MediaType.GDROM:
this[Flag.C2Opcode] = true;
break;
case MediaType.HDDVD:
this[Flag.CopyrightManagementInformation] = options.DICUseCMIFlag;
break;
case MediaType.BluRay:
// Currently no defaults set
break;
// Special Formats
case MediaType.NintendoGameCubeGameDisc:
this[Flag.Raw] = true;
break;
case MediaType.NintendoWiiOpticalDisc:
this[Flag.Raw] = true;
break;
// Non-optical
case MediaType.FloppyDisk:
// Currently no defaults set
break;
}
}
///
protected override bool ValidateAndSetParameters(string parameters)
{
// The string has to be valid by itself first
if (string.IsNullOrWhiteSpace(parameters))
return false;
// Now split the string into parts for easier validation
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
parameters = parameters.Trim();
List parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+")
.Cast()
.Select(m => m.Value)
.ToList();
// Determine what the commandline should look like given the first item
BaseCommand = Converters.StringToCommand(parts[0]);
if (BaseCommand == Command.NONE)
return false;
// Loop through ordered command-specific flags
int index = -1;
switch (BaseCommand)
{
case Command.Audio:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (!DoesExist(parts, 2) || IsFlag(parts[2]))
return false;
else
Filename = parts[2];
if (!DoesExist(parts, 3) || !IsValidInt32(parts[3], lowerBound: 0, upperBound: 72))
return false;
else
DriveSpeed = Int32.Parse(parts[3]);
if (!DoesExist(parts, 4) || !IsValidInt32(parts[4], lowerBound: 0))
return false;
else
StartLBAValue = Int32.Parse(parts[4]);
if (!DoesExist(parts, 5) || !IsValidInt32(parts[5], lowerBound: 0))
return false;
else
EndLBAValue = Int32.Parse(parts[5]);
index = 6;
break;
case Command.BluRay:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (!DoesExist(parts, 2) || IsFlag(parts[2]))
return false;
else
Filename = parts[2];
if (!DoesExist(parts, 3) || !IsValidInt32(parts[3], lowerBound: 0, upperBound: 72))
return false;
else
DriveSpeed = Int32.Parse(parts[3]);
index = 4;
break;
case Command.Close:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (parts.Count > 2)
return false;
break;
case Command.CompactDisc:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (!DoesExist(parts, 2) || IsFlag(parts[2]))
return false;
else
Filename = parts[2];
if (!DoesExist(parts, 3) || !IsValidInt32(parts[3], lowerBound: 0, upperBound: 72))
return false;
else
DriveSpeed = Int32.Parse(parts[3]);
index = 4;
break;
case Command.Data:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (!DoesExist(parts, 2) || IsFlag(parts[2]))
return false;
else
Filename = parts[2];
if (!DoesExist(parts, 3) || !IsValidInt32(parts[3], lowerBound: 0, upperBound: 72))
return false;
else
DriveSpeed = Int32.Parse(parts[3]);
if (!DoesExist(parts, 4) || !IsValidInt32(parts[4], lowerBound: 0))
return false;
else
StartLBAValue = Int32.Parse(parts[4]);
if (!DoesExist(parts, 5) || !IsValidInt32(parts[5], lowerBound: 0))
return false;
else
EndLBAValue = Int32.Parse(parts[5]);
index = 6;
break;
case Command.DigitalVideoDisc:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (!DoesExist(parts, 2) || IsFlag(parts[2]))
return false;
else
Filename = parts[2];
if (!DoesExist(parts, 3) || !IsValidInt32(parts[3], lowerBound: 0, upperBound: 24)) // Officially 0-16
return false;
else
DriveSpeed = Int32.Parse(parts[3]);
index = 4;
break;
case Command.Disk:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (!DoesExist(parts, 2) || IsFlag(parts[2]))
return false;
else
Filename = parts[2];
if (parts.Count > 3)
return false;
break;
case Command.DriveSpeed:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (parts.Count > 2)
return false;
break;
case Command.Eject:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (parts.Count > 2)
return false;
break;
case Command.Floppy:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (!DoesExist(parts, 2) || IsFlag(parts[2]))
return false;
else
Filename = parts[2];
if (parts.Count > 3)
return false;
break;
case Command.GDROM:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (!DoesExist(parts, 2) || IsFlag(parts[2]))
return false;
else
Filename = parts[2];
if (!DoesExist(parts, 3) || !IsValidInt32(parts[3], lowerBound: 0, upperBound: 72))
return false;
else
DriveSpeed = Int32.Parse(parts[3]);
index = 4;
break;
case Command.MDS:
if (!DoesExist(parts, 1) || IsFlag(parts[1]) || !File.Exists(parts[1]))
return false;
else
Filename = parts[1];
if (parts.Count > 2)
return false;
break;
case Command.Merge:
if (!DoesExist(parts, 1) || IsFlag(parts[1]) || !File.Exists(parts[1]))
return false;
else
Filename = parts[1];
if (!DoesExist(parts, 2) || IsFlag(parts[2]) || !File.Exists(parts[2]))
return false;
else
OptiarcFilename = parts[2];
if (parts.Count > 3)
return false;
break;
case Command.Reset:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (parts.Count > 2)
return false;
break;
case Command.SACD:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (!DoesExist(parts, 2) || IsFlag(parts[2]))
return false;
else
Filename = parts[2];
if (!DoesExist(parts, 3) || !IsValidInt32(parts[3], lowerBound: 0, upperBound: 16))
return false;
else
DriveSpeed = Int32.Parse(parts[3]);
index = 4;
break;
case Command.Start:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (parts.Count > 2)
return false;
break;
case Command.Stop:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (parts.Count > 2)
return false;
break;
case Command.Sub:
if (!DoesExist(parts, 1) || IsFlag(parts[1]) || !File.Exists(parts[1]))
return false;
else
Filename = parts[1];
if (parts.Count > 2)
return false;
break;
case Command.Swap:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (!DoesExist(parts, 2) || IsFlag(parts[2]))
return false;
else
Filename = parts[2];
if (!DoesExist(parts, 3) || !IsValidInt32(parts[3], lowerBound: 0, upperBound: 72))
return false;
else
DriveSpeed = Int32.Parse(parts[3]);
index = 4;
break;
case Command.Tape:
if (!DoesExist(parts, 1) || IsFlag(parts[1]) || !File.Exists(parts[1]))
return false;
else
Filename = parts[1];
if (parts.Count > 2)
return false;
break;
case Command.XBOX:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (!DoesExist(parts, 2) || IsFlag(parts[2]))
return false;
else
Filename = parts[2];
if (!DoesExist(parts, 3) || !IsValidInt32(parts[3], lowerBound: 0, upperBound: 72))
return false;
else
DriveSpeed = Int32.Parse(parts[3]);
index = 4;
break;
case Command.XBOXSwap:
case Command.XGD2Swap:
case Command.XGD3Swap:
if (!DoesExist(parts, 1) || !IsValidDriveLetter(parts[1]))
return false;
else
DriveLetter = parts[1];
if (!DoesExist(parts, 2) || IsFlag(parts[2]))
return false;
else
Filename = parts[2];
if (!DoesExist(parts, 3) || !IsValidInt32(parts[3], lowerBound: 0, upperBound: 72))
return false;
else
DriveSpeed = Int32.Parse(parts[3]);
for (int i = 4; i < parts.Count; i++)
{
if (!Int64.TryParse(parts[i], out long temp))
return false;
}
break;
default:
return false;
}
// Loop through all auxilary flags, if necessary
if (index > 0)
{
for (int i = index; i < parts.Count; i++)
{
switch (parts[i])
{
case FlagStrings.AddOffset:
if (!GetSupportedCommands(Flag.AddOffset).Contains(BaseCommand))
{
return false;
}
else if (!DoesExist(parts, i + 1))
{
this[Flag.AddOffset] = true;
break;
}
else if (IsFlag(parts[i + 1]))
{
this[Flag.AddOffset] = true;
break;
}
else if (!IsValidInt32(parts[i + 1]))
{
return false;
}
this[Flag.AddOffset] = true;
AddOffsetValue = Int32.Parse(parts[i + 1]);
i++;
break;
case FlagStrings.AMSF:
if (!GetSupportedCommands(Flag.AMSF).Contains(BaseCommand))
return false;
this[Flag.AMSF] = true;
break;
case FlagStrings.AtariJaguar:
if (!GetSupportedCommands(Flag.AtariJaguar).Contains(BaseCommand))
return false;
this[Flag.AtariJaguar] = true;
break;
case FlagStrings.BEOpcode:
if (!GetSupportedCommands(Flag.BEOpcode).Contains(BaseCommand))
{
return false;
}
else if (!DoesExist(parts, i + 1))
{
this[Flag.BEOpcode] = true;
break;
}
else if (IsFlag(parts[i + 1]))
{
this[Flag.BEOpcode] = true;
break;
}
else if (!string.Equals(parts[i + 1], "raw", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(parts[i + 1], "pack", StringComparison.OrdinalIgnoreCase))
{
return false;
}
this[Flag.BEOpcode] = true;
BEOpcodeValue = parts[i + 1].ToLowerInvariant();
i++;
break;
case FlagStrings.C2Opcode:
if (!GetSupportedCommands(Flag.C2Opcode).Contains(BaseCommand))
return false;
this[Flag.C2Opcode] = true;
for (int j = 0; j < 4; j++)
{
if (!DoesExist(parts, i + 1))
{
break;
}
else if (IsFlag(parts[i + 1]))
{
break;
}
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
{
return false;
}
else
{
C2OpcodeValue[j] = Int32.Parse(parts[i + 1]);
i++;
}
}
break;
case FlagStrings.CopyrightManagementInformation:
if (!GetSupportedCommands(Flag.CopyrightManagementInformation).Contains(BaseCommand))
return false;
this[Flag.CopyrightManagementInformation] = true;
break;
case FlagStrings.D8Opcode:
if (!GetSupportedCommands(Flag.D8Opcode).Contains(BaseCommand))
return false;
this[Flag.D8Opcode] = true;
break;
case FlagStrings.DisableBeep:
if (!GetSupportedCommands(Flag.DisableBeep).Contains(BaseCommand))
return false;
this[Flag.DisableBeep] = true;
break;
case FlagStrings.ExtractMicroSoftCabFile:
if (!GetSupportedCommands(Flag.ExtractMicroSoftCabFile).Contains(BaseCommand))
return false;
this[Flag.ExtractMicroSoftCabFile] = true;
break;
case FlagStrings.Fix:
if (!GetSupportedCommands(Flag.Fix).Contains(BaseCommand))
return false;
else if (!DoesExist(parts, i + 1))
return false;
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
return false;
this[Flag.Fix] = true;
FixValue = Int32.Parse(parts[i + 1]);
i++;
break;
case FlagStrings.ForceUnitAccess:
if (!GetSupportedCommands(Flag.ForceUnitAccess).Contains(BaseCommand))
{
return false;
}
else if (!DoesExist(parts, i + 1))
{
this[Flag.ForceUnitAccess] = true;
break;
}
else if (IsFlag(parts[i + 1]))
{
this[Flag.ForceUnitAccess] = true;
break;
}
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
{
return false;
}
this[Flag.ForceUnitAccess] = true;
ForceUnitAccessValue = Int32.Parse(parts[i + 1]);
i++;
break;
case FlagStrings.MultiSectorRead:
if (!GetSupportedCommands(Flag.MultiSectorRead).Contains(BaseCommand))
return false;
this[Flag.MultiSectorRead] = true;
break;
case FlagStrings.MultiSession:
if (!GetSupportedCommands(Flag.MultiSession).Contains(BaseCommand))
return false;
this[Flag.MultiSession] = true;
break;
case FlagStrings.NoFixSubP:
if (!GetSupportedCommands(Flag.NoFixSubP).Contains(BaseCommand))
return false;
this[Flag.NoFixSubP] = true;
break;
case FlagStrings.NoFixSubQ:
if (!GetSupportedCommands(Flag.NoFixSubQ).Contains(BaseCommand))
return false;
this[Flag.NoFixSubQ] = true;
break;
case FlagStrings.NoFixSubQLibCrypt:
if (!GetSupportedCommands(Flag.NoFixSubQLibCrypt).Contains(BaseCommand))
return false;
this[Flag.NoFixSubQLibCrypt] = true;
break;
case FlagStrings.NoFixSubQSecuROM:
if (!GetSupportedCommands(Flag.NoFixSubQSecuROM).Contains(BaseCommand))
return false;
this[Flag.NoFixSubQSecuROM] = true;
break;
case FlagStrings.NoFixSubRtoW:
if (!GetSupportedCommands(Flag.NoFixSubRtoW).Contains(BaseCommand))
return false;
this[Flag.NoFixSubRtoW] = true;
break;
case FlagStrings.NoSkipSS:
if (!GetSupportedCommands(Flag.NoSkipSS).Contains(BaseCommand))
{
return false;
}
else if (!DoesExist(parts, i + 1))
{
this[Flag.NoSkipSS] = true;
break;
}
else if (IsFlag(parts[i + 1]))
{
this[Flag.NoSkipSS] = true;
break;
}
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
{
return false;
}
this[Flag.NoSkipSS] = true;
NoSkipSecuritySectorValue = Int32.Parse(parts[i + 1]);
i++;
break;
case FlagStrings.PadSector:
if (!GetSupportedCommands(Flag.PadSector).Contains(BaseCommand))
{
return false;
}
else if (!DoesExist(parts, i + 1))
{
this[Flag.PadSector] = true;
break;
}
else if (IsFlag(parts[i + 1]))
{
this[Flag.PadSector] = true;
break;
}
else if (!IsValidInt32(parts[i + 1], lowerBound: 0, upperBound: 1))
{
return false;
}
this[Flag.PadSector] = true;
PadSectorValue = Int32.Parse(parts[i + 1]);
i++;
break;
case FlagStrings.Raw:
if (!GetSupportedCommands(Flag.Raw).Contains(BaseCommand))
return false;
this[Flag.Raw] = true;
break;
case FlagStrings.Resume:
if (!GetSupportedCommands(Flag.Resume).Contains(BaseCommand))
return false;
this[Flag.Resume] = true;
break;
case FlagStrings.Reverse:
if (!GetSupportedCommands(Flag.Reverse).Contains(BaseCommand))
return false;
// DVD specifically requires StartLBA and EndLBA
if (BaseCommand == Command.DigitalVideoDisc)
{
if (!DoesExist(parts, i + 1) || !DoesExist(parts, i + 2))
return false;
else if (!IsValidInt32(parts[i + 1], lowerBound: 0) || !IsValidInt32(parts[i + 2], lowerBound: 0))
return false;
ReverseStartLBAValue = Int32.Parse(parts[i + 1]);
ReverseEndLBAValue = Int32.Parse(parts[i + 2]);
i += 2;
}
this[Flag.Reverse] = true;
break;
case FlagStrings.ScanAntiMod:
if (!GetSupportedCommands(Flag.ScanAntiMod).Contains(BaseCommand))
return false;
this[Flag.ScanAntiMod] = true;
break;
case FlagStrings.ScanFileProtect:
if (!GetSupportedCommands(Flag.ScanFileProtect).Contains(BaseCommand))
{
return false;
}
else if (!DoesExist(parts, i + 1))
{
this[Flag.ScanFileProtect] = true;
break;
}
else if (IsFlag(parts[i + 1]))
{
this[Flag.ScanFileProtect] = true;
break;
}
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
{
return false;
}
this[Flag.ScanFileProtect] = true;
ScanFileProtectValue = Int32.Parse(parts[i + 1]);
i++;
break;
case FlagStrings.ScanSectorProtect:
if (!GetSupportedCommands(Flag.ScanSectorProtect).Contains(BaseCommand))
return false;
this[Flag.ScanSectorProtect] = true;
break;
case FlagStrings.SeventyFour:
if (!GetSupportedCommands(Flag.SeventyFour).Contains(BaseCommand))
return false;
this[Flag.SeventyFour] = true;
break;
case FlagStrings.SkipSector:
if (!GetSupportedCommands(Flag.SkipSector).Contains(BaseCommand))
return false;
this[Flag.SkipSector] = true;
for (int j = 0; j < 2; j++)
{
if (!DoesExist(parts, i + 1))
{
break;
}
else if (IsFlag(parts[i + 1]))
{
break;
}
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
{
return false;
}
else
{
SkipSectorValue[j] = Int32.Parse(parts[i + 1]);
i++;
}
}
break;
case FlagStrings.SubchannelReadLevel:
if (!GetSupportedCommands(Flag.SubchannelReadLevel).Contains(BaseCommand))
{
return false;
}
else if (!DoesExist(parts, i + 1))
{
this[Flag.SubchannelReadLevel] = true;
break;
}
else if (IsFlag(parts[i + 1]))
{
this[Flag.SubchannelReadLevel] = true;
break;
}
else if (!IsValidInt32(parts[i + 1], lowerBound: 0, upperBound: 2))
{
return false;
}
this[Flag.SubchannelReadLevel] = true;
SubchannelReadLevelValue = Int32.Parse(parts[i + 1]);
i++;
break;
case FlagStrings.UseAnchorVolumeDescriptorPointer:
if (!GetSupportedCommands(Flag.UseAnchorVolumeDescriptorPointer).Contains(BaseCommand))
return false;
this[Flag.UseAnchorVolumeDescriptorPointer] = true;
break;
case FlagStrings.VideoNow:
if (!GetSupportedCommands(Flag.VideoNow).Contains(BaseCommand))
{
return false;
}
else if (!DoesExist(parts, i + 1))
{
this[Flag.VideoNow] = true;
break;
}
else if (IsFlag(parts[i + 1]))
{
this[Flag.VideoNow] = true;
break;
}
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
{
return false;
}
this[Flag.VideoNow] = true;
VideoNowValue = Int32.Parse(parts[i + 1]);
i++;
break;
case FlagStrings.VideoNowColor:
if (!GetSupportedCommands(Flag.VideoNowColor).Contains(BaseCommand))
return false;
this[Flag.VideoNowColor] = true;
break;
case FlagStrings.VideoNowXP:
if (!GetSupportedCommands(Flag.VideoNowXP).Contains(BaseCommand))
return false;
this[Flag.VideoNowXP] = true;
break;
default:
return false;
}
}
}
return true;
}
#endregion
#region Private Extra Methods
///
/// Get the list of commands that use a given flag
///
/// Flag value to get commands for
/// List of DiscImageCreator.Commands, if possible
private static List GetSupportedCommands(Flag flag)
{
var commands = new List();
switch (flag)
{
case Flag.AMSF:
commands.Add(Command.CompactDisc);
break;
case Flag.AtariJaguar:
commands.Add(Command.CompactDisc);
break;
case Flag.BEOpcode:
commands.Add(Command.Audio);
commands.Add(Command.CompactDisc);
commands.Add(Command.Data);
commands.Add(Command.GDROM);
commands.Add(Command.Swap);
break;
case Flag.C2Opcode:
commands.Add(Command.Audio);
commands.Add(Command.CompactDisc);
commands.Add(Command.Data);
commands.Add(Command.GDROM);
commands.Add(Command.Swap);
break;
case Flag.CopyrightManagementInformation:
commands.Add(Command.DigitalVideoDisc);
break;
case Flag.D8Opcode:
commands.Add(Command.Audio);
commands.Add(Command.CompactDisc);
commands.Add(Command.Data);
commands.Add(Command.GDROM);
commands.Add(Command.Swap);
break;
case Flag.DisableBeep:
commands.Add(Command.Audio);
commands.Add(Command.BluRay);
commands.Add(Command.CompactDisc);
commands.Add(Command.Data);
commands.Add(Command.DigitalVideoDisc);
commands.Add(Command.GDROM);
commands.Add(Command.SACD);
commands.Add(Command.Swap);
commands.Add(Command.XBOX);
commands.Add(Command.XBOXSwap);
commands.Add(Command.XGD2Swap);
commands.Add(Command.XGD3Swap);
break;
case Flag.ExtractMicroSoftCabFile:
commands.Add(Command.CompactDisc);
break;
case Flag.Fix:
commands.Add(Command.DigitalVideoDisc);
break;
case Flag.ForceUnitAccess:
commands.Add(Command.Audio);
commands.Add(Command.BluRay);
commands.Add(Command.CompactDisc);
commands.Add(Command.Data);
commands.Add(Command.DigitalVideoDisc);
commands.Add(Command.GDROM);
commands.Add(Command.SACD);
commands.Add(Command.Swap);
commands.Add(Command.XBOX);
commands.Add(Command.XBOXSwap);
commands.Add(Command.XGD2Swap);
commands.Add(Command.XGD3Swap);
break;
case Flag.MultiSectorRead:
commands.Add(Command.CompactDisc);
break;
case Flag.MultiSession:
commands.Add(Command.Audio);
commands.Add(Command.CompactDisc);
commands.Add(Command.Data);
commands.Add(Command.Swap);
break;
case Flag.NoFixSubP:
commands.Add(Command.Audio);
commands.Add(Command.CompactDisc);
commands.Add(Command.Data);
commands.Add(Command.GDROM);
commands.Add(Command.Swap);
break;
case Flag.NoFixSubQ:
commands.Add(Command.Audio);
commands.Add(Command.CompactDisc);
commands.Add(Command.Data);
commands.Add(Command.GDROM);
commands.Add(Command.Swap);
break;
case Flag.NoFixSubQLibCrypt:
commands.Add(Command.CompactDisc);
commands.Add(Command.Swap);
break;
case Flag.NoFixSubQSecuROM:
commands.Add(Command.CompactDisc);
commands.Add(Command.Swap);
break;
case Flag.NoFixSubRtoW:
commands.Add(Command.Audio);
commands.Add(Command.CompactDisc);
commands.Add(Command.Data);
commands.Add(Command.GDROM);
commands.Add(Command.Swap);
break;
case Flag.NoSkipSS:
commands.Add(Command.BluRay);
commands.Add(Command.XBOX);
commands.Add(Command.XBOXSwap);
commands.Add(Command.XGD2Swap);
commands.Add(Command.XGD3Swap);
break;
case Flag.PadSector:
commands.Add(Command.DigitalVideoDisc);
break;
case Flag.Raw:
commands.Add(Command.DigitalVideoDisc);
break;
case Flag.Resume:
commands.Add(Command.DigitalVideoDisc);
break;
case Flag.Reverse:
commands.Add(Command.Audio);
commands.Add(Command.Data);
commands.Add(Command.DigitalVideoDisc);
break;
case Flag.ScanAntiMod:
commands.Add(Command.Audio);
commands.Add(Command.CompactDisc);
commands.Add(Command.Data);
commands.Add(Command.Swap);
break;
case Flag.ScanFileProtect:
commands.Add(Command.Audio);
commands.Add(Command.CompactDisc);
commands.Add(Command.Data);
commands.Add(Command.DigitalVideoDisc);
commands.Add(Command.Swap);
break;
case Flag.ScanSectorProtect:
commands.Add(Command.Audio);
commands.Add(Command.CompactDisc);
commands.Add(Command.Data);
commands.Add(Command.Swap);
break;
case Flag.SeventyFour:
commands.Add(Command.CompactDisc);
commands.Add(Command.Swap);
break;
case Flag.SkipSector:
commands.Add(Command.Audio);
commands.Add(Command.Data);
break;
case Flag.SubchannelReadLevel:
commands.Add(Command.Audio);
commands.Add(Command.CompactDisc);
commands.Add(Command.Data);
commands.Add(Command.GDROM);
commands.Add(Command.Swap);
break;
case Flag.UseAnchorVolumeDescriptorPointer:
commands.Add(Command.BluRay);
commands.Add(Command.DigitalVideoDisc);
commands.Add(Command.XBOX);
break;
case Flag.VideoNow:
commands.Add(Command.CompactDisc);
commands.Add(Command.Swap);
break;
case Flag.VideoNowColor:
commands.Add(Command.CompactDisc);
commands.Add(Command.Swap);
break;
case Flag.VideoNowXP:
commands.Add(Command.CompactDisc);
commands.Add(Command.Swap);
break;
case Flag.NONE:
default:
return commands;
}
return commands;
}
///
/// Set the DIC command to be used for a given system and media type
///
/// KnownSystem value to check
/// MediaType value to check
private void SetBaseCommand(KnownSystem? system, MediaType? type)
{
// If we have an invalid combination, we should BaseCommand = null
if (!Validators.GetValidMediaTypes(system).Contains(type))
{
BaseCommand = Command.NONE;
return;
}
switch (type)
{
case MediaType.CDROM:
if (system == KnownSystem.SuperAudioCD)
BaseCommand = Command.SACD;
else
BaseCommand = Command.CompactDisc;
return;
case MediaType.DVD:
if (system == KnownSystem.MicrosoftXBOX
|| system == KnownSystem.MicrosoftXBOX360)
{
BaseCommand = Command.XBOX;
return;
}
BaseCommand = Command.DigitalVideoDisc;
return;
case MediaType.GDROM:
BaseCommand = Command.GDROM;
return;
case MediaType.HDDVD:
BaseCommand = Command.DigitalVideoDisc;
return;
case MediaType.BluRay:
BaseCommand = Command.BluRay;
return;
case MediaType.NintendoGameCubeGameDisc:
BaseCommand = Command.DigitalVideoDisc;
return;
case MediaType.NintendoWiiOpticalDisc:
BaseCommand = Command.DigitalVideoDisc;
return;
case MediaType.FloppyDisk:
BaseCommand = Command.Floppy;
return;
case MediaType.HardDisk:
BaseCommand = Command.Disk;
return;
case MediaType.DataCartridge:
BaseCommand = Command.Tape;
return;
default:
BaseCommand = Command.NONE;
return;
}
}
#endregion
#region Information Extraction Methods
///
/// Get the proper datfile from the input file, if possible
///
/// .dat file location
/// Relevant pieces of the datfile, null on error
private static string GetDatfile(string dat)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(dat))
return null;
using (StreamReader sr = File.OpenText(dat))
{
try
{
// Make sure this file is a .dat
if (sr.ReadLine() != "")
return null;
if (sr.ReadLine() != "")
return null;
// Fast forward to the rom lines
while (!sr.ReadLine().TrimStart().StartsWith("Games
sr.ReadLine(); // Plextor
// Now that we're at the relevant entries, read each line in and concatenate
string datString = "", line = sr.ReadLine().Trim();
while (line.StartsWith("
/// Get the DVD protection information, if possible
///
/// _CSSKey.txt file location
/// _disc.txt file location
/// Formatted string representing the DVD protection, null on error
private static string GetDVDProtection(string cssKey, string disc)
{
// If one of the files doesn't exist, we can't get info from them
if (!File.Exists(disc))
return null;
// Setup all of the individual pieces
string region = null, rceProtection = null, copyrightProtectionSystemType = null, vobKeys = null, decryptedDiscKey = null;
// Get everything from _disc.txt first
using (StreamReader sr = File.OpenText(disc))
{
try
{
// Fast forward to the copyright information
while (!sr.ReadLine().Trim().StartsWith("========== CopyrightInformation ==========")) ;
// Now read until we hit the manufacturing information
string line = sr.ReadLine().Trim();
while (!line.StartsWith("========== ManufacturingInformation =========="))
{
if (line.StartsWith("CopyrightProtectionType"))
copyrightProtectionSystemType = line.Substring("CopyrightProtectionType: ".Length);
else if (line.StartsWith("RegionManagementInformation"))
region = line.Substring("RegionManagementInformation: ".Length);
line = sr.ReadLine().Trim();
}
}
catch { }
}
// Get everything from _CSSKey.txt next, if it exists
if (File.Exists(cssKey))
{
using (StreamReader sr = File.OpenText(cssKey))
{
try
{
// Read until the end
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
if (line.StartsWith("DecryptedDiscKey"))
{
decryptedDiscKey = line.Substring("DecryptedDiscKey[020]: ".Length);
}
else if (line.StartsWith("LBA:"))
{
// Set the key string if necessary
if (vobKeys == null)
vobKeys = string.Empty;
// No keys
if (line.Contains("No TitleKey"))
{
var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), No TitleKey$");
vobKeys += $"{match.Groups[1].Value} Title Key: No TitleKey\n";
}
else
{
var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), EncryptedTitleKey: .*?, DecryptedTitleKey: (.*?)$");
vobKeys += $"{match.Groups[1].Value} Title Key: {match.Groups[2].Value}\n";
}
}
}
}
catch { }
}
}
// Now we format everything we can
string protection = string.Empty;
if (!string.IsNullOrEmpty(region))
protection += $"Region: {region}\n";
if (!string.IsNullOrEmpty(rceProtection))
protection += $"RCE Protection: {rceProtection}\n";
if (!string.IsNullOrEmpty(copyrightProtectionSystemType))
protection += $"Copyright Protection System Type: {copyrightProtectionSystemType}\n";
if (!string.IsNullOrEmpty(vobKeys))
protection += vobKeys;
if (!string.IsNullOrEmpty(decryptedDiscKey))
protection += $"Decrypted Disc Key: {decryptedDiscKey}\n";
return protection;
}
///
/// Get the detected error count from the input files, if possible
///
/// .img_EdcEcc.txt/.img_EccEdc.txt file location
/// Error count if possible, -1 on error
private static long GetErrorCount(string edcecc)
{
// TODO: Better usage of _mainInfo and _c2Error for uncorrectable errors
// If the file doesn't exist, we can't get info from it
if (!File.Exists(edcecc))
return -1;
// Get a total error count for after
long? totalErrors = null;
// First line of defense is the EdcEcc error file
using (StreamReader sr = File.OpenText(edcecc))
{
try
{
// Read in the error count whenever we find it
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
if (line.StartsWith("[NO ERROR]"))
{
totalErrors = 0;
break;
}
else if (line.StartsWith("Total errors"))
{
if (totalErrors == null)
totalErrors = 0;
if (Int64.TryParse(line.Substring("Total errors: ".Length).Trim(), out long te))
totalErrors += te;
}
else if (line.StartsWith("Total warnings"))
{
if (totalErrors == null)
totalErrors = 0;
if (Int64.TryParse(line.Substring("Total warnings: ".Length).Trim(), out long tw))
totalErrors += tw;
}
}
// If we haven't found anything, return -1
return totalErrors ?? -1;
}
catch
{
// We don't care what the exception is right now
return Int64.MaxValue;
}
}
}
///
/// Get the build info from a GD-ROM LD area, if possible
///
/// <String representing a formatter variant of the GD-ROM header
/// True on successful extraction of info, false otherwise
private static bool GetGDROMBuildInfo(string segaHeader, out string serial, out string version, out string date)
{
serial = null; version = null; date = null;
// If the input header is null, we can't do a thing
if (string.IsNullOrWhiteSpace(segaHeader))
return false;
// Now read it in cutting it into lines for easier parsing
try
{
string[] header = segaHeader.Split('\n');
string serialLine = header[2].Substring(58);
string versionLine = header[4].Substring(58);
string dateLine = header[5].Substring(58);
serial = serialLine.Substring(0, 4);
version = versionLine.Substring(10, 6).TrimStart('V', 'v');
date = dateLine.Substring(0, 8);
return true;
}
catch
{
// We don't care what the error is
return false;
}
}
///
/// Get the layerbreak from the input file, if possible
///
/// _disc.txt file location
/// True if XGD layerbreak info should be used, false otherwise
/// Layerbreak if possible, null on error
private static string GetLayerbreak(string disc, bool xgd)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(disc))
return null;
using (StreamReader sr = File.OpenText(disc))
{
try
{
string line = sr.ReadLine();
while (line != null)
{
// Trim the line for later use
line = line.Trim();
// Single-layer discs have no layerbreak
if (line.Contains("NumberOfLayers: Single Layer"))
{
return null;
}
// Xbox discs have a special layerbreaks
else if (xgd && line.StartsWith("LayerBreak"))
{
// LayerBreak: (L0 Video: , L0 Middle: , L0 Game: )
return line.Split(' ')[1];
}
// Dual-layer discs have a regular layerbreak
else if (!xgd && line.StartsWith("LayerZeroSector"))
{
// LayerZeroSector: ()
return line.Split(' ')[1];
}
line = sr.ReadLine();
}
// If we get to the end, there's an issue
return null;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
}
///
/// Get the layerbreak info associated with the dump
///
/// Path to the PIC.bin file associated with the dump
/// True if layerbreak info was set, false otherwise
/// https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters
private static bool GetLayerbreak(string picPath, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3)
{
// Set the default values
layerbreak1 = null; layerbreak2 = null; layerbreak3 = null;
// If the file doesn't exist, we can't get the info
if (!File.Exists(picPath))
return false;
try
{
using (BinaryReader br = new BinaryReader(File.OpenRead(picPath)))
{
// Layerbreak 1
br.BaseStream.Seek(0x1C, SeekOrigin.Begin);
long layerbreak1Offset = br.ReadInt32BigEndian();
long layerbreak1Value = br.ReadInt32BigEndian();
if (layerbreak1Value == 0)
return false;
layerbreak1 = layerbreak1Value - layerbreak1Offset + 2;
// Layerbreak 2
br.BaseStream.Seek(0x5C, SeekOrigin.Begin);
long layerbreak2Offset = br.ReadInt32BigEndian();
long layerbreak2Value = br.ReadInt32BigEndian();
if (layerbreak2Value == 0)
return true;
layerbreak2 = layerbreak1 + layerbreak2Value - layerbreak2Offset + 2;
// Layerbreak 3
br.BaseStream.Seek(0x9C, SeekOrigin.Begin);
long layerbreak3Offset = br.ReadInt32BigEndian();
long layerbreak3Value = br.ReadInt32BigEndian();
if (layerbreak3Value == 0)
return true;
layerbreak3 = layerbreak2 + layerbreak3Value - layerbreak3Offset + 2;
return true;
}
}
catch
{
// We don't care what the error was
return false;
}
}
///
/// Get if LibCrypt data is detected in the subchannel file, if possible
///
/// .sub file location
/// Status of the LibCrypt data, if possible
private static bool? GetLibCryptDetected(string sub)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(sub))
return null;
return LibCrypt.CheckSubfile(sub);
}
///
/// Get the hex contents of the PIC file
///
/// Path to the PIC.bin file associated with the dump
/// PIC data as a hex string if possible, null on error
/// https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters
private static string GetPIC(string picPath)
{
// If the file doesn't exist, we can't get the info
if (!File.Exists(picPath))
return null;
try
{
string hex = GetFullFile(picPath, true);
return Regex.Replace(hex, ".{32}", "$0\n");
}
catch
{
// We don't care what the error was right now
return null;
}
}
///
/// Get the existance of an anti-modchip string from the input file, if possible
///
/// _disc.txt file location
/// Anti-modchip existance if possible, false on error
private static bool GetPlayStationAntiModchipDetected(string disc)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(disc))
return false;
using (StreamReader sr = File.OpenText(disc))
{
try
{
// Check for either antimod string
string line = sr.ReadLine().Trim();
while (!sr.EndOfStream)
{
if (line.StartsWith("Detected anti-mod string"))
return true;
else if (line.StartsWith("No anti-mod string"))
return false;
line = sr.ReadLine().Trim();
}
return false;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
}
///
/// Get the detected missing EDC count from the input files, if possible
///
/// .img_EdcEcc.txt file location
/// Status of PS1 EDC, if possible
private static bool? GetPlayStationEDCStatus(string edcecc)
{
// If one of the files doesn't exist, we can't get info from them
if (!File.Exists(edcecc))
return null;
// First line of defense is the EdcEcc error file
int modeTwoNoEdc = 0;
int modeTwoFormTwo = 0;
using (StreamReader sr = File.OpenText(edcecc))
{
try
{
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
if (line.Contains("mode 2 form 2"))
modeTwoFormTwo++;
else if (line.Contains("mode 2 no edc"))
modeTwoNoEdc++;
}
// This shouldn't happen
if (modeTwoNoEdc == 0 && modeTwoFormTwo == 0)
return null;
// EDC exists
else if (modeTwoNoEdc == 0 && modeTwoFormTwo != 0)
return true;
// EDC doesn't exist
else if (modeTwoNoEdc != 0 && modeTwoFormTwo == 0)
return false;
// This shouldn't happen
else if (modeTwoNoEdc != 0 && modeTwoFormTwo != 0)
return null;
// No idea how it would fall through
return null;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
}
///
/// Get the PVD from the input file, if possible
///
/// _mainInfo.txt file location
/// Newline-deliminated PVD if possible, null on error
private static string GetPVD(string mainInfo)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(mainInfo))
return null;
using (StreamReader sr = File.OpenText(mainInfo))
{
try
{
// If we're in a new mainInfo, the location of the header changed
string line = sr.ReadLine();
if (line.StartsWith("========== OpCode") || line.StartsWith("========== TOC (Binary)"))
{
// Seek to unscrambled data
while (!(line = sr.ReadLine()).StartsWith("========== Check Volume Descriptor ==========")) ;
// Read the next line so the search goes properly
line = sr.ReadLine();
}
// Make sure we're in the area
if (!line.StartsWith("========== LBA"))
while (!(line = sr.ReadLine()).StartsWith("========== LBA")) ;
// If we have a Sega disc, skip sector 0
if (line.StartsWith("========== LBA[000000, 0000000]: Main Channel =========="))
while (!(line = sr.ReadLine()).StartsWith("========== LBA")) ;
// If we have a PlayStation disc, skip sector 4
if (line.StartsWith("========== LBA[000004, 0x00004]: Main Channel =========="))
while (!(line = sr.ReadLine()).StartsWith("========== LBA")) ;
// We assume the first non-LBA0/4 sector listed is the proper one
// Fast forward to the PVD
while (!(line = sr.ReadLine()).StartsWith("0310")) ;
// Now that we're at the PVD, read each line in and concatenate
string pvd = "";
for (int i = 0; i < 6; i++)
pvd += sr.ReadLine() + "\n"; // 320-370
return pvd;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
}
///
/// Get the build info from a Saturn disc, if possible
///
/// <String representing a formatter variant of the Saturn header
/// True on successful extraction of info, false otherwise
private static bool GetSaturnBuildInfo(string segaHeader, out string serial, out string version, out string date)
{
serial = null; version = null; date = null;
// If the input header is null, we can't do a thing
if (string.IsNullOrWhiteSpace(segaHeader))
return false;
// Now read it in cutting it into lines for easier parsing
try
{
string[] header = segaHeader.Split('\n');
string serialVersionLine = header[2].Substring(58);
string dateLine = header[3].Substring(58);
serial = serialVersionLine.Substring(0, 8);
version = serialVersionLine.Substring(10, 6).TrimStart('V', 'v');
date = dateLine.Substring(0, 8);
return true;
}
catch
{
// We don't care what the error is
return false;
}
}
///
/// Get the build info from a Sega CD disc, if possible
///
/// <String representing a formatter variant of the Sega CD header
/// True on successful extraction of info, false otherwise
/// Note that this works for MOST headers, except ones where the copyright stretches > 1 line
private static bool GetSegaCDBuildInfo(string segaHeader, out string serial, out string date)
{
serial = null; date = null;
// If the input header is null, we can't do a thing
if (string.IsNullOrWhiteSpace(segaHeader))
return false;
// Now read it in cutting it into lines for easier parsing
try
{
string[] header = segaHeader.Split('\n');
string serialVersionLine = header[8].Substring(58);
string dateLine = header[1].Substring(58);
serial = serialVersionLine.Substring(3, 7);
date = dateLine.Substring(8).Trim();
// Properly format the date string, if possible
string[] dateSplit = date.Split('.');
if (dateSplit.Length == 1)
dateSplit = new string[] { date.Substring(0, 4), date.Substring(4) };
string month = dateSplit[1];
switch (month)
{
case "JAN":
dateSplit[1] = "01";
break;
case "FEB":
dateSplit[1] = "02";
break;
case "MAR":
dateSplit[1] = "03";
break;
case "APR":
dateSplit[1] = "04";
break;
case "MAY":
dateSplit[1] = "05";
break;
case "JUN":
dateSplit[1] = "06";
break;
case "JUL":
dateSplit[1] = "07";
break;
case "AUG":
dateSplit[1] = "08";
break;
case "SEP":
dateSplit[1] = "09";
break;
case "OCT":
dateSplit[1] = "10";
break;
case "NOV":
dateSplit[1] = "11";
break;
case "DEC":
dateSplit[1] = "12";
break;
default:
dateSplit[1] = "00";
break;
}
date = string.Join("-", dateSplit);
return true;
}
catch
{
// We don't care what the error is
return false;
}
}
///
/// Get the header from a Sega CD / Mega CD, Saturn, or Dreamcast Low-Density region, if possible
///
/// _mainInfo.txt file location
/// Header as a byte array if possible, null on error
private static string GetSegaHeader(string mainInfo)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(mainInfo))
return null;
using (StreamReader sr = File.OpenText(mainInfo))
{
try
{
// If we're in a new mainInfo, the location of the header changed
string line = sr.ReadLine();
if (line.StartsWith("========== OpCode") || line.StartsWith("========== TOC (Binary)"))
{
// Seek to unscrambled data
while (!(line = sr.ReadLine()).StartsWith("========== Check Volume Descriptor ==========")) ;
// Read the next line so the search goes properly
line = sr.ReadLine();
}
// Make sure we're in the area
if (!line.StartsWith("========== LBA"))
while (!(line = sr.ReadLine()).StartsWith("========== LBA")) ;
// Make sure we're in the right sector
if (!line.StartsWith("========== LBA[000000, 0000000]: Main Channel =========="))
while (!(line = sr.ReadLine()).StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) ;
// Fast forward to the header
while (!(line = sr.ReadLine()).Trim().StartsWith("+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F")) ;
// Now that we're at the Header, read each line in and concatenate
string header = "";
for (int i = 0; i < 32; i++)
header += sr.ReadLine() + "\n"; // 0000-01F0
return header;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
}
///
/// Get the write offset from the input file, if possible
///
/// _disc.txt file location
/// Sample write offset if possible, null on error
private static string GetWriteOffset(string disc)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(disc))
return null;
using (StreamReader sr = File.OpenText(disc))
{
try
{
// Fast forward to the offsets
while (!sr.ReadLine().Trim().StartsWith("========== Offset")) ;
sr.ReadLine(); // Combined Offset
sr.ReadLine(); // Drive Offset
sr.ReadLine(); // Separator line
// Now that we're at the offsets, attempt to get the sample offset
return sr.ReadLine().Split(' ').LastOrDefault();
}
catch
{
// We don't care what the exception is right now
return null;
}
}
}
///
/// Get the XGD auxiliary info from the outputted files, if possible
///
/// _disc.txt file location
/// True on successful extraction of info, false otherwise
private static bool GetXgdAuxInfo(string disc, out string dmihash, out string pfihash, out string sshash, out string ss, out string ssver)
{
dmihash = null; pfihash = null; sshash = null; ss = null; ssver = null;
// If the file doesn't exist, we can't get info from it
if (!File.Exists(disc))
return false;
// This flag is needed because recent versions of DIC include security data twice
bool foundSecuritySectors = false;
using (StreamReader sr = File.OpenText(disc))
{
try
{
while(!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
// Security Sector version
if (line.StartsWith("Version of challenge table"))
{
ssver = line.Split(' ')[4]; // "Version of challenge table: "
}
// Security Sector ranges
else if (line.StartsWith("Number of security sector ranges:") && !foundSecuritySectors)
{
// Set the flag so we don't read duplicate data
foundSecuritySectors = true;
Regex layerRegex = new Regex(@"Layer [01].*, startLBA-endLBA:\s*(\d+)-\s*(\d+)");
line = sr.ReadLine().Trim();
while (!line.StartsWith("========== TotalLength ==========")
&& !line.StartsWith("========== Unlock 2 state(wxripper) =========="))
{
// If we have a recognized line format, parse it
if (line.StartsWith("Layer "))
{
var match = layerRegex.Match(line);
ss += $"{match.Groups[1]}-{match.Groups[2]}\n";
}
line = sr.ReadLine().Trim();
}
}
// Special File Hashes
else if (line.StartsWith("
/// Get the Xbox serial info from the DMI.bin file, if possible
///
/// DMI.bin file location
/// True on successful extraction of info, false otherwise
private static bool GetXboxDMIInfo(string dmi, out string serial, out string version, out RedumpRegion? region)
{
serial = null; version = null; region = RedumpRegion.World;
if (!File.Exists(dmi))
return false;
using (BinaryReader br = new BinaryReader(File.OpenRead(dmi)))
{
try
{
br.BaseStream.Seek(8, SeekOrigin.Begin);
char[] str = br.ReadChars(8);
serial = $"{str[0]}{str[1]}-{str[2]}{str[3]}{str[4]}";
version = $"1.{str[5]}{str[6]}";
region = GetXgdRegion(str[7]);
return true;
}
catch
{
return false;
}
}
}
///
/// Get the Xbox 360 serial info from the DMI.bin file, if possible
///
/// DMI.bin file location
/// True on successful extraction of info, false otherwise
private static bool GetXbox360DMIInfo(string dmi, out string serial, out string version, out RedumpRegion? region)
{
serial = null; version = null; region = RedumpRegion.World;
if (!File.Exists(dmi))
return false;
using (BinaryReader br = new BinaryReader(File.OpenRead(dmi)))
{
try
{
br.BaseStream.Seek(64, SeekOrigin.Begin);
char[] str = br.ReadChars(14);
serial = $"{str[0]}{str[1]}-{str[2]}{str[3]}{str[4]}{str[5]}";
version = $"1.{str[6]}{str[7]}";
region = GetXgdRegion(str[8]);
// str[9], str[10], str[11] - unknown purpose
// str[12], str[13] - disc <12> of <13>
return true;
}
catch
{
return false;
}
}
}
#endregion
}
}