Files
MPF/MPF.Library/CleanRIp/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

274 lines
11 KiB
C#

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Data;
using MPF.Utilities;
namespace MPF.CleanRip
{
/// <summary>
/// Represents a generic set of CleanRip parameters
/// </summary>
public class Parameters : BaseParameters
{
/// <inheritdoc/>
public Parameters(string parameters)
: base(parameters)
{
this.InternalProgram = InternalProgram.CleanRip;
}
/// <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.CleanRip;
}
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath)
{
List<string> missingFiles = new List<string>();
switch (this.Type)
{
case MediaType.DVD: // Only added here to help users; not strictly correct
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (!File.Exists($"{basePath}-dumpinfo.txt"))
missingFiles.Add($"{basePath}-dumpinfo.txt");
if (!File.Exists($"{basePath}.bca"))
missingFiles.Add($"{basePath}.bca");
break;
default:
return (false, missingFiles);
}
return (!missingFiles.Any(), missingFiles);
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive)
{
info.TracksAndWriteOffsets.ClrMameProData = GetCleanripDatfile(basePath + ".iso", basePath + "-dumpinfo.txt");
// 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;
// Dual-layer discs have the same size and layerbreak
if (size == 8511160320)
info.SizeAndChecksums.Layerbreak = 2084960;
}
// Extract info based generically on MediaType
switch (this.Type)
{
case MediaType.DVD: // Only added here to help users; not strictly correct
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (File.Exists(basePath + ".bca"))
info.Extras.BCA = GetBCA(basePath + ".bca");
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out RedumpRegion? gcRegion, out string gcVersion))
{
info.CommonDiscInfo.Region = gcRegion ?? info.CommonDiscInfo.Region;
info.VersionAndEditions.Version = gcVersion ?? info.VersionAndEditions.Version;
}
break;
}
// Fill in any artifacts that exist, Base64-encoded
if (File.Exists(basePath + ".bca"))
info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true));
if (File.Exists(basePath + "-dumpinfo.txt"))
info.Artifacts["dumpinfo"] = GetBase64(GetFullFile(basePath + "-dumpinfo.txt"));
}
#region Information Extraction Methods
/// <summary>
/// Get the hex contents of the BCA file
/// </summary>
/// <param name="bcaPath">Path to the BCA file associated with the dump</param>
/// <returns>BCA 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 GetBCA(string bcaPath)
{
// If the file doesn't exist, we can't get the info
if (!File.Exists(bcaPath))
return null;
try
{
string hex = GetFullFile(bcaPath, true);
return Regex.Replace(hex, ".{32}", "$0\n");
}
catch
{
// We don't care what the error was right now
return null;
}
}
/// <summary>
/// Get a formatted datfile from the cleanrip output, if possible
/// </summary>
/// <param name="iso">Path to ISO file</param>
/// <param name="dumpinfo">Path to discinfo file</param>
/// <returns></returns>
private static string GetCleanripDatfile(string iso, string dumpinfo)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(dumpinfo))
return null;
using (StreamReader sr = File.OpenText(dumpinfo))
{
long size = new FileInfo(iso).Length;
string crc = string.Empty;
string md5 = string.Empty;
string sha1 = string.Empty;
try
{
// Make sure this file is a dumpinfo
if (!sr.ReadLine().Contains("--File Generated by CleanRip"))
return null;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
if (line.StartsWith("CRC32"))
crc = line.Substring(7).ToLowerInvariant();
else if (line.StartsWith("MD5"))
md5 = line.Substring(5);
else if (line.StartsWith("SHA-1"))
sha1 = line.Substring(7);
}
return $"<rom name=\"{Path.GetFileName(iso)}\" size=\"{size}\" crc=\"{crc}\" md5=\"{md5}\" sha1=\"{sha1}\" />";
}
catch
{
// We don't care what the exception is right now
return null;
}
}
}
/// <summary>
/// Get the extracted GC and Wii version
/// </summary>
/// <param name="dumpinfo">Path to discinfo file</param>
/// <param name="region">Output region, if possible</param>
/// <param name="version">Output internal version of the game</param>
/// <returns></returns>
private static bool GetGameCubeWiiInformation(string dumpinfo, out RedumpRegion? region, out string version)
{
region = null; version = null;
// If the file doesn't exist, we can't get info from it
if (!File.Exists(dumpinfo))
return false;
using (StreamReader sr = File.OpenText(dumpinfo))
{
try
{
// Make sure this file is a dumpinfo
if (!sr.ReadLine().Contains("--File Generated by CleanRip"))
return false;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
if (line.StartsWith("Version"))
{
version = line.Substring(9);
}
else if (line.StartsWith("Filename"))
{
string serial = line.Substring(10);
// char gameType = serial[0];
// string gameid = serial[1] + serial[2];
// string version = serial[4] + serial[5]
switch (serial[3])
{
case 'A':
region = RedumpRegion.World;
break;
case 'D':
region = RedumpRegion.Germany;
break;
case 'E':
region = RedumpRegion.USA;
break;
case 'F':
region = RedumpRegion.France;
break;
case 'I':
region = RedumpRegion.Italy;
break;
case 'J':
region = RedumpRegion.Japan;
break;
case 'K':
region = RedumpRegion.Korea;
break;
case 'L':
region = RedumpRegion.Europe; // Japanese import to Europe
break;
case 'M':
region = RedumpRegion.Europe; // American import to Europe
break;
case 'N':
region = RedumpRegion.USA; // Japanese import to USA
break;
case 'P':
region = RedumpRegion.Europe;
break;
case 'R':
region = RedumpRegion.Russia;
break;
case 'S':
region = RedumpRegion.Spain;
break;
case 'Q':
region = RedumpRegion.Korea; // Korea with Japanese language
break;
case 'T':
region = RedumpRegion.Korea; // Korea with English language
break;
case 'X':
region = null; // Not a real region code
break;
}
}
}
return true;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
}
#endregion
}
}