diff --git a/CHANGELIST.md b/CHANGELIST.md index 08f07f4c..19d844f6 100644 --- a/CHANGELIST.md +++ b/CHANGELIST.md @@ -12,6 +12,7 @@ - Use PSX/PS2 serial as filename when Volume Label not present (Deterous) - Allow variables in output path (Deterous) - Check for presence of complete dump from other programs (Deterous) +- Retrieve volume label from logs (Deterous) ### 3.0.3 (2023-12-04) diff --git a/MPF.Core/Data/Drive.cs b/MPF.Core/Data/Drive.cs index 54d4068a..2d7ac4df 100644 --- a/MPF.Core/Data/Drive.cs +++ b/MPF.Core/Data/Drive.cs @@ -277,7 +277,7 @@ namespace MPF.Core.Data return RedumpSystem.IBMPCcompatible; // Check volume labels first - RedumpSystem? systemFromLabel = GetRedumpSystemFromVolumeLabel(); + RedumpSystem? systemFromLabel = GetRedumpSystemFromVolumeLabel(this.VolumeLabel); if (systemFromLabel != null) return systemFromLabel; @@ -512,48 +512,48 @@ namespace MPF.Core.Data /// Get the current system from the drive volume label /// /// The system based on volume label, null if none detected - public RedumpSystem? GetRedumpSystemFromVolumeLabel() + public static RedumpSystem? GetRedumpSystemFromVolumeLabel(string? volumeLabel) { // If the volume label is empty, we can't do anything - if (string.IsNullOrEmpty(this.VolumeLabel)) + if (string.IsNullOrEmpty(volumeLabel)) return null; // Audio CD - if (this.VolumeLabel!.Equals("Audio CD", StringComparison.OrdinalIgnoreCase)) + if (volumeLabel!.Equals("Audio CD", StringComparison.OrdinalIgnoreCase)) return RedumpSystem.AudioCD; // Microsoft Xbox - if (this.VolumeLabel.Equals("SEP13011042", StringComparison.OrdinalIgnoreCase)) + if (volumeLabel.Equals("SEP13011042", StringComparison.OrdinalIgnoreCase)) return RedumpSystem.MicrosoftXbox; - else if (this.VolumeLabel.Equals("SEP13011042072", StringComparison.OrdinalIgnoreCase)) + else if (volumeLabel.Equals("SEP13011042072", StringComparison.OrdinalIgnoreCase)) return RedumpSystem.MicrosoftXbox; // Microsoft Xbox 360 - if (this.VolumeLabel.Equals("XBOX360", StringComparison.OrdinalIgnoreCase)) + if (volumeLabel.Equals("XBOX360", StringComparison.OrdinalIgnoreCase)) return RedumpSystem.MicrosoftXbox360; - else if (this.VolumeLabel.Equals("XGD2DVD_NTSC", StringComparison.OrdinalIgnoreCase)) + else if (volumeLabel.Equals("XGD2DVD_NTSC", StringComparison.OrdinalIgnoreCase)) return RedumpSystem.MicrosoftXbox360; // Microsoft Xbox 360 - Too overly broad even if a lot of discs use this - //if (this.VolumeLabel.Equals("CD_ROM", StringComparison.OrdinalIgnoreCase)) + //if (volumeLabel.Equals("CD_ROM", StringComparison.OrdinalIgnoreCase)) // return RedumpSystem.MicrosoftXbox360; // Also for Xbox One? - //if (this.VolumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase)) + //if (volumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase)) // return RedumpSystem.MicrosoftXbox360; // Sega Mega-CD / Sega-CD - if (this.VolumeLabel.Equals("Sega_CD", StringComparison.OrdinalIgnoreCase)) + if (volumeLabel.Equals("Sega_CD", StringComparison.OrdinalIgnoreCase)) return RedumpSystem.SegaMegaCDSegaCD; // Sony PlayStation 3 - if (this.VolumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase)) + if (volumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase)) return RedumpSystem.SonyPlayStation3; // Sony PlayStation 4 - if (this.VolumeLabel.Equals("PS4VOLUME", StringComparison.OrdinalIgnoreCase)) + if (volumeLabel.Equals("PS4VOLUME", StringComparison.OrdinalIgnoreCase)) return RedumpSystem.SonyPlayStation4; // Sony PlayStation 5 - if (this.VolumeLabel.Equals("PS5VOLUME", StringComparison.OrdinalIgnoreCase)) + if (volumeLabel.Equals("PS5VOLUME", StringComparison.OrdinalIgnoreCase)) return RedumpSystem.SonyPlayStation5; return null; diff --git a/MPF.Core/Modules/BaseParameters.cs b/MPF.Core/Modules/BaseParameters.cs index 66f22710..dd96ba36 100644 --- a/MPF.Core/Modules/BaseParameters.cs +++ b/MPF.Core/Modules/BaseParameters.cs @@ -74,6 +74,11 @@ namespace MPF.Core.Modules /// private Process? process; + /// + /// All found volume labels and their corresponding file systems + /// + public Dictionary>? VolumeLabels; + #endregion #region Virtual Dumping Information diff --git a/MPF.Core/Modules/DiscImageCreator/Parameters.cs b/MPF.Core/Modules/DiscImageCreator/Parameters.cs index 2a0f9b12..62884976 100644 --- a/MPF.Core/Modules/DiscImageCreator/Parameters.cs +++ b/MPF.Core/Modules/DiscImageCreator/Parameters.cs @@ -409,6 +409,10 @@ namespace MPF.Core.Modules.DiscImageCreator // 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) { @@ -2722,6 +2726,89 @@ namespace MPF.Core.Modules.DiscImageCreator } } + /// + /// 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 /// diff --git a/MPF.Core/Modules/Redumper/Parameters.cs b/MPF.Core/Modules/Redumper/Parameters.cs index 93e0c1b1..b28af8c4 100644 --- a/MPF.Core/Modules/Redumper/Parameters.cs +++ b/MPF.Core/Modules/Redumper/Parameters.cs @@ -303,6 +303,10 @@ namespace MPF.Core.Modules.Redumper 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: @@ -1452,20 +1456,20 @@ namespace MPF.Core.Modules.Redumper /// /// Get reported disc type information, if possible /// - /// _disc.txt file location + /// Log file location /// True if disc type info was set, false otherwise - private static bool GetDiscType(string drive, out string? discTypeOrBookType) + 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(drive)) + if (!File.Exists(log)) return false; try { - using var sr = File.OpenText(drive); + using var sr = File.OpenText(log); var line = sr.ReadLine(); while (line != null) { @@ -1492,6 +1496,60 @@ namespace MPF.Core.Modules.Redumper } } + /// + /// 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 /// diff --git a/MPF.Core/SubmissionInfoTool.cs b/MPF.Core/SubmissionInfoTool.cs index 4333f100..6fb54ca9 100644 --- a/MPF.Core/SubmissionInfoTool.cs +++ b/MPF.Core/SubmissionInfoTool.cs @@ -110,8 +110,9 @@ namespace MPF.Core info.TracksAndWriteOffsets.ClrMameProData = null; // Add the volume label to comments, if possible or necessary - if (drive?.VolumeLabel != null && drive.GetRedumpSystemFromVolumeLabel() == null) - info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.VolumeLabel] = drive.VolumeLabel; + string? volLabels = FormatVolumeLabels(drive?.VolumeLabel, parameters?.VolumeLabels); + if (volLabels != null) + info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.VolumeLabel] = volLabels; // Extract info based generically on MediaType switch (mediaType) @@ -670,6 +671,64 @@ namespace MPF.Core return true; } -#endregion + #endregion + + #region Helper Functions + + /// + /// Formats a list of volume labels and their corresponding filesystems + /// + /// Dictionary of volume labels and their filesystems + /// Formatted string of volume labels and their filesystems + private static string? FormatVolumeLabels(string? driveLabel, Dictionary>? labels) + { + // Must have at least one label to format + if (driveLabel == null && (labels == null || labels.Count == 0)) + return null; + + // If no labels given, use drive label + if (labels == null || labels.Count == 0) + { + // Ignore common volume labels + if (Drive.GetRedumpSystemFromVolumeLabel(driveLabel) != null) + return null; + + return driveLabel; + } + + // If only one label, don't mention fs + string firstLabel = labels.First().Key; + if (labels.Count == 1 && (firstLabel == driveLabel || driveLabel == null)) + { + // Ignore common volume labels + if (Drive.GetRedumpSystemFromVolumeLabel(firstLabel) != null) + return null; + + return firstLabel; + } + + // Otherwise, state filesystem for each label + List volLabels = []; + + // Begin formatted output with the label from Windows, if it is unique and not a common volume label + if (driveLabel != null && !labels.TryGetValue(driveLabel, out List? value) && Drive.GetRedumpSystemFromVolumeLabel(driveLabel) == null) + volLabels.Add(driveLabel); + + // Add remaining labels with their corresponding filesystems + foreach (KeyValuePair> label in labels) + { + // Ignore common volume labels + if (Drive.GetRedumpSystemFromVolumeLabel(label.Key) == null) + volLabels.Add($"{label.Key} ({string.Join(", ", [.. label.Value])})"); + } + + // Print each label separated by a comma and a space + if (volLabels.Count == 0) + return null; + + return string.Join(", ", [.. volLabels]); + } + + #endregion } }