From 9dc976e42308c6521e3ab4db26850be3bdae906d Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 21 May 2024 13:10:02 -0400 Subject: [PATCH] Migrate processor functionality --- CHANGELIST.md | 1 + MPF.Core/DumpEnvironment.cs | 36 +- MPF.Core/InfoTool.cs | 25 +- MPF.Core/Modules/Aaru/Parameters.cs | 1451 ----------- MPF.Core/Modules/BaseParameters.cs | 134 +- MPF.Core/Modules/CleanRip/Parameters.cs | 380 +-- .../Modules/DiscImageCreator/Parameters.cs | 2163 ----------------- MPF.Core/Modules/PS3CFW/Parameters.cs | 212 +- MPF.Core/Modules/Redumper/Parameters.cs | 1676 ------------- .../Modules/UmdImageCreator/Parameters.cs | 310 +-- .../Modules/XboxBackupCreator/Parameters.cs | 652 +---- MPF.Core/SubmissionInfoTool.cs | 9 +- MPF.Core/UI/ViewModels/MainViewModel.cs | 26 +- 13 files changed, 65 insertions(+), 7010 deletions(-) diff --git a/CHANGELIST.md b/CHANGELIST.md index e3bbc528..4959a82d 100644 --- a/CHANGELIST.md +++ b/CHANGELIST.md @@ -3,6 +3,7 @@ - Create currently-unused processors - Move DataFile to Core.Data - Seal XBC processor +- Migrate processor functionality ### 3.1.9a (2024-05-21) diff --git a/MPF.Core/DumpEnvironment.cs b/MPF.Core/DumpEnvironment.cs index b17cf899..5f819200 100644 --- a/MPF.Core/DumpEnvironment.cs +++ b/MPF.Core/DumpEnvironment.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading.Tasks; using MPF.Core.Data; using MPF.Core.Modules; +using MPF.Core.Processors; using MPF.Core.Utilities; using SabreTools.RedumpLib; using SabreTools.RedumpLib.Data; @@ -58,6 +59,11 @@ namespace MPF.Core /// public BaseParameters? Parameters { get; private set; } + /// + /// Processor object representing post-dump processing + /// + public BaseProcessor? Processor { get; private set; } + #endregion #region Event Handlers @@ -128,6 +134,7 @@ namespace MPF.Core // Dumping program SetParameters(parameters); + SetProcessor(); } #region Public Functionality @@ -167,6 +174,28 @@ namespace MPF.Core } } + /// + /// Set the processor object based on the internal program + /// + public void SetProcessor() + { + Processor = InternalProgram switch + { + InternalProgram.Aaru => new Processors.Aaru(System, Type), + InternalProgram.CleanRip => new CleanRip(System, Type), + InternalProgram.DCDumper => null, // TODO: Create correct parameter type when supported + InternalProgram.DiscImageCreator => new DiscImageCreator(System, Type), + InternalProgram.PS3CFW => new PS3CFW(System, Type), + InternalProgram.Redumper => new Redumper(System, Type), + InternalProgram.UmdImageCreator => new UmdImageCreator(System, Type), + InternalProgram.XboxBackupCreator => new XboxBackupCreator(System, Type), + + // If no dumping program found, set to null + InternalProgram.NONE => null, + _ => null, + }; + } + /// /// Get the full parameter string for either DiscImageCreator or Aaru /// @@ -299,7 +328,7 @@ namespace MPF.Core var outputFilename = Path.GetFileName(OutputPath); // Check to make sure that the output had all the correct files - (bool foundFiles, List missingFiles) = Parameters.FoundAllFiles(outputDirectory, outputFilename, false); + (bool foundFiles, List missingFiles) = Processor.FoundAllFiles(outputDirectory, outputFilename, false); if (!foundFiles) { resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}")); @@ -315,6 +344,7 @@ namespace MPF.Core Type, Options, Parameters, + Processor, resultProgress, protectionProgress); resultProgress?.Report(Result.Success("Extracting information complete!")); @@ -408,7 +438,7 @@ namespace MPF.Core if (Options.CompressLogFiles) { resultProgress?.Report(Result.Success("Compressing log files...")); - (bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, filenameSuffix, outputFilename, Parameters); + (bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, filenameSuffix, outputFilename, Processor); if (compressSuccess) resultProgress?.Report(Result.Success(compressResult)); else @@ -419,7 +449,7 @@ namespace MPF.Core if (Options.DeleteUnnecessaryFiles) { resultProgress?.Report(Result.Success("Deleting unnecessary files...")); - (bool deleteSuccess, string deleteResult) = InfoTool.DeleteUnnecessaryFiles(outputDirectory, outputFilename, Parameters); + (bool deleteSuccess, string deleteResult) = InfoTool.DeleteUnnecessaryFiles(outputDirectory, outputFilename, Processor); if (deleteSuccess) resultProgress?.Report(Result.Success(deleteResult)); else diff --git a/MPF.Core/InfoTool.cs b/MPF.Core/InfoTool.cs index 7c5b7a90..a085e9ba 100644 --- a/MPF.Core/InfoTool.cs +++ b/MPF.Core/InfoTool.cs @@ -11,6 +11,7 @@ using System.Xml.Schema; using System.Xml.Serialization; using MPF.Core.Data; using MPF.Core.Modules; +using MPF.Core.Processors; using MPF.Core.Utilities; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -32,13 +33,13 @@ namespace MPF.Core /// /// Output folder to write to /// Output filename to use as the base path - /// Parameters object representing what to send to the internal program + /// 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 - internal static (bool, List) FoundAllFiles(this BaseParameters? parameters, string? outputDirectory, string outputFilename, bool preCheck) + internal static (bool, List) FoundAllFiles(this BaseProcessor? processor, string? outputDirectory, string outputFilename, bool preCheck) { // If there are no parameters set - if (parameters == null) + if (processor == null) return (false, new List()); // First, sanitized the output filename to strip off any potential extension @@ -52,7 +53,7 @@ namespace MPF.Core basePath = Path.Combine(outputDirectory, outputFilename); // Finally, let the parameters say if all files exist - return parameters.CheckAllOutputFilesExist(basePath, preCheck); + return processor.CheckAllOutputFilesExist(basePath, preCheck); } /// @@ -1153,15 +1154,15 @@ namespace MPF.Core /// Output folder to write to /// Output filename to use as the base path /// Output filename to use as the base path - /// Parameters object to use to derive log file paths + /// Processor object representing how to process the outputs /// True if the process succeeded, false otherwise - public static (bool, string) CompressLogFiles(string? outputDirectory, string? filenameSuffix, string outputFilename, BaseParameters? parameters) + public static (bool, string) CompressLogFiles(string? outputDirectory, string? filenameSuffix, string outputFilename, BaseProcessor? processor) { #if NET20 || NET35 || NET40 return (false, "Log compression is not available for this framework version"); #else // If there are no parameters - if (parameters == null) + if (processor == null) return (false, "No parameters provided!"); // Prepare the necessary paths @@ -1175,7 +1176,7 @@ namespace MPF.Core string archiveName = combinedBase + "_logs.zip"; // Get the list of log files from the parameters object - var files = parameters.GetLogFilePaths(combinedBase); + var files = processor.GetLogFilePaths(combinedBase); // Add on generated log files if they exist var mpfFiles = GetGeneratedFilePaths(outputDirectory, filenameSuffix); @@ -1246,12 +1247,12 @@ namespace MPF.Core /// /// Output folder to write to /// Output filename to use as the base path - /// Parameters object to use to derive log file paths + /// Processor object representing how to process the outputs /// True if the process succeeded, false otherwise - public static (bool, string) DeleteUnnecessaryFiles(string? outputDirectory, string outputFilename, BaseParameters? parameters) + public static (bool, string) DeleteUnnecessaryFiles(string? outputDirectory, string outputFilename, BaseProcessor? processor) { // If there are no parameters - if (parameters == null) + if (processor == null) return (false, "No parameters provided!"); // Prepare the necessary paths @@ -1263,7 +1264,7 @@ namespace MPF.Core combinedBase = Path.Combine(outputDirectory, outputFilename); // Get the list of deleteable files from the parameters object - var files = parameters.GetDeleteableFilePaths(combinedBase); + var files = processor.GetDeleteableFilePaths(combinedBase); if (!files.Any()) return (true, "No files to delete!"); diff --git a/MPF.Core/Modules/Aaru/Parameters.cs b/MPF.Core/Modules/Aaru/Parameters.cs index ccf12f56..d3fea41b 100644 --- a/MPF.Core/Modules/Aaru/Parameters.cs +++ b/MPF.Core/Modules/Aaru/Parameters.cs @@ -1,21 +1,9 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Text; using System.Text.RegularExpressions; -using System.Xml; -using System.Xml.Schema; -using System.Xml.Serialization; -using MPF.Core.Converters; using MPF.Core.Data; -using SabreTools.Models.CueSheets; -using SabreTools.RedumpLib; using SabreTools.RedumpLib.Data; -using Schemas; - -#pragma warning disable CS0618 // Ignore "Type or member is obsolete" -#pragma warning disable IDE0059 // Unnecessary assignment of a value namespace MPF.Core.Modules.Aaru { @@ -143,288 +131,6 @@ namespace MPF.Core.Modules.Aaru #region BaseParameters Implementations - /// - public override (bool, List) CheckAllOutputFilesExist(string basePath, bool preCheck) - { - var missingFiles = new List(); - switch (this.Type) - { - case MediaType.CDROM: - if (!File.Exists($"{basePath}_logs.zip") || !preCheck) - { - if (!File.Exists($"{basePath}.cicm.xml")) - missingFiles.Add($"{basePath}.cicm.xml"); - if (!File.Exists($"{basePath}.ibg")) - missingFiles.Add($"{basePath}.ibg"); - if (!File.Exists($"{basePath}.log")) - missingFiles.Add($"{basePath}.log"); - if (!File.Exists($"{basePath}.mhddlog.bin")) - missingFiles.Add($"{basePath}.mhddlog.bin"); - if (!File.Exists($"{basePath}.resume.xml")) - missingFiles.Add($"{basePath}.resume.xml"); - if (!File.Exists($"{basePath}.sub.log")) - missingFiles.Add($"{basePath}.sub.log"); - } - - break; - - case MediaType.DVD: - case MediaType.HDDVD: - case MediaType.BluRay: - if (!File.Exists($"{basePath}_logs.zip") || !preCheck) - { - if (!File.Exists($"{basePath}.cicm.xml")) - missingFiles.Add($"{basePath}.cicm.xml"); - if (!File.Exists($"{basePath}.ibg")) - missingFiles.Add($"{basePath}.ibg"); - if (!File.Exists($"{basePath}.log")) - missingFiles.Add($"{basePath}.log"); - if (!File.Exists($"{basePath}.mhddlog.bin")) - missingFiles.Add($"{basePath}.mhddlog.bin"); - if (!File.Exists($"{basePath}.resume.xml")) - missingFiles.Add($"{basePath}.resume.xml"); - } - - break; - - default: - missingFiles.Add("Media and system combination not supported for Aaru"); - break; - } - - return (!missingFiles.Any(), missingFiles); - } - - /// - public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts) - { - // TODO: Fill in submission info specifics for Aaru - var outputDirectory = Path.GetDirectoryName(basePath); - - // Ensure that required sections exist - info = Builder.EnsureAllSections(info); - - // TODO: Determine if there's an Aaru version anywhere - info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram); - info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + ".cicm.xml")?.ToString("yyyy-MM-dd HH:mm:ss"); - - // Deserialize the sidecar, if possible - var sidecar = GenerateSidecar(basePath + ".cicm.xml"); - - // Fill in the hardware data - if (GetHardwareInfo(sidecar, out var manufacturer, out var model, out var firmware)) - { - info.DumpingInfo.Manufacturer = manufacturer; - info.DumpingInfo.Model = model; - info.DumpingInfo.Firmware = firmware; - } - - // Fill in the disc type data - if (GetDiscType(sidecar, out var discType, out var discSubType)) - { - string fullDiscType = string.Empty; - if (!string.IsNullOrEmpty(discType) && !string.IsNullOrEmpty(discSubType)) - fullDiscType = $"{discType} ({discSubType})"; - else if (!string.IsNullOrEmpty(discType) && string.IsNullOrEmpty(discSubType)) - fullDiscType = discType!; - else if (string.IsNullOrEmpty(discType) && !string.IsNullOrEmpty(discSubType)) - fullDiscType = discSubType!; - - info.DumpingInfo.ReportedDiscType = fullDiscType; - } - - // Get the Datafile information - var datafile = GenerateDatafile(sidecar, basePath); - - // Fill in the hash data - info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile); - - switch (this.Type) - { - // TODO: Can this do GD-ROM? - case MediaType.CDROM: - // TODO: Re-enable once PVD generation / finding is fixed - // Generate / obtain the PVD - //info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD"; - - long errorCount = -1; - if (File.Exists(basePath + ".resume.xml")) - errorCount = GetErrorCount(basePath + ".resume.xml"); - - info.CommonDiscInfo!.ErrorsCount = (errorCount == -1 ? "Error retrieving error count" : errorCount.ToString()); - - info.TracksAndWriteOffsets.Cuesheet = GenerateCuesheet(sidecar, basePath) ?? string.Empty; - - string cdWriteOffset = GetWriteOffset(sidecar) ?? string.Empty; - info.CommonDiscInfo.RingWriteOffset = cdWriteOffset; - info.TracksAndWriteOffsets.OtherWriteOffsets = cdWriteOffset; - break; - - case MediaType.DVD: - case MediaType.HDDVD: - case MediaType.BluRay: - - // Get the individual hash data, as per internal - if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1)) - { - info.SizeAndChecksums!.CRC32 = crc32; - info.SizeAndChecksums.CRC32 = crc32; - info.SizeAndChecksums.MD5 = md5; - info.SizeAndChecksums.SHA1 = sha1; - } - - // TODO: Re-enable once PVD generation / finding is fixed - // Generate / obtain the PVD - //info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD"; - - // Deal with the layerbreak - string? layerbreak = null; - if (this.Type == MediaType.DVD) - layerbreak = GetLayerbreak(sidecar) ?? string.Empty; - else if (this.Type == MediaType.BluRay) - layerbreak = info.SizeAndChecksums!.Size > 25_025_314_816 ? "25025314816" : null; - - // If we have a single-layer disc - if (string.IsNullOrEmpty(layerbreak)) - { - // Currently no-op - } - // If we have a dual-layer disc - else - { - info.SizeAndChecksums!.Layerbreak = Int64.Parse(layerbreak); - } - - // TODO: Investigate XGD disc outputs - // TODO: Investigate BD specifics like PIC - - break; - } - - switch (this.System) - { - // TODO: Can we get SecuROM data? - // TODO: Can we get SS version/ranges? - // TODO: Can we get DMI info? - // TODO: Can we get Sega Header info? - // TODO: Can we get PS1 EDC status? - // TODO: Can we get PS1 LibCrypt status? - - case RedumpSystem.DVDAudio: - case RedumpSystem.DVDVideo: - info.CopyProtection!.Protection = GetDVDProtection(sidecar) ?? string.Empty; - break; - - case RedumpSystem.KonamiPython2: - if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty; - info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion; - info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate; - } - - info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty; - break; - - case RedumpSystem.MicrosoftXbox: - if (GetXgdAuxInfo(sidecar, out var xgd1DMIHash, out var xgd1PFIHash, out var xgd1SSHash, out var ss, out var xgd1SSVer)) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = xgd1DMIHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd1SSVer ?? string.Empty; - info.Extras!.SecuritySectorRanges = ss ?? string.Empty; - } - - if (GetXboxDMIInfo(sidecar, out var serial, out var version, out Region? region)) - { - info.CommonDiscInfo!.Serial = serial ?? string.Empty; - info.VersionAndEditions!.Version = version ?? string.Empty; - info.CommonDiscInfo.Region = region; - } - - break; - - case RedumpSystem.MicrosoftXbox360: - if (GetXgdAuxInfo(sidecar, out var xgd23DMIHash, out var xgd23PFIHash, out var xgd23SSHash, out var ss360, out var xgd23SSVer)) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = xgd23DMIHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd23SSVer ?? string.Empty; - info.Extras!.SecuritySectorRanges = ss360 ?? string.Empty; - } - - if (GetXbox360DMIInfo(sidecar, out var serial360, out var version360, out Region? region360)) - { - info.CommonDiscInfo!.Serial = serial360 ?? string.Empty; - info.VersionAndEditions!.Version = version360 ?? string.Empty; - info.CommonDiscInfo.Region = region360; - } - break; - - case RedumpSystem.SonyPlayStation: - if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationSerial ?? string.Empty; - info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion; - info.CommonDiscInfo.EXEDateBuildDate = playstationDate; - } - - break; - - case RedumpSystem.SonyPlayStation2: - if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty; - info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion; - info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate; - } - - info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty; - break; - - case RedumpSystem.SonyPlayStation3: - info.VersionAndEditions!.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty; - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty; - string? firmwareVersion = InfoTool.GetPlayStation3FirmwareVersion(drive?.Name); - if (firmwareVersion != null) - info.CommonDiscInfo!.ContentsSpecialFields![SiteCode.Patches] = $"PS3 Firmware {firmwareVersion}"; - break; - - case RedumpSystem.SonyPlayStation4: - info.VersionAndEditions!.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty; - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty; - break; - - case RedumpSystem.SonyPlayStation5: - info.VersionAndEditions!.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty; - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty; - break; - } - - // Fill in any artifacts that exist, Base64-encoded, if we need to - if (includeArtifacts) - { - info.Artifacts ??= []; - if (File.Exists(basePath + ".cicm.xml")) - info.Artifacts["cicm"] = GetBase64(GetFullFile(basePath + ".cicm.xml")) ?? string.Empty; - if (File.Exists(basePath + ".ibg")) - info.Artifacts["ibg"] = Convert.ToBase64String(File.ReadAllBytes(basePath + ".ibg")); - if (File.Exists(basePath + ".log")) - info.Artifacts["log"] = GetBase64(GetFullFile(basePath + ".log")) ?? string.Empty; - if (File.Exists(basePath + ".mhddlog.bin")) - info.Artifacts["mhddlog_bin"] = Convert.ToBase64String(File.ReadAllBytes(basePath + ".mhddlog.bin")); - if (File.Exists(basePath + ".resume.xml")) - info.Artifacts["resume"] = GetBase64(GetFullFile(basePath + ".resume.xml")) ?? string.Empty; - if (File.Exists(basePath + ".sub.log")) - info.Artifacts["sub_log"] = GetBase64(GetFullFile(basePath + ".sub.log")) ?? string.Empty; - } - } - /// public override string? GenerateParameters() { @@ -1431,52 +1137,6 @@ namespace MPF.Core.Modules.Aaru /// public override string GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType); - /// - public override List GetLogFilePaths(string basePath) - { - var logFiles = new List(); - switch (this.Type) - { - case MediaType.CDROM: - if (File.Exists($"{basePath}.cicm.xml")) - logFiles.Add($"{basePath}.cicm.xml"); - if (File.Exists($"{basePath}.error.log")) - logFiles.Add($"{basePath}.error.log"); - if (File.Exists($"{basePath}.ibg")) - logFiles.Add($"{basePath}.ibg"); - if (File.Exists($"{basePath}.log")) - logFiles.Add($"{basePath}.log"); - if (File.Exists($"{basePath}.mhddlog.bin")) - logFiles.Add($"{basePath}.mhddlog.bin"); - if (File.Exists($"{basePath}.resume.xml")) - logFiles.Add($"{basePath}.resume.xml"); - if (File.Exists($"{basePath}.sub.log")) - logFiles.Add($"{basePath}.sub.log"); - - break; - - case MediaType.DVD: - case MediaType.HDDVD: - case MediaType.BluRay: - if (File.Exists($"{basePath}.cicm.xml")) - logFiles.Add($"{basePath}.cicm.xml"); - if (File.Exists($"{basePath}.error.log")) - logFiles.Add($"{basePath}.error.log"); - if (File.Exists($"{basePath}.ibg")) - logFiles.Add($"{basePath}.ibg"); - if (File.Exists($"{basePath}.log")) - logFiles.Add($"{basePath}.log"); - if (File.Exists($"{basePath}.mhddlog.bin")) - logFiles.Add($"{basePath}.mhddlog.bin"); - if (File.Exists($"{basePath}.resume.xml")) - logFiles.Add($"{basePath}.resume.xml"); - - break; - } - - return logFiles; - } - /// public override bool IsDumpingCommand() { @@ -2299,1116 +1959,5 @@ namespace MPF.Core.Modules.Aaru } #endregion - - #region Information Extraction Methods - - /// - /// Convert the TrackTypeTrackType value to a CueTrackDataType - /// - /// TrackTypeTrackType to convert - /// Sector size to help with specific subtypes - /// CueTrackDataType representing the input data - private static CueTrackDataType ConvertToDataType(TrackTypeTrackType trackType, uint bytesPerSector) - { - switch (trackType) - { - case TrackTypeTrackType.audio: - return CueTrackDataType.AUDIO; - - case TrackTypeTrackType.mode1: - if (bytesPerSector == 2048) - return CueTrackDataType.MODE1_2048; - else - return CueTrackDataType.MODE1_2352; - - case TrackTypeTrackType.mode2: - case TrackTypeTrackType.m2f1: - case TrackTypeTrackType.m2f2: - if (bytesPerSector == 2336) - return CueTrackDataType.MODE2_2336; - else - return CueTrackDataType.MODE2_2352; - - default: - return CueTrackDataType.MODE1_2352; - } - } - - /// - /// Convert the TrackFlagsType value to a CueTrackFlag - /// - /// TrackFlagsType containing flag data - /// CueTrackFlag representing the flags - private static CueTrackFlag ConvertToTrackFlag(TrackFlagsType trackFlagsType) - { - if (trackFlagsType == null) - return 0; - - CueTrackFlag flag = 0; - - if (trackFlagsType.CopyPermitted) - flag |= CueTrackFlag.DCP; - - if (trackFlagsType.Quadraphonic) - flag |= CueTrackFlag.FourCH; - - if (trackFlagsType.PreEmphasis) - flag |= CueTrackFlag.PRE; - - return flag; - } - - /// - /// Generate a cuesheet string based on CICM sidecar data - /// - /// CICM Sidecar data generated by Aaru - /// Base path for determining file names - /// String containing the cuesheet, null on error - private static string? GenerateCuesheet(CICMMetadataType? cicmSidecar, string basePath) - { - // If the object is null, we can't get information from it - if (cicmSidecar == null) - return null; - - // Required variables - uint totalTracks = 0; - var cueFiles = new List(); - var cueSheet = new CueSheet - { - Performer = string.Join(", ", cicmSidecar.Performer ?? []), - }; - - // Only care about OpticalDisc types - if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0) - return null; - - // Loop through each OpticalDisc in the metadata - foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) - { - // Only capture the first total track count - if (opticalDisc.Tracks != null && opticalDisc.Tracks.Length > 0) - totalTracks = opticalDisc.Tracks[0]; - - // If there are no tracks, we can't get a cuesheet - if (opticalDisc.Track == null || opticalDisc.Track.Length == 0) - continue; - - // Get cuesheet-level information - cueSheet.Catalog = opticalDisc.MediaCatalogueNumber; - - // Loop through each track - foreach (TrackType track in opticalDisc.Track) - { - // Create cue track entry - var cueTrack = new CueTrack - { - Number = (int)(track.Sequence?.TrackNumber ?? 0), - DataType = ConvertToDataType(track.TrackType1, track.BytesPerSector), - Flags = ConvertToTrackFlag(track.Flags), - ISRC = track.ISRC, - }; - - // Create cue file entry - var cueFile = new CueFile - { - FileName = GenerateTrackName(basePath, (int)totalTracks, cueTrack.Number, opticalDisc.DiscType), - FileType = CueFileType.BINARY, - }; - - // Add index data - var cueTracks = new List(); - if (track.Indexes != null && track.Indexes.Length > 0) - { - var cueIndicies = new List(); - - // Loop through each index - foreach (TrackIndexType trackIndex in track.Indexes) - { - // Get timestamp from frame count - int absoluteLength = Math.Abs(trackIndex.Value); - int frames = absoluteLength % 75; - int seconds = (absoluteLength / 75) % 60; - int minutes = (absoluteLength / 75 / 60); - string timeString = $"{minutes:D2}:{seconds:D2}:{frames:D2}"; - - // Pregap information - if (trackIndex.Value < 0) - { - string[] timeStringSplit = timeString.Split(':'); - cueTrack.PreGap = new PreGap - { - Minutes = int.Parse(timeStringSplit[0]), - Seconds = int.Parse(timeStringSplit[1]), - Frames = int.Parse(timeStringSplit[2]), - }; - } - - // Individual indexes - else - { - string[] timeStringSplit = timeString.Split(':'); - cueIndicies.Add(new CueIndex - { - Index = trackIndex.index, - Minutes = int.Parse(timeStringSplit[0]), - Seconds = int.Parse(timeStringSplit[1]), - Frames = int.Parse(timeStringSplit[2]), - }); - } - } - - cueTrack.Indices = [.. cueIndicies]; - } - else - { - // Default if index data missing from sidecar - cueTrack.Indices = new CueIndex[] - { - new() - { - Index = 1, - Minutes = 0, - Seconds = 0, - Frames = 0, - }, - }; - } - - // Add the track to the file - cueTracks.Add(cueTrack); - - // Add the file to the cuesheet - cueFiles.Add(cueFile); - } - } - - // If we have a cuesheet to write out, do so - cueSheet.Files = [.. cueFiles]; - if (cueSheet != null && cueSheet != default) - { - var ms = SabreTools.Serialization.Serializers.CueSheet.SerializeStream(cueSheet); - if (ms == null) - return null; - - using var sr = new StreamReader(ms); - return sr.ReadToEnd(); - } - - return null; - } - - /// - /// Generate a CMP XML datfile string based on CICM sidecar data - /// - /// CICM Sidecar data generated by Aaru - /// Base path for determining file names - /// String containing the datfile, null on error - private static string? GenerateDatfile(CICMMetadataType? cicmSidecar, string basePath) - { - // If the object is null, we can't get information from it - if (cicmSidecar == null) - return null; - - // Required variables - string datfile = string.Empty; - - // Process OpticalDisc, if possible - if (cicmSidecar.OpticalDisc != null && cicmSidecar.OpticalDisc.Length > 0) - { - // Loop through each OpticalDisc in the metadata - foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) - { - // Only capture the first total track count - uint totalTracks = 0; - if (opticalDisc.Tracks != null && opticalDisc.Tracks.Length > 0) - totalTracks = opticalDisc.Tracks[0]; - - // If there are no tracks, we can't get a datfile - if (opticalDisc.Track == null || opticalDisc.Track.Length == 0) - continue; - - // Loop through each track - foreach (TrackType track in opticalDisc.Track) - { - uint trackNumber = track.Sequence?.TrackNumber ?? 0; - ulong size = track.Size; - string crc32 = string.Empty; - string md5 = string.Empty; - string sha1 = string.Empty; - - // If we don't have any checksums, we can't get a DAT for this track - if (track.Checksums == null || track.Checksums.Length == 0) - continue; - - // Extract only relevant checksums - foreach (ChecksumType checksum in track.Checksums) - { - switch (checksum.type) - { - case ChecksumTypeType.crc32: - crc32 = checksum.Value; - break; - case ChecksumTypeType.md5: - md5 = checksum.Value; - break; - case ChecksumTypeType.sha1: - sha1 = checksum.Value; - break; - } - } - - // Build the track datfile data and append - string trackName = GenerateTrackName(basePath, (int)totalTracks, (int)trackNumber, opticalDisc.DiscType); - datfile += $"\n"; - } - } - } - - // Process BlockMedia, if possible - if (cicmSidecar.BlockMedia != null && cicmSidecar.BlockMedia.Length > 0) - { - // Loop through each BlockMedia in the metadata - foreach (BlockMediaType blockMedia in cicmSidecar.BlockMedia) - { - ulong size = blockMedia.Size; - string crc32 = string.Empty; - string md5 = string.Empty; - string sha1 = string.Empty; - - // If we don't have any checksums, we can't get a DAT for this track - if (blockMedia.Checksums == null || blockMedia.Checksums.Length == 0) - continue; - - // Extract only relevant checksums - foreach (ChecksumType checksum in blockMedia.Checksums) - { - switch (checksum.type) - { - case ChecksumTypeType.crc32: - crc32 = checksum.Value; - break; - case ChecksumTypeType.md5: - md5 = checksum.Value; - break; - case ChecksumTypeType.sha1: - sha1 = checksum.Value; - break; - } - } - - // Build the track datfile data and append - string trackName = $"{basePath}.bin"; - datfile += $"\n"; - } - } - - return datfile; - } - - /// - /// Generate a CMP XML datfile string based on CICM sidecar data - /// - /// CICM Sidecar data generated by Aaru - /// Base path for determining file names - /// Datafile containing the hash information, null on error - private static Datafile? GenerateDatafile(CICMMetadataType? cicmSidecar, string basePath) - { - // If the object is null, we can't get information from it - if (cicmSidecar == null) - return null; - - // Required variables - var datafile = new Datafile(); - var roms = new List(); - - // Process OpticalDisc, if possible - if (cicmSidecar.OpticalDisc != null && cicmSidecar.OpticalDisc.Length > 0) - { - // Loop through each OpticalDisc in the metadata - foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) - { - // Only capture the first total track count - uint totalTracks = 0; - if (opticalDisc.Tracks != null && opticalDisc.Tracks.Length > 0) - totalTracks = opticalDisc.Tracks[0]; - - // If there are no tracks, we can't get a datfile - if (opticalDisc.Track == null || opticalDisc.Track.Length == 0) - continue; - - // Loop through each track - foreach (TrackType track in opticalDisc.Track) - { - uint trackNumber = track.Sequence?.TrackNumber ?? 0; - ulong size = track.Size; - string crc32 = string.Empty; - string md5 = string.Empty; - string sha1 = string.Empty; - - // If we don't have any checksums, we can't get a DAT for this track - if (track.Checksums == null || track.Checksums.Length == 0) - continue; - - // Extract only relevant checksums - foreach (ChecksumType checksum in track.Checksums) - { - switch (checksum.type) - { - case ChecksumTypeType.crc32: - crc32 = checksum.Value; - break; - case ChecksumTypeType.md5: - md5 = checksum.Value; - break; - case ChecksumTypeType.sha1: - sha1 = checksum.Value; - break; - } - } - - // Build the track datfile data and append - string trackName = GenerateTrackName(basePath, (int)totalTracks, (int)trackNumber, opticalDisc.DiscType); - roms.Add(new Rom { Name = trackName, Size = size.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1 }); - } - } - } - - // Process BlockMedia, if possible - if (cicmSidecar.BlockMedia != null && cicmSidecar.BlockMedia.Length > 0) - { - // Loop through each BlockMedia in the metadata - foreach (BlockMediaType blockMedia in cicmSidecar.BlockMedia) - { - ulong size = blockMedia.Size; - string crc32 = string.Empty; - string md5 = string.Empty; - string sha1 = string.Empty; - - // If we don't have any checksums, we can't get a DAT for this track - if (blockMedia.Checksums == null || blockMedia.Checksums.Length == 0) - continue; - - // Extract only relevant checksums - foreach (ChecksumType checksum in blockMedia.Checksums) - { - switch (checksum.type) - { - case ChecksumTypeType.crc32: - crc32 = checksum.Value; - break; - case ChecksumTypeType.md5: - md5 = checksum.Value; - break; - case ChecksumTypeType.sha1: - sha1 = checksum.Value; - break; - } - } - - // Build the track datfile data and append - string trackName = $"{basePath}.bin"; - roms.Add(new Rom { Name = trackName, Size = size.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1 }); - } - } - - // Assign the roms to a new game - datafile.Games = new Game[1]; - datafile.Games[0] = new Game { Roms = [.. roms] }; - - return datafile; - } - - /// - /// Generate a track name based on current path and tracks - /// - /// Base path for determining file names - /// Total number of tracks in the media - /// Current track index - /// Current disc type, used for determining extension - /// Formatted string representing the track name according to Redump standards - private static string GenerateTrackName(string basePath, int totalTracks, int trackNumber, string discType) - { - string extension = "bin"; - if (discType.Contains("BD") || discType.Contains("DVD")) - extension = "iso"; - - string trackName = Path.GetFileNameWithoutExtension(basePath); - if (totalTracks == 1) - trackName = $"{trackName}.{extension}"; - else if (totalTracks > 1 && totalTracks < 10) - trackName = $"{trackName} (Track {trackNumber}).{extension}"; - else - trackName = $"{trackName} (Track {trackNumber:D2}).{extension}"; - - return trackName; - } - - /// - /// Generate a Redump-compatible PVD block based on CICM sidecar file - /// - /// CICM Sidecar data generated by Aaru - /// String containing the PVD, null on error - private static string? GeneratePVD(CICMMetadataType? cicmSidecar) - { - // If the object is null, we can't get information from it - if (cicmSidecar == null) - return null; - - // Process OpticalDisc, if possible - if (cicmSidecar.OpticalDisc != null && cicmSidecar.OpticalDisc.Length > 0) - { - // Loop through each OpticalDisc in the metadata - foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) - { - var pvdData = GeneratePVDData(opticalDisc); - - // If we got a null value, we skip this disc - if (pvdData == null) - continue; - - // Build each row in consecutive order - string pvd = string.Empty; -#if NET20 || NET35 || NET40 - byte[] pvdLine = new byte[16]; - Array.Copy(pvdData, 0, pvdLine, 0, 16); - pvd += GenerateSectorOutputLine("0320", pvdLine); - Array.Copy(pvdData, 16, pvdLine, 0, 16); - pvd += GenerateSectorOutputLine("0330", pvdLine); - Array.Copy(pvdData, 32, pvdLine, 0, 16); - pvd += GenerateSectorOutputLine("0340", pvdLine); - Array.Copy(pvdData, 48, pvdLine, 0, 16); - pvd += GenerateSectorOutputLine("0350", pvdLine); - Array.Copy(pvdData, 64, pvdLine, 0, 16); - pvd += GenerateSectorOutputLine("0360", pvdLine); - Array.Copy(pvdData, 80, pvdLine, 0, 16); - pvd += GenerateSectorOutputLine("0370", pvdLine); -#else - pvd += GenerateSectorOutputLine("0320", new ReadOnlySpan(pvdData, 0, 16).ToArray()); - pvd += GenerateSectorOutputLine("0330", new ReadOnlySpan(pvdData, 16, 16).ToArray()); - pvd += GenerateSectorOutputLine("0340", new ReadOnlySpan(pvdData, 32, 16).ToArray()); - pvd += GenerateSectorOutputLine("0350", new ReadOnlySpan(pvdData, 48, 16).ToArray()); - pvd += GenerateSectorOutputLine("0360", new ReadOnlySpan(pvdData, 64, 16).ToArray()); - pvd += GenerateSectorOutputLine("0370", new ReadOnlySpan(pvdData, 80, 16).ToArray()); -#endif - - return pvd; - } - } - - return null; - } - - /// - /// Generate the byte array representing the current PVD information - /// - /// OpticalDisc type from CICM Sidecar data - /// Byte array representing the PVD, null on error - private static byte[]? GeneratePVDData(OpticalDiscType? opticalDisc) - { - // Required variables - DateTime creation = DateTime.MinValue; - DateTime modification = DateTime.MinValue; - DateTime expiration = DateTime.MinValue; - DateTime effective = DateTime.MinValue; - - // If there are no tracks, we can't get a PVD - if (opticalDisc?.Track == null || opticalDisc.Track.Length == 0) - return null; - - // Take the first track only - TrackType track = opticalDisc.Track[0]; - - // If there are no partitions, we can't get a PVD - if (track.FileSystemInformation == null || track.FileSystemInformation.Length == 0) - return null; - - // Loop through each Partition - foreach (PartitionType partition in track.FileSystemInformation) - { - // If the partition has no file systems, we can't get a PVD - if (partition.FileSystems == null || partition.FileSystems.Length == 0) - continue; - - // Loop through each FileSystem until we find a PVD - foreach (FileSystemType fileSystem in partition.FileSystems) - { - // If we don't have a PVD-able filesystem, we can't get a PVD - if (!fileSystem.CreationDateSpecified - && !fileSystem.ModificationDateSpecified - && !fileSystem.ExpirationDateSpecified - && !fileSystem.EffectiveDateSpecified) - { - continue; - } - - // Creation Date - if (fileSystem.CreationDateSpecified) - creation = fileSystem.CreationDate; - - // Modification Date - if (fileSystem.ModificationDateSpecified) - modification = fileSystem.ModificationDate; - - // Expiration Date - if (fileSystem.ExpirationDateSpecified) - expiration = fileSystem.ExpirationDate; - - // Effective Date - if (fileSystem.EffectiveDateSpecified) - effective = fileSystem.EffectiveDate; - - break; - } - - // If we found a Partition with PVD data, we break - if (creation != DateTime.MinValue - || modification != DateTime.MinValue - || expiration != DateTime.MinValue - || effective != DateTime.MinValue) - { - break; - } - } - - // If we found no partitions, we return null - if (creation == DateTime.MinValue - && modification == DateTime.MinValue - && expiration == DateTime.MinValue - && effective == DateTime.MinValue) - { - return null; - } - - // Now generate the byte array data - var pvdData = new List(); - pvdData.AddRange(new string(' ', 13).ToCharArray().Select(c => (byte)c)); - pvdData.AddRange(GeneratePVDDateTimeBytes(creation)); - pvdData.AddRange(GeneratePVDDateTimeBytes(modification)); - pvdData.AddRange(GeneratePVDDateTimeBytes(expiration)); - pvdData.AddRange(GeneratePVDDateTimeBytes(effective)); - pvdData.Add(0x01); - pvdData.AddRange(new string((char)0, 14).ToCharArray().Select(c => (byte)c)); - - // Return the filled array - return [.. pvdData]; - } - - /// - /// Generate the required bytes from a DateTime object - /// - /// DateTime to get representation of - /// Byte array representing the DateTime - private static byte[] GeneratePVDDateTimeBytes(DateTime dateTime) - { - string emptyTime = "0000000000000000"; - string dateTimeString = emptyTime; - byte timeZoneNumber = 0; - - // If we don't have default values, set the proper string - if (dateTime != DateTime.MinValue) - { - dateTimeString = dateTime.ToString("yyyyMMddHHmmssff"); - - // Get timezone offset (0 == GMT, up and down in 15-minute increments) - string timeZoneString; - try - { - timeZoneString = dateTime.ToString("zzz"); - } - catch - { - timeZoneString = "00:00"; - } - - // Format is hh:mm - string[] splitTimeZoneString = timeZoneString.Split(':'); - if (int.TryParse(splitTimeZoneString[0], out int hours)) - timeZoneNumber += (byte)(hours * 4); - if (int.TryParse(splitTimeZoneString[1], out int minutes)) - timeZoneNumber += (byte)(minutes / 15); - } - - // Get and return the byte array - List dateTimeList = dateTimeString.ToCharArray().Select(c => (byte)c).ToList(); - dateTimeList.Add(timeZoneNumber); - return [.. dateTimeList]; - } - - /// - /// Generate a single 16-byte sector line from a byte array - /// - /// Row ID for outputting - /// Bytes representing the data to write - /// Formatted string representing the sector line - private static string? GenerateSectorOutputLine(string row, byte[] bytes) - { - // If the data isn't correct, return null - if (bytes == null || bytes.Length != 16) - return null; - - string pvdLine = $"{row} : "; - pvdLine += BitConverter.ToString(bytes.Take(8).ToArray()).Replace("-", " "); - pvdLine += " "; - pvdLine += BitConverter.ToString(bytes.Skip(8).Take(8).ToArray()).Replace("-", " "); - pvdLine += " "; - pvdLine += Encoding.ASCII.GetString([.. bytes]).Replace((char)0, '.').Replace('?', '.'); - pvdLine += "\n"; - - return pvdLine; - } - - /// - /// Read the CICM Sidecar as an object - /// - /// CICM Sidecar data generated by Aaru - /// Object containing the data, null on error - private static CICMMetadataType? GenerateSidecar(string cicmSidecar) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(cicmSidecar)) - return null; - - // Open and read in the XML file - XmlReader xtr = XmlReader.Create(cicmSidecar, new XmlReaderSettings - { - CheckCharacters = false, -#if NET40_OR_GREATER || NETCOREAPP - DtdProcessing = DtdProcessing.Ignore, -#endif - IgnoreComments = true, - IgnoreWhitespace = true, - ValidationFlags = XmlSchemaValidationFlags.None, - ValidationType = ValidationType.None, - }); - - // If the reader is null for some reason, we can't do anything - if (xtr == null) - return null; - - var serializer = new XmlSerializer(typeof(CICMMetadataType)); - return serializer.Deserialize(xtr) as CICMMetadataType; - } - - /// - /// Get reported disc type information, if possible - /// - /// CICM Sidecar data generated by Aaru - /// True if disc type info was set, false otherwise - private static bool GetDiscType(CICMMetadataType? cicmSidecar, out string? discType, out string? discSubType) - { - // Set the default values - discType = null; discSubType = null; - - // If the object is null, we can't get information from it - if (cicmSidecar == null) - return false; - - // Only care about OpticalDisc types - if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0) - return false; - - // Find and return the hardware info, if possible - foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) - { - // Store the first instance of each value - if (string.IsNullOrEmpty(discType) && !string.IsNullOrEmpty(opticalDisc.DiscType)) - discType = opticalDisc.DiscType; - if (string.IsNullOrEmpty(discSubType) && !string.IsNullOrEmpty(opticalDisc.DiscSubType)) - discSubType = opticalDisc.DiscSubType; - } - - return !string.IsNullOrEmpty(discType) || !string.IsNullOrEmpty(discSubType); - } - - /// - /// Get the DVD protection information, if possible - /// - /// CICM Sidecar data generated by Aaru - /// Formatted string representing the DVD protection, null on error - private static string? GetDVDProtection(CICMMetadataType? cicmSidecar) - { - // If the object is null, we can't get information from it - if (cicmSidecar == null) - return null; - - // Only care about OpticalDisc types - if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0) - return null; - - // Get an output for the copyright protection - string copyrightProtectionSystemType = string.Empty; - - // Loop through each OpticalDisc in the metadata - foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) - { - if (!string.IsNullOrEmpty(opticalDisc.CopyProtection)) - copyrightProtectionSystemType += $", {opticalDisc.CopyProtection}"; - } - - // Trim the values - copyrightProtectionSystemType = copyrightProtectionSystemType.TrimStart(',').Trim(); - - // TODO: Note- Most of the below values are not currently captured by Aaru. - // At the time of writing, there are open issues to capture more of this - // information and store it in the output. For now, only the copyright - // protection system can be retrieved. - - // Now we format everything we can - string protection = string.Empty; - //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(vobKeys)) - // protection += vobKeys; - //if (!string.IsNullOrEmpty(decryptedDiscKey)) - // protection += $"Decrypted Disc Key: {decryptedDiscKey}\n"; - - return protection; - } - - /// - /// Get the detected error count from the input files, if possible - /// - /// .resume.xml file location - /// Error count if possible, -1 on error - private static long GetErrorCount(string resume) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(resume)) - return -1; - - // Get a total error count for after - long? totalErrors = null; - - // Parse the resume XML file - try - { - // Read in the error count whenever we find it - using var sr = File.OpenText(resume); - while (!sr.EndOfStream) - { - var line = sr.ReadLine()?.Trim(); - - // Initialize on seeing the open tag - if (string.IsNullOrEmpty(line)) - continue; - else if (line!.StartsWith("")) - totalErrors = 0; - else if (line.StartsWith("")) - return totalErrors ?? -1; - else if (line.StartsWith("") && totalErrors != null) - totalErrors++; - } - - // If we haven't found anything, return -1 - return totalErrors ?? -1; - } - catch - { - // We don't care what the exception is right now - return Int64.MaxValue; - } - } - - /// - /// Get hardware information, if possible - /// - /// CICM Sidecar data generated by Aaru - /// True if hardware info was set, false otherwise - private static bool GetHardwareInfo(CICMMetadataType? cicmSidecar, out string? manufacturer, out string? model, out string? firmware) - { - // Set the default values - manufacturer = null; model = null; firmware = null; - - // If the object is null, we can't get information from it - if (cicmSidecar == null) - return false; - - // Only care about OpticalDisc types - if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0) - return false; - - // Find and return the hardware info, if possible - foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) - { - // If there's no hardware information, skip - if (opticalDisc.DumpHardwareArray == null || !opticalDisc.DumpHardwareArray.Any()) - continue; - - foreach (DumpHardwareType hardware in opticalDisc.DumpHardwareArray) - { - // If the hardware information is invalid, skip - if (hardware == null) - continue; - - // Store the first instance of each value - if (string.IsNullOrEmpty(manufacturer) && !string.IsNullOrEmpty(hardware.Manufacturer)) - manufacturer = hardware.Manufacturer; - if (string.IsNullOrEmpty(model) && !string.IsNullOrEmpty(hardware.Model)) - model = hardware.Model; - if (string.IsNullOrEmpty(firmware) && !string.IsNullOrEmpty(hardware.Firmware)) - firmware = hardware.Firmware; - } - } - - return !string.IsNullOrEmpty(manufacturer) || !string.IsNullOrEmpty(model) || !string.IsNullOrEmpty(firmware); - } - - /// - /// Get the layerbreak from the input file, if possible - /// - /// CICM Sidecar data generated by Aaru - /// Layerbreak if possible, null on error - private static string? GetLayerbreak(CICMMetadataType? cicmSidecar) - { - // If the object is null, we can't get information from it - if (cicmSidecar == null) - return null; - - // Only care about OpticalDisc types - if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0) - return null; - - // Setup the layerbreak - string? layerbreak = null; - - // Find and return the layerbreak, if possible - foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) - { - // If there's no layer information, skip - if (opticalDisc.Layers == null) - continue; - - // TODO: Determine how to find the layerbreak from the CICM or other outputs - } - - return layerbreak; - } - - /// - /// Get the write offset from the CICM Sidecar file, if possible - /// - /// CICM Sidecar data generated by Aaru - /// Sample write offset if possible, null on error - private static string? GetWriteOffset(CICMMetadataType? cicmSidecar) - { - // If the object is null, we can't get information from it - if (cicmSidecar == null) - return null; - - // Only care about OpticalDisc types - if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0) - return null; - - // Loop through each OpticalDisc in the metadata - foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) - { - // If the disc doesn't have an offset specified, we skip it; - if (!opticalDisc.OffsetSpecified) - continue; - - return opticalDisc.Offset.ToString(); - } - - return null; - } - - /// - /// Get the XGD auxiliary info from the CICM Sidecar file, if possible - /// - /// CICM Sidecar data generated by Aaru - /// True on successful extraction of info, false otherwise - private static bool GetXgdAuxInfo(CICMMetadataType? cicmSidecar, 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 object is null, we can't get information from it - if (cicmSidecar == null) - return false; - - // Only care about OpticalDisc types - if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0) - return false; - - // Loop through each OpticalDisc in the metadata - foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) - { - // If the Xbox type isn't set, we can't extract information - if (opticalDisc.Xbox == null) - continue; - - // Get the Xbox information - XboxType xbox = opticalDisc.Xbox; - - // DMI - if (xbox.DMI != null) - { - DumpType dmi = xbox.DMI; - if (dmi.Checksums != null && dmi.Checksums.Length != 0) - { - foreach (ChecksumType checksum in dmi.Checksums) - { - // We only care about the CRC32 - if (checksum.type == ChecksumTypeType.crc32) - { - dmihash = checksum.Value; - break; - } - } - } - } - - // PFI - if (xbox.PFI != null) - { - DumpType pfi = xbox.PFI; - if (pfi.Checksums != null && pfi.Checksums.Length != 0) - { - foreach (ChecksumType checksum in pfi.Checksums) - { - // We only care about the CRC32 - if (checksum.type == ChecksumTypeType.crc32) - { - pfihash = checksum.Value; - break; - } - } - } - } - - // SS - if (xbox.SecuritySectors != null && xbox.SecuritySectors.Length > 0) - { - foreach (XboxSecuritySectorsType securitySector in xbox.SecuritySectors) - { - DumpType security = securitySector.SecuritySectors; - if (security.Checksums != null && security.Checksums.Length != 0) - { - foreach (ChecksumType checksum in security.Checksums) - { - // We only care about the CRC32 - if (checksum.type == ChecksumTypeType.crc32) - { - // TODO: Validate correctness for all 3 fields - ss = security.Image; - ssver = securitySector.RequestVersion.ToString(); - sshash = checksum.Value; - break; - } - } - } - - // If we got a hash, we can break - if (sshash != null) - break; - } - } - } - - return false; - } - - /// - /// Get the Xbox serial info from the CICM Sidecar file, if possible - /// - /// CICM Sidecar data generated by Aaru - /// True on successful extraction of info, false otherwise - private static bool GetXboxDMIInfo(CICMMetadataType? cicmSidecar, out string? serial, out string? version, out Region? region) - { - serial = null; version = null; region = Region.World; - - // If the object is null, we can't get information from it - if (cicmSidecar == null) - return false; - - // Only care about OpticalDisc types - if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0) - return false; - - // Loop through each OpticalDisc in the metadata - foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) - { - // If the Xbox type isn't set, we can't extract information - if (opticalDisc.Xbox == null) - continue; - - // Get the Xbox information - XboxType xbox = opticalDisc.Xbox; - - // DMI - if (xbox.DMI != null) - { - DumpType dmi = xbox.DMI; - string image = dmi.Image; - - // TODO: Figure out if `image` is the right thing here - // TODO: Figure out how to extract info from `image` - //br.BaseStream.Seek(8, SeekOrigin.Begin); - //char[] str = br.ReadChars(8); - - //serial = $"{str[0]}{str[1]}-{str[2]}{str[3]}{str[4]}"; - //version = $"1.{str[5]}{str[6]}"; - //region = GetXgdRegion(str[7]); - //return true; - } - } - - return false; - } - - /// - /// Get the Xbox 360 serial info from the CICM Sidecar file, if possible - /// - /// CICM Sidecar data generated by Aaru - /// True on successful extraction of info, false otherwise - private static bool GetXbox360DMIInfo(CICMMetadataType? cicmSidecar, out string? serial, out string? version, out Region? region) - { - serial = null; version = null; region = Region.World; - - // If the object is null, we can't get information from it - if (cicmSidecar == null) - return false; - - // Only care about OpticalDisc types - if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0) - return false; - - // Loop through each OpticalDisc in the metadata - foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) - { - // If the Xbox type isn't set, we can't extract information - if (opticalDisc.Xbox == null) - continue; - - // Get the Xbox information - XboxType xbox = opticalDisc.Xbox; - - // DMI - if (xbox.DMI != null) - { - DumpType dmi = xbox.DMI; - string image = dmi.Image; - - // TODO: Figure out if `image` is the right thing here - // TODO: Figure out how to extract info from `image` - //br.BaseStream.Seek(64, SeekOrigin.Begin); - //char[] str = br.ReadChars(14); - - //serial = $"{str[0]}{str[1]}-{str[2]}{str[3]}{str[4]}{str[5]}"; - //version = $"1.{str[6]}{str[7]}"; - //region = GetXgdRegion(str[8]); - // str[9], str[10], str[11] - unknown purpose - // str[12], str[13] - disc <12> of <13> - //return true; - } - } - - return false; - } - -#endregion } } diff --git a/MPF.Core/Modules/BaseParameters.cs b/MPF.Core/Modules/BaseParameters.cs index 9ec1d0f7..40136f5b 100644 --- a/MPF.Core/Modules/BaseParameters.cs +++ b/MPF.Core/Modules/BaseParameters.cs @@ -75,11 +75,6 @@ namespace MPF.Core.Modules /// private Process? process; - /// - /// All found volume labels and their corresponding file systems - /// - public Dictionary>? VolumeLabels; - #endregion #region Virtual Dumping Information @@ -158,28 +153,6 @@ namespace MPF.Core.Modules SetDefaultParameters(drivePath, filename, driveSpeed, options); } - #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 /// @@ -201,20 +174,6 @@ namespace MPF.Core.Modules /// String representing the media type, null on error public virtual string? GetDefaultExtension(MediaType? mediaType) => null; - /// - /// 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) => new(); - - /// - /// 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) => new(); - /// /// Get the MediaType from the current set of parameters /// @@ -329,7 +288,7 @@ namespace MPF.Core.Modules { } } -#endregion + #endregion #region Parameter Parsing @@ -1173,96 +1132,5 @@ namespace MPF.Core.Modules } #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 } } diff --git a/MPF.Core/Modules/CleanRip/Parameters.cs b/MPF.Core/Modules/CleanRip/Parameters.cs index dc8eba9b..217b587f 100644 --- a/MPF.Core/Modules/CleanRip/Parameters.cs +++ b/MPF.Core/Modules/CleanRip/Parameters.cs @@ -1,11 +1,4 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using MPF.Core.Converters; -using MPF.Core.Data; -using SabreTools.Hashing; -using SabreTools.RedumpLib; +using MPF.Core.Data; using SabreTools.RedumpLib.Data; namespace MPF.Core.Modules.CleanRip @@ -30,376 +23,5 @@ namespace MPF.Core.Modules.CleanRip : base(system, type, drivePath, filename, driveSpeed, options) { } - - #region BaseParameters Implementations - - /// - public override (bool, List) CheckAllOutputFilesExist(string basePath, bool preCheck) - { - var 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}_logs.zip") || !preCheck) - { - if (!File.Exists($"{basePath}-dumpinfo.txt")) - missingFiles.Add($"{basePath}-dumpinfo.txt"); - if (!File.Exists($"{basePath}.bca")) - missingFiles.Add($"{basePath}.bca"); - } - - break; - - default: - missingFiles.Add("Media and system combination not supported for CleanRip"); - break; - } - - return (!missingFiles.Any(), missingFiles); - } - - /// - public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts) - { - // Ensure that required sections exist - info = Builder.EnsureAllSections(info); - - // TODO: Determine if there's a CleanRip version anywhere - info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram); - info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "-dumpinfo.txt")?.ToString("yyyy-MM-dd HH:mm:ss"); - - // Get the Datafile information - var datafile = GenerateCleanripDatafile(basePath + ".iso", basePath + "-dumpinfo.txt"); - info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile); - - // Get the individual hash data, as per internal - if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var 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 Region? gcRegion, out var gcVersion, out var gcName, out var gcSerial)) - { - info.CommonDiscInfo!.Region = gcRegion ?? info.CommonDiscInfo.Region; - info.VersionAndEditions!.Version = gcVersion ?? info.VersionAndEditions.Version; - info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalName] = gcName ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = gcSerial ?? string.Empty; - } - - break; - } - - // Fill in any artifacts that exist, Base64-encoded, if we need to - if (includeArtifacts) - { - info.Artifacts ??= []; - - if (File.Exists(basePath + ".bca")) - info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true)) ?? string.Empty; - if (File.Exists(basePath + "-dumpinfo.txt")) - info.Artifacts["dumpinfo"] = GetBase64(GetFullFile(basePath + "-dumpinfo.txt")) ?? string.Empty; - } - } - - /// - public override List GetLogFilePaths(string basePath) - { - var logFiles = 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")) - logFiles.Add($"{basePath}-dumpinfo.txt"); - if (File.Exists($"{basePath}.bca")) - logFiles.Add($"{basePath}.bca"); - - break; - } - - return logFiles; - } - - #endregion - - #region Information Extraction Methods - - /// - /// Get a formatted datfile from the cleanrip output, if possible - /// - /// Path to ISO file - /// Path to discinfo file - /// - private static Datafile? GenerateCleanripDatafile(string iso, string dumpinfo) - { - // If the files don't exist, we can't get info from it - if (!File.Exists(iso) || !File.Exists(dumpinfo)) - return null; - - 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 - using var sr = File.OpenText(dumpinfo); - if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true) - return null; - - // Read all lines and gather dat information - while (!sr.EndOfStream) - { - var line = sr.ReadLine()?.Trim(); - if (string.IsNullOrEmpty(line)) - continue; - else 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); - } - - // Ensure all checksums were found in log - if (crc == string.Empty || md5 == string.Empty || sha1 == string.Empty) - { - if (HashTool.GetStandardHashes(iso, out long isoSize, out string? isoCRC, out string? isoMD5, out string? isoSHA1)) - { - crc = isoCRC ?? crc; - md5 = isoMD5 ?? md5; - sha1 = isoSHA1 ?? sha1; - } - } - - return new Datafile - { - Games = - [ - new() - { - Roms = - [ - new Rom { Name = Path.GetFileName(iso), Size = size.ToString(), Crc = crc, Md5 = md5, Sha1 = sha1 }, - ] - } - ] - }; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// 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 - { - var hex = GetFullFile(bcaPath, true); - if (hex == null) - return null; - - 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 files don't exist, we can't get info from it - if (!File.Exists(iso) || !File.Exists(dumpinfo)) - return null; - - 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 - using var sr = File.OpenText(dumpinfo); - if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true) - return null; - - // Read all lines and gather dat information - while (!sr.EndOfStream) - { - var line = sr.ReadLine()?.Trim(); - if (string.IsNullOrEmpty(line)) - continue; - else 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 - /// Output internal name of the game - /// Output internal serial of the game - /// - private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string? version, out string? name, out string? serial) - { - region = null; version = null; name = null; serial = null; - - // If the file doesn't exist, we can't get info from it - if (!File.Exists(dumpinfo)) - return false; - - try - { - // Make sure this file is a dumpinfo - using var sr = File.OpenText(dumpinfo); - if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true) - return false; - - // Read all lines and gather dat information - while (!sr.EndOfStream) - { - var line = sr.ReadLine()?.Trim(); - if (string.IsNullOrEmpty(line)) - { - continue; - } - else if (line!.StartsWith("Version")) - { - version = line.Substring("Version: ".Length); - } - else if (line.StartsWith("Internal Name")) - { - name = line.Substring("Internal Name: ".Length); - } - else if (line.StartsWith("Filename")) - { - serial = line.Substring("Filename: ".Length); - if (serial.EndsWith("-disc2")) - serial = serial.Replace("-disc2", string.Empty); - - // char gameType = serial[0]; - // string gameid = serial[1] + serial[2]; - // string version = serial[4] + serial[5] - - switch (serial[3]) - { - case 'A': - region = Region.World; - break; - case 'D': - region = Region.Germany; - break; - case 'E': - region = Region.UnitedStatesOfAmerica; - break; - case 'F': - region = Region.France; - break; - case 'I': - region = Region.Italy; - break; - case 'J': - region = Region.Japan; - break; - case 'K': - region = Region.SouthKorea; - break; - case 'L': - region = Region.Europe; // Japanese import to Europe - break; - case 'M': - region = Region.Europe; // American import to Europe - break; - case 'N': - region = Region.UnitedStatesOfAmerica; // Japanese import to USA - break; - case 'P': - region = Region.Europe; - break; - case 'R': - region = Region.RussianFederation; - break; - case 'S': - region = Region.Spain; - break; - case 'Q': - region = Region.SouthKorea; // Korea with Japanese language - break; - case 'T': - region = Region.SouthKorea; // 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 } } diff --git a/MPF.Core/Modules/DiscImageCreator/Parameters.cs b/MPF.Core/Modules/DiscImageCreator/Parameters.cs index cedd230f..60c16b3d 100644 --- a/MPF.Core/Modules/DiscImageCreator/Parameters.cs +++ b/MPF.Core/Modules/DiscImageCreator/Parameters.cs @@ -3,10 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; -using MPF.Core.Converters; using MPF.Core.Data; -using MPF.Core.Utilities; -using SabreTools.RedumpLib; using SabreTools.RedumpLib.Data; namespace MPF.Core.Modules.DiscImageCreator @@ -173,734 +170,6 @@ namespace MPF.Core.Modules.DiscImageCreator #region BaseParameters Implementations - /// - public override (bool, List) CheckAllOutputFilesExist(string basePath, bool preCheck) - { - /* - If there are no external programs, such as error checking, etc., DIC outputs - a slightly different set of files. This reduced set needs to be documented in - order for special use cases, such as self-built versions of DIC or removed - helper programs, can be detected to the best of our ability. Below is the list - of files that are generated in that case: - - .bin - .c2 - .ccd - .cue - .img/.imgtmp - .scm/.scmtmp - .sub/.subtmp - _cmd.txt (formerly) - _img.cue - - This list needs to be translated into the minimum viable set of information - such that things like error checking can be passed back as a flag, or some - similar method. - - Here are some notes about the various output files and what they represent: - - bin - Final split output disc image (CD/GD only) - - c2 - Represents each byte per sector as one bit; 0 means no error, 1 means error - - c2Error - Human-readable version of `c2`; only errors are printed - - ccd - CloneCD control file referencing the `img` file - - cmd - Represents the commandline that was run - - cue - CDRWIN cuesheet referencing the `bin` file(s) - - dat - Logiqx datfile referencing the `bin` file(s) - - disc - Disc metadata and information - - drive - Drive metadata and information - - img - CloneCD output disc image (CD/GD only) - - img.cue - CDRWIN cuesheet referencing the `img` file - - img_EdcEcc - ECC check output as run on the `img` file - - iso - Final output disc image (DVD/BD only) - - mainError - Read, drive, or system errors - - mainInfo - ISOBuster-formatted sector information - - scm - Scrambled disc image - - sub - Binary subchannel data as read from the disc - - subError - Subchannel read errors - - subInfo - Subchannel informational messages - - subIntention - Subchannel intentional error information - - subReadable - Human-readable version of `sub` - - toc - Binary representation of the table of contents - - volDesc - Volume descriptor information - */ - - var missingFiles = new List(); - switch (this.Type) - { - case MediaType.CDROM: - case MediaType.GDROM: - if (!File.Exists($"{basePath}.cue")) - missingFiles.Add($"{basePath}.cue"); - if (!File.Exists($"{basePath}.img") && !File.Exists($"{basePath}.imgtmp")) - missingFiles.Add($"{basePath}.img"); - - // Audio-only discs don't output these files - if (!this.System.IsAudio()) - { - if (!File.Exists($"{basePath}.scm") && !File.Exists($"{basePath}.scmtmp")) - missingFiles.Add($"{basePath}.scm"); - } - - if (!File.Exists($"{basePath}_logs.zip") || !preCheck) - { - // GD-ROM and GD-R don't output this for the HD area - if (this.Type != MediaType.GDROM) - { - if (!File.Exists($"{basePath}.ccd")) - missingFiles.Add($"{basePath}.ccd"); - } - - if (!File.Exists($"{basePath}.dat")) - missingFiles.Add($"{basePath}.dat"); - if (!File.Exists($"{basePath}.sub") && !File.Exists($"{basePath}.subtmp")) - missingFiles.Add($"{basePath}.sub"); - if (!File.Exists($"{basePath}_disc.txt")) - missingFiles.Add($"{basePath}_disc.txt"); - if (!File.Exists($"{basePath}_drive.txt")) - missingFiles.Add($"{basePath}_drive.txt"); - if (!File.Exists($"{basePath}_img.cue")) - missingFiles.Add($"{basePath}_img.cue"); - if (!File.Exists($"{basePath}_mainError.txt")) - missingFiles.Add($"{basePath}_mainError.txt"); - if (!File.Exists($"{basePath}_mainInfo.txt")) - missingFiles.Add($"{basePath}_mainInfo.txt"); - if (!File.Exists($"{basePath}_subError.txt")) - missingFiles.Add($"{basePath}_subError.txt"); - if (!File.Exists($"{basePath}_subInfo.txt")) - missingFiles.Add($"{basePath}_subInfo.txt"); - if (!File.Exists($"{basePath}_subReadable.txt") && !File.Exists($"{basePath}_sub.txt")) - missingFiles.Add($"{basePath}_subReadable.txt"); - if (!File.Exists($"{basePath}_volDesc.txt")) - missingFiles.Add($"{basePath}_volDesc.txt"); - - // Audio-only discs don't output these files - if (!this.System.IsAudio()) - { - if (!File.Exists($"{basePath}.img_EdcEcc.txt") && !File.Exists($"{basePath}.img_EccEdc.txt")) - missingFiles.Add($"{basePath}.img_EdcEcc.txt"); - } - } - - // Removed or inconsistent files - //{ - // // Doesn't output on Linux - // if (!File.Exists($"{basePath}.c2")) - // missingFiles.Add($"{basePath}.c2"); - - // // Doesn't output on Linux - // if (!File.Exists($"{basePath}_c2Error.txt")) - // missingFiles.Add($"{basePath}_c2Error.txt"); - - // // Replaced by timestamp-named file - // if (!File.Exists($"{basePath}_cmd.txt")) - // missingFiles.Add($"{basePath}_cmd.txt"); - - // // Not guaranteed output - // if (!File.Exists($"{basePath}_subIntention.txt")) - // missingFiles.Add($"{basePath}_subIntention.txt"); - - // // Not guaranteed output - // if (File.Exists($"{basePath}_suppl.dat")) - // missingFiles.Add($"{basePath}_suppl.dat"); - - // // Not guaranteed output (at least PCE) - // if (!File.Exists($"{basePath}.toc")) - // missingFiles.Add($"{basePath}.toc"); - //} - - break; - - case MediaType.DVD: - case MediaType.HDDVD: - case MediaType.BluRay: - case MediaType.NintendoGameCubeGameDisc: - case MediaType.NintendoWiiOpticalDisc: - if (!File.Exists($"{basePath}_logs.zip") || !preCheck) - { - if (!File.Exists($"{basePath}.dat")) - missingFiles.Add($"{basePath}.dat"); - if (!File.Exists($"{basePath}_disc.txt")) - missingFiles.Add($"{basePath}_disc.txt"); - if (!File.Exists($"{basePath}_drive.txt")) - missingFiles.Add($"{basePath}_drive.txt"); - if (!File.Exists($"{basePath}_mainError.txt")) - missingFiles.Add($"{basePath}_mainError.txt"); - if (!File.Exists($"{basePath}_mainInfo.txt")) - missingFiles.Add($"{basePath}_mainInfo.txt"); - if (!File.Exists($"{basePath}_volDesc.txt")) - missingFiles.Add($"{basePath}_volDesc.txt"); - } - - // Removed or inconsistent files - //{ - // // Replaced by timestamp-named file - // if (!File.Exists($"{basePath}_cmd.txt")) - // missingFiles.Add($"{basePath}_cmd.txt"); - - // // Not guaranteed output - // if (File.Exists($"{basePath}_CSSKey.txt")) - // missingFiles.Add($"{basePath}_CSSKey.txt"); - - // // Only output for some parameters - // if (File.Exists($"{basePath}.raw")) - // missingFiles.Add($"{basePath}.raw"); - - // // Not guaranteed output - // if (File.Exists($"{basePath}_suppl.dat")) - // missingFiles.Add($"{basePath}_suppl.dat"); - //} - - break; - - case MediaType.FloppyDisk: - case MediaType.HardDisk: - // TODO: Determine what outputs come out from a HDD, SD, etc. - if (!File.Exists($"{basePath}_logs.zip") || !preCheck) - { - if (!File.Exists($"{basePath}.dat")) - missingFiles.Add($"{basePath}.dat"); - if (!File.Exists($"{basePath}_disc.txt")) - missingFiles.Add($"{basePath}_disc.txt"); - } - - // Removed or inconsistent files - //{ - // // Replaced by timestamp-named file - // if (!File.Exists($"{basePath}_cmd.txt")) - // missingFiles.Add($"{basePath}_cmd.txt"); - //} - - break; - - default: - missingFiles.Add("Media and system combination not supported for DiscImageCreator"); - break; - } - - return (!missingFiles.Any(), missingFiles); - } - - /// - public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts) - { - var outputDirectory = Path.GetDirectoryName(basePath); - - // Ensure that required sections exist - info = Builder.EnsureAllSections(info); - - // Get the dumping program and version - var (dicCmd, dicVersion) = GetCommandFilePathAndVersion(basePath); - info.DumpingInfo!.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {dicVersion ?? "Unknown Version"}"; - info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(dicCmd)?.ToString("yyyy-MM-dd HH:mm:ss"); - - // Fill in the hardware data - if (GetHardwareInfo($"{basePath}_drive.txt", out var manufacturer, out var model, out var firmware)) - { - info.DumpingInfo.Manufacturer = manufacturer; - info.DumpingInfo.Model = model; - info.DumpingInfo.Firmware = firmware; - } - - // Fill in the disc type data - if (GetDiscType($"{basePath}_disc.txt", out var discTypeOrBookType)) - info.DumpingInfo.ReportedDiscType = discTypeOrBookType; - - // Get the Datafile information - var datafile = InfoTool.GetDatafile($"{basePath}.dat"); - - // Fill in the hash data - info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile); - - // Fill in the volume labels - if (GetVolumeLabels($"{basePath}_volDesc.txt", out var volLabels)) - VolumeLabels = volLabels; - - // Extract info based generically on MediaType - switch (this.Type) - { - case MediaType.CDROM: - case MediaType.GDROM: // TODO: Verify GD-ROM outputs this - info.Extras!.PVD = GetPVD($"{basePath}_mainInfo.txt") ?? "Disc has no PVD"; - - // Audio-only discs will fail if there are any C2 errors, so they would never get here - if (this.System.IsAudio()) - { - info.CommonDiscInfo!.ErrorsCount = "0"; - } - else - { - long errorCount = -1; - if (File.Exists($"{basePath}.img_EdcEcc.txt")) - errorCount = GetErrorCount($"{basePath}.img_EdcEcc.txt"); - else if (File.Exists($"{basePath}.img_EccEdc.txt")) - errorCount = GetErrorCount($"{basePath}.img_EccEdc.txt"); - - info.CommonDiscInfo!.ErrorsCount = (errorCount == -1 ? "Error retrieving error count" : errorCount.ToString()); - } - - info.TracksAndWriteOffsets.Cuesheet = GetFullFile($"{basePath}.cue") ?? string.Empty; - //var cueSheet = new CueSheet($"{basePath}.cue"); // TODO: Do something with this - - // Attempt to get the write offset - string cdWriteOffset = GetWriteOffset($"{basePath}_disc.txt") ?? string.Empty; - info.CommonDiscInfo.RingWriteOffset = cdWriteOffset; - info.TracksAndWriteOffsets.OtherWriteOffsets = cdWriteOffset; - - // Attempt to get multisession data - string cdMultiSessionInfo = GetMultisessionInformation($"{basePath}_disc.txt") ?? string.Empty; - if (!string.IsNullOrEmpty(cdMultiSessionInfo)) - info.CommonDiscInfo.CommentsSpecialFields![SiteCode.Multisession] = cdMultiSessionInfo; - - break; - - case MediaType.DVD: - case MediaType.HDDVD: - case MediaType.BluRay: - - // Get the individual hash data, as per internal - if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1)) - { - info.SizeAndChecksums!.Size = size; - info.SizeAndChecksums.CRC32 = crc32; - info.SizeAndChecksums.MD5 = md5; - info.SizeAndChecksums.SHA1 = sha1; - } - - // Deal with the layerbreaks - if (this.Type == MediaType.DVD) - { - string layerbreak = GetLayerbreak($"{basePath}_disc.txt", System.IsXGD()) ?? string.Empty; - info.SizeAndChecksums!.Layerbreak = !string.IsNullOrEmpty(layerbreak) ? Int64.Parse(layerbreak) : default; - } - else if (this.Type == MediaType.BluRay) - { - var di = InfoTool.GetDiscInformation($"{basePath}_PIC.bin"); - info.SizeAndChecksums!.PICIdentifier = InfoTool.GetPICIdentifier(di); - if (InfoTool.GetLayerbreaks(di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3)) - { - if (layerbreak1 != null && layerbreak1 * 2048 < info.SizeAndChecksums.Size) - info.SizeAndChecksums.Layerbreak = layerbreak1.Value; - - if (layerbreak2 != null && layerbreak2 * 2048 < info.SizeAndChecksums.Size) - info.SizeAndChecksums.Layerbreak2 = layerbreak2.Value; - - if (layerbreak3 != null && layerbreak3 * 2048 < info.SizeAndChecksums.Size) - info.SizeAndChecksums.Layerbreak3 = layerbreak3.Value; - } - } - - // Read the PVD - if (!options.EnableRedumpCompatibility || System != RedumpSystem.MicrosoftXbox) - info.Extras!.PVD = GetPVD($"{basePath}_mainInfo.txt") ?? string.Empty; - - // Bluray-specific options - if (this.Type == MediaType.BluRay) - { - int trimLength = -1; - switch (this.System) - { - case RedumpSystem.MicrosoftXboxOne: - case RedumpSystem.MicrosoftXboxSeriesXS: - case RedumpSystem.SonyPlayStation3: - case RedumpSystem.SonyPlayStation4: - case RedumpSystem.SonyPlayStation5: - if (info.SizeAndChecksums!.Layerbreak3 != default) - trimLength = 520; - else if (info.SizeAndChecksums!.Layerbreak2 != default) - trimLength = 392; - else - trimLength = 264; - break; - } - - info.Extras!.PIC = GetPIC($"{basePath}_PIC.bin", trimLength) ?? string.Empty; - } - - break; - } - - // Extract info based specifically on RedumpSystem - switch (this.System) - { - case RedumpSystem.AppleMacintosh: - case RedumpSystem.EnhancedCD: - case RedumpSystem.IBMPCcompatible: - case RedumpSystem.RainbowDisc: - case RedumpSystem.SonyElectronicBook: - if (File.Exists($"{basePath}_subIntention.txt")) - { - var fi = new FileInfo($"{basePath}_subIntention.txt"); - if (fi.Length > 0) - info.CopyProtection!.SecuROMData = GetFullFile($"{basePath}_subIntention.txt") ?? string.Empty; - } - - // Needed for some odd copy protections - info.CopyProtection!.Protection = GetDVDProtection($"{basePath}_CSSKey.txt", $"{basePath}_disc.txt", false) ?? string.Empty; - - break; - - case RedumpSystem.DVDAudio: - case RedumpSystem.DVDVideo: - info.CopyProtection!.Protection = GetDVDProtection($"{basePath}_CSSKey.txt", $"{basePath}_disc.txt", true) ?? string.Empty; - break; - - case RedumpSystem.KonamiPython2: - info.CommonDiscInfo!.EXEDateBuildDate = GetPlayStationEXEDate($"{basePath}_volDesc.txt", InfoTool.GetPlayStationExecutableName(drive?.Name)); - - if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty; - info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion; - info.CommonDiscInfo.EXEDateBuildDate ??= pythonTwoDate; - } - - info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty; - break; - - case RedumpSystem.MicrosoftXbox: - - string xmidString; - if (string.IsNullOrEmpty(outputDirectory)) - xmidString = Tools.GetXGD1XMID($"{basePath}_DMI.bin"); - else - xmidString = Tools.GetXGD1XMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin")); - - var xmid = SabreTools.Serialization.Wrappers.XMID.Create(xmidString); - if (xmid != null) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XMID] = xmidString?.TrimEnd('\0') ?? string.Empty; - info.CommonDiscInfo.Serial = xmid.Serial ?? string.Empty; - if (!options.EnableRedumpCompatibility) - info.VersionAndEditions!.Version = xmid.Version ?? string.Empty; - - info.CommonDiscInfo.Region = InfoTool.GetXGDRegion(xmid.Model.RegionIdentifier); - } - - // If we have the new, external DAT - if (File.Exists($"{basePath}_suppl.dat")) - { - var suppl = InfoTool.GetDatafile($"{basePath}_suppl.dat"); - if (GetXGDAuxHashInfo(suppl, out var xgd1DMIHash, out var xgd1PFIHash, out var xgd1SSHash)) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = xgd1DMIHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash ?? string.Empty; - } - - if (GetXGDAuxSSInfo($"{basePath}_disc.txt", out var xgd1SS, out var xgd1SSVer)) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.SSVersion] = xgd1SSVer ?? string.Empty; - info.Extras!.SecuritySectorRanges = xgd1SS ?? string.Empty; - } - } - else - { - if (GetXGDAuxInfo($"{basePath}_disc.txt", out var xgd1DMIHash, out var xgd1PFIHash, out var xgd1SSHash, out var xgd1SS, out var xgd1SSVer)) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = xgd1DMIHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd1SSVer ?? string.Empty; - info.Extras!.SecuritySectorRanges = xgd1SS ?? string.Empty; - } - } - - break; - - case RedumpSystem.MicrosoftXbox360: - string xemidString; - if (string.IsNullOrEmpty(outputDirectory)) - xemidString = Tools.GetXGD23XeMID($"{basePath}_DMI.bin"); - else - xemidString = Tools.GetXGD23XeMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin")); - - var xemid = SabreTools.Serialization.Wrappers.XeMID.Create(xemidString); - if (xemid != null) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XeMID] = xemidString?.TrimEnd('\0') ?? string.Empty; - info.CommonDiscInfo.Serial = xemid.Serial ?? string.Empty; - if (!options.EnableRedumpCompatibility) - info.VersionAndEditions!.Version = xemid.Version ?? string.Empty; - - info.CommonDiscInfo.Region = InfoTool.GetXGDRegion(xemid.Model.RegionIdentifier); - } - - // If we have the new, external DAT - if (File.Exists($"{basePath}_suppl.dat")) - { - var suppl = InfoTool.GetDatafile($"{basePath}_suppl.dat"); - if (GetXGDAuxHashInfo(suppl, out var xgd23DMIHash, out var xgd23PFIHash, out var xgd23SSHash)) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = xgd23DMIHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash ?? string.Empty; - } - - if (GetXGDAuxSSInfo($"{basePath}_disc.txt", out var xgd23SS, out var xgd23SSVer)) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.SSVersion] = xgd23SSVer ?? string.Empty; - info.Extras!.SecuritySectorRanges = xgd23SS ?? string.Empty; - } - } - else - { - if (GetXGDAuxInfo($"{basePath}_disc.txt", out var xgd23DMIHash, out var xgd23PFIHash, out var xgd23SSHash, out var xgd23SS, out var xgd23SSVer)) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = xgd23DMIHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd23SSVer ?? string.Empty; - info.Extras!.SecuritySectorRanges = xgd23SS ?? string.Empty; - } - } - - break; - - case RedumpSystem.NamcoSegaNintendoTriforce: - if (this.Type == MediaType.CDROM) - { - info.Extras!.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? string.Empty; - - // Take only the first 16 lines for GD-ROM - if (!string.IsNullOrEmpty(info.Extras.Header)) - info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray()); - - if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = gdSerial ?? string.Empty; - info.VersionAndEditions!.Version = gdVersion ?? string.Empty; - info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? string.Empty; - } - } - - break; - - case RedumpSystem.SegaMegaCDSegaCD: - info.Extras!.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? string.Empty; - - // Take only the last 16 lines for Sega CD - if (!string.IsNullOrEmpty(info.Extras.Header)) - info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Skip(16).ToArray()); - - if (GetSegaCDBuildInfo(info.Extras.Header, out var scdSerial, out var fixedDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = scdSerial ?? string.Empty; - info.CommonDiscInfo.EXEDateBuildDate = fixedDate ?? string.Empty; - } - - break; - - case RedumpSystem.SegaChihiro: - if (this.Type == MediaType.CDROM) - { - info.Extras!.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? string.Empty; - - // Take only the first 16 lines for GD-ROM - if (!string.IsNullOrEmpty(info.Extras.Header)) - info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray()); - - if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = gdSerial ?? string.Empty; - info.VersionAndEditions!.Version = gdVersion ?? string.Empty; - info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? string.Empty; - } - } - - break; - - case RedumpSystem.SegaDreamcast: - if (this.Type == MediaType.CDROM) - { - info.Extras!.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? string.Empty; - - // Take only the first 16 lines for GD-ROM - if (!string.IsNullOrEmpty(info.Extras.Header)) - info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray()); - - if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = gdSerial ?? string.Empty; - info.VersionAndEditions!.Version = gdVersion ?? string.Empty; - info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? string.Empty; - } - } - - break; - - case RedumpSystem.SegaNaomi: - if (this.Type == MediaType.CDROM) - { - info.Extras!.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? string.Empty; - - // Take only the first 16 lines for GD-ROM - if (!string.IsNullOrEmpty(info.Extras.Header)) - info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray()); - - if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = gdSerial ?? string.Empty; - info.VersionAndEditions!.Version = gdVersion ?? string.Empty; - info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? string.Empty; - } - } - - break; - - case RedumpSystem.SegaNaomi2: - if (this.Type == MediaType.CDROM) - { - info.Extras!.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? string.Empty; - - // Take only the first 16 lines for GD-ROM - if (!string.IsNullOrEmpty(info.Extras.Header)) - info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray()); - - if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = gdSerial ?? string.Empty; - info.VersionAndEditions!.Version = gdVersion ?? string.Empty; - info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? string.Empty; - } - } - - break; - - case RedumpSystem.SegaSaturn: - info.Extras!.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? string.Empty; - - // Take only the first 16 lines for Saturn - if (!string.IsNullOrEmpty(info.Extras.Header)) - info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray()); - - if (GetSaturnBuildInfo(info.Extras.Header, out var saturnSerial, out var saturnVersion, out var buildDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = saturnSerial ?? string.Empty; - info.VersionAndEditions!.Version = saturnVersion ?? string.Empty; - info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? string.Empty; - } - - break; - - case RedumpSystem.SonyPlayStation: - info.CommonDiscInfo!.EXEDateBuildDate = GetPlayStationEXEDate($"{basePath}_volDesc.txt", InfoTool.GetPlayStationExecutableName(drive?.Name), true); - - if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationSerial ?? string.Empty; - info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion; - info.CommonDiscInfo.EXEDateBuildDate ??= playstationDate; - } - - bool? psEdcStatus = null; - if (File.Exists($"{basePath}.img_EdcEcc.txt")) - psEdcStatus = GetPlayStationEDCStatus($"{basePath}.img_EdcEcc.txt"); - else if (File.Exists($"{basePath}.img_EccEdc.txt")) - psEdcStatus = GetPlayStationEDCStatus($"{basePath}.img_EccEdc.txt"); - - info.EDC!.EDC = psEdcStatus.ToYesNo(); - info.CopyProtection!.AntiModchip = GetPlayStationAntiModchipDetected($"{basePath}_disc.txt").ToYesNo(); - break; - - case RedumpSystem.SonyPlayStation2: - info.CommonDiscInfo!.EXEDateBuildDate = GetPlayStationEXEDate($"{basePath}_volDesc.txt", InfoTool.GetPlayStationExecutableName(drive?.Name)); - - if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty; - info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion; - info.CommonDiscInfo.EXEDateBuildDate ??= playstationTwoDate; - } - - info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty; - break; - - case RedumpSystem.SonyPlayStation3: - info.VersionAndEditions!.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty; - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty; - string? firmwareVersion = InfoTool.GetPlayStation3FirmwareVersion(drive?.Name); - if (firmwareVersion != null) - info.CommonDiscInfo!.ContentsSpecialFields![SiteCode.Patches] = $"PS3 Firmware {firmwareVersion}"; - break; - - case RedumpSystem.SonyPlayStation4: - info.VersionAndEditions!.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty; - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty; - break; - - case RedumpSystem.SonyPlayStation5: - info.VersionAndEditions!.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty; - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty; - break; - } - - // Fill in any artifacts that exist, Base64-encoded, if we need to - if (includeArtifacts) - { - info.Artifacts ??= []; - - //if (File.Exists($"{basePath}.c2")) - // info.Artifacts["c2"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}.c2")) ?? string.Empty; - if (File.Exists($"{basePath}_c2Error.txt")) - info.Artifacts["c2Error"] = GetBase64(GetFullFile($"{basePath}_c2Error.txt")) ?? string.Empty; - if (File.Exists($"{basePath}.ccd")) - info.Artifacts["ccd"] = GetBase64(GetFullFile($"{basePath}.ccd")) ?? string.Empty; - if (File.Exists($"{basePath}_cmd.txt")) // TODO: Figure out how to read in the timestamp-named file - info.Artifacts["cmd"] = GetBase64(GetFullFile($"{basePath}_cmd.txt")) ?? string.Empty; - if (File.Exists($"{basePath}_CSSKey.txt")) - info.Artifacts["csskey"] = GetBase64(GetFullFile($"{basePath}_CSSKey.txt")) ?? string.Empty; - if (File.Exists($"{basePath}.cue")) - info.Artifacts["cue"] = GetBase64(GetFullFile($"{basePath}.cue")) ?? string.Empty; - if (File.Exists($"{basePath}.dat")) - info.Artifacts["dat"] = GetBase64(GetFullFile($"{basePath}.dat")) ?? string.Empty; - if (File.Exists($"{basePath}_disc.txt")) - info.Artifacts["disc"] = GetBase64(GetFullFile($"{basePath}_disc.txt")) ?? string.Empty; - //if (File.Exists(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"))) - // info.Artifacts["dmi"] = Convert.ToBase64String(File.ReadAllBytes(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"))) ?? string.Empty; - if (File.Exists($"{basePath}_drive.txt")) - info.Artifacts["drive"] = GetBase64(GetFullFile($"{basePath}_drive.txt")) ?? string.Empty; - if (File.Exists($"{basePath}_img.cue")) - info.Artifacts["img_cue"] = GetBase64(GetFullFile($"{basePath}_img.cue")) ?? string.Empty; - if (File.Exists($"{basePath}.img_EdcEcc.txt")) - info.Artifacts["img_EdcEcc"] = GetBase64(GetFullFile($"{basePath}.img_EdcEcc.txt")) ?? string.Empty; - if (File.Exists($"{basePath}.img_EccEdc.txt")) - info.Artifacts["img_EdcEcc"] = GetBase64(GetFullFile($"{basePath}.img_EccEdc.txt")) ?? string.Empty; - if (File.Exists($"{basePath}_mainError.txt")) - info.Artifacts["mainError"] = GetBase64(GetFullFile($"{basePath}_mainError.txt")) ?? string.Empty; - if (File.Exists($"{basePath}_mainInfo.txt")) - info.Artifacts["mainInfo"] = GetBase64(GetFullFile($"{basePath}_mainInfo.txt")) ?? string.Empty; - //if (File.Exists($"{basePath}_PFI.bin")) - // info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PFI.bin")) ?? string.Empty; - //if (File.Exists($"{basePath}_PIC.bin")) - // info.Artifacts["pic"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PIC.bin")) ?? string.Empty; - //if (File.Exists($"{basePath}_SS.bin")) - // info.Artifacts["ss"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_SS.bin")) ?? string.Empty; - if (File.Exists($"{basePath}.sub")) - info.Artifacts["sub"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}.sub")) ?? string.Empty; - if (File.Exists($"{basePath}_subError.txt")) - info.Artifacts["subError"] = GetBase64(GetFullFile($"{basePath}_subError.txt")) ?? string.Empty; - if (File.Exists($"{basePath}_subInfo.txt")) - info.Artifacts["subInfo"] = GetBase64(GetFullFile($"{basePath}_subInfo.txt")) ?? string.Empty; - if (File.Exists($"{basePath}_subIntention.txt")) - info.Artifacts["subIntention"] = GetBase64(GetFullFile($"{basePath}_subIntention.txt")) ?? string.Empty; - //if (File.Exists($"{basePath}_sub.txt")) - // info.Artifacts["subReadable"] = GetBase64(GetFullFile($"{basePath}_sub.txt")) ?? string.Empty; - //if (File.Exists($"{basePath}_subReadable.txt")) - // info.Artifacts["subReadable"] = GetBase64(GetFullFile($"{basePath}_subReadable.txt")) ?? string.Empty; - if (File.Exists($"{basePath}_volDesc.txt")) - info.Artifacts["volDesc"] = GetBase64(GetFullFile($"{basePath}_volDesc.txt")) ?? string.Empty; - } - } - /// public override string? GenerateParameters() { @@ -1596,182 +865,6 @@ namespace MPF.Core.Modules.DiscImageCreator /// public override string? GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType); - /// - public override List GetDeleteableFilePaths(string basePath) - { - var deleteableFiles = new List(); - switch (this.Type) - { - case MediaType.CDROM: - case MediaType.GDROM: - if (File.Exists($"{basePath}.img")) - deleteableFiles.Add($"{basePath}.img"); - if (File.Exists($"{basePath} (Track 0).img")) - deleteableFiles.Add($"{basePath} (Track 0).img"); - if (File.Exists($"{basePath} (Track 00).img")) - deleteableFiles.Add($"{basePath} (Track 00).img"); - if (File.Exists($"{basePath} (Track 1)(-LBA).img")) - deleteableFiles.Add($"{basePath} (Track 1)(-LBA).img"); - if (File.Exists($"{basePath} (Track 01)(-LBA).img")) - deleteableFiles.Add($"{basePath} (Track 01)(-LBA).img"); - if (File.Exists($"{basePath} (Track AA).img")) - deleteableFiles.Add($"{basePath} (Track AA).img"); - - if (File.Exists($"{basePath}.scm")) - deleteableFiles.Add($"{basePath}.scm"); - if (File.Exists($"{basePath} (Track 0).scm")) - deleteableFiles.Add($"{basePath} (Track 0).scm"); - if (File.Exists($"{basePath} (Track 00).scm")) - deleteableFiles.Add($"{basePath} (Track 00).scm"); - if (File.Exists($"{basePath} (Track 1)(-LBA).scm")) - deleteableFiles.Add($"{basePath} (Track 1)(-LBA).scm"); - if (File.Exists($"{basePath} (Track 01)(-LBA).scm")) - deleteableFiles.Add($"{basePath} (Track 01)(-LBA).scm"); - if (File.Exists($"{basePath} (Track AA).scm")) - deleteableFiles.Add($"{basePath} (Track AA).scm"); - - break; - - case MediaType.DVD: - case MediaType.HDDVD: - case MediaType.BluRay: - case MediaType.NintendoGameCubeGameDisc: - case MediaType.NintendoWiiOpticalDisc: - if (File.Exists($"{basePath}.raw")) - deleteableFiles.Add($"{basePath}.raw"); - - break; - } - - return deleteableFiles; - } - - /// - public override List GetLogFilePaths(string basePath) - { - (var cmdPath, _) = GetCommandFilePathAndVersion(basePath); - - var logFiles = new List(); - switch (this.Type) - { - case MediaType.CDROM: - case MediaType.GDROM: - if (File.Exists($"{basePath}.c2")) - logFiles.Add($"{basePath}.c2"); - if (File.Exists($"{basePath}_c2Error.txt")) - logFiles.Add($"{basePath}_c2Error.txt"); - if (File.Exists($"{basePath}.ccd")) - logFiles.Add($"{basePath}.ccd"); - if (cmdPath != null && File.Exists(cmdPath)) - logFiles.Add(cmdPath); - if (File.Exists($"{basePath}_cmd.txt")) - logFiles.Add($"{basePath}_cmd.txt"); - if (File.Exists($"{basePath}.dat")) - logFiles.Add($"{basePath}.dat"); - if (File.Exists($"{basePath}.sub")) - logFiles.Add($"{basePath}.sub"); - if (File.Exists($"{basePath} (Track 0).sub")) - logFiles.Add($"{basePath} (Track 0).sub"); - if (File.Exists($"{basePath} (Track 00).sub")) - logFiles.Add($"{basePath} (Track 00).sub"); - if (File.Exists($"{basePath} (Track 1)(-LBA).sub")) - logFiles.Add($"{basePath} (Track 1)(-LBA).sub"); - if (File.Exists($"{basePath} (Track 01)(-LBA).sub")) - logFiles.Add($"{basePath} (Track 01)(-LBA).sub"); - if (File.Exists($"{basePath} (Track AA).sub")) - logFiles.Add($"{basePath} (Track AA).sub"); - if (File.Exists($"{basePath}.subtmp")) - logFiles.Add($"{basePath}.subtmp"); - if (File.Exists($"{basePath}.toc")) - logFiles.Add($"{basePath}.toc"); - if (File.Exists($"{basePath}_disc.txt")) - logFiles.Add($"{basePath}_disc.txt"); - if (File.Exists($"{basePath}_drive.txt")) - logFiles.Add($"{basePath}_drive.txt"); - if (File.Exists($"{basePath}_img.cue")) - logFiles.Add($"{basePath}_img.cue"); - if (File.Exists($"{basePath}.img_EdcEcc.txt")) - logFiles.Add($"{basePath}.img_EdcEcc.txt"); - if (File.Exists($"{basePath}.img_EccEdc.txt")) - logFiles.Add($"{basePath}.img_EccEdc.txt"); - if (File.Exists($"{basePath}_mainError.txt")) - logFiles.Add($"{basePath}_mainError.txt"); - if (File.Exists($"{basePath}_mainInfo.txt")) - logFiles.Add($"{basePath}_mainInfo.txt"); - if (File.Exists($"{basePath}_sub.txt")) - logFiles.Add($"{basePath}_sub.txt"); - if (File.Exists($"{basePath}_subError.txt")) - logFiles.Add($"{basePath}_subError.txt"); - if (File.Exists($"{basePath}_subInfo.txt")) - logFiles.Add($"{basePath}_subInfo.txt"); - if (File.Exists($"{basePath}_subIntention.txt")) - logFiles.Add($"{basePath}_subIntention.txt"); - if (File.Exists($"{basePath}_subReadable.txt")) - logFiles.Add($"{basePath}_subReadable.txt"); - if (File.Exists($"{basePath}_suppl.dat")) - logFiles.Add($"{basePath}_suppl.dat"); - if (File.Exists($"{basePath}_volDesc.txt")) - logFiles.Add($"{basePath}_volDesc.txt"); - - break; - - case MediaType.DVD: - case MediaType.HDDVD: - case MediaType.BluRay: - case MediaType.NintendoGameCubeGameDisc: - case MediaType.NintendoWiiOpticalDisc: - if (cmdPath != null && File.Exists(cmdPath)) - logFiles.Add(cmdPath); - if (File.Exists($"{basePath}_cmd.txt")) - logFiles.Add($"{basePath}_cmd.txt"); - if (File.Exists($"{basePath}_CSSKey.txt")) - logFiles.Add($"{basePath}_CSSKey.txt"); - if (File.Exists($"{basePath}.dat")) - logFiles.Add($"{basePath}.dat"); - if (File.Exists($"{basePath}.toc")) - logFiles.Add($"{basePath}.toc"); - if (File.Exists($"{basePath}_disc.txt")) - logFiles.Add($"{basePath}_disc.txt"); - if (File.Exists($"{basePath}_drive.txt")) - logFiles.Add($"{basePath}_drive.txt"); - if (File.Exists($"{basePath}_mainError.txt")) - logFiles.Add($"{basePath}_mainError.txt"); - if (File.Exists($"{basePath}_mainInfo.txt")) - logFiles.Add($"{basePath}_mainInfo.txt"); - if (File.Exists($"{basePath}_suppl.dat")) - logFiles.Add($"{basePath}_suppl.dat"); - if (File.Exists($"{basePath}_volDesc.txt")) - logFiles.Add($"{basePath}_volDesc.txt"); - - if (File.Exists($"{basePath}_DMI.bin")) - logFiles.Add($"{basePath}_DMI.bin"); - if (File.Exists($"{basePath}_PFI.bin")) - logFiles.Add($"{basePath}_PFI.bin"); - if (File.Exists($"{basePath}_PIC.bin")) - logFiles.Add($"{basePath}_PIC.bin"); - if (File.Exists($"{basePath}_SS.bin")) - logFiles.Add($"{basePath}_SS.bin"); - - break; - - case MediaType.FloppyDisk: - case MediaType.HardDisk: - // TODO: Determine what outputs come out from a HDD, SD, etc. - if (cmdPath != null && File.Exists(cmdPath)) - logFiles.Add(cmdPath); - if (File.Exists($"{basePath}_cmd.txt")) - logFiles.Add($"{basePath}_cmd.txt"); - if (File.Exists($"{basePath}.dat")) - logFiles.Add($"{basePath}.dat"); - if (File.Exists($"{basePath}_disc.txt")) - logFiles.Add($"{basePath}_disc.txt"); - - break; - } - - return logFiles; - } - /// public override MediaType? GetMediaType() => Converters.ToMediaType(BaseCommand); @@ -2569,37 +1662,6 @@ namespace MPF.Core.Modules.DiscImageCreator #region Private Extra Methods - /// - /// Get the command file path and extract the version from it - /// - /// Base filename and path to use for checking - /// Tuple of file path and version as strings, both null on error - private static (string?, string?) GetCommandFilePathAndVersion(string basePath) - { - // If we have an invalid base path, we can do nothing - if (string.IsNullOrEmpty(basePath)) - return (null, null); - - // Generate the matching regex based on the base path - string basePathFileName = Path.GetFileName(basePath); - var cmdFilenameRegex = new Regex(Regex.Escape(basePathFileName) + @"_(\d{8})T\d{6}\.txt"); - - // Find the first match for the command file - var parentDirectory = Path.GetDirectoryName(basePath); - if (string.IsNullOrEmpty(parentDirectory)) - return (null, null); - - var currentFiles = Directory.GetFiles(parentDirectory); - var commandPath = currentFiles.FirstOrDefault(f => cmdFilenameRegex.IsMatch(f)); - if (string.IsNullOrEmpty(commandPath)) - return (null, null); - - // Extract the version string - var match = cmdFilenameRegex.Match(commandPath); - string version = match.Groups[1].Value; - return (commandPath, version); - } - /// /// Set the DIC command to be used for a given system and media type /// @@ -2663,1230 +1725,5 @@ namespace MPF.Core.Modules.DiscImageCreator } #endregion - - #region Information Extraction Methods - - /// - /// Get reported disc type information, if possible - /// - /// _disc.txt file location - /// True if disc type info was set, false otherwise - private static bool GetDiscType(string drive, out string? discTypeOrBookType) - { - // Set the default values - discTypeOrBookType = null; - - // If the file doesn't exist, we can't get the info - if (!File.Exists(drive)) - return false; - - try - { - // Create a hashset to contain all of the found values - var discTypeOrBookTypeSet = new HashSet(); - - using var sr = File.OpenText(drive); - var line = sr.ReadLine(); - while (line != null) - { - // Trim the line for later use - line = line.Trim(); - - // Concatenate all found values for each possible line type - if (line.StartsWith("DiscType:")) - { - // DiscType: - string identifier = line.Substring("DiscType: ".Length); - discTypeOrBookTypeSet.Add(identifier); - } - else if (line.StartsWith("DiscTypeIdentifier:")) - { - // DiscTypeIdentifier: - string identifier = line.Substring("DiscTypeIdentifier: ".Length); - discTypeOrBookTypeSet.Add(identifier); - } - else if (line.StartsWith("DiscTypeSpecific:")) - { - // DiscTypeSpecific: - string identifier = line.Substring("DiscTypeSpecific: ".Length); - discTypeOrBookTypeSet.Add(identifier); - } - else if (line.StartsWith("BookType:")) - { - // BookType: - string identifier = line.Substring("BookType: ".Length); - discTypeOrBookTypeSet.Add(identifier); - } - - line = sr.ReadLine(); - } - - // Create the output string - if (discTypeOrBookTypeSet.Any()) - discTypeOrBookType = string.Join(", ", [.. discTypeOrBookTypeSet.OrderBy(s => s)]); - - return true; - } - catch - { - // We don't care what the exception is right now - discTypeOrBookType = null; - return false; - } - } - - /// - /// Get all Volume Identifiers - /// - /// _volDesc.txt file location - /// Volume labels (by type), or null if none present - private static bool GetVolumeLabels(string volDesc, out Dictionary> volLabels) - { - // If the file doesn't exist, can't get the volume labels - volLabels = []; - if (!File.Exists(volDesc)) - return false; - - try - { - using var sr = File.OpenText(volDesc); - var line = sr.ReadLine(); - - string volType = "UNKNOWN"; - string label; - while (line != null) - { - // Trim the line for later use - line = line.Trim(); - - // ISO9660 and extensions section - if (line.StartsWith("Volume Descriptor Type: ")) - { - Int32.TryParse(line.Substring("Volume Descriptor Type: ".Length), out int volTypeInt); - volType = volTypeInt switch - { - // 0 => "Boot Record" // Should not not contain a Volume Identifier - 1 => "ISO", // ISO9660 - 2 => "Joliet", - // 3 => "Volume Partition Descriptor" // Should not not contain a Volume Identifier - // 255 => "???" // Should not not contain a Volume Identifier - _ => "UNKNOWN" // Should not contain a Volume Identifier - }; - } - // UDF section - else if (line.StartsWith("Primary Volume Descriptor Number:")) - { - volType = "UDF"; - } - // Identifier - else if (line.StartsWith("Volume Identifier: ")) - { - label = line.Substring("Volume Identifier: ".Length); - - // Remove leading non-printable character (unsure why DIC outputs this) - if (Convert.ToUInt32(label[0]) == 0x7F || Convert.ToUInt32(label[0]) < 0x20) - label = label.Substring(1); - - // Skip if label is blank - if (label == null || label.Length <= 0) - { - volType = "UNKNOWN"; - line = sr.ReadLine(); - continue; - } - - if (volLabels.ContainsKey(label)) - volLabels[label].Add(volType); - else - volLabels.Add(label, [volType]); - - // Reset volume type - volType = "UNKNOWN"; - } - - line = sr.ReadLine(); - } - - // Return true if a volume label was found - return volLabels.Count > 0; - } - catch - { - // We don't care what the exception is right now - volLabels = []; - return false; - } - } - - /// - /// Get the DVD protection information, if possible - /// - /// _CSSKey.txt file location - /// _disc.txt file location - /// Indicates whether region and protection type are always included - /// Formatted string representing the DVD protection, null on error - private static string? GetDVDProtection(string cssKey, string disc, bool includeAlways) - { - // 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, vobKeys = null, decryptedDiscKey = null; - - // Get everything from _disc.txt first - using (var sr = File.OpenText(disc)) - { - try - { - // Fast forward to the copyright information - while (sr.ReadLine()?.Trim()?.StartsWith("========== CopyrightInformation ==========") == false) ; - - // Now read until we hit the manufacturing information - var line = sr.ReadLine()?.Trim(); - while (line?.StartsWith("========== ManufacturingInformation ==========") == false) - { - if (line == null) - break; - - 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)) - { - try - { - // Read until the end - using var sr = File.OpenText(cssKey); - while (!sr.EndOfStream) - { - var line = sr.ReadLine()?.Trim(); - if (line == null) - break; - - if (line.StartsWith("DecryptedDiscKey")) - { - decryptedDiscKey = line.Substring("DecryptedDiscKey[020]: ".Length); - } - else if (line.StartsWith("LBA:")) - { - // Set the key string if necessary - vobKeys ??= string.Empty; - - // No keys - if (line.Contains("No TitleKey")) - { - var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), No TitleKey$", RegexOptions.Compiled); - string matchedFilename = match.Groups[1].Value; - if (matchedFilename.EndsWith(";1")) - matchedFilename = matchedFilename.Substring(0, matchedFilename.Length - 2); - - vobKeys += $"{matchedFilename} Title Key: No Title Key\n"; - } - else - { - var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), EncryptedTitleKey: .*?, DecryptedTitleKey: (.*?)$", RegexOptions.Compiled); - string matchedFilename = match.Groups[1].Value; - if (matchedFilename.EndsWith(";1")) - matchedFilename = matchedFilename.Substring(0, matchedFilename.Length - 2); - - vobKeys += $"{matchedFilename} Title Key: {match.Groups[2].Value}\n"; - } - } - } - } - catch { } - } - - // Filter out if we're not always including information - if (!includeAlways) - { - if (region == "1 2 3 4 5 6 7 8") - region = null; - if (copyrightProtectionSystemType == "No") - copyrightProtectionSystemType = null; - } - - // Now we format everything we can - string protection = string.Empty; - 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(vobKeys)) - protection += vobKeys; - if (!string.IsNullOrEmpty(decryptedDiscKey)) - protection += $"Decrypted Disc Key: {decryptedDiscKey}\n"; - - return protection; - } - - /// - /// Get the detected error count from the input files, if possible - /// - /// .img_EdcEcc.txt/.img_EccEdc.txt file location - /// Error count if possible, -1 on error - private static 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; - - // Get a total error count for after - long? totalErrors = null; - - // First line of defense is the EdcEcc error file - try - { - // Read in the error count whenever we find it - using var sr = File.OpenText(edcecc); - while (!sr.EndOfStream) - { - var line = sr.ReadLine()?.Trim(); - if (line == null) - break; - - if (line.StartsWith("[NO ERROR]")) - { - totalErrors = 0; - break; - } - else if (line.StartsWith("Total errors")) - { - totalErrors ??= 0; - - if (Int64.TryParse(line.Substring("Total errors: ".Length).Trim(), out long te)) - totalErrors += te; - } - else if (line.StartsWith("Total warnings")) - { - totalErrors ??= 0; - - if (Int64.TryParse(line.Substring("Total warnings: ".Length).Trim(), out long tw)) - totalErrors += tw; - } - } - - // If we haven't found anything, return -1 - return totalErrors ?? -1; - } - catch - { - // We don't care what the exception is right now - return Int64.MaxValue; - } - } - - /// - /// Get the PSX/PS2/KP2 EXE Date from the log, if possible - /// - /// Log file location - /// Internal serial - /// True if PSX disc, false otherwise - /// EXE date if possible, null otherwise - public static string? GetPlayStationEXEDate(string log, string? exeName, bool psx = false) - { - // If the file doesn't exist, we can't get the info - if (!File.Exists(log)) - return null; - - // If the EXE name is not valid, we can't get the info - if (string.IsNullOrEmpty(exeName)) - return null; - - try - { - string? exeDate = null; - using var sr = File.OpenText(log); - var line = sr.ReadLine(); - while (line != null) - { - // Trim the line for later use - line = line.Trim(); - - // The exe date is listed in a single line, File Identifier: ABCD_123.45;1 - if (line.Length >= "File Identifier: ".Length + 11 && - line.StartsWith("File Identifier:") && - line.Substring("File Identifier: ".Length) == exeName) - { - // Account for Y2K date problem - if (exeDate != null && exeDate!.Substring(0, 2) == "19") - { - string decade = exeDate!.Substring(2, 1); - // Does only PSX need to account for 1920s-60s? - if (decade == "0" || decade == "1" || - psx && (decade == "2" || decade == "3" || decade == "4" || decade == "5" || decade == "6")) - exeDate = $"20{exeDate!.Substring(2)}"; - } - - // Currently stored date is the EXE date, return it - return exeDate; - } - - // The exe datetime is listed in a single line - if (line.Length >= "Recording Date and Time: ".Length + 10 && - line.StartsWith("Recording Date and Time:")) - { - // exe date: ISO datetime (yyyy-MM-ddT.....) - exeDate = line.Substring("Recording Date and Time: ".Length, 10); - } - - line = sr.ReadLine(); - } - - return null; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the build info from a GD-ROM LD area, if possible - /// - /// <String representing a formatter variant of the GD-ROM header - /// True on successful extraction of info, false otherwise - private static bool GetGDROMBuildInfo(string? segaHeader, 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.IsNullOrEmpty(segaHeader)) - return false; - - // Now read it in cutting it into lines for easier parsing - try - { - string[] header = segaHeader!.Split('\n'); - string versionLine = header[4].Substring(58); - string dateLine = header[5].Substring(58); - serial = versionLine.Substring(0, 10).TrimEnd(); - version = versionLine.Substring(10, 6).TrimStart('V', 'v'); - date = dateLine.Substring(0, 8); - return true; - } - catch - { - // We don't care what the error is - return false; - } - } - - /// - /// Get hardware information from the input file, if possible - /// - /// _drive.txt file location - /// True if hardware info was set, false otherwise - private static bool GetHardwareInfo(string drive, out string? manufacturer, out string? model, out string? firmware) - { - // Set the default values - manufacturer = null; model = null; firmware = null; - - // If the file doesn't exist, we can't get the info - if (!File.Exists(drive)) - return false; - - try - { - using var sr = File.OpenText(drive); - var line = sr.ReadLine(); - while (line != null) - { - // Trim the line for later use - line = line.Trim(); - - // Only take the first instance of each value - if (string.IsNullOrEmpty(manufacturer) && line.StartsWith("VendorId")) - { - // VendorId: - manufacturer = line.Substring("VendorId: ".Length); - } - else if (string.IsNullOrEmpty(model) && line.StartsWith("ProductId")) - { - // ProductId: - model = line.Substring("ProductId: ".Length); - } - else if (string.IsNullOrEmpty(firmware) && line.StartsWith("ProductRevisionLevel")) - { - // ProductRevisionLevel: - firmware = line.Substring("ProductRevisionLevel: ".Length); - } - - line = sr.ReadLine(); - } - - return true; - } - catch - { - // We don't care what the exception is right now - return false; - } - } - - /// - /// Get the layerbreak from the input file, if possible - /// - /// _disc.txt file location - /// True if XGD layerbreak info should be used, false otherwise - /// Layerbreak if possible, null on error - private static string? GetLayerbreak(string disc, bool xgd) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(disc)) - return null; - - try - { - using var sr = File.OpenText(disc); - var line = sr.ReadLine(); - while (line != null) - { - // Trim the line for later use - line = line.Trim(); - - // Single-layer discs have no layerbreak - if (line.Contains("NumberOfLayers: Single Layer")) - { - return null; - } - - // Xbox discs have a special layerbreaks - else if (xgd && line.StartsWith("LayerBreak")) - { - // LayerBreak: (L0 Video: , L0 Middle: , L0 Game: ) - string[] split = line.Split(' ').Where(s => !string.IsNullOrEmpty(s)).ToArray(); - return split[1]; - } - - // Dual-layer discs have a regular layerbreak - else if (!xgd && line.StartsWith("LayerZeroSector")) - { - // LayerZeroSector: () - string[] split = line.Split(' ').Where(s => !string.IsNullOrEmpty(s)).ToArray(); - return split[1]; - } - - line = sr.ReadLine(); - } - - // If we get to the end, there's an issue - return null; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get multisession information from the input file, if possible - /// - /// _disc.txt file location - /// Formatted multisession information, null on error - private static string? GetMultisessionInformation(string disc) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(disc)) - return null; - - try - { - // Seek to the TOC data - using var sr = File.OpenText(disc); - var line = sr.ReadLine(); - if (line == null) - return null; - - if (!line.StartsWith("========== TOC")) - while ((line = sr.ReadLine())?.StartsWith("========== TOC") == false) ; - if (line == null) - return null; - - // Create the required regex - var trackLengthRegex = new Regex(@"^\s*.*?Track\s*([0-9]{1,2}), LBA\s*[0-9]{1,8} - \s*[0-9]{1,8}, Length\s*([0-9]{1,8})$", RegexOptions.Compiled); - - // Read in the track length data - var trackLengthMapping = new Dictionary(); - while ((line = sr.ReadLine())?.Contains("Track") == true) - { - var match = trackLengthRegex.Match(line); - trackLengthMapping[match.Groups[1].Value] = match.Groups[2].Value; - } - - if (line == null) - return null; - - // Seek to the FULL TOC data - line = sr.ReadLine(); - if (line == null) - return null; - - if (!line.StartsWith("========== FULL TOC")) - while ((line = sr.ReadLine())?.StartsWith("========== FULL TOC") == false) ; - if (line == null) - return null; - - // Create the required regex - var trackSessionRegex = new Regex(@"^\s*Session\s*([0-9]{1,2}),.*?,\s*Track\s*([0-9]{1,2}).*?$", RegexOptions.Compiled); - - // Read in the track session data - var trackSessionMapping = new Dictionary(); - while ((line = sr.ReadLine())?.StartsWith("========== OpCode") == false) - { - if (line == null) - return null; - - var match = trackSessionRegex.Match(line); - if (!match.Success) - continue; - - trackSessionMapping[match.Groups[2].Value] = match.Groups[1].Value; - } - - // If we have all Session 1, we can just skip out - if (trackSessionMapping.All(kvp => kvp.Value == "1")) - return null; - - // Seek to the multisession data - line = sr.ReadLine()?.Trim(); - if (line == null) - return null; - - if (!line.StartsWith("Lead-out length")) - while ((line = sr.ReadLine()?.Trim())?.StartsWith("Lead-out length") == false) ; - - // TODO: Are there any examples of 3+ session discs? - - // Read the first session lead-out - var firstSessionLeadOutLengthString = line?.Substring("Lead-out length of 1st session: ".Length); - line = sr.ReadLine()?.Trim(); - if (line == null) - return null; - - // Read the second session lead-in, if it exists - string? secondSessionLeadInLengthString = null; - while (line?.StartsWith("Lead-in length") == false) - { - secondSessionLeadInLengthString = line?.Substring("Lead-in length of 2nd session: ".Length); - line = sr.ReadLine()?.Trim(); - } - - // Read the second session pregap - var secondSessionPregapLengthString = line?.Substring("Pregap length of 1st track of 2nd session: ".Length); - - // Calculate the session gap total - if (!int.TryParse(firstSessionLeadOutLengthString, out int firstSessionLeadOutLength)) - firstSessionLeadOutLength = 0; - if (!int.TryParse(secondSessionLeadInLengthString, out int secondSessionLeadInLength)) - secondSessionLeadInLength = 0; - if (!int.TryParse(secondSessionPregapLengthString, out int secondSessionPregapLength)) - secondSessionPregapLength = 0; - int sessionGapTotal = firstSessionLeadOutLength + secondSessionLeadInLength + secondSessionPregapLength; - - // Calculate first session length and total length - int firstSessionLength = 0, totalLength = 0; - foreach (var lengthMapping in trackLengthMapping) - { - if (!int.TryParse(lengthMapping.Value, out int trackLength)) - trackLength = 0; - - if (trackSessionMapping.TryGetValue(lengthMapping.Key, out var session)) - firstSessionLength += session == "1" ? trackLength : 0; - - totalLength += trackLength; - } - - // Adjust the session gap in a consistent way - if (firstSessionLength - sessionGapTotal < 0) - sessionGapTotal = firstSessionLeadOutLength + secondSessionLeadInLength; - - // Create and return the formatted output - string multisessionData = - $"Session 1: 0-{firstSessionLength - sessionGapTotal - 1}\n" - + $"Session 2: {firstSessionLength}-{totalLength - 1}"; - - return multisessionData; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the existence of an anti-modchip string from the input file, if possible - /// - /// _disc.txt file location - /// Anti-modchip existence if possible, false on error - private static bool? GetPlayStationAntiModchipDetected(string disc) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(disc)) - return null; - - try - { - // Check for either antimod string - using var sr = File.OpenText(disc); - var line = sr.ReadLine()?.Trim(); - if (line == null) - return null; - - while (!sr.EndOfStream) - { - if (line == null) - return false; - - 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 null; - } - } - - /// - /// Get the detected missing EDC count from the input files, if possible - /// - /// .img_EdcEcc.txt file location - /// Status of PS1 EDC, if possible - private static bool? GetPlayStationEDCStatus(string edcecc) - { - // If one of the files doesn't exist, we can't get info from them - if (!File.Exists(edcecc)) - return null; - - // First line of defense is the EdcEcc error file - int modeTwoNoEdc = 0; - int modeTwoFormTwo = 0; - try - { - using var sr = File.OpenText(edcecc); - while (!sr.EndOfStream) - { - var line = sr.ReadLine(); - if (line == null) - break; - - if (line.Contains("mode 2 form 2")) - modeTwoFormTwo++; - else if (line.Contains("mode 2 no edc")) - modeTwoNoEdc++; - } - - // This shouldn't happen - if (modeTwoNoEdc == 0 && modeTwoFormTwo == 0) - return null; - - // EDC exists - else if (modeTwoNoEdc == 0 && modeTwoFormTwo != 0) - return true; - - // EDC doesn't exist - else if (modeTwoNoEdc != 0 && modeTwoFormTwo == 0) - return false; - - // This shouldn't happen - else if (modeTwoNoEdc != 0 && modeTwoFormTwo != 0) - return null; - - // No idea how it would fall through - return null; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the PVD from the input file, if possible - /// - /// _mainInfo.txt file location - /// Newline-delimited PVD if possible, null on error - private static string? GetPVD(string mainInfo) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(mainInfo)) - return null; - - try - { - // If we're in a new mainInfo, the location of the header changed - using var sr = File.OpenText(mainInfo); - var line = sr.ReadLine(); - if (line == null) - return null; - - if (line.StartsWith("========== OpCode") - || line.StartsWith("========== TOC (Binary)") - || line.StartsWith("========== FULL TOC (Binary)")) - { - // Seek to unscrambled data - while ((line = sr.ReadLine())?.StartsWith("========== Check Volume Descriptor ==========") == false) ; - - // Read the next line so the search goes properly - line = sr.ReadLine(); - } - - if (line == null) - return null; - - // Make sure we're in the area - if (line.StartsWith("========== LBA") == false) - while ((line = sr.ReadLine())?.StartsWith("========== LBA") == false) ; - if (line == null) - return null; - - // If we have a Sega disc, skip sector 0 - if (line.StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) - while ((line = sr.ReadLine())?.StartsWith("========== LBA") == false) ; - if (line == null) - return null; - - // If we have a PlayStation disc, skip sector 4 - if (line.StartsWith("========== LBA[000004, 0x00004]: Main Channel ==========")) - while ((line = sr.ReadLine())?.StartsWith("========== LBA") == false) ; - if (line == null) - return null; - - // We assume the first non-LBA0/4 sector listed is the proper one - // Fast forward to the PVD - while ((line = sr.ReadLine())?.StartsWith("0310") == false) ; - - // 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; - } - } - - /// - /// Get the build info from a Saturn disc, if possible - /// - /// <String representing a formatter variant of the Saturn header - /// True on successful extraction of info, false otherwise - private static bool GetSaturnBuildInfo(string? segaHeader, 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.IsNullOrEmpty(segaHeader)) - return false; - - // Now read it in cutting it into lines for easier parsing - try - { - string[] header = segaHeader!.Split('\n'); - string serialVersionLine = header[2].Substring(58); - string dateLine = header[3].Substring(58); - serial = serialVersionLine.Substring(0, 10).Trim(); - version = serialVersionLine.Substring(10, 6).TrimStart('V', 'v'); - date = dateLine.Substring(0, 8); - date = $"{date[0]}{date[1]}{date[2]}{date[3]}-{date[4]}{date[5]}-{date[6]}{date[7]}"; - return true; - } - catch - { - // We don't care what the error is - return false; - } - } - - /// - /// Get the build info from a Sega CD disc, if possible - /// - /// <String representing a formatter variant of the Sega CD header - /// True on successful extraction of info, false otherwise - /// Note that this works for MOST headers, except ones where the copyright stretches > 1 line - private static bool GetSegaCDBuildInfo(string? segaHeader, out string? serial, out string? date) - { - serial = null; date = null; - - // If the input header is null, we can't do a thing - if (string.IsNullOrEmpty(segaHeader)) - return false; - - // Now read it in cutting it into lines for easier parsing - try - { - string[] header = segaHeader!.Split('\n'); - string serialVersionLine = header[8].Substring(58); - string dateLine = header[1].Substring(58); - serial = serialVersionLine.Substring(3, 8).TrimEnd('-', ' '); - date = dateLine.Substring(8).Trim(); - - // Properly format the date string, if possible - string[] dateSplit = date.Split('.'); - - if (dateSplit.Length == 1) - dateSplit = [date.Substring(0, 4), date.Substring(4)]; - - string month = dateSplit[1]; - dateSplit[1] = month switch - { - "JAN" => "01", - "FEB" => "02", - "MAR" => "03", - "APR" => "04", - "MAY" => "05", - "JUN" => "06", - "JUL" => "07", - "AUG" => "08", - "SEP" => "09", - "OCT" => "10", - "NOV" => "11", - "DEC" => "12", - _ => "00", - }; - - date = string.Join("-", dateSplit); - - return true; - } - catch - { - // We don't care what the error is - return false; - } - } - - /// - /// Get the header from a Sega CD / Mega CD, Saturn, or Dreamcast Low-Density region, if possible - /// - /// _mainInfo.txt file location - /// Header as a byte array if possible, null on error - private static string? GetSegaHeader(string mainInfo) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(mainInfo)) - return null; - - try - { - // If we're in a new mainInfo, the location of the header changed - using var sr = File.OpenText(mainInfo); - var line = sr.ReadLine(); - if (line == null) - return null; - - if (line.StartsWith("========== OpCode") - || line.StartsWith("========== TOC (Binary)") - || line.StartsWith("========== FULL TOC (Binary)")) - { - // Seek to unscrambled data - while ((line = sr.ReadLine())?.Contains("Check MCN and/or ISRC") == false) ; - if (line == null) - return null; - - // Read the next line so the search goes properly - line = sr.ReadLine(); - } - - if (line == null) - return null; - - // Make sure we're in the area - if (!line.StartsWith("========== LBA")) - while ((line = sr.ReadLine())?.StartsWith("========== LBA") == false) ; - if (line == null) - return null; - - // Make sure we're in the right sector - if (!line.StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) - while ((line = sr.ReadLine())?.StartsWith("========== LBA[000000, 0000000]: Main Channel ==========") == false) ; - if (line == null) - return null; - - // Fast forward to the header - while ((line = sr.ReadLine())?.Trim()?.StartsWith("+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F") == false) ; - if (line == null) - return null; - - // Now that we're at the Header, read each line in and concatenate - string header = ""; - for (int i = 0; i < 32; i++) - header += sr.ReadLine() + "\n"; // 0000-01F0 - - return header; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the write offset from the input file, if possible - /// - /// _disc.txt file location - /// Sample write offset if possible, null on error - private static string? GetWriteOffset(string disc) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(disc)) - return null; - - try - { - // Fast forward to the offsets - using var sr = File.OpenText(disc); - while (sr.ReadLine()?.Trim()?.StartsWith("========== Offset") == false) ; - 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; - } - } - - /// - /// Get the XGD auxiliary hash info from the outputted files, if possible - /// - /// Datafile representing the supplementary hashes - /// Extracted DMI.bin CRC32 hash (upper-cased) - /// Extracted PFI.bin CRC32 hash (upper-cased) - /// Extracted SS.bin CRC32 hash (upper-cased) - /// True on successful extraction of info, false otherwise - /// Currently only the CRC32 values are returned for each, this may change in the future - private static bool GetXGDAuxHashInfo(Datafile? suppl, out string? dmihash, out string? pfihash, out string? sshash) - { - // Assign values to all outputs first - dmihash = null; pfihash = null; sshash = null; - - // If we don't have a valid datafile, we can't do anything - if (suppl?.Games == null) - return false; - - // Try to extract the hash information - var roms = suppl.Games[0].Roms; - if (roms == null || roms.Length == 0) - return false; - - dmihash = roms.FirstOrDefault(r => r.Name?.EndsWith("DMI.bin") == true)?.Crc?.ToUpperInvariant(); - pfihash = roms.FirstOrDefault(r => r.Name?.EndsWith("PFI.bin") == true)?.Crc?.ToUpperInvariant(); - sshash = roms.FirstOrDefault(r => r.Name?.EndsWith("SS.bin") == true)?.Crc?.ToUpperInvariant(); - - return true; - } - - /// - /// Get the XGD auxiliary info from the outputted files, if possible - /// - /// _disc.txt file location - /// Extracted DMI.bin CRC32 hash (upper-cased) - /// Extracted PFI.bin CRC32 hash (upper-cased) - /// Extracted SS.bin CRC32 hash (upper-cased) - /// Extracted security sector data - /// Extracted security sector version - /// True on successful extraction of info, false otherwise - private static bool GetXGDAuxInfo(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; - - // This flag is needed because recent versions of DIC include security data twice - bool foundSecuritySectors = false; - - // SS version for all Kreon DIC dumps is v1 - ssver = "01"; - - try - { - using var sr = File.OpenText(disc); - while (!sr.EndOfStream) - { - var line = sr.ReadLine()?.Trim(); - if (line == null) - break; - - // XGD version (1 = Xbox, 2 = Xbox360) - /* - if (line.StartsWith("Version of challenge table")) - { - xgdver = line.Split(' ')[4]; // "Version of challenge table: " - } - */ - - // Security Sector ranges - else if (line.StartsWith("Number of security sector ranges:") && !foundSecuritySectors) - { - // Set the flag so we don't read duplicate data - foundSecuritySectors = true; - - var layerRegex = new Regex(@"Layer [01].*, startLBA-endLBA:\s*(\d+)-\s*(\d+)", RegexOptions.Compiled); - - line = sr.ReadLine()?.Trim(); - if (line == null) - break; - - while (!line.StartsWith("========== TotalLength ==========") - && !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(); - if (line == null) - break; - } - - if (line == null) - break; - } - - // Special File Hashes - else if (line.StartsWith(" - /// Get the XGD auxiliary security sector info from the outputted files, if possible - /// - /// _disc.txt file location - /// Extracted security sector data - /// Extracted security sector version - /// True on successful extraction of info, false otherwise - private static bool GetXGDAuxSSInfo(string disc, out string? ss, out string? ssver) - { - ss = null; ssver = null; - - // If the file doesn't exist, we can't get info from it - if (!File.Exists(disc)) - return false; - - // This flag is needed because recent versions of DIC include security data twice - bool foundSecuritySectors = false; - - // SS version for all Kreon DIC dumps is v1 - ssver = "01"; - - try - { - using var sr = File.OpenText(disc); - while (!sr.EndOfStream) - { - var line = sr.ReadLine()?.Trim(); - if (line == null) - break; - - // XGD version (1 = Xbox, 2 = Xbox360) - /* - if (line.StartsWith("Version of challenge table")) - { - xgdver = line.Split(' ')[4]; // "Version of challenge table: " - } - */ - - // Security Sector ranges - else if (line.StartsWith("Number of security sector ranges:") && !foundSecuritySectors) - { - // Set the flag so we don't read duplicate data - foundSecuritySectors = true; - - var layerRegex = new Regex(@"Layer [01].*, startLBA-endLBA:\s*(\d+)-\s*(\d+)", RegexOptions.Compiled); - - line = sr.ReadLine()?.Trim(); - if (line == null) - break; - - while (!line.StartsWith("========== TotalLength ==========") - && !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(); - if (line == null) - break; - } - - if (line == null) - break; - } - } - - return true; - } - catch - { - // We don't care what the exception is right now - return false; - } - } - - #endregion } } diff --git a/MPF.Core/Modules/PS3CFW/Parameters.cs b/MPF.Core/Modules/PS3CFW/Parameters.cs index c75f7ce3..e770bb67 100644 --- a/MPF.Core/Modules/PS3CFW/Parameters.cs +++ b/MPF.Core/Modules/PS3CFW/Parameters.cs @@ -1,10 +1,4 @@ -using System.Collections.Generic; -using System.IO; -using System.Text.RegularExpressions; -using MPF.Core.Converters; -using MPF.Core.Data; -using SabreTools.Hashing; -using SabreTools.RedumpLib; +using MPF.Core.Data; using SabreTools.RedumpLib.Data; namespace MPF.Core.Modules.PS3CFW @@ -29,209 +23,5 @@ namespace MPF.Core.Modules.PS3CFW : base(system, type, drivePath, filename, driveSpeed, options) { } - - #region BaseParameters Implementations - - /// - public override (bool, List) CheckAllOutputFilesExist(string basePath, bool preCheck) - { - var missingFiles = new List(); - - if (this.Type != MediaType.BluRay || this.System != RedumpSystem.SonyPlayStation3) - { - missingFiles.Add("Media and system combination not supported for PS3 CFW"); - } - else - { - string? getKeyBasePath = GetCFWBasePath(basePath); - if (!File.Exists($"{getKeyBasePath}.getkey.log")) - missingFiles.Add($"{getKeyBasePath}.getkey.log"); - if (!File.Exists($"{getKeyBasePath}.disc.pic")) - missingFiles.Add($"{getKeyBasePath}.disc.pic"); - } - - return (missingFiles.Count == 0, missingFiles); - } - - /// - public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts) - { - // Ensure that required sections exist - info = Builder.EnsureAllSections(info); - - info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram); - - // Get the Datafile information - Datafile? datafile = GeneratePS3CFWDatafile(basePath + ".iso"); - - // Fill in the hash data - info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile); - - // Get the individual hash data, as per internal - if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1)) - { - info.SizeAndChecksums!.Size = size; - info.SizeAndChecksums.CRC32 = crc32; - info.SizeAndChecksums.MD5 = md5; - info.SizeAndChecksums.SHA1 = sha1; - } - - // Get the PVD from the ISO - if (GetPVD(basePath + ".iso", out string? pvd)) - info.Extras!.PVD = pvd; - - // Try get the serial, version, and firmware version if a drive is provided - if (drive != null) - { - info.VersionAndEditions!.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty; - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty; - string? firmwareVersion = InfoTool.GetPlayStation3FirmwareVersion(drive?.Name); - if (firmwareVersion != null) - info.CommonDiscInfo!.ContentsSpecialFields![SiteCode.Patches] = $"PS3 Firmware {firmwareVersion}"; - } - - // Try to determine the name of the GetKey file(s) - string? getKeyBasePath = GetCFWBasePath(basePath); - - // If GenerateSubmissionInfo is run, .getkey.log existence should already be checked - if (!File.Exists(getKeyBasePath + ".getkey.log")) - return; - - // Get dumping date from GetKey log date - info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(getKeyBasePath + ".getkey.log")?.ToString("yyyy-MM-dd HH:mm:ss"); - - // TODO: Put info about abnormal PIC info beyond 132 bytes in comments? - if (File.Exists(getKeyBasePath + ".disc.pic")) - info.Extras!.PIC = GetPIC(getKeyBasePath + ".disc.pic", 264); - - // Parse Disc Key, Disc ID, and PIC from the .getkey.log file - if (Utilities.Tools.ParseGetKeyLog(getKeyBasePath + ".getkey.log", out string? key, out string? id, out string? pic)) - { - if (key != null) - info.Extras!.DiscKey = key.ToUpperInvariant(); - if (id != null) - info.Extras!.DiscID = id.ToUpperInvariant().Substring(0, 24) + "XXXXXXXX"; - if (string.IsNullOrEmpty(info.Extras!.PIC) && !string.IsNullOrEmpty(pic)) - { - pic = Regex.Replace(pic, ".{32}", "$0\n"); - info.Extras.PIC = pic; - } - } - - // Fill in any artifacts that exist, Base64-encoded, if we need to - if (includeArtifacts) - { - info.Artifacts ??= []; - - if (File.Exists(getKeyBasePath + ".disc.pic")) - info.Artifacts["discpic"] = GetBase64(GetFullFile(getKeyBasePath + ".disc.pic", binary: true)) ?? string.Empty; - if (File.Exists(getKeyBasePath + ".getkey.log")) - info.Artifacts["getkeylog"] = GetBase64(GetFullFile(getKeyBasePath + ".getkey.log")) ?? string.Empty; - } - } - - /// - public override List GetLogFilePaths(string basePath) - { - var logFiles = new List(); - string? getKeyBasePath = GetCFWBasePath(basePath); - - if (this.System != RedumpSystem.SonyPlayStation3) - return logFiles; - - switch (this.Type) - { - case MediaType.BluRay: - if (File.Exists($"{getKeyBasePath}.getkey.log")) - logFiles.Add($"{getKeyBasePath}.getkey.log"); - if (File.Exists($"{getKeyBasePath}.disc.pic")) - logFiles.Add($"{getKeyBasePath}.disc.pic"); - - break; - } - - return logFiles; - } - - #endregion - - #region Information Extraction Methods - - /// - /// Get a formatted datfile from the PS3 CFW output, if possible - /// - /// Path to ISO file - /// - private static Datafile? GeneratePS3CFWDatafile(string iso) - { - // If the ISO file doesn't exist, we can't get info from it - if (!File.Exists(iso)) - return null; - - try - { - if (HashTool.GetStandardHashes(iso, out long size, out string? crc, out string? md5, out string? sha1)) - { - return new Datafile - { - Games = [new Game { Roms = [new Rom { Name = Path.GetFileName(iso), Size = size.ToString(), Crc = crc, Md5 = md5, Sha1 = sha1, }] }] - }; - } - return null; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get a formatted datfile from the PS3 CFW output, if possible - /// - /// Path to ISO file - /// Formatted datfile, null if not valid - private static string? GetPS3CFWDatfile(string iso) - { - // If the files don't exist, we can't get info from it - if (!File.Exists(iso)) - return null; - - try - { - if (HashTool.GetStandardHashes(iso, out long size, out string? crc, out string? md5, out string? sha1)) - return $""; - return null; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - #endregion - - #region Helper Functions - - /// - /// Estimate the base filename of the .getkey.log file associated with the dump - /// - /// Path to ISO file - /// Base filename, null if not found - private string? GetCFWBasePath(string iso) - { - string? dir = Path.GetDirectoryName(iso); - dir ??= "."; - - string[] files = Directory.GetFiles(dir, "*.getkey.log"); - - if (files.Length != 1) - return null; - - return files[0].Substring(0, files[0].Length - 11); - } - - #endregion } } diff --git a/MPF.Core/Modules/Redumper/Parameters.cs b/MPF.Core/Modules/Redumper/Parameters.cs index 73f4cbd7..b2c48406 100644 --- a/MPF.Core/Modules/Redumper/Parameters.cs +++ b/MPF.Core/Modules/Redumper/Parameters.cs @@ -3,10 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; -using MPF.Core.Converters; using MPF.Core.Data; -using SabreTools.Models.CueSheets; -using SabreTools.RedumpLib; using SabreTools.RedumpLib.Data; namespace MPF.Core.Modules.Redumper @@ -176,469 +173,6 @@ namespace MPF.Core.Modules.Redumper #region BaseParameters Implementations - /// - public override (bool, List) CheckAllOutputFilesExist(string basePath, bool preCheck) - { - var missingFiles = new List(); - - switch (this.Type) - { - case MediaType.CDROM: - if (!File.Exists($"{basePath}.cue")) - missingFiles.Add($"{basePath}.cue"); - if (!File.Exists($"{basePath}.scram") && !File.Exists($"{basePath}.scrap")) - missingFiles.Add($"{basePath}.scram"); - - if (!File.Exists($"{basePath}_logs.zip") || !preCheck) - { - if (!File.Exists($"{basePath}.fulltoc")) - missingFiles.Add($"{basePath}.fulltoc"); - if (!File.Exists($"{basePath}.log")) - missingFiles.Add($"{basePath}.log"); - else if (GetDatfile($"{basePath}.log") == null) - missingFiles.Add($"{basePath}.log (dat section)"); - if (!File.Exists($"{basePath}.state")) - missingFiles.Add($"{basePath}.state"); - if (!File.Exists($"{basePath}.subcode")) - missingFiles.Add($"{basePath}.subcode"); - if (!File.Exists($"{basePath}.toc")) - missingFiles.Add($"{basePath}.toc"); - } - - // Removed or inconsistent files - //{ - // // Depends on the disc - // if (!File.Exists($"{basePath}.cdtext")) - // missingFiles.Add($"{basePath}.cdtext"); - // - // // Not available in all versions - // if (!File.Exists($"{basePath}.hash")) - // missingFiles.Add($"{basePath}.hash"); - // // Also: "{basePath} (Track X).hash" (get from cuesheet) - // if (!File.Exists($"{basePath}.skeleton")) - // missingFiles.Add($"{basePath}.skeleton"); - // // Also: "{basePath} (Track X).skeleton" (get from cuesheet) - //} - - break; - - case MediaType.DVD: - if (!File.Exists($"{basePath}_logs.zip") || !preCheck) - { - if (!File.Exists($"{basePath}.log")) - missingFiles.Add($"{basePath}.log"); - else if (GetDatfile($"{basePath}.log") == null) - missingFiles.Add($"{basePath}.dat"); - if (!File.Exists($"{basePath}.manufacturer") && !File.Exists($"{basePath}.1.manufacturer") && !File.Exists($"{basePath}.2.manufacturer")) - missingFiles.Add($"{basePath}.manufacturer"); - if (!File.Exists($"{basePath}.physical") && !File.Exists($"{basePath}.0.physical") && !File.Exists($"{basePath}.1.physical") && !File.Exists($"{basePath}.2.physical")) - missingFiles.Add($"{basePath}.physical"); - if (!File.Exists($"{basePath}.state")) - missingFiles.Add($"{basePath}.state"); - } - - // Removed or inconsistent files - //{ - // // Not available in all versions - // if (!File.Exists($"{basePath}.hash")) - // missingFiles.Add($"{basePath}.hash"); - // if (!File.Exists($"{basePath}.skeleton")) - // missingFiles.Add($"{basePath}.skeleton"); - //} - - break; - - case MediaType.HDDVD: // TODO: Verify that this is output - case MediaType.BluRay: - if (!File.Exists($"{basePath}_logs.zip") || !preCheck) - { - if (!File.Exists($"{basePath}.log")) - missingFiles.Add($"{basePath}.log"); - else if (GetDatfile($"{basePath}.log") == null) - missingFiles.Add($"{basePath}.dat"); - if (!File.Exists($"{basePath}.physical") && !File.Exists($"{basePath}.0.physical") && !File.Exists($"{basePath}.1.physical") && !File.Exists($"{basePath}.2.physical")) - missingFiles.Add($"{basePath}.physical"); - if (!File.Exists($"{basePath}.state")) - missingFiles.Add($"{basePath}.state"); - } - - // Removed or inconsistent files - //{ - // // Not available in all versions - // if (!File.Exists($"{basePath}.hash")) - // missingFiles.Add($"{basePath}.hash"); - // if (!File.Exists($"{basePath}.skeleton")) - // missingFiles.Add($"{basePath}.skeleton"); - //} - - break; - - default: - missingFiles.Add("Media and system combination not supported for Redumper"); - break; - } - - return (!missingFiles.Any(), missingFiles); - } - - /// - public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts) - { - // Ensure that required sections exist - info = Builder.EnsureAllSections(info); - - // Get the dumping program and version - info.DumpingInfo!.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {GetVersion($"{basePath}.log") ?? "Unknown Version"}"; - info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate($"{basePath}.log")?.ToString("yyyy-MM-dd HH:mm:ss"); - - // Fill in the hardware data - if (GetHardwareInfo($"{basePath}.log", out var manufacturer, out var model, out var firmware)) - { - info.DumpingInfo.Manufacturer = manufacturer; - info.DumpingInfo.Model = model; - info.DumpingInfo.Firmware = firmware; - } - - // Fill in the disc type data - if (GetDiscType($"{basePath}.log", out var discTypeOrBookType)) - info.DumpingInfo.ReportedDiscType = discTypeOrBookType; - - // Fill in the volume labels - if (GetVolumeLabels($"{basePath}.log", out var volLabels)) - VolumeLabels = volLabels; - - switch (this.Type) - { - case MediaType.CDROM: - info.Extras!.PVD = GetPVD($"{basePath}.log") ?? "Disc has no PVD"; - info.TracksAndWriteOffsets!.ClrMameProData = GetDatfile($"{basePath}.log"); - info.TracksAndWriteOffsets.Cuesheet = GetFullFile($"{basePath}.cue") ?? string.Empty; - - // Attempt to get the write offset - string cdWriteOffset = GetWriteOffset($"{basePath}.log") ?? string.Empty; - info.CommonDiscInfo!.RingWriteOffset = cdWriteOffset; - info.TracksAndWriteOffsets.OtherWriteOffsets = cdWriteOffset; - - // Attempt to get the error count - if (GetErrorCount($"{basePath}.log", out long redumpErrors, out long c2Errors)) - { - info.CommonDiscInfo.ErrorsCount = (redumpErrors == -1 ? "Error retrieving error count" : redumpErrors.ToString()); - info.DumpingInfo.C2ErrorsCount = (c2Errors == -1 ? "Error retrieving error count" : c2Errors.ToString()); - } - - // Attempt to get multisession data - string cdMultiSessionInfo = GetMultisessionInformation($"{basePath}.log") ?? string.Empty; - if (!string.IsNullOrEmpty(cdMultiSessionInfo)) - info.CommonDiscInfo.CommentsSpecialFields![SiteCode.Multisession] = cdMultiSessionInfo; - - // Attempt to get the universal hash, if it's an audio disc - if (this.System.IsAudio()) - { - string universalHash = GetUniversalHash($"{basePath}.log") ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields![SiteCode.UniversalHash] = universalHash; - } - - // Attempt to get the non-zero data start, if it's an audio disc - if (this.System.IsAudio()) - { - string ringNonZeroDataStart = GetRingNonZeroDataStart($"{basePath}.log") ?? string.Empty; - info.CommonDiscInfo.CommentsSpecialFields![SiteCode.RingNonZeroDataStart] = ringNonZeroDataStart; - } - - break; - - case MediaType.DVD: - case MediaType.HDDVD: - case MediaType.BluRay: - info.Extras!.PVD = GetPVD($"{basePath}.log") ?? "Disc has no PVD"; - info.TracksAndWriteOffsets!.ClrMameProData = GetDatfile($"{basePath}.log"); - - // Get the individual hash data, as per internal - if (InfoTool.GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out var crc32, out var md5, out var sha1)) - { - info.SizeAndChecksums!.Size = size; - info.SizeAndChecksums.CRC32 = crc32; - info.SizeAndChecksums.MD5 = md5; - info.SizeAndChecksums.SHA1 = sha1; - } - - // Deal with the layerbreaks - if (GetLayerbreaks($"{basePath}.log", out var layerbreak1, out var layerbreak2, out var layerbreak3)) - { - info.SizeAndChecksums!.Layerbreak = !string.IsNullOrEmpty(layerbreak1) ? Int64.Parse(layerbreak1) : default; - info.SizeAndChecksums!.Layerbreak2 = !string.IsNullOrEmpty(layerbreak2) ? Int64.Parse(layerbreak2) : default; - info.SizeAndChecksums!.Layerbreak3 = !string.IsNullOrEmpty(layerbreak3) ? Int64.Parse(layerbreak3) : default; - } - - // Bluray-specific options - if (this.Type == MediaType.BluRay) - { - int trimLength = -1; - switch (this.System) - { - case RedumpSystem.MicrosoftXboxOne: - case RedumpSystem.MicrosoftXboxSeriesXS: - case RedumpSystem.SonyPlayStation3: - case RedumpSystem.SonyPlayStation4: - case RedumpSystem.SonyPlayStation5: - if (info.SizeAndChecksums!.Layerbreak3 != default) - trimLength = 520; - else if (info.SizeAndChecksums!.Layerbreak2 != default) - trimLength = 392; - else - trimLength = 264; - break; - } - - info.Extras!.PIC = GetPIC($"{basePath}.physical", trimLength) - ?? GetPIC($"{basePath}.0.physical", trimLength) - ?? GetPIC($"{basePath}.1.physical", trimLength) - ?? string.Empty; - - var di = InfoTool.GetDiscInformation($"{basePath}.physical") - ?? InfoTool.GetDiscInformation($"{basePath}.0.physical") - ?? InfoTool.GetDiscInformation($"{basePath}.1.physical"); - info.SizeAndChecksums!.PICIdentifier = InfoTool.GetPICIdentifier(di); - } - - break; - } - - switch (this.System) - { - case RedumpSystem.AppleMacintosh: - case RedumpSystem.EnhancedCD: - case RedumpSystem.IBMPCcompatible: - case RedumpSystem.RainbowDisc: - case RedumpSystem.SonyElectronicBook: - info.CopyProtection!.SecuROMData = GetSecuROMData($"{basePath}.log") ?? string.Empty; - - // Needed for some odd copy protections - info.CopyProtection!.Protection = GetDVDProtection($"{basePath}.log", false) ?? string.Empty; - break; - - case RedumpSystem.DVDAudio: - case RedumpSystem.DVDVideo: - info.CopyProtection!.Protection = GetDVDProtection($"{basePath}.log", true) ?? string.Empty; - break; - - case RedumpSystem.KonamiPython2: - // Get metadata from log if possible - if (GetPlayStationInfo($"{basePath}.log", out string? kp2EXEDate, out string? kp2Serial, out string? kp2Version, out var _)) - { - info.CommonDiscInfo!.EXEDateBuildDate = kp2EXEDate; - info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = kp2Serial ?? string.Empty; - if (!string.IsNullOrEmpty(kp2Serial)) - info.CommonDiscInfo.Region = InfoTool.GetPlayStationRegion(info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName]); - info.VersionAndEditions!.Version = kp2Version ?? string.Empty; - } - // Get metadata from drive if not available from log - else if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate)) - { - info.CommonDiscInfo!.EXEDateBuildDate = pythonTwoDate; - info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty; - info.CommonDiscInfo.Region ??= pythonTwoRegion; - info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty; - } - - break; - - case RedumpSystem.MicrosoftXbox: - // TODO: Support DMI and additional file information when generated - break; - - case RedumpSystem.MicrosoftXbox360: - // TODO: Support DMI and additional file information when generated - break; - - case RedumpSystem.NamcoSegaNintendoTriforce: - // TODO: Support header information and GD-ROM info when generated - break; - - case RedumpSystem.SegaMegaCDSegaCD: - info.Extras!.Header = GetSegaCDHeader($"{basePath}.log", out var scdBuildDate, out var scdSerial, out _) ?? string.Empty; - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = scdSerial ?? string.Empty; - info.CommonDiscInfo.EXEDateBuildDate = scdBuildDate ?? string.Empty; - // TODO: Support region setting from parsed value - break; - - case RedumpSystem.SegaChihiro: - // TODO: Support header information and GD-ROM info when generated - break; - - case RedumpSystem.SegaDreamcast: - // TODO: Support header information and GD-ROM info when generated - break; - - case RedumpSystem.SegaNaomi: - // TODO: Support header information and GD-ROM info when generated - break; - - case RedumpSystem.SegaNaomi2: - // TODO: Support header information and GD-ROM info when generated - break; - - case RedumpSystem.SegaSaturn: - info.Extras!.Header = GetSaturnHeader($"{basePath}.log") ?? string.Empty; - - // Take only the first 16 lines for Saturn - if (!string.IsNullOrEmpty(info.Extras.Header)) - info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray()); - - if (GetSaturnBuildInfo(info.Extras.Header, out var saturnSerial, out var saturnVersion, out var buildDate)) - { - // Ensure internal serial is pulled from local data - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = saturnSerial ?? string.Empty; - info.VersionAndEditions!.Version = saturnVersion ?? string.Empty; - info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? string.Empty; - } - - break; - - case RedumpSystem.SonyPlayStation: - // Get metadata from log if possible - if (GetPlayStationInfo($"{basePath}.log", out string? psxEXEDate, out string? psxSerial, out var _, out var _)) - { - info.CommonDiscInfo!.EXEDateBuildDate = psxEXEDate; - info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = psxSerial ?? string.Empty; - if (!string.IsNullOrEmpty(psxSerial)) - info.CommonDiscInfo.Region = InfoTool.GetPlayStationRegion(info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName]); - } - // Get metadata from drive if not available from log - else if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate)) - { - info.CommonDiscInfo!.EXEDateBuildDate = playstationDate; - info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationSerial ?? string.Empty; - info.CommonDiscInfo.Region ??= playstationRegion; - } - - info.CopyProtection!.AntiModchip = GetPlayStationAntiModchipDetected($"{basePath}.log").ToYesNo(); - info.EDC!.EDC = GetPlayStationEDCStatus($"{basePath}.log").ToYesNo(); - info.CopyProtection.LibCrypt = GetPlayStationLibCryptStatus($"{basePath}.log").ToYesNo(); - info.CopyProtection.LibCryptData = GetPlayStationLibCryptData($"{basePath}.log"); - break; - - case RedumpSystem.SonyPlayStation2: - // Get metadata from log if possible - if (GetPlayStationInfo($"{basePath}.log", out string? ps2EXEDate, out string? ps2Serial, out var ps2Version, out var _)) - { - info.CommonDiscInfo!.EXEDateBuildDate = ps2EXEDate; - info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = ps2Serial ?? string.Empty; - if (!string.IsNullOrEmpty(ps2Serial)) - info.CommonDiscInfo.Region = InfoTool.GetPlayStationRegion(info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName]); - info.VersionAndEditions!.Version = ps2Version ?? string.Empty; - } - // Get metadata from drive if not available from log - else if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate)) - { - info.CommonDiscInfo!.EXEDateBuildDate ??= playstationTwoDate; - info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty; - info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion; - info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty; - } - - break; - - case RedumpSystem.SonyPlayStation3: - // Get metadata from log if possible - if (GetPlayStationInfo($"{basePath}.log", out var _, out string? ps3Serial, out var ps3Version, out string? firmwareVersion)) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = ps3Serial ?? string.Empty; - info.VersionAndEditions!.Version = ps3Version ?? string.Empty; - } - // Get metadata from drive if not available from log - else - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty; - info.VersionAndEditions!.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty; - firmwareVersion = InfoTool.GetPlayStation3FirmwareVersion(drive?.Name); - } - - // Set the firmware version as a piece of content - if (firmwareVersion != null) - info.CommonDiscInfo.ContentsSpecialFields![SiteCode.Patches] = $"PS3 Firmware {firmwareVersion}"; - - break; - - case RedumpSystem.SonyPlayStation4: - // Get metadata from log if possible - if (GetPlayStationInfo($"{basePath}.log", out var _, out string? ps4Serial, out var ps4Version, out var _)) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = ps4Serial ?? string.Empty; - info.VersionAndEditions!.Version = ps4Version ?? string.Empty; - } - // Get metadata from drive if not available from log - else - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty; - info.VersionAndEditions!.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty; - } - - break; - - case RedumpSystem.SonyPlayStation5: - // Get metadata from log if possible - if (GetPlayStationInfo($"{basePath}.log", out var _, out string? ps5Serial, out var ps5Version, out var _)) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = ps5Serial ?? string.Empty; - info.VersionAndEditions!.Version = ps5Version ?? string.Empty; - } - // Get metadata from drive if not available from log - else - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty; - info.VersionAndEditions!.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty; - } - - break; - } - - // Fill in any artifacts that exist, Base64-encoded, if we need to - if (includeArtifacts) - { - info.Artifacts ??= []; - - if (File.Exists($"{basePath}.cdtext")) - info.Artifacts["cdtext"] = GetBase64(GetFullFile($"{basePath}.cdtext")) ?? string.Empty; - if (File.Exists($"{basePath}.cue")) - info.Artifacts["cue"] = GetBase64(GetFullFile($"{basePath}.cue")) ?? string.Empty; - if (File.Exists($"{basePath}.fulltoc")) - info.Artifacts["fulltoc"] = GetBase64(GetFullFile($"{basePath}.fulltoc")) ?? string.Empty; - if (File.Exists($"{basePath}.hash")) - info.Artifacts["hash"] = GetBase64(GetFullFile($"{basePath}.hash")) ?? string.Empty; - // TODO: "{basePath} (Track X).hash" (get from cuesheet) - if (File.Exists($"{basePath}.log")) - info.Artifacts["log"] = GetBase64(GetFullFile($"{basePath}.log")) ?? string.Empty; - if (File.Exists($"{basePath}.manufacturer")) - info.Artifacts["manufacturer"] = GetBase64(GetFullFile($"{basePath}.manufacturer")) ?? string.Empty; - if (File.Exists($"{basePath}.1.manufacturer")) - info.Artifacts["manufacturer1"] = GetBase64(GetFullFile($"{basePath}.1.manufacturer")) ?? string.Empty; - if (File.Exists($"{basePath}.2.manufacturer")) - info.Artifacts["manufacturer2"] = GetBase64(GetFullFile($"{basePath}.2.manufacturer")) ?? string.Empty; - if (File.Exists($"{basePath}.physical")) - info.Artifacts["physical"] = GetBase64(GetFullFile($"{basePath}.physical")) ?? string.Empty; - if (File.Exists($"{basePath}.0.physical")) - info.Artifacts["physical0"] = GetBase64(GetFullFile($"{basePath}.0.physical")) ?? string.Empty; - if (File.Exists($"{basePath}.1.physical")) - info.Artifacts["physical1"] = GetBase64(GetFullFile($"{basePath}.1.physical")) ?? string.Empty; - if (File.Exists($"{basePath}.2.physical")) - info.Artifacts["physical2"] = GetBase64(GetFullFile($"{basePath}.2.physical")) ?? string.Empty; - // if (File.Exists($"{basePath}.skeleton")) - // info.Artifacts["skeleton"] = GetBase64(GetFullFile($"{basePath}.skeleton")) ?? string.Empty; - // // Also: "{basePath} (Track X).skeleton" (get from cuesheet) - // if (File.Exists($"{basePath}.scram")) - // info.Artifacts["scram"] = GetBase64(GetFullFile($"{basePath}.scram")) ?? string.Empty; - // if (File.Exists($"{basePath}.scrap")) - // info.Artifacts["scrap"] = GetBase64(GetFullFile($"{basePath}.scrap")) ?? string.Empty; - if (File.Exists($"{basePath}.state")) - info.Artifacts["state"] = GetBase64(GetFullFile($"{basePath}.state")) ?? string.Empty; - if (File.Exists($"{basePath}.subcode")) - info.Artifacts["subcode"] = GetBase64(GetFullFile($"{basePath}.subcode")) ?? string.Empty; - if (File.Exists($"{basePath}.toc")) - info.Artifacts["toc"] = GetBase64(GetFullFile($"{basePath}.toc")) ?? string.Empty; - } - } - /// /// /// Redumper is unique in that the base command can be multiple @@ -958,117 +492,6 @@ namespace MPF.Core.Modules.Redumper /// public override string? GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType); - /// - public override List GetDeleteableFilePaths(string basePath) - { - var deleteableFiles = new List(); - - if (File.Exists($"{basePath}.scram")) - deleteableFiles.Add($"{basePath}.scram"); - if (File.Exists($"{basePath}.scrap")) - deleteableFiles.Add($"{basePath}.scrap"); - - return deleteableFiles; - } - - /// - public override List GetLogFilePaths(string basePath) - { - var logFiles = new List(); - - switch (this.Type) - { - case MediaType.CDROM: - if (File.Exists($"{basePath}.cdtext")) - logFiles.Add($"{basePath}.cdtext"); - if (File.Exists($"{basePath}.fulltoc")) - logFiles.Add($"{basePath}.fulltoc"); - if (File.Exists($"{basePath}.log")) - logFiles.Add($"{basePath}.log"); - if (File.Exists($"{basePath}.state")) - logFiles.Add($"{basePath}.state"); - if (File.Exists($"{basePath}.subcode")) - logFiles.Add($"{basePath}.subcode"); - if (File.Exists($"{basePath}.toc")) - logFiles.Add($"{basePath}.toc"); - - // Include .hash and .skeleton for all files in cuesheet - var cueSheet = SabreTools.Serialization.Deserializers.CueSheet.DeserializeFile($"{basePath}.cue"); - string? baseDir = Path.GetDirectoryName(basePath); - if (cueSheet?.Files != null && baseDir != null) - { - foreach (CueFile? file in cueSheet.Files) - { - string? trackName = Path.GetFileNameWithoutExtension(file?.FileName); - if (trackName == null) - continue; - - string trackPath = Path.Combine(baseDir, trackName); - if (File.Exists($"{trackPath}.hash")) - logFiles.Add($"{trackPath}.hash"); - if (File.Exists($"{trackPath}.skeleton")) - logFiles.Add($"{trackPath}.skeleton"); - } - } - else - { - if (File.Exists($"{basePath}.hash")) - logFiles.Add($"{basePath}.hash"); - if (File.Exists($"{basePath}.skeleton")) - logFiles.Add($"{basePath}.skeleton"); - } - - break; - - case MediaType.DVD: - if (File.Exists($"{basePath}.hash")) - logFiles.Add($"{basePath}.hash"); - if (File.Exists($"{basePath}.log")) - logFiles.Add($"{basePath}.log"); - if (File.Exists($"{basePath}.manufacturer")) - logFiles.Add($"{basePath}.manufacturer"); - if (File.Exists($"{basePath}.1.manufacturer")) - logFiles.Add($"{basePath}.1.manufacturer"); - if (File.Exists($"{basePath}.2.manufacturer")) - logFiles.Add($"{basePath}.2.manufacturer"); - if (File.Exists($"{basePath}.physical")) - logFiles.Add($"{basePath}.physical"); - if (File.Exists($"{basePath}.0.physical")) - logFiles.Add($"{basePath}.0.physical"); - if (File.Exists($"{basePath}.1.physical")) - logFiles.Add($"{basePath}.1.physical"); - if (File.Exists($"{basePath}.2.physical")) - logFiles.Add($"{basePath}.2.physical"); - if (File.Exists($"{basePath}.skeleton")) - logFiles.Add($"{basePath}.skeleton"); - if (File.Exists($"{basePath}.state")) - logFiles.Add($"{basePath}.state"); - break; - - case MediaType.HDDVD: // TODO: Confirm that this information outputs - case MediaType.BluRay: - if (File.Exists($"{basePath}.hash")) - logFiles.Add($"{basePath}.hash"); - if (File.Exists($"{basePath}.log")) - logFiles.Add($"{basePath}.log"); - if (File.Exists($"{basePath}.physical")) - logFiles.Add($"{basePath}.physical"); - if (File.Exists($"{basePath}.0.physical")) - logFiles.Add($"{basePath}.0.physical"); - if (File.Exists($"{basePath}.1.physical")) - logFiles.Add($"{basePath}.1.physical"); - if (File.Exists($"{basePath}.2.physical")) - logFiles.Add($"{basePath}.2.physical"); - if (File.Exists($"{basePath}.skeleton")) - logFiles.Add($"{basePath}.skeleton"); - if (File.Exists($"{basePath}.state")) - logFiles.Add($"{basePath}.state"); - break; - } - - return logFiles; - } - /// public override bool IsDumpingCommand() { @@ -1473,1104 +896,5 @@ namespace MPF.Core.Modules.Redumper } #endregion - - #region Information Extraction Methods - - /// - /// Get the cuesheet from the input file, if possible - /// - /// Log file location - /// Newline-delimited cuesheet if possible, null on error - private static string? GetCuesheet(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - // Fast forward to the dat line - using var sr = File.OpenText(log); - while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("CUE [") == false) ; - if (sr.EndOfStream) - return null; - - // Now that we're at the relevant entries, read each line in and concatenate - string? cueString = string.Empty, line = sr.ReadLine()?.Trim(); - while (!string.IsNullOrEmpty(line)) - { - cueString += line + "\n"; - line = sr.ReadLine()?.Trim(); - } - - return cueString.TrimEnd('\n'); - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the datfile from the input file, if possible - /// - /// Log file location - /// Newline-delimited datfile if possible, null on error - private static string? GetDatfile(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - using var sr = File.OpenText(log); - string? datString = null; - - // Find all occurrences of the hash information - while (!sr.EndOfStream) - { - // Fast forward to the dat line - while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("dat:") == false) ; - if (sr.EndOfStream) - break; - - // Now that we're at the relevant entries, read each line in and concatenate - datString = string.Empty; - var line = sr.ReadLine()?.Trim(); - while (line?.StartsWith(" - /// Get reported disc type information, if possible - /// - /// Log file location - /// True if disc type info was set, false otherwise - private static bool GetDiscType(string log, out string? discTypeOrBookType) - { - // Set the default values - discTypeOrBookType = null; - - // If the file doesn't exist, we can't get the info - if (!File.Exists(log)) - return false; - - try - { - using var sr = File.OpenText(log); - var line = sr.ReadLine(); - while (line != null) - { - // Trim the line for later use - line = line.Trim(); - - // The profile is listed in a single line - if (line.StartsWith("current profile:")) - { - // current profile: - discTypeOrBookType = line.Substring("current profile: ".Length); - } - - line = sr.ReadLine(); - } - - return true; - } - catch - { - // We don't care what the exception is right now - discTypeOrBookType = null; - return false; - } - } - - /// - /// Get all Volume Identifiers - /// - /// Log file location - /// Volume labels (by type), or null if none present - private static bool GetVolumeLabels(string log, out Dictionary> volLabels) - { - // If the file doesn't exist, can't get the volume labels - volLabels = []; - if (!File.Exists(log)) - return false; - - try - { - using var sr = File.OpenText(log); - var line = sr.ReadLine(); - - while (line != null) - { - // Trim the line for later use - line = line.Trim(); - - // ISO9660 Volume Identifier - if (line.StartsWith("volume identifier: ")) - { - string label = line.Substring("volume identifier: ".Length); - - // Skip if label is blank - if (label == null || label.Length <= 0) - break; - - if (volLabels.ContainsKey(label)) - volLabels[label].Add("ISO"); - else - volLabels[label] = ["ISO"]; - - // Redumper log currently only outputs ISO9660 label, end here - break; - } - - line = sr.ReadLine(); - } - - // Return true if a volume label was found - return volLabels.Count > 0; - } - catch - { - // We don't care what the exception is right now - volLabels = []; - return false; - } - } - - /// - /// Get the DVD protection information, if possible - /// - /// Log file location - /// Indicates whether region and protection type are always included - /// Formatted string representing the DVD protection, null on error - private static string? GetDVDProtection(string log, bool includeAlways) - { - // If one of the files doesn't exist, we can't get info from them - if (!File.Exists(log)) - return null; - - // Setup all of the individual pieces - string? region = null, rceProtection = null, copyrightProtectionSystemType = null, vobKeys = null, decryptedDiscKey = null; - using (var sr = File.OpenText(log)) - { - try - { - // Fast forward to the copyright information - while (sr.ReadLine()?.Trim().StartsWith("copyright:") == false) ; - - // Now read until we hit the manufacturing information - var line = sr.ReadLine()?.Trim(); - while (line != null && !sr.EndOfStream) - { - if (line.StartsWith("protection system type")) - { - copyrightProtectionSystemType = line.Substring("protection system type: ".Length); - if (copyrightProtectionSystemType == "none" || copyrightProtectionSystemType == "") - copyrightProtectionSystemType = "No"; - } - else if (line.StartsWith("region management information:")) - { - region = line.Substring("region management information: ".Length); - } - else if (line.StartsWith("disc key")) - { - decryptedDiscKey = line.Substring("disc key: ".Length).Replace(':', ' '); - } - else if (line.StartsWith("title keys")) - { - vobKeys = string.Empty; - - line = sr.ReadLine()?.Trim(); - while (!string.IsNullOrEmpty(line)) - { - var match = Regex.Match(line, @"^(.*?): (.*?)$", RegexOptions.Compiled); - if (match.Success) - { - string normalizedKey = match.Groups[2].Value.Replace(':', ' '); - if (normalizedKey == "none" || normalizedKey == "") - normalizedKey = "No Title Key"; - else if (normalizedKey == "") - normalizedKey = "Error Retrieving Title Key"; - - vobKeys += $"{match.Groups[1].Value} Title Key: {match.Groups[2].Value.Replace(':', ' ')}\n"; - } - else - { - break; - } - - line = sr.ReadLine()?.Trim(); - } - } - else - { - break; - } - - line = sr.ReadLine()?.Trim(); - } - } - catch { } - } - - // Filter out if we're not always including information - if (!includeAlways) - { - if (region == "1 2 3 4 5 6 7 8") - region = null; - if (copyrightProtectionSystemType == "No") - copyrightProtectionSystemType = null; - } - - // Now we format everything we can - string protection = string.Empty; - 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(vobKeys)) - protection += vobKeys; - if (!string.IsNullOrEmpty(decryptedDiscKey)) - protection += $"Decrypted Disc Key: {decryptedDiscKey}\n"; - - return protection; - } - - /// - /// Get the detected error counts from the input files, if possible - /// - /// Log file location - /// True if error counts could be retrieved, false otherwise - public static bool GetErrorCount(string log, out long redumpErrors, out long c2Errors) - { - // Set the default values for error counts - redumpErrors = -1; c2Errors = -1; - - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return false; - - try - { - using var sr = File.OpenText(log); - - // Find the error counts - while (!sr.EndOfStream) - { - var line = sr.ReadLine()?.Trim(); - if (line == null) - break; - - // C2: - if (line.StartsWith("C2:")) - { - string[] parts = line.Split(' '); - if (!long.TryParse(parts[1], out c2Errors)) - c2Errors = -1; - } - - // REDUMP.ORG errors: - else if (line.StartsWith("REDUMP.ORG errors:")) - { - string[] parts = line!.Split(' '); - if (!long.TryParse(parts[2], out redumpErrors)) - redumpErrors = -1; - } - } - - // If the Redump error count is -1, then an issue occurred - return redumpErrors != -1; - } - catch - { - // We don't care what the exception is right now - return false; - } - } - - /// - /// Get hardware information from the input file, if possible - /// - /// Log file location - /// True if hardware info was set, false otherwise - private static bool GetHardwareInfo(string log, out string? manufacturer, out string? model, out string? firmware) - { - // Set the default values - manufacturer = null; model = null; firmware = null; - - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return false; - - try - { - // Fast forward to the drive information line - using var sr = File.OpenText(log); - while (!(sr.ReadLine()?.Trim().StartsWith("drive path:") ?? true)) ; - - // If we find the hardware info line, return each value - // drive: - (revision level: , vendor specific: ) - var regex = new Regex(@"drive: (.+) - (.+) \(revision level: (.+), vendor specific: (.+)\)", RegexOptions.Compiled); - - string? line; - while ((line = sr.ReadLine()) != null) - { - var match = regex.Match(line.Trim()); - if (match.Success) - { - manufacturer = match.Groups[1].Value; - model = match.Groups[2].Value; - firmware = match.Groups[3].Value; - firmware += match.Groups[4].Value == "" ? "" : $" ({match.Groups[4].Value})"; - return true; - } - } - - // We couldn't detect it then - return false; - } - catch - { - // We don't care what the exception is right now - return false; - } - } - - /// - /// Get the layerbreaks from the input file, if possible - /// - /// Log file location - /// True if any layerbreaks were found, false otherwise - private static bool GetLayerbreaks(string log, out string? layerbreak1, out string? layerbreak2, out string? layerbreak3) - { - // Set the default values - layerbreak1 = null; layerbreak2 = null; layerbreak3 = null; - - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return false; - - try - { - // Find the layerbreak - using var sr = File.OpenText(log); - while (!sr.EndOfStream) - { - var line = sr.ReadLine()?.Trim(); - - // If we have a null line, just break - if (line == null) - break; - - // Single-layer discs have no layerbreak - if (line.Contains("layers count: 1")) - { - return false; - } - - // Dual-layer discs have a regular layerbreak (old) - else if (line.StartsWith("data ")) - { - // data { LBA: .. , length: , hLBA: .. } - string[] split = line.Split(' ').Where(s => !string.IsNullOrEmpty(s)).ToArray(); - layerbreak1 ??= split[7].TrimEnd(','); - } - - // Dual-layer discs have a regular layerbreak (new) - else if (line.StartsWith("layer break:")) - { - // layer break: - layerbreak1 = line.Substring("layer break: ".Length).Trim(); - } - - // Multi-layer discs have the layer in the name - else if (line.StartsWith("layer break (layer: 0):")) - { - // layer break (layer: 0): - layerbreak1 = line.Substring("layer break (layer: 0): ".Length).Trim(); - } - else if (line.StartsWith("layer break (layer: 1):")) - { - // layer break (layer: 1): - layerbreak2 = line.Substring("layer break (layer: 1): ".Length).Trim(); - } - else if (line.StartsWith("layer break (layer: 2):")) - { - // layer break (layer: 2): - layerbreak3 = line.Substring("layer break (layer: 2): ".Length).Trim(); - } - } - - // Return the layerbreak, if possible - return true; - } - catch - { - // We don't care what the exception is right now - return false; - } - } - - /// - /// Get multisession information from the input file, if possible - /// - /// Log file location - /// Formatted multisession information, null on error - private static string? GetMultisessionInformation(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - // Fast forward to the multisession lines - using var sr = File.OpenText(log); - while (!sr.EndOfStream && sr.ReadLine()?.Trim()?.StartsWith("multisession:") == false) ; - if (sr.EndOfStream) - return null; - - // Now that we're at the relevant lines, find the session info - string? firstSession = null, secondSession = null; - while (!sr.EndOfStream) - { - var line = sr.ReadLine()?.Trim(); - - // If we have a null line, just break - if (line == null) - break; - - // Store the first session range - if (line.Contains("session 1:")) - firstSession = line.Substring("session 1: ".Length).Trim(); - - // Store the secomd session range - else if (line.Contains("session 2:")) - secondSession = line.Substring("session 2: ".Length).Trim(); - } - - // If either is blank, we don't have multisession - if (string.IsNullOrEmpty(firstSession) || string.IsNullOrEmpty(secondSession)) - return null; - - // Create and return the formatted output - return $"Session 1: {firstSession}\nSession 2: {secondSession}"; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the existence of an anti-modchip string from the input file, if possible - /// - /// Log file location - /// Anti-modchip existence if possible, false on error - private static bool? GetPlayStationAntiModchipDetected(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - // Check for the anti-modchip strings - using var sr = File.OpenText(log); - var line = sr.ReadLine()?.Trim(); - while (!sr.EndOfStream) - { - if (line == null) - return false; - - if (line.StartsWith("anti-modchip: no")) - return false; - else if (line.StartsWith("anti-modchip: yes")) - return true; - - line = sr.ReadLine()?.Trim(); - } - - return false; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the detected missing EDC count from the input files, if possible - /// - /// Log file location - /// Status of PS1 EDC, if possible - private static bool? GetPlayStationEDCStatus(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - // Check for the EDC strings - using var sr = File.OpenText(log); - var line = sr.ReadLine()?.Trim(); - while (!sr.EndOfStream) - { - if (line == null) - return false; - - if (line.Contains("EDC: no")) - return false; - else if (line.Contains("EDC: yes")) - return true; - - line = sr.ReadLine()?.Trim(); - } - - return false; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the info from a PlayStation disc, if possible - /// - /// Log file location - /// True if section found, null on error - private static bool GetPlayStationInfo(string log, out string? exeDate, out string? serial, out string? version, out string? firmware) - { - // Set the default values - exeDate = null; serial = null; version = null; firmware = null; - - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return false; - - try - { - // Fast forward to the PS info line - using var sr = File.OpenText(log); - string? line; - while (!sr.EndOfStream) - { - line = sr.ReadLine()?.TrimStart(); - if (line?.StartsWith("PSX [") == true || - line?.StartsWith("PS2 [") == true || - line?.StartsWith("PS3 [") == true || - line?.StartsWith("PS4 [") == true || - line?.StartsWith("PS5 [") == true) - break; - } - if (sr.EndOfStream) - return false; - - while (!sr.EndOfStream) - { - line = sr.ReadLine()?.TrimStart(); - if (line == null) - break; - - if (line.StartsWith("EXE date:")) - { - exeDate = line.Substring("EXE date: ".Length).Trim(); - } - else if (line.StartsWith("serial:")) - { - serial = line.Substring("serial: ".Length).Trim(); - } - else if (line.StartsWith("version:")) - { - version = line.Substring("version: ".Length).Trim(); - } - else if (line.StartsWith("firmware:")) - { - firmware = line.Substring("firmware: ".Length).Trim(); - } - else - { - break; - } - } - - return true; - } - catch - { - // We don't care what the exception is right now - return false; - } - } - - /// - /// Get the LibCrypt data from the input file, if possible - /// - /// Log file location - /// PS1 LibCrypt data, if possible - private static string? GetPlayStationLibCryptData(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - // Fast forward to the LibCrypt line - using var sr = File.OpenText(log); - while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("libcrypt:") == false) ; - if (sr.EndOfStream) - return null; - - // Now that we're at the relevant entries, read each line in and concatenate - string? libCryptString = "", line = sr.ReadLine()?.Trim(); - while (line?.StartsWith("MSF:") == true) - { - libCryptString += line + "\n"; - line = sr.ReadLine()?.Trim(); - } - - return libCryptString.TrimEnd('\n'); - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the existence of LibCrypt from the input file, if possible - /// - /// Log file location - /// Status of PS1 LibCrypt, if possible - private static bool? GetPlayStationLibCryptStatus(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - // Check for the libcrypt strings - using var sr = File.OpenText(log); - var line = sr.ReadLine()?.Trim(); - while (!sr.EndOfStream) - { - if (line == null) - return false; - - if (line.StartsWith("libcrypt: no")) - return false; - else if (line.StartsWith("libcrypt: yes")) - return true; - - line = sr.ReadLine()?.Trim(); - } - - return false; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the PVD from the input file, if possible - /// - /// Log file location - /// Newline-delimited PVD if possible, null on error - private static string? GetPVD(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - // Fast forward to the PVD line - using var sr = File.OpenText(log); - while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("PVD:") == false) ; - if (sr.EndOfStream) - return null; - - // Now that we're at the relevant entries, read each line in and concatenate - string? pvdString = "", line = sr.ReadLine(); - while (line?.StartsWith("03") == true) - { - pvdString += line + "\n"; - line = sr.ReadLine(); - } - - return pvdString.TrimEnd('\n'); - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the non-zero data start from the input file, if possible - /// - /// Log file location - /// Non-zero dta start if possible, null on error - private static string? GetRingNonZeroDataStart(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - // If we find the sample range, return the start value only - using var sr = File.OpenText(log); - while (!sr.EndOfStream) - { - string? line = sr.ReadLine()?.TrimStart(); - if (line?.StartsWith("non-zero data sample range") == true) - return line.Substring("non-zero data sample range: [".Length).Trim().Split(' ')[0]; - } - - // We couldn't detect it then - return null; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the build info from a Saturn disc, if possible - /// - /// <String representing a formatter variant of the Saturn header - /// True on successful extraction of info, false otherwise - /// TODO: Remove when Redumper gets native reading support - private static bool GetSaturnBuildInfo(string? segaHeader, 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.IsNullOrEmpty(segaHeader)) - return false; - - // Now read it in cutting it into lines for easier parsing - try - { - string[] header = segaHeader!.Split('\n'); - string serialVersionLine = header[2].Substring(58); - string dateLine = header[3].Substring(58); - serial = serialVersionLine.Substring(0, 10).Trim(); - version = serialVersionLine.Substring(10, 6).TrimStart('V', 'v'); - date = dateLine.Substring(0, 8); - date = $"{date[0]}{date[1]}{date[2]}{date[3]}-{date[4]}{date[5]}-{date[6]}{date[7]}"; - return true; - } - catch - { - // We don't care what the error is - return false; - } - } - - /// - /// Get the header from a Saturn, if possible - /// - /// Log file location - /// Header as a byte array if possible, null on error - private static string? GetSaturnHeader(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - // Fast forward to the SS line - using var sr = File.OpenText(log); - while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("SS [") == false) ; - if (sr.EndOfStream) - return null; - - string? line, headerString = ""; - while (!sr.EndOfStream) - { - line = sr.ReadLine()?.TrimStart(); - if (line?.StartsWith("header:") == true) - { - line = sr.ReadLine()?.TrimStart(); - while (line?.StartsWith("00") == true) - { - headerString += line + "\n"; - line = sr.ReadLine()?.Trim(); - } - } - else - { - break; - } - } - - return headerString.TrimEnd('\n'); - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the header from a Saturn, if possible - /// - /// Log file location - /// Header as a byte array if possible, null on error - private static string? GetSecuROMData(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - // Fast forward to the SecuROM line - using var sr = File.OpenText(log); - while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("SecuROM [") == false) ; - if (sr.EndOfStream) - return null; - - var lines = new List(); - while (!sr.EndOfStream) - { - var line = sr.ReadLine()?.TrimStart(); - - // Skip the "version"/"scheme" line - if (line?.StartsWith("version:") == true || line?.StartsWith("scheme:") == true) - continue; - - // Only read until while there are MSF lines - if (line?.StartsWith("MSF:") != true) - break; - - lines.Add(line); - } - - return string.Join("\n", [.. lines]).TrimEnd('\n'); - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the header from a Sega CD / Mega CD, if possible - /// - /// Log file location - /// Header as a byte array if possible, null on error - private static string? GetSegaCDHeader(string log, out string? buildDate, out string? serial, out string? region) - { - // Set the default values - buildDate = null; serial = null; region = null; - - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - // Fast forward to the MCD line - using var sr = File.OpenText(log); - while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("MCD [") == false) ; - if (sr.EndOfStream) - return null; - - string? line, headerString = string.Empty; - while (!sr.EndOfStream) - { - line = sr.ReadLine()?.TrimStart(); - if (line == null) - break; - - if (line.StartsWith("build date:")) - { - buildDate = line.Substring("build date: ".Length).Trim(); - } - else if (line.StartsWith("serial:")) - { - serial = line.Substring("serial: ".Length).Trim(); - } - else if (line.StartsWith("region:")) - { - region = line.Substring("region: ".Length).Trim(); - } - else if (line.StartsWith("regions:")) - { - region = line.Substring("regions: ".Length).Trim(); - } - else if (line.StartsWith("header:")) - { - line = sr.ReadLine()?.TrimStart(); - while (line?.StartsWith("01") == true) - { - headerString += line + "\n"; - line = sr.ReadLine()?.Trim(); - } - } - else - { - break; - } - } - - return headerString.TrimEnd('\n'); - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the universal hash from the input file, if possible - /// - /// Log file location - /// Universal hash if possible, null on error - private static string? GetUniversalHash(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - // If we find the universal hash line, return the hash only - using var sr = File.OpenText(log); - while (!sr.EndOfStream) - { - string? line = sr.ReadLine()?.TrimStart(); - if (line?.StartsWith("Universal Hash") == true) - return line.Substring("Universal Hash (SHA-1): ".Length).Trim(); - } - - // We couldn't detect it then - return null; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the write offset from the input file, if possible - /// - /// Log file location - /// Sample write offset if possible, null on error - private static string? GetWriteOffset(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - try - { - // If we find the disc write offset line, return the offset - using var sr = File.OpenText(log); - while (!sr.EndOfStream) - { - string? line = sr.ReadLine()?.TrimStart(); - if (line?.StartsWith("disc write offset") == true) - return line.Substring("disc write offset: ".Length).Trim(); - } - - // We couldn't detect it then - return null; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the version. if possible - /// - /// Log file location - /// Version if possible, null on error - private static string? GetVersion(string log) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(log)) - return null; - - // Samples: - // redumper v2022.10.28 [Oct 28 2022, 05:41:43] (print usage: --help,-h) - // redumper v2022.12.22 build_87 [Dec 22 2022, 01:56:26] - - try - { - // Skip first line (dump date) - using var sr = File.OpenText(log); - sr.ReadLine(); - - // Get the next non-warning line - string nextLine = sr.ReadLine()?.Trim() ?? string.Empty; - if (nextLine.StartsWith("warning:", StringComparison.OrdinalIgnoreCase)) - nextLine = sr.ReadLine()?.Trim() ?? string.Empty; - - // Generate regex - // Permissive - var regex = new Regex(@"^redumper (v.+) \[.+\]", RegexOptions.Compiled); - // Strict - //var regex = new Regex(@"^redumper (v\d{4}\.\d{2}\.\d{2}(| build_\d+)) \[.+\]", RegexOptions.Compiled); - - // Extract the version string - var match = regex.Match(nextLine); - var version = match.Groups[1].Value; - return string.IsNullOrEmpty(version) ? null : version; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - #endregion } } diff --git a/MPF.Core/Modules/UmdImageCreator/Parameters.cs b/MPF.Core/Modules/UmdImageCreator/Parameters.cs index 79294d11..62e50c60 100644 --- a/MPF.Core/Modules/UmdImageCreator/Parameters.cs +++ b/MPF.Core/Modules/UmdImageCreator/Parameters.cs @@ -1,11 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using MPF.Core.Converters; -using MPF.Core.Data; -using SabreTools.Hashing; -using SabreTools.RedumpLib; +using MPF.Core.Data; using SabreTools.RedumpLib.Data; namespace MPF.Core.Modules.UmdImageCreator @@ -30,306 +23,5 @@ namespace MPF.Core.Modules.UmdImageCreator : base(system, type, drivePath, filename, driveSpeed, options) { } - - #region BaseParameters Implementations - - /// - public override (bool, List) CheckAllOutputFilesExist(string basePath, bool preCheck) - { - var missingFiles = new List(); - switch (this.Type) - { - case MediaType.UMD: - if (!File.Exists($"{basePath}_logs.zip") || !preCheck) - { - if (!File.Exists($"{basePath}_disc.txt")) - missingFiles.Add($"{basePath}_disc.txt"); - if (!File.Exists($"{basePath}_mainError.txt")) - missingFiles.Add($"{basePath}_mainError.txt"); - if (!File.Exists($"{basePath}_mainInfo.txt")) - missingFiles.Add($"{basePath}_mainInfo.txt"); - if (!File.Exists($"{basePath}_volDesc.txt")) - missingFiles.Add($"{basePath}_volDesc.txt"); - } - - break; - - default: - missingFiles.Add("Media and system combination not supported for UmdImageCreator"); - break; - } - - return (!missingFiles.Any(), missingFiles); - } - - /// - public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts) - { - // Ensure that required sections exist - info = Builder.EnsureAllSections(info); - - // TODO: Determine if there's a UMDImageCreator version anywhere - info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram); - info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "_disc.txt")?.ToString("yyyy-MM-dd HH:mm:ss"); - - // Fill in the volume labels - if (GetVolumeLabels($"{basePath}_volDesc.txt", out var volLabels)) - VolumeLabels = volLabels; - - // Extract info based generically on MediaType - switch (this.Type) - { - case MediaType.UMD: - info.Extras!.PVD = GetPVD(basePath + "_mainInfo.txt") ?? string.Empty; - - if (HashTool.GetStandardHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1)) - { - // Get the Datafile information - var datafile = new Datafile - { - Games = [new Game { Roms = [new Rom { Name = string.Empty, Size = filesize.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1, }] }] - }; - - // Fill in the hash data - info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile); - - info.SizeAndChecksums!.Size = filesize; - info.SizeAndChecksums.CRC32 = crc32; - info.SizeAndChecksums.MD5 = md5; - info.SizeAndChecksums.SHA1 = sha1; - } - - if (GetUMDAuxInfo(basePath + "_disc.txt", out var title, out DiscCategory? umdcat, out var umdversion, out var umdlayer, out long umdsize)) - { - info.CommonDiscInfo!.Title = title ?? string.Empty; - info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games; - info.VersionAndEditions!.Version = umdversion ?? string.Empty; - info.SizeAndChecksums!.Size = umdsize; - - if (!string.IsNullOrEmpty(umdlayer)) - info.SizeAndChecksums.Layerbreak = Int64.Parse(umdlayer ?? "-1"); - } - - break; - } - - // Fill in any artifacts that exist, Base64-encoded, if we need to - if (includeArtifacts) - { - info.Artifacts ??= []; - - if (File.Exists($"{basePath}_disc.txt")) - info.Artifacts["disc"] = GetBase64(GetFullFile($"{basePath}_disc.txt")) ?? string.Empty; - if (File.Exists($"{basePath}_drive.txt")) - info.Artifacts["drive"] = GetBase64(GetFullFile($"{basePath}_drive.txt")) ?? string.Empty; - if (File.Exists($"{basePath}_mainError.txt")) - info.Artifacts["mainError"] = GetBase64(GetFullFile($"{basePath}_mainError.txt")) ?? string.Empty; - if (File.Exists($"{basePath}_mainInfo.txt")) - info.Artifacts["mainInfo"] = GetBase64(GetFullFile($"{basePath}_mainInfo.txt")) ?? string.Empty; - //if (File.Exists($"{basePath}_PFI.bin")) - // info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PFI.bin")) ?? string.Empty; - if (File.Exists($"{basePath}_volDesc.txt")) - info.Artifacts["volDesc"] = GetBase64(GetFullFile($"{basePath}_volDesc.txt")) ?? string.Empty; - } - } - - /// - public override List GetLogFilePaths(string basePath) - { - var logFiles = new List(); - switch (this.Type) - { - case MediaType.UMD: - if (File.Exists($"{basePath}_disc.txt")) - logFiles.Add($"{basePath}_disc.txt"); - if (File.Exists($"{basePath}_drive.txt")) - logFiles.Add($"{basePath}_drive.txt"); - if (File.Exists($"{basePath}_mainError.txt")) - logFiles.Add($"{basePath}_mainError.txt"); - if (File.Exists($"{basePath}_mainInfo.txt")) - logFiles.Add($"{basePath}_mainInfo.txt"); - if (File.Exists($"{basePath}_volDesc.txt")) - logFiles.Add($"{basePath}_volDesc.txt"); - - if (File.Exists($"{basePath}_PFI.bin")) - logFiles.Add($"{basePath}_PFI.bin"); - - break; - } - - return logFiles; - } - - #endregion - - #region Information Extraction Methods - - /// - /// Get the PVD from the input file, if possible - /// - /// _mainInfo.txt file location - /// Newline-deliminated PVD if possible, null on error - private static string? GetPVD(string mainInfo) - { - // If the file doesn't exist, we can't get info from it - if (!File.Exists(mainInfo)) - return null; - - try - { - // Make sure we're in the right sector - using var sr = File.OpenText(mainInfo); - while (sr.ReadLine()?.StartsWith("========== LBA[000016, 0x0000010]: Main Channel ==========") == false) ; - - // Fast forward to the PVD - while (sr.ReadLine()?.StartsWith("0310") == false) ; - - // 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; - } - } - - /// - /// Get the UMD auxiliary info from the outputted files, if possible - /// - /// _disc.txt file location - /// True on successful extraction of info, false otherwise - private static bool GetUMDAuxInfo(string disc, out string? title, out DiscCategory? umdcat, out string? umdversion, out string? umdlayer, out long umdsize) - { - title = null; umdcat = null; umdversion = null; umdlayer = null; umdsize = -1; - - // If the file doesn't exist, we can't get info from it - if (!File.Exists(disc)) - return false; - - try - { - // Loop through everything to get the first instance of each required field - using var sr = File.OpenText(disc); - while (!sr.EndOfStream) - { - string? line = sr.ReadLine()?.Trim(); - if (line == null) - break; - - if (line.StartsWith("TITLE") && title == null) - title = line.Substring("TITLE: ".Length); - else if (line.StartsWith("DISC_VERSION") && umdversion == null) - umdversion = line.Split(' ')[1]; - else if (line.StartsWith("pspUmdTypes")) - umdcat = InfoTool.GetUMDCategory(line.Split(' ')[1]); - else if (line.StartsWith("L0 length")) - umdlayer = line.Split(' ')[2]; - else if (line.StartsWith("FileSize:")) - umdsize = Int64.Parse(line.Split(' ')[1]); - } - - // If the L0 length is the size of the full disc, there's no layerbreak - if (Int64.TryParse(umdlayer, out long umdlayerValue) && umdlayerValue * 2048 == umdsize) - umdlayer = null; - - return true; - } - catch - { - // We don't care what the exception is right now - return false; - } - } - - /// - /// Get all Volume Identifiers - /// - /// _volDesc.txt file location - /// Volume labels (by type), or null if none present - /// This is a copy of the code from DiscImageCreator and has extrandous checks - private static bool GetVolumeLabels(string volDesc, out Dictionary> volLabels) - { - // If the file doesn't exist, can't get the volume labels - volLabels = []; - if (!File.Exists(volDesc)) - return false; - - try - { - using var sr = File.OpenText(volDesc); - var line = sr.ReadLine(); - - string volType = "UNKNOWN"; - string label; - while (line != null) - { - // Trim the line for later use - line = line.Trim(); - - // ISO9660 and extensions section - if (line.StartsWith("Volume Descriptor Type: ")) - { - Int32.TryParse(line.Substring("Volume Descriptor Type: ".Length), out int volTypeInt); - volType = volTypeInt switch - { - // 0 => "Boot Record" // Should not not contain a Volume Identifier - 1 => "ISO", // ISO9660 - 2 => "Joliet", - // 3 => "Volume Partition Descriptor" // Should not not contain a Volume Identifier - // 255 => "???" // Should not not contain a Volume Identifier - _ => "UNKNOWN" // Should not contain a Volume Identifier - }; - } - // UDF section - else if (line.StartsWith("Primary Volume Descriptor Number:")) - { - volType = "UDF"; - } - // Identifier - else if (line.StartsWith("Volume Identifier: ")) - { - label = line.Substring("Volume Identifier: ".Length); - - // Remove leading non-printable character (unsure why DIC outputs this) - if (Convert.ToUInt32(label[0]) == 0x7F || Convert.ToUInt32(label[0]) < 0x20) - label = label.Substring(1); - - // Skip if label is blank - if (label == null || label.Length <= 0) - { - volType = "UNKNOWN"; - line = sr.ReadLine(); - continue; - } - - if (volLabels.ContainsKey(label)) - volLabels[label].Add(volType); - else - volLabels.Add(label, [volType]); - - // Reset volume type - volType = "UNKNOWN"; - } - - line = sr.ReadLine(); - } - - // Return true if a volume label was found - return volLabels.Count > 0; - } - catch - { - // We don't care what the exception is right now - volLabels = []; - return false; - } - } - - #endregion } } diff --git a/MPF.Core/Modules/XboxBackupCreator/Parameters.cs b/MPF.Core/Modules/XboxBackupCreator/Parameters.cs index 27d9aa8f..453aa5c7 100644 --- a/MPF.Core/Modules/XboxBackupCreator/Parameters.cs +++ b/MPF.Core/Modules/XboxBackupCreator/Parameters.cs @@ -1,11 +1,4 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using MPF.Core.Converters; -using MPF.Core.Data; -using MPF.Core.Utilities; -using SabreTools.Hashing; -using SabreTools.RedumpLib; +using MPF.Core.Data; using SabreTools.RedumpLib.Data; namespace MPF.Core.Modules.XboxBackupCreator @@ -30,648 +23,5 @@ namespace MPF.Core.Modules.XboxBackupCreator : base(system, type, drivePath, filename, driveSpeed, options) { } - - #region BaseParameters Implementations - - /// - public override (bool, List) CheckAllOutputFilesExist(string basePath, bool preCheck) - { - var missingFiles = new List(); - switch (this.Type) - { - case MediaType.DVD: - if (!File.Exists($"{basePath}_logs.zip") || !preCheck) - { - string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar; - string? logPath = GetLogName(baseDir); - if (string.IsNullOrEmpty(logPath)) - missingFiles.Add($"{baseDir}Log.txt"); - if (!File.Exists($"{baseDir}DMI.bin")) - missingFiles.Add($"{baseDir}DMI.bin"); - if (!File.Exists($"{baseDir}PFI.bin")) - missingFiles.Add($"{baseDir}PFI.bin"); - if (!File.Exists($"{baseDir}SS.bin")) - missingFiles.Add($"{baseDir}SS.bin"); - - // Not required from XBC - //if (!File.Exists($"{basePath}.dvd")) - // missingFiles.Add($"{basePath}.dvd"); - } - - break; - - default: - missingFiles.Add("Media and system combination not supported for XboxBackupCreator"); - break; - } - - return (!missingFiles.Any(), missingFiles); - } - - /// - public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts) - { - // Ensure that required sections exist - info = Builder.EnsureAllSections(info); - - // Get base directory - string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar; - - // Get log filename - string? logPath = GetLogName(baseDir); - if (string.IsNullOrEmpty(logPath)) - return; - - // XBC dump info - info.DumpingInfo!.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {GetVersion(logPath) ?? "Unknown Version"}"; - info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(logPath)?.ToString("yyyy-MM-dd HH:mm:ss"); - info.DumpingInfo.Model = GetDrive(logPath) ?? "Unknown Drive"; - - // Look for read errors - if (GetReadErrors(logPath, out long readErrors)) - info.CommonDiscInfo!.ErrorsCount = readErrors == -1 ? "Error retrieving error count" : readErrors.ToString(); - - // Extract info based generically on MediaType - switch (this.Type) - { - case MediaType.DVD: - - // Get Layerbreak from .dvd file if possible - if (GetLayerbreak($"{basePath}.dvd", out long layerbreak)) - info.SizeAndChecksums!.Layerbreak = layerbreak; - - // Hash data - if (HashTool.GetStandardHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1)) - { - // Get the Datafile information - var datafile = new Datafile - { - Games = [new Game { Roms = [new Rom { Name = string.Empty, Size = filesize.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1, }] }] - }; - - // Fill in the hash data - info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile); - - info.SizeAndChecksums!.Size = filesize; - info.SizeAndChecksums.CRC32 = crc32; - info.SizeAndChecksums.MD5 = md5; - info.SizeAndChecksums.SHA1 = sha1; - } - - switch (this.System) - { - case RedumpSystem.MicrosoftXbox: - - // Parse DMI.bin - string xmidString = Tools.GetXGD1XMID($"{baseDir}DMI.bin"); - var xmid = SabreTools.Serialization.Wrappers.XMID.Create(xmidString); - if (xmid != null) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XMID] = xmidString?.TrimEnd('\0') ?? string.Empty; - info.CommonDiscInfo.Serial = xmid.Serial ?? string.Empty; - if (!options.EnableRedumpCompatibility) - info.VersionAndEditions!.Version = xmid.Version ?? string.Empty; - - info.CommonDiscInfo.Region = InfoTool.GetXGDRegion(xmid.Model.RegionIdentifier); - } - - break; - - case RedumpSystem.MicrosoftXbox360: - - // Get PVD from ISO - if (GetPVD(basePath + ".iso", out string? pvd)) - info.Extras!.PVD = pvd; - - // Parse Media ID - //string? mediaID = GetMediaID(logPath); - - // Parse DMI.bin - string xemidString = Tools.GetXGD23XeMID($"{baseDir}DMI.bin"); - var xemid = SabreTools.Serialization.Wrappers.XeMID.Create(xemidString); - if (xemid != null) - { - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XeMID] = xemidString?.TrimEnd('\0') ?? string.Empty; - info.CommonDiscInfo.Serial = xemid.Serial ?? string.Empty; - if (!options.EnableRedumpCompatibility) - info.VersionAndEditions!.Version = xemid.Version ?? string.Empty; - - info.CommonDiscInfo.Region = InfoTool.GetXGDRegion(xemid.Model.RegionIdentifier); - } - - break; - } - - // Deal with SS.bin - if (File.Exists($"{baseDir}SS.bin")) - { - // Save security sector ranges - string? ranges = Tools.GetSSRanges($"{baseDir}SS.bin"); - if (!string.IsNullOrEmpty(ranges)) - info.Extras!.SecuritySectorRanges = ranges; - - // TODO: Determine SS version? - //info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.SSVersion] = - - // Recreate RawSS.bin - RecreateSS(logPath!, $"{baseDir}SS.bin", $"{baseDir}RawSS.bin"); - - // Run ss_sector_range to get repeatable SS hash - Tools.CleanSS($"{baseDir}SS.bin", $"{baseDir}SS.bin"); - } - - // DMI/PFI/SS CRC32 hashes - if (File.Exists($"{baseDir}DMI.bin")) - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = HashTool.GetFileHash($"{baseDir}DMI.bin", HashType.CRC32)?.ToUpperInvariant() ?? string.Empty; - if (File.Exists($"{baseDir}PFI.bin")) - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.PFIHash] = HashTool.GetFileHash($"{baseDir}PFI.bin", HashType.CRC32)?.ToUpperInvariant() ?? string.Empty; - if (File.Exists($"{baseDir}SS.bin")) - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.SSHash] = HashTool.GetFileHash($"{baseDir}SS.bin", HashType.CRC32)?.ToUpperInvariant() ?? string.Empty; - - break; - } - - // Fill in any artifacts that exist, Base64-encoded, if we need to - if (includeArtifacts) - { - info.Artifacts ??= []; - - if (File.Exists(logPath)) - info.Artifacts["log"] = GetBase64(GetFullFile(logPath!)) ?? string.Empty; - if (File.Exists($"{basePath}.dvd")) - info.Artifacts["dvd"] = GetBase64(GetFullFile($"{basePath}.dvd")) ?? string.Empty; - //if (File.Exists($"{baseDir}DMI.bin")) - // info.Artifacts["dmi"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}DMI.bin")) ?? string.Empty; - // TODO: Include PFI artifact only if the hash doesn't match known PFI hashes - //if (File.Exists($"{baseDir}PFI.bin")) - // info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}PFI.bin")) ?? string.Empty; - //if (File.Exists($"{baseDir}SS.bin")) - // info.Artifacts["ss"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}SS.bin")) ?? string.Empty; - //if (File.Exists($"{baseDir}RawSS.bin")) - // info.Artifacts["rawss"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}RawSS.bin")) ?? string.Empty; - } - } - - /// - public override List GetLogFilePaths(string basePath) - { - var logFiles = new List(); - string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar; - switch (this.Type) - { - case MediaType.DVD: - string? logPath = GetLogName(baseDir); - if (!string.IsNullOrEmpty(logPath)) - logFiles.Add(logPath!); - if (File.Exists($"{basePath}.dvd")) - logFiles.Add($"{basePath}.dvd"); - if (File.Exists($"{baseDir}DMI.bin")) - logFiles.Add($"{baseDir}DMI.bin"); - if (File.Exists($"{baseDir}PFI.bin")) - logFiles.Add($"{baseDir}PFI.bin"); - if (File.Exists($"{baseDir}SS.bin")) - logFiles.Add($"{baseDir}SS.bin"); - if (File.Exists($"{baseDir}RawSS.bin")) - logFiles.Add($"{baseDir}RawSS.bin"); - - break; - } - - return logFiles; - } - - #endregion - - #region Information Extraction Methods - - /// - /// Determines the file path of the XBC log - /// - /// Base directory to search in - /// Log path if found, null otherwise - private static string? GetLogName(string baseDir) - { - if (IsSuccessfulLog($"{baseDir}Log.txt")) - return $"{baseDir}Log.txt"; - - // Search for a renamed log file (assume there is only one) - string[] files = Directory.GetFiles(baseDir, "*.txt", SearchOption.TopDirectoryOnly); - foreach (string file in files) - { - if (IsSuccessfulLog(file)) - return file; - } - - return null; - } - - /// - /// Checks if Log file has a successful read in it - /// - /// Path to log file - /// True if successful log found, false otherwise - private static bool IsSuccessfulLog(string log) - { - if (!File.Exists(log)) - return false; - - // Successful Example: - // Read completed in 00:50:23 - // Failed Example: - // Read failed - - try - { - // If Version is not found, not a valid log file - if (string.IsNullOrEmpty(GetVersion(log))) - return false; - - // Look for " Read completed in " in log file - using var sr = File.OpenText(log); - while (!sr.EndOfStream) - { - string? line = sr.ReadLine(); - if (line?.StartsWith(" Read completed in ") == true) - { - return true; - } - } - - // We couldn't find a successful dump - return false; - } - catch - { - // We don't care what the exception is right now - return false; - } - } - - /// - /// Get the XBC version if possible - /// - /// Path to XBC log file - /// Version if possible, null on error - private static string? GetVersion(string? log) - { - if (string.IsNullOrEmpty(log) || !File.Exists(log)) - return null; - - // Sample: - // ==================================================================== - // Xbox Backup Creator v2.9 Build:0425 By Redline99 - // - - try - { - // Assume version is appended after first mention of Xbox Backup Creator - using var sr = File.OpenText(log); - while (!sr.EndOfStream) - { - string? line = sr.ReadLine()?.Trim(); - if (line?.StartsWith("Xbox Backup Creator ") == true) - return line.Substring("Xbox Backup Creator ".Length).Trim(); - } - - // We couldn't detect the version - return null; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the drive model from the log - /// - /// Path to XBC log file - /// Drive model if found, null otherwise - private static string? GetDrive(string? log) - { - if (string.IsNullOrEmpty(log) || !File.Exists(log)) - return null; - - // Example: - // ======================================== - // < --Security Sector Details --> - // Source Drive: SH-D162D - // ---------------------------------------- - - try - { - // Parse drive model from log file - using var sr = File.OpenText(log); - while (!sr.EndOfStream) - { - string? line = sr.ReadLine()?.Trim(); - if (line?.StartsWith("Source Drive: ") == true) - { - return line.Substring("Source Drive: ".Length).Trim(); - } - } - - // We couldn't detect the drive model - return null; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Get the Layerbreak value if possible - /// - /// Path to layerbreak file - /// Layerbreak value if found - /// True if successful, otherwise false - /// - private static bool GetLayerbreak(string? dvd, out long layerbreak) - { - layerbreak = 0; - - if (string.IsNullOrEmpty(dvd) || !File.Exists(dvd)) - return false; - - // Example: - // LayerBreak=1913776 - // track.iso - - try - { - // Parse Layerbreak value from DVD file - using var sr = File.OpenText(dvd); - while (!sr.EndOfStream) - { - string? line = sr.ReadLine()?.Trim(); - if (line?.StartsWith("LayerBreak=") == true) - { - return long.TryParse(line.Substring("LayerBreak=".Length).Trim(), out layerbreak); - } - } - - // We couldn't detect the Layerbreak - return false; - } - catch - { - // We don't care what the exception is right now - return false; - } - } - - /// - /// Get the read error count if possible - /// - /// Path to XBC log file - /// Read error count if found, -1 otherwise - /// True if sucessful, otherwise false - private bool GetReadErrors(string? log, out long readErrors) - { - readErrors = -1; - - if (string.IsNullOrEmpty(log) || !File.Exists(log)) - return false; - - // TODO: Logic when more than one dump is in the logs - - // Example: (replace [E] with drive letter) - // Creating SplitVid backup image [E] - // ... - // Reading Game Partition - // Setting read speed to 1x - // Unrecovered read error at Partition LBA: 0 - - // Example: (replace track with base filename) - // Creating Layer Break File - // LayerBreak file saved as: "track.dvd" - // A total of 1 sectors were zeroed out. - - // Example: (for Original Xbox) - // A total of 65,536 sectors were zeroed out. - // A total of 31 sectors with read errors were recovered. - - try - { - // Parse Layerbreak value from DVD file - using var sr = File.OpenText(log); - while (!sr.EndOfStream) - { - string? line = sr.ReadLine()?.Trim(); - if (line?.StartsWith("Creating Layer Break File") == true) - { - // Read error count is two lines below - line = sr.ReadLine()?.Trim(); - line = sr.ReadLine()?.Trim(); - if (line?.StartsWith("A total of ") == true && line?.EndsWith(" sectors were zeroed out.") == true) - { - string? errorCount = line.Substring("A total of ".Length, line.Length - 36).Replace(",", "").Trim(); - bool success = long.TryParse(errorCount, out readErrors); - - // Original Xbox should have 65536 read errors when dumping with XBC - if (this.System == RedumpSystem.MicrosoftXbox) - { - if (readErrors == 65536) - readErrors = 0; - else if (readErrors > 65536) - readErrors -= 65536; - } - - return success; - } - } - } - - // We couldn't detect the read error count - return false; - } - catch - { - // We don't care what the exception is right now - return false; - } - } - - /// - /// Get Xbox360 Media ID from XBC log file - /// - /// Path to XBC log file - /// Media ID if Log successfully parsed, null otherwise - private string? GetMediaID(string? log) - { - if (string.IsNullOrEmpty(log) || !File.Exists(log)) - return null; - - if (this.System == RedumpSystem.MicrosoftXbox) - return null; - - // Example: - // ---------------------------------------- - // Media ID - // A76B9983D170EFF8749A892BC-8B62A812 - // ---------------------------------------- - - try - { - // Parse Layerbreak value from DVD file - using var sr = File.OpenText(log); - while (!sr.EndOfStream) - { - string? line = sr.ReadLine()?.Trim(); - if (line?.StartsWith("Media ID") == true) - { - line = sr.ReadLine()?.Trim(); - return line?.Substring(25).Trim(); - } - } - - // We couldn't detect the Layerbreak - return null; - } - catch - { - // We don't care what the exception is right now - return null; - } - } - - /// - /// Recreate an SS.bin file from XBC log and write it to a file - /// - /// Path to XBC log - /// Path to the clean SS file to read from - /// Path to the raw SS file to write to - /// True if successful, false otherwise - private static bool RecreateSS(string log, string cleanSS, string rawSS) - { - if (!File.Exists(log) || !File.Exists(cleanSS)) - return false; - - byte[] ss = File.ReadAllBytes(cleanSS); - if (ss.Length != 2048) - return false; - - if (!RecreateSS(log!, ss)) - return false; - - File.WriteAllBytes(rawSS, ss); - return true; - } - - /// - /// Recreate an SS.bin byte array from an XBC log. - /// With help from https://github.com/hadzz/SS-Angle-Fixer/ - /// - /// Path to XBC log - /// Byte array of SS sector - /// True if successful, false otherwise - private static bool RecreateSS(string log, byte[] ss) - { - // Log file must exist - if (!File.Exists(log)) - return false; - - // SS must be complete sector - if (ss.Length != 2048) - return false; - - // Ignore XGD1 discs - if (!Tools.GetXGDType(ss, out int xgdType)) - return false; - if (xgdType == 0) - return false; - - // Don't recreate an already raw SS - // (but do save to file, so return true) - if (!Tools.IsCleanSS(ss)) - return true; - - // Example replay table: - /* - ---------------------------------------- - RT CID MOD DATA Drive Response - -- -- -- ------------- ------------------- - 01 14 00 033100 0340FF B7D8C32A B703590100 - 03 BE 00 244530 24552F F4B9B528 BE46360500 - 01 97 00 DBBAD0 DBCACF DD7787F4 484977ED00 - 03 45 00 FCAF00 FCBEFF FB7A7773 AAB662FC00 - 05 6B 00 033100 033E7F 0A31252A 0200000200 - 07 46 00 244530 2452AF F8E77EBC 5B00005B00 - 05 36 00 DBBAD0 DBC84F F5DFA735 B50000B500 - 07 A1 00 FCAF00 FCBC7F 6B749DBF 0E01000E01 - E0 50 00 42F4E1 00B6F7 00000000 0000000000 - -------------------------------------------- - */ - - try - { - // Parse Replay Table from log - using var sr = File.OpenText(log); - while (!sr.EndOfStream) - { - string? line = sr.ReadLine()?.Trim(); - if (line?.StartsWith("RT CID MOD DATA Drive Response") == true) - { - // Ignore next line - line = sr.ReadLine()?.Trim(); - if (sr.EndOfStream) - return false; - - byte[][] responses = new byte[4][]; - - // Parse the nine rows from replay table - for (int i = 0; i < 9; i++) - { - line = sr.ReadLine()?.Trim(); - // Validate line - if (sr.EndOfStream || string.IsNullOrEmpty(line) || line!.Length < 44) - return false; - - // Save useful angle responses - if (i >= 4 && i <= 7) - { - byte[]? angles = Tools.HexStringToByteArray(line!.Substring(34, 10)); - if (angles == null || angles.Length != 5) - return false; - responses[i - 4] = angles!; - } - } - - int rtOffset = 0x204; - if (xgdType == 3) - rtOffset = 0x24; - - // Replace angles - for (int i = 0; i < 4; i++) - { - int offset = rtOffset + (9 * (i + 4)); - for (int j = 0; j < 5; j++) - { - // Ignore the middle byte - if (j == 2) - continue; - - ss[offset + j] = responses[i][j]; - } - } - - return true; - } - } - - // We couldn't detect the replay table - return false; - } - catch - { - // We don't care what the exception is right now - return false; - } - } - - #endregion } } diff --git a/MPF.Core/SubmissionInfoTool.cs b/MPF.Core/SubmissionInfoTool.cs index 43a258a0..a167a561 100644 --- a/MPF.Core/SubmissionInfoTool.cs +++ b/MPF.Core/SubmissionInfoTool.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using MPF.Core.Data; using MPF.Core.Modules; +using MPF.Core.Processors; using SabreTools.RedumpLib; using SabreTools.RedumpLib.Data; using SabreTools.RedumpLib.Web; @@ -27,6 +28,7 @@ namespace MPF.Core /// Currently selected media type /// Options object representing user-defined options /// Parameters object representing what to send to the internal program + /// Processor object representing how to process the outputs /// Optional result progress callback /// Optional protection progress callback /// SubmissionInfo populated based on outputs, null on error @@ -37,6 +39,7 @@ namespace MPF.Core MediaType? mediaType, Options options, BaseParameters? parameters, + BaseProcessor? processor, IProgress? resultProgress = null, IProgress? protectionProgress = null) { @@ -49,7 +52,7 @@ namespace MPF.Core string outputFilename = Path.GetFileName(outputPath); // Check that all of the relevant files are there - (bool foundFiles, List missingFiles) = parameters.FoundAllFiles(outputDirectory, outputFilename, false); + (bool foundFiles, List missingFiles) = processor.FoundAllFiles(outputDirectory, outputFilename, false); if (!foundFiles) { resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}")); @@ -99,7 +102,7 @@ namespace MPF.Core info = Builder.EnsureAllSections(info); // Get specific tool output handling - parameters?.GenerateSubmissionInfo(info, options, combinedBase, drive, options.IncludeArtifacts); + processor?.GenerateSubmissionInfo(info, options, combinedBase, drive, options.IncludeArtifacts); // Get a list of matching IDs for each line in the DAT if (!string.IsNullOrEmpty(info.TracksAndWriteOffsets!.ClrMameProData) && options.HasRedumpLogin) @@ -114,7 +117,7 @@ namespace MPF.Core info.TracksAndWriteOffsets.ClrMameProData = null; // Add the volume label to comments, if possible or necessary - string? volLabels = FormatVolumeLabels(drive?.VolumeLabel, parameters?.VolumeLabels); + string? volLabels = FormatVolumeLabels(drive?.VolumeLabel, processor?.VolumeLabels); if (volLabels != null) info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.VolumeLabel] = volLabels; diff --git a/MPF.Core/UI/ViewModels/MainViewModel.cs b/MPF.Core/UI/ViewModels/MainViewModel.cs index 89f1e28b..41b3d256 100644 --- a/MPF.Core/UI/ViewModels/MainViewModel.cs +++ b/MPF.Core/UI/ViewModels/MainViewModel.cs @@ -1810,7 +1810,7 @@ namespace MPF.Core.UI.ViewModels string outputFilename = Path.GetFileName(_environment.OutputPath); // If a complete dump already exists - (bool foundFiles, List _) = _environment.Parameters.FoundAllFiles(outputDirectory, outputFilename, true); + (bool foundFiles, List _) = _environment.Processor.FoundAllFiles(outputDirectory, outputFilename, true); if (foundFiles && _displayUserMessage != null) { bool? mbresult = _displayUserMessage("Overwrite?", "A complete dump already exists! Are you sure you want to overwrite?", 2, true); @@ -1826,34 +1826,22 @@ namespace MPF.Core.UI.ViewModels InternalProgram? programFound = null; if (programFound == null && _environment.InternalProgram != InternalProgram.Aaru) { - Modules.Aaru.Parameters parameters = new("") - { - Type = _environment.Type, - System = _environment.System - }; - (bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true); + var processor = new Processors.Aaru(_environment.System, _environment.Type); + (bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename, true); if (foundOtherFiles) programFound = InternalProgram.Aaru; } if (programFound == null && _environment.InternalProgram != InternalProgram.DiscImageCreator) { - Modules.DiscImageCreator.Parameters parameters = new("") - { - Type = _environment.Type, - System = _environment.System - }; - (bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true); + var processor = new Processors.DiscImageCreator(_environment.System, _environment.Type); + (bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename, true); if (foundOtherFiles) programFound = InternalProgram.DiscImageCreator; } if (programFound == null && _environment.InternalProgram != InternalProgram.Redumper) { - Modules.Redumper.Parameters parameters = new("") - { - Type = _environment.Type, - System = _environment.System - }; - (bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true); + var processor = new Processors.Redumper(_environment.System, _environment.Type); + (bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename, true); if (foundOtherFiles) programFound = InternalProgram.Redumper; }