File indexing completed on 2025-03-23 04:28:08
0001 /* 0002 SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "k3bcdtext.h" 0008 #include "k3bcrc.h" 0009 0010 #include <config-k3b.h> 0011 0012 #include <QDebug> 0013 #include <QSharedData> 0014 #include <QTextCodec> 0015 0016 #include <string.h> 0017 0018 0019 namespace { 0020 0021 struct cdtext_pack { 0022 unsigned char id1; 0023 unsigned char id2; 0024 unsigned char id3; 0025 #ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN 0026 unsigned char dbcc: 1; 0027 unsigned char blocknum: 3; 0028 unsigned char charpos: 4; 0029 #else 0030 unsigned char charpos: 4; 0031 unsigned char blocknum: 3; 0032 unsigned char dbcc: 1; 0033 #endif 0034 unsigned char data[12]; 0035 unsigned char crc[2]; 0036 }; 0037 0038 /** 0039 * This one is taken from cdrecord 0040 */ 0041 struct text_size_block { 0042 char charcode; 0043 char first_track; 0044 char last_track; 0045 char copyr_flags; 0046 char pack_count[16]; 0047 char last_seqnum[8]; 0048 char language_codes[8]; 0049 }; 0050 0051 void debugRawTextPackData( const unsigned char* data, int dataLen ) 0052 { 0053 qDebug() << Qt::endl << " id1 | id2 | id3 | charps | blockn | dbcc | data | crc |"; 0054 0055 cdtext_pack* pack = (cdtext_pack*)data; 0056 0057 for( int i = 0; i < dataLen/18; ++i ) { 0058 QString s; 0059 s += QString( " %1 |" ).arg( pack[i].id1, 6, 16 ); 0060 s += QString( " %1 |" ).arg( pack[i].id2, 6 ); 0061 s += QString( " %1 |" ).arg( pack[i].id3, 6 ); 0062 s += QString( " %1 |" ).arg( pack[i].charpos, 6 ); 0063 s += QString( " %1 |" ).arg( pack[i].blocknum, 6 ); 0064 s += QString( " %1 |" ).arg( pack[i].dbcc, 4 ); 0065 // char str[12]; 0066 // sprintf( str, "%c%c%c%c%c%c%c%c%c%c%c%c", 0067 // pack[i].data[0] == '\0' ? '�' : pack[i].data[0], 0068 // pack[i].data[1] == '\0' ? '�' : pack[i].data[1], 0069 // pack[i].data[2] == '\0' ? '�' : pack[i].data[2], 0070 // pack[i].data[3] == '\0' ? '�' : pack[i].data[3], 0071 // pack[i].data[4] == '\0' ? '�' : pack[i].data[4], 0072 // pack[i].data[5] == '\0' ? '�' : pack[i].data[5], 0073 // pack[i].data[6] == '\0' ? '�' : pack[i].data[6], 0074 // pack[i].data[7] == '\0' ? '�' : pack[i].data[7], 0075 // pack[i].data[8] == '\0' ? '�' : pack[i].data[8], 0076 // pack[i].data[9] == '\0' ? '�' : pack[i].data[9], 0077 // pack[i].data[10] == '\0' ? '�' : pack[i].data[10], 0078 // pack[i].data[11] == '\0' ? '�' : pack[i].data[11] ); 0079 // s += QString( " %1 |" ).arg( "'" + QCString(str,13) + "'", 14 ); 0080 // quint16 crc = pack[i].crc[0]<<8|pack[i].crc[1]; 0081 // s += QString( " %1 |" ).arg( crc ); 0082 qDebug() << s; 0083 } 0084 } 0085 0086 // TODO: remove this (see above) 0087 void fixup( QString& s ) 0088 { 0089 s.replace( '/', "_" ); 0090 s.replace( '\"', "_" ); 0091 } 0092 0093 void savePack( cdtext_pack* pack, QByteArray& data, int& dataFill ) 0094 { 0095 // create CRC 0096 quint16 crc = K3b::Device::calcX25( reinterpret_cast<unsigned char*>(pack), sizeof(cdtext_pack)-2 ); 0097 0098 // invert for Redbook compliance 0099 crc ^= 0xffff; 0100 0101 pack->crc[0] = (crc>>8) & 0xff; 0102 pack->crc[1] = crc & 0xff; 0103 0104 0105 // append the pack to data 0106 if( data.size() < dataFill + ( int )sizeof(cdtext_pack) ) 0107 data.resize( dataFill + sizeof(cdtext_pack) ); 0108 0109 ::memcpy( &data.data()[dataFill], reinterpret_cast<char*>( pack ), sizeof(cdtext_pack) ); 0110 0111 dataFill += sizeof(cdtext_pack); 0112 } 0113 0114 0115 void appendByteArray( QByteArray& a, const QByteArray& b ) 0116 { 0117 int oldSize = a.size(); 0118 a.resize( oldSize + b.size() ); 0119 ::memcpy( &a.data()[oldSize], b.data(), b.size() ); 0120 } 0121 0122 0123 QByteArray encodeCdText( const QString& s, bool* illegalChars = 0 ) 0124 { 0125 if( illegalChars ) 0126 *illegalChars = false; 0127 0128 // TODO: do this without QT 0129 QTextCodec* codec = QTextCodec::codecForName("ISO8859-1"); 0130 if( codec ) { 0131 QByteArray encoded = codec->fromUnicode( s ); 0132 return encoded; 0133 } 0134 else { 0135 QByteArray r( s.length()+1, 0 ); 0136 0137 for( int i = 0; i < s.length(); ++i ) { 0138 if( s[i].toLatin1() == 0 ) { // non-ASCII character 0139 r[i] = ' '; 0140 if( illegalChars ) 0141 *illegalChars = true; 0142 } 0143 else 0144 r[i] = s[i].toLatin1(); 0145 } 0146 0147 return r; 0148 } 0149 } 0150 } 0151 0152 0153 class K3b::Device::TrackCdText::Private : public QSharedData 0154 { 0155 public: 0156 QString title; 0157 QString performer; 0158 QString songwriter; 0159 QString composer; 0160 QString arranger; 0161 QString message; 0162 QString isrc; 0163 }; 0164 0165 0166 K3b::Device::TrackCdText::TrackCdText() 0167 : d( new Private() ) 0168 { 0169 } 0170 0171 0172 K3b::Device::TrackCdText::TrackCdText( const TrackCdText& other ) 0173 { 0174 d = other.d; 0175 } 0176 0177 0178 K3b::Device::TrackCdText::~TrackCdText() 0179 { 0180 } 0181 0182 0183 K3b::Device::TrackCdText& K3b::Device::TrackCdText::operator=( const TrackCdText& other ) 0184 { 0185 d = other.d; 0186 return *this; 0187 } 0188 0189 0190 void K3b::Device::TrackCdText::clear() 0191 { 0192 d->title.truncate(0); 0193 d->performer.truncate(0); 0194 d->songwriter.truncate(0); 0195 d->composer.truncate(0); 0196 d->arranger.truncate(0); 0197 d->message.truncate(0); 0198 d->isrc.truncate(0); 0199 } 0200 0201 0202 QString K3b::Device::TrackCdText::title() const 0203 { 0204 return d->title; 0205 } 0206 0207 0208 QString K3b::Device::TrackCdText::performer() const 0209 { 0210 return d->performer; 0211 } 0212 0213 0214 QString K3b::Device::TrackCdText::songwriter() const 0215 { 0216 return d->songwriter; 0217 } 0218 0219 0220 QString K3b::Device::TrackCdText::composer() const 0221 { 0222 return d->composer; 0223 } 0224 0225 0226 QString K3b::Device::TrackCdText::arranger() const 0227 { 0228 return d->arranger; 0229 } 0230 0231 0232 QString K3b::Device::TrackCdText::message() const 0233 { 0234 return d->message; 0235 } 0236 0237 0238 QString K3b::Device::TrackCdText::isrc() const 0239 { 0240 return d->isrc; 0241 } 0242 0243 0244 void K3b::Device::TrackCdText::setTitle( const QString& s ) 0245 { 0246 d->title = s; 0247 fixup(d->title); 0248 } 0249 0250 0251 void K3b::Device::TrackCdText::setPerformer( const QString& s ) 0252 { 0253 d->performer = s; 0254 fixup(d->performer); 0255 } 0256 0257 0258 void K3b::Device::TrackCdText::setSongwriter( const QString& s ) 0259 { 0260 d->songwriter = s; 0261 fixup(d->songwriter); 0262 } 0263 0264 0265 void K3b::Device::TrackCdText::setComposer( const QString& s ) 0266 { 0267 d->composer = s; 0268 fixup(d->composer); 0269 } 0270 0271 0272 void K3b::Device::TrackCdText::setArranger( const QString& s ) 0273 { 0274 d->arranger = s; 0275 fixup(d->arranger); 0276 } 0277 0278 0279 void K3b::Device::TrackCdText::setMessage( const QString& s ) 0280 { 0281 d->message = s; 0282 fixup(d->message); 0283 } 0284 0285 0286 void K3b::Device::TrackCdText::setIsrc( const QString& s ) 0287 { 0288 d->isrc = s; 0289 fixup(d->isrc); 0290 } 0291 0292 0293 bool K3b::Device::TrackCdText::isEmpty() const 0294 { 0295 if( !d->title.isEmpty() ) 0296 return false; 0297 if( !d->performer.isEmpty() ) 0298 return false; 0299 if( !d->songwriter.isEmpty() ) 0300 return false; 0301 if( !d->composer.isEmpty() ) 0302 return false; 0303 if( !d->arranger.isEmpty() ) 0304 return false; 0305 if( !d->message.isEmpty() ) 0306 return false; 0307 if( !d->isrc.isEmpty() ) 0308 return false; 0309 0310 return true; 0311 } 0312 0313 0314 bool K3b::Device::TrackCdText::operator==( const K3b::Device::TrackCdText& other ) const 0315 { 0316 return( d->title == other.d->title && 0317 d->performer == other.d->performer && 0318 d->songwriter == other.d->songwriter && 0319 d->composer == other.d->composer && 0320 d->arranger == other.d->arranger && 0321 d->message == other.d->message && 0322 d->isrc == other.d->isrc ); 0323 } 0324 0325 0326 bool K3b::Device::TrackCdText::operator!=( const K3b::Device::TrackCdText& other ) const 0327 { 0328 return !operator==( other ); 0329 } 0330 0331 0332 0333 // TODO: use the real CD-TEXT charset (a modified ISO8859-1) 0334 0335 class K3b::Device::CdText::Private : public QSharedData 0336 { 0337 public: 0338 Private() { 0339 } 0340 0341 Private( const Private& other ) 0342 : QSharedData( other ), 0343 title(other.title), 0344 performer(other.performer), 0345 songwriter(other.songwriter), 0346 composer(other.composer), 0347 arranger(other.arranger), 0348 message(other.message), 0349 discId(other.discId), 0350 upcEan(other.upcEan), 0351 tracks(other.tracks) { 0352 // do not copy rawData. it needs to be recreated if the cdtext changes 0353 } 0354 0355 QString title; 0356 QString performer; 0357 QString songwriter; 0358 QString composer; 0359 QString arranger; 0360 QString message; 0361 QString discId; 0362 QString upcEan; 0363 0364 QList<TrackCdText> tracks; 0365 0366 mutable QByteArray rawData; 0367 0368 QString textForPackType( int packType, int track ) const; 0369 int textLengthForPackType( int packType ) const; 0370 QByteArray createPackData( int packType, int& ) const; 0371 }; 0372 0373 0374 K3b::Device::CdText::CdText() 0375 : d( new Private() ) 0376 { 0377 } 0378 0379 0380 K3b::Device::CdText::CdText( const K3b::Device::CdText& text ) 0381 { 0382 d = text.d; 0383 } 0384 0385 0386 K3b::Device::CdText::CdText( const unsigned char* data, int len ) 0387 : d( new Private() ) 0388 { 0389 setRawPackData( data, len ); 0390 } 0391 0392 0393 K3b::Device::CdText::CdText( const QByteArray& b ) 0394 : d( new Private() ) 0395 { 0396 setRawPackData( b ); 0397 } 0398 0399 0400 K3b::Device::CdText::~CdText() 0401 { 0402 } 0403 0404 0405 K3b::Device::CdText& K3b::Device::CdText::operator=( const CdText& other ) 0406 { 0407 d = other.d; 0408 return *this; 0409 } 0410 0411 0412 K3b::Device::TrackCdText K3b::Device::CdText::operator[]( int i ) const 0413 { 0414 return d->tracks[i]; 0415 } 0416 0417 0418 K3b::Device::TrackCdText& K3b::Device::CdText::operator[]( int i ) 0419 { 0420 return track( i ); 0421 } 0422 0423 0424 int K3b::Device::CdText::count() const 0425 { 0426 return d->tracks.count(); 0427 } 0428 0429 0430 K3b::Device::TrackCdText K3b::Device::CdText::track( int i ) const 0431 { 0432 return d->tracks[i]; 0433 } 0434 0435 0436 K3b::Device::TrackCdText& K3b::Device::CdText::track( int i ) 0437 { 0438 while ( i >= d->tracks.count() ) { 0439 d->tracks.append( TrackCdText() ); 0440 } 0441 return d->tracks[i]; 0442 } 0443 0444 0445 void K3b::Device::CdText::insert( int index, const TrackCdText& tt ) 0446 { 0447 d->tracks.insert( index, tt ); 0448 } 0449 0450 0451 void K3b::Device::CdText::clear() 0452 { 0453 d->tracks.clear(); 0454 0455 d->title.clear(); 0456 d->performer.clear(); 0457 d->songwriter.clear(); 0458 d->composer.clear(); 0459 d->arranger.clear(); 0460 d->message.clear(); 0461 d->discId.clear(); 0462 d->upcEan.clear(); 0463 } 0464 0465 0466 bool K3b::Device::CdText::empty() const 0467 { 0468 if( !d->title.isEmpty() ) 0469 return false; 0470 if( !d->performer.isEmpty() ) 0471 return false; 0472 if( !d->songwriter.isEmpty() ) 0473 return false; 0474 if( !d->composer.isEmpty() ) 0475 return false; 0476 if( !d->arranger.isEmpty() ) 0477 return false; 0478 if( !d->message.isEmpty() ) 0479 return false; 0480 if( !d->discId.isEmpty() ) 0481 return false; 0482 if( !d->upcEan.isEmpty() ) 0483 return false; 0484 0485 for( int i = 0; i < count(); ++i ) 0486 if( !d->tracks.at(i).isEmpty() ) 0487 return false; 0488 0489 return true; 0490 } 0491 0492 0493 bool K3b::Device::CdText::isEmpty() const 0494 { 0495 return empty(); 0496 } 0497 0498 0499 QString K3b::Device::CdText::title() const 0500 { 0501 return d->title; 0502 } 0503 0504 0505 QString K3b::Device::CdText::performer() const 0506 { 0507 return d->performer; 0508 } 0509 0510 0511 QString K3b::Device::CdText::songwriter() const 0512 { 0513 return d->songwriter; 0514 } 0515 0516 0517 QString K3b::Device::CdText::composer() const 0518 { 0519 return d->composer; 0520 } 0521 0522 0523 QString K3b::Device::CdText::arranger() const 0524 { 0525 return d->arranger; 0526 } 0527 0528 0529 QString K3b::Device::CdText::message() const 0530 { 0531 return d->message; 0532 } 0533 0534 0535 QString K3b::Device::CdText::discId() const 0536 { 0537 return d->discId; 0538 } 0539 0540 0541 QString K3b::Device::CdText::upcEan() const 0542 { 0543 return d->upcEan; 0544 } 0545 0546 0547 void K3b::Device::CdText::setTitle( const QString& s ) 0548 { 0549 d->title = s; 0550 fixup(d->title); 0551 } 0552 0553 0554 void K3b::Device::CdText::setPerformer( const QString& s ) 0555 { 0556 d->performer = s; 0557 fixup(d->performer); 0558 } 0559 0560 0561 void K3b::Device::CdText::setSongwriter( const QString& s ) 0562 { 0563 d->songwriter = s; 0564 fixup(d->songwriter); 0565 } 0566 0567 0568 void K3b::Device::CdText::setComposer( const QString& s ) 0569 { 0570 d->composer = s; 0571 fixup(d->composer); 0572 } 0573 0574 0575 void K3b::Device::CdText::setArranger( const QString& s ) 0576 { 0577 d->arranger = s; 0578 fixup(d->arranger); 0579 } 0580 0581 0582 void K3b::Device::CdText::setMessage( const QString& s ) 0583 { 0584 d->message = s; 0585 fixup(d->message); 0586 } 0587 0588 0589 void K3b::Device::CdText::setDiscId( const QString& s ) 0590 { 0591 d->discId = s; 0592 fixup(d->discId); 0593 } 0594 0595 0596 void K3b::Device::CdText::setUpcEan( const QString& s ) 0597 { 0598 d->upcEan = s; 0599 fixup(d->upcEan); 0600 } 0601 0602 0603 void K3b::Device::CdText::setRawPackData( const unsigned char* data, int len ) 0604 { 0605 clear(); 0606 0607 int r = len%18; 0608 if( r > 0 && r != 4 ) { 0609 qDebug() << "(K3b::Device::CdText) invalid cdtext size: " << len; 0610 } 0611 else if( len-r > 0 ) { 0612 debugRawTextPackData( &data[r], len-r ); 0613 0614 cdtext_pack* pack = (cdtext_pack*)&data[r]; 0615 0616 0617 for( int i = 0; i < (len-r)/18; ++i ) { 0618 0619 if( pack[i].dbcc ) { 0620 qDebug() << "(K3b::Device::CdText) Double byte code not supported"; 0621 return; 0622 } 0623 0624 // 0625 // For some reason all crc bits are inverted. 0626 // 0627 pack[i].crc[0] ^= 0xff; 0628 pack[i].crc[1] ^= 0xff; 0629 0630 quint16 crc = calcX25( reinterpret_cast<unsigned char*>(&pack[i]), 18 ); 0631 0632 pack[i].crc[0] ^= 0xff; 0633 pack[i].crc[1] ^= 0xff; 0634 0635 if( crc != 0x0000 ) 0636 qDebug() << "(K3b::Device::CdText) CRC invalid!"; 0637 0638 0639 // 0640 // pack.data has a length of 12 0641 // 0642 // id1 tells us the tracknumber of the data (0 for global) 0643 // data may contain multiple \0. In that case after every \0 the track number increases 1 0644 // 0645 0646 char* nullPos = (char*)pack[i].data - 1; 0647 0648 int trackNo = pack[i].id2; 0649 0650 while( nullPos ) { 0651 char* nextNullPos = (char*)::memchr( nullPos+1, '\0', 11 - (nullPos - (char*)pack[i].data) ); 0652 QString txtstr; 0653 if( nextNullPos ) // take all chars up to the next null 0654 txtstr = QString::fromLatin1( (char*)nullPos+1, nextNullPos - nullPos - 1 ); 0655 else // take all chars to the end of the pack data (12 bytes) 0656 txtstr = QString::fromLatin1( (char*)nullPos+1, 11 - (nullPos - (char*)pack[i].data) ); 0657 0658 // 0659 // a tab character means to use the same as for the previous track 0660 // 0661 if( txtstr == "\t" ) 0662 txtstr = d->textForPackType( pack[i].id1, trackNo-1 ); 0663 0664 switch( pack[i].id1 ) { 0665 case 0x80: // Title 0666 if( trackNo == 0 ) 0667 d->title.append( txtstr ); 0668 else 0669 track(trackNo-1).d->title.append( txtstr ); 0670 break; 0671 0672 case 0x81: // Performer 0673 if( trackNo == 0 ) 0674 d->performer.append( txtstr ); 0675 else 0676 track(trackNo-1).d->performer.append( txtstr ); 0677 break; 0678 0679 case 0x82: // Writer 0680 if( trackNo == 0 ) 0681 d->songwriter.append( txtstr ); 0682 else 0683 track(trackNo-1).d->songwriter.append( txtstr ); 0684 break; 0685 0686 case 0x83: // Composer 0687 if( trackNo == 0 ) 0688 d->composer.append( txtstr ); 0689 else 0690 track(trackNo-1).d->composer.append( txtstr ); 0691 break; 0692 0693 case 0x84: // Arranger 0694 if( trackNo == 0 ) 0695 d->arranger.append( txtstr ); 0696 else 0697 track(trackNo-1).d->arranger.append( txtstr ); 0698 break; 0699 0700 case 0x85: // Message 0701 if( trackNo == 0 ) 0702 d->message.append( txtstr ); 0703 else 0704 track(trackNo-1).d->message.append( txtstr ); 0705 break; 0706 0707 case 0x86: // Disc identification 0708 // only global 0709 if( trackNo == 0 ) 0710 d->discId.append( txtstr ); 0711 break; 0712 0713 case 0x8e: // Upc or isrc 0714 if( trackNo == 0 ) 0715 d->upcEan.append( txtstr ); 0716 else 0717 track(trackNo-1).d->isrc.append( txtstr ); 0718 break; 0719 0720 // TODO: support for binary data 0721 // 0x88: TOC 0722 // 0x89: second TOC 0723 // 0x8f: Size information 0724 0725 default: 0726 break; 0727 } 0728 0729 trackNo++; 0730 nullPos = nextNullPos; 0731 } 0732 } 0733 0734 // remove empty fields at the end 0735 while( !d->tracks.isEmpty() && d->tracks.last().isEmpty() ) { 0736 d->tracks.removeLast(); 0737 } 0738 0739 // we preserve the original data for clean 1-to-1 copies 0740 d->rawData = QByteArray( reinterpret_cast<const char*>(data), len ); 0741 } 0742 else 0743 qDebug() << "(K3b::Device::CdText) zero-sized CD-TEXT: " << len; 0744 } 0745 0746 0747 void K3b::Device::CdText::setRawPackData( const QByteArray& b ) 0748 { 0749 setRawPackData( reinterpret_cast<const unsigned char*>(b.data()), b.size() ); 0750 } 0751 0752 QByteArray K3b::Device::CdText::rawPackData() const 0753 { 0754 if( d->rawData.isEmpty() ) { 0755 // FIXME: every pack block may only consist of up to 255 packs. 0756 0757 int pc = 0; 0758 int alreadyCountedPacks = 0; 0759 0760 0761 // 0762 // prepare the size information block 0763 // 0764 text_size_block tsize; 0765 ::memset( &tsize, 0, sizeof(text_size_block) ); 0766 tsize.charcode = 0; // ISO 8859-1 0767 tsize.first_track = 1; 0768 tsize.last_track = count(); 0769 tsize.pack_count[0xF] = 3; 0770 tsize.language_codes[0] = 0x09; // English (from cdrecord) 0771 0772 0773 // 0774 // create the CD-Text packs 0775 // 0776 QByteArray data; 0777 for( int i = 0; i <= 6; ++i ) { 0778 if( d->textLengthForPackType( 0x80 | i ) ) { 0779 appendByteArray( data, d->createPackData( 0x80 | i, pc ) ); 0780 tsize.pack_count[i] = pc - alreadyCountedPacks; 0781 alreadyCountedPacks = pc; 0782 } 0783 } 0784 if( d->textLengthForPackType( 0x8E ) ) { 0785 appendByteArray( data, d->createPackData( 0x8E, pc ) ); 0786 tsize.pack_count[0xE] = pc - alreadyCountedPacks; 0787 alreadyCountedPacks = pc; 0788 } 0789 0790 0791 // pc is the number of the next pack and we add 3 size packs 0792 tsize.last_seqnum[0] = pc + 2; 0793 0794 0795 // 0796 // create the size info packs 0797 // 0798 int dataFill = data.size(); 0799 data.resize( data.size() + 3 * sizeof(cdtext_pack) ); 0800 for( int i = 0; i < 3; ++i ) { 0801 cdtext_pack pack; 0802 ::memset( &pack, 0, sizeof(cdtext_pack) ); 0803 pack.id1 = 0x8F; 0804 pack.id2 = i; 0805 pack.id3 = pc+i; 0806 ::memcpy( pack.data, &reinterpret_cast<char*>(&tsize)[i*12], 12 ); 0807 savePack( &pack, data, dataFill ); 0808 } 0809 0810 // 0811 // add MMC header 0812 // 0813 QByteArray a( 4, 0 ); 0814 a[0] = (data.size()+2)>>8 & 0xff; 0815 a[1] = (data.size()+2) & 0xff; 0816 a[2] = a[3] = 0; 0817 appendByteArray( a, data ); 0818 0819 d->rawData = a; 0820 } 0821 0822 return d->rawData; 0823 } 0824 0825 0826 // this method also creates completely empty packs 0827 QByteArray K3b::Device::CdText::Private::createPackData( int packType, int& packCount ) const 0828 { 0829 QByteArray data; 0830 int dataFill = 0; 0831 QByteArray text = encodeCdText( textForPackType( packType, 0 ) ); 0832 int currentTrack = 0; 0833 int textPos = 0; 0834 int packPos = 0; 0835 0836 // 0837 // initialize the first pack 0838 // 0839 cdtext_pack pack; 0840 ::memset( &pack, 0, sizeof(cdtext_pack) ); 0841 pack.id1 = packType; 0842 pack.id3 = packCount; 0843 0844 // 0845 // We break this loop when all texts have been packed 0846 // 0847 while( 1 ) { 0848 // 0849 // Copy as many bytes as possible into the pack 0850 // 0851 size_t copyBytes = qMin(12 - packPos, text.length() - textPos); 0852 if (copyBytes) { 0853 ::memcpy(reinterpret_cast<char*>(&pack.data[packPos]), 0854 &text.data()[textPos], copyBytes ); 0855 } 0856 textPos += copyBytes; 0857 packPos += copyBytes; 0858 0859 0860 // 0861 // Check if the packdata is full 0862 // 0863 if( packPos > 11 ) { 0864 0865 savePack( &pack, data, dataFill ); 0866 ++packCount; 0867 0868 // 0869 // reset the pack 0870 // 0871 ::memset( &pack, 0, sizeof(cdtext_pack) ); 0872 pack.id1 = packType; 0873 pack.id2 = currentTrack; 0874 pack.id3 = packCount; 0875 packPos = 0; 0876 0877 // update the charpos in case we continue a text in the next pack 0878 if( textPos <= text.length() ) 0879 pack.charpos = ( textPos > 15 ? 15 : textPos ); 0880 } 0881 0882 0883 // 0884 // Check if we have no text data left 0885 // 0886 if( textPos >= text.length() ) { 0887 0888 // add one zero spacer byte 0889 ++packPos; 0890 0891 ++currentTrack; 0892 0893 // Check if all texts have been packed 0894 if( currentTrack > tracks.count() ) { 0895 savePack( &pack, data, dataFill ); 0896 ++packCount; 0897 0898 data.resize( dataFill ); 0899 return data; 0900 } 0901 0902 // next text block 0903 text = encodeCdText( textForPackType( packType, currentTrack ) ); 0904 textPos = 0; 0905 } 0906 } 0907 } 0908 0909 0910 // track 0 means global cdtext 0911 QString K3b::Device::CdText::Private::textForPackType( int packType, int track ) const 0912 { 0913 switch( packType ) { 0914 default: 0915 case 0x80: 0916 if( track == 0 ) 0917 return title; 0918 else if( track > 0 && track <= tracks.count() ) 0919 return tracks[track-1].title(); 0920 else 0921 return QString(); 0922 0923 case 0x81: 0924 if( track == 0 ) 0925 return performer; 0926 else if( track > 0 && track <= tracks.count() ) 0927 return tracks[track-1].performer(); 0928 else 0929 return QString(); 0930 0931 case 0x82: 0932 if( track == 0 ) 0933 return songwriter; 0934 else if( track > 0 && track <= tracks.count() ) 0935 return tracks[track-1].songwriter(); 0936 else 0937 return QString(); 0938 0939 case 0x83: 0940 if( track == 0 ) 0941 return composer; 0942 else if( track > 0 && track <= tracks.count() ) 0943 return tracks[track-1].composer(); 0944 else 0945 return QString(); 0946 0947 case 0x84: 0948 if( track == 0 ) 0949 return arranger; 0950 else if( track > 0 && track <= tracks.count() ) 0951 return tracks[track-1].arranger(); 0952 else 0953 return QString(); 0954 0955 case 0x85: 0956 if( track == 0 ) 0957 return message; 0958 else if( track > 0 && track <= tracks.count() ) 0959 return tracks[track-1].message(); 0960 else 0961 return QString(); 0962 0963 case 0x86: 0964 if( track == 0 ) 0965 return discId; 0966 else 0967 return QString(); 0968 0969 // case 0x87: 0970 // if( track == 0 ) 0971 // return genre; 0972 // else if( track > 0 && track <= tracks.count() ) 0973 // return tracks[track-1].title(); 0974 // else 0975 // return QString(); 0976 0977 case 0x8E: 0978 if( track == 0 ) 0979 return upcEan; 0980 else if( track > 0 && track <= tracks.count() ) 0981 return tracks[track-1].isrc(); 0982 else 0983 return QString(); 0984 } 0985 } 0986 0987 0988 // count the overall length of a certain packtype texts 0989 int K3b::Device::CdText::Private::textLengthForPackType( int packType ) const 0990 { 0991 int len = 0; 0992 for( int i = 0; i <= tracks.count(); ++i ) 0993 len += encodeCdText( textForPackType( packType, i ) ).length(); 0994 return len; 0995 } 0996 0997 0998 void K3b::Device::CdText::debug() const 0999 { 1000 // debug the stuff 1001 qDebug() << "CD-TEXT data:" << Qt::endl 1002 << "Global:" << Qt::endl 1003 << " Title: '" << title() << "'" << Qt::endl 1004 << " Performer: '" << performer() << "'" << Qt::endl 1005 << " Songwriter: '" << songwriter() << "'" << Qt::endl 1006 << " Composer: '" << composer() << "'" << Qt::endl 1007 << " Arranger: '" << arranger() << "'" << Qt::endl 1008 << " Message: '" << message() << "'" << Qt::endl 1009 << " Disc ID: '" << discId() << "'" << Qt::endl 1010 << " Upc Ean: '" << upcEan() << "'" << Qt::endl; 1011 for( int i = 0; i < count(); ++i ) { 1012 qDebug() << "Track " << (i+1) << ":" << Qt::endl 1013 << " Title: '" << d->tracks[i].title() << "'" << Qt::endl 1014 << " Performer: '" << d->tracks[i].performer() << "'" << Qt::endl 1015 << " Songwriter: '" << d->tracks[i].songwriter() << "'" << Qt::endl 1016 << " Composer: '" << d->tracks[i].composer() << "'" << Qt::endl 1017 << " Arranger: '" << d->tracks[i].arranger() << "'" << Qt::endl 1018 << " Message: '" << d->tracks[i].message() << "'" << Qt::endl 1019 << " Isrc: '" << d->tracks[i].isrc() << "'" << Qt::endl; 1020 } 1021 } 1022 1023 1024 bool K3b::Device::CdText::operator==( const K3b::Device::CdText& other ) const 1025 { 1026 return( d->title == other.d->title && 1027 d->performer == other.d->performer && 1028 d->songwriter == other.d->songwriter && 1029 d->composer == other.d->composer && 1030 d->arranger == other.d->arranger && 1031 d->message == other.d->message && 1032 d->discId == other.d->discId && 1033 d->upcEan == other.d->upcEan && 1034 d->tracks == other.d->tracks ); 1035 } 1036 1037 1038 bool K3b::Device::CdText::operator!=( const K3b::Device::CdText& other ) const 1039 { 1040 return !operator==( other ); 1041 } 1042 1043 1044 bool K3b::Device::CdText::checkCrc( const unsigned char* data, int len ) 1045 { 1046 int r = len%18; 1047 if( r > 0 && r != 4 ) { 1048 qDebug() << "(K3b::Device::CdText) invalid cdtext size: " << len; 1049 return false; 1050 } 1051 else { 1052 len -= r; 1053 1054 // TODO: what if the crc field is not used? All zeros? 1055 1056 for( int i = 0; i < (len-r)/18; ++i ) { 1057 cdtext_pack* pack = (cdtext_pack*)&data[r]; 1058 1059 // 1060 // For some reason all crc bits are inverted. 1061 // 1062 pack[i].crc[0] ^= 0xff; 1063 pack[i].crc[1] ^= 0xff; 1064 1065 int crc = calcX25( reinterpret_cast<unsigned char*>(&pack[i]), 18 ); 1066 1067 pack[i].crc[0] ^= 0xff; 1068 pack[i].crc[1] ^= 0xff; 1069 1070 if( crc != 0x0000 ) 1071 return false; 1072 } 1073 1074 return true; 1075 } 1076 } 1077 1078 1079 bool K3b::Device::CdText::checkCrc( const QByteArray& rawData ) 1080 { 1081 return checkCrc( reinterpret_cast<const unsigned char*>(rawData.data()), rawData.size() ); 1082 }