From bd0d0c08060c2231ba62115fb1a766ba03d92903 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Mon, 19 Oct 2015 02:40:30 +0100 Subject: [PATCH] Separated CD decoders by type. --- CD/ATIP.cs | 381 ++++++++ CD/CD.cs | 1603 --------------------------------- CD/CDTextOnLeadIn.cs | 393 ++++++++ CD/Enums.cs | 98 ++ CD/FullTOC.cs | 374 ++++++++ CD/PMA.cs | 227 +++++ CD/Session.cs | 235 +++++ CD/TOC.cs | 235 +++++ ChangeLog | 13 + DiscImageChef.Decoders.csproj | 8 +- 10 files changed, 1963 insertions(+), 1604 deletions(-) create mode 100644 CD/ATIP.cs delete mode 100644 CD/CD.cs create mode 100644 CD/CDTextOnLeadIn.cs create mode 100644 CD/Enums.cs create mode 100644 CD/FullTOC.cs create mode 100644 CD/PMA.cs create mode 100644 CD/Session.cs create mode 100644 CD/TOC.cs diff --git a/CD/ATIP.cs b/CD/ATIP.cs new file mode 100644 index 0000000..1e0887c --- /dev/null +++ b/CD/ATIP.cs @@ -0,0 +1,381 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : ATIP.cs +// Version : 1.0 +// Author(s) : Natalia Portillo +// +// Component : Component +// +// Revision : $Revision$ +// Last change by : $Author$ +// Date : $Date$ +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright (C) 2011-2015 Claunia.com +// ****************************************************************************/ +// //$Id$ +using System; +using DiscImageChef.Console; +using System.Text; + +namespace DiscImageChef.Decoders.CD +{ + /// + /// Information from the following standards: + /// ANSI X3.304-1997 + /// T10/1048-D revision 9.0 + /// T10/1048-D revision 10a + /// T10/1228-D revision 7.0c + /// T10/1228-D revision 11a + /// T10/1363-D revision 10g + /// T10/1545-D revision 1d + /// T10/1545-D revision 5 + /// T10/1545-D revision 5a + /// T10/1675-D revision 2c + /// T10/1675-D revision 4 + /// T10/1836-D revision 2g + /// + public static class ATIP + { + public struct CDATIP + { + /// + /// Bytes 1 to 0 + /// Total size of returned session information minus this field + /// + public UInt16 DataLength; + /// + /// Byte 2 + /// Reserved + /// + public byte Reserved1; + /// + /// Byte 3 + /// Reserved + /// + public byte Reserved2; + /// + /// Byte 4, bits 7 to 4 + /// Indicative target writing power + /// + public byte ITWP; + /// + /// Byte 4, bit 3 + /// Set if DDCD + /// + public bool DDCD; + /// + /// Byte 4, bits 2 to 0 + /// Reference speed + /// + public byte ReferenceSpeed; + /// + /// Byte 5, bit 7 + /// Always unset + /// + public bool AlwaysZero; + /// + /// Byte 5, bit 6 + /// Unrestricted media + /// + public bool URU; + /// + /// Byte 5, bits 5 to 0 + /// Reserved + /// + public byte Reserved3; + /// + /// Byte 6, bit 7 + /// Always set + /// + public bool AlwaysOne; + /// + /// Byte 6, bit 6 + /// Set if rewritable (CD-RW or DDCD-RW) + /// + public bool DiscType; + /// + /// Byte 6, bits 5 to 3 + /// Disc subtype + /// + public byte DiscSubType; + /// + /// Byte 6, bit 2 + /// A1 values are valid + /// + public bool A1Valid; + /// + /// Byte 6, bit 1 + /// A2 values are valid + /// + public bool A2Valid; + /// + /// Byte 6, bit 0 + /// A3 values are valid + /// + public bool A3Valid; + /// + /// Byte 7 + /// Reserved + /// + public byte Reserved4; + /// + /// Byte 8 + /// ATIP Start time of Lead-In (Minute) + /// + public byte LeadInStartMin; + /// + /// Byte 9 + /// ATIP Start time of Lead-In (Second) + /// + public byte LeadInStartSec; + /// + /// Byte 10 + /// ATIP Start time of Lead-In (Frame) + /// + public byte LeadInStartFrame; + /// + /// Byte 11 + /// Reserved + /// + public byte Reserved5; + /// + /// Byte 12 + /// ATIP Last possible start time of Lead-Out (Minute) + /// + public byte LeadOutStartMin; + /// + /// Byte 13 + /// ATIP Last possible start time of Lead-Out (Second) + /// + public byte LeadOutStartSec; + /// + /// Byte 14 + /// ATIP Last possible start time of Lead-Out (Frame) + /// + public byte LeadOutStartFrame; + /// + /// Byte 15 + /// Reserved + /// + public byte Reserved6; + /// + /// Bytes 16 to 18 + /// A1 values + /// + public byte[] A1Values; + /// + /// Byte 19 + /// Reserved + /// + public byte Reserved7; + /// + /// Bytes 20 to 22 + /// A2 values + /// + public byte[] A2Values; + /// + /// Byte 23 + /// Reserved + /// + public byte Reserved8; + /// + /// Bytes 24 to 26 + /// A3 values + /// + public byte[] A3Values; + /// + /// Byte 27 + /// Reserved + /// + public byte Reserved9; + /// + /// Bytes 28 to 30 + /// S4 values + /// + public byte[] S4Values; + /// + /// Byte 31 + /// Reserved + /// + public byte Reserved10; + } + + public static CDATIP? DecodeCDATIP(byte[] CDATIPResponse) + { + if (CDATIPResponse == null) + return null; + + CDATIP decoded = new CDATIP(); + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + + if (CDATIPResponse.Length != 32) + { + DicConsole.DebugWriteLine("CD ATIP decoder", "Expected CD ATIP size (32 bytes) is not received size ({0} bytes), not decoding", CDATIPResponse.Length); + return null; + } + + decoded.DataLength = BigEndianBitConverter.ToUInt16(CDATIPResponse, 0); + decoded.Reserved1 = CDATIPResponse[2]; + decoded.Reserved2 = CDATIPResponse[3]; + decoded.ITWP = (byte)((CDATIPResponse[4] & 0xF0) >> 4); + decoded.DDCD = Convert.ToBoolean(CDATIPResponse[4] & 0x08); + decoded.ReferenceSpeed = (byte)(CDATIPResponse[4] & 0x07); + decoded.AlwaysZero = Convert.ToBoolean(CDATIPResponse[5] & 0x80); + decoded.URU = Convert.ToBoolean(CDATIPResponse[5] & 0x40); + decoded.Reserved3 = (byte)(CDATIPResponse[5] & 0x3F); + + decoded.AlwaysOne = Convert.ToBoolean(CDATIPResponse[6] & 0x80); + decoded.DiscType = Convert.ToBoolean(CDATIPResponse[6] & 0x40); + decoded.DiscSubType = (byte)((CDATIPResponse[6] & 0x38) >> 3); + decoded.A1Valid = Convert.ToBoolean(CDATIPResponse[6] & 0x04); + decoded.A2Valid = Convert.ToBoolean(CDATIPResponse[6] & 0x02); + decoded.A3Valid = Convert.ToBoolean(CDATIPResponse[6] & 0x01); + + decoded.Reserved4 = CDATIPResponse[7]; + decoded.LeadInStartMin = CDATIPResponse[8]; + decoded.LeadInStartSec = CDATIPResponse[9]; + decoded.LeadInStartFrame = CDATIPResponse[10]; + decoded.Reserved5 = CDATIPResponse[11]; + decoded.LeadOutStartMin = CDATIPResponse[12]; + decoded.LeadOutStartSec = CDATIPResponse[13]; + decoded.LeadOutStartFrame = CDATIPResponse[14]; + decoded.Reserved6 = CDATIPResponse[15]; + + decoded.A1Values = new byte[3]; + decoded.A2Values = new byte[3]; + decoded.A3Values = new byte[3]; + decoded.S4Values = new byte[3]; + + Array.Copy(CDATIPResponse, 16, decoded.A1Values, 0, 3); + Array.Copy(CDATIPResponse, 20, decoded.A1Values, 0, 3); + Array.Copy(CDATIPResponse, 24, decoded.A1Values, 0, 3); + Array.Copy(CDATIPResponse, 28, decoded.A1Values, 0, 3); + + decoded.Reserved7 = CDATIPResponse[19]; + decoded.Reserved8 = CDATIPResponse[23]; + decoded.Reserved9 = CDATIPResponse[27]; + decoded.Reserved10 = CDATIPResponse[31]; + + return decoded; + } + + public static string PrettifyCDATIP(CDATIP? CDATIPResponse) + { + if (CDATIPResponse == null) + return null; + + CDATIP response = CDATIPResponse.Value; + + StringBuilder sb = new StringBuilder(); + + if (response.DDCD) + { + sb.AppendFormat("Indicative Target Writing Power: 0x{0:X2}", response.ITWP).AppendLine(); + if (response.DiscType) + sb.AppendLine("Disc is DDCD-RW"); + else + sb.AppendLine("Disc is DDCD-R"); + switch (response.ReferenceSpeed) + { + case 2: + sb.AppendLine("Reference speed is 4x"); + break; + case 3: + sb.AppendLine("Reference speed is 8x"); + break; + default: + sb.AppendFormat("Reference speed set is unknown: {0}", response.ReferenceSpeed).AppendLine(); + break; + } + sb.AppendFormat("ATIP Start time of Lead-in: 0x{0:X6}", (response.LeadInStartMin << 16) + (response.LeadInStartSec << 8) + response.LeadInStartFrame).AppendLine(); + sb.AppendFormat("ATIP Last possible start time of Lead-out: 0x{0:X6}", (response.LeadOutStartMin << 16) + (response.LeadOutStartSec << 8) + response.LeadOutStartFrame).AppendLine(); + sb.AppendFormat("S4 value: 0x{0:X6}", (response.S4Values[0] << 16) + (response.S4Values[1] << 8) + response.S4Values[2]).AppendLine(); + } + else + { + sb.AppendFormat("Indicative Target Writing Power: 0x{0:X2}", response.ITWP & 0x07).AppendLine(); + if (response.DiscType) + { + switch (response.DiscSubType) + { + case 0: + sb.AppendLine("Disc is CD-RW"); + break; + case 1: + sb.AppendLine("Disc is High-Speed CD-RW"); + break; + default: + sb.AppendFormat("Unknown CD-RW disc subtype: {0}", response.DiscSubType).AppendLine(); + break; + } + switch (response.ReferenceSpeed) + { + case 1: + sb.AppendLine("Reference speed is 2x"); + break; + default: + sb.AppendFormat("Reference speed set is unknown: {0}", response.ReferenceSpeed).AppendLine(); + break; + } + } + else + { + sb.AppendLine("Disc is CD-R"); + switch (response.DiscSubType) + { + default: + sb.AppendFormat("Unknown CD-R disc subtype: {0}", response.DiscSubType).AppendLine(); + break; + } + } + + if (response.URU) + sb.AppendLine("Disc use is unrestricted"); + else + sb.AppendLine("Disc use is restricted"); + + sb.AppendFormat("ATIP Start time of Lead-in: {0:X2}:{1:X2}:{2:X2}", response.LeadInStartMin, response.LeadInStartSec, response.LeadInStartFrame).AppendLine(); + sb.AppendFormat("ATIP Last possible start time of Lead-out: {0:X2}:{1:X2}:{2:X2}", response.LeadOutStartMin, response.LeadOutStartSec, response.LeadOutStartFrame).AppendLine(); + if(response.A1Valid) + sb.AppendFormat("A1 value: 0x{0:X6}", (response.A1Values[0] << 16) + (response.A1Values[1] << 8) + response.A1Values[2]).AppendLine(); + if(response.A2Valid) + sb.AppendFormat("A2 value: 0x{0:X6}", (response.A2Values[0] << 16) + (response.A2Values[1] << 8) + response.A2Values[2]).AppendLine(); + if(response.A3Valid) + sb.AppendFormat("A3 value: 0x{0:X6}", (response.A3Values[0] << 16) + (response.A3Values[1] << 8) + response.A3Values[2]).AppendLine(); + sb.AppendFormat("S4 value: 0x{0:X6}", (response.S4Values[0] << 16) + (response.S4Values[1] << 8) + response.S4Values[2]).AppendLine(); + } + + return sb.ToString(); + } + + public static string PrettifyCDATIP(byte[] CDATIPResponse) + { + CDATIP? decoded = DecodeCDATIP(CDATIPResponse); + return PrettifyCDATIP(decoded); + } + } +} + diff --git a/CD/CD.cs b/CD/CD.cs deleted file mode 100644 index db2b64b..0000000 --- a/CD/CD.cs +++ /dev/null @@ -1,1603 +0,0 @@ -/*************************************************************************** -The Disc Image Chef ----------------------------------------------------------------------------- - -Filename : CD.cs -Version : 1.0 -Author(s) : Natalia Portillo - -Component : Decoders. - -Revision : $Revision$ -Last change by : $Author$ -Date : $Date$ - ---[ Description ] ---------------------------------------------------------- - -Decodes CD and DDCD structures. - ---[ License ] -------------------------------------------------------------- - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as - published by the Free Software Foundation, either version 3 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - ----------------------------------------------------------------------------- -Copyright (C) 2011-2014 Claunia.com -****************************************************************************/ -//$Id$ -using System; -using System.Text; -using DiscImageChef.Console; - -namespace DiscImageChef.Decoders -{ - /// - /// Information from the following standards: - /// ANSI X3.304-1997 - /// T10/1048-D revision 9.0 - /// T10/1048-D revision 10a - /// T10/1228-D revision 7.0c - /// T10/1228-D revision 11a - /// T10/1363-D revision 10g - /// T10/1545-D revision 1d - /// T10/1545-D revision 5 - /// T10/1545-D revision 5a - /// T10/1675-D revision 2c - /// T10/1675-D revision 4 - /// T10/1836-D revision 2g - /// - public static class CD - { - #region Enumerations - - public enum TOC_ADR : byte - { - /// - /// Q Sub-channel mode information not supplied - /// - NoInformation = 0x00, - /// - /// Q Sub-channel encodes current position data - /// - CurrentPosition = 0x01, - /// - /// Q Sub-channel encodes the media catalog number - /// - MediaCatalogNumber = 0x02, - /// - /// Q Sub-channel encodes the ISRC - /// - ISRC = 0x03 - } - - public enum TOC_CONTROL : byte - { - /// - /// Stereo audio, no pre-emphasis - /// - TwoChanNoPreEmph = 0x00, - /// - /// Stereo audio with pre-emphasis - /// - TwoChanPreEmph = 0x01, - /// - /// If mask applied, track can be copied - /// - CopyPermissionMask = 0x02, - /// - /// Data track, recorded uninterrumpted - /// - DataTrack = 0x04, - /// - /// Data track, recorded incrementally - /// - DataTrackIncremental = 0x05, - /// - /// Quadraphonic audio, no pre-emphasis - /// - FourChanNoPreEmph = 0x08, - /// - /// Quadraphonic audio with pre-emphasis - /// - FourChanPreEmph = 0x09, - /// - /// Reserved mask - /// - ReservedMask = 0x0C - } - - public enum CDTextPackTypeIndicator : byte - { - /// - /// Title of the track (or album if track == 0) - /// - Title = 0x80, - /// - /// Performer - /// - Performer = 0x81, - /// - /// Songwriter - /// - Songwriter = 0x82, - /// - /// Composer - /// - Composer = 0x83, - /// - /// Arranger - /// - Arranger = 0x84, - /// - /// Message from the content provider or artist - /// - Message = 0x85, - /// - /// Disc identification information - /// - DiscIdentification = 0x86, - /// - /// Genre identification - /// - GenreIdentification = 0x87, - /// - /// Table of content information - /// - TOCInformation = 0x88, - /// - /// Second table of content information - /// - SecondTOCInformation = 0x89, - /// - /// Reserved - /// - Reserved1 = 0x8A, - /// - /// Reserved - /// - Reserved2 = 0x8B, - /// - /// Reserved - /// - Reserved3 = 0x8C, - /// - /// Reserved for content provider only - /// - ReservedForContentProvider = 0x8D, - /// - /// UPC of album or ISRC of track - /// - UPCorISRC = 0x8E, - /// - /// Size information of the block - /// - BlockSizeInformation = 0x8F - } - - #endregion Enumerations - - #region Public methods - - public static CDTOC? DecodeCDTOC(byte[] CDTOCResponse) - { - if (CDTOCResponse == null) - return null; - - CDTOC decoded = new CDTOC(); - - BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; - - decoded.DataLength = BigEndianBitConverter.ToUInt16(CDTOCResponse, 0); - decoded.FirstTrack = CDTOCResponse[2]; - decoded.LastTrack = CDTOCResponse[3]; - decoded.TrackDescriptors = new CDTOCTrackDataDescriptor[(decoded.DataLength - 2) / 8]; - - if (decoded.DataLength + 2 != CDTOCResponse.Length) - { - DicConsole.DebugWriteLine("CD TOC decoder", "Expected CDTOC size ({0} bytes) is not received size ({1} bytes), not decoding", decoded.DataLength + 2, CDTOCResponse.Length); - return null; - } - - for (int i = 0; i < ((decoded.DataLength - 2) / 8); i++) - { - decoded.TrackDescriptors[i].Reserved1 = CDTOCResponse[0 + i * 8 + 4]; - decoded.TrackDescriptors[i].ADR = (byte)((CDTOCResponse[1 + i * 8 + 4] & 0xF0) >> 4); - decoded.TrackDescriptors[i].CONTROL = (byte)(CDTOCResponse[1 + i * 8 + 4] & 0x0F); - decoded.TrackDescriptors[i].TrackNumber = CDTOCResponse[2 + i * 8 + 4]; - decoded.TrackDescriptors[i].Reserved2 = CDTOCResponse[3 + i * 8 + 4]; - decoded.TrackDescriptors[i].TrackStartAddress = BigEndianBitConverter.ToUInt32(CDTOCResponse, 4 + i * 8 + 4); - } - - return decoded; - } - - public static string PrettifyCDTOC(CDTOC? CDTOCResponse) - { - if (CDTOCResponse == null) - return null; - - CDTOC response = CDTOCResponse.Value; - - StringBuilder sb = new StringBuilder(); - - sb.AppendFormat("First track number in first complete session: {0}", response.FirstTrack).AppendLine(); - sb.AppendFormat("Last track number in last complete session: {0}", response.LastTrack).AppendLine(); - foreach (CDTOCTrackDataDescriptor descriptor in response.TrackDescriptors) - { - sb.AppendFormat("Track number: {0}", descriptor.TrackNumber); - sb.AppendFormat("Track starts at LBA {0}, or MSF {1:X2}:{2:X2}:{3:X2}", descriptor.TrackStartAddress, - (descriptor.TrackStartAddress & 0x0000FF00) >> 8, - (descriptor.TrackStartAddress & 0x00FF0000) >> 16, - (descriptor.TrackStartAddress & 0xFF000000) >> 24); - - switch ((TOC_ADR)descriptor.ADR) - { - case TOC_ADR.NoInformation: - sb.AppendLine("Q subchannel mode not given"); - break; - case TOC_ADR.CurrentPosition: - sb.AppendLine("Q subchannel stores current position"); - break; - case TOC_ADR.ISRC: - sb.AppendLine("Q subchannel stores ISRC"); - break; - case TOC_ADR.MediaCatalogNumber: - sb.AppendLine("Q subchannel stores media catalog number"); - break; - } - - if((descriptor.CONTROL & (byte)TOC_CONTROL.ReservedMask) == (byte)TOC_CONTROL.ReservedMask) - sb.AppendFormat("Reserved flags 0x{0:X2} set", descriptor.CONTROL).AppendLine(); - else - { - switch ((TOC_CONTROL)(descriptor.CONTROL & 0x0D)) - { - case TOC_CONTROL.TwoChanNoPreEmph: - sb.AppendLine("Stereo audio track with no pre-emphasis"); - break; - case TOC_CONTROL.TwoChanPreEmph: - sb.AppendLine("Stereo audio track with 50/15 μs pre-emphasis"); - break; - case TOC_CONTROL.FourChanNoPreEmph: - sb.AppendLine("Quadraphonic audio track with no pre-emphasis"); - break; - case TOC_CONTROL.FourChanPreEmph: - sb.AppendLine("Stereo audio track with 50/15 μs pre-emphasis"); - break; - case TOC_CONTROL.DataTrack: - sb.AppendLine("Data track, recorded uninterrupted"); - break; - case TOC_CONTROL.DataTrackIncremental: - sb.AppendLine("Data track, recorded incrementally"); - break; - } - - if ((descriptor.CONTROL & (byte)TOC_CONTROL.CopyPermissionMask) == (byte)TOC_CONTROL.CopyPermissionMask) - sb.AppendLine("Digital copy of track is permitted"); - else - sb.AppendLine("Digital copy of track is prohibited"); - - #if DEBUG - if(descriptor.Reserved1 != 0) - sb.AppendFormat("Reserved1 = 0x{0:X2}", descriptor.Reserved1).AppendLine(); - if(descriptor.Reserved2 != 0) - sb.AppendFormat("Reserved2 = 0x{0:X2}", descriptor.Reserved2).AppendLine(); - #endif - - sb.AppendLine(); - } - } - - return sb.ToString(); - } - - public static string PrettifyCDTOC(byte[] CDTOCResponse) - { - CDTOC? decoded = DecodeCDTOC(CDTOCResponse); - return PrettifyCDTOC(decoded); - } - - public static CDSessionInfo? DecodeCDSessionInfo(byte[] CDSessionInfoResponse) - { - if (CDSessionInfoResponse == null) - return null; - - CDSessionInfo decoded = new CDSessionInfo(); - - BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; - - decoded.DataLength = BigEndianBitConverter.ToUInt16(CDSessionInfoResponse, 0); - decoded.FirstCompleteSession = CDSessionInfoResponse[2]; - decoded.LastCompleteSession = CDSessionInfoResponse[3]; - decoded.TrackDescriptors = new CDSessionInfoTrackDataDescriptor[(decoded.DataLength - 2) / 8]; - - if (decoded.DataLength + 2 != CDSessionInfoResponse.Length) - { - DicConsole.DebugWriteLine("CD Session Info decoder", "Expected CDSessionInfo size ({0} bytes) is not received size ({1} bytes), not decoding", decoded.DataLength + 2, CDSessionInfoResponse.Length); - return null; - } - - for (int i = 0; i < ((decoded.DataLength - 2) / 8); i++) - { - decoded.TrackDescriptors[i].Reserved1 = CDSessionInfoResponse[0 + i * 8 + 4]; - decoded.TrackDescriptors[i].ADR = (byte)((CDSessionInfoResponse[1 + i * 8 + 4] & 0xF0) >> 4); - decoded.TrackDescriptors[i].CONTROL = (byte)(CDSessionInfoResponse[1 + i * 8 + 4] & 0x0F); - decoded.TrackDescriptors[i].TrackNumber = CDSessionInfoResponse[2 + i * 8 + 4]; - decoded.TrackDescriptors[i].Reserved2 = CDSessionInfoResponse[3 + i * 8 + 4]; - decoded.TrackDescriptors[i].TrackStartAddress = BigEndianBitConverter.ToUInt32(CDSessionInfoResponse, 4 + i * 8 + 4); - } - - return decoded; - } - - public static string PrettifyCDSessionInfo(CDSessionInfo? CDSessionInfoResponse) - { - if (CDSessionInfoResponse == null) - return null; - - CDSessionInfo response = CDSessionInfoResponse.Value; - - StringBuilder sb = new StringBuilder(); - - sb.AppendFormat("First complete session number: {0}", response.FirstCompleteSession).AppendLine(); - sb.AppendFormat("Last complete session number: {0}", response.LastCompleteSession).AppendLine(); - foreach (CDSessionInfoTrackDataDescriptor descriptor in response.TrackDescriptors) - { - sb.AppendFormat("First track number in last complete session: {0}", descriptor.TrackNumber); - sb.AppendFormat("Track starts at LBA {0}, or MSF {1:X2}:{2:X2}:{3:X2}", descriptor.TrackStartAddress, - (descriptor.TrackStartAddress & 0x0000FF00) >> 8, - (descriptor.TrackStartAddress & 0x00FF0000) >> 16, - (descriptor.TrackStartAddress & 0xFF000000) >> 24); - - switch ((TOC_ADR)descriptor.ADR) - { - case TOC_ADR.NoInformation: - sb.AppendLine("Q subchannel mode not given"); - break; - case TOC_ADR.CurrentPosition: - sb.AppendLine("Q subchannel stores current position"); - break; - case TOC_ADR.ISRC: - sb.AppendLine("Q subchannel stores ISRC"); - break; - case TOC_ADR.MediaCatalogNumber: - sb.AppendLine("Q subchannel stores media catalog number"); - break; - } - - if((descriptor.CONTROL & (byte)TOC_CONTROL.ReservedMask) == (byte)TOC_CONTROL.ReservedMask) - sb.AppendFormat("Reserved flags 0x{0:X2} set", descriptor.CONTROL).AppendLine(); - else - { - switch ((TOC_CONTROL)(descriptor.CONTROL & 0x0D)) - { - case TOC_CONTROL.TwoChanNoPreEmph: - sb.AppendLine("Stereo audio track with no pre-emphasis"); - break; - case TOC_CONTROL.TwoChanPreEmph: - sb.AppendLine("Stereo audio track with 50/15 μs pre-emphasis"); - break; - case TOC_CONTROL.FourChanNoPreEmph: - sb.AppendLine("Quadraphonic audio track with no pre-emphasis"); - break; - case TOC_CONTROL.FourChanPreEmph: - sb.AppendLine("Stereo audio track with 50/15 μs pre-emphasis"); - break; - case TOC_CONTROL.DataTrack: - sb.AppendLine("Data track, recorded uninterrupted"); - break; - case TOC_CONTROL.DataTrackIncremental: - sb.AppendLine("Data track, recorded incrementally"); - break; - } - - if ((descriptor.CONTROL & (byte)TOC_CONTROL.CopyPermissionMask) == (byte)TOC_CONTROL.CopyPermissionMask) - sb.AppendLine("Digital copy of track is permitted"); - else - sb.AppendLine("Digital copy of track is prohibited"); - - #if DEBUG - if(descriptor.Reserved1 != 0) - sb.AppendFormat("Reserved1 = 0x{0:X2}", descriptor.Reserved1).AppendLine(); - if(descriptor.Reserved2 != 0) - sb.AppendFormat("Reserved2 = 0x{0:X2}", descriptor.Reserved2).AppendLine(); - #endif - - sb.AppendLine(); - } - } - - return sb.ToString(); - } - - public static string PrettifyCDSessionInfo(byte[] CDSessionInfoResponse) - { - CDSessionInfo? decoded = DecodeCDSessionInfo(CDSessionInfoResponse); - return PrettifyCDSessionInfo(decoded); - } - - public static CDFullTOC? DecodeCDFullTOC(byte[] CDFullTOCResponse) - { - if (CDFullTOCResponse == null) - return null; - - CDFullTOC decoded = new CDFullTOC(); - - BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; - - decoded.DataLength = BigEndianBitConverter.ToUInt16(CDFullTOCResponse, 0); - decoded.FirstCompleteSession = CDFullTOCResponse[2]; - decoded.LastCompleteSession = CDFullTOCResponse[3]; - decoded.TrackDescriptors = new CDFullTOCInfoTrackDataDescriptor[(decoded.DataLength - 2) / 11]; - - if (decoded.DataLength + 2 != CDFullTOCResponse.Length) - { - DicConsole.DebugWriteLine("CD full TOC decoder", "Expected CDFullTOC size ({0} bytes) is not received size ({1} bytes), not decoding", decoded.DataLength + 2, CDFullTOCResponse.Length); - return null; - } - - for (int i = 0; i < ((decoded.DataLength - 2) / 11); i++) - { - decoded.TrackDescriptors[i].SessionNumber = CDFullTOCResponse[0 + i * 11 + 4]; - decoded.TrackDescriptors[i].ADR = (byte)((CDFullTOCResponse[1 + i * 11 + 4] & 0xF0) >> 4); - decoded.TrackDescriptors[i].CONTROL = (byte)(CDFullTOCResponse[1 + i * 11 + 4] & 0x0F); - decoded.TrackDescriptors[i].TNO = CDFullTOCResponse[2 + i * 11 + 4]; - decoded.TrackDescriptors[i].POINT = CDFullTOCResponse[3 + i * 11 + 4]; - decoded.TrackDescriptors[i].Min = CDFullTOCResponse[4 + i * 11 + 4]; - decoded.TrackDescriptors[i].Sec = CDFullTOCResponse[5 + i * 11 + 4]; - decoded.TrackDescriptors[i].Frame = CDFullTOCResponse[6 + i * 11 + 4]; - decoded.TrackDescriptors[i].Zero = CDFullTOCResponse[7 + i * 11 + 4]; - decoded.TrackDescriptors[i].HOUR = (byte)((CDFullTOCResponse[7 + i * 11 + 4] & 0xF0) >> 4); - decoded.TrackDescriptors[i].PHOUR = (byte)(CDFullTOCResponse[7 + i * 11 + 4] & 0x0F); - decoded.TrackDescriptors[i].PMIN = CDFullTOCResponse[8 + i * 11 + 4]; - decoded.TrackDescriptors[i].PSEC = CDFullTOCResponse[9 + i * 11 + 4]; - decoded.TrackDescriptors[i].PFRAME = CDFullTOCResponse[10 + i * 11 + 4]; - } - - return decoded; - } - - public static string PrettifyCDFullTOC(CDFullTOC? CDFullTOCResponse) - { - if (CDFullTOCResponse == null) - return null; - - CDFullTOC response = CDFullTOCResponse.Value; - - StringBuilder sb = new StringBuilder(); - - sb.AppendFormat("First complete session number: {0}", response.FirstCompleteSession).AppendLine(); - sb.AppendFormat("Last complete session number: {0}", response.LastCompleteSession).AppendLine(); - foreach (CDFullTOCInfoTrackDataDescriptor descriptor in response.TrackDescriptors) - { - if ((descriptor.CONTROL != 4 && descriptor.CONTROL != 6) || - (descriptor.ADR != 1 && descriptor.ADR != 5) || - descriptor.TNO != 0) - { - sb.AppendLine("Unknown TOC entry format, printing values as-is"); - sb.AppendFormat("SessionNumber = {0}", descriptor.SessionNumber).AppendLine(); - sb.AppendFormat("ADR = {0}", descriptor.ADR).AppendLine(); - sb.AppendFormat("CONTROL = {0}", descriptor.CONTROL).AppendLine(); - sb.AppendFormat("TNO = {0}", descriptor.TNO).AppendLine(); - sb.AppendFormat("POINT = {0}", descriptor.POINT).AppendLine(); - sb.AppendFormat("Min = {0}", descriptor.Min).AppendLine(); - sb.AppendFormat("Sec = {0}", descriptor.Sec).AppendLine(); - sb.AppendFormat("Frame = {0}", descriptor.Frame).AppendLine(); - sb.AppendFormat("HOUR = {0}", descriptor.HOUR).AppendLine(); - sb.AppendFormat("PHOUR = {0}", descriptor.PHOUR).AppendLine(); - sb.AppendFormat("PMIN = {0}", descriptor.PMIN).AppendLine(); - sb.AppendFormat("PSEC = {0}", descriptor.PSEC).AppendLine(); - sb.AppendFormat("PFRAME = {0}", descriptor.PFRAME).AppendLine(); - } - else - { - sb.AppendFormat("Session {0}", descriptor.SessionNumber).AppendLine(); - switch (descriptor.ADR) - { - case 1: - { - switch (descriptor.POINT) - { - case 0xA0: - { - sb.AppendFormat("First track number: {0}", descriptor.PMIN).AppendLine(); - sb.AppendFormat("Disc type: {0}", descriptor.PSEC).AppendLine(); - sb.AppendFormat("Absolute time: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); - break; - } - case 0xA1: - { - sb.AppendFormat("Last track number: {0}", descriptor.PMIN).AppendLine(); - sb.AppendFormat("Absolute time: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); - break; - } - case 0xA2: - { - sb.AppendFormat("Lead-out start position: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.PMIN, descriptor.PSEC, descriptor.PFRAME, descriptor.PHOUR).AppendLine(); - sb.AppendFormat("Absolute time: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); - break; - } - case 0xF0: - { - sb.AppendFormat("Book type: 0x{0:X2}", descriptor.PMIN); - sb.AppendFormat("Material type: 0x{0:X2}", descriptor.PSEC); - sb.AppendFormat("Moment of inertia: 0x{0:X2}", descriptor.PFRAME); - sb.AppendFormat("Absolute time: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); - break; - } - default: - { - if (descriptor.POINT >= 0x01 && descriptor.POINT <= 0x63) - { - sb.AppendFormat("Track start position for track {3}: {4:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.PMIN, descriptor.PSEC, descriptor.PFRAME, descriptor.POINT, descriptor.PHOUR).AppendLine(); - sb.AppendFormat("Absolute time: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); - } - else - { - sb.AppendFormat("ADR = {0}", descriptor.ADR).AppendLine(); - sb.AppendFormat("CONTROL = {0}", descriptor.CONTROL).AppendLine(); - sb.AppendFormat("TNO = {0}", descriptor.TNO).AppendLine(); - sb.AppendFormat("POINT = {0}", descriptor.POINT).AppendLine(); - sb.AppendFormat("Min = {0}", descriptor.Min).AppendLine(); - sb.AppendFormat("Sec = {0}", descriptor.Sec).AppendLine(); - sb.AppendFormat("Frame = {0}", descriptor.Frame).AppendLine(); - sb.AppendFormat("HOUR = {0}", descriptor.HOUR).AppendLine(); - sb.AppendFormat("PHOUR = {0}", descriptor.PHOUR).AppendLine(); - sb.AppendFormat("PMIN = {0}", descriptor.PMIN).AppendLine(); - sb.AppendFormat("PSEC = {0}", descriptor.PSEC).AppendLine(); - sb.AppendFormat("PFRAME = {0}", descriptor.PFRAME).AppendLine(); - } - break; - } - } - break; - } - case 5: - { - switch (descriptor.POINT) - { - case 0xB0: - { - sb.AppendFormat("Start of next possible program in the recordable area of the disc: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); - sb.AppendFormat("Maximum start of outermost Lead-out in the recordable area of the disc: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.PMIN, descriptor.PSEC, descriptor.PFRAME, descriptor.PHOUR).AppendLine(); - break; - } - case 0xB1: - { - sb.AppendFormat("Number of skip interval pointers: {0:X2}", descriptor.PMIN).AppendLine(); - sb.AppendFormat("Number of skip track pointers: {0:X2}", descriptor.PSEC).AppendLine(); - break; - } - case 0xB2: - case 0xB3: - case 0xB4: - { - sb.AppendFormat("Skip track {0}", descriptor.Min).AppendLine(); - sb.AppendFormat("Skip track {0}", descriptor.Sec).AppendLine(); - sb.AppendFormat("Skip track {0}", descriptor.Frame).AppendLine(); - sb.AppendFormat("Skip track {0}", descriptor.Zero).AppendLine(); - sb.AppendFormat("Skip track {0}", descriptor.PMIN).AppendLine(); - sb.AppendFormat("Skip track {0}", descriptor.PSEC).AppendLine(); - sb.AppendFormat("Skip track {0}", descriptor.PFRAME).AppendLine(); - break; - } - case 0xC0: - { - sb.AppendFormat("Optimum recording power: 0x{0:X2}", descriptor.Min).AppendLine(); - sb.AppendFormat("Start time of the first Lead-in area in the disc: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.PMIN, descriptor.PSEC, descriptor.PFRAME, descriptor.PHOUR).AppendLine(); - break; - } - case 0xC1: - { - sb.AppendFormat("Copy of information of A1 from ATIP found"); - sb.AppendFormat("Min = {0}", descriptor.Min).AppendLine(); - sb.AppendFormat("Sec = {0}", descriptor.Sec).AppendLine(); - sb.AppendFormat("Frame = {0}", descriptor.Frame).AppendLine(); - sb.AppendFormat("Zero = {0}", descriptor.Zero).AppendLine(); - sb.AppendFormat("PMIN = {0}", descriptor.PMIN).AppendLine(); - sb.AppendFormat("PSEC = {0}", descriptor.PSEC).AppendLine(); - sb.AppendFormat("PFRAME = {0}", descriptor.PFRAME).AppendLine(); - break; - } - case 0xCF: - { - sb.AppendFormat("Start position of outer part lead-in area: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.PMIN, descriptor.PSEC, descriptor.PFRAME, descriptor.PHOUR).AppendLine(); - sb.AppendFormat("Stop position of inner part lead-out area: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); - break; - } - default: - { - if (descriptor.POINT >= 0x01 && descriptor.POINT <= 0x40) - { - sb.AppendFormat("Start time for interval that should be skipped: {0:X2}:{1:X2}:{2:X2}", descriptor.PMIN, descriptor.PSEC, descriptor.PFRAME).AppendLine(); - sb.AppendFormat("Ending time for interval that should be skipped: {0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame).AppendLine(); - } - else - { - sb.AppendFormat("ADR = {0}", descriptor.ADR).AppendLine(); - sb.AppendFormat("CONTROL = {0}", descriptor.CONTROL).AppendLine(); - sb.AppendFormat("TNO = {0}", descriptor.TNO).AppendLine(); - sb.AppendFormat("POINT = {0}", descriptor.POINT).AppendLine(); - sb.AppendFormat("Min = {0}", descriptor.Min).AppendLine(); - sb.AppendFormat("Sec = {0}", descriptor.Sec).AppendLine(); - sb.AppendFormat("Frame = {0}", descriptor.Frame).AppendLine(); - sb.AppendFormat("HOUR = {0}", descriptor.HOUR).AppendLine(); - sb.AppendFormat("PHOUR = {0}", descriptor.PHOUR).AppendLine(); - sb.AppendFormat("PMIN = {0}", descriptor.PMIN).AppendLine(); - sb.AppendFormat("PSEC = {0}", descriptor.PSEC).AppendLine(); - sb.AppendFormat("PFRAME = {0}", descriptor.PFRAME).AppendLine(); - } - break; - } - } - break; - } - } - } - } - - return sb.ToString(); - } - - public static string PrettifyCDFullTOC(byte[] CDFullTOCResponse) - { - CDFullTOC? decoded = DecodeCDFullTOC(CDFullTOCResponse); - return PrettifyCDFullTOC(decoded); - } - - public static CDPMA? DecodeCDPMA(byte[] CDPMAResponse) - { - if (CDPMAResponse == null) - return null; - - CDPMA decoded = new CDPMA(); - - BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; - - decoded.DataLength = BigEndianBitConverter.ToUInt16(CDPMAResponse, 0); - decoded.Reserved1 = CDPMAResponse[2]; - decoded.Reserved2 = CDPMAResponse[3]; - decoded.PMADescriptors = new CDPMADescriptors[(decoded.DataLength - 2) / 11]; - - if (decoded.DataLength + 2 != CDPMAResponse.Length) - { - DicConsole.DebugWriteLine("CD PMA decoder", "Expected CDPMA size ({0} bytes) is not received size ({1} bytes), not decoding", decoded.DataLength + 2, CDPMAResponse.Length); - return null; - } - - for (int i = 0; i < ((decoded.DataLength - 2) / 11); i++) - { - decoded.PMADescriptors[i].Reserved = CDPMAResponse[0 + i * 11 + 4]; - decoded.PMADescriptors[i].ADR = (byte)((CDPMAResponse[1 + i * 11 + 4] & 0xF0) >> 4); - decoded.PMADescriptors[i].CONTROL = (byte)(CDPMAResponse[1 + i * 11 + 4] & 0x0F); - decoded.PMADescriptors[i].TNO = CDPMAResponse[2 + i * 11 + 4]; - decoded.PMADescriptors[i].POINT = CDPMAResponse[3 + i * 11 + 4]; - decoded.PMADescriptors[i].Min = CDPMAResponse[4 + i * 11 + 4]; - decoded.PMADescriptors[i].Sec = CDPMAResponse[5 + i * 11 + 4]; - decoded.PMADescriptors[i].Frame = CDPMAResponse[6 + i * 11 + 4]; - decoded.PMADescriptors[i].HOUR = (byte)((CDPMAResponse[7 + i * 11 + 4] & 0xF0) >> 4); - decoded.PMADescriptors[i].PHOUR = (byte)(CDPMAResponse[7 + i * 11 + 4] & 0x0F); - decoded.PMADescriptors[i].PMIN = CDPMAResponse[8 + i * 11 + 4]; - decoded.PMADescriptors[i].PSEC = CDPMAResponse[9 + i * 11 + 4]; - decoded.PMADescriptors[i].PFRAME = CDPMAResponse[10 + i * 11 + 4]; - } - - return decoded; - } - - public static string PrettifyCDPMA(CDPMA? CDPMAResponse) - { - if (CDPMAResponse == null) - return null; - - CDPMA response = CDPMAResponse.Value; - - StringBuilder sb = new StringBuilder(); - - #if DEBUG - if(response.Reserved1 != 0) - sb.AppendFormat("Reserved1 = 0x{0:X2}", response.Reserved1).AppendLine(); - if(response.Reserved2 != 0) - sb.AppendFormat("Reserved2 = 0x{0:X2}", response.Reserved2).AppendLine(); - #endif - - foreach (CDPMADescriptors descriptor in response.PMADescriptors) - { - #if DEBUG - if(descriptor.Reserved != 0) - sb.AppendFormat("Reserved = 0x{0:X2}", descriptor.Reserved).AppendLine(); - #endif - - sb.AppendFormat("ADR = {0}", descriptor.ADR).AppendLine(); - sb.AppendFormat("CONTROL = {0}", descriptor.CONTROL).AppendLine(); - sb.AppendFormat("TNO = {0}", descriptor.TNO).AppendLine(); - sb.AppendFormat("POINT = {0}", descriptor.POINT).AppendLine(); - sb.AppendFormat("Min = {0}", descriptor.Min).AppendLine(); - sb.AppendFormat("Sec = {0}", descriptor.Sec).AppendLine(); - sb.AppendFormat("Frame = {0}", descriptor.Frame).AppendLine(); - sb.AppendFormat("HOUR = {0}", descriptor.HOUR).AppendLine(); - sb.AppendFormat("PHOUR = {0}", descriptor.PHOUR).AppendLine(); - sb.AppendFormat("PMIN = {0}", descriptor.PMIN).AppendLine(); - sb.AppendFormat("PSEC = {0}", descriptor.PSEC).AppendLine(); - sb.AppendFormat("PFRAME = {0}", descriptor.PFRAME).AppendLine(); - } - - return sb.ToString(); - } - - public static string PrettifyCDPMA(byte[] CDPMAResponse) - { - CDPMA? decoded = DecodeCDPMA(CDPMAResponse); - return PrettifyCDPMA(decoded); - } - - public static CDATIP? DecodeCDATIP(byte[] CDATIPResponse) - { - if (CDATIPResponse == null) - return null; - - CDATIP decoded = new CDATIP(); - - BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; - - if (CDATIPResponse.Length != 32) - { - DicConsole.DebugWriteLine("CD ATIP decoder", "Expected CD ATIP size (32 bytes) is not received size ({0} bytes), not decoding", CDATIPResponse.Length); - return null; - } - - decoded.DataLength = BigEndianBitConverter.ToUInt16(CDATIPResponse, 0); - decoded.Reserved1 = CDATIPResponse[2]; - decoded.Reserved2 = CDATIPResponse[3]; - decoded.ITWP = (byte)((CDATIPResponse[4] & 0xF0) >> 4); - decoded.DDCD = Convert.ToBoolean(CDATIPResponse[4] & 0x08); - decoded.ReferenceSpeed = (byte)(CDATIPResponse[4] & 0x07); - decoded.AlwaysZero = Convert.ToBoolean(CDATIPResponse[5] & 0x80); - decoded.URU = Convert.ToBoolean(CDATIPResponse[5] & 0x40); - decoded.Reserved3 = (byte)(CDATIPResponse[5] & 0x3F); - - decoded.AlwaysOne = Convert.ToBoolean(CDATIPResponse[6] & 0x80); - decoded.DiscType = Convert.ToBoolean(CDATIPResponse[6] & 0x40); - decoded.DiscSubType = (byte)((CDATIPResponse[6] & 0x38) >> 3); - decoded.A1Valid = Convert.ToBoolean(CDATIPResponse[6] & 0x04); - decoded.A2Valid = Convert.ToBoolean(CDATIPResponse[6] & 0x02); - decoded.A3Valid = Convert.ToBoolean(CDATIPResponse[6] & 0x01); - - decoded.Reserved4 = CDATIPResponse[7]; - decoded.LeadInStartMin = CDATIPResponse[8]; - decoded.LeadInStartSec = CDATIPResponse[9]; - decoded.LeadInStartFrame = CDATIPResponse[10]; - decoded.Reserved5 = CDATIPResponse[11]; - decoded.LeadOutStartMin = CDATIPResponse[12]; - decoded.LeadOutStartSec = CDATIPResponse[13]; - decoded.LeadOutStartFrame = CDATIPResponse[14]; - decoded.Reserved6 = CDATIPResponse[15]; - - decoded.A1Values = new byte[3]; - decoded.A2Values = new byte[3]; - decoded.A3Values = new byte[3]; - decoded.S4Values = new byte[3]; - - Array.Copy(CDATIPResponse, 16, decoded.A1Values, 0, 3); - Array.Copy(CDATIPResponse, 20, decoded.A1Values, 0, 3); - Array.Copy(CDATIPResponse, 24, decoded.A1Values, 0, 3); - Array.Copy(CDATIPResponse, 28, decoded.A1Values, 0, 3); - - decoded.Reserved7 = CDATIPResponse[19]; - decoded.Reserved8 = CDATIPResponse[23]; - decoded.Reserved9 = CDATIPResponse[27]; - decoded.Reserved10 = CDATIPResponse[31]; - - return decoded; - } - - public static string PrettifyCDATIP(CDATIP? CDATIPResponse) - { - if (CDATIPResponse == null) - return null; - - CDATIP response = CDATIPResponse.Value; - - StringBuilder sb = new StringBuilder(); - - if (response.DDCD) - { - sb.AppendFormat("Indicative Target Writing Power: 0x{0:X2}", response.ITWP).AppendLine(); - if (response.DiscType) - sb.AppendLine("Disc is DDCD-RW"); - else - sb.AppendLine("Disc is DDCD-R"); - switch (response.ReferenceSpeed) - { - case 2: - sb.AppendLine("Reference speed is 4x"); - break; - case 3: - sb.AppendLine("Reference speed is 8x"); - break; - default: - sb.AppendFormat("Reference speed set is unknown: {0}", response.ReferenceSpeed).AppendLine(); - break; - } - sb.AppendFormat("ATIP Start time of Lead-in: 0x{0:X6}", (response.LeadInStartMin << 16) + (response.LeadInStartSec << 8) + response.LeadInStartFrame).AppendLine(); - sb.AppendFormat("ATIP Last possible start time of Lead-out: 0x{0:X6}", (response.LeadOutStartMin << 16) + (response.LeadOutStartSec << 8) + response.LeadOutStartFrame).AppendLine(); - sb.AppendFormat("S4 value: 0x{0:X6}", (response.S4Values[0] << 16) + (response.S4Values[1] << 8) + response.S4Values[2]).AppendLine(); - } - else - { - sb.AppendFormat("Indicative Target Writing Power: 0x{0:X2}", response.ITWP & 0x07).AppendLine(); - if (response.DiscType) - { - switch (response.DiscSubType) - { - case 0: - sb.AppendLine("Disc is CD-RW"); - break; - case 1: - sb.AppendLine("Disc is High-Speed CD-RW"); - break; - default: - sb.AppendFormat("Unknown CD-RW disc subtype: {0}", response.DiscSubType).AppendLine(); - break; - } - switch (response.ReferenceSpeed) - { - case 1: - sb.AppendLine("Reference speed is 2x"); - break; - default: - sb.AppendFormat("Reference speed set is unknown: {0}", response.ReferenceSpeed).AppendLine(); - break; - } - } - else - { - sb.AppendLine("Disc is CD-R"); - switch (response.DiscSubType) - { - default: - sb.AppendFormat("Unknown CD-R disc subtype: {0}", response.DiscSubType).AppendLine(); - break; - } - } - - if (response.URU) - sb.AppendLine("Disc use is unrestricted"); - else - sb.AppendLine("Disc use is restricted"); - - sb.AppendFormat("ATIP Start time of Lead-in: {0:X2}:{1:X2}:{2:X2}", response.LeadInStartMin, response.LeadInStartSec, response.LeadInStartFrame).AppendLine(); - sb.AppendFormat("ATIP Last possible start time of Lead-out: {0:X2}:{1:X2}:{2:X2}", response.LeadOutStartMin, response.LeadOutStartSec, response.LeadOutStartFrame).AppendLine(); - if(response.A1Valid) - sb.AppendFormat("A1 value: 0x{0:X6}", (response.A1Values[0] << 16) + (response.A1Values[1] << 8) + response.A1Values[2]).AppendLine(); - if(response.A2Valid) - sb.AppendFormat("A2 value: 0x{0:X6}", (response.A2Values[0] << 16) + (response.A2Values[1] << 8) + response.A2Values[2]).AppendLine(); - if(response.A3Valid) - sb.AppendFormat("A3 value: 0x{0:X6}", (response.A3Values[0] << 16) + (response.A3Values[1] << 8) + response.A3Values[2]).AppendLine(); - sb.AppendFormat("S4 value: 0x{0:X6}", (response.S4Values[0] << 16) + (response.S4Values[1] << 8) + response.S4Values[2]).AppendLine(); - } - - return sb.ToString(); - } - - public static string PrettifyCDATIP(byte[] CDATIPResponse) - { - CDATIP? decoded = DecodeCDATIP(CDATIPResponse); - return PrettifyCDATIP(decoded); - } - - public static CDTextLeadIn? DecodeCDTextLeadIn(byte[] CDTextResponse) - { - if (CDTextResponse == null) - return null; - - CDTextLeadIn decoded = new CDTextLeadIn(); - - BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; - - decoded.DataLength = BigEndianBitConverter.ToUInt16(CDTextResponse, 0); - decoded.Reserved1 = CDTextResponse[2]; - decoded.Reserved2 = CDTextResponse[3]; - decoded.DataPacks = new CDTextPack[(decoded.DataLength - 2) / 18]; - - if (decoded.DataLength + 2 != CDTextResponse.Length) - { - DicConsole.DebugWriteLine("CD-TEXT decoder", "Expected CD-TEXT size ({0} bytes) is not received size ({1} bytes), not decoding", decoded.DataLength + 2, CDTextResponse.Length); - return null; - } - - for (int i = 0; i < ((decoded.DataLength - 2) / 18); i++) - { - decoded.DataPacks[i].HeaderID1 = CDTextResponse[0 + i * 18 + 4]; - decoded.DataPacks[i].HeaderID2 = CDTextResponse[1 + i * 18 + 4]; - decoded.DataPacks[i].HeaderID3 = CDTextResponse[2 + i * 18 + 4]; - decoded.DataPacks[i].DBCC = Convert.ToBoolean(CDTextResponse[3 + i * 8 + 4] & 0x80); - decoded.DataPacks[i].BlockNumber = (byte)((CDTextResponse[3 + i * 8 + 4] & 0x70) >> 4); - decoded.DataPacks[i].CharacterPosition = (byte)(CDTextResponse[3 + i * 8 + 4] & 0x0F); - decoded.DataPacks[i].TextDataField = new byte[12]; - Array.Copy(CDTextResponse, 4, decoded.DataPacks[i].TextDataField, 0, 12); - decoded.DataPacks[i].CRC = BigEndianBitConverter.ToUInt16(CDTextResponse, 16 + i * 8 + 4); - } - - return decoded; - } - - public static string PrettifyCDTextLeadIn(CDTextLeadIn? CDTextResponse) - { - if (CDTextResponse == null) - return null; - - CDTextLeadIn response = CDTextResponse.Value; - - StringBuilder sb = new StringBuilder(); - - #if DEBUG - if(response.Reserved1 != 0) - sb.AppendFormat("Reserved1 = 0x{0:X2}", response.Reserved1).AppendLine(); - if(response.Reserved2 != 0) - sb.AppendFormat("Reserved2 = 0x{0:X2}", response.Reserved2).AppendLine(); - #endif - foreach (CDTextPack descriptor in response.DataPacks) - { - if ((descriptor.HeaderID1 & 0x80) != 0x80) - sb.AppendFormat("Incorrect CD-Text pack type {0}, not decoding", descriptor.HeaderID1).AppendLine(); - else - { - switch (descriptor.HeaderID1) - { - case 0x80: - { - sb.Append("CD-Text pack contains title for "); - if (descriptor.HeaderID2 == 0x00) - sb.AppendLine("album"); - else - sb.AppendFormat("track {0}", descriptor.HeaderID2).AppendLine(); - break; - } - case 0x81: - { - sb.Append("CD-Text pack contains performer for "); - if (descriptor.HeaderID2 == 0x00) - sb.AppendLine("album"); - else - sb.AppendFormat("track {0}", descriptor.HeaderID2).AppendLine(); - break; - } - case 0x82: - { - sb.Append("CD-Text pack contains songwriter for "); - if (descriptor.HeaderID2 == 0x00) - sb.AppendLine("album"); - else - sb.AppendFormat("track {0}", descriptor.HeaderID2).AppendLine(); - break; - } - case 0x83: - { - sb.Append("CD-Text pack contains composer for "); - if (descriptor.HeaderID2 == 0x00) - sb.AppendLine("album"); - else - sb.AppendFormat("track {0}", descriptor.HeaderID2).AppendLine(); - break; - } - case 0x84: - { - sb.Append("CD-Text pack contains arranger for "); - if (descriptor.HeaderID2 == 0x00) - sb.AppendLine("album"); - else - sb.AppendFormat("track {0}", descriptor.HeaderID2).AppendLine(); - break; - } - case 0x85: - { - sb.Append("CD-Text pack contains content provider's message for "); - if (descriptor.HeaderID2 == 0x00) - sb.AppendLine("album"); - else - sb.AppendFormat("track {0}", descriptor.HeaderID2).AppendLine(); - break; - } - case 0x86: - { - sb.AppendLine("CD-Text pack contains disc identification information"); - break; - } - case 0x87: - { - sb.AppendLine("CD-Text pack contains genre identification information"); - break; - } - case 0x88: - { - sb.AppendLine("CD-Text pack contains table of contents information"); - break; - } - case 0x89: - { - sb.AppendLine("CD-Text pack contains second table of contents information"); - break; - } - case 0x8A: - case 0x8B: - case 0x8C: - { - sb.AppendLine("CD-Text pack contains reserved data"); - break; - } - case 0x8D: - { - sb.AppendLine("CD-Text pack contains data reserved for content provider only"); - break; - } - case 0x8E: - { - if (descriptor.HeaderID2 == 0x00) - sb.AppendLine("CD-Text pack contains UPC"); - else - sb.AppendFormat("CD-Text pack contains ISRC for track {0}", descriptor.HeaderID2).AppendLine(); - break; - } - case 0x8F: - { - sb.AppendLine("CD-Text pack contains size block information"); - break; - } - } - - switch (descriptor.HeaderID1) - { - case 0x80: - case 0x81: - case 0x82: - case 0x83: - case 0x84: - case 0x85: - case 0x86: - case 0x87: - case 0x8E: - { - if (descriptor.DBCC) - sb.AppendLine("Double Byte Character Code is used"); - sb.AppendFormat("Block number {0}", descriptor.BlockNumber).AppendLine(); - sb.AppendFormat("Character position {0}", descriptor.CharacterPosition).AppendLine(); - sb.AppendFormat("Text field: \"{0}\"", StringHandlers.CToString(descriptor.TextDataField)).AppendLine(); - break; - } - default: - { - sb.AppendFormat("Binary contents: {0}", PrintHex.ByteArrayToHexArrayString(descriptor.TextDataField, 28)).AppendLine(); - break; - } - } - - sb.AppendFormat("CRC: 0x{0:X4}", descriptor.CRC).AppendLine(); - } - } - - return sb.ToString(); - } - - public static string PrettifyCDTextLeadIn(byte[] CDTextResponse) - { - CDTextLeadIn? decoded = DecodeCDTextLeadIn(CDTextResponse); - return PrettifyCDTextLeadIn(decoded); - } - - #endregion Public methods - - #region Public structures - - public struct CDTOC - { - /// - /// Total size of returned TOC minus this field - /// - public UInt16 DataLength; - /// - /// First track number in hex - /// - public byte FirstTrack; - /// - /// Last track number in hex - /// - public byte LastTrack; - /// - /// Track descriptors - /// - public CDTOCTrackDataDescriptor[] TrackDescriptors; - } - - public struct CDTOCTrackDataDescriptor - { - /// - /// Byte 0 - /// Reserved - /// - public byte Reserved1; - /// - /// Byte 1, bits 7 to 4 - /// Type of information in Q subchannel of block where this TOC entry was found - /// - public byte ADR; - /// - /// Byte 1, bits 3 to 0 - /// Track attributes - /// - public byte CONTROL; - /// - /// Byte 2 - /// Track number - /// - public byte TrackNumber; - /// - /// Byte 3 - /// Reserved - /// - public byte Reserved2; - /// - /// Bytes 4 to 7 - /// The track start address in LBA or in MSF - /// - public UInt32 TrackStartAddress; - } - - public struct CDSessionInfo - { - /// - /// Total size of returned session information minus this field - /// - public UInt16 DataLength; - /// - /// First track number in hex - /// - public byte FirstCompleteSession; - /// - /// Last track number in hex - /// - public byte LastCompleteSession; - /// - /// Track descriptors - /// - public CDSessionInfoTrackDataDescriptor[] TrackDescriptors; - } - - public struct CDSessionInfoTrackDataDescriptor - { - /// - /// Byte 0 - /// Reserved - /// - public byte Reserved1; - /// - /// Byte 1, bits 7 to 4 - /// Type of information in Q subchannel of block where this TOC entry was found - /// - public byte ADR; - /// - /// Byte 1, bits 3 to 0 - /// Track attributes - /// - public byte CONTROL; - /// - /// Byte 2 - /// First track number in last complete session - /// - public byte TrackNumber; - /// - /// Byte 3 - /// Reserved - /// - public byte Reserved2; - /// - /// Bytes 4 to 7 - /// First track number in last complete session start address in LBA or in MSF - /// - public UInt32 TrackStartAddress; - } - - public struct CDFullTOC - { - /// - /// Total size of returned session information minus this field - /// - public UInt16 DataLength; - /// - /// First complete session number in hex - /// - public byte FirstCompleteSession; - /// - /// Last complete session number in hex - /// - public byte LastCompleteSession; - /// - /// Track descriptors - /// - public CDFullTOCInfoTrackDataDescriptor[] TrackDescriptors; - } - - public struct CDFullTOCInfoTrackDataDescriptor - { - /// - /// Byte 0 - /// Session number in hex - /// - public byte SessionNumber; - /// - /// Byte 1, bits 7 to 4 - /// Type of information in Q subchannel of block where this TOC entry was found - /// - public byte ADR; - /// - /// Byte 1, bits 3 to 0 - /// Track attributes - /// - public byte CONTROL; - /// - /// Byte 2 - /// - public byte TNO; - /// - /// Byte 3 - /// - public byte POINT; - /// - /// Byte 4 - /// - public byte Min; - /// - /// Byte 5 - /// - public byte Sec; - /// - /// Byte 6 - /// - public byte Frame; - /// - /// Byte 7, CD only - /// - public byte Zero; - /// - /// Byte 7, bits 7 to 4, DDCD only - /// - public byte HOUR; - /// - /// Byte 7, bits 3 to 0, DDCD only - /// - public byte PHOUR; - /// - /// Byte 8 - /// - public byte PMIN; - /// - /// Byte 9 - /// - public byte PSEC; - /// - /// Byte 10 - /// - public byte PFRAME; - } - - public struct CDPMA - { - /// - /// Total size of returned session information minus this field - /// - public UInt16 DataLength; - /// - /// Reserved - /// - public byte Reserved1; - /// - /// Reserved - /// - public byte Reserved2; - /// - /// Track descriptors - /// - public CDPMADescriptors[] PMADescriptors; - } - - public struct CDPMADescriptors - { - /// - /// Byte 0 - /// Reserved - /// - public byte Reserved; - /// - /// Byte 1, bits 7 to 4 - /// Type of information in Q subchannel of block where this TOC entry was found - /// - public byte ADR; - /// - /// Byte 1, bits 3 to 0 - /// Track attributes - /// - public byte CONTROL; - /// - /// Byte 2 - /// - public byte TNO; - /// - /// Byte 3 - /// - public byte POINT; - /// - /// Byte 4 - /// - public byte Min; - /// - /// Byte 5 - /// - public byte Sec; - /// - /// Byte 6 - /// - public byte Frame; - /// - /// Byte 7, bits 7 to 4 - /// - public byte HOUR; - /// - /// Byte 7, bits 3 to 0 - /// - public byte PHOUR; - /// - /// Byte 8 - /// - public byte PMIN; - /// - /// Byte 9 - /// - public byte PSEC; - /// - /// Byte 10 - /// - public byte PFRAME; - } - - public struct CDATIP - { - /// - /// Bytes 1 to 0 - /// Total size of returned session information minus this field - /// - public UInt16 DataLength; - /// - /// Byte 2 - /// Reserved - /// - public byte Reserved1; - /// - /// Byte 3 - /// Reserved - /// - public byte Reserved2; - /// - /// Byte 4, bits 7 to 4 - /// Indicative target writing power - /// - public byte ITWP; - /// - /// Byte 4, bit 3 - /// Set if DDCD - /// - public bool DDCD; - /// - /// Byte 4, bits 2 to 0 - /// Reference speed - /// - public byte ReferenceSpeed; - /// - /// Byte 5, bit 7 - /// Always unset - /// - public bool AlwaysZero; - /// - /// Byte 5, bit 6 - /// Unrestricted media - /// - public bool URU; - /// - /// Byte 5, bits 5 to 0 - /// Reserved - /// - public byte Reserved3; - /// - /// Byte 6, bit 7 - /// Always set - /// - public bool AlwaysOne; - /// - /// Byte 6, bit 6 - /// Set if rewritable (CD-RW or DDCD-RW) - /// - public bool DiscType; - /// - /// Byte 6, bits 5 to 3 - /// Disc subtype - /// - public byte DiscSubType; - /// - /// Byte 6, bit 2 - /// A1 values are valid - /// - public bool A1Valid; - /// - /// Byte 6, bit 1 - /// A2 values are valid - /// - public bool A2Valid; - /// - /// Byte 6, bit 0 - /// A3 values are valid - /// - public bool A3Valid; - /// - /// Byte 7 - /// Reserved - /// - public byte Reserved4; - /// - /// Byte 8 - /// ATIP Start time of Lead-In (Minute) - /// - public byte LeadInStartMin; - /// - /// Byte 9 - /// ATIP Start time of Lead-In (Second) - /// - public byte LeadInStartSec; - /// - /// Byte 10 - /// ATIP Start time of Lead-In (Frame) - /// - public byte LeadInStartFrame; - /// - /// Byte 11 - /// Reserved - /// - public byte Reserved5; - /// - /// Byte 12 - /// ATIP Last possible start time of Lead-Out (Minute) - /// - public byte LeadOutStartMin; - /// - /// Byte 13 - /// ATIP Last possible start time of Lead-Out (Second) - /// - public byte LeadOutStartSec; - /// - /// Byte 14 - /// ATIP Last possible start time of Lead-Out (Frame) - /// - public byte LeadOutStartFrame; - /// - /// Byte 15 - /// Reserved - /// - public byte Reserved6; - /// - /// Bytes 16 to 18 - /// A1 values - /// - public byte[] A1Values; - /// - /// Byte 19 - /// Reserved - /// - public byte Reserved7; - /// - /// Bytes 20 to 22 - /// A2 values - /// - public byte[] A2Values; - /// - /// Byte 23 - /// Reserved - /// - public byte Reserved8; - /// - /// Bytes 24 to 26 - /// A3 values - /// - public byte[] A3Values; - /// - /// Byte 27 - /// Reserved - /// - public byte Reserved9; - /// - /// Bytes 28 to 30 - /// S4 values - /// - public byte[] S4Values; - /// - /// Byte 31 - /// Reserved - /// - public byte Reserved10; - } - - public struct CDTextLeadIn - { - /// - /// Total size of returned CD-Text information minus this field - /// - public UInt16 DataLength; - /// - /// Reserved - /// - public byte Reserved1; - /// - /// Reserved - /// - public byte Reserved2; - /// - /// CD-Text data packs - /// - public CDTextPack[] DataPacks; - } - - public struct CDTextPack - { - /// - /// Byte 0 - /// Pack ID1 (Pack Type) - /// - public byte HeaderID1; - /// - /// Byte 1 - /// Pack ID2 (Track number) - /// - public byte HeaderID2; - /// - /// Byte 2 - /// Pack ID3 - /// - public byte HeaderID3; - /// - /// Byte 3, bit 7 - /// Double Byte Character Code - /// - public bool DBCC; - /// - /// Byte 3, bits 6 to 4 - /// Block number - /// - public byte BlockNumber; - /// - /// Byte 3, bits 3 to 0 - /// Character position - /// - public byte CharacterPosition; - /// - /// Bytes 4 to 15 - /// Text data - /// - public byte[] TextDataField; - /// - /// Bytes 16 to 17 - /// CRC16 - /// - public UInt16 CRC; - } - - #endregion Public structures - } -} - diff --git a/CD/CDTextOnLeadIn.cs b/CD/CDTextOnLeadIn.cs new file mode 100644 index 0000000..c421776 --- /dev/null +++ b/CD/CDTextOnLeadIn.cs @@ -0,0 +1,393 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : CDText.cs +// Version : 1.0 +// Author(s) : Natalia Portillo +// +// Component : Component +// +// Revision : $Revision$ +// Last change by : $Author$ +// Date : $Date$ +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright (C) 2011-2015 Claunia.com +// ****************************************************************************/ +// //$Id$ +using System; +using DiscImageChef.Console; +using System.Text; + +namespace DiscImageChef.Decoders.CD +{ + /// + /// Information from the following standards: + /// ANSI X3.304-1997 + /// T10/1048-D revision 9.0 + /// T10/1048-D revision 10a + /// T10/1228-D revision 7.0c + /// T10/1228-D revision 11a + /// T10/1363-D revision 10g + /// T10/1545-D revision 1d + /// T10/1545-D revision 5 + /// T10/1545-D revision 5a + /// T10/1675-D revision 2c + /// T10/1675-D revision 4 + /// T10/1836-D revision 2g + /// + public static class CDTextOnLeadIn + { + public enum PackTypeIndicator : byte + { + /// + /// Title of the track (or album if track == 0) + /// + Title = 0x80, + /// + /// Performer + /// + Performer = 0x81, + /// + /// Songwriter + /// + Songwriter = 0x82, + /// + /// Composer + /// + Composer = 0x83, + /// + /// Arranger + /// + Arranger = 0x84, + /// + /// Message from the content provider or artist + /// + Message = 0x85, + /// + /// Disc identification information + /// + DiscIdentification = 0x86, + /// + /// Genre identification + /// + GenreIdentification = 0x87, + /// + /// Table of content information + /// + TOCInformation = 0x88, + /// + /// Second table of content information + /// + SecondTOCInformation = 0x89, + /// + /// Reserved + /// + Reserved1 = 0x8A, + /// + /// Reserved + /// + Reserved2 = 0x8B, + /// + /// Reserved + /// + Reserved3 = 0x8C, + /// + /// Reserved for content provider only + /// + ReservedForContentProvider = 0x8D, + /// + /// UPC of album or ISRC of track + /// + UPCorISRC = 0x8E, + /// + /// Size information of the block + /// + BlockSizeInformation = 0x8F + } + + public struct CDTextLeadIn + { + /// + /// Total size of returned CD-Text information minus this field + /// + public UInt16 DataLength; + /// + /// Reserved + /// + public byte Reserved1; + /// + /// Reserved + /// + public byte Reserved2; + /// + /// CD-Text data packs + /// + public CDTextPack[] DataPacks; + } + + public struct CDTextPack + { + /// + /// Byte 0 + /// Pack ID1 (Pack Type) + /// + public byte HeaderID1; + /// + /// Byte 1 + /// Pack ID2 (Track number) + /// + public byte HeaderID2; + /// + /// Byte 2 + /// Pack ID3 + /// + public byte HeaderID3; + /// + /// Byte 3, bit 7 + /// Double Byte Character Code + /// + public bool DBCC; + /// + /// Byte 3, bits 6 to 4 + /// Block number + /// + public byte BlockNumber; + /// + /// Byte 3, bits 3 to 0 + /// Character position + /// + public byte CharacterPosition; + /// + /// Bytes 4 to 15 + /// Text data + /// + public byte[] TextDataField; + /// + /// Bytes 16 to 17 + /// CRC16 + /// + public UInt16 CRC; + } + + public static CDTextLeadIn? DecodeCDTextLeadIn(byte[] CDTextResponse) + { + if (CDTextResponse == null) + return null; + + CDTextLeadIn decoded = new CDTextLeadIn(); + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + + decoded.DataLength = BigEndianBitConverter.ToUInt16(CDTextResponse, 0); + decoded.Reserved1 = CDTextResponse[2]; + decoded.Reserved2 = CDTextResponse[3]; + decoded.DataPacks = new CDTextPack[(decoded.DataLength - 2) / 18]; + + if (decoded.DataLength + 2 != CDTextResponse.Length) + { + DicConsole.DebugWriteLine("CD-TEXT decoder", "Expected CD-TEXT size ({0} bytes) is not received size ({1} bytes), not decoding", decoded.DataLength + 2, CDTextResponse.Length); + return null; + } + + for (int i = 0; i < ((decoded.DataLength - 2) / 18); i++) + { + decoded.DataPacks[i].HeaderID1 = CDTextResponse[0 + i * 18 + 4]; + decoded.DataPacks[i].HeaderID2 = CDTextResponse[1 + i * 18 + 4]; + decoded.DataPacks[i].HeaderID3 = CDTextResponse[2 + i * 18 + 4]; + decoded.DataPacks[i].DBCC = Convert.ToBoolean(CDTextResponse[3 + i * 8 + 4] & 0x80); + decoded.DataPacks[i].BlockNumber = (byte)((CDTextResponse[3 + i * 8 + 4] & 0x70) >> 4); + decoded.DataPacks[i].CharacterPosition = (byte)(CDTextResponse[3 + i * 8 + 4] & 0x0F); + decoded.DataPacks[i].TextDataField = new byte[12]; + Array.Copy(CDTextResponse, 4, decoded.DataPacks[i].TextDataField, 0, 12); + decoded.DataPacks[i].CRC = BigEndianBitConverter.ToUInt16(CDTextResponse, 16 + i * 8 + 4); + } + + return decoded; + } + + public static string PrettifyCDTextLeadIn(CDTextLeadIn? CDTextResponse) + { + if (CDTextResponse == null) + return null; + + CDTextLeadIn response = CDTextResponse.Value; + + StringBuilder sb = new StringBuilder(); + + #if DEBUG + if(response.Reserved1 != 0) + sb.AppendFormat("Reserved1 = 0x{0:X2}", response.Reserved1).AppendLine(); + if(response.Reserved2 != 0) + sb.AppendFormat("Reserved2 = 0x{0:X2}", response.Reserved2).AppendLine(); + #endif + foreach (CDTextPack descriptor in response.DataPacks) + { + if ((descriptor.HeaderID1 & 0x80) != 0x80) + sb.AppendFormat("Incorrect CD-Text pack type {0}, not decoding", descriptor.HeaderID1).AppendLine(); + else + { + switch (descriptor.HeaderID1) + { + case 0x80: + { + sb.Append("CD-Text pack contains title for "); + if (descriptor.HeaderID2 == 0x00) + sb.AppendLine("album"); + else + sb.AppendFormat("track {0}", descriptor.HeaderID2).AppendLine(); + break; + } + case 0x81: + { + sb.Append("CD-Text pack contains performer for "); + if (descriptor.HeaderID2 == 0x00) + sb.AppendLine("album"); + else + sb.AppendFormat("track {0}", descriptor.HeaderID2).AppendLine(); + break; + } + case 0x82: + { + sb.Append("CD-Text pack contains songwriter for "); + if (descriptor.HeaderID2 == 0x00) + sb.AppendLine("album"); + else + sb.AppendFormat("track {0}", descriptor.HeaderID2).AppendLine(); + break; + } + case 0x83: + { + sb.Append("CD-Text pack contains composer for "); + if (descriptor.HeaderID2 == 0x00) + sb.AppendLine("album"); + else + sb.AppendFormat("track {0}", descriptor.HeaderID2).AppendLine(); + break; + } + case 0x84: + { + sb.Append("CD-Text pack contains arranger for "); + if (descriptor.HeaderID2 == 0x00) + sb.AppendLine("album"); + else + sb.AppendFormat("track {0}", descriptor.HeaderID2).AppendLine(); + break; + } + case 0x85: + { + sb.Append("CD-Text pack contains content provider's message for "); + if (descriptor.HeaderID2 == 0x00) + sb.AppendLine("album"); + else + sb.AppendFormat("track {0}", descriptor.HeaderID2).AppendLine(); + break; + } + case 0x86: + { + sb.AppendLine("CD-Text pack contains disc identification information"); + break; + } + case 0x87: + { + sb.AppendLine("CD-Text pack contains genre identification information"); + break; + } + case 0x88: + { + sb.AppendLine("CD-Text pack contains table of contents information"); + break; + } + case 0x89: + { + sb.AppendLine("CD-Text pack contains second table of contents information"); + break; + } + case 0x8A: + case 0x8B: + case 0x8C: + { + sb.AppendLine("CD-Text pack contains reserved data"); + break; + } + case 0x8D: + { + sb.AppendLine("CD-Text pack contains data reserved for content provider only"); + break; + } + case 0x8E: + { + if (descriptor.HeaderID2 == 0x00) + sb.AppendLine("CD-Text pack contains UPC"); + else + sb.AppendFormat("CD-Text pack contains ISRC for track {0}", descriptor.HeaderID2).AppendLine(); + break; + } + case 0x8F: + { + sb.AppendLine("CD-Text pack contains size block information"); + break; + } + } + + switch (descriptor.HeaderID1) + { + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x8E: + { + if (descriptor.DBCC) + sb.AppendLine("Double Byte Character Code is used"); + sb.AppendFormat("Block number {0}", descriptor.BlockNumber).AppendLine(); + sb.AppendFormat("Character position {0}", descriptor.CharacterPosition).AppendLine(); + sb.AppendFormat("Text field: \"{0}\"", StringHandlers.CToString(descriptor.TextDataField)).AppendLine(); + break; + } + default: + { + sb.AppendFormat("Binary contents: {0}", PrintHex.ByteArrayToHexArrayString(descriptor.TextDataField, 28)).AppendLine(); + break; + } + } + + sb.AppendFormat("CRC: 0x{0:X4}", descriptor.CRC).AppendLine(); + } + } + + return sb.ToString(); + } + + public static string PrettifyCDTextLeadIn(byte[] CDTextResponse) + { + CDTextLeadIn? decoded = DecodeCDTextLeadIn(CDTextResponse); + return PrettifyCDTextLeadIn(decoded); + } + } +} + diff --git a/CD/Enums.cs b/CD/Enums.cs new file mode 100644 index 0000000..2964328 --- /dev/null +++ b/CD/Enums.cs @@ -0,0 +1,98 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : Enums.cs +// Version : 1.0 +// Author(s) : Natalia Portillo +// +// Component : Component +// +// Revision : $Revision$ +// Last change by : $Author$ +// Date : $Date$ +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright (C) 2011-2015 Claunia.com +// ****************************************************************************/ +// //$Id$ +using System; + +namespace DiscImageChef.Decoders.CD +{ + public enum TOC_ADR : byte + { + /// + /// Q Sub-channel mode information not supplied + /// + NoInformation = 0x00, + /// + /// Q Sub-channel encodes current position data + /// + CurrentPosition = 0x01, + /// + /// Q Sub-channel encodes the media catalog number + /// + MediaCatalogNumber = 0x02, + /// + /// Q Sub-channel encodes the ISRC + /// + ISRC = 0x03 + } + + public enum TOC_CONTROL : byte + { + /// + /// Stereo audio, no pre-emphasis + /// + TwoChanNoPreEmph = 0x00, + /// + /// Stereo audio with pre-emphasis + /// + TwoChanPreEmph = 0x01, + /// + /// If mask applied, track can be copied + /// + CopyPermissionMask = 0x02, + /// + /// Data track, recorded uninterrumpted + /// + DataTrack = 0x04, + /// + /// Data track, recorded incrementally + /// + DataTrackIncremental = 0x05, + /// + /// Quadraphonic audio, no pre-emphasis + /// + FourChanNoPreEmph = 0x08, + /// + /// Quadraphonic audio with pre-emphasis + /// + FourChanPreEmph = 0x09, + /// + /// Reserved mask + /// + ReservedMask = 0x0C + } +} + diff --git a/CD/FullTOC.cs b/CD/FullTOC.cs new file mode 100644 index 0000000..779c42f --- /dev/null +++ b/CD/FullTOC.cs @@ -0,0 +1,374 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : FullTOC.cs +// Version : 1.0 +// Author(s) : Natalia Portillo +// +// Component : Component +// +// Revision : $Revision$ +// Last change by : $Author$ +// Date : $Date$ +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright (C) 2011-2015 Claunia.com +// ****************************************************************************/ +// //$Id$ +using System; +using DiscImageChef.Console; +using System.Text; + +namespace DiscImageChef.Decoders.CD +{ + /// + /// Information from the following standards: + /// ANSI X3.304-1997 + /// T10/1048-D revision 9.0 + /// T10/1048-D revision 10a + /// T10/1228-D revision 7.0c + /// T10/1228-D revision 11a + /// T10/1363-D revision 10g + /// T10/1545-D revision 1d + /// T10/1545-D revision 5 + /// T10/1545-D revision 5a + /// T10/1675-D revision 2c + /// T10/1675-D revision 4 + /// T10/1836-D revision 2g + /// + public static class FullTOC + { + public struct CDFullTOC + { + /// + /// Total size of returned session information minus this field + /// + public UInt16 DataLength; + /// + /// First complete session number in hex + /// + public byte FirstCompleteSession; + /// + /// Last complete session number in hex + /// + public byte LastCompleteSession; + /// + /// Track descriptors + /// + public CDFullTOCInfoTrackDataDescriptor[] TrackDescriptors; + } + + public struct CDFullTOCInfoTrackDataDescriptor + { + /// + /// Byte 0 + /// Session number in hex + /// + public byte SessionNumber; + /// + /// Byte 1, bits 7 to 4 + /// Type of information in Q subchannel of block where this TOC entry was found + /// + public byte ADR; + /// + /// Byte 1, bits 3 to 0 + /// Track attributes + /// + public byte CONTROL; + /// + /// Byte 2 + /// + public byte TNO; + /// + /// Byte 3 + /// + public byte POINT; + /// + /// Byte 4 + /// + public byte Min; + /// + /// Byte 5 + /// + public byte Sec; + /// + /// Byte 6 + /// + public byte Frame; + /// + /// Byte 7, CD only + /// + public byte Zero; + /// + /// Byte 7, bits 7 to 4, DDCD only + /// + public byte HOUR; + /// + /// Byte 7, bits 3 to 0, DDCD only + /// + public byte PHOUR; + /// + /// Byte 8 + /// + public byte PMIN; + /// + /// Byte 9 + /// + public byte PSEC; + /// + /// Byte 10 + /// + public byte PFRAME; + } + + public static CDFullTOC? DecodeCDFullTOC(byte[] CDFullTOCResponse) + { + if (CDFullTOCResponse == null) + return null; + + CDFullTOC decoded = new CDFullTOC(); + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + + decoded.DataLength = BigEndianBitConverter.ToUInt16(CDFullTOCResponse, 0); + decoded.FirstCompleteSession = CDFullTOCResponse[2]; + decoded.LastCompleteSession = CDFullTOCResponse[3]; + decoded.TrackDescriptors = new CDFullTOCInfoTrackDataDescriptor[(decoded.DataLength - 2) / 11]; + + if (decoded.DataLength + 2 != CDFullTOCResponse.Length) + { + DicConsole.DebugWriteLine("CD full TOC decoder", "Expected CDFullTOC size ({0} bytes) is not received size ({1} bytes), not decoding", decoded.DataLength + 2, CDFullTOCResponse.Length); + return null; + } + + for (int i = 0; i < ((decoded.DataLength - 2) / 11); i++) + { + decoded.TrackDescriptors[i].SessionNumber = CDFullTOCResponse[0 + i * 11 + 4]; + decoded.TrackDescriptors[i].ADR = (byte)((CDFullTOCResponse[1 + i * 11 + 4] & 0xF0) >> 4); + decoded.TrackDescriptors[i].CONTROL = (byte)(CDFullTOCResponse[1 + i * 11 + 4] & 0x0F); + decoded.TrackDescriptors[i].TNO = CDFullTOCResponse[2 + i * 11 + 4]; + decoded.TrackDescriptors[i].POINT = CDFullTOCResponse[3 + i * 11 + 4]; + decoded.TrackDescriptors[i].Min = CDFullTOCResponse[4 + i * 11 + 4]; + decoded.TrackDescriptors[i].Sec = CDFullTOCResponse[5 + i * 11 + 4]; + decoded.TrackDescriptors[i].Frame = CDFullTOCResponse[6 + i * 11 + 4]; + decoded.TrackDescriptors[i].Zero = CDFullTOCResponse[7 + i * 11 + 4]; + decoded.TrackDescriptors[i].HOUR = (byte)((CDFullTOCResponse[7 + i * 11 + 4] & 0xF0) >> 4); + decoded.TrackDescriptors[i].PHOUR = (byte)(CDFullTOCResponse[7 + i * 11 + 4] & 0x0F); + decoded.TrackDescriptors[i].PMIN = CDFullTOCResponse[8 + i * 11 + 4]; + decoded.TrackDescriptors[i].PSEC = CDFullTOCResponse[9 + i * 11 + 4]; + decoded.TrackDescriptors[i].PFRAME = CDFullTOCResponse[10 + i * 11 + 4]; + } + + return decoded; + } + + public static string PrettifyCDFullTOC(CDFullTOC? CDFullTOCResponse) + { + if (CDFullTOCResponse == null) + return null; + + CDFullTOC response = CDFullTOCResponse.Value; + + StringBuilder sb = new StringBuilder(); + + sb.AppendFormat("First complete session number: {0}", response.FirstCompleteSession).AppendLine(); + sb.AppendFormat("Last complete session number: {0}", response.LastCompleteSession).AppendLine(); + foreach (CDFullTOCInfoTrackDataDescriptor descriptor in response.TrackDescriptors) + { + if ((descriptor.CONTROL != 4 && descriptor.CONTROL != 6) || + (descriptor.ADR != 1 && descriptor.ADR != 5) || + descriptor.TNO != 0) + { + sb.AppendLine("Unknown TOC entry format, printing values as-is"); + sb.AppendFormat("SessionNumber = {0}", descriptor.SessionNumber).AppendLine(); + sb.AppendFormat("ADR = {0}", descriptor.ADR).AppendLine(); + sb.AppendFormat("CONTROL = {0}", descriptor.CONTROL).AppendLine(); + sb.AppendFormat("TNO = {0}", descriptor.TNO).AppendLine(); + sb.AppendFormat("POINT = {0}", descriptor.POINT).AppendLine(); + sb.AppendFormat("Min = {0}", descriptor.Min).AppendLine(); + sb.AppendFormat("Sec = {0}", descriptor.Sec).AppendLine(); + sb.AppendFormat("Frame = {0}", descriptor.Frame).AppendLine(); + sb.AppendFormat("HOUR = {0}", descriptor.HOUR).AppendLine(); + sb.AppendFormat("PHOUR = {0}", descriptor.PHOUR).AppendLine(); + sb.AppendFormat("PMIN = {0}", descriptor.PMIN).AppendLine(); + sb.AppendFormat("PSEC = {0}", descriptor.PSEC).AppendLine(); + sb.AppendFormat("PFRAME = {0}", descriptor.PFRAME).AppendLine(); + } + else + { + sb.AppendFormat("Session {0}", descriptor.SessionNumber).AppendLine(); + switch (descriptor.ADR) + { + case 1: + { + switch (descriptor.POINT) + { + case 0xA0: + { + sb.AppendFormat("First track number: {0}", descriptor.PMIN).AppendLine(); + sb.AppendFormat("Disc type: {0}", descriptor.PSEC).AppendLine(); + sb.AppendFormat("Absolute time: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); + break; + } + case 0xA1: + { + sb.AppendFormat("Last track number: {0}", descriptor.PMIN).AppendLine(); + sb.AppendFormat("Absolute time: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); + break; + } + case 0xA2: + { + sb.AppendFormat("Lead-out start position: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.PMIN, descriptor.PSEC, descriptor.PFRAME, descriptor.PHOUR).AppendLine(); + sb.AppendFormat("Absolute time: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); + break; + } + case 0xF0: + { + sb.AppendFormat("Book type: 0x{0:X2}", descriptor.PMIN); + sb.AppendFormat("Material type: 0x{0:X2}", descriptor.PSEC); + sb.AppendFormat("Moment of inertia: 0x{0:X2}", descriptor.PFRAME); + sb.AppendFormat("Absolute time: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); + break; + } + default: + { + if (descriptor.POINT >= 0x01 && descriptor.POINT <= 0x63) + { + sb.AppendFormat("Track start position for track {3}: {4:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.PMIN, descriptor.PSEC, descriptor.PFRAME, descriptor.POINT, descriptor.PHOUR).AppendLine(); + sb.AppendFormat("Absolute time: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); + } + else + { + sb.AppendFormat("ADR = {0}", descriptor.ADR).AppendLine(); + sb.AppendFormat("CONTROL = {0}", descriptor.CONTROL).AppendLine(); + sb.AppendFormat("TNO = {0}", descriptor.TNO).AppendLine(); + sb.AppendFormat("POINT = {0}", descriptor.POINT).AppendLine(); + sb.AppendFormat("Min = {0}", descriptor.Min).AppendLine(); + sb.AppendFormat("Sec = {0}", descriptor.Sec).AppendLine(); + sb.AppendFormat("Frame = {0}", descriptor.Frame).AppendLine(); + sb.AppendFormat("HOUR = {0}", descriptor.HOUR).AppendLine(); + sb.AppendFormat("PHOUR = {0}", descriptor.PHOUR).AppendLine(); + sb.AppendFormat("PMIN = {0}", descriptor.PMIN).AppendLine(); + sb.AppendFormat("PSEC = {0}", descriptor.PSEC).AppendLine(); + sb.AppendFormat("PFRAME = {0}", descriptor.PFRAME).AppendLine(); + } + break; + } + } + break; + } + case 5: + { + switch (descriptor.POINT) + { + case 0xB0: + { + sb.AppendFormat("Start of next possible program in the recordable area of the disc: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); + sb.AppendFormat("Maximum start of outermost Lead-out in the recordable area of the disc: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.PMIN, descriptor.PSEC, descriptor.PFRAME, descriptor.PHOUR).AppendLine(); + break; + } + case 0xB1: + { + sb.AppendFormat("Number of skip interval pointers: {0:X2}", descriptor.PMIN).AppendLine(); + sb.AppendFormat("Number of skip track pointers: {0:X2}", descriptor.PSEC).AppendLine(); + break; + } + case 0xB2: + case 0xB3: + case 0xB4: + { + sb.AppendFormat("Skip track {0}", descriptor.Min).AppendLine(); + sb.AppendFormat("Skip track {0}", descriptor.Sec).AppendLine(); + sb.AppendFormat("Skip track {0}", descriptor.Frame).AppendLine(); + sb.AppendFormat("Skip track {0}", descriptor.Zero).AppendLine(); + sb.AppendFormat("Skip track {0}", descriptor.PMIN).AppendLine(); + sb.AppendFormat("Skip track {0}", descriptor.PSEC).AppendLine(); + sb.AppendFormat("Skip track {0}", descriptor.PFRAME).AppendLine(); + break; + } + case 0xC0: + { + sb.AppendFormat("Optimum recording power: 0x{0:X2}", descriptor.Min).AppendLine(); + sb.AppendFormat("Start time of the first Lead-in area in the disc: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.PMIN, descriptor.PSEC, descriptor.PFRAME, descriptor.PHOUR).AppendLine(); + break; + } + case 0xC1: + { + sb.AppendFormat("Copy of information of A1 from ATIP found"); + sb.AppendFormat("Min = {0}", descriptor.Min).AppendLine(); + sb.AppendFormat("Sec = {0}", descriptor.Sec).AppendLine(); + sb.AppendFormat("Frame = {0}", descriptor.Frame).AppendLine(); + sb.AppendFormat("Zero = {0}", descriptor.Zero).AppendLine(); + sb.AppendFormat("PMIN = {0}", descriptor.PMIN).AppendLine(); + sb.AppendFormat("PSEC = {0}", descriptor.PSEC).AppendLine(); + sb.AppendFormat("PFRAME = {0}", descriptor.PFRAME).AppendLine(); + break; + } + case 0xCF: + { + sb.AppendFormat("Start position of outer part lead-in area: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.PMIN, descriptor.PSEC, descriptor.PFRAME, descriptor.PHOUR).AppendLine(); + sb.AppendFormat("Stop position of inner part lead-out area: {3:X2}:{0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame, descriptor.HOUR).AppendLine(); + break; + } + default: + { + if (descriptor.POINT >= 0x01 && descriptor.POINT <= 0x40) + { + sb.AppendFormat("Start time for interval that should be skipped: {0:X2}:{1:X2}:{2:X2}", descriptor.PMIN, descriptor.PSEC, descriptor.PFRAME).AppendLine(); + sb.AppendFormat("Ending time for interval that should be skipped: {0:X2}:{1:X2}:{2:X2}", descriptor.Min, descriptor.Sec, descriptor.Frame).AppendLine(); + } + else + { + sb.AppendFormat("ADR = {0}", descriptor.ADR).AppendLine(); + sb.AppendFormat("CONTROL = {0}", descriptor.CONTROL).AppendLine(); + sb.AppendFormat("TNO = {0}", descriptor.TNO).AppendLine(); + sb.AppendFormat("POINT = {0}", descriptor.POINT).AppendLine(); + sb.AppendFormat("Min = {0}", descriptor.Min).AppendLine(); + sb.AppendFormat("Sec = {0}", descriptor.Sec).AppendLine(); + sb.AppendFormat("Frame = {0}", descriptor.Frame).AppendLine(); + sb.AppendFormat("HOUR = {0}", descriptor.HOUR).AppendLine(); + sb.AppendFormat("PHOUR = {0}", descriptor.PHOUR).AppendLine(); + sb.AppendFormat("PMIN = {0}", descriptor.PMIN).AppendLine(); + sb.AppendFormat("PSEC = {0}", descriptor.PSEC).AppendLine(); + sb.AppendFormat("PFRAME = {0}", descriptor.PFRAME).AppendLine(); + } + break; + } + } + break; + } + } + } + } + + return sb.ToString(); + } + + public static string PrettifyCDFullTOC(byte[] CDFullTOCResponse) + { + CDFullTOC? decoded = DecodeCDFullTOC(CDFullTOCResponse); + return PrettifyCDFullTOC(decoded); + } + } +} + diff --git a/CD/PMA.cs b/CD/PMA.cs new file mode 100644 index 0000000..9fcad4d --- /dev/null +++ b/CD/PMA.cs @@ -0,0 +1,227 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : PMA.cs +// Version : 1.0 +// Author(s) : Natalia Portillo +// +// Component : Component +// +// Revision : $Revision$ +// Last change by : $Author$ +// Date : $Date$ +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright (C) 2011-2015 Claunia.com +// ****************************************************************************/ +// //$Id$ +using System; +using DiscImageChef.Console; +using System.Text; + +namespace DiscImageChef.Decoders.CD +{ + /// + /// Information from the following standards: + /// ANSI X3.304-1997 + /// T10/1048-D revision 9.0 + /// T10/1048-D revision 10a + /// T10/1228-D revision 7.0c + /// T10/1228-D revision 11a + /// T10/1363-D revision 10g + /// T10/1545-D revision 1d + /// T10/1545-D revision 5 + /// T10/1545-D revision 5a + /// T10/1675-D revision 2c + /// T10/1675-D revision 4 + /// T10/1836-D revision 2g + /// + public static class PMA + { + public struct CDPMA + { + /// + /// Total size of returned session information minus this field + /// + public UInt16 DataLength; + /// + /// Reserved + /// + public byte Reserved1; + /// + /// Reserved + /// + public byte Reserved2; + /// + /// Track descriptors + /// + public CDPMADescriptors[] PMADescriptors; + } + + public struct CDPMADescriptors + { + /// + /// Byte 0 + /// Reserved + /// + public byte Reserved; + /// + /// Byte 1, bits 7 to 4 + /// Type of information in Q subchannel of block where this TOC entry was found + /// + public byte ADR; + /// + /// Byte 1, bits 3 to 0 + /// Track attributes + /// + public byte CONTROL; + /// + /// Byte 2 + /// + public byte TNO; + /// + /// Byte 3 + /// + public byte POINT; + /// + /// Byte 4 + /// + public byte Min; + /// + /// Byte 5 + /// + public byte Sec; + /// + /// Byte 6 + /// + public byte Frame; + /// + /// Byte 7, bits 7 to 4 + /// + public byte HOUR; + /// + /// Byte 7, bits 3 to 0 + /// + public byte PHOUR; + /// + /// Byte 8 + /// + public byte PMIN; + /// + /// Byte 9 + /// + public byte PSEC; + /// + /// Byte 10 + /// + public byte PFRAME; + } + + public static CDPMA? DecodeCDPMA(byte[] CDPMAResponse) + { + if (CDPMAResponse == null) + return null; + + CDPMA decoded = new CDPMA(); + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + + decoded.DataLength = BigEndianBitConverter.ToUInt16(CDPMAResponse, 0); + decoded.Reserved1 = CDPMAResponse[2]; + decoded.Reserved2 = CDPMAResponse[3]; + decoded.PMADescriptors = new CDPMADescriptors[(decoded.DataLength - 2) / 11]; + + if (decoded.DataLength + 2 != CDPMAResponse.Length) + { + DicConsole.DebugWriteLine("CD PMA decoder", "Expected CDPMA size ({0} bytes) is not received size ({1} bytes), not decoding", decoded.DataLength + 2, CDPMAResponse.Length); + return null; + } + + for (int i = 0; i < ((decoded.DataLength - 2) / 11); i++) + { + decoded.PMADescriptors[i].Reserved = CDPMAResponse[0 + i * 11 + 4]; + decoded.PMADescriptors[i].ADR = (byte)((CDPMAResponse[1 + i * 11 + 4] & 0xF0) >> 4); + decoded.PMADescriptors[i].CONTROL = (byte)(CDPMAResponse[1 + i * 11 + 4] & 0x0F); + decoded.PMADescriptors[i].TNO = CDPMAResponse[2 + i * 11 + 4]; + decoded.PMADescriptors[i].POINT = CDPMAResponse[3 + i * 11 + 4]; + decoded.PMADescriptors[i].Min = CDPMAResponse[4 + i * 11 + 4]; + decoded.PMADescriptors[i].Sec = CDPMAResponse[5 + i * 11 + 4]; + decoded.PMADescriptors[i].Frame = CDPMAResponse[6 + i * 11 + 4]; + decoded.PMADescriptors[i].HOUR = (byte)((CDPMAResponse[7 + i * 11 + 4] & 0xF0) >> 4); + decoded.PMADescriptors[i].PHOUR = (byte)(CDPMAResponse[7 + i * 11 + 4] & 0x0F); + decoded.PMADescriptors[i].PMIN = CDPMAResponse[8 + i * 11 + 4]; + decoded.PMADescriptors[i].PSEC = CDPMAResponse[9 + i * 11 + 4]; + decoded.PMADescriptors[i].PFRAME = CDPMAResponse[10 + i * 11 + 4]; + } + + return decoded; + } + + public static string PrettifyCDPMA(CDPMA? CDPMAResponse) + { + if (CDPMAResponse == null) + return null; + + CDPMA response = CDPMAResponse.Value; + + StringBuilder sb = new StringBuilder(); + + #if DEBUG + if(response.Reserved1 != 0) + sb.AppendFormat("Reserved1 = 0x{0:X2}", response.Reserved1).AppendLine(); + if(response.Reserved2 != 0) + sb.AppendFormat("Reserved2 = 0x{0:X2}", response.Reserved2).AppendLine(); + #endif + + foreach (CDPMADescriptors descriptor in response.PMADescriptors) + { + #if DEBUG + if(descriptor.Reserved != 0) + sb.AppendFormat("Reserved = 0x{0:X2}", descriptor.Reserved).AppendLine(); + #endif + + sb.AppendFormat("ADR = {0}", descriptor.ADR).AppendLine(); + sb.AppendFormat("CONTROL = {0}", descriptor.CONTROL).AppendLine(); + sb.AppendFormat("TNO = {0}", descriptor.TNO).AppendLine(); + sb.AppendFormat("POINT = {0}", descriptor.POINT).AppendLine(); + sb.AppendFormat("Min = {0}", descriptor.Min).AppendLine(); + sb.AppendFormat("Sec = {0}", descriptor.Sec).AppendLine(); + sb.AppendFormat("Frame = {0}", descriptor.Frame).AppendLine(); + sb.AppendFormat("HOUR = {0}", descriptor.HOUR).AppendLine(); + sb.AppendFormat("PHOUR = {0}", descriptor.PHOUR).AppendLine(); + sb.AppendFormat("PMIN = {0}", descriptor.PMIN).AppendLine(); + sb.AppendFormat("PSEC = {0}", descriptor.PSEC).AppendLine(); + sb.AppendFormat("PFRAME = {0}", descriptor.PFRAME).AppendLine(); + } + + return sb.ToString(); + } + + public static string PrettifyCDPMA(byte[] CDPMAResponse) + { + CDPMA? decoded = DecodeCDPMA(CDPMAResponse); + return PrettifyCDPMA(decoded); + } + } +} + diff --git a/CD/Session.cs b/CD/Session.cs new file mode 100644 index 0000000..1a5baae --- /dev/null +++ b/CD/Session.cs @@ -0,0 +1,235 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : Session.cs +// Version : 1.0 +// Author(s) : Natalia Portillo +// +// Component : Component +// +// Revision : $Revision$ +// Last change by : $Author$ +// Date : $Date$ +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright (C) 2011-2015 Claunia.com +// ****************************************************************************/ +// //$Id$ +using System; +using DiscImageChef.Console; +using System.Text; + +namespace DiscImageChef.Decoders.CD +{ + /// + /// Information from the following standards: + /// ANSI X3.304-1997 + /// T10/1048-D revision 9.0 + /// T10/1048-D revision 10a + /// T10/1228-D revision 7.0c + /// T10/1228-D revision 11a + /// T10/1363-D revision 10g + /// T10/1545-D revision 1d + /// T10/1545-D revision 5 + /// T10/1545-D revision 5a + /// T10/1675-D revision 2c + /// T10/1675-D revision 4 + /// T10/1836-D revision 2g + /// + public static class Session + { + public struct CDSessionInfo + { + /// + /// Total size of returned session information minus this field + /// + public UInt16 DataLength; + /// + /// First track number in hex + /// + public byte FirstCompleteSession; + /// + /// Last track number in hex + /// + public byte LastCompleteSession; + /// + /// Track descriptors + /// + public CDSessionInfoTrackDataDescriptor[] TrackDescriptors; + } + + public struct CDSessionInfoTrackDataDescriptor + { + /// + /// Byte 0 + /// Reserved + /// + public byte Reserved1; + /// + /// Byte 1, bits 7 to 4 + /// Type of information in Q subchannel of block where this TOC entry was found + /// + public byte ADR; + /// + /// Byte 1, bits 3 to 0 + /// Track attributes + /// + public byte CONTROL; + /// + /// Byte 2 + /// First track number in last complete session + /// + public byte TrackNumber; + /// + /// Byte 3 + /// Reserved + /// + public byte Reserved2; + /// + /// Bytes 4 to 7 + /// First track number in last complete session start address in LBA or in MSF + /// + public UInt32 TrackStartAddress; + } + + public static CDSessionInfo? DecodeCDSessionInfo(byte[] CDSessionInfoResponse) + { + if (CDSessionInfoResponse == null) + return null; + + CDSessionInfo decoded = new CDSessionInfo(); + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + + decoded.DataLength = BigEndianBitConverter.ToUInt16(CDSessionInfoResponse, 0); + decoded.FirstCompleteSession = CDSessionInfoResponse[2]; + decoded.LastCompleteSession = CDSessionInfoResponse[3]; + decoded.TrackDescriptors = new CDSessionInfoTrackDataDescriptor[(decoded.DataLength - 2) / 8]; + + if (decoded.DataLength + 2 != CDSessionInfoResponse.Length) + { + DicConsole.DebugWriteLine("CD Session Info decoder", "Expected CDSessionInfo size ({0} bytes) is not received size ({1} bytes), not decoding", decoded.DataLength + 2, CDSessionInfoResponse.Length); + return null; + } + + for (int i = 0; i < ((decoded.DataLength - 2) / 8); i++) + { + decoded.TrackDescriptors[i].Reserved1 = CDSessionInfoResponse[0 + i * 8 + 4]; + decoded.TrackDescriptors[i].ADR = (byte)((CDSessionInfoResponse[1 + i * 8 + 4] & 0xF0) >> 4); + decoded.TrackDescriptors[i].CONTROL = (byte)(CDSessionInfoResponse[1 + i * 8 + 4] & 0x0F); + decoded.TrackDescriptors[i].TrackNumber = CDSessionInfoResponse[2 + i * 8 + 4]; + decoded.TrackDescriptors[i].Reserved2 = CDSessionInfoResponse[3 + i * 8 + 4]; + decoded.TrackDescriptors[i].TrackStartAddress = BigEndianBitConverter.ToUInt32(CDSessionInfoResponse, 4 + i * 8 + 4); + } + + return decoded; + } + + public static string PrettifyCDSessionInfo(CDSessionInfo? CDSessionInfoResponse) + { + if (CDSessionInfoResponse == null) + return null; + + CDSessionInfo response = CDSessionInfoResponse.Value; + + StringBuilder sb = new StringBuilder(); + + sb.AppendFormat("First complete session number: {0}", response.FirstCompleteSession).AppendLine(); + sb.AppendFormat("Last complete session number: {0}", response.LastCompleteSession).AppendLine(); + foreach (CDSessionInfoTrackDataDescriptor descriptor in response.TrackDescriptors) + { + sb.AppendFormat("First track number in last complete session: {0}", descriptor.TrackNumber); + sb.AppendFormat("Track starts at LBA {0}, or MSF {1:X2}:{2:X2}:{3:X2}", descriptor.TrackStartAddress, + (descriptor.TrackStartAddress & 0x0000FF00) >> 8, + (descriptor.TrackStartAddress & 0x00FF0000) >> 16, + (descriptor.TrackStartAddress & 0xFF000000) >> 24); + + switch ((TOC_ADR)descriptor.ADR) + { + case TOC_ADR.NoInformation: + sb.AppendLine("Q subchannel mode not given"); + break; + case TOC_ADR.CurrentPosition: + sb.AppendLine("Q subchannel stores current position"); + break; + case TOC_ADR.ISRC: + sb.AppendLine("Q subchannel stores ISRC"); + break; + case TOC_ADR.MediaCatalogNumber: + sb.AppendLine("Q subchannel stores media catalog number"); + break; + } + + if((descriptor.CONTROL & (byte)TOC_CONTROL.ReservedMask) == (byte)TOC_CONTROL.ReservedMask) + sb.AppendFormat("Reserved flags 0x{0:X2} set", descriptor.CONTROL).AppendLine(); + else + { + switch ((TOC_CONTROL)(descriptor.CONTROL & 0x0D)) + { + case TOC_CONTROL.TwoChanNoPreEmph: + sb.AppendLine("Stereo audio track with no pre-emphasis"); + break; + case TOC_CONTROL.TwoChanPreEmph: + sb.AppendLine("Stereo audio track with 50/15 μs pre-emphasis"); + break; + case TOC_CONTROL.FourChanNoPreEmph: + sb.AppendLine("Quadraphonic audio track with no pre-emphasis"); + break; + case TOC_CONTROL.FourChanPreEmph: + sb.AppendLine("Stereo audio track with 50/15 μs pre-emphasis"); + break; + case TOC_CONTROL.DataTrack: + sb.AppendLine("Data track, recorded uninterrupted"); + break; + case TOC_CONTROL.DataTrackIncremental: + sb.AppendLine("Data track, recorded incrementally"); + break; + } + + if ((descriptor.CONTROL & (byte)TOC_CONTROL.CopyPermissionMask) == (byte)TOC_CONTROL.CopyPermissionMask) + sb.AppendLine("Digital copy of track is permitted"); + else + sb.AppendLine("Digital copy of track is prohibited"); + + #if DEBUG + if(descriptor.Reserved1 != 0) + sb.AppendFormat("Reserved1 = 0x{0:X2}", descriptor.Reserved1).AppendLine(); + if(descriptor.Reserved2 != 0) + sb.AppendFormat("Reserved2 = 0x{0:X2}", descriptor.Reserved2).AppendLine(); + #endif + + sb.AppendLine(); + } + } + + return sb.ToString(); + } + + public static string PrettifyCDSessionInfo(byte[] CDSessionInfoResponse) + { + CDSessionInfo? decoded = DecodeCDSessionInfo(CDSessionInfoResponse); + return PrettifyCDSessionInfo(decoded); + } + } +} + diff --git a/CD/TOC.cs b/CD/TOC.cs new file mode 100644 index 0000000..84c6655 --- /dev/null +++ b/CD/TOC.cs @@ -0,0 +1,235 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : TOC.cs +// Version : 1.0 +// Author(s) : Natalia Portillo +// +// Component : Component +// +// Revision : $Revision$ +// Last change by : $Author$ +// Date : $Date$ +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright (C) 2011-2015 Claunia.com +// ****************************************************************************/ +// //$Id$ +using System; +using DiscImageChef.Console; +using System.Text; + +namespace DiscImageChef.Decoders.CD +{ + /// + /// Information from the following standards: + /// ANSI X3.304-1997 + /// T10/1048-D revision 9.0 + /// T10/1048-D revision 10a + /// T10/1228-D revision 7.0c + /// T10/1228-D revision 11a + /// T10/1363-D revision 10g + /// T10/1545-D revision 1d + /// T10/1545-D revision 5 + /// T10/1545-D revision 5a + /// T10/1675-D revision 2c + /// T10/1675-D revision 4 + /// T10/1836-D revision 2g + /// + public static class TOC + { + public struct CDTOC + { + /// + /// Total size of returned TOC minus this field + /// + public UInt16 DataLength; + /// + /// First track number in hex + /// + public byte FirstTrack; + /// + /// Last track number in hex + /// + public byte LastTrack; + /// + /// Track descriptors + /// + public CDTOCTrackDataDescriptor[] TrackDescriptors; + } + + public struct CDTOCTrackDataDescriptor + { + /// + /// Byte 0 + /// Reserved + /// + public byte Reserved1; + /// + /// Byte 1, bits 7 to 4 + /// Type of information in Q subchannel of block where this TOC entry was found + /// + public byte ADR; + /// + /// Byte 1, bits 3 to 0 + /// Track attributes + /// + public byte CONTROL; + /// + /// Byte 2 + /// Track number + /// + public byte TrackNumber; + /// + /// Byte 3 + /// Reserved + /// + public byte Reserved2; + /// + /// Bytes 4 to 7 + /// The track start address in LBA or in MSF + /// + public UInt32 TrackStartAddress; + } + + public static CDTOC? DecodeCDTOC(byte[] CDTOCResponse) + { + if (CDTOCResponse == null) + return null; + + CDTOC decoded = new CDTOC(); + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + + decoded.DataLength = BigEndianBitConverter.ToUInt16(CDTOCResponse, 0); + decoded.FirstTrack = CDTOCResponse[2]; + decoded.LastTrack = CDTOCResponse[3]; + decoded.TrackDescriptors = new CDTOCTrackDataDescriptor[(decoded.DataLength - 2) / 8]; + + if (decoded.DataLength + 2 != CDTOCResponse.Length) + { + DicConsole.DebugWriteLine("CD TOC decoder", "Expected CDTOC size ({0} bytes) is not received size ({1} bytes), not decoding", decoded.DataLength + 2, CDTOCResponse.Length); + return null; + } + + for (int i = 0; i < ((decoded.DataLength - 2) / 8); i++) + { + decoded.TrackDescriptors[i].Reserved1 = CDTOCResponse[0 + i * 8 + 4]; + decoded.TrackDescriptors[i].ADR = (byte)((CDTOCResponse[1 + i * 8 + 4] & 0xF0) >> 4); + decoded.TrackDescriptors[i].CONTROL = (byte)(CDTOCResponse[1 + i * 8 + 4] & 0x0F); + decoded.TrackDescriptors[i].TrackNumber = CDTOCResponse[2 + i * 8 + 4]; + decoded.TrackDescriptors[i].Reserved2 = CDTOCResponse[3 + i * 8 + 4]; + decoded.TrackDescriptors[i].TrackStartAddress = BigEndianBitConverter.ToUInt32(CDTOCResponse, 4 + i * 8 + 4); + } + + return decoded; + } + + public static string PrettifyCDTOC(CDTOC? CDTOCResponse) + { + if (CDTOCResponse == null) + return null; + + CDTOC response = CDTOCResponse.Value; + + StringBuilder sb = new StringBuilder(); + + sb.AppendFormat("First track number in first complete session: {0}", response.FirstTrack).AppendLine(); + sb.AppendFormat("Last track number in last complete session: {0}", response.LastTrack).AppendLine(); + foreach (CDTOCTrackDataDescriptor descriptor in response.TrackDescriptors) + { + sb.AppendFormat("Track number: {0}", descriptor.TrackNumber); + sb.AppendFormat("Track starts at LBA {0}, or MSF {1:X2}:{2:X2}:{3:X2}", descriptor.TrackStartAddress, + (descriptor.TrackStartAddress & 0x0000FF00) >> 8, + (descriptor.TrackStartAddress & 0x00FF0000) >> 16, + (descriptor.TrackStartAddress & 0xFF000000) >> 24); + + switch ((TOC_ADR)descriptor.ADR) + { + case TOC_ADR.NoInformation: + sb.AppendLine("Q subchannel mode not given"); + break; + case TOC_ADR.CurrentPosition: + sb.AppendLine("Q subchannel stores current position"); + break; + case TOC_ADR.ISRC: + sb.AppendLine("Q subchannel stores ISRC"); + break; + case TOC_ADR.MediaCatalogNumber: + sb.AppendLine("Q subchannel stores media catalog number"); + break; + } + + if((descriptor.CONTROL & (byte)TOC_CONTROL.ReservedMask) == (byte)TOC_CONTROL.ReservedMask) + sb.AppendFormat("Reserved flags 0x{0:X2} set", descriptor.CONTROL).AppendLine(); + else + { + switch ((TOC_CONTROL)(descriptor.CONTROL & 0x0D)) + { + case TOC_CONTROL.TwoChanNoPreEmph: + sb.AppendLine("Stereo audio track with no pre-emphasis"); + break; + case TOC_CONTROL.TwoChanPreEmph: + sb.AppendLine("Stereo audio track with 50/15 μs pre-emphasis"); + break; + case TOC_CONTROL.FourChanNoPreEmph: + sb.AppendLine("Quadraphonic audio track with no pre-emphasis"); + break; + case TOC_CONTROL.FourChanPreEmph: + sb.AppendLine("Stereo audio track with 50/15 μs pre-emphasis"); + break; + case TOC_CONTROL.DataTrack: + sb.AppendLine("Data track, recorded uninterrupted"); + break; + case TOC_CONTROL.DataTrackIncremental: + sb.AppendLine("Data track, recorded incrementally"); + break; + } + + if ((descriptor.CONTROL & (byte)TOC_CONTROL.CopyPermissionMask) == (byte)TOC_CONTROL.CopyPermissionMask) + sb.AppendLine("Digital copy of track is permitted"); + else + sb.AppendLine("Digital copy of track is prohibited"); + + #if DEBUG + if(descriptor.Reserved1 != 0) + sb.AppendFormat("Reserved1 = 0x{0:X2}", descriptor.Reserved1).AppendLine(); + if(descriptor.Reserved2 != 0) + sb.AppendFormat("Reserved2 = 0x{0:X2}", descriptor.Reserved2).AppendLine(); + #endif + + sb.AppendLine(); + } + } + + return sb.ToString(); + } + + public static string PrettifyCDTOC(byte[] CDTOCResponse) + { + CDTOC? decoded = DecodeCDTOC(CDTOCResponse); + return PrettifyCDTOC(decoded); + } + } +} + diff --git a/ChangeLog b/ChangeLog index 52a5858..f2ce224 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2015-10-19 Natalia Portillo + + * CD/CD.cs: + * CD/TOC.cs: + * CD/PMA.cs: + * CD/ATIP.cs: + * CD/Enums.cs: + * CD/FullTOC.cs: + * CD/Session.cs: + * CD/CDTextOnLeadIn.cs: + * DiscImageChef.Decoders.csproj: + Separated CD decoders by type. + 2015-10-19 Natalia Portillo * DVD/DVD.cs: diff --git a/DiscImageChef.Decoders.csproj b/DiscImageChef.Decoders.csproj index e2eca88..bb33630 100644 --- a/DiscImageChef.Decoders.csproj +++ b/DiscImageChef.Decoders.csproj @@ -38,7 +38,6 @@ - @@ -63,6 +62,13 @@ + + + + + + +