Fix multiple things in Aaru

Addresses overlooked issue with DatFile generation, consolidates common code into methods, fixes PVD generation and write
This commit is contained in:
Matt Nadareski
2020-09-16 15:59:11 -07:00
parent 318a1a303c
commit aaab84f90a
2 changed files with 194 additions and 143 deletions

View File

@@ -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] == "-?")
{

View File

@@ -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<CueTrack>();
// 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<CueTrack>();
// 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<CueIndex>()
{
new CueIndex("01", "00:00:00"),
};
}
// Add the track to the file
@@ -2311,7 +2307,7 @@ namespace DICUI.Aaru
}
}
return string.Empty;
return null;
}
/// <summary>
@@ -2320,6 +2316,7 @@ namespace DICUI.Aaru
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <param name="basePath">Base path for determining file names</param>
/// <returns>String containing the datfile, null on error</returns>
/// 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 += $"<rom name=\"{trackName}\" size=\"{size}\" crc=\"{crc32}\" md5=\"{md5}\" sha1=\"{sha1}\" />\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;
}
/// <summary>
/// Generate a track name based on current path and tracks
/// </summary>
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;
}
/// <summary>
/// Generate a Redump-compatible PVD block based on CICM sidecar file
/// </summary>
@@ -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;
}
/// <summary>
/// Generate the byte array representing the current PVD information
/// </summary>
/// <param name="opticalDisc">OpticalDisc type from CICM Sidecar data</param>
/// <returns>Byte array representing the PVD, null on error</returns>
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<byte> pvdData = new List<byte>();
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();
}
/// <summary>
/// Generate the required bytes from a DateTime object
/// </summary>
/// <param name="dateTime">DateTime to get representation of</param>
/// <returns>Byte array representing the DateTime</returns>
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<byte> dateTimeList = dateTimeString.ToCharArray().Select(c => (byte)c).ToList();
dateTimeList.Add(timeZoneNumber);
return dateTimeList.ToArray();
}
/// <summary>
/// Generate a single PVD line from a byte array
/// </summary>
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;
}
/// <summary>
/// Read the CICM Sidecar as an object
/// </summary>