Ejemplo n.º 1
0
/*!
  Initialize image structures.
 */
static bool
_init_cdrdao (_img_private_t *env)
{
  lsn_t lead_lsn;

  if (env->gen.init)
    return false;

  /* Have to set init before calling get_disc_last_lsn_cdrdao() or we will
     get into infinite recursion calling passing right here.
   */
  env->gen.init          = true;  
  env->gen.i_first_track = 1;
  env->psz_mcn           = NULL;
  env->disc_mode         = CDIO_DISC_MODE_NO_INFO;

  cdtext_init (&(env->gen.cdtext));

  /* Read in TOC sheet. */
  if ( !parse_tocfile(env, env->psz_cue_name) ) return false;
  
  lead_lsn = get_disc_last_lsn_cdrdao( (_img_private_t *) env);

  if (-1 == lead_lsn) 
    return false;

  /* Fake out leadout track and sector count for last track*/
  cdio_lsn_to_msf (lead_lsn, &env->tocent[env->gen.i_tracks].start_msf);
  env->tocent[env->gen.i_tracks].start_lba = cdio_lsn_to_lba(lead_lsn);
  env->tocent[env->gen.i_tracks-env->gen.i_first_track].sec_count = 
    cdio_lsn_to_lba(lead_lsn - env->tocent[env->gen.i_tracks-1].start_lba);

  return true;
}
Ejemplo n.º 2
0
/*
  Get cdtext information in p_user_data for track i_track.
  For disc information i_track is 0.

  Return the CD-TEXT or NULL if obj is NULL, CD-TEXT information does
  not exist, or we don't know how to get this implemented.
*/
cdtext_t *
get_cdtext_generic (void *p_user_data)
{
  generic_img_private_t *p_env = p_user_data;
  uint8_t *p_cdtext_data = NULL;
  size_t  len;

  if (!p_env) return NULL;

  if (p_env->b_cdtext_error) return NULL;

  if (NULL == p_env->cdtext) {
    p_cdtext_data = read_cdtext_generic (p_env);

    if (NULL != p_cdtext_data) {
      len = CDIO_MMC_GET_LEN16(p_cdtext_data)-2;
      p_env->cdtext = cdtext_init();

      if(len <= 0 || 0 != cdtext_data_init (p_env->cdtext, &p_cdtext_data[4], len)) {
        p_env->b_cdtext_error = true;
        cdtext_destroy (p_env->cdtext);
        free(p_env->cdtext);
        p_env->cdtext = NULL;
      }

      free(p_cdtext_data);
    }
  }

  return p_env->cdtext;
}
Ejemplo n.º 3
0
Track *track_init ()
{
	Track *track = NULL;
	track = malloc(sizeof(Track));

	if (NULL == track) {
		fprintf(stderr, "unable to create track\n");
	} else {
		track->zero_pre.type = DATA_ZERO;
		track->zero_pre.name = NULL;
		track->zero_pre.start = 0;
		track->zero_pre.length = 0;

		track->file.type = DATA_AUDIO;
		track->file.name = NULL;
		track->file.start = 0;
		track->file.length = 0;

		track->zero_post.type = DATA_ZERO;
		track->zero_post.name = NULL;
		track->zero_post.start = 0;
		track->zero_post.length = 0;

		track->mode = MODE_AUDIO;
		track->sub_mode = SUB_MODE_RW;
		track->flags = FLAG_NONE;
		track->isrc = NULL;
		track->cdtext = cdtext_init();
		track->nindex = 0;
	}

	return track;
}
Ejemplo n.º 4
0
/* returns 0 if field is a CD-TEXT keyword, returns non-zero otherwise */
int cdtext_is_keyword (char *key)
{
	int i;
	CdText *cdtext = cdtext_init ();

	for (i = 0; NULL != cdtext[i].key; i++)
		if (0 == strcmp (cdtext[i].key, key)) {
			free (cdtext);
			return (0);
		}

	free (cdtext);
	return (-1);
}
Ejemplo n.º 5
0
Cd *cd_init ()
{
	Cd *cd = NULL;
	cd = malloc(sizeof(Cd));

	if(NULL == cd) {
		fprintf(stderr, "unable to create cd\n");
	} else {
		cd->mode = MODE_CD_DA;
		cd->catalog = NULL;
		cd->cdtext = cdtext_init();
		cd->ntrack = 0;
	}

	return cd;
}
Ejemplo n.º 6
0
Archivo: cd.c Proyecto: Runcy/libcue
Track *track_init(void)
{
	Track *track = NULL;
	track = malloc(sizeof(Track));

	if (NULL == track) {
		fprintf(stderr, "unable to create track\n");
	} else {
		track->zero_pre.type = DATA_ZERO;
		track->zero_pre.name = NULL;
		track->zero_pre.start = -1;
		track->zero_pre.length = -1;

		track->file.type = DATA_AUDIO;
		track->file.name = NULL;
		track->file.start = -1;
		track->file.length = -1;

		track->zero_post.type = DATA_ZERO;
		track->zero_post.name = NULL;
		track->zero_post.start = -1;
		track->zero_post.length = -1;

		track->mode = MODE_AUDIO;
		track->sub_mode = SUB_MODE_RW;
		track->flags = FLAG_NONE;
		track->isrc = NULL;
		track->cdtext = cdtext_init();
		track->rem = rem_new();

                int i;
                for (i=0; i<MAXTRACK; i++)
                   track->index[i] = -1;
	}

	return track;
}
Ejemplo n.º 7
0
 CdioCDText(cdtext_t *p)
 { 
   p_cdtext = p;
   cdtext_init(p); // make sure we're initialized on the C side
 }
Ejemplo n.º 8
0
/* 
   Disk and track information for a Nero file are located at the end
   of the file. This routine extracts that information.

   FIXME: right now psz_nrg_name is not used. It will be in the future.
 */
static bool
parse_nrg (_img_private_t *p_env, const char *psz_nrg_name, 
	   const cdio_log_level_t log_level)
{
  off_t footer_start;
  off_t size;
  char *footer_buf = NULL;
  if (!p_env) return false;
  size = cdio_stream_stat (p_env->gen.data_source);
  if (-1 == size) return false;

  {
    _footer_t buf;
    cdio_assert (sizeof (buf) == 12);
 
    cdio_stream_seek (p_env->gen.data_source, size - sizeof (buf), SEEK_SET);
    cdio_stream_read (p_env->gen.data_source, (void *) &buf, sizeof (buf), 1);
    
    if (buf.v50.ID == UINT32_TO_BE (NERO_ID)) {
      cdio_debug ("detected Nero version 5.0 (32-bit offsets) NRG magic");
      footer_start = uint32_to_be (buf.v50.footer_ofs); 
    } else if (buf.v55.ID == UINT32_TO_BE (NER5_ID)) {
      cdio_debug ("detected Nero version 5.5.x (64-bit offsets) NRG magic");
      footer_start = uint64_from_be (buf.v55.footer_ofs);
    } else {
      cdio_log (log_level, "Image not recognized as either version 5.0 or "
		"version 5.5.x-6.x type NRG");
      return false;
    }

    cdio_debug (".NRG footer start = %ld, length = %ld", 
	       (long) footer_start, (long) (size - footer_start));

    cdio_assert ((size - footer_start) <= 4096);

    footer_buf = calloc(1, (size_t)(size - footer_start));

    cdio_stream_seek (p_env->gen.data_source, footer_start, SEEK_SET);
    cdio_stream_read (p_env->gen.data_source, footer_buf, 
		      (size_t)(size - footer_start), 1);
  }
  {
    int pos = 0;

    while (pos < size - footer_start) {
      _chunk_t *chunk = (void *) (footer_buf + pos);
      uint32_t opcode = UINT32_FROM_BE (chunk->id);
      
      bool break_out = false;
      
      switch (opcode) {

      case CUES_ID: /* "CUES" Seems to have sector size 2336 and 150 sector
		       pregap seems to be included at beginning of image.
		       */
      case CUEX_ID: /* "CUEX" */ 
	{
	  unsigned entries = UINT32_FROM_BE (chunk->len);
	  _cuex_array_t *_entries = (void *) chunk->data;
	  
	  cdio_assert (p_env->mapping == NULL);
	  
	  cdio_assert ( sizeof (_cuex_array_t) == 8 );
	  cdio_assert ( UINT32_FROM_BE (chunk->len) % sizeof(_cuex_array_t) 
			== 0 );
	  
	  entries /= sizeof (_cuex_array_t);
	  
	  if (CUES_ID == opcode) {
	    lsn_t lsn = UINT32_FROM_BE (_entries[0].lsn);
	    unsigned int idx;
	    unsigned int i = 0;
	    
	    cdio_debug ("CUES type image detected" );

	    /* CUES LSN has 150 pregap include at beginning? -/
	       cdio_assert (lsn == 0?);
	    */
	    
	    p_env->is_cues           = true; /* HACK alert. */
	    p_env->gen.i_tracks      = 0;
	    p_env->gen.i_first_track = 1;
	    for (idx = 1; idx < entries-1; idx += 2, i++) {
	      lsn_t sec_count;
	      int cdte_format = _entries[idx].addr_ctrl / 16;
	      int cdte_ctrl   = _entries[idx].type >> 4;

	      if ( COPY_PERMITTED & cdte_ctrl ) {
		if (p_env) p_env->tocent[i].flags |= COPY_PERMITTED;
	      } else {
		if (p_env) p_env->tocent[i].flags &= ~COPY_PERMITTED;
	      }
	      
	      if ( PRE_EMPHASIS & cdte_ctrl ) {
		if (p_env) p_env->tocent[i].flags |= PRE_EMPHASIS;
	      } else {
		if (p_env) p_env->tocent[i].flags &= ~PRE_EMPHASIS;
	      }
	      
	      if ( FOUR_CHANNEL_AUDIO & cdte_ctrl ) {
		if (p_env) p_env->tocent[i].flags |= FOUR_CHANNEL_AUDIO;
	      } else {
		if (p_env) p_env->tocent[i].flags &= ~FOUR_CHANNEL_AUDIO;
	      }
	      
	      cdio_assert (_entries[idx].track == _entries[idx + 1].track);
	      
	      /* lsn and sec_count*2 aren't correct, but it comes closer on the
		 single example I have: svcdgs.nrg
		 We are picking up the wrong fields and/or not interpreting
		 them correctly.
	      */

	      switch (cdte_format) {
	      case 0:
		lsn = UINT32_FROM_BE (_entries[idx].lsn);
		break;
	      case 1: 
		{
#if 0
		  msf_t msf = (msf_t) _entries[idx].lsn;
		  lsn = cdio_msf_to_lsn(&msf);
#else
		  lsn = CDIO_INVALID_LSN;
#endif		  
		  cdio_log (log_level, 
			    "untested (i.e. probably wrong) CUE MSF code");
		  break;
		}
	      default:
		lsn = CDIO_INVALID_LSN;
		cdio_log(log_level,
			 "unknown cdte_format %d", cdte_format);
	      }
	      
	      sec_count = UINT32_FROM_BE (_entries[idx + 1].lsn);
	      
	      _register_mapping (p_env, lsn, sec_count*2, 
				 (lsn+CDIO_PREGAP_SECTORS) * M2RAW_SECTOR_SIZE,
				 M2RAW_SECTOR_SIZE, TRACK_FORMAT_XA, true);
	    }
	  } else {
	    lsn_t lsn = UINT32_FROM_BE (_entries[0].lsn);
	    unsigned int idx;
	    unsigned int i = 0;
	    
	    cdio_debug ("CUEX type image detected");

	    /* LSN must start at -150 (LBA 0)? */
	    cdio_assert (lsn == -150); 
	    
	    for (idx = 2; idx < entries; idx += 2, i++) {
	      lsn_t sec_count;
	      int cdte_format = _entries[idx].addr_ctrl >> 4;
	      int cdte_ctrl   = _entries[idx].type >> 4;

	      if ( COPY_PERMITTED & cdte_ctrl ) {
		if (p_env) p_env->tocent[i].flags |= COPY_PERMITTED;
	      } else {
		if (p_env) p_env->tocent[i].flags &= ~COPY_PERMITTED;
	      }
	      
	      if ( PRE_EMPHASIS & cdte_ctrl ) {
		if (p_env) p_env->tocent[i].flags |= PRE_EMPHASIS;
	      } else {
		if (p_env) p_env->tocent[i].flags &= ~PRE_EMPHASIS;
	      }
	      
	      if ( FOUR_CHANNEL_AUDIO & cdte_ctrl ) {
		if (p_env) p_env->tocent[i].flags |= FOUR_CHANNEL_AUDIO;
	      } else {
		if (p_env) p_env->tocent[i].flags &= ~FOUR_CHANNEL_AUDIO;
	      }
	      
	      /* extractnrg.pl has cdte_format for LBA's 0, and
		 for MSF 1. ???

		 FIXME: Should decode as appropriate for cdte_format.
	       */
	      cdio_assert ( cdte_format == 0 || cdte_format == 1 );

	      cdio_assert (_entries[idx].track != _entries[idx + 1].track);
	      
	      lsn       = UINT32_FROM_BE (_entries[idx].lsn);
	      sec_count = UINT32_FROM_BE (_entries[idx + 1].lsn);
	      
	      _register_mapping (p_env, lsn, sec_count - lsn, 
				 (lsn + CDIO_PREGAP_SECTORS)*M2RAW_SECTOR_SIZE,
				 M2RAW_SECTOR_SIZE, TRACK_FORMAT_XA, true);
	    }
	  }
	  break;
	}
	
      case DAOX_ID: /* "DAOX" */ 
      case DAOI_ID: /* "DAOI" */
	{
	  _daox_t *_xentries = NULL;
	  _daoi_t *_ientries = NULL;
	  _dao_array_common_t *_dao_array_common = NULL;
	  _dao_common_t *_dao_common = (void *) chunk->data;
	  int disc_mode = _dao_common->unknown[1];
	  track_format_t track_format;
	  int i;

	  /* We include an extra 0 byte so these can be used as C strings.*/
	  p_env->psz_mcn = calloc(1, CDIO_MCN_SIZE+1);
	  memcpy(p_env->psz_mcn, &(_dao_common->psz_mcn), CDIO_MCN_SIZE);
	  p_env->psz_mcn[CDIO_MCN_SIZE] = '\0';

	  if (DAOX_ID == opcode) {
	    _xentries = (void *) chunk->data;
	    p_env->dtyp = _xentries->track_info[0].common.unknown[2];
	  } else {
	    _ientries = (void *) chunk->data;
	    p_env->dtyp = _ientries->track_info[0].common.unknown[2];
	  }

	  p_env->is_dao = true;
	  cdio_debug ("DAO%c tag detected, track format %d, mode %x\n", 
		      opcode==DAOX_ID ? 'X': 'I', p_env->dtyp, disc_mode);
	  switch (p_env->dtyp) {
	  case 0:
	    /* Mode 1 */
	    track_format     = TRACK_FORMAT_DATA;
	    p_env->disc_mode = CDIO_DISC_MODE_CD_DATA;
	    break;
	  case 2:
	    /* Mode 2 form 1 */
	    disc_mode             = 0;
	    track_format     = TRACK_FORMAT_XA;
	    p_env->disc_mode = CDIO_DISC_MODE_CD_XA;
	    break;
	  case 3:
	    /* Mode 2 */
	    track_format     = TRACK_FORMAT_XA;
	    p_env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ?? */
	    break;
	  case 0x6:
	    /* Mode2 form mix */
	    track_format     = TRACK_FORMAT_XA;
	    p_env->disc_mode = CDIO_DISC_MODE_CD_MIXED;
	    break;
	  case 0x20: /* ??? Mode2 form 2, Mode2 raw?? */
	    track_format     = TRACK_FORMAT_XA;
	    p_env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ??. */
	    break;
	  case 0x7:
	    track_format     = TRACK_FORMAT_AUDIO;
	    p_env->disc_mode = CDIO_DISC_MODE_CD_DA;
	    break;
	  default:
	    cdio_log (log_level, "Unknown track format %x\n", 
		      p_env->dtyp);
	    track_format = TRACK_FORMAT_AUDIO;
	  }
	  if (0 == disc_mode) {
	    for (i=0; i<p_env->gen.i_tracks; i++) {
	      p_env->tocent[i].track_format= track_format;
	      p_env->tocent[i].datastart   = 0;
	      p_env->tocent[i].track_green = false;
	      if (TRACK_FORMAT_AUDIO == track_format) {
		p_env->tocent[i].blocksize   = CDIO_CD_FRAMESIZE_RAW;
		p_env->tocent[i].datasize    = CDIO_CD_FRAMESIZE_RAW;
		p_env->tocent[i].endsize     = 0;
	      } else {
		p_env->tocent[i].datasize    = CDIO_CD_FRAMESIZE;
		p_env->tocent[i].datastart  =  0;
	      }
	    }
	  } else if (2 == disc_mode) {
	    for (i=0; i<p_env->gen.i_tracks; i++) {
	      p_env->tocent[i].track_green = true;
	      p_env->tocent[i].track_format= track_format;
	      p_env->tocent[i].datasize    = CDIO_CD_FRAMESIZE;
	      if (TRACK_FORMAT_XA == track_format) {
		p_env->tocent[i].datastart   = CDIO_CD_SYNC_SIZE 
		  + CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE;
		p_env->tocent[i].endsize     = CDIO_CD_SYNC_SIZE 
		  + CDIO_CD_ECC_SIZE;
	      } else {
		p_env->tocent[i].datastart   = CDIO_CD_SYNC_SIZE 
		  + CDIO_CD_HEADER_SIZE;
		p_env->tocent[i].endsize     = CDIO_CD_EDC_SIZE 
		  + CDIO_CD_M1F1_ZERO_SIZE + CDIO_CD_ECC_SIZE;
	      
	      }
	    }
	  } else if (0x20 == disc_mode) {
	    cdio_debug ("Mixed mode CD?\n");
	  } else {
	    /* Mixed mode CD */
	    cdio_log (log_level, 
		      "Don't know if mode 1, mode 2 or mixed: %x\n", 
		      disc_mode);
	  }

	  for (i=0; i<p_env->gen.i_tracks; i++) {

	    if (DAOX_ID == opcode) {
	      _dao_array_common = &_xentries->track_info[i].common;
	    } else {
	      _dao_array_common = &_ientries->track_info[i].common;
	    }
	    p_env->tocent[i].isrc = calloc(1, CDIO_ISRC_SIZE+1);
	    memcpy(p_env->tocent[i].isrc, _dao_array_common->psz_isrc, CDIO_ISRC_SIZE);
	    p_env->tocent[i].isrc[CDIO_ISRC_SIZE] = '\0';
	    if (p_env->tocent[i].isrc[0]) {
	       cdio_info("nrg isrc has value \"%s\"", p_env->tocent[i].isrc);
	    }

	    if (!p_env->tocent[i].datasize) {
	      continue;
	    }
	    if (DAOX_ID == opcode) {
	       p_env->tocent[i].pregap = (uint64_from_be
		(_xentries->track_info[i].index0)) / (p_env->tocent[i].datasize);
	    } else {
	       p_env->tocent[i].pregap = (uint32_from_be
		(_ientries->track_info[i].index0)) / (p_env->tocent[i].datasize);
	    }
	  }

	  break;
	}
      case NERO_ID: 
      case NER5_ID: 
	cdio_error ("unexpected nrg magic ID NER%c detected",
		    opcode==NERO_ID ? 'O': '5');
	free(footer_buf);
	return false;

      case END1_ID: /* "END!" */
	cdio_debug ("nrg end tag detected");
	break_out = true;
	break;
	
      case ETNF_ID: /* "ETNF" */ {
	unsigned entries = UINT32_FROM_BE (chunk->len);
	_etnf_array_t *_entries = (void *) chunk->data;
	
	cdio_assert (p_env->mapping == NULL);
	
	cdio_assert ( sizeof (_etnf_array_t) == 20 );
	cdio_assert ( UINT32_FROM_BE(chunk->len) % sizeof(_etnf_array_t) 
		      == 0 );
	
	entries /= sizeof (_etnf_array_t);
	
	cdio_debug ("SAO type image (ETNF) detected");
	
	{
	  int idx;
	  for (idx = 0; idx < entries; idx++) {
	    uint32_t _len = UINT32_FROM_BE (_entries[idx].length);
	    uint32_t _start = UINT32_FROM_BE (_entries[idx].start_lsn);
	    uint32_t _start2 = UINT32_FROM_BE (_entries[idx].start);
	    uint32_t track_mode= uint32_from_be (_entries[idx].type);
	    bool     track_green = true;
	    track_format_t track_format = TRACK_FORMAT_XA;
	    uint16_t  blocksize;     
	    
	    switch (track_mode) {
	    case 0:
	      /* Mode 1 */
	      track_format   = TRACK_FORMAT_DATA;
	      track_green    = false; /* ?? */
	      blocksize      = CDIO_CD_FRAMESIZE;
	      p_env->disc_mode = CDIO_DISC_MODE_CD_DATA;
	      cdio_debug ("Format DATA, blocksize %u", CDIO_CD_FRAMESIZE);
	      break;
	    case 2:
	      /* Mode 2 form 1 */
	      track_format   = TRACK_FORMAT_XA;
	      track_green    = false; /* ?? */
	      blocksize      = CDIO_CD_FRAMESIZE;
	      p_env->disc_mode = CDIO_DISC_MODE_CD_XA;
	      cdio_debug ("Format XA, blocksize %u", CDIO_CD_FRAMESIZE);
	      break;
	    case 3:
	      /* Mode 2 */
	      track_format   = TRACK_FORMAT_XA;
	      track_green    = true;
	      blocksize      = M2RAW_SECTOR_SIZE;
	      p_env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ?? */
	      cdio_debug ("Format XA, blocksize %u", M2RAW_SECTOR_SIZE);
	      break;
	    case 06:
	      /* Mode2 form mix */
	      track_format   = TRACK_FORMAT_XA;
	      track_green    = true;
	      blocksize      = M2RAW_SECTOR_SIZE;
	      p_env->disc_mode = CDIO_DISC_MODE_CD_MIXED;
	      cdio_debug ("Format MIXED CD, blocksize %u", M2RAW_SECTOR_SIZE);
	      break;
	    case 0x20: /* ??? Mode2 form 2, Mode2 raw?? */
	      track_format   = TRACK_FORMAT_XA;
	      track_green    = true;
	      blocksize      = M2RAW_SECTOR_SIZE;
	      p_env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ??. */
	      cdio_debug ("Format MIXED CD, blocksize %u", M2RAW_SECTOR_SIZE);
	      break;
	    case 7:
	      track_format   = TRACK_FORMAT_AUDIO;
	      track_green    = false;
	      blocksize      = CDIO_CD_FRAMESIZE_RAW;
	      p_env->disc_mode = CDIO_DISC_MODE_CD_DA;
	      cdio_debug ("Format CD_DA, blocksize %u", CDIO_CD_FRAMESIZE_RAW);
	      break;
	    default:
	      cdio_log (log_level, 
			"Don't know how to handle track mode (%lu)?",
			(long unsigned int) track_mode);
	      free(footer_buf);
	      return false;
	    }
	    
	    cdio_assert (_len % blocksize == 0);
	    
	    _len /= blocksize;
	    
	    cdio_assert (_start * blocksize == _start2);
	    
	    _start += idx * CDIO_PREGAP_SECTORS;
	    _register_mapping (p_env, _start, _len, _start2, blocksize,
			       track_format, track_green);

	  }
	}
	break;
      }
      
      case ETN2_ID: { /* "ETN2", same as above, but with 64bit stuff instead */
	unsigned entries = uint32_from_be (chunk->len);
	_etn2_array_t *_entries = (void *) chunk->data;
	
	cdio_assert (p_env->mapping == NULL);
	
	cdio_assert (sizeof (_etn2_array_t) == 32);
	cdio_assert (uint32_from_be (chunk->len) % sizeof (_etn2_array_t) == 0);
	
	entries /= sizeof (_etn2_array_t);
	
	cdio_debug ("SAO type image (ETN2) detected");

	{
	  int idx;
	  for (idx = 0; idx < entries; idx++) {
	    uint32_t _len = uint64_from_be (_entries[idx].length);
	    uint32_t _start = uint32_from_be (_entries[idx].start_lsn);
	    uint32_t _start2 = uint64_from_be (_entries[idx].start);
	    uint32_t track_mode= uint32_from_be (_entries[idx].type);
	    bool     track_green = true;
	    track_format_t track_format = TRACK_FORMAT_XA;
	    uint16_t  blocksize;     


	    switch (track_mode) {
	    case 0:
	      track_format = TRACK_FORMAT_DATA;
	      track_green  = false; /* ?? */
	      blocksize    = CDIO_CD_FRAMESIZE;
	      break;
	    case 2:
	      track_format = TRACK_FORMAT_XA;
	      track_green  = false; /* ?? */
	      blocksize    = CDIO_CD_FRAMESIZE;
	      break;
	    case 3:
	      track_format = TRACK_FORMAT_XA;
	      track_green  = true;
	      blocksize    = M2RAW_SECTOR_SIZE;
	      break;
	    case 7:
	      track_format = TRACK_FORMAT_AUDIO;
	      track_green  = false;
	      blocksize    = CDIO_CD_FRAMESIZE_RAW;
	      break;
	    default:
	      cdio_log (log_level, 
			"Don't know how to handle track mode (%lu)?",
			(long unsigned int) track_mode);
	      free(footer_buf);
	      return false;
	    }
	    
	    if (_len % blocksize != 0) {
	      cdio_log (log_level, 
			"length is not a multiple of blocksize " 
			 "len %lu, size %d, rem %lu", 
			 (long unsigned int) _len, blocksize, 
			 (long unsigned int) _len % blocksize);
	      if (0 == _len % CDIO_CD_FRAMESIZE) {
		cdio_log(log_level, "Adjusting blocksize to %d", 
			 CDIO_CD_FRAMESIZE);
		blocksize = CDIO_CD_FRAMESIZE;
	      } else if (0 == _len % M2RAW_SECTOR_SIZE) {
		cdio_log(log_level,
			 "Adjusting blocksize to %d", M2RAW_SECTOR_SIZE);
		blocksize = M2RAW_SECTOR_SIZE;
	      } else if (0 == _len % CDIO_CD_FRAMESIZE_RAW) {
		cdio_log(log_level, 
			 "Adjusting blocksize to %d", CDIO_CD_FRAMESIZE_RAW);
		blocksize = CDIO_CD_FRAMESIZE_RAW;
	      }
	    }
	    
	    _len /= blocksize;
	    
	    if (_start * blocksize != _start2) {
	      cdio_log (log_level,
			"%lu * %d != %lu", 
			 (long unsigned int) _start, blocksize, 
			 (long unsigned int) _start2);
	      if (_start * CDIO_CD_FRAMESIZE == _start2) {
		cdio_log(log_level,
			 "Adjusting blocksize to %d", CDIO_CD_FRAMESIZE);
		blocksize = CDIO_CD_FRAMESIZE;
	      } else if (_start * M2RAW_SECTOR_SIZE == _start2) {
		cdio_log(log_level,
			 "Adjusting blocksize to %d", M2RAW_SECTOR_SIZE);
		blocksize = M2RAW_SECTOR_SIZE;
	      } else if (_start * CDIO_CD_FRAMESIZE_RAW == _start2) {
		cdio_log(log_level,
			 "Adjusting blocksize to %d", CDIO_CD_FRAMESIZE_RAW);
		blocksize = CDIO_CD_FRAMESIZE_RAW;
	      }
	    }
	    
	    _start += idx * CDIO_PREGAP_SECTORS;
	    _register_mapping (p_env, _start, _len, _start2, blocksize,
			       track_format, track_green);
	  }
	}
	break;
      }
	
      case SINF_ID: { /* "SINF" */
	
	uint32_t _sessions;
	
	cdio_assert (UINT32_FROM_BE (chunk->len) == 4);
	
	memcpy(&_sessions, chunk->data, 4);

	cdio_debug ("SINF: %lu sessions", 
		    (long unsigned int) UINT32_FROM_BE (_sessions));
      }
	break;
	
      case MTYP_ID: { /* "MTYP" */
	uint32_t mtyp_be;
	uint32_t mtyp;
	
	cdio_assert (UINT32_FROM_BE (chunk->len) == 4);

	memcpy(&mtyp_be, chunk->data, 4);
	mtyp = UINT32_FROM_BE (mtyp_be);

	cdio_debug ("MTYP: %lu", 
		    (long unsigned int) mtyp);

	if (mtyp != MTYP_AUDIO_CD) {
	  cdio_log (log_level,
		    "Unknown MTYP value: %u", (unsigned int) mtyp);
	}
	p_env->mtyp = mtyp;
      }
	break;
	
      case CDTX_ID: { /* "CD TEXT" */
        uint8_t *wdata = (uint8_t *) chunk->data;
        int len = UINT32_FROM_BE (chunk->len);
        cdio_assert (len % CDTEXT_LEN_PACK == 0);
        p_env->gen.cdtext = cdtext_init ();
        if(0 !=cdtext_data_init (p_env->gen.cdtext, wdata, len))
        {
          cdtext_destroy(p_env->gen.cdtext);
          free(p_env->gen.cdtext);
          p_env->gen.cdtext = NULL;
        }

        break;
      }

      default:
	cdio_log (log_level,
		  "unknown tag %8.8x seen", 
		  (unsigned int) UINT32_FROM_BE (chunk->id));
	break;
      }
	
      if (break_out)
	break;
      
      pos += 8;
      pos += UINT32_FROM_BE (chunk->len);
    }
Ejemplo n.º 9
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;
}