using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using MPF.Data; using MPF.Utilities; namespace MPF.CleanRip { /// /// Represents a generic set of CleanRip parameters /// public class Parameters : BaseParameters { #region Metadata /// public override InternalProgram InternalProgram => InternalProgram.CleanRip; #endregion /// public Parameters(string parameters) : base(parameters) { } /// public Parameters(KnownSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options) : base(system, type, driveLetter, filename, driveSpeed, options) { } #region BaseParameters Implementations /// public override (bool, List) CheckAllOutputFilesExist(string basePath) { List missingFiles = new List(); 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); } /// 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")); } #endregion #region Information Extraction Methods /// /// Get the hex contents of the BCA file /// /// Path to the BCA file associated with the dump /// BCA data as a hex string if possible, null on error /// https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters private static string 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; } } /// /// Get a formatted datfile from the cleanrip output, if possible /// /// Path to ISO file /// Path to discinfo file /// 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 $""; } catch { // We don't care what the exception is right now return null; } } } /// /// Get the extracted GC and Wii version /// /// Path to discinfo file /// Output region, if possible /// Output internal version of the game /// 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 } }