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 {