diff --git a/DiscImageChef.Decoders/ChangeLog b/DiscImageChef.Decoders/ChangeLog index fbd295f6..2afd564d 100644 --- a/DiscImageChef.Decoders/ChangeLog +++ b/DiscImageChef.Decoders/ChangeLog @@ -1,3 +1,8 @@ +2015-11-24 Natalia Portillo + + * SCSI/MMC/DiscInformation.cs: + Added decoding and prettifying READ DISC INFORMATION. + 2015-11-24 Natalia Portillo * CD/ATIP.cs: diff --git a/DiscImageChef.Decoders/SCSI/MMC/DiscInformation.cs b/DiscImageChef.Decoders/SCSI/MMC/DiscInformation.cs index f223f2c9..c0e3b0df 100644 --- a/DiscImageChef.Decoders/SCSI/MMC/DiscInformation.cs +++ b/DiscImageChef.Decoders/SCSI/MMC/DiscInformation.cs @@ -36,6 +36,7 @@ // ****************************************************************************/ // //$Id$ using System; +using System.Text; namespace DiscImageChef.Decoders.SCSI.MMC { @@ -92,17 +93,17 @@ namespace DiscImageChef.Decoders.SCSI.MMC /// Byte 9 (MSB) and byte 4 (LSB) /// Number of sessions /// - public byte Sessions; + public UInt16 Sessions; /// /// Byte 10 (MSB) and byte 5 (LSB) /// Number of first track in last session /// - public byte FirstTrackLastSession; + public UInt16 FirstTrackLastSession; /// /// Byte 11 (MSB) and byte 6 (LSB) /// Number of last track in last session /// - public byte LastTrackLastSession; + public UInt16 LastTrackLastSession; /// /// Byte 7, bit 7 /// If set, DiscIdentification is valid @@ -276,6 +277,288 @@ namespace DiscImageChef.Decoders.SCSI.MMC /// public UInt32 RemainingPOWUpdates; } + + public static StandardDiscInformation? Decode000b(byte[] response) + { + if (response.Length < 34) + return null; + + if ((response[2] & 0xE0) != 0) + return null; + + StandardDiscInformation decoded = new StandardDiscInformation(); + decoded.DataLength = (ushort)((response[0] << 8) + response[1]); + + if ((decoded.DataLength + 2) != response.Length) + return null; + + decoded.DataType = (byte)((response[2] & 0xE0) >> 5); + decoded.Erasable |= (response[2] & 0x10) == 0x10; + decoded.LastSessionStatus = (byte)((response[2] & 0x0C) >> 2); + decoded.DiscStatus = (byte)(response[2] & 0x03); + decoded.FirstTrackNumber = response[3]; + decoded.Sessions = (ushort)((response[9] << 8) + response[4]); + decoded.FirstTrackLastSession = (ushort)((response[10] << 8) + response[5]); + decoded.LastTrackLastSession = (ushort)((response[11] << 8) + response[6]); + + decoded.DID_V |= (response[7] & 0x80) == 0x80; + decoded.DBC_V |= (response[7] & 0x40) == 0x40; + decoded.URU |= (response[7] & 0x20) == 0x20; + decoded.DAC_V |= (response[7] & 0x10) == 0x10; + decoded.Reserved |= (response[7] & 0x08) == 0x08; + decoded.Dbit |= (response[7] & 0x04) == 0x04; + decoded.BGFormatStatus = (byte)(response[7] & 0x03); + + decoded.DiscIdentification = (uint)((response[12] << 24) + (response[13] << 16) + + (response[14] << 8) + response[15]); + decoded.LastSessionLeadInStartLBA = (uint)((response[16] << 24) + (response[17] << 16) + + (response[18] << 8) + response[19]); + decoded.LastPossibleLeadOutStartLBA = (uint)((response[20] << 24) + (response[21] << 16) + + (response[22] << 8) + response[23]); + + byte[] temp = new byte[8]; + Array.Copy(response, 24, temp, 0, 8); + Array.Reverse(temp); + decoded.DiscBarcode = BitConverter.ToUInt64(temp, 0); + + decoded.DiscApplicationCode = response[32]; + decoded.OPCTablesNumber = response[33]; + + if (decoded.OPCTablesNumber > 0 && response.Length == (decoded.OPCTablesNumber * 8) + 34) + { + decoded.OPCTables = new OPCTable[decoded.OPCTablesNumber]; + for (int i = 0; i < decoded.OPCTablesNumber; i++) + { + decoded.OPCTables[i].Speed = (ushort)((response[34 + i * 8 + 0] << 16) + response[34 + i * 8 + 1]); + decoded.OPCTables[i].OPCValues = new byte[6]; + Array.Copy(response, 34 + i * 8 + 2, decoded.OPCTables[i].OPCValues, 0, 6); + } + } + + return decoded; + } + + public static string Prettify000b(StandardDiscInformation? information) + { + if (!information.HasValue) + return null; + + StandardDiscInformation decoded = information.Value; + + if (decoded.DataType != 0) + return null; + + StringBuilder sb = new StringBuilder(); + + switch (decoded.DiscType) + { + case 0x00: + sb.AppendLine("Disc type declared as CD-DA or CD-ROM"); + break; + case 0x10: + sb.AppendLine("Disc type declared as CD-i"); + break; + case 0x20: + sb.AppendLine("Disc type declared as CD-ROM XA"); + break; + case 0xFF: + sb.AppendLine("Disc type is undefined"); + break; + default: + sb.AppendFormat("Unknown disc type {0:X2}h", decoded.DiscType).AppendLine(); + break; + } + + switch (decoded.DiscStatus) + { + case 0: + sb.AppendLine("Disc is empty"); + break; + case 1: + sb.AppendLine("Disc is incomplete"); + break; + case 2: + sb.AppendLine("Disc is finalized"); + break; + } + + if (decoded.Erasable) + sb.AppendLine("Disc is erasable"); + + switch (decoded.LastSessionStatus) + { + case 0: + sb.AppendLine("Last session is empty"); + break; + case 1: + sb.AppendLine("Last session is incomplete"); + break; + case 2: + sb.AppendLine("Last session is damaged"); + break; + case 3: + sb.AppendLine("Last session is complete"); + break; + } + + switch (decoded.BGFormatStatus) + { + case 1: + sb.AppendLine("Media was being formatted in the background but it is stopped and incomplete"); + break; + case 2: + sb.AppendLine("Media is currently being formatted in the background"); + break; + case 3: + sb.AppendLine("Media background formatting has completed"); + break; + } + + if (decoded.Dbit) + sb.AppendLine("MRW is dirty"); + + sb.AppendFormat("First track on disc is track {0}", decoded.FirstTrackNumber).AppendLine(); + sb.AppendFormat("Disc has {0} sessions", decoded.Sessions).AppendLine(); + sb.AppendFormat("First track in last session is track {0}", decoded.FirstTrackLastSession).AppendLine(); + sb.AppendFormat("Last track in last session is track {0}", decoded.LastTrackLastSession).AppendLine(); + sb.AppendFormat("Last session Lead-In address is {0} (as LBA) or {1:X2}:{2:X2}:{3:X2}", decoded.LastSessionLeadInStartLBA, + (decoded.LastSessionLeadInStartLBA & 0xFF0000) >> 16, + (decoded.LastSessionLeadInStartLBA & 0xFF00) >> 8, + (decoded.LastSessionLeadInStartLBA & 0xFF)).AppendLine(); + sb.AppendFormat("Last possible Lead-Out address is {0} (as LBA) or {1:X2}:{2:X2}:{3:X2}", decoded.LastPossibleLeadOutStartLBA, + (decoded.LastPossibleLeadOutStartLBA & 0xFF0000) >> 16, + (decoded.LastPossibleLeadOutStartLBA & 0xFF00) >> 8, + (decoded.LastPossibleLeadOutStartLBA & 0xFF)).AppendLine(); + + if (decoded.URU) + sb.AppendLine("Disc is defined for unrestricted use"); + else + sb.AppendLine("Disc is defined for restricted use"); + + if (decoded.DID_V) + sb.AppendFormat("Disc ID: {0:X8}", decoded.DiscIdentification).AppendLine(); + if (decoded.DBC_V) + sb.AppendFormat("Disc barcode: {0:X16}", decoded.DiscBarcode).AppendLine(); + if (decoded.DAC_V) + sb.AppendFormat("Disc application code: {0}", decoded.DiscApplicationCode).AppendLine(); + + if (decoded.OPCTables != null) + { + foreach (OPCTable table in decoded.OPCTables) + { + sb.AppendFormat("OPC values for {0}Kbit/sec.: {1}, {2}, {3}, {4}, {5}, {6}", table.Speed, + table.OPCValues[0], table.OPCValues[1], table.OPCValues[2], + table.OPCValues[3], table.OPCValues[4], table.OPCValues[5]).AppendLine(); + } + } + + return sb.ToString(); + } + + public static TrackResourcesInformation? Decode001b(byte[] response) + { + if (response.Length != 12) + return null; + + if ((response[2] & 0xE0) != 0x20) + return null; + + TrackResourcesInformation decoded = new TrackResourcesInformation(); + decoded.DataLength = (ushort)((response[0] << 8) + response[1]); + + if ((decoded.DataLength + 2) != response.Length) + return null; + + decoded.DataType = (byte)((response[2] & 0xE0) >> 5); + decoded.MaxTracks = (ushort)((response[4] << 8) + response[5]); + decoded.AssignedTracks = (ushort)((response[6] << 8) + response[7]); + decoded.MaxAppendableTracks = (ushort)((response[8] << 8) + response[9]); + decoded.AppendableTracks = (ushort)((response[10] << 8) + response[11]); + + return decoded; + } + + public static string Prettify001b(TrackResourcesInformation? information) + { + if (!information.HasValue) + return null; + + TrackResourcesInformation decoded = information.Value; + + if (decoded.DataType != 1) + return null; + + StringBuilder sb = new StringBuilder(); + + sb.AppendFormat("{0} maximum possible tracks on the disc", decoded.MaxTracks).AppendLine(); + sb.AppendFormat("{0} assigned tracks on the disc", decoded.AssignedTracks).AppendLine(); + sb.AppendFormat("{0} maximum possible appendable tracks on the disc", decoded.AppendableTracks).AppendLine(); + sb.AppendFormat("{0} current appendable tracks on the disc", decoded.MaxAppendableTracks).AppendLine(); + + return sb.ToString(); + } + + public static POWResourcesInformation? Decode010b(byte[] response) + { + if (response.Length != 16) + return null; + + if ((response[2] & 0xE0) != 0x40) + return null; + + POWResourcesInformation decoded = new POWResourcesInformation(); + decoded.DataLength = (ushort)((response[0] << 8) + response[1]); + + if ((decoded.DataLength + 2) != response.Length) + return null; + + decoded.DataType = (byte)((response[2] & 0xE0) >> 5); + decoded.RemainingPOWReplacements = (ushort)((response[4] << 24) + (response[5] << 16) + (response[6] << 8) + response[7]); + decoded.RemainingPOWReallocation = (ushort)((response[8] << 24) + (response[9] << 16) + (response[10] << 8) + response[11]); + decoded.RemainingPOWUpdates = (ushort)((response[12] << 24) + (response[13] << 16) + (response[14] << 8) + response[15]); + + return decoded; + } + + public static string Prettify010b(POWResourcesInformation? information) + { + if (!information.HasValue) + return null; + + POWResourcesInformation decoded = information.Value; + + if (decoded.DataType != 1) + return null; + + StringBuilder sb = new StringBuilder(); + + sb.AppendFormat("{0} remaining POW replacements", decoded.RemainingPOWReplacements).AppendLine(); + sb.AppendFormat("{0} remaining POW reallocation map entries", decoded.RemainingPOWReallocation).AppendLine(); + sb.AppendFormat("{0} remaining POW updates", decoded.RemainingPOWUpdates).AppendLine(); + + return sb.ToString(); + } + + public static string Prettify(byte[] response) + { + if (response == null) + return null; + + if (response.Length < 12) + return null; + + switch (response[2] & 0xE0) + { + case 0x00: + return Prettify000b(Decode000b(response)); + case 0x20: + return Prettify001b(Decode001b(response)); + case 0x40: + return Prettify010b(Decode010b(response)); + } + + return null; + } } } diff --git a/DiscImageChef/ChangeLog b/DiscImageChef/ChangeLog index 020eeb0e..4fcae4d6 100644 --- a/DiscImageChef/ChangeLog +++ b/DiscImageChef/ChangeLog @@ -1,3 +1,8 @@ +2015-11-24 Natalia Portillo + + * Commands/MediaInfo.cs: + Added decoding and prettifying READ DISC INFORMATION. + 2015-11-24 Natalia Portillo * Commands/MediaInfo.cs: diff --git a/DiscImageChef/Commands/MediaInfo.cs b/DiscImageChef/Commands/MediaInfo.cs index 35e0c578..38ea5374 100644 --- a/DiscImageChef/Commands/MediaInfo.cs +++ b/DiscImageChef/Commands/MediaInfo.cs @@ -286,20 +286,26 @@ namespace DiscImageChef.Commands DicConsole.ErrorWriteLine("READ DISC INFORMATION 000b\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); else { + DicConsole.WriteLine("Standard Disc Information:\n{0}", Decoders.SCSI.MMC.DiscInformation.Prettify(cmdBuf)); doWriteFile(outputPrefix, "_readdiscinformation_000b.bin", "SCSI READ DISC INFORMATION", cmdBuf); } sense = dev.ReadDiscInformation(out cmdBuf, out senseBuf, MmcDiscInformationDataTypes.TrackResources, dev.Timeout, out duration); - if(sense) + if (sense) DicConsole.ErrorWriteLine("READ DISC INFORMATION 001b\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); else + { + DicConsole.WriteLine("Track Resources Information:\n{0}", Decoders.SCSI.MMC.DiscInformation.Prettify(cmdBuf)); doWriteFile(outputPrefix, "_readdiscinformation_001b.bin", "SCSI READ DISC INFORMATION", cmdBuf); - + } sense = dev.ReadDiscInformation(out cmdBuf, out senseBuf, MmcDiscInformationDataTypes.POWResources, dev.Timeout, out duration); - if(sense) + if (sense) DicConsole.ErrorWriteLine("READ DISC INFORMATION 010b\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); else + { + DicConsole.WriteLine("POW Resources Information:\n{0}", Decoders.SCSI.MMC.DiscInformation.Prettify(cmdBuf)); doWriteFile(outputPrefix, "_readdiscinformation_010b.bin", "SCSI READ DISC INFORMATION", cmdBuf); + } sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSVolId, 0, dev.Timeout, out duration); if(sense)