1 /*
2 SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "k3bcdtext.h"
8 #include "k3bcrc.h"
9
10 #include <config-k3b.h>
11
12 #include <QDebug>
13 #include <QSharedData>
14 #include <QTextCodec>
15
16 #include <string.h>
17
18
19 namespace {
20
21 struct cdtext_pack {
22 unsigned char id1;
23 unsigned char id2;
24 unsigned char id3;
25 #ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
26 unsigned char dbcc: 1;
27 unsigned char blocknum: 3;
28 unsigned char charpos: 4;
29 #else
30 unsigned char charpos: 4;
31 unsigned char blocknum: 3;
32 unsigned char dbcc: 1;
33 #endif
34 unsigned char data[12];
35 unsigned char crc[2];
36 };
37
38 /**
39 * This one is taken from cdrecord
40 */
41 struct text_size_block {
42 char charcode;
43 char first_track;
44 char last_track;
45 char copyr_flags;
46 char pack_count[16];
47 char last_seqnum[8];
48 char language_codes[8];
49 };
50
debugRawTextPackData(const unsigned char * data,int dataLen)51 void debugRawTextPackData( const unsigned char* data, int dataLen )
52 {
53 qDebug() << endl << " id1 | id2 | id3 | charps | blockn | dbcc | data | crc |";
54
55 cdtext_pack* pack = (cdtext_pack*)data;
56
57 for( int i = 0; i < dataLen/18; ++i ) {
58 QString s;
59 s += QString( " %1 |" ).arg( pack[i].id1, 6, 16 );
60 s += QString( " %1 |" ).arg( pack[i].id2, 6 );
61 s += QString( " %1 |" ).arg( pack[i].id3, 6 );
62 s += QString( " %1 |" ).arg( pack[i].charpos, 6 );
63 s += QString( " %1 |" ).arg( pack[i].blocknum, 6 );
64 s += QString( " %1 |" ).arg( pack[i].dbcc, 4 );
65 // char str[12];
66 // sprintf( str, "%c%c%c%c%c%c%c%c%c%c%c%c",
67 // pack[i].data[0] == '\0' ? '�' : pack[i].data[0],
68 // pack[i].data[1] == '\0' ? '�' : pack[i].data[1],
69 // pack[i].data[2] == '\0' ? '�' : pack[i].data[2],
70 // pack[i].data[3] == '\0' ? '�' : pack[i].data[3],
71 // pack[i].data[4] == '\0' ? '�' : pack[i].data[4],
72 // pack[i].data[5] == '\0' ? '�' : pack[i].data[5],
73 // pack[i].data[6] == '\0' ? '�' : pack[i].data[6],
74 // pack[i].data[7] == '\0' ? '�' : pack[i].data[7],
75 // pack[i].data[8] == '\0' ? '�' : pack[i].data[8],
76 // pack[i].data[9] == '\0' ? '�' : pack[i].data[9],
77 // pack[i].data[10] == '\0' ? '�' : pack[i].data[10],
78 // pack[i].data[11] == '\0' ? '�' : pack[i].data[11] );
79 // s += QString( " %1 |" ).arg( "'" + QCString(str,13) + "'", 14 );
80 // quint16 crc = pack[i].crc[0]<<8|pack[i].crc[1];
81 // s += QString( " %1 |" ).arg( crc );
82 qDebug() << s;
83 }
84 }
85
86 // TODO: remove this (see above)
fixup(QString & s)87 void fixup( QString& s )
88 {
89 s.replace( '/', "_" );
90 s.replace( '\"', "_" );
91 }
92
savePack(cdtext_pack * pack,QByteArray & data,int & dataFill)93 void savePack( cdtext_pack* pack, QByteArray& data, int& dataFill )
94 {
95 // create CRC
96 quint16 crc = K3b::Device::calcX25( reinterpret_cast<unsigned char*>(pack), sizeof(cdtext_pack)-2 );
97
98 // invert for Redbook compliance
99 crc ^= 0xffff;
100
101 pack->crc[0] = (crc>>8) & 0xff;
102 pack->crc[1] = crc & 0xff;
103
104
105 // append the pack to data
106 if( data.size() < dataFill + ( int )sizeof(cdtext_pack) )
107 data.resize( dataFill + sizeof(cdtext_pack) );
108
109 ::memcpy( &data.data()[dataFill], reinterpret_cast<char*>( pack ), sizeof(cdtext_pack) );
110
111 dataFill += sizeof(cdtext_pack);
112 }
113
114
appendByteArray(QByteArray & a,const QByteArray & b)115 void appendByteArray( QByteArray& a, const QByteArray& b )
116 {
117 int oldSize = a.size();
118 a.resize( oldSize + b.size() );
119 ::memcpy( &a.data()[oldSize], b.data(), b.size() );
120 }
121
122
encodeCdText(const QString & s,bool * illegalChars=0)123 QByteArray encodeCdText( const QString& s, bool* illegalChars = 0 )
124 {
125 if( illegalChars )
126 *illegalChars = false;
127
128 // TODO: do this without QT
129 QTextCodec* codec = QTextCodec::codecForName("ISO8859-1");
130 if( codec ) {
131 QByteArray encoded = codec->fromUnicode( s );
132 return encoded;
133 }
134 else {
135 QByteArray r( s.length()+1, 0 );
136
137 for( int i = 0; i < s.length(); ++i ) {
138 if( s[i].toLatin1() == 0 ) { // non-ASCII character
139 r[i] = ' ';
140 if( illegalChars )
141 *illegalChars = true;
142 }
143 else
144 r[i] = s[i].toLatin1();
145 }
146
147 return r;
148 }
149 }
150 }
151
152
153 class K3b::Device::TrackCdText::Private : public QSharedData
154 {
155 public:
156 QString title;
157 QString performer;
158 QString songwriter;
159 QString composer;
160 QString arranger;
161 QString message;
162 QString isrc;
163 };
164
165
TrackCdText()166 K3b::Device::TrackCdText::TrackCdText()
167 : d( new Private() )
168 {
169 }
170
171
TrackCdText(const TrackCdText & other)172 K3b::Device::TrackCdText::TrackCdText( const TrackCdText& other )
173 {
174 d = other.d;
175 }
176
177
~TrackCdText()178 K3b::Device::TrackCdText::~TrackCdText()
179 {
180 }
181
182
operator =(const TrackCdText & other)183 K3b::Device::TrackCdText& K3b::Device::TrackCdText::operator=( const TrackCdText& other )
184 {
185 d = other.d;
186 return *this;
187 }
188
189
clear()190 void K3b::Device::TrackCdText::clear()
191 {
192 d->title.truncate(0);
193 d->performer.truncate(0);
194 d->songwriter.truncate(0);
195 d->composer.truncate(0);
196 d->arranger.truncate(0);
197 d->message.truncate(0);
198 d->isrc.truncate(0);
199 }
200
201
title() const202 QString K3b::Device::TrackCdText::title() const
203 {
204 return d->title;
205 }
206
207
performer() const208 QString K3b::Device::TrackCdText::performer() const
209 {
210 return d->performer;
211 }
212
213
songwriter() const214 QString K3b::Device::TrackCdText::songwriter() const
215 {
216 return d->songwriter;
217 }
218
219
composer() const220 QString K3b::Device::TrackCdText::composer() const
221 {
222 return d->composer;
223 }
224
225
arranger() const226 QString K3b::Device::TrackCdText::arranger() const
227 {
228 return d->arranger;
229 }
230
231
message() const232 QString K3b::Device::TrackCdText::message() const
233 {
234 return d->message;
235 }
236
237
isrc() const238 QString K3b::Device::TrackCdText::isrc() const
239 {
240 return d->isrc;
241 }
242
243
setTitle(const QString & s)244 void K3b::Device::TrackCdText::setTitle( const QString& s )
245 {
246 d->title = s;
247 fixup(d->title);
248 }
249
250
setPerformer(const QString & s)251 void K3b::Device::TrackCdText::setPerformer( const QString& s )
252 {
253 d->performer = s;
254 fixup(d->performer);
255 }
256
257
setSongwriter(const QString & s)258 void K3b::Device::TrackCdText::setSongwriter( const QString& s )
259 {
260 d->songwriter = s;
261 fixup(d->songwriter);
262 }
263
264
setComposer(const QString & s)265 void K3b::Device::TrackCdText::setComposer( const QString& s )
266 {
267 d->composer = s;
268 fixup(d->composer);
269 }
270
271
setArranger(const QString & s)272 void K3b::Device::TrackCdText::setArranger( const QString& s )
273 {
274 d->arranger = s;
275 fixup(d->arranger);
276 }
277
278
setMessage(const QString & s)279 void K3b::Device::TrackCdText::setMessage( const QString& s )
280 {
281 d->message = s;
282 fixup(d->message);
283 }
284
285
setIsrc(const QString & s)286 void K3b::Device::TrackCdText::setIsrc( const QString& s )
287 {
288 d->isrc = s;
289 fixup(d->isrc);
290 }
291
292
isEmpty() const293 bool K3b::Device::TrackCdText::isEmpty() const
294 {
295 if( !d->title.isEmpty() )
296 return false;
297 if( !d->performer.isEmpty() )
298 return false;
299 if( !d->songwriter.isEmpty() )
300 return false;
301 if( !d->composer.isEmpty() )
302 return false;
303 if( !d->arranger.isEmpty() )
304 return false;
305 if( !d->message.isEmpty() )
306 return false;
307 if( !d->isrc.isEmpty() )
308 return false;
309
310 return true;
311 }
312
313
operator ==(const K3b::Device::TrackCdText & other) const314 bool K3b::Device::TrackCdText::operator==( const K3b::Device::TrackCdText& other ) const
315 {
316 return( d->title == other.d->title &&
317 d->performer == other.d->performer &&
318 d->songwriter == other.d->songwriter &&
319 d->composer == other.d->composer &&
320 d->arranger == other.d->arranger &&
321 d->message == other.d->message &&
322 d->isrc == other.d->isrc );
323 }
324
325
operator !=(const K3b::Device::TrackCdText & other) const326 bool K3b::Device::TrackCdText::operator!=( const K3b::Device::TrackCdText& other ) const
327 {
328 return !operator==( other );
329 }
330
331
332
333 // TODO: use the real CD-TEXT charset (a modified ISO8859-1)
334
335 class K3b::Device::CdText::Private : public QSharedData
336 {
337 public:
Private()338 Private() {
339 }
340
Private(const Private & other)341 Private( const Private& other )
342 : QSharedData( other ),
343 title(other.title),
344 performer(other.performer),
345 songwriter(other.songwriter),
346 composer(other.composer),
347 arranger(other.arranger),
348 message(other.message),
349 discId(other.discId),
350 upcEan(other.upcEan),
351 tracks(other.tracks) {
352 // do not copy rawData. it needs to be recreated if the cdtext changes
353 }
354
355 QString title;
356 QString performer;
357 QString songwriter;
358 QString composer;
359 QString arranger;
360 QString message;
361 QString discId;
362 QString upcEan;
363
364 QList<TrackCdText> tracks;
365
366 mutable QByteArray rawData;
367
368 QString textForPackType( int packType, int track ) const;
369 int textLengthForPackType( int packType ) const;
370 QByteArray createPackData( int packType, int& ) const;
371 };
372
373
CdText()374 K3b::Device::CdText::CdText()
375 : d( new Private() )
376 {
377 }
378
379
CdText(const K3b::Device::CdText & text)380 K3b::Device::CdText::CdText( const K3b::Device::CdText& text )
381 {
382 d = text.d;
383 }
384
385
CdText(const unsigned char * data,int len)386 K3b::Device::CdText::CdText( const unsigned char* data, int len )
387 : d( new Private() )
388 {
389 setRawPackData( data, len );
390 }
391
392
CdText(const QByteArray & b)393 K3b::Device::CdText::CdText( const QByteArray& b )
394 : d( new Private() )
395 {
396 setRawPackData( b );
397 }
398
399
~CdText()400 K3b::Device::CdText::~CdText()
401 {
402 }
403
404
operator =(const CdText & other)405 K3b::Device::CdText& K3b::Device::CdText::operator=( const CdText& other )
406 {
407 d = other.d;
408 return *this;
409 }
410
411
operator [](int i) const412 K3b::Device::TrackCdText K3b::Device::CdText::operator[]( int i ) const
413 {
414 return d->tracks[i];
415 }
416
417
operator [](int i)418 K3b::Device::TrackCdText& K3b::Device::CdText::operator[]( int i )
419 {
420 return track( i );
421 }
422
423
count() const424 int K3b::Device::CdText::count() const
425 {
426 return d->tracks.count();
427 }
428
429
track(int i) const430 K3b::Device::TrackCdText K3b::Device::CdText::track( int i ) const
431 {
432 return d->tracks[i];
433 }
434
435
track(int i)436 K3b::Device::TrackCdText& K3b::Device::CdText::track( int i )
437 {
438 while ( i >= d->tracks.count() ) {
439 d->tracks.append( TrackCdText() );
440 }
441 return d->tracks[i];
442 }
443
444
insert(int index,const TrackCdText & tt)445 void K3b::Device::CdText::insert( int index, const TrackCdText& tt )
446 {
447 d->tracks.insert( index, tt );
448 }
449
450
clear()451 void K3b::Device::CdText::clear()
452 {
453 d->tracks.clear();
454
455 d->title.clear();
456 d->performer.clear();
457 d->songwriter.clear();
458 d->composer.clear();
459 d->arranger.clear();
460 d->message.clear();
461 d->discId.clear();
462 d->upcEan.clear();
463 }
464
465
empty() const466 bool K3b::Device::CdText::empty() const
467 {
468 if( !d->title.isEmpty() )
469 return false;
470 if( !d->performer.isEmpty() )
471 return false;
472 if( !d->songwriter.isEmpty() )
473 return false;
474 if( !d->composer.isEmpty() )
475 return false;
476 if( !d->arranger.isEmpty() )
477 return false;
478 if( !d->message.isEmpty() )
479 return false;
480 if( !d->discId.isEmpty() )
481 return false;
482 if( !d->upcEan.isEmpty() )
483 return false;
484
485 for( int i = 0; i < count(); ++i )
486 if( !d->tracks.at(i).isEmpty() )
487 return false;
488
489 return true;
490 }
491
492
isEmpty() const493 bool K3b::Device::CdText::isEmpty() const
494 {
495 return empty();
496 }
497
498
title() const499 QString K3b::Device::CdText::title() const
500 {
501 return d->title;
502 }
503
504
performer() const505 QString K3b::Device::CdText::performer() const
506 {
507 return d->performer;
508 }
509
510
songwriter() const511 QString K3b::Device::CdText::songwriter() const
512 {
513 return d->songwriter;
514 }
515
516
composer() const517 QString K3b::Device::CdText::composer() const
518 {
519 return d->composer;
520 }
521
522
arranger() const523 QString K3b::Device::CdText::arranger() const
524 {
525 return d->arranger;
526 }
527
528
message() const529 QString K3b::Device::CdText::message() const
530 {
531 return d->message;
532 }
533
534
discId() const535 QString K3b::Device::CdText::discId() const
536 {
537 return d->discId;
538 }
539
540
upcEan() const541 QString K3b::Device::CdText::upcEan() const
542 {
543 return d->upcEan;
544 }
545
546
setTitle(const QString & s)547 void K3b::Device::CdText::setTitle( const QString& s )
548 {
549 d->title = s;
550 fixup(d->title);
551 }
552
553
setPerformer(const QString & s)554 void K3b::Device::CdText::setPerformer( const QString& s )
555 {
556 d->performer = s;
557 fixup(d->performer);
558 }
559
560
setSongwriter(const QString & s)561 void K3b::Device::CdText::setSongwriter( const QString& s )
562 {
563 d->songwriter = s;
564 fixup(d->songwriter);
565 }
566
567
setComposer(const QString & s)568 void K3b::Device::CdText::setComposer( const QString& s )
569 {
570 d->composer = s;
571 fixup(d->composer);
572 }
573
574
setArranger(const QString & s)575 void K3b::Device::CdText::setArranger( const QString& s )
576 {
577 d->arranger = s;
578 fixup(d->arranger);
579 }
580
581
setMessage(const QString & s)582 void K3b::Device::CdText::setMessage( const QString& s )
583 {
584 d->message = s;
585 fixup(d->message);
586 }
587
588
setDiscId(const QString & s)589 void K3b::Device::CdText::setDiscId( const QString& s )
590 {
591 d->discId = s;
592 fixup(d->discId);
593 }
594
595
setUpcEan(const QString & s)596 void K3b::Device::CdText::setUpcEan( const QString& s )
597 {
598 d->upcEan = s;
599 fixup(d->upcEan);
600 }
601
602
setRawPackData(const unsigned char * data,int len)603 void K3b::Device::CdText::setRawPackData( const unsigned char* data, int len )
604 {
605 clear();
606
607 int r = len%18;
608 if( r > 0 && r != 4 ) {
609 qDebug() << "(K3b::Device::CdText) invalid cdtext size: " << len;
610 }
611 else if( len-r > 0 ) {
612 debugRawTextPackData( &data[r], len-r );
613
614 cdtext_pack* pack = (cdtext_pack*)&data[r];
615
616
617 for( int i = 0; i < (len-r)/18; ++i ) {
618
619 if( pack[i].dbcc ) {
620 qDebug() << "(K3b::Device::CdText) Double byte code not supported";
621 return;
622 }
623
624 //
625 // For some reason all crc bits are inverted.
626 //
627 pack[i].crc[0] ^= 0xff;
628 pack[i].crc[1] ^= 0xff;
629
630 quint16 crc = calcX25( reinterpret_cast<unsigned char*>(&pack[i]), 18 );
631
632 pack[i].crc[0] ^= 0xff;
633 pack[i].crc[1] ^= 0xff;
634
635 if( crc != 0x0000 )
636 qDebug() << "(K3b::Device::CdText) CRC invalid!";
637
638
639 //
640 // pack.data has a length of 12
641 //
642 // id1 tells us the tracknumber of the data (0 for global)
643 // data may contain multiple \0. In that case after every \0 the track number increases 1
644 //
645
646 char* nullPos = (char*)pack[i].data - 1;
647
648 int trackNo = pack[i].id2;
649
650 while( nullPos ) {
651 char* nextNullPos = (char*)::memchr( nullPos+1, '\0', 11 - (nullPos - (char*)pack[i].data) );
652 QString txtstr;
653 if( nextNullPos ) // take all chars up to the next null
654 txtstr = QString::fromLatin1( (char*)nullPos+1, nextNullPos - nullPos - 1 );
655 else // take all chars to the end of the pack data (12 bytes)
656 txtstr = QString::fromLatin1( (char*)nullPos+1, 11 - (nullPos - (char*)pack[i].data) );
657
658 //
659 // a tab character means to use the same as for the previous track
660 //
661 if( txtstr == "\t" )
662 txtstr = d->textForPackType( pack[i].id1, trackNo-1 );
663
664 switch( pack[i].id1 ) {
665 case 0x80: // Title
666 if( trackNo == 0 )
667 d->title.append( txtstr );
668 else
669 track(trackNo-1).d->title.append( txtstr );
670 break;
671
672 case 0x81: // Performer
673 if( trackNo == 0 )
674 d->performer.append( txtstr );
675 else
676 track(trackNo-1).d->performer.append( txtstr );
677 break;
678
679 case 0x82: // Writer
680 if( trackNo == 0 )
681 d->songwriter.append( txtstr );
682 else
683 track(trackNo-1).d->songwriter.append( txtstr );
684 break;
685
686 case 0x83: // Composer
687 if( trackNo == 0 )
688 d->composer.append( txtstr );
689 else
690 track(trackNo-1).d->composer.append( txtstr );
691 break;
692
693 case 0x84: // Arranger
694 if( trackNo == 0 )
695 d->arranger.append( txtstr );
696 else
697 track(trackNo-1).d->arranger.append( txtstr );
698 break;
699
700 case 0x85: // Message
701 if( trackNo == 0 )
702 d->message.append( txtstr );
703 else
704 track(trackNo-1).d->message.append( txtstr );
705 break;
706
707 case 0x86: // Disc identification
708 // only global
709 if( trackNo == 0 )
710 d->discId.append( txtstr );
711 break;
712
713 case 0x8e: // Upc or isrc
714 if( trackNo == 0 )
715 d->upcEan.append( txtstr );
716 else
717 track(trackNo-1).d->isrc.append( txtstr );
718 break;
719
720 // TODO: support for binary data
721 // 0x88: TOC
722 // 0x89: second TOC
723 // 0x8f: Size information
724
725 default:
726 break;
727 }
728
729 trackNo++;
730 nullPos = nextNullPos;
731 }
732 }
733
734 // remove empty fields at the end
735 while( !d->tracks.isEmpty() && d->tracks.last().isEmpty() ) {
736 d->tracks.removeLast();
737 }
738
739 // we preserve the original data for clean 1-to-1 copies
740 d->rawData = QByteArray( reinterpret_cast<const char*>(data), len );
741 }
742 else
743 qDebug() << "(K3b::Device::CdText) zero-sized CD-TEXT: " << len;
744 }
745
746
setRawPackData(const QByteArray & b)747 void K3b::Device::CdText::setRawPackData( const QByteArray& b )
748 {
749 setRawPackData( reinterpret_cast<const unsigned char*>(b.data()), b.size() );
750 }
751
rawPackData() const752 QByteArray K3b::Device::CdText::rawPackData() const
753 {
754 if( d->rawData.isEmpty() ) {
755 // FIXME: every pack block may only consist of up to 255 packs.
756
757 int pc = 0;
758 int alreadyCountedPacks = 0;
759
760
761 //
762 // prepare the size information block
763 //
764 text_size_block tsize;
765 ::memset( &tsize, 0, sizeof(text_size_block) );
766 tsize.charcode = 0; // ISO 8859-1
767 tsize.first_track = 1;
768 tsize.last_track = count();
769 tsize.pack_count[0xF] = 3;
770 tsize.language_codes[0] = 0x09; // English (from cdrecord)
771
772
773 //
774 // create the CD-Text packs
775 //
776 QByteArray data;
777 for( int i = 0; i <= 6; ++i ) {
778 if( d->textLengthForPackType( 0x80 | i ) ) {
779 appendByteArray( data, d->createPackData( 0x80 | i, pc ) );
780 tsize.pack_count[i] = pc - alreadyCountedPacks;
781 alreadyCountedPacks = pc;
782 }
783 }
784 if( d->textLengthForPackType( 0x8E ) ) {
785 appendByteArray( data, d->createPackData( 0x8E, pc ) );
786 tsize.pack_count[0xE] = pc - alreadyCountedPacks;
787 alreadyCountedPacks = pc;
788 }
789
790
791 // pc is the number of the next pack and we add 3 size packs
792 tsize.last_seqnum[0] = pc + 2;
793
794
795 //
796 // create the size info packs
797 //
798 int dataFill = data.size();
799 data.resize( data.size() + 3 * sizeof(cdtext_pack) );
800 for( int i = 0; i < 3; ++i ) {
801 cdtext_pack pack;
802 ::memset( &pack, 0, sizeof(cdtext_pack) );
803 pack.id1 = 0x8F;
804 pack.id2 = i;
805 pack.id3 = pc+i;
806 ::memcpy( pack.data, &reinterpret_cast<char*>(&tsize)[i*12], 12 );
807 savePack( &pack, data, dataFill );
808 }
809
810 //
811 // add MMC header
812 //
813 QByteArray a( 4, 0 );
814 a[0] = (data.size()+2)>>8 & 0xff;
815 a[1] = (data.size()+2) & 0xff;
816 a[2] = a[3] = 0;
817 appendByteArray( a, data );
818
819 d->rawData = a;
820 }
821
822 return d->rawData;
823 }
824
825
826 // this method also creates completely empty packs
createPackData(int packType,int & packCount) const827 QByteArray K3b::Device::CdText::Private::createPackData( int packType, int& packCount ) const
828 {
829 QByteArray data;
830 int dataFill = 0;
831 QByteArray text = encodeCdText( textForPackType( packType, 0 ) );
832 int currentTrack = 0;
833 unsigned int textPos = 0;
834 unsigned int packPos = 0;
835
836 //
837 // initialize the first pack
838 //
839 cdtext_pack pack;
840 ::memset( &pack, 0, sizeof(cdtext_pack) );
841 pack.id1 = packType;
842 pack.id3 = packCount;
843
844 //
845 // We break this loop when all texts have been packed
846 //
847 while( 1 ) {
848 //
849 // Copy as many bytes as possible into the pack
850 //
851 size_t copyBytes = qMin(12 - packPos, text.length() - textPos);
852 if (copyBytes) {
853 ::memcpy(reinterpret_cast<char*>(&pack.data[packPos]),
854 &text.data()[textPos], copyBytes );
855 }
856 textPos += copyBytes;
857 packPos += copyBytes;
858
859
860 //
861 // Check if the packdata is full
862 //
863 if( packPos > 11 ) {
864
865 savePack( &pack, data, dataFill );
866 ++packCount;
867
868 //
869 // reset the pack
870 //
871 ::memset( &pack, 0, sizeof(cdtext_pack) );
872 pack.id1 = packType;
873 pack.id2 = currentTrack;
874 pack.id3 = packCount;
875 packPos = 0;
876
877 // update the charpos in case we continue a text in the next pack
878 if( textPos <= text.length() )
879 pack.charpos = ( textPos > 15 ? 15 : textPos );
880 }
881
882
883 //
884 // Check if we have no text data left
885 //
886 if( textPos >= text.length() ) {
887
888 // add one zero spacer byte
889 ++packPos;
890
891 ++currentTrack;
892
893 // Check if all texts have been packed
894 if( currentTrack > tracks.count() ) {
895 savePack( &pack, data, dataFill );
896 ++packCount;
897
898 data.resize( dataFill );
899 return data;
900 }
901
902 // next text block
903 text = encodeCdText( textForPackType( packType, currentTrack ) );
904 textPos = 0;
905 }
906 }
907 }
908
909
910 // track 0 means global cdtext
textForPackType(int packType,int track) const911 QString K3b::Device::CdText::Private::textForPackType( int packType, int track ) const
912 {
913 switch( packType ) {
914 default:
915 case 0x80:
916 if( track == 0 )
917 return title;
918 else if( track > 0 && track <= tracks.count() )
919 return tracks[track-1].title();
920 else
921 return QString();
922
923 case 0x81:
924 if( track == 0 )
925 return performer;
926 else if( track > 0 && track <= tracks.count() )
927 return tracks[track-1].performer();
928 else
929 return QString();
930
931 case 0x82:
932 if( track == 0 )
933 return songwriter;
934 else if( track > 0 && track <= tracks.count() )
935 return tracks[track-1].songwriter();
936 else
937 return QString();
938
939 case 0x83:
940 if( track == 0 )
941 return composer;
942 else if( track > 0 && track <= tracks.count() )
943 return tracks[track-1].composer();
944 else
945 return QString();
946
947 case 0x84:
948 if( track == 0 )
949 return arranger;
950 else if( track > 0 && track <= tracks.count() )
951 return tracks[track-1].arranger();
952 else
953 return QString();
954
955 case 0x85:
956 if( track == 0 )
957 return message;
958 else if( track > 0 && track <= tracks.count() )
959 return tracks[track-1].message();
960 else
961 return QString();
962
963 case 0x86:
964 if( track == 0 )
965 return discId;
966 else
967 return QString();
968
969 // case 0x87:
970 // if( track == 0 )
971 // return genre;
972 // else if( track > 0 && track <= tracks.count() )
973 // return tracks[track-1].title();
974 // else
975 // return QString();
976
977 case 0x8E:
978 if( track == 0 )
979 return upcEan;
980 else if( track > 0 && track <= tracks.count() )
981 return tracks[track-1].isrc();
982 else
983 return QString();
984 }
985 }
986
987
988 // count the overall length of a certain packtype texts
textLengthForPackType(int packType) const989 int K3b::Device::CdText::Private::textLengthForPackType( int packType ) const
990 {
991 int len = 0;
992 for( int i = 0; i <= tracks.count(); ++i )
993 len += encodeCdText( textForPackType( packType, i ) ).length();
994 return len;
995 }
996
997
debug() const998 void K3b::Device::CdText::debug() const
999 {
1000 // debug the stuff
1001 qDebug() << "CD-TEXT data:" << endl
1002 << "Global:" << endl
1003 << " Title: '" << title() << "'" << endl
1004 << " Performer: '" << performer() << "'" << endl
1005 << " Songwriter: '" << songwriter() << "'" << endl
1006 << " Composer: '" << composer() << "'" << endl
1007 << " Arranger: '" << arranger() << "'" << endl
1008 << " Message: '" << message() << "'" << endl
1009 << " Disc ID: '" << discId() << "'" << endl
1010 << " Upc Ean: '" << upcEan() << "'" << endl;
1011 for( int i = 0; i < count(); ++i ) {
1012 qDebug() << "Track " << (i+1) << ":" << endl
1013 << " Title: '" << d->tracks[i].title() << "'" << endl
1014 << " Performer: '" << d->tracks[i].performer() << "'" << endl
1015 << " Songwriter: '" << d->tracks[i].songwriter() << "'" << endl
1016 << " Composer: '" << d->tracks[i].composer() << "'" << endl
1017 << " Arranger: '" << d->tracks[i].arranger() << "'" << endl
1018 << " Message: '" << d->tracks[i].message() << "'" << endl
1019 << " Isrc: '" << d->tracks[i].isrc() << "'" << endl;
1020 }
1021 }
1022
1023
operator ==(const K3b::Device::CdText & other) const1024 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
operator !=(const K3b::Device::CdText & other) const1038 bool K3b::Device::CdText::operator!=( const K3b::Device::CdText& other ) const
1039 {
1040 return !operator==( other );
1041 }
1042
1043
checkCrc(const unsigned char * data,int len)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
checkCrc(const QByteArray & rawData)1079 bool K3b::Device::CdText::checkCrc( const QByteArray& rawData )
1080 {
1081 return checkCrc( reinterpret_cast<const unsigned char*>(rawData.data()), rawData.size() );
1082 }
1083