From 46840cc545083088a1d17866bad75bb808445e24 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Sat, 6 Sep 2014 03:45:53 +0100 Subject: [PATCH] Implement decoders for CD disk structures. --- DiscImageChef/Commands/Decode.cs | 84 ++ DiscImageChef/Decoders/CD.cs | 1603 ++++++++++++++++++++++++++++ DiscImageChef/DiscImageChef.csproj | 1 + 3 files changed, 1688 insertions(+) create mode 100644 DiscImageChef/Decoders/CD.cs diff --git a/DiscImageChef/Commands/Decode.cs b/DiscImageChef/Commands/Decode.cs index 2fdc8413..ccbb8dd5 100644 --- a/DiscImageChef/Commands/Decode.cs +++ b/DiscImageChef/Commands/Decode.cs @@ -89,6 +89,90 @@ namespace DiscImageChef.Commands } break; } + case DiskTagType.CD_ATIP: + { + byte[] atip = inputFormat.ReadDiskTag(DiskTagType.CD_ATIP); + if (atip == null) + Console.WriteLine("Error reading CD ATIP from disc image"); + else + { + Console.WriteLine("CD ATIP:"); + Console.WriteLine("================================================================================"); + Console.WriteLine(Decoders.CD.PrettifyCDATIP(atip)); + Console.WriteLine("================================================================================"); + } + break; + } + case DiskTagType.CD_FullTOC: + { + byte[] fulltoc = inputFormat.ReadDiskTag(DiskTagType.CD_FullTOC); + if (fulltoc == null) + Console.WriteLine("Error reading CD full TOC from disc image"); + else + { + Console.WriteLine("CD full TOC:"); + Console.WriteLine("================================================================================"); + Console.WriteLine(Decoders.CD.PrettifyCDFullTOC(fulltoc)); + Console.WriteLine("================================================================================"); + } + break; + } + case DiskTagType.CD_PMA: + { + byte[] pma = inputFormat.ReadDiskTag(DiskTagType.CD_PMA); + if (pma == null) + Console.WriteLine("Error reading CD PMA from disc image"); + else + { + Console.WriteLine("CD PMA:"); + Console.WriteLine("================================================================================"); + Console.WriteLine(Decoders.CD.PrettifyCDPMA(pma)); + Console.WriteLine("================================================================================"); + } + break; + } + case DiskTagType.CD_SessionInfo: + { + byte[] sessioninfo = inputFormat.ReadDiskTag(DiskTagType.CD_SessionInfo); + if (sessioninfo == null) + Console.WriteLine("Error reading CD session information from disc image"); + else + { + Console.WriteLine("CD session information:"); + Console.WriteLine("================================================================================"); + Console.WriteLine(Decoders.CD.PrettifyCDSessionInfo(sessioninfo)); + Console.WriteLine("================================================================================"); + } + break; + } + case DiskTagType.CD_TEXT: + { + byte[] cdtext = inputFormat.ReadDiskTag(DiskTagType.CD_TEXT); + if (cdtext == null) + Console.WriteLine("Error reading CD-TEXT from disc image"); + else + { + Console.WriteLine("CD-TEXT:"); + Console.WriteLine("================================================================================"); + Console.WriteLine(Decoders.CD.PrettifyCDTextLeadIn(cdtext)); + Console.WriteLine("================================================================================"); + } + break; + } + case DiskTagType.CD_TOC: + { + byte[] toc = inputFormat.ReadDiskTag(DiskTagType.CD_TOC); + if (toc == null) + Console.WriteLine("Error reading CD TOC from disc image"); + else + { + Console.WriteLine("CD TOC:"); + Console.WriteLine("================================================================================"); + Console.WriteLine(Decoders.CD.PrettifyCDTOC(toc)); + Console.WriteLine("================================================================================"); + } + break; + } default: Console.WriteLine("Decoder for disk tag type \"{0}\" not yet implemented, sorry.", tag); break; diff --git a/DiscImageChef/Decoders/CD.cs b/DiscImageChef/Decoders/CD.cs new file mode 100644 index 00000000..a1ad5c8c --- /dev/null +++ b/DiscImageChef/Decoders/CD.cs @@ -0,0 +1,1603 @@ +/*************************************************************************** +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; + +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) + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDTOC 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 (MainClass.isDebug) + { + sb.AppendFormat("Reserved1: {0:X2}", descriptor.Reserved1).AppendLine(); + sb.AppendFormat("Reserved2: {0:X2}", descriptor.Reserved2).AppendLine(); + } + + 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) + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDSessionInfo 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 (MainClass.isDebug) + { + sb.AppendFormat("Reserved1: {0:X2}", descriptor.Reserved1).AppendLine(); + sb.AppendFormat("Reserved2: {0:X2}", descriptor.Reserved2).AppendLine(); + } + + 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) + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDFullTOC 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"); + if (MainClass.isDebug) + { + 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) + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDPMA 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 (MainClass.isDebug) + { + sb.AppendFormat("Reserved1: 0x{0:X2}", response.Reserved1).AppendLine(); + sb.AppendFormat("Reserved2: 0x{0:X2}", response.Reserved2).AppendLine(); + } + foreach (CDPMADescriptors descriptor in response.PMADescriptors) + { + if (MainClass.isDebug) + sb.AppendFormat("Reserved1: 0x{0:X2}", descriptor.Reserved).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(); + } + + 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) + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDATIP Decoder): Expected CDATIP 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) + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (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 (MainClass.isDebug) + { + sb.AppendFormat("Reserved1: 0x{0:X2}", response.Reserved1).AppendLine(); + sb.AppendFormat("Reserved2: 0x{0:X2}", response.Reserved2).AppendLine(); + } + 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/DiscImageChef/DiscImageChef.csproj b/DiscImageChef/DiscImageChef.csproj index 43ac6e47..c3ef9897 100644 --- a/DiscImageChef/DiscImageChef.csproj +++ b/DiscImageChef/DiscImageChef.csproj @@ -101,6 +101,7 @@ +