/*! Read a binary CD-TEXT and fill a cdtext struct. @param p_cdtext the CD-TEXT object @param wdata the data @param i_data size of wdata @returns 0 on success, non-zero on failure */ int cdtext_data_init(cdtext_t *p_cdtext, uint8_t *wdata, size_t i_data) { uint8_t *p_data; int j; uint8_t buffer[256]; uint8_t tab_buffer[256]; int i_buf = 0; int i_block; int i_seq = 0; int i; cdtext_blocksize_t blocksize; char *charset = NULL; uint8_t cur_track; memset( buffer, 0, sizeof(buffer) ); memset( tab_buffer, 0, sizeof(buffer) ); p_data = wdata; if (i_data < CDTEXT_LEN_PACK || 0 != i_data % CDTEXT_LEN_PACK) { cdio_warn("CD-Text size is too small or not a multiple of pack size"); return -1; } #if 0 for(i=0; i < i_data; i++) printf("%0x%c", wdata[i], ((i+1) % 18 == 0 ? '\n' : ' ')); #endif /* Iterate over blocks */ i_block = -1; while(i_data > 0) { cdtext_pack_t pack; cdtext_read_pack(&pack, p_data); if (i_block != pack.block || i_seq != pack.seq) { cdtext_pack_t tpack; i_block = pack.block; if (i_block >= CDTEXT_NUM_BLOCKS_MAX) { cdio_warn("CD-TEXT: Invalid blocknumber %d.\n", i_block); return -1; } p_cdtext->block_i = i_block; i_seq = 0; memset( &blocksize, 0, CDTEXT_LEN_BLOCKSIZE); /* first read block size information for sanity checks and encoding */ for(i=0; i <= i_data-CDTEXT_LEN_PACK; i+=CDTEXT_LEN_PACK) { if (p_data[i+0] == CDTEXT_PACK_BLOCKSIZE) { cdtext_read_pack(&tpack, p_data+i); switch (tpack.i_track) { case 0: blocksize.charcode = tpack.text[0]; blocksize.i_first_track = tpack.text[1]; blocksize.i_last_track = tpack.text[2]; blocksize.copyright = tpack.text[3]; blocksize.i_packs[0] = tpack.text[4]; blocksize.i_packs[1] = tpack.text[5]; blocksize.i_packs[2] = tpack.text[6]; blocksize.i_packs[3] = tpack.text[7]; blocksize.i_packs[4] = tpack.text[8]; blocksize.i_packs[5] = tpack.text[9]; blocksize.i_packs[6] = tpack.text[10]; blocksize.i_packs[7] = tpack.text[11]; break; case 1: blocksize.i_packs[8] = tpack.text[0]; blocksize.i_packs[9] = tpack.text[1]; blocksize.i_packs[10] = tpack.text[2]; blocksize.i_packs[11] = tpack.text[3]; blocksize.i_packs[12] = tpack.text[4]; blocksize.i_packs[13] = tpack.text[5]; blocksize.i_packs[14] = tpack.text[6]; blocksize.i_packs[15] = tpack.text[7]; blocksize.lastseq[0] = tpack.text[8]; blocksize.lastseq[1] = tpack.text[9]; blocksize.lastseq[2] = tpack.text[10]; blocksize.lastseq[3] = tpack.text[11]; break; case 2: blocksize.lastseq[4] = tpack.text[0]; blocksize.lastseq[5] = tpack.text[1]; blocksize.lastseq[6] = tpack.text[2]; blocksize.lastseq[7] = tpack.text[3]; blocksize.langcode[0] = tpack.text[4]; blocksize.langcode[1] = tpack.text[5]; blocksize.langcode[2] = tpack.text[6]; blocksize.langcode[3] = tpack.text[7]; blocksize.langcode[4] = tpack.text[8]; blocksize.langcode[5] = tpack.text[9]; blocksize.langcode[6] = tpack.text[10]; blocksize.langcode[7] = tpack.text[11]; break; } } } if(blocksize.i_packs[15] == 3) { cdtext_lang_t lcode; /* if there were 3 BLOCKSIZE packs */ /* set copyright */ p_cdtext->block[i_block].copyright = (0x03 == (blocksize.copyright & 0x03)); /* set Language */ lcode = blocksize.langcode[i_block]; if(lcode <= CDTEXT_LANGUAGE_WALLON || (lcode >= CDTEXT_LANGUAGE_ZULU && lcode <= CDTEXT_LANGUAGE_AMHARIC) ) p_cdtext->block[i_block].language_code = lcode; else p_cdtext->block[i_block].language_code = CDTEXT_LANGUAGE_INVALID; /* determine encoding */ switch (blocksize.charcode){ case CDTEXT_CHARCODE_ISO_8859_1: /* default */ charset = (char *) "ISO-8859-1"; break; case CDTEXT_CHARCODE_ASCII: charset = (char *) "ASCII"; break; case CDTEXT_CHARCODE_SHIFT_JIS: charset = (char *) "SHIFT_JIS"; break; } /* set track numbers */ p_cdtext->block[i_block].first_track = blocksize.i_first_track; p_cdtext->block[i_block].last_track = blocksize.i_last_track; } else { cdio_warn("CD-TEXT: No blocksize information available for block %d.\n", i_block); return -1; } } cdtext_read_pack(&pack, p_data); #ifndef _CDTEXT_DBCC if ( pack.db_chars ) { cdio_warn("CD-TEXT: Double-byte characters not supported"); return -1; } #endif cur_track = pack.i_track; /* read text packs first */ j = 0; switch (pack.type) { case CDTEXT_PACK_GENRE: /* If pack.text starts with an unprintable character, it is likely to be the genre_code. * While the specification requires the first GENRE pack to start with the 2 byte genre code, * it is not specific about the following ones. */ if (pack.text[0] <= 31) { j = 2; if (CDTEXT_GENRE_UNUSED == p_cdtext->block[i_block].genre_code) p_cdtext->block[i_block].genre_code = CDTEXT_GET_LEN16(pack.text); } case CDTEXT_PACK_TITLE: case CDTEXT_PACK_PERFORMER: case CDTEXT_PACK_SONGWRITER: case CDTEXT_PACK_COMPOSER: case CDTEXT_PACK_ARRANGER: case CDTEXT_PACK_MESSAGE: case CDTEXT_PACK_DISCID: case CDTEXT_PACK_UPC: while (j < CDTEXT_LEN_TEXTDATA) { /* not terminated */ if ( i_buf+2 >= sizeof(buffer)) { cdio_warn("CD-TEXT: Field too long."); return -1; } /* if the first character is a TAB, copy the buffer */ if ( i_buf == 0 && CDTEXT_COMPARE_CHAR(&pack.text[j], '\t', pack.db_chars)) { memcpy(tab_buffer, buffer, sizeof(tab_buffer)); } if ( ! CDTEXT_COMPARE_CHAR(&pack.text[j], '\0', pack.db_chars)) { buffer[i_buf++] = pack.text[j]; if(pack.db_chars) buffer[i_buf++] = pack.text[j+1]; } else if(i_buf > 0) { /* if end of string */ /* check if the buffer contains only the Tab Indicator */ if ( CDTEXT_COMPARE_CHAR(buffer, '\t', pack.db_chars) ) { if ( cur_track <= blocksize.i_first_track ) { cdio_warn("CD-TEXT: Invalid use of Tab Indicator."); return -1; } memcpy(buffer, tab_buffer, sizeof(buffer)); } else { buffer[i_buf++] = 0; if(pack.db_chars) buffer[i_buf++] = 0; } switch (pack.type) { case CDTEXT_PACK_TITLE: cdtext_set(p_cdtext, CDTEXT_FIELD_TITLE, buffer, cur_track, charset); break; case CDTEXT_PACK_PERFORMER: cdtext_set(p_cdtext, CDTEXT_FIELD_PERFORMER, buffer, cur_track, charset); break; case CDTEXT_PACK_SONGWRITER: cdtext_set(p_cdtext, CDTEXT_FIELD_SONGWRITER, buffer, cur_track, charset); break; case CDTEXT_PACK_COMPOSER: cdtext_set(p_cdtext, CDTEXT_FIELD_COMPOSER, buffer, cur_track, charset); break; case CDTEXT_PACK_ARRANGER: cdtext_set(p_cdtext, CDTEXT_FIELD_ARRANGER, buffer, cur_track, charset); break; case CDTEXT_PACK_MESSAGE: cdtext_set(p_cdtext, CDTEXT_FIELD_MESSAGE, buffer, cur_track, charset); break; case CDTEXT_PACK_DISCID: if (cur_track == 0) cdtext_set(p_cdtext, CDTEXT_FIELD_DISCID, buffer, cur_track, NULL); break; case CDTEXT_PACK_GENRE: cdtext_set(p_cdtext, CDTEXT_FIELD_GENRE, buffer, cur_track, "ASCII"); break; case CDTEXT_PACK_UPC: if (cur_track == 0) cdtext_set(p_cdtext, CDTEXT_FIELD_UPC_EAN, buffer, cur_track, "ASCII"); else cdtext_set(p_cdtext, CDTEXT_FIELD_ISRC, buffer, cur_track, "ISO-8859-1"); break; } i_buf = 0; ++cur_track; } if (pack.db_chars) j+=2; else j+=1; } break; } /* This would be the right place to parse TOC and TOC2 fields. */ i_seq++; i_data-=CDTEXT_LEN_PACK; p_data+=CDTEXT_LEN_PACK; } /* end of while loop */ p_cdtext->block_i = 0; return 0; }
static bool parse_tocfile (_img_private_t *cd, const char *psz_cue_name) { /* The below declarations may be common in other image-parse routines. */ FILE *fp; char psz_line[MAXLINE]; /* text of current line read in file fp. */ unsigned int i_line=0; /* line number in file of psz_line. */ int i = -1; /* Position in tocent. Same as cd->gen.i_tracks - 1 */ char *psz_keyword, *psz_field, *psz_cue_name_dup; cdio_log_level_t log_level = (cd) ? CDIO_LOG_WARN : CDIO_LOG_INFO ; cdtext_field_t cdtext_key; /* The below declaration(s) may be unique to this image-parse routine. */ unsigned int i_cdtext_nest = 0; if (NULL == psz_cue_name) return false; psz_cue_name_dup = _cdio_strdup_fixpath(psz_cue_name); if (NULL == psz_cue_name_dup) return false; fp = CDIO_FOPEN (psz_cue_name_dup, "r"); cdio_free(psz_cue_name_dup); if (fp == NULL) { cdio_log(log_level, "error opening %s for reading: %s", psz_cue_name, strerror(errno)); return false; } if (cd) { cd->gen.b_cdtext_error = false; } while (fgets(psz_line, MAXLINE, fp)) { i_line++; /* strip comment from line */ /* todo: // in quoted strings? */ /* //comment */ if ((psz_field = strstr (psz_line, "//"))) *psz_field = '\0'; if ((psz_keyword = strtok (psz_line, " \t\n\r"))) { /* CATALOG "ddddddddddddd" */ if (0 == strcmp ("CATALOG", psz_keyword)) { if (-1 == i) { if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) { if (13 != strlen(psz_field)) { cdio_log(log_level, "%s line %d after word CATALOG:", psz_cue_name, i_line); cdio_log(log_level, "Token %s has length %ld. Should be 13 digits.", psz_field, (long int) strlen(psz_field)); goto err_exit; } else { /* Check that we have all digits*/ unsigned int j; for (j=0; j<13; j++) { if (!isdigit((unsigned char) psz_field[j])) { cdio_log(log_level, "%s line %d after word CATALOG:", psz_cue_name, i_line); cdio_log(log_level, "Character \"%c\" at postition %i of token \"%s\"" " is not all digits.", psz_field[j], j+1, psz_field); goto err_exit; } } if (NULL != cd) cd->psz_mcn = strdup (psz_field); } } else { cdio_log(log_level, "%s line %d after word CATALOG:", psz_cue_name, i_line); cdio_log(log_level, "Expecting 13 digits; nothing seen."); goto err_exit; } } else { goto err_exit; } /* CD_DA | CD_ROM | CD_ROM_XA */ } else if (0 == strcmp ("CD_DA", psz_keyword)) { if (-1 == i) { if (NULL != cd) cd->disc_mode = CDIO_DISC_MODE_CD_DA; } else { goto not_in_global_section; } } else if (0 == strcmp ("CD_ROM", psz_keyword)) { if (-1 == i) { if (NULL != cd) cd->disc_mode = CDIO_DISC_MODE_CD_DATA; } else { goto not_in_global_section; } } else if (0 == strcmp ("CD_ROM_XA", psz_keyword)) { if (-1 == i) { if (NULL != cd) cd->disc_mode = CDIO_DISC_MODE_CD_XA; } else { goto not_in_global_section; } /* TRACK <track-mode> [<sub-channel-mode>] */ } else if (0 == strcmp ("TRACK", psz_keyword)) { i++; if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { if (0 == strcmp ("AUDIO", psz_field)) { if (NULL != cd) { cd->tocent[i].track_format = TRACK_FORMAT_AUDIO; cd->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW; cd->tocent[i].datasize = CDIO_CD_FRAMESIZE_RAW; cd->tocent[i].datastart = 0; cd->tocent[i].endsize = 0; switch(cd->disc_mode) { case CDIO_DISC_MODE_NO_INFO: cd->disc_mode = CDIO_DISC_MODE_CD_DA; break; case CDIO_DISC_MODE_CD_DA: case CDIO_DISC_MODE_CD_MIXED: case CDIO_DISC_MODE_ERROR: /* Disc type stays the same. */ break; case CDIO_DISC_MODE_CD_DATA: case CDIO_DISC_MODE_CD_XA: cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; break; default: cd->disc_mode = CDIO_DISC_MODE_ERROR; } } } else if (0 == strcmp ("MODE1", psz_field)) { if (NULL != cd) { cd->tocent[i].track_format = TRACK_FORMAT_DATA; cd->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW; cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE; cd->tocent[i].datasize = CDIO_CD_FRAMESIZE; cd->tocent[i].endsize = CDIO_CD_EDC_SIZE + CDIO_CD_M1F1_ZERO_SIZE + CDIO_CD_ECC_SIZE; switch(cd->disc_mode) { case CDIO_DISC_MODE_NO_INFO: cd->disc_mode = CDIO_DISC_MODE_CD_DATA; break; case CDIO_DISC_MODE_CD_DATA: case CDIO_DISC_MODE_CD_MIXED: case CDIO_DISC_MODE_ERROR: /* Disc type stays the same. */ break; case CDIO_DISC_MODE_CD_DA: case CDIO_DISC_MODE_CD_XA: cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; break; default: cd->disc_mode = CDIO_DISC_MODE_ERROR; } } } else if (0 == strcmp ("MODE1_RAW", psz_field)) { if (NULL != cd) { cd->tocent[i].track_format = TRACK_FORMAT_DATA; cd->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW; cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE; cd->tocent[i].datasize = CDIO_CD_FRAMESIZE; cd->tocent[i].endsize = CDIO_CD_EDC_SIZE + CDIO_CD_M1F1_ZERO_SIZE + CDIO_CD_ECC_SIZE; switch(cd->disc_mode) { case CDIO_DISC_MODE_NO_INFO: cd->disc_mode = CDIO_DISC_MODE_CD_DATA; break; case CDIO_DISC_MODE_CD_DATA: case CDIO_DISC_MODE_CD_MIXED: case CDIO_DISC_MODE_ERROR: /* Disc type stays the same. */ break; case CDIO_DISC_MODE_CD_DA: case CDIO_DISC_MODE_CD_XA: cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; break; default: cd->disc_mode = CDIO_DISC_MODE_ERROR; } } } else if (0 == strcmp ("MODE2", psz_field)) { if (NULL != cd) { cd->tocent[i].track_format = TRACK_FORMAT_XA; cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE; cd->tocent[i].datasize = M2RAW_SECTOR_SIZE; cd->tocent[i].endsize = 0; switch(cd->disc_mode) { case CDIO_DISC_MODE_NO_INFO: cd->disc_mode = CDIO_DISC_MODE_CD_XA; break; case CDIO_DISC_MODE_CD_XA: case CDIO_DISC_MODE_CD_MIXED: case CDIO_DISC_MODE_ERROR: /* Disc type stays the same. */ break; case CDIO_DISC_MODE_CD_DA: case CDIO_DISC_MODE_CD_DATA: cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; break; default: cd->disc_mode = CDIO_DISC_MODE_ERROR; } } } else if (0 == strcmp ("MODE2_FORM1", psz_field)) { if (NULL != cd) { cd->tocent[i].track_format = TRACK_FORMAT_XA; cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE; cd->tocent[i].datasize = CDIO_CD_FRAMESIZE_RAW; cd->tocent[i].endsize = 0; switch(cd->disc_mode) { case CDIO_DISC_MODE_NO_INFO: cd->disc_mode = CDIO_DISC_MODE_CD_XA; break; case CDIO_DISC_MODE_CD_XA: case CDIO_DISC_MODE_CD_MIXED: case CDIO_DISC_MODE_ERROR: /* Disc type stays the same. */ break; case CDIO_DISC_MODE_CD_DA: case CDIO_DISC_MODE_CD_DATA: cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; break; default: cd->disc_mode = CDIO_DISC_MODE_ERROR; } } } else if (0 == strcmp ("MODE2_FORM2", psz_field)) { if (NULL != cd) { cd->tocent[i].track_format = TRACK_FORMAT_XA; cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE; cd->tocent[i].datasize = CDIO_CD_FRAMESIZE; cd->tocent[i].endsize = CDIO_CD_SYNC_SIZE + CDIO_CD_ECC_SIZE; switch(cd->disc_mode) { case CDIO_DISC_MODE_NO_INFO: cd->disc_mode = CDIO_DISC_MODE_CD_XA; break; case CDIO_DISC_MODE_CD_XA: case CDIO_DISC_MODE_CD_MIXED: case CDIO_DISC_MODE_ERROR: /* Disc type stays the same. */ break; case CDIO_DISC_MODE_CD_DA: case CDIO_DISC_MODE_CD_DATA: cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; break; default: cd->disc_mode = CDIO_DISC_MODE_ERROR; } } } else if (0 == strcmp ("MODE2_FORM_MIX", psz_field)) { if (NULL != cd) { cd->tocent[i].track_format = TRACK_FORMAT_XA; cd->tocent[i].datasize = M2RAW_SECTOR_SIZE; cd->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW; cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE; cd->tocent[i].track_green = true; cd->tocent[i].endsize = 0; switch(cd->disc_mode) { case CDIO_DISC_MODE_NO_INFO: cd->disc_mode = CDIO_DISC_MODE_CD_XA; break; case CDIO_DISC_MODE_CD_XA: case CDIO_DISC_MODE_CD_MIXED: case CDIO_DISC_MODE_ERROR: /* Disc type stays the same. */ break; case CDIO_DISC_MODE_CD_DA: case CDIO_DISC_MODE_CD_DATA: cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; break; default: cd->disc_mode = CDIO_DISC_MODE_ERROR; } } } else if (0 == strcmp ("MODE2_RAW", psz_field)) { if (NULL != cd) { cd->tocent[i].track_format = TRACK_FORMAT_XA; cd->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW; cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE; cd->tocent[i].datasize = CDIO_CD_FRAMESIZE; cd->tocent[i].track_green = true; cd->tocent[i].endsize = 0; switch(cd->disc_mode) { case CDIO_DISC_MODE_NO_INFO: cd->disc_mode = CDIO_DISC_MODE_CD_XA; break; case CDIO_DISC_MODE_CD_XA: case CDIO_DISC_MODE_CD_MIXED: case CDIO_DISC_MODE_ERROR: /* Disc type stays the same. */ break; case CDIO_DISC_MODE_CD_DA: case CDIO_DISC_MODE_CD_DATA: cd->disc_mode = CDIO_DISC_MODE_CD_MIXED; break; default: cd->disc_mode = CDIO_DISC_MODE_ERROR; } } } else { cdio_log(log_level, "%s line %d after TRACK:", psz_cue_name, i_line); cdio_log(log_level, "'%s' not a valid mode.", psz_field); goto err_exit; } } if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { /* \todo: set sub-channel-mode */ #ifdef TODO if (0 == strcmp ("RW", psz_field)) ; else if (0 == strcmp ("RW_RAW", psz_field)) ; #endif } if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { goto format_error; } /* track flags */ /* [NO] COPY | [NO] PRE_EMPHASIS */ } else if (0 == strcmp ("NO", psz_keyword)) { if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { if (0 == strcmp ("COPY", psz_field)) { if (NULL != cd) cd->tocent[i].flags &= ~CDIO_TRACK_FLAG_COPY_PERMITTED; } else if (0 == strcmp ("PRE_EMPHASIS", psz_field)) if (NULL != cd) { cd->tocent[i].flags &= ~CDIO_TRACK_FLAG_PRE_EMPHASIS; } } else { goto format_error; } if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { goto format_error; } } else if (0 == strcmp ("COPY", psz_keyword)) { if (NULL != cd && i >= 0) cd->tocent[i].flags |= CDIO_TRACK_FLAG_COPY_PERMITTED; } else if (0 == strcmp ("PRE_EMPHASIS", psz_keyword)) { if (NULL != cd && i >= 0) cd->tocent[i].flags |= CDIO_TRACK_FLAG_PRE_EMPHASIS; /* TWO_CHANNEL_AUDIO */ } else if (0 == strcmp ("TWO_CHANNEL_AUDIO", psz_keyword)) { if (NULL != cd && i >= 0) cd->tocent[i].flags &= ~CDIO_TRACK_FLAG_FOUR_CHANNEL_AUDIO; /* FOUR_CHANNEL_AUDIO */ } else if (0 == strcmp ("FOUR_CHANNEL_AUDIO", psz_keyword)) { if (NULL != cd && i >= 0) cd->tocent[i].flags |= CDIO_TRACK_FLAG_FOUR_CHANNEL_AUDIO; /* ISRC "CCOOOYYSSSSS" */ } else if (0 == strcmp ("ISRC", psz_keyword)) { if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) { if (NULL != cd) cd->tocent[i].isrc = strdup(psz_field); } else { goto format_error; } /* SILENCE <length> */ } else if (0 == strcmp ("SILENCE", psz_keyword)) { if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { if (NULL != cd) cd->tocent[i].silence = cdio_mmssff_to_lba (psz_field); } else { goto format_error; } cdio_log(log_level, "%s line %d: SILENCE not fully implimented", psz_cue_name, i_line); /* ZERO <length> */ } else if (0 == strcmp ("ZERO", psz_keyword)) { UNIMPLIMENTED_MSG; /* [FILE|AUDIOFILE] "<filename>" <start-msf> [<length-msf>] */ } else if (0 == strcmp ("FILE", psz_keyword) || 0 == strcmp ("AUDIOFILE", psz_keyword)) { if (0 <= i) { if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) { /* Handle "<filename>" */ if (cd) { char *psz_dirname = cdio_dirname(psz_cue_name); char *psz_filename = cdio_abspath(psz_dirname, psz_field); cd->tocent[i].filename = strdup (psz_filename); free(psz_filename); free(psz_dirname); /* To do: do something about reusing existing files. */ if (!(cd->tocent[i].data_source = cdio_stdio_new (psz_field))) { cdio_log (log_level, "%s line %d: can't open file `%s' for reading", psz_cue_name, i_line, psz_field); goto err_exit; } } else { CdioDataSource_t *s = cdio_stdio_new (psz_field); if (!s) { cdio_log (log_level, "%s line %d: can't open file `%s' for reading", psz_cue_name, i_line, psz_field); goto err_exit; } cdio_stdio_destroy (s); } } if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { /* Handle <start-msf> */ lba_t i_start_lba = cdio_lsn_to_lba(cdio_mmssff_to_lba (psz_field)); if (CDIO_INVALID_LBA == i_start_lba) { cdio_log(log_level, "%s line %d: invalid MSF string %s", psz_cue_name, i_line, psz_field); goto err_exit; } if (NULL != cd) { cd->tocent[i].start_lba = i_start_lba; cdio_lba_to_msf(i_start_lba, &(cd->tocent[i].start_msf)); } } if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { /* Handle <length-msf> */ lba_t lba = cdio_mmssff_to_lba (psz_field); if (CDIO_INVALID_LBA == lba) { cdio_log(log_level, "%s line %d: invalid MSF string %s", psz_cue_name, i_line, psz_field); goto err_exit; } if (cd) { off_t i_size = cdio_stream_stat(cd->tocent[i].data_source); if (lba) { if ( (lba * cd->tocent[i].datasize) > i_size) { cdio_log(log_level, "%s line %d: MSF length %s exceeds end of file", psz_cue_name, i_line, psz_field); goto err_exit; } } else { lba = (lba_t) (i_size / cd->tocent[i].blocksize); } cd->tocent[i].sec_count = lba; } } if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { goto format_error; } } else { goto not_in_global_section; } /* DATAFILE "<filename>" #byte-offset <start-msf> */ } else if (0 == strcmp ("DATAFILE", psz_keyword)) { if (0 <= i) { if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) { /* Handle <filename> */ char *psz_dirname = cdio_dirname(psz_cue_name); char *psz_filename = cdio_abspath(psz_dirname, psz_field); if (cd) { cd->tocent[i].filename = strdup(psz_filename); /* To do: do something about reusing existing files. */ if (!(cd->tocent[i].data_source = cdio_stdio_new (psz_field))) { cdio_log (log_level, "%s line %d: can't open file `%s' for reading", psz_cue_name, i_line, psz_field); free(psz_filename); free(psz_dirname); goto err_exit; } } else { CdioDataSource_t *s = cdio_stdio_new (psz_filename); if (!s) { cdio_log (log_level, "%s line %d: can't open file `%s' for reading", psz_cue_name, i_line, psz_field); free(psz_filename); free(psz_dirname); goto err_exit; } cdio_stdio_destroy (s); } free(psz_filename); free(psz_dirname); } psz_field = strtok (NULL, " \t\n\r"); if (psz_field) { /* Handle optional #byte-offset */ if ( psz_field[0] == '#') { long int offset; psz_field++; errno = 0; offset = strtol(psz_field, (char **)NULL, 10); if ( (LONG_MIN == offset || LONG_MAX == offset) && 0 != errno ) { cdio_log (log_level, "%s line %d: can't convert `%s' to byte offset", psz_cue_name, i_line, psz_field); goto err_exit; } else { if (NULL != cd) { cd->tocent[i].offset = offset; } } psz_field = strtok (NULL, " \t\n\r"); } } if (psz_field) { /* Handle start-msf */ lba_t lba = cdio_mmssff_to_lba (psz_field); if (CDIO_INVALID_LBA == lba) { cdio_log(log_level, "%s line %d: invalid MSF string %s", psz_cue_name, i_line, psz_field); goto err_exit; } if (cd) { cd->tocent[i].start_lba = lba; cdio_lba_to_msf(cd->tocent[i].start_lba, &(cd->tocent[i].start_msf)); } } else { /* No start-msf. */ if (cd) { if (i) { uint16_t i_blocksize = cd->tocent[i-1].blocksize; off_t i_size = cdio_stream_stat(cd->tocent[i-1].data_source); check_track_is_blocksize_multiple(cd->tocent[i-1].filename, i-1, i_size, i_blocksize); /* Append size of previous datafile. */ cd->tocent[i].start_lba = (lba_t) (cd->tocent[i-1].start_lba + (i_size / i_blocksize)); } cd->tocent[i].offset = 0; cd->tocent[i].start_lba += CDIO_PREGAP_SECTORS; cdio_lba_to_msf(cd->tocent[i].start_lba, &(cd->tocent[i].start_msf)); } } } else { goto not_in_global_section; } /* FIFO "<fifo path>" [<length>] */ } else if (0 == strcmp ("FIFO", psz_keyword)) { goto unimplimented_error; /* START MM:SS:FF */ } else if (0 == strcmp ("START", psz_keyword)) { if (0 <= i) { if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { /* todo: line is too long! */ if (NULL != cd) { cd->tocent[i].pregap = cd->tocent[i].start_lba; cd->tocent[i].start_lba += cdio_mmssff_to_lba (psz_field); cdio_lba_to_msf(cd->tocent[i].start_lba, &(cd->tocent[i].start_msf)); } } if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { goto format_error; } } else { goto not_in_global_section; } /* PREGAP MM:SS:FF */ } else if (0 == strcmp ("PREGAP", psz_keyword)) { if (0 <= i) { if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { if (NULL != cd) cd->tocent[i].pregap = cdio_mmssff_to_lba (psz_field); } else { goto format_error; } if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { goto format_error; } } else { goto not_in_global_section; } /* INDEX MM:SS:FF */ } else if (0 == strcmp ("INDEX", psz_keyword)) { if (0 <= i) { if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { if (NULL != cd) { #if 0 if (1 == cd->tocent[i].nindex) { cd->tocent[i].indexes[1] = cd->tocent[i].indexes[0]; cd->tocent[i].nindex++; } cd->tocent[i].indexes[cd->tocent[i].nindex++] = cdio_mmssff_to_lba (psz_field) + cd->tocent[i].indexes[0]; #else ; #endif } } else { goto format_error; } if (NULL != strtok (NULL, " \t\n\r")) { goto format_error; } } else { goto not_in_global_section; } /* CD_TEXT { ... } */ /* todo: opening { must be on same line as CD_TEXT */ } else if (0 == strcmp ("CD_TEXT", psz_keyword)) { if (NULL == (psz_field = strtok (NULL, " \t\n\r"))) { goto format_error; } if ( 0 == strcmp( "{", psz_field ) ) { i_cdtext_nest++; } else { cdio_log (log_level, "%s line %d: expecting '{'", psz_cue_name, i_line); goto err_exit; } // TODO: implement language mapping } else if (0 == strcmp ("LANGUAGE_MAP", psz_keyword)) { /* LANGUAGE d { ... } */ } else if (0 == strcmp ("LANGUAGE", psz_keyword)) { /* Language number */ if (NULL == (psz_field = strtok (NULL, " \t\n\r"))) { goto format_error; } if ( 0 == strcmp( "{", psz_field ) ) { i_cdtext_nest++; } } else if (0 == strcmp ("{", psz_keyword)) { i_cdtext_nest++; } else if (0 == strcmp ("}", psz_keyword)) { if (i_cdtext_nest > 0) i_cdtext_nest--; } else if ( CDTEXT_FIELD_INVALID != (cdtext_key = cdtext_is_field (psz_keyword)) ) { if (NULL != cd) { if (NULL == cd->gen.cdtext) { cd->gen.cdtext = cdtext_init (); /* until language mapping is implemented ...*/ cd->gen.cdtext->block[cd->gen.cdtext->block_i].language_code = CDTEXT_LANGUAGE_ENGLISH; } cdtext_set (cd->gen.cdtext, cdtext_key, (uint8_t*) strtok (NULL, "\"\t\n\r"), (-1 == i ? 0 : cd->gen.i_first_track + i), "ISO-8859-1"); } /* unrecognized line */ } else { cdio_log(log_level, "%s line %d: warning: unrecognized word: %s", psz_cue_name, i_line, psz_keyword); goto err_exit; } } } if (NULL != cd) { cd->gen.i_tracks = i+1; cd->gen.toc_init = true; } fclose (fp); return true; unimplimented_error: UNIMPLIMENTED_MSG; goto err_exit; format_error: cdio_log(log_level, "%s line %d after word %s", psz_cue_name, i_line, psz_keyword); goto err_exit; not_in_global_section: cdio_log(log_level, "%s line %d: word %s only allowed in global section", psz_cue_name, i_line, psz_keyword); err_exit: fclose (fp); return false; }