using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using MPF.Core.Data; using MPF.Core.Utilities; using SabreTools.RedumpLib.Data; namespace MPF.Core.Processors { public abstract class BaseProcessor { /// /// All found volume labels and their corresponding file systems /// public Dictionary>? VolumeLabels; #region Metadata /// /// Currently represented system /// public RedumpSystem? System { get; private set; } /// /// Currently represented media type /// public MediaType? Type { get; private set; } #endregion /// /// Generate processor for a system and media type combination /// /// RedumpSystem value to use /// MediaType value to use public BaseProcessor(RedumpSystem? system, MediaType? type) { System = system; Type = type; } #region Abstract Methods /// /// Validate if all required output files exist /// /// Base filename and path to use for checking /// True if this is a check done before a dump, false if done after /// Tuple of true if all required files exist, false otherwise and a list representing missing files public abstract (bool, List) CheckAllOutputFilesExist(string basePath, bool preCheck); /// /// Generate artifacts and add to the SubmissionInfo /// /// Base submission info to fill in specifics for /// Base filename and path to use for checking public abstract void GenerateArtifacts(SubmissionInfo submissionInfo, string basePath); /// /// Generate a SubmissionInfo for the output files /// /// Base submission info to fill in specifics for /// Options object representing user-defined options /// Base filename and path to use for checking /// Drive representing the disc to get information from /// Determines if outputs are processed according to Redump specifications public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, string basePath, Drive? drive, bool redumpCompat); /// /// Generate a list of all log files generated /// /// Base filename and path to use for checking /// List of all log file paths, empty otherwise public abstract List GetLogFilePaths(string basePath); #endregion #region Virtual Methods /// /// Generate a list of all deleteable files generated /// /// Base filename and path to use for checking /// List of all deleteable file paths, empty otherwise public virtual List GetDeleteableFilePaths(string basePath) => []; #endregion #region Methods to Move /// /// Ensures that all required output files have been created /// /// Output folder to write to /// Output filename to use as the base path /// Processor object representing how to process the outputs /// True if this is a check done before a dump, false if done after /// Tuple of true if all required files exist, false otherwise and a list representing missing files public (bool, List) FoundAllFiles(string? outputDirectory, string outputFilename, bool preCheck) { // First, sanitized the output filename to strip off any potential extension outputFilename = Path.GetFileNameWithoutExtension(outputFilename); // Then get the base path for all checking string basePath; if (string.IsNullOrEmpty(outputDirectory)) basePath = outputFilename; else basePath = Path.Combine(outputDirectory, outputFilename); // Finally, let the parameters say if all files exist return CheckAllOutputFilesExist(basePath, preCheck); } /// /// Get the hex contents of the PIC file /// /// Path to the PIC.bin file associated with the dump /// Number of characters to trim the PIC to, if -1, ignored /// PIC data as a hex string if possible, null on error /// https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters protected static string? GetPIC(string picPath, int trimLength = -1) { // If the file doesn't exist, we can't get the info if (!File.Exists(picPath)) return null; try { var hex = InfoTool.GetFullFile(picPath, true); if (hex == null) return null; if (trimLength > -1) hex = hex.Substring(0, trimLength); // TODO: Check for non-zero values in discarded PIC return Regex.Replace(hex, ".{32}", "$0\n", RegexOptions.Compiled); } catch { // We don't care what the error was right now return null; } } /// /// Get a isobuster-formatted PVD from a 2048 byte-per-sector image, if possible /// /// Path to ISO file /// Formatted PVD string, otherwise null /// True if PVD was successfully parsed, otherwise false protected static bool GetPVD(string isoPath, out string? pvd) { pvd = null; try { // Get PVD bytes from ISO file var buf = new byte[96]; using (FileStream iso = File.OpenRead(isoPath)) { // TODO: Don't hardcode 0x8320 iso.Seek(0x8320, SeekOrigin.Begin); int offset = 0; while (offset < 96) { int read = iso.Read(buf, offset, buf.Length - offset); if (read == 0) throw new EndOfStreamException(); offset += read; } } // Format PVD to isobuster standard char[] pvdCharArray = new char[96]; for (int i = 0; i < 96; i++) { if (buf[i] >= 0x20 && buf[i] <= 0x7E) pvdCharArray[i] = (char)buf[i]; else pvdCharArray[i] = '.'; } string pvdASCII = new string(pvdCharArray, 0, 96); pvd = string.Empty; for (int i = 0; i < 96; i += 16) { pvd += $"{(0x0320 + i):X4} : {buf[i]:X2} {buf[i + 1]:X2} {buf[i + 2]:X2} {buf[i + 3]:X2} {buf[i + 4]:X2} {buf[i + 5]:X2} {buf[i + 6]:X2} {buf[i + 7]:X2} " + $"{buf[i + 8]:X2} {buf[i + 9]:X2} {buf[i + 10]:X2} {buf[i + 11]:X2} {buf[i + 12]:X2} {buf[i + 13]:X2} {buf[i + 14]:X2} {buf[i + 15]:X2} {pvdASCII.Substring(i, 16)}\n"; } return true; } catch { // We don't care what the error is return false; } } #endregion } }