diff --git a/CHANGELIST.md b/CHANGELIST.md
index 0587fcb6..914d6202 100644
--- a/CHANGELIST.md
+++ b/CHANGELIST.md
@@ -52,6 +52,7 @@
- Include BCA in zipped files for Redumper
- Add retry override for MPF.CLI
- Limit BCA read to 64 bytes for Redumper
+- Support more Redumper log outputs
### 3.6.0 (2025-11-28)
diff --git a/MPF.Processors.Test/RedumperTests.cs b/MPF.Processors.Test/RedumperTests.cs
index b10dbe50..dd6dcffc 100644
--- a/MPF.Processors.Test/RedumperTests.cs
+++ b/MPF.Processors.Test/RedumperTests.cs
@@ -306,6 +306,61 @@ namespace MPF.Processors.Test
#endregion
+ #region GetBCA
+
+ [Fact]
+ public void GetBCA_InvalidPath_Null()
+ {
+ string bcaPath = "INVALID";
+ string? actual = Redumper.GetBCA(bcaPath);
+ Assert.Null(actual);
+ }
+
+ [Fact]
+ public void GetBCA_ValidPath_Formatted()
+ {
+ string expected = "0001 0203 0405 0607 0809 0A0B 0C0D 0E0F\n0001 0203 0405 0607 0809 0A0B 0C0D 0E0F\n";
+ string bcaPath = Path.Combine(Environment.CurrentDirectory, "TestData", "Redumper", "DVD", "test.bca");
+
+ string? actual = Redumper.GetBCA(bcaPath);
+ Assert.NotNull(actual);
+ Assert.Equal(expected, actual);
+ }
+
+ #endregion
+
+ #region GetBookType
+
+ [Fact]
+ public void GetBookType_Empty_Null()
+ {
+ string log = string.Empty;
+ bool actual = Redumper.GetBookType(log, out MediaType? bookType);
+ Assert.False(actual);
+ Assert.Null(bookType);
+ }
+
+ [Fact]
+ public void GetBookType_Invalid_Null()
+ {
+ string log = "INVALID";
+ bool actual = Redumper.GetBookType(log, out MediaType? bookType);
+ Assert.False(actual);
+ Assert.Null(bookType);
+ }
+
+ [Fact]
+ public void GetBookType_Valid_Filled()
+ {
+ MediaType? expected = MediaType.DVD;
+ string log = Path.Combine(Environment.CurrentDirectory, "TestData", "Redumper", "DVD", "test.log");
+ bool actual = Redumper.GetBookType(log, out MediaType? bookType);
+ Assert.True(actual);
+ Assert.Equal(expected, bookType);
+ }
+
+ #endregion
+
#region GetCuesheet
[Fact]
@@ -507,6 +562,8 @@ namespace MPF.Processors.Test
[InlineData("HD DVD-RW", MediaType.HDDVD)]
[InlineData("HD DVD-R DL", MediaType.HDDVD)]
[InlineData("HD DVD-RW DL", MediaType.HDDVD)]
+ [InlineData("NINTENDO", MediaType.NintendoWiiOpticalDisc)]
+ [InlineData("RESERVED5", MediaType.NintendoWiiOpticalDisc)]
[InlineData("NON STANDARD", null)]
public void GetDiscTypeFromProfileTest(string? profile, MediaType? expected)
{
@@ -755,7 +812,7 @@ namespace MPF.Processors.Test
{
string? expectedManufacturer = "manufacturer";
string? expectedModel = "model";
- string? expectedFirmware = "revision (vendor)";
+ string? expectedFirmware = "revision (vendor), firmware";
string log = Path.Combine(Environment.CurrentDirectory, "TestData", "Redumper", "CDROM", "test.log");
bool actual = Redumper.GetHardwareInfo(log,
diff --git a/MPF.Processors.Test/TestData/Redumper/CDROM/test.log b/MPF.Processors.Test/TestData/Redumper/CDROM/test.log
index ba9ce177..f851df56 100644
--- a/MPF.Processors.Test/TestData/Redumper/CDROM/test.log
+++ b/MPF.Processors.Test/TestData/Redumper/CDROM/test.log
@@ -13,9 +13,6 @@ CUE [
dat:
-<< GetDiscProfile >>
-current profile: CD-ROM
-
<< GetDiscType >>
disc type: INVALID
disc type: CD
@@ -63,9 +60,12 @@ DC [
0090 TEST DATA
00A0 TEST DATA
+<< GetDiscProfile >>
<< GetHardwareInfo >>
-drive path:
- drive: manufacturer - model (revision level: revision, vendor specific: vendor)
+drive information
+ inquiry: manufacturer - model (revision level: revision, vendor specific: vendor)
+ profile: CD-ROM
+ firmware: firmware
<< GetLayerbreaks >>
layer break (layer: 0): 12345
diff --git a/MPF.Processors.Test/TestData/Redumper/DVD/test.bca b/MPF.Processors.Test/TestData/Redumper/DVD/test.bca
new file mode 100644
index 00000000..1c3094c6
Binary files /dev/null and b/MPF.Processors.Test/TestData/Redumper/DVD/test.bca differ
diff --git a/MPF.Processors.Test/TestData/Redumper/DVD/test.log b/MPF.Processors.Test/TestData/Redumper/DVD/test.log
index 5de95187..bd26e45b 100644
--- a/MPF.Processors.Test/TestData/Redumper/DVD/test.log
+++ b/MPF.Processors.Test/TestData/Redumper/DVD/test.log
@@ -1,3 +1,6 @@
+<< GetBookType >>
+ book type: DVD-ROM
+
<< GetDiscType >>
disc type: INVALID
disc type: DVD
\ No newline at end of file
diff --git a/MPF.Processors/Redumper.cs b/MPF.Processors/Redumper.cs
index b1ae4425..2f797be7 100644
--- a/MPF.Processors/Redumper.cs
+++ b/MPF.Processors/Redumper.cs
@@ -58,11 +58,15 @@ namespace MPF.Processors
}
#endif
- // Use the log first, if it exists
- if (GetDiscType($"{basePath}.log", out MediaType? mediaType))
- return mediaType;
+ // Use the disc type, if possible
+ if (GetDiscType($"{basePath}.log", out MediaType? discType))
+ return discType;
- // Use the profile for older Redumper versions
+ // Use the book type, if possible
+ if (GetBookType($"{basePath}.log", out MediaType? bookType))
+ return bookType;
+
+ // Use the profile, if possible
if (GetDiscProfile($"{basePath}.log", out string? discProfile))
return GetDiscTypeFromProfile(discProfile);
@@ -921,6 +925,80 @@ namespace MPF.Processors
}
}
+ ///
+ /// Get reported book type, if possible
+ ///
+ /// Log file location
+ /// True if book type was set, false otherwise
+ internal static bool GetBookType(string log, out MediaType? bookType)
+ {
+ // Set the default values
+ bookType = null;
+
+ // If the file doesn't exist, we can't get the info
+ if (string.IsNullOrEmpty(log))
+ return false;
+ if (!File.Exists(log))
+ return false;
+
+ try
+ {
+ using var sr = File.OpenText(log);
+ var line = sr.ReadLine()?.Trim();
+ while (line is not null)
+ {
+ // If the line isn't the book type, skip
+ if (!line.StartsWith("book type:"))
+ {
+ line = sr.ReadLine()?.Trim();
+ continue;
+ }
+
+ // book type:
+#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
+ string bookTypeStr = line["book type: ".Length..];
+#else
+ string bookTypeStr = line.Substring("book type: ".Length);
+#endif
+
+ // Set the media type based on the string
+ bookType = bookTypeStr switch
+ {
+ "DVD-ROM" => MediaType.DVD,
+ "DVD-RAM" => MediaType.DVD,
+ "DVD-R" => MediaType.DVD,
+ "DVD-RW" => MediaType.DVD,
+ "DVD+RW" => MediaType.DVD,
+ "DVD+R" => MediaType.DVD,
+ "DVD+RW DL" => MediaType.DVD,
+ "DVD+R DL" => MediaType.DVD,
+
+ "HD DVD-ROM" => MediaType.HDDVD,
+ "HD DVD-RAM" => MediaType.HDDVD,
+ "HD DVD-R" => MediaType.HDDVD,
+
+ "NINTENDO" => MediaType.NintendoWiiOpticalDisc, // Maybe also GC?
+ "RESERVED5" => MediaType.NintendoWiiOpticalDisc, // Maybe also GC?
+
+ "RESERVED1" => null,
+ "RESERVED2" => null,
+ "RESERVED3" => null,
+ "RESERVED4" => null,
+ _ => null,
+ };
+ return bookType is not null;
+ }
+
+ return false;
+ }
+ catch
+ {
+ // Absorb the exception
+ bookType = null;
+ return false;
+ }
+ }
+
///
/// Get the cuesheet from the input file, if possible
///
@@ -1193,6 +1271,9 @@ namespace MPF.Processors
"HD DVD-R DL" => MediaType.HDDVD,
"HD DVD-RW DL" => MediaType.HDDVD,
+ "NINTENDO" => MediaType.NintendoWiiOpticalDisc, // Maybe also GC?
+ "RESERVED5" => MediaType.NintendoWiiOpticalDisc, // Maybe also GC?
+
"NON STANDARD" => null,
_ => null,
@@ -1614,32 +1695,85 @@ namespace MPF.Processors
if (!File.Exists(log))
return false;
+ // Firmware can be split into different lines
+ List firmwarePieces = [];
+
try
{
- // Fast forward to the drive information line
+ // Fast forward to the drive information section
using var sr = File.OpenText(log);
- while (!(sr.ReadLine()?.Trim().StartsWith("drive path:") ?? true)) ;
+ do
+ {
+ // Read the next line until EOF
+ string? tempLine = sr.ReadLine()?.Trim();
+ if (tempLine is null)
+ break;
+
+ // Check old and new drive information sections
+ if (tempLine.StartsWith("drive path:"))
+ break;
+ else if (tempLine.StartsWith("drive information"))
+ break;
+
+ } while (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);
+ var oldRegex = new Regex(@"drive: (.+) - (.+) \(revision level: (.+), vendor specific: (.+)\)", RegexOptions.Compiled);
+
+ // inquiry: - (revision level: , vendor specific: )
+ var newRegex = new Regex(@"inquiry: (.+) - (.+) \(revision level: (.+), vendor specific: (.+)\)", RegexOptions.Compiled);
string? line;
- while ((line = sr.ReadLine()) is not null)
+ while ((line = sr.ReadLine()?.Trim()) is not null)
{
- var match = regex.Match(line.Trim());
+ // Check old version of line
+ var match = oldRegex.Match(line);
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;
+
+ string revisionLevel = match.Groups[3].Value;
+ revisionLevel += match.Groups[4].Value == "" ? "" : $" ({match.Groups[4].Value})";
+ firmwarePieces.Add(revisionLevel);
+ }
+
+ // Check new version of line
+ match = newRegex.Match(line);
+ if (match.Success)
+ {
+ manufacturer = match.Groups[1].Value;
+ model = match.Groups[2].Value;
+
+ string revisionLevel = match.Groups[3].Value;
+ revisionLevel += match.Groups[4].Value == "" ? "" : $" ({match.Groups[4].Value})";
+ firmwarePieces.Add(revisionLevel);
+ }
+
+ // Distinct additional firmware
+ if (line.StartsWith("firmware:"))
+ {
+#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
+ string exactFirmware = line["firmware: ".Length..].Trim();
+#else
+ string exactFirmware = line.Substring("firmware: ".Length).Trim();
+#endif
+ firmwarePieces.Add(exactFirmware);
+ }
+
+ if (string.IsNullOrEmpty(line))
+ {
+ // An empty line indicates the end of the section
+ break;
}
}
- // Required lines were not found
- return false;
+ // Assemble the firmware string, if needed
+ firmware = string.Join(", ", [.. firmwarePieces]);
+
+ // Return if the fields are filled
+ return manufacturer is not null && model is not null && firmware is not null;
}
catch
{