Files
MPF/MPF.Library/DiscImageCreator/Parameters.cs
Matt Nadareski e2a27479f5 Display dump progress in log window (fixes #131)
This builds heavily upon the work that Jack put into capturing and processing DIC outputs. For now, there are only matchers for DIC outputs. All other programs will still direct to the log window, but may not be processed as neatly
2021-03-10 14:51:13 -08:00

3329 lines
135 KiB
C#

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
{
/// <summary>
/// Represents a generic set of DiscImageCreator parameters
/// </summary>
public class Parameters : BaseParameters
{
/// <summary>
/// Base command to run
/// </summary>
public Command BaseCommand { get; set; }
/// <summary>
/// Set of flags to pass to the executable
/// </summary>
protected Dictionary<Flag, bool?> _flags = new Dictionary<Flag, bool?>();
public bool? this[Flag key]
{
get
{
if (_flags.ContainsKey(key))
return _flags[key];
return null;
}
set
{
_flags[key] = value;
}
}
protected internal IEnumerable<Flag> Keys => _flags.Keys;
#region Common Input Values
/// <summary>
/// Drive letter or path to pass to DiscImageCreator
/// </summary>
public string DriveLetter { get; set; }
/// <summary>
/// Drive speed to set, if applicable
/// </summary>
public int? DriveSpeed { get; set; }
/// <summary>
/// Destination filename for DiscImageCreator output
/// </summary>
public string Filename { get; set; }
/// <summary>
/// Optiarc drive output filename for merging
/// </summary>
public string OptiarcFilename { get; set; }
/// <summary>
/// Start LBA value for dumping specific sectors
/// </summary>
public int? StartLBAValue { get; set; }
/// <summary>
/// End LBA value for dumping specific sectors
/// </summary>
public int? EndLBAValue { get; set; }
#endregion
#region Flag Values
/// <summary>
/// Manual offset for Audio CD (default 0)
/// </summary>
public int? AddOffsetValue { get; set; }
/// <summary>
/// 0xbe opcode value for dumping
/// Possible values: raw (default), pack
/// </summary>
/// TODO: Make this an enum
public string BEOpcodeValue { get; set; }
/// <summary>
/// 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)
/// </summary>
public int?[] C2OpcodeValue { get; set; } = new int?[4];
/// <summary>
/// End LBA for fixing
/// </summary>
public int? FixValue { get; set; }
/// <summary>
/// Set the force unit access flag value (default 1)
/// </summary>
public int? ForceUnitAccessValue { get; set; }
/// <summary>
/// Set the no skip security sector flag value (default 100)
/// </summary>
public int? NoSkipSecuritySectorValue { get; set; }
/// <summary>
/// Set the pad sector flag value (default 0)
/// </summary>
public int? PadSectorValue { get; set; }
/// <summary>
/// Set the reverse End LBA value (required for DVD)
/// </summary>
public int? ReverseEndLBAValue { get; set; }
/// <summary>
/// Set the reverse Start LBA value (required for DVD)
/// </summary>
public int? ReverseStartLBAValue { get; set; }
/// <summary>
/// Set scan file timeout value (default 60)
/// </summary>
public int? ScanFileProtectValue { get; set; }
/// <summary>
/// Beginning and ending sectors to skip for physical protection (both default 0)
/// </summary>
public int?[] SkipSectorValue { get; set; } = new int?[2];
/// <summary>
/// Set the subchanel read level
/// Possible values: 0 no next sub, 1 next sub (default), 2 next and next next
/// </summary>
public int? SubchannelReadLevelValue { get; set; }
/// <summary>
/// Set number of empty bytes to insert at the head of first track for VideoNow (default 0)
/// </summary>
public int? VideoNowValue { get; set; }
#endregion
/// <inheritdoc/>
public Parameters(string parameters)
: base(parameters)
{
this.InternalProgram = InternalProgram.DiscImageCreator;
}
/// <inheritdoc/>
public Parameters(KnownSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
{
this.InternalProgram = InternalProgram.DiscImageCreator;
if (options.DICQuietMode)
this[Flag.DisableBeep] = true;
}
/// <inheritdoc/>
public override string GenerateParameters()
{
List<string> parameters = new List<string>();
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);
}
/// <inheritdoc/>
public override string InputPath() => DriveLetter;
/// <inheritdoc/>
public override string OutputPath() => Filename;
/// <inheritdoc/>
public override int? GetSpeed() => DriveSpeed;
/// <inheritdoc/>
public override void SetSpeed(int? speed) => DriveSpeed = speed;
/// <inheritdoc/>
public override MediaType? GetMediaType() => Converters.ToMediaType(BaseCommand);
/// <inheritdoc/>
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;
}
}
/// <inheritdoc/>
protected override void ResetValues()
{
BaseCommand = Command.NONE;
DriveLetter = null;
DriveSpeed = null;
Filename = null;
StartLBAValue = null;
EndLBAValue = null;
_flags = new Dictionary<Flag, bool?>();
AddOffsetValue = null;
BEOpcodeValue = null;
C2OpcodeValue = new int?[4];
FixValue = null;
ForceUnitAccessValue = null;
NoSkipSecuritySectorValue = null;
ScanFileProtectValue = null;
SubchannelReadLevelValue = null;
VideoNowValue = null;
}
/// <inheritdoc/>
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 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;
}
}
/// <inheritdoc/>
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<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+")
.Cast<Match>()
.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;
}
/// <inheritdoc/>
public override (bool, List<string>) 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<string> missingFiles = new List<string>();
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);
}
/// <inheritdoc/>
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"));
}
/// <summary>
/// Get the list of commands that use a given flag
/// </summary>
/// <param name="flag">Flag value to get commands for</param>
/// <returns>List of DiscImageCreator.Commands, if possible</returns>
private static List<Command> GetSupportedCommands(Flag flag)
{
var commands = new List<Command>();
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;
}
/// <summary>
/// Set the DIC command to be used for a given system and media type
/// </summary>
/// <param name="system">KnownSystem value to check</param>
/// <param name="type">MediaType value to check</param>
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;
}
}
#region Information Extraction Methods
/// <summary>
/// Get the proper datfile from the input file, if possible
/// </summary>
/// <param name="dat">.dat file location</param>
/// <returns>Relevant pieces of the datfile, null on error</returns>
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() != "<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
return null;
if (sr.ReadLine() != "<!DOCTYPE datafile PUBLIC \"-//Logiqx//DTD ROM Management Datafile//EN\" \"http://www.logiqx.com/Dats/datafile.dtd\">")
return null;
// Fast forward to the rom lines
while (!sr.ReadLine().TrimStart().StartsWith("<game")) ;
sr.ReadLine(); // <category>Games</category>
sr.ReadLine(); // <description>Plextor</description>
// Now that we're at the relevant entries, read each line in and concatenate
string datString = "", line = sr.ReadLine().Trim();
while (line.StartsWith("<rom"))
{
datString += line + "\n";
line = sr.ReadLine().Trim();
}
return datString.TrimEnd('\n');
}
catch
{
// We don't care what the exception is right now
return null;
}
}
}
/// <summary>
/// Get the DVD protection information, if possible
/// </summary>
/// <param name="cssKey">_CSSKey.txt file location</param>
/// <param name="disc">_disc.txt file location</param>
/// <returns>Formatted string representing the DVD protection, null on error</returns>
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;
}
/// <summary>
/// Get the detected error count from the input files, if possible
/// </summary>
/// <param name="edcecc">.img_EdcEcc.txt/.img_EccEdc.txt file location</param>
/// <returns>Error count if possible, -1 on error</returns>
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;
}
}
}
/// <summary>
/// Get the build info from a GD-ROM LD area, if possible
/// </summary>
/// <<param name="segaHeader">String representing a formatter variant of the GD-ROM header</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
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;
}
}
/// <summary>
/// Get the layerbreak from the input file, if possible
/// </summary>
/// <param name="disc">_disc.txt file location</param>
/// <param name="xgd">True if XGD layerbreak info should be used, false otherwise</param>
/// <returns>Layerbreak if possible, null on error</returns>
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: <size> (L0 Video: <size>, L0 Middle: <size>, L0 Game: <size>)
return line.Split(' ')[1];
}
// Dual-layer discs have a regular layerbreak
else if (!xgd && line.StartsWith("LayerZeroSector"))
{
// LayerZeroSector: <size> (<hex>)
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;
}
}
}
/// <summary>
/// Get the layerbreak info associated with the dump
/// </summary>
/// <param name="picPath">Path to the PIC.bin file associated with the dump</param>
/// <returns>True if layerbreak info was set, false otherwise</returns>
/// <remarks>https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters</remarks>
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;
}
}
/// <summary>
/// Get if LibCrypt data is detected in the subchannel file, if possible
/// </summary>
/// <param name="sub">.sub file location</param>
/// <returns>Status of the LibCrypt data, if possible</returns>
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);
}
/// <summary>
/// Get the hex contents of the PIC file
/// </summary>
/// <param name="picPath">Path to the PIC.bin file associated with the dump</param>
/// <returns>PIC data as a hex string if possible, null on error</returns>
/// <remarks>https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters</remarks>
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;
}
}
/// <summary>
/// Get the existance of an anti-modchip string from the input file, if possible
/// </summary>
/// <param name="disc">_disc.txt file location</param>
/// <returns>Anti-modchip existance if possible, false on error</returns>
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;
}
}
}
/// <summary>
/// Get the detected missing EDC count from the input files, if possible
/// </summary>
/// <param name="edcecc">.img_EdcEcc.txt file location</param>
/// <returns>Status of PS1 EDC, if possible</returns>
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;
}
}
}
/// <summary>
/// Get the PVD from the input file, if possible
/// </summary>
/// <param name="mainInfo">_mainInfo.txt file location</param>
/// <returns>Newline-deliminated PVD if possible, null on error</returns>
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;
}
}
}
/// <summary>
/// Get the build info from a Saturn disc, if possible
/// </summary>
/// <<param name="segaHeader">String representing a formatter variant of the Saturn header</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
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;
}
}
/// <summary>
/// Get the build info from a Sega CD disc, if possible
/// </summary>
/// <<param name="segaHeader">String representing a formatter variant of the Sega CD header</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
/// <remarks>Note that this works for MOST headers, except ones where the copyright stretches > 1 line</remarks>
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;
}
}
/// <summary>
/// Get the header from a Sega CD / Mega CD, Saturn, or Dreamcast Low-Density region, if possible
/// </summary>
/// <param name="mainInfo">_mainInfo.txt file location</param>
/// <returns>Header as a byte array if possible, null on error</returns>
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;
}
}
}
/// <summary>
/// Get the write offset from the input file, if possible
/// </summary>
/// <param name="disc">_disc.txt file location</param>
/// <returns>Sample write offset if possible, null on error</returns>
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;
}
}
}
/// <summary>
/// Get the XGD auxiliary info from the outputted files, if possible
/// </summary>
/// <param name="disc">_disc.txt file location</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
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: <VER>"
}
// 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("<rom"))
{
if (line.Contains("SS.bin"))
sshash = line;
else if (line.Contains("PFI.bin"))
pfihash = line;
else if (line.Contains("DMI.bin"))
dmihash = line;
}
}
return true;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
}
/// <summary>
/// Get the Xbox serial info from the DMI.bin file, if possible
/// </summary>
/// <param name="dmi">DMI.bin file location</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
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;
}
}
}
/// <summary>
/// Get the Xbox 360 serial info from the DMI.bin file, if possible
/// </summary>
/// <param name="dmi">DMI.bin file location</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
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
}
}