using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.RegularExpressions; using MPF.Core.Data; 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 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 /// True to include output files as encoded artifacts, false otherwise public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, Options options, string basePath, Drive? drive, bool includeArtifacts); #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) => []; /// /// 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 virtual List GetLogFilePaths(string basePath) => []; #endregion #region Parameter Parsing /// /// Get the Base64 representation of a string /// /// String content to encode /// Base64-encoded contents, if possible protected static string? GetBase64(string? content) { if (string.IsNullOrEmpty(content)) return null; byte[] temp = Encoding.UTF8.GetBytes(content); return Convert.ToBase64String(temp); } /// /// Get the full lines from the input file, if possible /// /// file location /// True if should read as binary, false otherwise (default) /// Full text of the file, null on error protected static string? GetFullFile(string filename, bool binary = false) { // If the file doesn't exist, we can't get info from it if (!File.Exists(filename)) return null; // If we're reading as binary if (binary) { byte[] bytes = File.ReadAllBytes(filename); return BitConverter.ToString(bytes).Replace("-", string.Empty); } return File.ReadAllText(filename); } #endregion #region Methods to Move /// /// 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 = 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 } }