Files
MPF/DICUI.Library/Utilities/DumpEnvironment.cs
Matt Nadareski ec23502275 Abstract out non-UI code to separate DLL (#122)
* Abstract out non-UI code to separate DLL

This change seems rather large, but it's mostly just moving anything that is not directly driving the UI to its own, separate library. This will make it easier at a later date for any UI improvements or changes as well as making the code much more discrete. One TODO has been added as a result of this change.

* Remove MessageBox from library

This change removes the last UI-driven elements from the library. This now makes the library (in theory) UI-agnostic and removes the TODO.

* Options is more UI-related

* Fix Nuget references in csproj

* Add Winforms UI

This is a clone of the current WPF UI but implemented in Windows Forms. WPF is not currently supported by Mono for Linux and macOS, so this can provide an alternative to those users who want to run on those systems instead. This also adds a second artifact for the Winforms build.
2018-10-08 11:06:22 -07:00

1653 lines
70 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using DICUI.Data;
namespace DICUI.Utilities
{
/// <summary>
/// Represents information for a single drive
/// </summary>
public class Drive
{
public char Letter { get; private set; }
public bool IsFloppy { get; private set; }
public string VolumeLabel { get; private set; }
public bool MarkedActive { get; private set; }
private Drive(char letter, string volumeLabel, bool isFloppy, bool markedActive)
{
this.Letter = letter;
this.IsFloppy = isFloppy;
this.VolumeLabel = volumeLabel;
this.MarkedActive = markedActive;
}
public static Drive Floppy(char letter) => new Drive(letter, null, true, true);
public static Drive Optical(char letter, string volumeLabel, bool active) => new Drive(letter, volumeLabel, false, active);
}
/// <summary>
/// Represents the state of all settings to be used during dumping
/// </summary>
public class DumpEnvironment
{
// Tool paths
public string DICPath;
public string SubdumpPath;
// Output paths
public string OutputDirectory;
public string OutputFilename;
// UI information
public Drive Drive;
public KnownSystem? System;
public MediaType? Type;
public bool IsFloppy { get => Drive.IsFloppy; }
public Parameters DICParameters;
// extra DIC arguments
public bool QuietMode;
public bool ParanoidMode;
public bool ScanForProtection;
public int RereadAmountC2;
// External process information
private Process dicProcess;
#region Public Functionality
/// <summary>
/// Cancel an in-progress dumping process
/// </summary>
public void CancelDumping()
{
try
{
if (dicProcess != null && !dicProcess.HasExited)
dicProcess.Kill();
}
catch
{ }
}
/// <summary>
/// Eject the disc using DIC
/// </summary>
public async void EjectDisc()
{
// Validate that the required program exists
if (!File.Exists(DICPath))
return;
CancelDumping();
// Validate we're not trying to eject a floppy disk
if (IsFloppy)
return;
Process childProcess;
await Task.Run(() =>
{
childProcess = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = DICPath,
Arguments = DICCommandStrings.Eject + " " + Drive.Letter,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
},
};
childProcess.Start();
childProcess.WaitForExit(1000);
// Just in case, we want to push a button 5 times to clear any errors
for (int i = 0; i < 5; i++)
childProcess.StandardInput.WriteLine("Y");
childProcess.Dispose();
});
}
/// <summary>
/// Gets if the current drive has the latest firmware
/// </summary>
/// <returns></returns>
public async Task<bool> DriveHasLatestFimrware()
{
// Validate that the required program exists
if (!File.Exists(DICPath))
return false;
// Use the drive speed command as a quick test
Process childProcess;
string output = await Task.Run(() =>
{
childProcess = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = DICPath,
Arguments = DICCommandStrings.DriveSpeed + " " + Drive.Letter,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
},
};
childProcess.Start();
childProcess.WaitForExit(1000);
// Just in case, we want to push a button 5 times to clear any errors
for (int i = 0; i < 5; i++)
childProcess.StandardInput.WriteLine("Y");
string stdout = childProcess.StandardOutput.ReadToEnd();
childProcess.Dispose();
return stdout;
});
// If we get the firmware message
if (output.Contains("[ERROR] This drive isn't latest firmware. Please update."))
return false;
// Otherwise, we know the firmware's good
return true;
}
/// <summary>
/// Get the full parameter string for DIC
/// </summary>
/// <param name="driveSpeed">Nullable int representing the drive speed</param>
/// <returns>String representing the params, null on error</returns>
public string GetFullParameters(int? driveSpeed)
{
// Populate with the correct params for inputs (if we're not on the default option)
if (System != KnownSystem.NONE && Type != MediaType.NONE)
{
// If drive letter is invalid, skip this
if (Drive == null)
return null;
FixOutputPaths();
// Set the proper parameters
DICParameters = new Parameters(System, Type, Drive.Letter, Path.Combine(OutputDirectory, OutputFilename), driveSpeed, ParanoidMode, RereadAmountC2);
if (QuietMode)
DICParameters[DICFlag.DisableBeep] = true;
// Generate and return the param string
return DICParameters.GenerateParameters();
}
return null;
}
/// <summary>
/// Execute a complete dump workflow
/// </summary>
public async Task<Result> StartDumping(IProgress<Result> progress)
{
Result result = IsValidForDump();
// Execute DIC and external tools, if needed
if (Validators.GetSupportStatus(System, Type)
&& !result.Message.Contains("not supported") // Completely unsupported media
&& !result.Message.Contains("submission info")) // Submission info-only media
{
// If the environment is invalid, return
if (!result)
return result;
progress?.Report(Result.Success("Executing DiscImageCreator... please wait!"));
await Task.Run(() => ExecuteDiskImageCreator());
progress?.Report(Result.Success("DiscImageCreator has finished!"));
// Execute additional tools
progress?.Report(Result.Success("Running any additional tools... please wait!"));
result = await Task.Run(() => ExecuteAdditionalToolsAfterDIC());
progress?.Report(result);
}
// Verify dump output and save it
progress?.Report(Result.Success("Gathering submission information... please wait!"));
result = await Task.Run(() => VerifyAndSaveDumpOutput(progress));
progress?.Report(Result.Success("All submission information gathered!"));
return result;
}
#endregion
#region Public for Testing Purposes
/// <summary>
/// Fix output paths to strip out any invalid characters
/// </summary>
public void FixOutputPaths()
{
try
{
// Cache if we had a directory separator or not
bool endedWithDirectorySeparator = OutputDirectory.EndsWith(Path.DirectorySeparatorChar.ToString());
// Normalize the path so the below filters work better
string combinedPath = Path.Combine(OutputDirectory, OutputFilename);
OutputDirectory = Path.GetDirectoryName(combinedPath);
OutputFilename = Path.GetFileName(combinedPath);
// If we have either a blank filename or path, just exit
if (String.IsNullOrWhiteSpace(OutputFilename) || String.IsNullOrWhiteSpace(OutputDirectory))
return;
// Take care of extra path characters
OutputDirectory = new StringBuilder(OutputDirectory.Replace('.', '_').Replace('&', '_'))
.Replace(':', '_', 0, OutputDirectory.LastIndexOf(':') == -1 ? 0 : OutputDirectory.LastIndexOf(':')).ToString();
OutputFilename = new StringBuilder(OutputFilename.Replace('&', '_'))
.Replace('.', '_', 0, OutputFilename.LastIndexOf('.') == -1 ? 0 : OutputFilename.LastIndexOf('.')).ToString();
// Sanitize everything else
foreach (char c in Path.GetInvalidPathChars())
OutputDirectory = OutputDirectory.Replace(c, '_');
foreach (char c in Path.GetInvalidFileNameChars())
OutputFilename = OutputFilename.Replace(c, '_');
// If we had a directory separator at the end before, add it again
if (endedWithDirectorySeparator)
OutputDirectory += Path.DirectorySeparatorChar;
}
catch
{
// We don't care what the error was
return;
}
}
/// <summary>
/// Checks if the parameters are valid
/// </summary>
/// <returns>True if the configuration is valid, false otherwise</returns>
public bool ParametersValid()
{
return DICParameters.IsValid() && !(IsFloppy ^ Type == MediaType.FloppyDisk);
}
/// <summary>
/// Ensures that all required output files have been created
/// </summary>
/// <returns></returns>
public bool FoundAllFiles()
{
// First, sanitized the output filename to strip off any potential extension
string outputFilename = Path.GetFileNameWithoutExtension(OutputFilename);
// Now ensure that all required files exist
string combinedBase = Path.Combine(OutputDirectory, outputFilename);
switch (Type)
{
case MediaType.CDROM:
case MediaType.GDROM: // TODO: Verify GD-ROM outputs this
return File.Exists(combinedBase + ".c2")
&& File.Exists(combinedBase + ".ccd")
&& File.Exists(combinedBase + ".cue")
&& File.Exists(combinedBase + ".dat")
&& File.Exists(combinedBase + ".img")
&& File.Exists(combinedBase + ".img_EdcEcc.txt")
&& File.Exists(combinedBase + ".scm")
&& File.Exists(combinedBase + ".sub")
&& File.Exists(combinedBase + "_c2Error.txt")
&& File.Exists(combinedBase + "_cmd.txt")
&& File.Exists(combinedBase + "_disc.txt")
&& File.Exists(combinedBase + "_drive.txt")
&& File.Exists(combinedBase + "_img.cue")
&& File.Exists(combinedBase + "_mainError.txt")
&& File.Exists(combinedBase + "_mainInfo.txt")
&& File.Exists(combinedBase + "_subError.txt")
&& File.Exists(combinedBase + "_subInfo.txt")
// && File.Exists(combinedBase + "_subIntention.txt")
&& (File.Exists(combinedBase + "_subReadable.txt") || File.Exists(combinedBase + "_sub.txt"))
&& File.Exists(combinedBase + "_volDesc.txt");
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
case MediaType.NintendoGameCube:
case MediaType.NintendoWiiOpticalDisc:
return File.Exists(combinedBase + ".dat")
&& File.Exists(combinedBase + "_cmd.txt")
&& File.Exists(combinedBase + "_disc.txt")
&& File.Exists(combinedBase + "_drive.txt")
&& File.Exists(combinedBase + "_mainError.txt")
&& File.Exists(combinedBase + "_mainInfo.txt")
&& File.Exists(combinedBase + "_volDesc.txt");
case MediaType.FloppyDisk:
return File.Exists(combinedBase + ".dat")
&& File.Exists(combinedBase + "_cmd.txt")
&& File.Exists(combinedBase + "_disc.txt");
case MediaType.UMD:
return File.Exists(combinedBase + "_disc.txt")
|| File.Exists(combinedBase + "_mainError.txt")
|| File.Exists(combinedBase + "_mainInfo.txt")
|| File.Exists(combinedBase + "_volDesc.txt");
default:
// Non-dumping commands will usually produce no output, so this is irrelevant
return true;
}
}
#endregion
#region Private Helpers
/// <summary>
/// Run any additional tools given a DumpEnvironment
/// </summary>
/// <returns>Result instance with the outcome</returns>
private Result ExecuteAdditionalToolsAfterDIC()
{
// Special cases
switch (System)
{
case KnownSystem.SegaSaturn:
if (!File.Exists(SubdumpPath))
return Result.Failure("Error! Could not find subdump!");
ExecuteSubdump();
return Result.Success("subdump has finished!");
}
return Result.Success("No external tools needed!");
}
/// <summary>
/// Run DiscImageCreator
/// </summary>
private void ExecuteDiskImageCreator()
{
dicProcess = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = DICPath,
Arguments = DICParameters.GenerateParameters() ?? "",
},
};
dicProcess.Start();
dicProcess.WaitForExit();
}
/// <summary>
/// Execute subdump for a (potential) Sega Saturn dump
/// </summary>
private async void ExecuteSubdump()
{
await Task.Run(() =>
{
Process childProcess = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = SubdumpPath,
Arguments = "-i " + Drive.Letter + ": -f " + Path.Combine(OutputDirectory, Path.GetFileNameWithoutExtension(OutputFilename) + "_subdump.sub") + "-mode 6 -rereadnum 25 -fix 2",
},
};
childProcess.Start();
childProcess.WaitForExit();
});
}
/// <summary>
/// Extract all of the possible information from a given input combination
/// </summary>
/// <param name="driveLetter">Drive letter to check</param>
/// <returns>Dictionary containing mapped output values, null on error</returns>
/// <remarks>TODO: Make sure that all special formats are accounted for</remarks>
private Dictionary<string, string> ExtractOutputInformation(IProgress<Result> progress)
{
// Ensure the current disc combination should exist
if (!Validators.GetValidMediaTypes(System).Contains(Type))
{
return null;
}
// Sanitize the output filename to strip off any potential extension
string outputFilename = Path.GetFileNameWithoutExtension(OutputFilename);
// Check that all of the relevant files are there
if (!FoundAllFiles())
{
return null;
}
// Create the output dictionary with all user-inputted values by default
string combinedBase = Path.Combine(OutputDirectory, outputFilename);
Dictionary<string, string> mappings = new Dictionary<string, string>
{
{ Template.TitleField, Template.RequiredValue },
{ Template.DiscNumberField, Template.OptionalValue },
{ Template.DiscTitleField, Template.OptionalValue },
{ Template.SystemField, System.Name() },
{ Template.MediaTypeField, Type.Name() },
{ Template.CategoryField, "Games" },
{ Template.RegionField, "World (CHANGE THIS)" },
{ Template.LanguagesField, "Klingon (CHANGE THIS)" },
{ Template.DiscSerialField, Template.RequiredIfExistsValue },
{ Template.BarcodeField, Template.OptionalValue},
{ Template.CommentsField, Template.OptionalValue },
{ Template.ContentsField, Template.OptionalValue },
{ Template.VersionField, Template.RequiredIfExistsValue },
{ Template.EditionField, "Original (VERIFY THIS)" },
{ Template.DATField, GetDatfile(combinedBase + ".dat") },
};
// Now we want to do a check by MediaType and extract all required info
switch (Type)
{
case MediaType.CDROM:
case MediaType.GDROM: // TODO: Verify GD-ROM outputs this
mappings[Template.MasteringRingField] = Template.RequiredIfExistsValue;
mappings[Template.MasteringSIDField] = Template.RequiredIfExistsValue;
mappings[Template.MouldSIDField] = Template.RequiredIfExistsValue;
mappings[Template.AdditionalMouldField] = Template.RequiredIfExistsValue;
mappings[Template.ToolstampField] = Template.RequiredIfExistsValue;
mappings[Template.PVDField] = GetPVD(combinedBase + "_mainInfo.txt") ?? "";
mappings[Template.ErrorCountField] = GetErrorCount(combinedBase + ".img_EdcEcc.txt").ToString();
mappings[Template.CuesheetField] = GetFullFile(combinedBase + ".cue") ?? "";
mappings[Template.WriteOffsetField] = GetWriteOffset(combinedBase + "_disc.txt") ?? "";
// System-specific options
switch (System)
{
case KnownSystem.AppleMacintosh:
case KnownSystem.EnhancedCD:
case KnownSystem.EnhancedDVD:
case KnownSystem.EnhancedBD:
case KnownSystem.IBMPCCompatible:
case KnownSystem.RainbowDisc:
mappings[Template.ISBNField] = Template.OptionalValue;
progress?.Report(Result.Success("Running copy protection scan... this might take a while!"));
mappings[Template.CopyProtectionField] = GetCopyProtection();
progress?.Report(Result.Success("Copy protection scan complete!"));
if (File.Exists(combinedBase + "_subIntention.txt"))
{
FileInfo fi = new FileInfo(combinedBase + "_subIntention.txt");
if (fi.Length > 0)
{
mappings[Template.SubIntentionField] = GetFullFile(combinedBase + "_subIntention.txt") ?? "";
}
}
break;
case KnownSystem.SegaSaturn:
mappings[Template.SaturnHeaderField] = GetSaturnHeader(combinedBase + "_mainInfo.txt") ?? "";
if (GetSaturnBuildInfo(mappings[Template.SaturnHeaderField], out string serial, out string version, out string buildDate))
{
mappings[Template.DiscSerialField] = serial ?? "";
mappings[Template.VersionField] = version ?? "";
mappings[Template.SaturnBuildDateField] = buildDate ?? "";
}
break;
case KnownSystem.SonyPlayStation:
mappings[Template.PlaystationEXEDateField] = GetPlayStationEXEDate(Drive.Letter) ?? "";
mappings[Template.PlayStationEDCField] = GetMissingEDCCount(combinedBase + ".img_EdcEcc.txt") > 0 ? "No" : "Yes";
mappings[Template.PlayStationAntiModchipField] = GetAntiModchipDetected(combinedBase + "_disc.txt") ? "Yes" : "No";
mappings[Template.PlayStationLibCryptField] = "No";
if (File.Exists(combinedBase + "_subIntention.txt"))
{
FileInfo fi = new FileInfo(combinedBase + "_subIntention.txt");
if (fi.Length > 0)
{
mappings[Template.PlayStationLibCryptField] = "Yes";
mappings[Template.SubIntentionField] = GetFullFile(combinedBase + "_subIntention.txt") ?? "";
}
}
break;
case KnownSystem.SonyPlayStation2:
mappings[Template.PlaystationEXEDateField] = GetPlayStationEXEDate(Drive.Letter) ?? "";
mappings[Template.VersionField] = GetPlayStation2Version(Drive.Letter) ?? "";
break;
}
break;
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
bool isXbox = (System == KnownSystem.MicrosoftXBOX || System == KnownSystem.MicrosoftXBOX360);
string layerbreak = GetLayerbreak(combinedBase + "_disc.txt", isXbox) ?? "";
// If we have a single-layer disc
if (String.IsNullOrWhiteSpace(layerbreak))
{
switch (Type)
{
case MediaType.DVD:
mappings[Template.MediaTypeField] += "-5";
break;
case MediaType.BluRay:
mappings[Template.MediaTypeField] += "-25";
break;
}
mappings[Template.MasteringRingField] = Template.RequiredIfExistsValue;
mappings[Template.MasteringSIDField] = Template.RequiredIfExistsValue;
mappings[Template.MouldSIDField] = Template.RequiredIfExistsValue;
mappings[Template.AdditionalMouldField] = Template.RequiredIfExistsValue;
mappings[Template.ToolstampField] = Template.RequiredIfExistsValue;
mappings[Template.PVDField] = GetPVD(combinedBase + "_mainInfo.txt") ?? "";
}
// If we have a dual-layer disc
else
{
switch (Type)
{
case MediaType.DVD:
mappings[Template.MediaTypeField] += "-9";
break;
case MediaType.BluRay:
mappings[Template.MediaTypeField] += "-50";
break;
}
mappings["Outer " + Template.MasteringRingField] = Template.RequiredIfExistsValue;
mappings["Inner " + Template.MasteringRingField] = Template.RequiredIfExistsValue;
mappings["Outer " + Template.MasteringSIDField] = Template.RequiredIfExistsValue;
mappings["Inner " + Template.MasteringSIDField] = Template.RequiredIfExistsValue;
mappings[Template.MouldSIDField] = Template.RequiredIfExistsValue;
mappings[Template.AdditionalMouldField] = Template.RequiredIfExistsValue;
mappings["Outer " + Template.ToolstampField] = Template.RequiredIfExistsValue;
mappings["Inner " + Template.ToolstampField] = Template.RequiredIfExistsValue;
mappings[Template.PVDField] = GetPVD(combinedBase + "_mainInfo.txt") ?? "";
mappings[Template.LayerbreakField] = layerbreak;
}
// System-specific options
switch (System)
{
case KnownSystem.AppleMacintosh:
case KnownSystem.EnhancedCD:
case KnownSystem.EnhancedDVD:
case KnownSystem.EnhancedBD:
case KnownSystem.IBMPCCompatible:
case KnownSystem.RainbowDisc:
mappings[Template.ISBNField] = Template.OptionalValue;
progress?.Report(Result.Success("Running copy protection scan... this might take a while!"));
mappings[Template.CopyProtectionField] = GetCopyProtection();
progress?.Report(Result.Success("Copy protection scan complete!"));
if (File.Exists(combinedBase + "_subIntention.txt"))
{
FileInfo fi = new FileInfo(combinedBase + "_subIntention.txt");
if (fi.Length > 0)
{
mappings[Template.SubIntentionField] = GetFullFile(combinedBase + "_subIntention.txt") ?? "";
}
}
break;
case KnownSystem.DVDVideo:
mappings[Template.CopyProtectionField] = GetDVDProtection(combinedBase + "_CSSKey.txt", combinedBase + "_disc.txt") ?? "";
break;
case KnownSystem.MicrosoftXBOX:
case KnownSystem.MicrosoftXBOX360:
if (GetXBOXAuxInfo(combinedBase + "_disc.txt", out string dmihash, out string pfihash, out string sshash, out string ss, out string ssver))
{
mappings[Template.XBOXDMIHash] = dmihash ?? "";
mappings[Template.XBOXPFIHash] = pfihash ?? "";
mappings[Template.XBOXSSHash] = sshash ?? "";
mappings[Template.XBOXSSVersion] = ssver ?? "";
mappings[Template.XBOXSSRanges] = ss ?? "";
}
break;
case KnownSystem.SonyPlayStation2:
mappings[Template.PlaystationEXEDateField] = GetPlayStationEXEDate(Drive.Letter) ?? "";
mappings[Template.VersionField] = GetPlayStation2Version(Drive.Letter) ?? "";
break;
case KnownSystem.SonyPlayStation4:
mappings[Template.PlayStation4PICField] = GetPlayStation4PIC(Path.Combine(OutputDirectory, "PIC.bin")) ?? "";
mappings[Template.VersionField] = GetPlayStation4Version(Drive.Letter) ?? "";
break;
}
break;
case MediaType.UMD:
mappings[Template.PVDField] = GetPVD(combinedBase + "_mainInfo.txt") ?? "";
mappings[Template.DATField] = Template.RequiredValue + " [Not automatically generated for UMD]";
if (GetUMDAuxInfo(combinedBase + "_disc.txt", out string title, out string umdversion, out string umdlayer))
{
mappings[Template.TitleField] = title ?? "";
mappings[Template.VersionField] = umdversion ?? "";
if (!String.IsNullOrWhiteSpace(umdlayer))
mappings[Template.LayerbreakField] = umdlayer ?? "";
}
break;
}
return mappings;
}
/// <summary>
/// Format the output data in a human readable way, separating each printed line into a new item in the list
/// </summary>
/// <param name="info">Information dictionary that should contain normalized values</param>
/// <returns>List of strings representing each line of an output file, null on error</returns>
/// <remarks>TODO: Get full list of customizable stuff for other systems</remarks>
private List<string> FormatOutputData(Dictionary<string, string> info)
{
// Check to see if the inputs are valid
if (info == null)
{
return null;
}
try
{
List<string> output = new List<string>();
output.Add(Template.TitleField + ": " + info[Template.TitleField]);
output.Add(Template.DiscNumberField + ": " + info[Template.DiscNumberField]);
output.Add(Template.DiscTitleField + ": " + info[Template.DiscTitleField]);
output.Add(Template.SystemField + ": " + info[Template.SystemField]);
output.Add(Template.MediaTypeField + ": " + info[Template.MediaTypeField]);
output.Add(Template.CategoryField + ": " + info[Template.CategoryField]);
output.Add(Template.RegionField + ": " + info[Template.RegionField]);
output.Add(Template.LanguagesField + ": " + info[Template.LanguagesField]);
output.Add(Template.DiscSerialField + ": " + info[Template.DiscSerialField]);
switch (System)
{
case KnownSystem.SegaSaturn:
output.Add(Template.SaturnBuildDateField + ": " + info[Template.SaturnBuildDateField]);
break;
case KnownSystem.SonyPlayStation:
case KnownSystem.SonyPlayStation2:
output.Add(Template.PlaystationEXEDateField + ": " + info[Template.PlaystationEXEDateField]);
break;
}
output.Add("Ringcode Information:");
switch (Type)
{
case MediaType.CDROM:
case MediaType.GDROM:
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
// If we have a dual-layer disc
if (info.ContainsKey(Template.LayerbreakField))
{
output.Add("\tOuter " + Template.MasteringRingField + ": " + info["Outer " + Template.MasteringRingField]);
output.Add("\tInner " + Template.MasteringRingField + ": " + info["Inner " + Template.MasteringRingField]);
output.Add("\tOuter " + Template.MasteringSIDField + ": " + info["Outer " + Template.MasteringSIDField]);
output.Add("\tInner " + Template.MasteringSIDField + ": " + info["Inner " + Template.MasteringSIDField]);
output.Add("\t" + Template.MouldSIDField + ": " + info[Template.MouldSIDField]);
output.Add("\t" + Template.AdditionalMouldField + ": " + info[Template.AdditionalMouldField]);
output.Add("\tOuter " + Template.ToolstampField + ": " + info["Outer " + Template.ToolstampField]);
output.Add("\tInner " + Template.ToolstampField + ": " + info["Inner " + Template.ToolstampField]);
}
// If we have a single-layer disc
else
{
output.Add("\t" + Template.MasteringRingField + ": " + info[Template.MasteringRingField]);
output.Add("\t" + Template.MasteringSIDField + ": " + info[Template.MasteringSIDField]);
output.Add("\t" + Template.MouldSIDField + ": " + info[Template.MouldSIDField]);
output.Add("\t" + Template.AdditionalMouldField + ": " + info[Template.AdditionalMouldField]);
output.Add("\t" + Template.ToolstampField + ": " + info[Template.ToolstampField]);
}
break;
}
output.Add(Template.BarcodeField + ": " + info[Template.BarcodeField]);
switch (System)
{
case KnownSystem.AppleMacintosh:
case KnownSystem.EnhancedCD:
case KnownSystem.EnhancedDVD:
case KnownSystem.EnhancedBD:
case KnownSystem.IBMPCCompatible:
case KnownSystem.RainbowDisc:
output.Add(Template.ISBNField + ": " + info[Template.ISBNField]);
break;
}
switch (Type)
{
case MediaType.CDROM:
case MediaType.GDROM:
output.Add(Template.ErrorCountField + ": " + info[Template.ErrorCountField]);
break;
}
output.Add(Template.CommentsField + ": " + info[Template.CommentsField]);
output.Add(Template.ContentsField + ": " + info[Template.ContentsField]);
output.Add(Template.VersionField + ": " + info[Template.VersionField]);
output.Add(Template.EditionField + ": " + info[Template.EditionField]);
switch (System)
{
case KnownSystem.SegaSaturn:
output.Add(Template.SaturnHeaderField + ":"); output.Add("");
output.AddRange(info[Template.SaturnHeaderField].Split('\n')); output.Add("");
break;
case KnownSystem.SonyPlayStation:
output.Add(Template.PlayStationEDCField + ": " + info[Template.PlayStationEDCField]);
output.Add(Template.PlayStationAntiModchipField + ": " + info[Template.PlayStationAntiModchipField]);
output.Add(Template.PlayStationLibCryptField + ": " + info[Template.PlayStationLibCryptField]);
break;
}
switch (Type)
{
case MediaType.DVD:
case MediaType.BluRay:
case MediaType.UMD:
// If we have a dual-layer disc
if (info.ContainsKey(Template.LayerbreakField))
{
output.Add(Template.LayerbreakField + ": " + info[Template.LayerbreakField]);
}
break;
}
output.Add(Template.PVDField + ":"); output.Add("");
output.AddRange(info[Template.PVDField].Split('\n'));
switch (System)
{
case KnownSystem.AppleMacintosh:
case KnownSystem.EnhancedCD:
case KnownSystem.EnhancedDVD:
case KnownSystem.EnhancedBD:
case KnownSystem.IBMPCCompatible:
case KnownSystem.RainbowDisc:
output.Add(Template.CopyProtectionField + ": " + info[Template.CopyProtectionField]); output.Add("");
break;
case KnownSystem.DVDVideo:
output.Add(Template.CopyProtectionField + ":"); output.Add("");
output.AddRange(info[Template.CopyProtectionField].Split('\n'));
break;
case KnownSystem.MicrosoftXBOX:
case KnownSystem.MicrosoftXBOX360:
if (Type == MediaType.DVD)
{
output.Add(Template.XBOXDMIHash + ": " + info[Template.XBOXDMIHash]);
output.Add(Template.XBOXPFIHash + ": " + info[Template.XBOXPFIHash]);
output.Add(Template.XBOXSSHash + ": " + info[Template.XBOXSSHash]); output.Add("");
output.Add(Template.XBOXSSVersion + ": " + info[Template.XBOXSSVersion]);
output.Add(Template.XBOXSSRanges + ":"); output.Add("");
output.AddRange(info[Template.XBOXSSRanges].Split('\n'));
}
break;
case KnownSystem.SonyPlayStation4:
output.Add(Template.PlayStation4PICField + ":"); output.Add("");
output.AddRange(info[Template.PlayStation4PICField].Split('\n'));
break;
}
if (info.ContainsKey(Template.SubIntentionField))
{
output.Add(Template.SubIntentionField + ":"); output.Add("");
output.AddRange(info[Template.SubIntentionField].Split('\n')); output.Add("");
}
switch (Type)
{
case MediaType.CDROM:
case MediaType.GDROM:
output.Add(Template.CuesheetField + ":"); output.Add("");
output.AddRange(info[Template.CuesheetField].Split('\n')); output.Add("");
output.Add(Template.WriteOffsetField + ": " + info[Template.WriteOffsetField]); output.Add("");
break;
}
if (info.ContainsKey(Template.DATField))
{
output.Add(Template.DATField + ":"); output.Add("");
output.AddRange(info[Template.DATField].Split('\n'));
}
return output;
}
catch
{
// We don't care what the error is
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>Antimodchip existance if possible, false on error</returns>
private bool GetAntiModchipDetected(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 current copy protection scheme, if possible
/// </summary>
/// <returns>Copy protection scheme if possible, null on error</returns>
private string GetCopyProtection()
{
if (ScanForProtection)
return Task.Run(() => Validators.RunProtectionScanOnPath(Drive.Letter + ":\\")).GetAwaiter().GetResult();
return "(CHECK WITH PROTECTIONID)";
}
/// <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 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 pvd = "", line = sr.ReadLine().Trim();
while (line.StartsWith("<rom"))
{
pvd += line + "\n";
line = sr.ReadLine().Trim();
}
return pvd.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 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, encryptedDiscKey = null, playerKey = 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("[001]"))
encryptedDiscKey = line.Substring("[001]: ".Length);
else if (line.StartsWith("PlayerKey"))
playerKey = line.Substring("PlayerKey[1]: ".Length);
else if (line.StartsWith("DecryptedDiscKey"))
decryptedDiscKey = line.Substring("DecryptedDiscKey[020]: ".Length);
}
}
catch { }
}
}
// Now we format everything we can
string protection = "";
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(encryptedDiscKey))
protection += $"Encrypted Disc Key: {encryptedDiscKey}\n";
if (!String.IsNullOrEmpty(playerKey))
protection += $"Player Key: {playerKey}\n";
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 file location</param>
/// <returns>Error count if possible, -1 on error</returns>
private 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;
}
// 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]"))
return 0;
else if (line.StartsWith("Total errors"))
{
if (Int64.TryParse(line.Substring("Total errors: ".Length).Trim(), out long te))
return te;
else
return Int64.MinValue;
}
}
// If we haven't found anything, return -1
return -1;
}
catch
{
// We don't care what the exception is right now
return Int64.MaxValue;
}
}
}
/// <summary>
/// Get the full lines from the input file, if possible
/// </summary>
/// <param name="filename">file location</param>
/// <returns>Full text of the file, null on error</returns>
private string GetFullFile(string filename)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(filename))
{
return null;
}
return string.Join("\n", File.ReadAllLines(filename));
}
/// <summary>
/// Get the layerbreak from the input file, if possible
/// </summary>
/// <param name="disc">_disc.txt file location</param>
/// <param name="ignoreFirst">True if the first sector length is to be ignored, false otherwise</param>
/// <returns>Layerbreak if possible, null on error</returns>
private string GetLayerbreak(string disc, bool ignoreFirst)
{
// 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 layerbreak
string line = sr.ReadLine();
while (line != null)
{
// We definitely found a single-layer disc
if (line.Contains("NumberOfLayers: Single Layer"))
return null;
else if (line.Trim().StartsWith("========== SectorLength =========="))
{
// Skip the first one and unset the flag
if (ignoreFirst)
ignoreFirst = false;
else
break;
}
line = sr.ReadLine();
}
// Now that we're at the layerbreak line, attempt to get the decimal version
return sr.ReadLine().Trim().Split(' ')[1];
}
catch
{
// We don't care what the exception is right now
return null;
}
}
}
/// <summary>
/// Get the detected missing EDC count from the input files, if possible
/// </summary>
/// <param name="edcecc">.img_EdcEcc.txt file location</param>
/// <returns>Missing EDC count if possible, -1 on error</returns>
private long GetMissingEDCCount(string edcecc)
{
// If one of the files doesn't exist, we can't get info from them
if (!File.Exists(edcecc))
{
return -1;
}
// First line of defense is the EdcEcc error file
using (StreamReader sr = File.OpenText(edcecc))
{
try
{
// Fast forward to the PVD
string line = sr.ReadLine();
while (!line.StartsWith("[INFO] Number of sector(s) where EDC doesn't exist: "))
{
line = sr.ReadLine();
}
return Int64.Parse(line.Remove(0, "[INFO] Number of sector(s) where EDC doesn't exist: ".Length).Trim());
}
catch
{
// We don't care what the exception is right now
return -1;
}
}
}
/// <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 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
{
// Make sure we're in the right sector
while (!sr.ReadLine().StartsWith("========== LBA[000016, 0x00010]: Main Channel =========="));
// Fast forward to the PVD
while (!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 EXE date from a PlayStation disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>EXE date in "yyyy-mm-dd" format if possible, null on error</returns>
private string GetPlayStationEXEDate(char driveLetter)
{
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
{
return null;
}
// If we can't find SYSTEM.CNF, we don't have a PlayStation disc
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
if (!File.Exists(systemCnfPath))
{
return null;
}
// Let's try reading SYSTEM.CNF to find the "BOOT" value
string exeName = null;
try
{
using (StreamReader sr = File.OpenText(systemCnfPath))
{
// Not assuming proper ordering, just in case
string line = sr.ReadLine();
while (!line.StartsWith("BOOT"))
{
line = sr.ReadLine();
}
// Once it finds the "BOOT" line, extract the name
exeName = Regex.Match(line, @"BOOT.? = cdrom.?:\\(.*?);.*").Groups[1].Value;
}
}
catch
{
// We don't care what the error was
return null;
}
// Now that we have the EXE name, try to get the fileinfo for it
string exePath = Path.Combine(drivePath, exeName);
if (!File.Exists(exePath))
{
return null;
}
FileInfo fi = new FileInfo(exePath);
return fi.LastWriteTimeUtc.ToString("yyyy-MM-dd");
}
/// <summary>
/// Get the version from a PlayStation 2 disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Game version if possible, null on error</returns>
private string GetPlayStation2Version(char driveLetter)
{
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
{
return null;
}
// If we can't find SYSTEM.CNF, we don't have a PlayStation 2 disc
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
if (!File.Exists(systemCnfPath))
{
return null;
}
// Let's try reading SYSTEM.CNF to find the "VER" value
try
{
using (StreamReader sr = File.OpenText(systemCnfPath))
{
// Not assuming proper ordering, just in case
string line = sr.ReadLine();
while (!line.StartsWith("VER"))
{
line = sr.ReadLine();
}
// Once it finds the "VER" line, extract the version
return Regex.Match(line, @"VER\s*=\s*(.*)").Groups[1].Value;
}
}
catch
{
// We don't care what the error was
return null;
}
}
/// <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 string GetPlayStation4PIC(string picPath)
{
// If the file doesn't exist, we can't get the info
if (!File.Exists(picPath))
{
return null;
}
try
{
using (BinaryReader br = new BinaryReader(File.OpenRead(picPath)))
{
string hex = BitConverter.ToString(br.ReadBytes(140)).Replace("-", string.Empty);
return Regex.Replace(hex, ".{32}", "$0\n");
}
}
catch
{
// We don't care what the error was right now
return null;
}
}
/// <summary>
/// Get the version from a PlayStation 4 disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Game version if possible, null on error</returns>
private string GetPlayStation4Version(char driveLetter)
{
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
{
return null;
}
// If we can't find param.sfo, we don't have a PlayStation 4 disc
string paramSfoPath = Path.Combine(drivePath, "bd", "param.sfo");
if (!File.Exists(paramSfoPath))
{
return null;
}
// Let's try reading param.sfo to find the version at the end of the file
try
{
using (BinaryReader br = new BinaryReader(File.OpenRead(paramSfoPath)))
{
br.BaseStream.Seek(0x9A4, SeekOrigin.Begin);
return new string(br.ReadChars(5));
}
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the header from a Saturn disc, if possible
/// </summary>
/// <param name="mainInfo">_mainInfo.txt file location</param>
/// <returns>Header as a byte array if possible, null on error</returns>
private string GetSaturnHeader(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
{
// Make sure we're in the right sector
while (!sr.ReadLine().StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) ;
// Fast forward to the header
while (!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 < 16; i++)
{
header += sr.ReadLine() + "\n"; // 0000-00F0
}
return header;
}
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="saturnHeader">String representing a formatter variant of the Saturn header</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
private bool GetSaturnBuildInfo(string saturnHeader, 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(saturnHeader))
{
return false;
}
// Now read it in cutting it into lines for easier parsing
try
{
string[] header = saturnHeader.Split('\n');
string serialVersionLine = header[2].Substring(58);
string dateLine = header[3].Substring(58);
serial = serialVersionLine.Substring(0, 8);
version = serialVersionLine.Substring(10, 6);
date = dateLine.Substring(0, 8);
return true;
}
catch
{
// We don't care what the error is
return false;
}
}
/// <summary>
/// Get the UMD 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 bool GetUMDAuxInfo(string disc, out string title, out string umdversion, out string umdlayer)
{
title = null; umdversion = null; umdlayer = null;
// 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
{
// Loop through everything to get the first instance of each required field
string line = string.Empty;
while (!sr.EndOfStream)
{
line = sr.ReadLine().Trim();
if (line.StartsWith("TITLE") && title == null)
title = line.Substring("TITLE: ".Length);
else if (line.StartsWith("version") && umdversion == null)
umdversion = line.Substring("version: ".Length);
else if (line.StartsWith("L0 length"))
umdlayer = line.Split(' ')[2];
}
return true;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
}
/// <summary>
/// Get the XBOX/360 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 bool GetXBOXAuxInfo(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;
}
using (StreamReader sr = File.OpenText(disc))
{
try
{
// Fast forward to the Security Sector version and read it
while (!sr.ReadLine().Trim().StartsWith("CPR_MAI Key"));
ssver = sr.ReadLine().Trim().Split(' ')[4]; // "Version of challenge table: <VER>"
// Fast forward to the Security Sector Ranges
while (!sr.ReadLine().Trim().StartsWith("Number of security sector ranges:"));
// Now that we're at the ranges, read each line in and concatenate
Regex layerRegex = new Regex(@"Layer [01].*, startLBA-endLBA:\s*(\d+)-\s*(\d+)");
string line = sr.ReadLine().Trim();
while (!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();
}
// Fast forward to the aux hashes
while (!line.StartsWith("<rom"))
{
line = sr.ReadLine().Trim();
}
// Read in the hashes to the proper parts
while (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;
if (sr.EndOfStream)
break;
line = sr.ReadLine().Trim();
}
return true;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
}
/// <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 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>
/// Validate the current environment is ready for a dump
/// </summary>
/// <returns>Result instance with the outcome</returns>
private Result IsValidForDump()
{
// Validate that everything is good
if (!ParametersValid())
return Result.Failure("Error! Current configuration is not supported!");
FixOutputPaths();
// Validate that the required program exists
if (!File.Exists(DICPath))
return Result.Failure("Error! Could not find DiscImageCreator!");
return Result.Success();
}
/// <summary>
/// Verify that the current environment has a complete dump and create submission info is possible
/// </summary>
/// <returns>Result instance with the outcome</returns>
private Result VerifyAndSaveDumpOutput(IProgress<Result> progress)
{
// Check to make sure that the output had all the correct files
if (!FoundAllFiles())
return Result.Failure("Error! Please check output directory as dump may be incomplete!");
progress?.Report(Result.Success("Extracting output information from output files..."));
Dictionary<string, string> templateValues = ExtractOutputInformation(progress);
progress?.Report(Result.Success("Extracting information complete!"));
progress?.Report(Result.Success("Formatting extracted information..."));
List<string> formattedValues = FormatOutputData(templateValues);
progress?.Report(Result.Success("Formatting complete!"));
progress?.Report(Result.Success("Writing information to !submissionInfo.txt..."));
bool success = WriteOutputData(formattedValues);
progress?.Report(Result.Success("Writing complete!"));
return Result.Success();
}
/// <summary>
/// Write the data to the output folder
/// </summary>
/// <param name="lines">Preformatted list of lines to write out to the file</param>
/// <returns>True on success, false on error</returns>
private bool WriteOutputData(List<string> lines)
{
// Check to see if the inputs are valid
if (lines == null)
return false;
// Now write out to a generic file
try
{
using (StreamWriter sw = new StreamWriter(File.Open(Path.Combine(OutputDirectory, "!submissionInfo.txt"), FileMode.Create, FileAccess.Write)))
{
foreach (string line in lines)
sw.WriteLine(line);
}
}
catch
{
// We don't care what the error is right now
return false;
}
return true;
}
#endregion
}
}