"Fossies" - the Fresh Open Source Software Archive

Member "libcdio-2.1.0/lib/driver/cdtext.c" (12 Aug 2018, 23336 Bytes) of package /linux/privat/libcdio-2.1.0.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "cdtext.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 0.94_vs_1.0.0.

    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 *
  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 *
  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 *
  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
  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 *
  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 *
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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 }