From aaab84f90a02df02cfff3bd547c48b9f12880157 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Wed, 16 Sep 2020 15:59:11 -0700 Subject: [PATCH] Fix multiple things in Aaru Addresses overlooked issue with DatFile generation, consolidates common code into methods, fixes PVD generation and write --- DICUI.Check/Program.cs | 8 + DICUI.Library/Aaru/Parameters.cs | 329 +++++++++++++++++-------------- 2 files changed, 194 insertions(+), 143 deletions(-) diff --git a/DICUI.Check/Program.cs b/DICUI.Check/Program.cs index b0db43f5..ff87b9c8 100644 --- a/DICUI.Check/Program.cs +++ b/DICUI.Check/Program.cs @@ -11,6 +11,14 @@ namespace DICUI.Check { public static void Main(string[] args) { + args = new string[] + { + "cd", + "ibm", + "--use", "aaru", + "B:\\_TEMP\\Aaru Dumps\\MONKEY4_CD2\\MONKEY4_CD2.aif" + }; + // Help options if (args.Length == 0 || args[0] == "-h" || args[0] == "-?") { diff --git a/DICUI.Library/Aaru/Parameters.cs b/DICUI.Library/Aaru/Parameters.cs index 86c31027..56f7ccd3 100644 --- a/DICUI.Library/Aaru/Parameters.cs +++ b/DICUI.Library/Aaru/Parameters.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Schema; @@ -2252,11 +2253,6 @@ namespace DICUI.Aaru // Loop through each track foreach (TrackType track in opticalDisc.Track) { - // Create cue file entry - CueFile cueFile = new CueFile(); - cueFile.FileType = CueFileType.BINARY; - cueFile.Tracks = new List(); - // Create cue track entry CueTrack cueTrack = new CueTrack(); cueTrack.Number = (int)(track.Sequence?.TrackNumber ?? 0); @@ -2264,14 +2260,11 @@ namespace DICUI.Aaru cueTrack.Flags = ConvertToTrackFlag(track.Flags); cueTrack.ISRC = track.ISRC; - // Build the track datfile data and append - cueFile.FileName = basePath; - if (totalTracks == 1) - cueFile.FileName = $"{cueFile.FileName}.bin"; - else if (totalTracks > 1 && totalTracks < 10) - cueFile.FileName = $"{cueFile.FileName} (Track {cueTrack.Number}).bin"; - else - cueFile.FileName = $"{cueFile.FileName } (Track {cueTrack.Number:D2}).bin"; + // Create cue file entry + CueFile cueFile = new CueFile(); + cueFile.FileName = GenerateTrackName(basePath, (int)totalTracks, cueTrack.Number); + cueFile.FileType = CueFileType.BINARY; + cueFile.Tracks = new List(); // Add index data if (track.Indexes != null && track.Indexes.Length > 0) @@ -2288,7 +2281,10 @@ namespace DICUI.Aaru else { // Default if index data missing from sidecar - cueTrack.Indices.Add(new CueIndex("01", "00:00:00")); + cueTrack.Indices = new List() + { + new CueIndex("01", "00:00:00"), + }; } // Add the track to the file @@ -2311,7 +2307,7 @@ namespace DICUI.Aaru } } - return string.Empty; + return null; } /// @@ -2320,6 +2316,7 @@ namespace DICUI.Aaru /// CICM Sidecar data generated by Aaru /// Base path for determining file names /// String containing the datfile, null on error + /// TODO: Handle DVDs private static string GenerateDatfile(CICMMetadataType cicmSidecar, string basePath) { // If the object is null, we can't get information from it @@ -2330,7 +2327,7 @@ namespace DICUI.Aaru string datfile = string.Empty; // Process OpticalDisc, if possible - if (cicmSidecar.OpticalDisc != null || cicmSidecar.OpticalDisc.Length > 0) + if (cicmSidecar.OpticalDisc != null && cicmSidecar.OpticalDisc.Length > 0) { // Loop through each OpticalDisc in the metadata foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) @@ -2375,21 +2372,14 @@ namespace DICUI.Aaru } // Build the track datfile data and append - string trackName = basePath; - if (totalTracks == 1) - trackName = $"{trackName}.bin"; - else if (totalTracks > 1 && totalTracks < 10) - trackName = $"{trackName} (Track {trackNumber}).bin"; - else - trackName = $"{trackName} (Track {trackNumber.ToString().PadLeft(2, '0')}).bin"; - + string trackName = GenerateTrackName(basePath, (int)totalTracks, (int)trackNumber); datfile += $"\n"; } } } // Process BlockMedia, if possible - if (cicmSidecar.BlockMedia != null || cicmSidecar.BlockMedia.Length > 0) + if (cicmSidecar.BlockMedia != null && cicmSidecar.BlockMedia.Length > 0) { // Loop through each BlockMedia in the metadata foreach (BlockMediaType blockMedia in cicmSidecar.BlockMedia) @@ -2429,6 +2419,22 @@ namespace DICUI.Aaru return datfile; } + /// + /// Generate a track name based on current path and tracks + /// + private static string GenerateTrackName(string basePath, int totalTracks, int trackNumber) + { + string trackName = Path.GetFileNameWithoutExtension(basePath); + if (totalTracks == 1) + trackName = $"{trackName}.bin"; + else if (totalTracks > 1 && totalTracks < 10) + trackName = $"{trackName} (Track {trackNumber}).bin"; + else + trackName = $"{trackName} (Track {trackNumber:D2}).bin"; + + return trackName; + } + /// /// Generate a Redump-compatible PVD block based on CICM sidecar file /// @@ -2447,71 +2453,12 @@ namespace DICUI.Aaru // Loop through each OpticalDisc in the metadata foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc) { - // Required variables - DateTime creation = DateTime.MinValue; - DateTime modification = DateTime.MinValue; - DateTime expiration = DateTime.MinValue; - DateTime effective = DateTime.MinValue; + byte[] pvdData = GeneratePVDData(opticalDisc); - // If there are no tracks, we can't get a PVD - if (opticalDisc.Track == null || opticalDisc.Track.Length == 0) + // If we got a null value, we skip this disc + if (pvdData == null) continue; - // Take the first track only - TrackType track = opticalDisc.Track[0]; - - // If there are no partitions, we can't get a PVD - if (track.FileSystemInformation == null || track.FileSystemInformation.Length == 0) - continue; - - // Loop through each Partition - foreach (PartitionType partition in track.FileSystemInformation) - { - // If the partition has no file systems, we can't get a PVD - if (partition.FileSystems == null || partition.FileSystems.Length == 0) - continue; - - // Loop through each FileSystem until we find a PVD - foreach (FileSystemType fileSystem in partition.FileSystems) - { - // If we don't have a PVD-able filesystem, we can't get a PVD - if (!fileSystem.CreationDateSpecified - && !fileSystem.ModificationDateSpecified - && !fileSystem.ExpirationDateSpecified - && !fileSystem.EffectiveDateSpecified) - { - continue; - } - - // Creation Date - if (fileSystem.CreationDateSpecified) - creation = fileSystem.CreationDate; - - // Modification Date - if (fileSystem.ModificationDateSpecified) - modification = fileSystem.ModificationDate; - - // Expiration Date - if (fileSystem.ExpirationDateSpecified) - expiration = fileSystem.ExpirationDate; - - // Effective Date - if (fileSystem.EffectiveDateSpecified) - effective = fileSystem.EffectiveDate; - - break; - } - - // If we found a Partition with PVD data, we break - if (creation != DateTime.MinValue - || modification != DateTime.MinValue - || expiration != DateTime.MinValue - || effective != DateTime.MinValue) - { - break; - } - } - /* Needs to look like this on the other side 0320 : 20 20 20 20 20 20 20 20 20 20 20 20 20 31 39 39 199 @@ -2522,64 +2469,14 @@ namespace DICUI.Aaru 0370 : 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ */ - // TODO: Hundredths of seconds are not part of the output, using 00 `30 30` bytes for now - // TODO: Timezones are not part of the output, using UTC `00` byte for now - // Build each row in consecutive order string pvd = string.Empty; - - // String versions of each DateTime - string emptyTime = "0000-00-00T00:00:00.00+00:00"; - string creationString = emptyTime; - string modificationString = emptyTime; - string expirationString = emptyTime; - string effectiveString = emptyTime; - - // If we don't have default values, set the proper string - if (creation != DateTime.MinValue) - creationString = creation.ToString("yyyy-MM-ddTHH:mm:ss.ffK"); - if (modification != DateTime.MinValue) - modificationString = emptyTime; - if (expiration != DateTime.MinValue) - expirationString = emptyTime; - if (effective != DateTime.MinValue) - effectiveString = emptyTime; - - // Get byte versions of each for the PVD - byte[] effectiveBytes = creationString.ToCharArray().Select(c => (byte)c).ToArray(); - byte[] creationBytes = modificationString.ToCharArray().Select(c => (byte)c).ToArray(); - byte[] modificationBytes = expirationString.ToCharArray().Select(c => (byte)c).ToArray(); - byte[] expirationBytes = effectiveString.ToCharArray().Select(c => (byte)c).ToArray(); - - // 0320 - pvd += $"0320 : 20 20 20 20 20 20 20 20"; - pvd += $" 20 20 20 20 20 {creationBytes[0]:x} {creationBytes[1]:x} {creationBytes[2]:x}"; - pvd += $" {creationString.Substring(0, 3)}\n"; - - // 0330 - pvd += $"0330 : {creationBytes[3]:x} {creationBytes[5]:x} {creationBytes[6]:x} {creationBytes[8]:x} {creationBytes[9]:x} {creationBytes[11]:x} {creationBytes[12]:x} {creationBytes[14]:x}"; - pvd += $" {creationBytes[15]:x} {creationBytes[17]:x} {creationBytes[18]:x} 30 30 00 {modificationBytes[0]:x} {modificationBytes[1]:x}"; - pvd += $" {creationString[3]}{creationString.Substring(5, 2)}{creationString.Substring(8, 2)}{creationString.Substring(11, 2)}{creationString.Substring(14, 2)}{creationString.Substring(17, 2)}00.{modificationString.Substring(0, 2)}\n"; - - // 0340 - pvd += $"0340 : {modificationBytes[2]:x} {modificationBytes[3]:x} {modificationBytes[5]:x} {modificationBytes[6]:x} {modificationBytes[8]:x} {modificationBytes[9]:x} {modificationBytes[11]:x} {modificationBytes[12]:x}"; - pvd += $" {modificationBytes[14]:x} {modificationBytes[15]:x} {modificationBytes[17]:x} {modificationBytes[18]:x} 30 30 00 {expirationBytes[0]:x}"; - pvd += $" {modificationString.Substring(2, 2)}{modificationString.Substring(5, 2)}{modificationString.Substring(8, 2)}{modificationString.Substring(11, 2)}{modificationString.Substring(14, 2)}{modificationString.Substring(17, 2)}00.{expirationString[0]}\n"; - - // 0350 - pvd += $"0350 : {expirationBytes[1]:x} {expirationBytes[2]:x} {expirationBytes[3]:x} {expirationBytes[5]:x} {expirationBytes[6]:x} {expirationBytes[8]:x} {expirationBytes[9]:x} {expirationBytes[11]:x}"; - pvd += $" {expirationBytes[12]:x} {expirationBytes[14]:x} {expirationBytes[15]:x} {expirationBytes[17]:x} {expirationBytes[18]:x} 30 30 00"; - pvd += $" {expirationString.Substring(1, 3)}{expirationString.Substring(5, 2)}{expirationString.Substring(8, 2)}{expirationString.Substring(11, 2)}{expirationString.Substring(14, 2)}{expirationString.Substring(17, 2)}00.\n"; - - // 0360 - pvd += $"0360 : {effectiveBytes[0]:x} {effectiveBytes[1]:x} {effectiveBytes[2]:x} {effectiveBytes[3]:x} {effectiveBytes[5]:x} {effectiveBytes[6]:x} {effectiveBytes[8]:x} {effectiveBytes[9]:x}"; - pvd += $" {effectiveBytes[11]:x} {effectiveBytes[12]:x} {effectiveBytes[14]:x} {effectiveBytes[15]:x} {effectiveBytes[17]:x} {effectiveBytes[18]:x} 30 30"; - pvd += $" {effectiveString.Substring(0, 4)}{effectiveString.Substring(5, 2)}{effectiveString.Substring(8, 2)}{effectiveString.Substring(11, 2)}{effectiveString.Substring(14, 2)}{effectiveString.Substring(17, 2)}00\n"; - - // 0370 - TODO: Get the sometimes burner string that appears in this block - pvd += $"0370 : 00 01 00 00 00 00 00 00"; - pvd += $" 00 00 00 00 00 00 00 00"; - pvd += $" ................\n"; + pvd += GeneratePVDOutputLine("0320", pvdData, 0); + pvd += GeneratePVDOutputLine("0330", pvdData, 16); + pvd += GeneratePVDOutputLine("0340", pvdData, 32); + pvd += GeneratePVDOutputLine("0350", pvdData, 48); + pvd += GeneratePVDOutputLine("0360", pvdData, 64); + pvd += GeneratePVDOutputLine("0370", pvdData, 80); return pvd; } @@ -2588,6 +2485,152 @@ namespace DICUI.Aaru return null; } + /// + /// Generate the byte array representing the current PVD information + /// + /// OpticalDisc type from CICM Sidecar data + /// Byte array representing the PVD, null on error + private static byte[] GeneratePVDData(OpticalDiscType opticalDisc) + { + // Required variables + DateTime creation = DateTime.MinValue; + DateTime modification = DateTime.MinValue; + DateTime expiration = DateTime.MinValue; + DateTime effective = DateTime.MinValue; + + // If there are no tracks, we can't get a PVD + if (opticalDisc.Track == null || opticalDisc.Track.Length == 0) + return null; + + // Take the first track only + TrackType track = opticalDisc.Track[0]; + + // If there are no partitions, we can't get a PVD + if (track.FileSystemInformation == null || track.FileSystemInformation.Length == 0) + return null; + + // Loop through each Partition + foreach (PartitionType partition in track.FileSystemInformation) + { + // If the partition has no file systems, we can't get a PVD + if (partition.FileSystems == null || partition.FileSystems.Length == 0) + continue; + + // Loop through each FileSystem until we find a PVD + foreach (FileSystemType fileSystem in partition.FileSystems) + { + // If we don't have a PVD-able filesystem, we can't get a PVD + if (!fileSystem.CreationDateSpecified + && !fileSystem.ModificationDateSpecified + && !fileSystem.ExpirationDateSpecified + && !fileSystem.EffectiveDateSpecified) + { + continue; + } + + // Creation Date + if (fileSystem.CreationDateSpecified) + creation = fileSystem.CreationDate; + + // Modification Date + if (fileSystem.ModificationDateSpecified) + modification = fileSystem.ModificationDate; + + // Expiration Date + if (fileSystem.ExpirationDateSpecified) + expiration = fileSystem.ExpirationDate; + + // Effective Date + if (fileSystem.EffectiveDateSpecified) + effective = fileSystem.EffectiveDate; + + break; + } + + // If we found a Partition with PVD data, we break + if (creation != DateTime.MinValue + || modification != DateTime.MinValue + || expiration != DateTime.MinValue + || effective != DateTime.MinValue) + { + break; + } + } + + // If we found no partitions, we return null + if (creation == DateTime.MinValue + && modification == DateTime.MinValue + && expiration == DateTime.MinValue + && effective == DateTime.MinValue) + { + return null; + } + + // Now generate the byte array data + List pvdData = new List(); + pvdData.AddRange(new string(' ', 13).ToCharArray().Select(c => (byte)c)); + pvdData.AddRange(GeneratePVDDateTimeBytes(creation)); + pvdData.AddRange(GeneratePVDDateTimeBytes(modification)); + pvdData.AddRange(GeneratePVDDateTimeBytes(expiration)); + pvdData.AddRange(GeneratePVDDateTimeBytes(effective)); + pvdData.AddRange(new string((char)0, 15).ToCharArray().Select(c => (byte)c)); + + // Return the filled array + return pvdData.ToArray(); + } + + /// + /// Generate the required bytes from a DateTime object + /// + /// DateTime to get representation of + /// Byte array representing the DateTime + private static byte[] GeneratePVDDateTimeBytes(DateTime dateTime) + { + string emptyTime = "0000000000000000"; + string dateTimeString = emptyTime; + byte timeZoneNumber = 0; + + // If we don't have default values, set the proper string + if (dateTime != DateTime.MinValue) + { + dateTimeString = dateTime.ToString("yyyyMMddHHmmssff"); + + // Get timezone offset (0 == GMT, up and down in 15-minute increments) + string timeZoneString = dateTime.ToString("zzz"); + + // Format is hh:mm + string[] splitTimeZoneString = timeZoneString.Split(':'); + if (int.TryParse(splitTimeZoneString[0], out int hours)) + timeZoneNumber += (byte)(hours * 4); + if (int.TryParse(splitTimeZoneString[1], out int minutes)) + timeZoneNumber += (byte)(minutes / 15); + } + + // Get and return the byte array + List dateTimeList = dateTimeString.ToCharArray().Select(c => (byte)c).ToList(); + dateTimeList.Add(timeZoneNumber); + return dateTimeList.ToArray(); + } + + /// + /// Generate a single PVD line from a byte array + /// + private static string GeneratePVDOutputLine(string row, byte[] bytes, int startIndex) + { + string pvdLine = string.Empty; + + // TODO: Make this more efficient to generate a single line + pvdLine += $"{row} : "; + pvdLine += BitConverter.ToString(bytes.Skip(startIndex).Take(8).ToArray()).Replace("-", " "); + pvdLine += " "; + pvdLine += BitConverter.ToString(bytes.Skip(startIndex + 8).Take(8).ToArray()).Replace("-", " "); + pvdLine += " "; + pvdLine += Encoding.ASCII.GetString(bytes.Skip(startIndex).Take(16).ToArray()).Replace((char)0, '.').Replace('?', '.'); + pvdLine += "\n"; + + return pvdLine; + } + /// /// Read the CICM Sidecar as an object ///