1 /*
2 Copyright (C) 2018 Thomas Schmitt
3 Copyright (C) 2004-2005, 2008, 2011, 2012, 2013 Rocky Bernstein <rocky@gnu.org>
4 toc reading routine adapted from cuetools
5 Copyright (C) 2003 Svend Sanjay Sorensen <ssorensen@fastmail.fm>
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 # define __CDIO_CONFIG_H__ 1
24 #endif
25
26 #include <cdio/cdtext.h>
27 #include <cdio/logging.h>
28 #include "cdtext_private.h"
29 #include <cdio/utf8.h>
30
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34
35 #ifdef HAVE_STRING_H
36 #include <string.h>
37 #endif
38
39 #define _CDTEXT_DBCC
40 #define MAX_CDTEXT_GENRE_CODE 28
41 #define MAX_CDTEXT_LANGUAGE_CODE 127
42
43 const char *cdtext_field[MAX_CDTEXT_FIELDS] =
44 {
45 "TITLE",
46 "PERFORMER",
47 "SONGWRITER",
48 "COMPOSER",
49 "MESSAGE",
50 "ARRANGER",
51 "ISRC",
52 "UPC_EAN",
53 "GENRE",
54 "DISC_ID",
55 };
56
57 const char *cdtext_genre[MAX_CDTEXT_GENRE_CODE] =
58 {
59 "Not Used",
60 "Not Defined",
61 "Adult Contemporary",
62 "Alternative Rock",
63 "Childrens Music",
64 "Classical",
65 "Contemporary Christian",
66 "Country",
67 "Dance",
68 "Easy Listening",
69 "Erotic",
70 "Folk",
71 "Gospel",
72 "Hip Hop",
73 "Jazz",
74 "Latin",
75 "Musical",
76 "New Age",
77 "Opera",
78 "Operetta",
79 "Pop Music",
80 "Rap",
81 "Reggae",
82 "Rock Music",
83 "Rhythm & Blues",
84 "Sound Effects",
85 "Spoken Word",
86 "World Music"
87 };
88
89 const char *cdtext_language[MAX_CDTEXT_LANGUAGE_CODE + 1] =
90 {
91 "Unknown",
92 "Albanian",
93 "Breton",
94 "Catalan",
95 "Croatian",
96 "Welsh",
97 "Czech",
98 "Danish",
99 "German",
100 "English",
101 "Spanish",
102 "Esperanto",
103 "Estonian",
104 "Basque",
105 "Faroese",
106 "French",
107 "Frisian",
108 "Irish",
109 "Gaelic",
110 "Galician",
111 "Icelandic",
112 "Italian",
113 "Lappish",
114 "Latin",
115 "Latvian",
116 "Luxembourgian",
117 "Lithuanian",
118 "Hungarian",
119 "Maltese",
120 "Dutch",
121 "Norwegian",
122 "Occitan",
123 "Polish",
124 "Portuguese",
125 "Romanian",
126 "Romansh",
127 "Serbian",
128 "Slovak",
129 "Slovenian",
130 "Finnish",
131 "Swedish",
132 "Turkish",
133 "Flemish",
134 "Wallon",
135 "", "", "", "", "", "", "", "", "", "",
136 "", "", "", "", "", "", "", "", "", "",
137 "", "", "", "", "",
138 "Zulu",
139 "Vietnamese",
140 "Uzbek",
141 "Urdu",
142 "Ukrainian",
143 "Thai",
144 "Telugu",
145 "Tatar",
146 "Tamil",
147 "Tadzhik",
148 "Swahili",
149 "SrananTongo",
150 "Somali",
151 "Sinhalese",
152 "Shona",
153 "Serbo-croat",
154 "Ruthenian",
155 "Russian",
156 "Quechua",
157 "Pushtu",
158 "Punjabi",
159 "Persian",
160 "Papamiento",
161 "Oriya",
162 "Nepali",
163 "Ndebele",
164 "Marathi",
165 "Moldavian",
166 "Malaysian",
167 "Malagasay",
168 "Macedonian",
169 "Laotian",
170 "Korean",
171 "Khmer",
172 "Kazakh",
173 "Kannada",
174 "Japanese",
175 "Indonesian",
176 "Hindi",
177 "Hebrew",
178 "Hausa",
179 "Gurani",
180 "Gujurati",
181 "Greek",
182 "Georgian",
183 "Fulani",
184 "Dari",
185 "Churash",
186 "Chinese",
187 "Burmese",
188 "Bulgarian",
189 "Bengali",
190 "Bielorussian",
191 "Bambora",
192 "Azerbaijani",
193 "Assamese",
194 "Armenian",
195 "Arabic",
196 "Amharic"
197 };
198
199 /*!
200 Return string representation of given field type.
201 */
202 const char *
cdtext_field2str(cdtext_field_t i)203 cdtext_field2str(cdtext_field_t i)
204 {
205 if (i >= MAX_CDTEXT_FIELDS)
206 return "INVALID";
207 else
208 return cdtext_field[i];
209 }
210
211 /*!
212 Return string representation of the given genre code.
213 */
214 const char *
cdtext_genre2str(cdtext_genre_t i)215 cdtext_genre2str(cdtext_genre_t i)
216 {
217 if (i >= MAX_CDTEXT_GENRE_CODE)
218 return "INVALID";
219 else
220 return cdtext_genre[i];
221 }
222
223 /*!
224 Return string representation of the given language code.
225 */
226 const char *
cdtext_lang2str(cdtext_lang_t i)227 cdtext_lang2str(cdtext_lang_t i)
228 {
229 if (i <= CDTEXT_LANGUAGE_WALLON)
230 return cdtext_language[i];
231 else if (i >= CDTEXT_LANGUAGE_ZULU && i <= CDTEXT_LANGUAGE_AMHARIC)
232 return cdtext_language[i];
233 return "INVALID";
234 }
235
236 /*!
237 Free memory associated with the given cdtext_t object.
238
239 @param p_cdtext the CD-TEXT object
240 */
241 void
cdtext_destroy(cdtext_t * p_cdtext)242 cdtext_destroy(cdtext_t *p_cdtext)
243 {
244 cdtext_field_t k;
245 track_t j;
246 int i;
247
248 if (!p_cdtext) return;
249 for (i=0; i<CDTEXT_NUM_BLOCKS_MAX; i++) {
250 for (j=0; j<CDTEXT_NUM_TRACKS_MAX; j++) {
251 for (k=0; k < MAX_CDTEXT_FIELDS; k++) {
252 if (p_cdtext->block[i].track[j].field[k]) {
253 free(p_cdtext->block[i].track[j].field[k]);
254 p_cdtext->block[i].track[j].field[k] = NULL;
255 }
256 }
257 }
258 }
259 free(p_cdtext);
260 }
261
262 /*!
263 Returns a copy of the return value of cdtext_get_const or NULL.
264
265 Must be freed using cdio_free() when done.
266 @see cdtext_get_const
267 */
268 char *
cdtext_get(const cdtext_t * p_cdtext,cdtext_field_t field,track_t track)269 cdtext_get(const cdtext_t *p_cdtext, cdtext_field_t field, track_t track)
270 {
271 const char *ret = cdtext_get_const(p_cdtext, field, track);
272 if (NULL == ret)
273 return NULL;
274 else
275 return strdup(ret);
276 }
277
278 /*!
279 Returns value of the given field.
280
281 NULL is returned if key is CDTEXT_INVALID or the field is not set.
282 Strings are encoded in UTF-8.
283
284 @param p_cdtext the CD-TEXT object
285 @param field type of the field to return
286 @param track specifies the track, 0 stands for disc
287 */
288 const char *
cdtext_get_const(const cdtext_t * p_cdtext,cdtext_field_t field,track_t track)289 cdtext_get_const(const cdtext_t *p_cdtext, cdtext_field_t field, track_t track)
290 {
291 if (CDTEXT_FIELD_INVALID == field
292 || NULL == p_cdtext
293 || CDIO_CD_MAX_TRACKS < track)
294 return NULL;
295
296 return p_cdtext->block[p_cdtext->block_i].track[track].field[field];
297 }
298
299 /*!
300 Returns the discs genre code.
301
302 @param p_cdtext the CD-TEXT object
303 */
304 cdtext_genre_t
cdtext_get_genre(const cdtext_t * p_cdtext)305 cdtext_get_genre(const cdtext_t *p_cdtext)
306 {
307 if (NULL == p_cdtext)
308 return CDTEXT_GENRE_UNUSED;
309 return p_cdtext->block[p_cdtext->block_i].genre_code;
310 }
311
312 /*!
313 Returns the currently active language.
314
315 @param p_cdtext the CD-TEXT object
316 */
317 cdtext_lang_t
cdtext_get_language(const cdtext_t * p_cdtext)318 cdtext_get_language(const cdtext_t *p_cdtext)
319 {
320 if (NULL == p_cdtext)
321 return CDTEXT_LANGUAGE_BLOCK_UNUSED;
322 return p_cdtext->block[p_cdtext->block_i].language_code;
323 }
324
325 /*!
326 Returns the first track number.
327
328 @param p_cdtext the CD-TEXT object
329 */
330 track_t
cdtext_get_first_track(const cdtext_t * p_cdtext)331 cdtext_get_first_track(const cdtext_t *p_cdtext)
332 {
333 if (NULL == p_cdtext)
334 return 0;
335 return p_cdtext->block[p_cdtext->block_i].first_track;
336 }
337
338 /*!
339 Returns the last track number.
340
341 @param p_cdtext the CD-TEXT object
342 */
343 track_t
cdtext_get_last_track(const cdtext_t * p_cdtext)344 cdtext_get_last_track(const cdtext_t *p_cdtext)
345 {
346 if (NULL == p_cdtext)
347 return 0;
348 return p_cdtext->block[p_cdtext->block_i].last_track;
349 }
350
351 /*!
352 @deprecated Use cdtext_list_languages_v2()
353
354 Returns a list of available languages or NULL.
355
356 __WARNING__: The indices in the returned array _do not_ match the indexing
357 as expected by cdtext_set_language_index().
358 Use cdtext_select_language with the values of array elements.
359
360 Internally the list is stored in a static array.
361
362 @param p_cdtext the CD-TEXT object
363 @return NULL if p_cdtext is NULL.
364 Else an array of 8 cdtext_lang_t elements:
365 CDTEXT_LANGUAGE_UNKNOWN not only marks language code 0x00
366 but also invalid language codes and invalid language blocks.
367 */
368 cdtext_lang_t
cdtext_list_languages(const cdtext_t * p_cdtext)369 *cdtext_list_languages(const cdtext_t *p_cdtext)
370 {
371 static cdtext_lang_t avail[CDTEXT_NUM_BLOCKS_MAX];
372 int i, j=0;
373
374 if (NULL == p_cdtext)
375 return NULL;
376
377 for (i=0; i<CDTEXT_NUM_BLOCKS_MAX; i++)
378 {
379 avail[i] = CDTEXT_LANGUAGE_UNKNOWN;
380 if (CDTEXT_LANGUAGE_UNKNOWN != p_cdtext->block[i].language_code &&
381 CDTEXT_LANGUAGE_INVALID != p_cdtext->block[i].language_code &&
382 CDTEXT_LANGUAGE_BLOCK_UNUSED != p_cdtext->block[i].language_code)
383 avail[j++] = p_cdtext->block[i].language_code;
384 }
385
386 return avail;
387 }
388
389 /*!
390 Returns an array of available languages or NULL.
391 The index of an array element may be used to select the corresponding
392 language block by call cdtext_set_language_index().
393
394 The return value is a pointer into the memory range of *p_cdtext.
395 Do not use it after having freed that memory range.
396
397 @param p_cdtext the CD-TEXT object
398 @return NULL if p_cdtext is NULL, or an array of 8 cdtext_lang_t elements.
399
400 If an enumeration is CDTEXT_LANGUAGE_INVALID, then the language block has an invalid
401 language code.
402
403 If an enumeration is CDTEXT_LANGUAGE_BLOCK_UNUSED, then the block does not
404 exist on CD or could not be read in CD-TEXT for some reason.
405
406 Otherwise, the enumeration of element will be a value in
407 CDTEXT_LANGUAGE_UNKNOWN to CDTEXT_LANGUAGE_AMHARIC, and is a block
408 in that language.
409 */
410 cdtext_lang_t
cdtext_list_languages_v2(cdtext_t * p_cdtext)411 *cdtext_list_languages_v2(cdtext_t *p_cdtext)
412 {
413 int i;
414
415 if (NULL == p_cdtext)
416 return NULL;
417 for (i = 0; i < CDTEXT_NUM_BLOCKS_MAX; i++)
418 {
419 p_cdtext->languages[i] = p_cdtext->block[i].language_code;
420 }
421 return p_cdtext->languages;
422 }
423
424 /*!
425 Select the given language by block index. See cdtext_list_languages_v2().
426 If the index is bad, or no language block with that index was read:
427 select the default language at index 0 and return false.
428
429 @param p_cdtext the CD-TEXT object
430 @param idx the desired index: 0 to 7.
431
432 @return true on success, false if no language block is associated to idx
433 */
434 bool
cdtext_set_language_index(cdtext_t * p_cdtext,int idx)435 cdtext_set_language_index(cdtext_t *p_cdtext, int idx)
436 {
437 if (NULL == p_cdtext)
438 return false;
439 p_cdtext->block_i = 0;
440 if (idx < 0 || idx > 7)
441 return false;
442 if (p_cdtext->block[idx].language_code == CDTEXT_LANGUAGE_BLOCK_UNUSED)
443 return false;
444 p_cdtext->block_i = idx;
445 return true;
446 }
447
448 /*!
449 Try to select the given language.
450 Select default language if specified is not available or invalid and
451 return false.
452
453 @param p_cdtext the CD-TEXT object
454 @param language language identifier
455
456 @return true on success, false if language is not available
457 */
458 bool
cdtext_select_language(cdtext_t * p_cdtext,cdtext_lang_t language)459 cdtext_select_language(cdtext_t *p_cdtext, cdtext_lang_t language)
460 {
461 if(NULL == p_cdtext)
462 return false;
463
464 if (CDTEXT_LANGUAGE_BLOCK_UNUSED != language)
465 {
466 int i;
467 for (i=0; i<CDTEXT_NUM_BLOCKS_MAX; i++) {
468 if (language == p_cdtext->block[i].language_code) {
469 p_cdtext->block_i = i;
470 return true;
471 }
472 }
473 }
474 p_cdtext->block_i = 0;
475 return false;
476 }
477
478 /*!
479 Initialize a new cdtext structure.
480
481 When the structure is no longer needed, release the
482 resources using cdtext_delete.
483
484 */
485 cdtext_t
cdtext_init(void)486 *cdtext_init(void)
487 {
488 cdtext_field_t k;
489 track_t j;
490 int i;
491 cdtext_t *p_cdtext;
492
493 p_cdtext = (cdtext_t *) malloc(sizeof(struct cdtext_s));
494
495 for (i=0; i<CDTEXT_NUM_BLOCKS_MAX; i++) {
496 for (j=0; j<CDTEXT_NUM_TRACKS_MAX; j++) {
497 for (k=0; k < MAX_CDTEXT_FIELDS; k++) {
498 p_cdtext->block[i].track[j].field[k] = NULL;
499 }
500 }
501 p_cdtext->block[i].genre_code = CDTEXT_GENRE_UNUSED;
502 p_cdtext->block[i].language_code = CDTEXT_LANGUAGE_BLOCK_UNUSED;
503 }
504
505 p_cdtext->block_i = 0;
506
507 return p_cdtext;
508 }
509
510 /*!
511 Returns associated cdtext_field_t if field is a CD-TEXT keyword.
512
513 Internal function.
514
515 @param key key to test
516
517 @return CDTEXT_INVALID if the given keyword is invalid
518 */
519 cdtext_field_t
cdtext_is_field(const char * key)520 cdtext_is_field (const char *key)
521 {
522 unsigned int i;
523
524 for (i = 0; i < MAX_CDTEXT_FIELDS ; i++)
525 if (0 == strcmp(cdtext_field[i], key)) {
526 return i;
527 }
528 return CDTEXT_FIELD_INVALID;
529 }
530
531 /*!
532 Return the language code of a given language string representation.
533 This is the inverse of cdtext_lang2str().
534
535 @param lang language to look up
536
537 @return if lang is among the possible results of cdtext_lang2str():
538 the cdtext_lang_t which is associated.
539 else: CDTEXT_LANGUAGE_INVALID
540 */
541 cdtext_lang_t
cdtext_str2lang(const char * lang)542 cdtext_str2lang (const char *lang)
543 {
544 unsigned int i;
545
546 if(0 == lang[0]) /* The empty texts in cdtext_language[] are invalid */
547 return CDTEXT_LANGUAGE_INVALID;
548
549 for (i = 0; i <= MAX_CDTEXT_LANGUAGE_CODE; i++)
550 if (0 == strcmp(cdtext_language[i], lang)) {
551 return i;
552 }
553 return CDTEXT_LANGUAGE_INVALID;
554 }
555
556 /*!
557 Sets the given field at the given track to the given value.
558
559 Recodes to UTF-8 if charset is not NULL.
560
561 @param p_cdtext the CD-TEXT object
562 @param key field to set
563 @param value value to set
564 @param track track to work on
565 @param charset charset to convert from
566 */
567 void
cdtext_set(cdtext_t * p_cdtext,cdtext_field_t key,const uint8_t * value,track_t track,const char * charset)568 cdtext_set(cdtext_t *p_cdtext, cdtext_field_t key, const uint8_t *value,
569 track_t track, const char *charset)
570 {
571 if (NULL == value || key == CDTEXT_FIELD_INVALID
572 || CDIO_CD_MAX_TRACKS < track)
573 return;
574
575 /* free old memory */
576 if (p_cdtext->block[p_cdtext->block_i].track[track].field[key])
577 free(p_cdtext->block[p_cdtext->block_i].track[track].field[key]);
578
579 /* recode to UTF-8 */
580 if (NULL != charset) {
581 cdio_utf8_t *utf8_str = NULL;
582 cdio_charset_to_utf8((const char*) value, strlen((const char*)value),
583 &utf8_str, charset);
584 p_cdtext->block[p_cdtext->block_i].track[track].field[key] = (char *)utf8_str;
585 } else
586 p_cdtext->block[p_cdtext->block_i].track[track].field[key] = strdup((const char *)value);
587 }
588
589 #define CDTEXT_COMPARE_CHAR(buf, c, db) ((buf)[0] == c && (! db || (buf)[1] == c) )
590
591 /*!
592 Read a binary CD-TEXT and fill a cdtext struct.
593
594 @param p_cdtext the CD-TEXT object
595 @param wdata the data
596 @param i_data size of wdata
597
598 @returns 0 on success, non-zero on failure
599 */
600 int
cdtext_data_init(cdtext_t * p_cdtext,uint8_t * wdata,size_t i_data)601 cdtext_data_init(cdtext_t *p_cdtext, uint8_t *wdata, size_t i_data)
602 {
603 uint8_t *p_data;
604 int j;
605 uint8_t buffer[256];
606 uint8_t tab_buffer[256];
607 int i_buf = 0;
608 int i_block;
609 int i_seq = 0;
610 int i;
611 cdtext_blocksize_t blocksize;
612 char *charset = NULL;
613 uint8_t cur_track;
614
615 memset( buffer, 0, sizeof(buffer) );
616 memset( tab_buffer, 0, sizeof(buffer) );
617
618 p_data = wdata;
619 if (i_data < CDTEXT_LEN_PACK || 0 != i_data % CDTEXT_LEN_PACK) {
620 cdio_warn("CD-Text size is too small or not a multiple of pack size");
621 return -1;
622 }
623
624 #if 0
625 for(i=0; i < i_data; i++)
626 printf("%0x%c", wdata[i], ((i+1) % 18 == 0 ? '\n' : ' '));
627 #endif
628
629
630 /* Iterate over blocks */
631 i_block = -1;
632 while(i_data > 0) {
633 cdtext_pack_t pack;
634 cdtext_read_pack(&pack, p_data);
635
636 if (i_block != pack.block || i_seq != pack.seq) {
637 cdtext_pack_t tpack;
638 i_block = pack.block;
639 if (i_block >= CDTEXT_NUM_BLOCKS_MAX) {
640 cdio_warn("CD-TEXT: Invalid blocknumber %d.\n", i_block);
641 return -1;
642 }
643 p_cdtext->block_i = i_block;
644 i_seq = 0;
645 memset( &blocksize, 0, CDTEXT_LEN_BLOCKSIZE);
646
647 /* first read block size information for sanity checks and encoding */
648 for(i=0; i <= i_data-CDTEXT_LEN_PACK; i+=CDTEXT_LEN_PACK) {
649
650 if (p_data[i+0] == CDTEXT_PACK_BLOCKSIZE) {
651 cdtext_read_pack(&tpack, p_data+i);
652 switch (tpack.i_track) {
653 case 0:
654 blocksize.charcode = tpack.text[0];
655 blocksize.i_first_track = tpack.text[1];
656 blocksize.i_last_track = tpack.text[2];
657 blocksize.copyright = tpack.text[3];
658 blocksize.i_packs[0] = tpack.text[4];
659 blocksize.i_packs[1] = tpack.text[5];
660 blocksize.i_packs[2] = tpack.text[6];
661 blocksize.i_packs[3] = tpack.text[7];
662 blocksize.i_packs[4] = tpack.text[8];
663 blocksize.i_packs[5] = tpack.text[9];
664 blocksize.i_packs[6] = tpack.text[10];
665 blocksize.i_packs[7] = tpack.text[11];
666 break;
667 case 1:
668 blocksize.i_packs[8] = tpack.text[0];
669 blocksize.i_packs[9] = tpack.text[1];
670 blocksize.i_packs[10] = tpack.text[2];
671 blocksize.i_packs[11] = tpack.text[3];
672 blocksize.i_packs[12] = tpack.text[4];
673 blocksize.i_packs[13] = tpack.text[5];
674 blocksize.i_packs[14] = tpack.text[6];
675 blocksize.i_packs[15] = tpack.text[7];
676 blocksize.lastseq[0] = tpack.text[8];
677 blocksize.lastseq[1] = tpack.text[9];
678 blocksize.lastseq[2] = tpack.text[10];
679 blocksize.lastseq[3] = tpack.text[11];
680 break;
681 case 2:
682 blocksize.lastseq[4] = tpack.text[0];
683 blocksize.lastseq[5] = tpack.text[1];
684 blocksize.lastseq[6] = tpack.text[2];
685 blocksize.lastseq[7] = tpack.text[3];
686 blocksize.langcode[0] = tpack.text[4];
687 blocksize.langcode[1] = tpack.text[5];
688 blocksize.langcode[2] = tpack.text[6];
689 blocksize.langcode[3] = tpack.text[7];
690 blocksize.langcode[4] = tpack.text[8];
691 blocksize.langcode[5] = tpack.text[9];
692 blocksize.langcode[6] = tpack.text[10];
693 blocksize.langcode[7] = tpack.text[11];
694 break;
695 }
696 }
697 }
698
699 if(blocksize.i_packs[15] == 3) {
700 cdtext_lang_t lcode;
701 /* if there were 3 BLOCKSIZE packs */
702 /* set copyright */
703 p_cdtext->block[i_block].copyright = (0x03 == (blocksize.copyright & 0x03));
704
705 /* set Language */
706 lcode = blocksize.langcode[i_block];
707 if(lcode <= CDTEXT_LANGUAGE_WALLON ||
708 (lcode >= CDTEXT_LANGUAGE_ZULU && lcode <= CDTEXT_LANGUAGE_AMHARIC) )
709 p_cdtext->block[i_block].language_code = lcode;
710 else
711 p_cdtext->block[i_block].language_code = CDTEXT_LANGUAGE_INVALID;
712
713 /* determine encoding */
714 switch (blocksize.charcode){
715 case CDTEXT_CHARCODE_ISO_8859_1:
716 /* default */
717 charset = (char *) "ISO-8859-1";
718 break;
719 case CDTEXT_CHARCODE_ASCII:
720 charset = (char *) "ASCII";
721 break;
722 case CDTEXT_CHARCODE_SHIFT_JIS:
723 charset = (char *) "SHIFT_JIS";
724 break;
725 }
726
727 /* set track numbers */
728 p_cdtext->block[i_block].first_track = blocksize.i_first_track;
729 p_cdtext->block[i_block].last_track = blocksize.i_last_track;
730
731 } else {
732 cdio_warn("CD-TEXT: No blocksize information available for block %d.\n", i_block);
733 return -1;
734 }
735
736 }
737
738 cdtext_read_pack(&pack, p_data);
739
740 #ifndef _CDTEXT_DBCC
741 if ( pack.db_chars ) {
742 cdio_warn("CD-TEXT: Double-byte characters not supported");
743 return -1;
744 }
745 #endif
746
747 cur_track = pack.i_track;
748
749 /* read text packs first */
750 j = 0;
751 switch (pack.type) {
752 case CDTEXT_PACK_GENRE:
753 /* If pack.text starts with an unprintable character, it is likely to be the genre_code.
754 * While the specification requires the first GENRE pack to start with the 2 byte genre code,
755 * it is not specific about the following ones. */
756 if (pack.text[0] <= 31) {
757 j = 2;
758 if (CDTEXT_GENRE_UNUSED == p_cdtext->block[i_block].genre_code)
759 p_cdtext->block[i_block].genre_code = CDTEXT_GET_LEN16(pack.text);
760 }
761 case CDTEXT_PACK_TITLE:
762 case CDTEXT_PACK_PERFORMER:
763 case CDTEXT_PACK_SONGWRITER:
764 case CDTEXT_PACK_COMPOSER:
765 case CDTEXT_PACK_ARRANGER:
766 case CDTEXT_PACK_MESSAGE:
767 case CDTEXT_PACK_DISCID:
768 case CDTEXT_PACK_UPC:
769 while (j < CDTEXT_LEN_TEXTDATA) {
770 /* not terminated */
771
772 if ( i_buf+2 >= sizeof(buffer)) {
773 cdio_warn("CD-TEXT: Field too long.");
774 return -1;
775 }
776
777 /* if the first character is a TAB, copy the buffer */
778 if ( i_buf == 0 && CDTEXT_COMPARE_CHAR(&pack.text[j], '\t', pack.db_chars)) {
779 memcpy(tab_buffer, buffer, sizeof(tab_buffer));
780 }
781
782 if ( ! CDTEXT_COMPARE_CHAR(&pack.text[j], '\0', pack.db_chars)) {
783 buffer[i_buf++] = pack.text[j];
784 if(pack.db_chars)
785 buffer[i_buf++] = pack.text[j+1];
786 } else if(i_buf > 0) {
787 /* if end of string */
788
789 /* check if the buffer contains only the Tab Indicator */
790 if ( CDTEXT_COMPARE_CHAR(buffer, '\t', pack.db_chars) ) {
791 if ( cur_track <= blocksize.i_first_track ) {
792 cdio_warn("CD-TEXT: Invalid use of Tab Indicator.");
793 return -1;
794 }
795 memcpy(buffer, tab_buffer, sizeof(buffer));
796 } else {
797 buffer[i_buf++] = 0;
798 if(pack.db_chars)
799 buffer[i_buf++] = 0;
800 }
801
802 switch (pack.type) {
803 case CDTEXT_PACK_TITLE:
804 cdtext_set(p_cdtext, CDTEXT_FIELD_TITLE, buffer, cur_track, charset);
805 break;
806 case CDTEXT_PACK_PERFORMER:
807 cdtext_set(p_cdtext, CDTEXT_FIELD_PERFORMER, buffer, cur_track, charset);
808 break;
809 case CDTEXT_PACK_SONGWRITER:
810 cdtext_set(p_cdtext, CDTEXT_FIELD_SONGWRITER, buffer, cur_track, charset);
811 break;
812 case CDTEXT_PACK_COMPOSER:
813 cdtext_set(p_cdtext, CDTEXT_FIELD_COMPOSER, buffer, cur_track, charset);
814 break;
815 case CDTEXT_PACK_ARRANGER:
816 cdtext_set(p_cdtext, CDTEXT_FIELD_ARRANGER, buffer, cur_track, charset);
817 break;
818 case CDTEXT_PACK_MESSAGE:
819 cdtext_set(p_cdtext, CDTEXT_FIELD_MESSAGE, buffer, cur_track, charset);
820 break;
821 case CDTEXT_PACK_DISCID:
822 if (cur_track == 0)
823 cdtext_set(p_cdtext, CDTEXT_FIELD_DISCID, buffer, cur_track, NULL);
824 break;
825 case CDTEXT_PACK_GENRE:
826 cdtext_set(p_cdtext, CDTEXT_FIELD_GENRE, buffer, cur_track, "ASCII");
827 break;
828 case CDTEXT_PACK_UPC:
829 if (cur_track == 0)
830 cdtext_set(p_cdtext, CDTEXT_FIELD_UPC_EAN, buffer, cur_track, "ASCII");
831 else
832 cdtext_set(p_cdtext, CDTEXT_FIELD_ISRC, buffer, cur_track, "ISO-8859-1");
833 break;
834 }
835 i_buf = 0;
836 ++cur_track;
837
838 }
839 if (pack.db_chars)
840 j+=2;
841 else
842 j+=1;
843 }
844 break;
845 }
846 /* This would be the right place to parse TOC and TOC2 fields. */
847
848 i_seq++;
849 i_data-=CDTEXT_LEN_PACK;
850 p_data+=CDTEXT_LEN_PACK;
851 } /* end of while loop */
852
853 p_cdtext->block_i = 0;
854 return 0;
855 }
856
857
858 /*!
859 Fills cdtext_pack_t with information read from p_data
860
861 @param p_pack out
862 @param p_data in
863 */
864 int
cdtext_read_pack(cdtext_pack_t * p_pack,const uint8_t * p_data)865 cdtext_read_pack(cdtext_pack_t *p_pack, const uint8_t *p_data) {
866 p_pack->type = p_data[0];
867 p_pack->i_track = p_data[1];
868 p_pack->seq = p_data[2];
869 p_pack->char_pos = p_data[3] & 0x0F;
870 p_pack->block = (p_data[3] >> 4) & 0x07;
871 p_pack->db_chars = (p_data[3] >> 7) & 0x01;
872 p_pack->text[0] = p_data[4];
873 p_pack->text[1] = p_data[5];
874 p_pack->text[2] = p_data[6];
875 p_pack->text[3] = p_data[7];
876 p_pack->text[4] = p_data[8];
877 p_pack->text[5] = p_data[9];
878 p_pack->text[6] = p_data[10];
879 p_pack->text[7] = p_data[11];
880 p_pack->text[8] = p_data[12];
881 p_pack->text[9] = p_data[13];
882 p_pack->text[10] = p_data[14];
883 p_pack->text[11] = p_data[15];
884 p_pack->crc[0] = p_data[16];
885 p_pack->crc[1] = p_data[17];
886
887 return 0;
888 }
889