File indexing completed on 2024-04-28 08:41:50

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 }