Support more Redumper log outputs (fixes #951)

This commit is contained in:
Matt Nadareski
2026-03-02 09:31:06 -05:00
parent 9056b099ab
commit 6b71b972e9
6 changed files with 215 additions and 20 deletions

View File

@@ -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)

View File

@@ -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,

View File

@@ -13,9 +13,6 @@ CUE [
dat:
<rom name="INVALID" size="12345" crc="00000000" md5="d41d8cd98f00b204e9800998ecf8427e" sha1="da39a3ee5e6b4b0d3255bfef95601890afd80709" />
<< 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

Binary file not shown.

View File

@@ -1,3 +1,6 @@
<< GetBookType >>
book type: DVD-ROM
<< GetDiscType >>
disc type: INVALID
disc type: DVD

View File

@@ -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
}
}
/// <summary>
/// Get reported book type, if possible
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>True if book type was set, false otherwise</returns>
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: <bookType>
#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;
}
}
/// <summary>
/// Get the cuesheet from the input file, if possible
/// </summary>
@@ -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<string> 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: <vendor_id> - <product_id> (revision level: <product_revision_level>, vendor specific: <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: <vendor_id> - <product_id> (revision level: <product_revision_level>, vendor specific: <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 == "<empty>" ? "" : $" ({match.Groups[4].Value})";
return true;
string revisionLevel = match.Groups[3].Value;
revisionLevel += match.Groups[4].Value == "<empty>" ? "" : $" ({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 == "<empty>" ? "" : $" ({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
{