// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : CDTextOnLeadIn.cs // Author(s) : Natalia Portillo // // Component : Device structures decoders. // // --[ Description ] ---------------------------------------------------------- // // Decodes CD-TEXT on Lead-In. // // --[ License ] -------------------------------------------------------------- // // This library is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation; either version 2.1 of the // License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, see . // // ---------------------------------------------------------------------------- // Copyright © 2011-2023 Natalia Portillo // ****************************************************************************/ using System; using System.Diagnostics.CodeAnalysis; using System.Text; using Aaru.Console; using Aaru.Helpers; namespace Aaru.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 [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "MemberCanBeInternal"), SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "NotAccessedField.Global")] 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 static CDText? Decode(byte[] CDTextResponse) { if(CDTextResponse is not { Length: > 4 }) return null; var decoded = new CDText { DataLength = BigEndianBitConverter.ToUInt16(CDTextResponse, 0), Reserved1 = CDTextResponse[2], Reserved2 = CDTextResponse[3] }; decoded.DataPacks = new CDTextPack[(decoded.DataLength - 2) / 18]; if(decoded.DataLength == 2) return null; if(decoded.DataLength + 2 != CDTextResponse.Length) { AaruConsole.DebugWriteLine("CD-TEXT decoder", Localization. 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 * 18) + 4] & 0x80); decoded.DataPacks[i].BlockNumber = (byte)((CDTextResponse[3 + (i * 18) + 4] & 0x70) >> 4); decoded.DataPacks[i].CharacterPosition = (byte)(CDTextResponse[3 + (i * 18) + 4] & 0x0F); decoded.DataPacks[i].TextDataField = new byte[12]; Array.Copy(CDTextResponse, 4 + (i * 18) + 4, decoded.DataPacks[i].TextDataField, 0, 12); decoded.DataPacks[i].CRC = BigEndianBitConverter.ToUInt16(CDTextResponse, 16 + (i * 18) + 4); } return decoded; } public static string Prettify(CDText? CDTextResponse) { if(CDTextResponse == null) return null; CDText response = CDTextResponse.Value; var sb = new StringBuilder(); #if DEBUG if(response.Reserved1 != 0) sb.AppendFormat(Localization.Reserved1_equals_0_X8, response.Reserved1).AppendLine(); if(response.Reserved2 != 0) sb.AppendFormat(Localization.Reserved2_equals_0_X8, response.Reserved2).AppendLine(); #endif foreach(CDTextPack descriptor in response.DataPacks) if((descriptor.HeaderID1 & 0x80) != 0x80) { // Ignore NOPs if((descriptor.HeaderID1 & 0x80) != 0) sb.AppendFormat(Localization.Incorrect_CD_Text_pack_type_0_not_decoding, descriptor.HeaderID1). AppendLine(); } else { switch(descriptor.HeaderID1) { case 0x80: { if(descriptor.HeaderID2 == 0x00) sb.AppendLine(Localization.CD_Text_pack_contains_title_for_album); else sb.AppendFormat(Localization.CD_Text_pack_contains_title_for_track_0, descriptor.HeaderID2). AppendLine(); break; } case 0x81: { if(descriptor.HeaderID2 == 0x00) sb.AppendLine(Localization.CD_Text_pack_contains_performer_for_album); else sb.AppendFormat(Localization.CD_Text_pack_contains_performer_for_track_0, descriptor.HeaderID2).AppendLine(); break; } case 0x82: { if(descriptor.HeaderID2 == 0x00) sb.AppendLine(Localization.CD_Text_pack_contains_songwriter_for_album); else sb.AppendFormat(Localization.CD_Text_pack_contains_songwriter_for_track_0, descriptor.HeaderID2).AppendLine(); break; } case 0x83: { if(descriptor.HeaderID2 == 0x00) sb.AppendLine(Localization.album); else sb.AppendFormat(Localization.track_0, descriptor.HeaderID2).AppendLine(); break; } case 0x84: { if(descriptor.HeaderID2 == 0x00) sb.AppendLine(Localization.CD_Text_pack_contains_arranger_for_album); else sb.AppendFormat(Localization.CD_Text_pack_contains_arranger_for_track_0, descriptor.HeaderID2).AppendLine(); break; } case 0x85: { if(descriptor.HeaderID2 == 0x00) sb.AppendLine(Localization.CD_Text_pack_contains_content_provider_message_for_album); else sb.AppendFormat(Localization.CD_Text_pack_contains_content_provider_message_for_track_0, descriptor.HeaderID2).AppendLine(); break; } case 0x86: { sb.AppendLine(Localization.CD_Text_pack_contains_disc_identification_information); break; } case 0x87: { sb.AppendLine(Localization.CD_Text_pack_contains_genre_identification_information); break; } case 0x88: { sb.AppendLine(Localization.CD_Text_pack_contains_table_of_contents_information); break; } case 0x89: { sb.AppendLine(Localization.CD_Text_pack_contains_second_table_of_contents_information); break; } case 0x8A: case 0x8B: case 0x8C: { sb.AppendLine(Localization.CD_Text_pack_contains_reserved_data); break; } case 0x8D: { sb.AppendLine(Localization.CD_Text_pack_contains_data_reserved_for_content_provider_only); break; } case 0x8E: { if(descriptor.HeaderID2 == 0x00) sb.AppendLine(Localization.CD_Text_pack_contains_UPC); else sb.AppendFormat(Localization.track_0, descriptor.HeaderID2).AppendLine(); break; } case 0x8F: { sb.AppendLine(Localization.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(Localization.Double_Byte_Character_Code_is_used); sb.AppendFormat(Localization.Block_number_0, descriptor.BlockNumber).AppendLine(); sb.AppendFormat(Localization.Character_position_0, descriptor.CharacterPosition).AppendLine(); sb.AppendFormat(Localization.Text_field_0, StringHandlers.CToString(descriptor.TextDataField, Encoding.GetEncoding("iso-8859-1"))).AppendLine(); break; } default: { sb.AppendFormat(Localization.Binary_contents_0, PrintHex.ByteArrayToHexArrayString(descriptor.TextDataField, 28)).AppendLine(); break; } } sb.AppendFormat(Localization.CRC_0_X4, descriptor.CRC).AppendLine(); } return sb.ToString(); } public static string Prettify(byte[] CDTextResponse) { CDText? decoded = Decode(CDTextResponse); return Prettify(decoded); } public struct CDText { /// Total size of returned CD-Text information minus this field public ushort 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 ushort CRC; } }