From 640e7091cceba41f0e0ddbfddbb25915381fc64b Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 23 Apr 2023 23:38:57 -0400 Subject: [PATCH] Attempt to more accurately parse layerbreaks --- CHANGELIST.md | 1 + MPF.Core/Data/PICDiscInformationUnit.cs | 4 +- MPF.Modules/BaseParameters.cs | 120 +++++++++++++++++++++ MPF.Modules/DiscImageCreator/Parameters.cs | 76 +------------ 4 files changed, 125 insertions(+), 76 deletions(-) diff --git a/CHANGELIST.md b/CHANGELIST.md index b905e255..562768eb 100644 --- a/CHANGELIST.md +++ b/CHANGELIST.md @@ -36,6 +36,7 @@ - Support single digit subs - Fix info tool hash finding - Fix missing size for ISO data +- Attempt to more accurately parse layerbreaks ### 2.5 (2023-03-12) diff --git a/MPF.Core/Data/PICDiscInformationUnit.cs b/MPF.Core/Data/PICDiscInformationUnit.cs index f2479885..392c28a0 100644 --- a/MPF.Core/Data/PICDiscInformationUnit.cs +++ b/MPF.Core/Data/PICDiscInformationUnit.cs @@ -27,7 +27,7 @@ /// /// Should be 0x00 /// - public byte Reserved2 { get; set; } + public byte Reserved0 { get; set; } /// /// DI unit Sequence Number @@ -42,7 +42,7 @@ /// /// Should be 0x00 /// - public byte Reserved3 { get; set; } + public byte Reserved1 { get; set; } #endregion diff --git a/MPF.Modules/BaseParameters.cs b/MPF.Modules/BaseParameters.cs index 421102ca..7b7b9602 100644 --- a/MPF.Modules/BaseParameters.cs +++ b/MPF.Modules/BaseParameters.cs @@ -1160,6 +1160,126 @@ namespace MPF.Modules } } + /// + /// Gets disc information from a PIC file + /// + /// Path to a PIC.bin file + /// Filled PICDiscInformation on success, null on error + /// This omits the emergency brake information, if it exists + protected static PICDiscInformation GetDiscInformation(string pic) + { + try + { + using (BinaryReader br = new BinaryReader(File.OpenRead(pic))) + { + var di = new PICDiscInformation(); + + // Read the initial disc information + di.DataStructureLength = br.ReadUInt16BigEndian(); + di.Reserved0 = br.ReadByte(); + di.Reserved1 = br.ReadByte(); + + // Create a list for the units + var diUnits = new List(); + + // Loop and read all available units + for (int i = 0; i < 32; i++) + { + var unit = new PICDiscInformationUnit(); + + // We only accept Disc Information units, not Emergency Brake or other + unit.DiscInformationIdentifier = Encoding.ASCII.GetString(br.ReadBytes(2)); + if (unit.DiscInformationIdentifier != "DI") + break; + + unit.DiscInformationFormat = br.ReadByte(); + unit.NumberOfUnitsInBlock = br.ReadByte(); + unit.Reserved0 = br.ReadByte(); + unit.SequenceNumber = br.ReadByte(); + unit.BytesInUse = br.ReadByte(); + unit.Reserved1 = br.ReadByte(); + + unit.DiscTypeIdentifier = Encoding.ASCII.GetString(br.ReadBytes(3)); + unit.DiscSizeClassVersion = br.ReadByte(); + switch (unit.DiscTypeIdentifier) + { + case PICDiscInformationUnit.DiscTypeIdentifierROM: + unit.FormatDependentContents = br.ReadBytes(52); + break; + case PICDiscInformationUnit.DiscTypeIdentifierReWritable: + case PICDiscInformationUnit.DiscTypeIdentifierRecordable: + unit.FormatDependentContents = br.ReadBytes(100); + unit.DiscManufacturerID = br.ReadBytes(6); + unit.MediaTypeID = br.ReadBytes(3); + unit.TimeStamp = br.ReadUInt16(); + unit.ProductRevisionNumber = br.ReadByte(); + break; + } + + diUnits.Add(unit); + } + + // Assign the units and return + di.Units = diUnits.ToArray(); + return di; + } + } + catch + { + // We don't care what the error was + return null; + } + } + + /// + /// Get the layerbreak info associated from the disc information + /// + /// Disc information containing unformatted data + /// True if layerbreak info was set, false otherwise + protected static bool GetLayerbreaks(PICDiscInformation di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3) + { + // Set the default values + layerbreak1 = null; layerbreak2 = null; layerbreak3 = null; + + // If we don't have valid disc information, we can't do anything + if (di?.Units == null || di.Units.Length <= 1) + return false; + + int ReadFromArrayBigEndian(byte[] bytes, int offset) + { + var span = new ReadOnlySpan(bytes, offset, 0x04); + byte[] rev = span.ToArray(); + Array.Reverse(rev); + return BitConverter.ToInt32(rev, 0); + } + + // Layerbreak 1 (2+ layers) + if (di.Units.Length >= 2) + { + long offset = ReadFromArrayBigEndian(di.Units[0].FormatDependentContents, 0x0C); + long value = ReadFromArrayBigEndian(di.Units[0].FormatDependentContents, 0x10); + layerbreak1 = value - offset + 2; + } + + // Layerbreak 2 (3+ layers) + if (di.Units.Length >= 3) + { + long offset = ReadFromArrayBigEndian(di.Units[1].FormatDependentContents, 0x0C); + long value = ReadFromArrayBigEndian(di.Units[1].FormatDependentContents, 0x10); + layerbreak2 = layerbreak1 + value - offset + 2; + } + + // Layerbreak 3 (4 layers) + if (di.Units.Length >= 4) + { + long offset = ReadFromArrayBigEndian(di.Units[2].FormatDependentContents, 0x0C); + long value = ReadFromArrayBigEndian(di.Units[2].FormatDependentContents, 0x10); + layerbreak3 = layerbreak2 + value - offset + 2; + } + + return true; + } + /// /// Get hashes from an input file path /// diff --git a/MPF.Modules/DiscImageCreator/Parameters.cs b/MPF.Modules/DiscImageCreator/Parameters.cs index 541b317c..9857f60c 100644 --- a/MPF.Modules/DiscImageCreator/Parameters.cs +++ b/MPF.Modules/DiscImageCreator/Parameters.cs @@ -461,7 +461,8 @@ namespace MPF.Modules.DiscImageCreator } else if (this.Type == MediaType.BluRay) { - if (GetLayerbreak($"{basePath}_PIC.bin", out long? layerbreak1, out long? layerbreak2, out long? layerbreak3)) + var di = GetDiscInformation($"{basePath}_PIC.bin"); + if (GetLayerbreaks(di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3)) { if (layerbreak1 != null && layerbreak1 * 2048 < info.SizeAndChecksums.Size) info.SizeAndChecksums.Layerbreak = layerbreak1.Value; @@ -2978,79 +2979,6 @@ namespace MPF.Modules.DiscImageCreator } } - /// - /// Get the layerbreak info associated with the dump - /// - /// Path to the PIC.bin file associated with the dump - /// True if layerbreak info was set, false otherwise - /// https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters - private static bool GetLayerbreak(string picPath, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3) - { - // Set the default values - layerbreak1 = null; layerbreak2 = null; layerbreak3 = null; - - // If the file doesn't exist, we can't get the info - if (!File.Exists(picPath)) - return false; - - try - { - using (BinaryReader br = new BinaryReader(File.OpenRead(picPath))) - { - // Disc type - int infoLength; - br.BaseStream.Seek(0x0C, SeekOrigin.Begin); - string identifier = Encoding.ASCII.GetString(br.ReadBytes(3)); - switch (identifier) - { - case "BDO": - infoLength = 0x40; - break; - case "BDW": - case "BDR": - infoLength = 0x74; - break; - default: - return false; - } - - // Layerbreak 1 - br.BaseStream.Seek((0 * infoLength) + 0x1C, SeekOrigin.Begin); - long layerbreak1Offset = br.ReadInt32BigEndian(); - long layerbreak1Value = br.ReadInt32BigEndian(); - if (layerbreak1Value == 0) - return false; - - layerbreak1 = layerbreak1Value - layerbreak1Offset + 2; - - // Layerbreak 2 - br.BaseStream.Seek((1 * infoLength) + 0x1C, SeekOrigin.Begin); - long layerbreak2Offset = br.ReadInt32BigEndian(); - long layerbreak2Value = br.ReadInt32BigEndian(); - if (layerbreak2Value == 0) - return true; - - layerbreak2 = layerbreak1 + layerbreak2Value - layerbreak2Offset + 2; - - // Layerbreak 3 - br.BaseStream.Seek((2 * infoLength) + 0x1C, SeekOrigin.Begin); - long layerbreak3Offset = br.ReadInt32BigEndian(); - long layerbreak3Value = br.ReadInt32BigEndian(); - if (layerbreak3Value == 0) - return true; - - layerbreak3 = layerbreak2 + layerbreak3Value - layerbreak3Offset + 2; - - return true; - } - } - catch - { - // We don't care what the error was - return false; - } - } - /// /// Get multisession information from the input file, if possible ///