Blame lib/driver/image/cdrdao.c

Packit dd8086
/*
Packit dd8086
  Copyright (C) 2004-2008, 2011-2012, 2014, 2017
Packit dd8086
  Rocky Bernstein <rocky@gnu.org>
Packit dd8086
    toc reading routine adapted from cuetools
Packit dd8086
  Copyright (C) 2003 Svend Sanjay Sorensen <ssorensen@fastmail.fm>
Packit dd8086
Packit dd8086
  This program is free software: you can redistribute it and/or modify
Packit dd8086
  it under the terms of the GNU General Public License as published by
Packit dd8086
  the Free Software Foundation, either version 3 of the License, or
Packit dd8086
  (at your option) any later version.
Packit dd8086
Packit dd8086
  This program is distributed in the hope that it will be useful,
Packit dd8086
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit dd8086
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit dd8086
  GNU General Public License for more details.
Packit dd8086
Packit dd8086
  You should have received a copy of the GNU General Public License
Packit dd8086
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit dd8086
*/
Packit dd8086
Packit dd8086
/* This code implements low-level access functions for a CD images
Packit dd8086
   residing inside a disk file (*.bin) and its associated cue sheet.
Packit dd8086
   (*.cue).
Packit dd8086
*/
Packit dd8086
#include "portable.h"
Packit dd8086
Packit dd8086
#ifdef HAVE_LIMITS_H
Packit dd8086
#include <limits.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_STDIO_H
Packit dd8086
#include <stdio.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_STDLIB_H
Packit dd8086
#include <stdlib.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_STRING_H
Packit dd8086
#include <string.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_STRINGS_H
Packit dd8086
#include <strings.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_GLOB_H
Packit dd8086
#include <glob.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_ERRNO_H
Packit dd8086
#include <errno.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_INTTYPES_H
Packit dd8086
#include <inttypes.h>
Packit dd8086
#else
Packit dd8086
#define PRId64 "lld"
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#include <ctype.h>
Packit dd8086
Packit dd8086
#include <cdio/logging.h>
Packit dd8086
#include <cdio/sector.h>
Packit dd8086
#include <cdio/util.h>
Packit dd8086
#include <stdio.h>
Packit dd8086
#include <cdio/utf8.h>
Packit dd8086
#include <cdio/version.h>
Packit dd8086
Packit dd8086
#include "image.h"
Packit dd8086
#include "cdio_assert.h"
Packit dd8086
#include "_cdio_stdio.h"
Packit dd8086
Packit dd8086
/* reader */
Packit dd8086
Packit dd8086
#define DEFAULT_CDIO_DEVICE "videocd.bin"
Packit dd8086
#define DEFAULT_CDIO_CDRDAO "videocd.toc"
Packit dd8086
Packit dd8086
#ifdef _WIN32
Packit dd8086
#define CDIO_FOPEN fopen_utf8
Packit dd8086
#else
Packit dd8086
#define CDIO_FOPEN fopen
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#include "image_common.h"
Packit dd8086
#include "cdtext_private.h"
Packit dd8086
Packit dd8086
static lsn_t get_disc_last_lsn_cdrdao (void *p_user_data);
Packit dd8086
static bool parse_tocfile (_img_private_t *cd, const char *p_toc_name);
Packit dd8086
Packit dd8086
Packit dd8086
static bool
Packit dd8086
check_track_is_blocksize_multiple(const char *psz_fname,
Packit dd8086
				  track_t i_track, off_t i_size,
Packit dd8086
				  uint16_t i_blocksize)
Packit dd8086
{
Packit dd8086
  if (i_size % i_blocksize) {
Packit dd8086
    cdio_info ("image %s track %d size (%" PRId64 ") not a multiple"
Packit dd8086
	       " of the blocksize (%ld)",
Packit dd8086
	       psz_fname ? psz_fname : "unknown??", i_track, (int64_t)i_size,
Packit dd8086
	       (long int) i_blocksize);
Packit dd8086
    if (i_size % M2RAW_SECTOR_SIZE == 0)
Packit dd8086
      cdio_info ("this may be a 2336-type disc image");
Packit dd8086
    else if (i_size % CDIO_CD_FRAMESIZE_RAW == 0)
Packit dd8086
      cdio_info ("this may be a 2352-type disc image");
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
  return true;
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Initialize image structures.
Packit dd8086
 */
Packit dd8086
static bool
Packit dd8086
_init_cdrdao (_img_private_t *env)
Packit dd8086
{
Packit dd8086
  lsn_t lead_lsn;
Packit dd8086
Packit dd8086
  if (env->gen.init)
Packit dd8086
    return false;
Packit dd8086
Packit dd8086
  /* Have to set init before calling get_disc_last_lsn_cdrdao() or we will
Packit dd8086
     get into infinite recursion calling passing right here.
Packit dd8086
   */
Packit dd8086
  env->gen.init          = true;
Packit dd8086
  env->gen.i_first_track = 1;
Packit dd8086
  env->psz_mcn           = NULL;
Packit dd8086
  env->disc_mode         = CDIO_DISC_MODE_NO_INFO;
Packit dd8086
Packit dd8086
  /* Read in TOC sheet. */
Packit dd8086
  if ( !parse_tocfile(env, env->psz_cue_name) ) return false;
Packit dd8086
Packit dd8086
  lead_lsn = get_disc_last_lsn_cdrdao( (_img_private_t *) env);
Packit dd8086
Packit dd8086
  if (-1 == lead_lsn)
Packit dd8086
    return false;
Packit dd8086
Packit dd8086
  /* Fake out leadout track and sector count for last track*/
Packit dd8086
  cdio_lsn_to_msf (lead_lsn, &env->tocent[env->gen.i_tracks].start_msf);
Packit dd8086
  env->tocent[env->gen.i_tracks].start_lba = cdio_lsn_to_lba(lead_lsn);
Packit dd8086
  env->tocent[env->gen.i_tracks-env->gen.i_first_track].sec_count =
Packit dd8086
    cdio_lsn_to_lba(lead_lsn - env->tocent[env->gen.i_tracks-1].start_lba);
Packit dd8086
Packit dd8086
  return true;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Reads into buf the next size bytes.
Packit dd8086
  Returns -1 on error.
Packit dd8086
  Would be libc's seek() but we have to adjust for the extra track header
Packit dd8086
  information in each sector.
Packit dd8086
*/
Packit dd8086
static off_t
Packit dd8086
_lseek_cdrdao (void *user_data, off_t offset, int whence)
Packit dd8086
{
Packit dd8086
  _img_private_t *env = user_data;
Packit dd8086
Packit dd8086
  /* real_offset is the real byte offset inside the disk image
Packit dd8086
     The number below was determined empirically. I'm guessing
Packit dd8086
     the 1st 24 bytes of a bin file are used for something.
Packit dd8086
  */
Packit dd8086
  off_t real_offset=0;
Packit dd8086
Packit dd8086
  unsigned int i;
Packit dd8086
Packit dd8086
  env->pos.lba = 0;
Packit dd8086
  for (i=0; i<env->gen.i_tracks; i++) {
Packit dd8086
    track_info_t  *this_track=&(env->tocent[i]);
Packit dd8086
    env->pos.index = i;
Packit dd8086
    if ( (this_track->sec_count*this_track->datasize) >= offset) {
Packit dd8086
      int blocks            = (int) (offset / this_track->datasize);
Packit dd8086
      int rem               = (int) (offset % this_track->datasize);
Packit dd8086
      off_t block_offset    = blocks * this_track->blocksize;
Packit dd8086
      real_offset          += block_offset + rem;
Packit dd8086
      env->pos.buff_offset = rem;
Packit dd8086
      env->pos.lba        += (lba_t)blocks;
Packit dd8086
      break;
Packit dd8086
    }
Packit dd8086
    real_offset   += this_track->sec_count*this_track->blocksize;
Packit dd8086
    offset        -= this_track->sec_count*this_track->datasize;
Packit dd8086
    env->pos.lba += this_track->sec_count;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (i==env->gen.i_tracks) {
Packit dd8086
    cdio_warn ("seeking outside range of disk image");
Packit dd8086
    return -1;
Packit dd8086
  } else {
Packit dd8086
    real_offset += env->tocent[i].datastart;
Packit dd8086
    return cdio_stream_seek(env->tocent[i].data_source, real_offset, whence);
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Reads into buf the next size bytes.
Packit dd8086
  Returns -1 on error.
Packit dd8086
  FIXME:
Packit dd8086
   At present we assume a read doesn't cross sector or track
Packit dd8086
   boundaries.
Packit dd8086
*/
Packit dd8086
static ssize_t
Packit dd8086
_read_cdrdao (void *user_data, void *data, size_t size)
Packit dd8086
{
Packit dd8086
  _img_private_t *env = user_data;
Packit dd8086
  char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
Packit dd8086
  char *p = data;
Packit dd8086
  ssize_t final_size=0;
Packit dd8086
  ssize_t this_size;
Packit dd8086
  track_info_t  *this_track=&(env->tocent[env->pos.index]);
Packit dd8086
  ssize_t skip_size = this_track->datastart + this_track->endsize;
Packit dd8086
Packit dd8086
  while (size > 0) {
Packit dd8086
    int rem = (int) (this_track->datasize - env->pos.buff_offset);
Packit dd8086
    if (size <= rem) {
Packit dd8086
      this_size = cdio_stream_read(this_track->data_source, buf, size, 1);
Packit dd8086
      final_size += this_size;
Packit dd8086
      memcpy (p, buf, this_size);
Packit dd8086
      break;
Packit dd8086
    }
Packit dd8086
Packit dd8086
    /* Finish off reading this sector. */
Packit dd8086
    cdio_warn ("Reading across block boundaries not finished");
Packit dd8086
Packit dd8086
    size -= rem;
Packit dd8086
    this_size = cdio_stream_read(this_track->data_source, buf, rem, 1);
Packit dd8086
    final_size += this_size;
Packit dd8086
    memcpy (p, buf, this_size);
Packit dd8086
    p += this_size;
Packit dd8086
    cdio_stream_read(this_track->data_source, buf, rem, 1);
Packit dd8086
Packit dd8086
    /* Skip over stuff at end of this sector and the beginning of the next.
Packit dd8086
     */
Packit dd8086
    cdio_stream_read(this_track->data_source, buf, skip_size, 1);
Packit dd8086
Packit dd8086
    /* Get ready to read another sector. */
Packit dd8086
    env->pos.buff_offset=0;
Packit dd8086
    env->pos.lba++;
Packit dd8086
Packit dd8086
    /* Have gone into next track. */
Packit dd8086
    if (env->pos.lba >= env->tocent[env->pos.index+1].start_lba) {
Packit dd8086
      env->pos.index++;
Packit dd8086
      this_track=&(env->tocent[env->pos.index]);
Packit dd8086
      skip_size = this_track->datastart + this_track->endsize;
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  return final_size;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
   Return the size of the CD in logical block address (LBA) units.
Packit dd8086
Packit dd8086
   FIXME: this assumes there is only one source for data or
Packit dd8086
   one track of silence.
Packit dd8086
 */
Packit dd8086
static lsn_t
Packit dd8086
get_disc_last_lsn_cdrdao (void *p_user_data)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  track_t i_leadout = p_env->gen.i_tracks;
Packit dd8086
  uint16_t i_blocksize  = p_env->tocent[i_leadout-1].blocksize;
Packit dd8086
  off_t i_size;
Packit dd8086
Packit dd8086
  if (p_env->tocent[i_leadout-1].sec_count) {
Packit dd8086
    i_size = p_env->tocent[i_leadout-1].sec_count;
Packit dd8086
  } else {
Packit dd8086
      if (NULL == p_env->tocent[i_leadout-1].data_source) {
Packit dd8086
	  if (!p_env->tocent[i_leadout-1].silence) {
Packit dd8086
	      cdio_warn ("Data source for image %s is null",
Packit dd8086
			 p_env->gen.source_name);
Packit dd8086
	      return -1;
Packit dd8086
	  }
Packit dd8086
	  /* FIXME: this is only correct if there is one
Packit dd8086
	     track of silence. */
Packit dd8086
	  i_size = p_env->tocent[i_leadout-1].silence;
Packit dd8086
      } else {
Packit dd8086
	  /* FIXME: this is only correct if there is one data source. */
Packit dd8086
	  i_size = cdio_stream_stat(p_env->tocent[i_leadout-1].data_source)
Packit dd8086
	      - p_env->tocent[i_leadout-1].offset;
Packit dd8086
      }
Packit dd8086
    if (i_size < 0) {
Packit dd8086
      cdio_error ("Disc data size too small for track specification in image %s",
Packit dd8086
		  p_env->gen.source_name);
Packit dd8086
      return (lsn_t)i_size;
Packit dd8086
    }
Packit dd8086
    if (check_track_is_blocksize_multiple(p_env->tocent[i_leadout-1].filename,
Packit dd8086
					  i_leadout-1, i_size, i_blocksize)) {
Packit dd8086
      i_size /= i_blocksize;
Packit dd8086
    } else {
Packit dd8086
      /* Round up */
Packit dd8086
      i_size = (i_size / i_blocksize) + 1;
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  i_size += p_env->tocent[i_leadout-1].start_lba;
Packit dd8086
  i_size -= CDIO_PREGAP_SECTORS;
Packit dd8086
Packit dd8086
  return (lsn_t)i_size;
Packit dd8086
}
Packit dd8086
Packit dd8086
#define MAXLINE 512
Packit dd8086
#define UNIMPLIMENTED_MSG \
Packit dd8086
  cdio_log(log_level, "%s line %d: unimplimented keyword: %s",  \
Packit dd8086
	   psz_cue_name, i_line, psz_keyword)
Packit dd8086
Packit dd8086
Packit dd8086
static bool
Packit dd8086
parse_tocfile (_img_private_t *cd, const char *psz_cue_name)
Packit dd8086
{
Packit dd8086
  /* The below declarations may be common in other image-parse routines. */
Packit dd8086
  FILE        *fp;
Packit dd8086
  char         psz_line[MAXLINE];   /* text of current line read in file fp. */
Packit dd8086
  unsigned int i_line=0;            /* line number in file of psz_line. */
Packit dd8086
  int          i = -1;              /* Position in tocent. Same as
Packit dd8086
				       cd->gen.i_tracks - 1 */
Packit dd8086
  char *psz_keyword, *psz_field, *psz_cue_name_dup;
Packit dd8086
  cdio_log_level_t log_level = (cd) ? CDIO_LOG_WARN : CDIO_LOG_INFO ;
Packit dd8086
  cdtext_field_t cdtext_key;
Packit dd8086
Packit dd8086
  /* The below declaration(s) may be unique to this image-parse routine. */
Packit dd8086
  unsigned int i_cdtext_nest = 0;
Packit dd8086
Packit dd8086
  if (NULL == psz_cue_name)
Packit dd8086
    return false;
Packit dd8086
Packit dd8086
  psz_cue_name_dup = _cdio_strdup_fixpath(psz_cue_name);
Packit dd8086
  if (NULL == psz_cue_name_dup)
Packit dd8086
    return false;
Packit dd8086
Packit dd8086
  fp = CDIO_FOPEN (psz_cue_name_dup, "r");
Packit dd8086
  cdio_free(psz_cue_name_dup);
Packit dd8086
  if (fp == NULL) {
Packit dd8086
    cdio_log(log_level, "error opening %s for reading: %s",
Packit dd8086
	     psz_cue_name, strerror(errno));
Packit dd8086
    return false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (cd) {
Packit dd8086
    cd->gen.b_cdtext_error = false;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  while (fgets(psz_line, MAXLINE, fp)) {
Packit dd8086
Packit dd8086
    i_line++;
Packit dd8086
Packit dd8086
    /* strip comment from line */
Packit dd8086
    /* todo: // in quoted strings? */
Packit dd8086
    /* //comment */
Packit dd8086
    if ((psz_field = strstr (psz_line, "//")))
Packit dd8086
      *psz_field = '\0';
Packit dd8086
Packit dd8086
    if ((psz_keyword = strtok (psz_line, " \t\n\r"))) {
Packit dd8086
      /* CATALOG "ddddddddddddd" */
Packit dd8086
      if (0 == strcmp ("CATALOG", psz_keyword)) {
Packit dd8086
	if (-1 == i) {
Packit dd8086
	  if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) {
Packit dd8086
	    if (13 != strlen(psz_field)) {
Packit dd8086
	      cdio_log(log_level,
Packit dd8086
		       "%s line %d after word CATALOG:",
Packit dd8086
		       psz_cue_name, i_line);
Packit dd8086
	      cdio_log(log_level,
Packit dd8086
		       "Token %s has length %ld. Should be 13 digits.",
Packit dd8086
		       psz_field, (long int) strlen(psz_field));
Packit dd8086
Packit dd8086
	      goto err_exit;
Packit dd8086
	    } else {
Packit dd8086
	      /* Check that we have all digits*/
Packit dd8086
	      unsigned int j;
Packit dd8086
	      for (j=0; j<13; j++) {
Packit dd8086
		if (!isdigit((unsigned char) psz_field[j])) {
Packit dd8086
		    cdio_log(log_level,
Packit dd8086
			     "%s line %d after word CATALOG:",
Packit dd8086
			     psz_cue_name, i_line);
Packit dd8086
		    cdio_log(log_level,
Packit dd8086
			     "Character \"%c\" at postition %i of token \"%s\""
Packit dd8086
			     " is not all digits.",
Packit dd8086
			     psz_field[j], j+1, psz_field);
Packit dd8086
		    goto err_exit;
Packit dd8086
		}
Packit dd8086
	      }
Packit dd8086
	      if (NULL != cd) cd->psz_mcn = strdup (psz_field);
Packit dd8086
	    }
Packit dd8086
	  } else {
Packit dd8086
	    cdio_log(log_level,
Packit dd8086
		     "%s line %d after word CATALOG:",
Packit dd8086
		     psz_cue_name, i_line);
Packit dd8086
	    cdio_log(log_level, "Expecting 13 digits; nothing seen.");
Packit dd8086
	    goto err_exit;
Packit dd8086
	  }
Packit dd8086
	} else {
Packit dd8086
	  goto err_exit;
Packit dd8086
	}
Packit dd8086
Packit dd8086
	/* CD_DA | CD_ROM | CD_ROM_XA */
Packit dd8086
      } else if (0 == strcmp ("CD_DA", psz_keyword)) {
Packit dd8086
	if (-1 == i) {
Packit dd8086
	  if (NULL != cd)
Packit dd8086
	    cd->disc_mode = CDIO_DISC_MODE_CD_DA;
Packit dd8086
	} else {
Packit dd8086
	  goto not_in_global_section;
Packit dd8086
	}
Packit dd8086
      } else if (0 == strcmp ("CD_ROM", psz_keyword)) {
Packit dd8086
	if (-1 == i) {
Packit dd8086
	  if (NULL != cd)
Packit dd8086
	    cd->disc_mode = CDIO_DISC_MODE_CD_DATA;
Packit dd8086
	} else {
Packit dd8086
	  goto not_in_global_section;
Packit dd8086
	}
Packit dd8086
Packit dd8086
      } else if (0 == strcmp ("CD_ROM_XA", psz_keyword)) {
Packit dd8086
	if (-1 == i) {
Packit dd8086
	  if (NULL != cd)
Packit dd8086
	    cd->disc_mode = CDIO_DISC_MODE_CD_XA;
Packit dd8086
	} else {
Packit dd8086
	  goto not_in_global_section;
Packit dd8086
	}
Packit dd8086
Packit dd8086
	/* TRACK <track-mode> [<sub-channel-mode>] */
Packit dd8086
      } else if (0 == strcmp ("TRACK", psz_keyword)) {
Packit dd8086
	i++;
Packit dd8086
	if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	  if (0 == strcmp ("AUDIO", psz_field)) {
Packit dd8086
	    if (NULL != cd) {
Packit dd8086
	      cd->tocent[i].track_format = TRACK_FORMAT_AUDIO;
Packit dd8086
	      cd->tocent[i].blocksize    = CDIO_CD_FRAMESIZE_RAW;
Packit dd8086
	      cd->tocent[i].datasize     = CDIO_CD_FRAMESIZE_RAW;
Packit dd8086
	      cd->tocent[i].datastart    = 0;
Packit dd8086
	      cd->tocent[i].endsize      = 0;
Packit dd8086
	      switch(cd->disc_mode) {
Packit dd8086
	      case CDIO_DISC_MODE_NO_INFO:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_DA;
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_DA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_MIXED:
Packit dd8086
	      case CDIO_DISC_MODE_ERROR:
Packit dd8086
		/* Disc type stays the same. */
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_DATA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_XA:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
Packit dd8086
		break;
Packit dd8086
	      default:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_ERROR;
Packit dd8086
	      }
Packit dd8086
Packit dd8086
	    }
Packit dd8086
	  } else if (0 == strcmp ("MODE1", psz_field)) {
Packit dd8086
	    if (NULL != cd) {
Packit dd8086
	      cd->tocent[i].track_format = TRACK_FORMAT_DATA;
Packit dd8086
	      cd->tocent[i].blocksize    = CDIO_CD_FRAMESIZE_RAW;
Packit dd8086
	      cd->tocent[i].datastart    = CDIO_CD_SYNC_SIZE
Packit dd8086
		+ CDIO_CD_HEADER_SIZE;
Packit dd8086
	      cd->tocent[i].datasize     = CDIO_CD_FRAMESIZE;
Packit dd8086
	      cd->tocent[i].endsize      = CDIO_CD_EDC_SIZE
Packit dd8086
		+ CDIO_CD_M1F1_ZERO_SIZE + CDIO_CD_ECC_SIZE;
Packit dd8086
	      switch(cd->disc_mode) {
Packit dd8086
	      case CDIO_DISC_MODE_NO_INFO:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_DATA;
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_DATA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_MIXED:
Packit dd8086
	      case CDIO_DISC_MODE_ERROR:
Packit dd8086
		/* Disc type stays the same. */
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_DA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_XA:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
Packit dd8086
		break;
Packit dd8086
	      default:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_ERROR;
Packit dd8086
	      }
Packit dd8086
	    }
Packit dd8086
	  } else if (0 == strcmp ("MODE1_RAW", psz_field)) {
Packit dd8086
	    if (NULL != cd) {
Packit dd8086
	      cd->tocent[i].track_format = TRACK_FORMAT_DATA;
Packit dd8086
	      cd->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW;
Packit dd8086
	      cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE
Packit dd8086
		+ CDIO_CD_HEADER_SIZE;
Packit dd8086
	      cd->tocent[i].datasize  = CDIO_CD_FRAMESIZE;
Packit dd8086
	      cd->tocent[i].endsize   = CDIO_CD_EDC_SIZE
Packit dd8086
		+ CDIO_CD_M1F1_ZERO_SIZE + CDIO_CD_ECC_SIZE;
Packit dd8086
	      switch(cd->disc_mode) {
Packit dd8086
	      case CDIO_DISC_MODE_NO_INFO:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_DATA;
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_DATA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_MIXED:
Packit dd8086
	      case CDIO_DISC_MODE_ERROR:
Packit dd8086
		/* Disc type stays the same. */
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_DA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_XA:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
Packit dd8086
		break;
Packit dd8086
	      default:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_ERROR;
Packit dd8086
	      }
Packit dd8086
	    }
Packit dd8086
	  } else if (0 == strcmp ("MODE2", psz_field)) {
Packit dd8086
	    if (NULL != cd) {
Packit dd8086
	      cd->tocent[i].track_format = TRACK_FORMAT_XA;
Packit dd8086
	      cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE
Packit dd8086
		+ CDIO_CD_HEADER_SIZE;
Packit dd8086
	      cd->tocent[i].datasize = M2RAW_SECTOR_SIZE;
Packit dd8086
	      cd->tocent[i].endsize   = 0;
Packit dd8086
	      switch(cd->disc_mode) {
Packit dd8086
	      case CDIO_DISC_MODE_NO_INFO:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_XA;
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_XA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_MIXED:
Packit dd8086
	      case CDIO_DISC_MODE_ERROR:
Packit dd8086
		/* Disc type stays the same. */
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_DA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_DATA:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
Packit dd8086
		break;
Packit dd8086
	      default:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_ERROR;
Packit dd8086
	      }
Packit dd8086
	    }
Packit dd8086
	  } else if (0 == strcmp ("MODE2_FORM1", psz_field)) {
Packit dd8086
	    if (NULL != cd) {
Packit dd8086
	      cd->tocent[i].track_format = TRACK_FORMAT_XA;
Packit dd8086
	      cd->tocent[i].datastart = CDIO_CD_SYNC_SIZE
Packit dd8086
		+ CDIO_CD_HEADER_SIZE;
Packit dd8086
	      cd->tocent[i].datasize  = CDIO_CD_FRAMESIZE_RAW;
Packit dd8086
	      cd->tocent[i].endsize   = 0;
Packit dd8086
	      switch(cd->disc_mode) {
Packit dd8086
	      case CDIO_DISC_MODE_NO_INFO:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_XA;
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_XA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_MIXED:
Packit dd8086
	      case CDIO_DISC_MODE_ERROR:
Packit dd8086
		/* Disc type stays the same. */
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_DA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_DATA:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
Packit dd8086
		break;
Packit dd8086
	      default:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_ERROR;
Packit dd8086
	      }
Packit dd8086
	    }
Packit dd8086
	  } else if (0 == strcmp ("MODE2_FORM2", psz_field)) {
Packit dd8086
	    if (NULL != cd) {
Packit dd8086
	      cd->tocent[i].track_format = TRACK_FORMAT_XA;
Packit dd8086
	      cd->tocent[i].datastart    = CDIO_CD_SYNC_SIZE
Packit dd8086
		+ CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE;
Packit dd8086
	      cd->tocent[i].datasize     = CDIO_CD_FRAMESIZE;
Packit dd8086
	      cd->tocent[i].endsize      = CDIO_CD_SYNC_SIZE
Packit dd8086
		+ CDIO_CD_ECC_SIZE;
Packit dd8086
	      switch(cd->disc_mode) {
Packit dd8086
	      case CDIO_DISC_MODE_NO_INFO:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_XA;
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_XA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_MIXED:
Packit dd8086
	      case CDIO_DISC_MODE_ERROR:
Packit dd8086
		/* Disc type stays the same. */
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_DA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_DATA:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
Packit dd8086
		break;
Packit dd8086
	      default:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_ERROR;
Packit dd8086
	      }
Packit dd8086
	    }
Packit dd8086
	  } else if (0 == strcmp ("MODE2_FORM_MIX", psz_field)) {
Packit dd8086
	    if (NULL != cd) {
Packit dd8086
	      cd->tocent[i].track_format = TRACK_FORMAT_XA;
Packit dd8086
	      cd->tocent[i].datasize     = M2RAW_SECTOR_SIZE;
Packit dd8086
	      cd->tocent[i].blocksize    = CDIO_CD_FRAMESIZE_RAW;
Packit dd8086
	      cd->tocent[i].datastart    = CDIO_CD_SYNC_SIZE +
Packit dd8086
		CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE;
Packit dd8086
	      cd->tocent[i].track_green  = true;
Packit dd8086
	      cd->tocent[i].endsize      = 0;
Packit dd8086
	      switch(cd->disc_mode) {
Packit dd8086
	      case CDIO_DISC_MODE_NO_INFO:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_XA;
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_XA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_MIXED:
Packit dd8086
	      case CDIO_DISC_MODE_ERROR:
Packit dd8086
		/* Disc type stays the same. */
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_DA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_DATA:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
Packit dd8086
		break;
Packit dd8086
	      default:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_ERROR;
Packit dd8086
	      }
Packit dd8086
	    }
Packit dd8086
	  } else if (0 == strcmp ("MODE2_RAW", psz_field)) {
Packit dd8086
	    if (NULL != cd) {
Packit dd8086
	      cd->tocent[i].track_format = TRACK_FORMAT_XA;
Packit dd8086
	      cd->tocent[i].blocksize    = CDIO_CD_FRAMESIZE_RAW;
Packit dd8086
	      cd->tocent[i].datastart    = CDIO_CD_SYNC_SIZE +
Packit dd8086
		CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE;
Packit dd8086
	      cd->tocent[i].datasize     = CDIO_CD_FRAMESIZE;
Packit dd8086
	      cd->tocent[i].track_green  = true;
Packit dd8086
	      cd->tocent[i].endsize      = 0;
Packit dd8086
	      switch(cd->disc_mode) {
Packit dd8086
	      case CDIO_DISC_MODE_NO_INFO:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_XA;
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_XA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_MIXED:
Packit dd8086
	      case CDIO_DISC_MODE_ERROR:
Packit dd8086
		/* Disc type stays the same. */
Packit dd8086
		break;
Packit dd8086
	      case CDIO_DISC_MODE_CD_DA:
Packit dd8086
	      case CDIO_DISC_MODE_CD_DATA:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
Packit dd8086
		break;
Packit dd8086
	      default:
Packit dd8086
		cd->disc_mode = CDIO_DISC_MODE_ERROR;
Packit dd8086
	      }
Packit dd8086
	    }
Packit dd8086
	  } else {
Packit dd8086
	    cdio_log(log_level, "%s line %d after TRACK:",
Packit dd8086
		     psz_cue_name, i_line);
Packit dd8086
	    cdio_log(log_level, "'%s' not a valid mode.", psz_field);
Packit dd8086
	    goto err_exit;
Packit dd8086
	  }
Packit dd8086
	}
Packit dd8086
	if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	  /* \todo: set sub-channel-mode */
Packit dd8086
#ifdef TODO
Packit dd8086
	  if (0 == strcmp ("RW", psz_field))
Packit dd8086
	    ;
Packit dd8086
	  else if (0 == strcmp ("RW_RAW", psz_field))
Packit dd8086
	    ;
Packit dd8086
#endif
Packit dd8086
	}
Packit dd8086
	if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	  goto format_error;
Packit dd8086
	}
Packit dd8086
Packit dd8086
	/* track flags */
Packit dd8086
	/* [NO] COPY | [NO] PRE_EMPHASIS */
Packit dd8086
      } else if (0 == strcmp ("NO", psz_keyword)) {
Packit dd8086
	if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	  if (0 == strcmp ("COPY", psz_field)) {
Packit dd8086
	    if (NULL != cd)
Packit dd8086
	      cd->tocent[i].flags &= ~CDIO_TRACK_FLAG_COPY_PERMITTED;
Packit dd8086
Packit dd8086
	  } else if (0 == strcmp ("PRE_EMPHASIS", psz_field))
Packit dd8086
	    if (NULL != cd) {
Packit dd8086
	      cd->tocent[i].flags &= ~CDIO_TRACK_FLAG_PRE_EMPHASIS;
Packit dd8086
	    }
Packit dd8086
	} else {
Packit dd8086
	  goto format_error;
Packit dd8086
	}
Packit dd8086
	if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	  goto format_error;
Packit dd8086
	}
Packit dd8086
      } else if (0 == strcmp ("COPY", psz_keyword)) {
Packit dd8086
	if (NULL != cd && i >= 0)
Packit dd8086
	  cd->tocent[i].flags |= CDIO_TRACK_FLAG_COPY_PERMITTED;
Packit dd8086
      } else if (0 == strcmp ("PRE_EMPHASIS", psz_keyword)) {
Packit dd8086
	if (NULL != cd && i >= 0)
Packit dd8086
	  cd->tocent[i].flags |= CDIO_TRACK_FLAG_PRE_EMPHASIS;
Packit dd8086
	/* TWO_CHANNEL_AUDIO */
Packit dd8086
      } else if (0 == strcmp ("TWO_CHANNEL_AUDIO", psz_keyword)) {
Packit dd8086
	if (NULL != cd && i >= 0)
Packit dd8086
	  cd->tocent[i].flags &= ~CDIO_TRACK_FLAG_FOUR_CHANNEL_AUDIO;
Packit dd8086
	/* FOUR_CHANNEL_AUDIO */
Packit dd8086
      } else if (0 == strcmp ("FOUR_CHANNEL_AUDIO", psz_keyword)) {
Packit dd8086
	if (NULL != cd && i >= 0)
Packit dd8086
	  cd->tocent[i].flags |= CDIO_TRACK_FLAG_FOUR_CHANNEL_AUDIO;
Packit dd8086
Packit dd8086
	/* ISRC "CCOOOYYSSSSS" */
Packit dd8086
      } else if (0 == strcmp ("ISRC", psz_keyword)) {
Packit dd8086
	if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) {
Packit dd8086
	  if (NULL != cd)
Packit dd8086
	    cd->tocent[i].isrc = strdup(psz_field);
Packit dd8086
	} else {
Packit dd8086
	  goto format_error;
Packit dd8086
	}
Packit dd8086
Packit dd8086
	/* SILENCE <length> */
Packit dd8086
      } else if (0 == strcmp ("SILENCE", psz_keyword)) {
Packit dd8086
	  if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	      if (NULL != cd)
Packit dd8086
		  cd->tocent[i].silence = cdio_mmssff_to_lba (psz_field);
Packit dd8086
	  } else {
Packit dd8086
	      goto format_error;
Packit dd8086
	  }
Packit dd8086
	  cdio_log(log_level, "%s line %d: SILENCE not fully implimented",
Packit dd8086
		   psz_cue_name, i_line);
Packit dd8086
Packit dd8086
	/* ZERO <length> */
Packit dd8086
      } else if (0 == strcmp ("ZERO", psz_keyword)) {
Packit dd8086
	UNIMPLIMENTED_MSG;
Packit dd8086
Packit dd8086
	/* [FILE|AUDIOFILE] "<filename>" <start-msf> [<length-msf>] */
Packit dd8086
      } else if (0 == strcmp ("FILE", psz_keyword)
Packit dd8086
		 || 0 == strcmp ("AUDIOFILE", psz_keyword)) {
Packit dd8086
	if (0 <= i) {
Packit dd8086
	  if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) {
Packit dd8086
	    /* Handle "<filename>" */
Packit dd8086
	    if (cd) {
Packit dd8086
	      char *psz_dirname = cdio_dirname(psz_cue_name);
Packit dd8086
	      char *psz_filename = cdio_abspath(psz_dirname, psz_field);
Packit dd8086
	      cd->tocent[i].filename = strdup (psz_filename);
Packit dd8086
	      free(psz_filename);
Packit dd8086
	      free(psz_dirname);
Packit dd8086
	      /* To do: do something about reusing existing files. */
Packit dd8086
	      if (!(cd->tocent[i].data_source = cdio_stdio_new (psz_field))) {
Packit dd8086
		cdio_log (log_level,
Packit dd8086
			  "%s line %d: can't open file `%s' for reading",
Packit dd8086
			   psz_cue_name, i_line, psz_field);
Packit dd8086
		goto err_exit;
Packit dd8086
	      }
Packit dd8086
	    } else {
Packit dd8086
	      CdioDataSource_t *s = cdio_stdio_new (psz_field);
Packit dd8086
	      if (!s) {
Packit dd8086
		cdio_log (log_level,
Packit dd8086
			  "%s line %d: can't open file `%s' for reading",
Packit dd8086
			  psz_cue_name, i_line, psz_field);
Packit dd8086
		cdio_stdio_destroy (s);
Packit dd8086
		goto err_exit;
Packit dd8086
	      }
Packit dd8086
	      cdio_stdio_destroy (s);
Packit dd8086
	    }
Packit dd8086
	  }
Packit dd8086
Packit dd8086
	  if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	    /* Handle <start-msf> */
Packit dd8086
	    lba_t i_start_lba =
Packit dd8086
	      cdio_lsn_to_lba(cdio_mmssff_to_lba (psz_field));
Packit dd8086
	    if (CDIO_INVALID_LBA == i_start_lba) {
Packit dd8086
	      cdio_log(log_level, "%s line %d: invalid MSF string %s",
Packit dd8086
		       psz_cue_name, i_line, psz_field);
Packit dd8086
	      goto err_exit;
Packit dd8086
	    }
Packit dd8086
Packit dd8086
	    if (NULL != cd) {
Packit dd8086
	      cd->tocent[i].start_lba = i_start_lba;
Packit dd8086
	      cdio_lba_to_msf(i_start_lba, &(cd->tocent[i].start_msf));
Packit dd8086
	    }
Packit dd8086
	  }
Packit dd8086
	  if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	    /* Handle <length-msf> */
Packit dd8086
	    lba_t lba = cdio_mmssff_to_lba (psz_field);
Packit dd8086
	    if (CDIO_INVALID_LBA == lba) {
Packit dd8086
	      cdio_log(log_level, "%s line %d: invalid MSF string %s",
Packit dd8086
		       psz_cue_name, i_line, psz_field);
Packit dd8086
	      goto err_exit;
Packit dd8086
	    }
Packit dd8086
	    if (cd) {
Packit dd8086
	      off_t i_size = cdio_stream_stat(cd->tocent[i].data_source);
Packit dd8086
	      if (lba) {
Packit dd8086
		if ( (lba * cd->tocent[i].datasize) > i_size) {
Packit dd8086
		  cdio_log(log_level,
Packit dd8086
			   "%s line %d: MSF length %s exceeds end of file",
Packit dd8086
			   psz_cue_name, i_line, psz_field);
Packit dd8086
		  goto err_exit;
Packit dd8086
		}
Packit dd8086
	      } else {
Packit dd8086
		lba = (lba_t) (i_size / cd->tocent[i].blocksize);
Packit dd8086
	      }
Packit dd8086
	      cd->tocent[i].sec_count = lba;
Packit dd8086
	    }
Packit dd8086
	  }
Packit dd8086
	  if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	    goto format_error;
Packit dd8086
	  }
Packit dd8086
	} else {
Packit dd8086
	  goto not_in_global_section;
Packit dd8086
	}
Packit dd8086
Packit dd8086
	/* DATAFILE "<filename>" #byte-offset <start-msf> */
Packit dd8086
      } else if (0 == strcmp ("DATAFILE", psz_keyword)) {
Packit dd8086
	if (0 <= i) {
Packit dd8086
	  if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) {
Packit dd8086
	    /* Handle <filename> */
Packit dd8086
	    char *psz_dirname = cdio_dirname(psz_cue_name);
Packit dd8086
	    char *psz_filename = cdio_abspath(psz_dirname, psz_field);
Packit dd8086
	    if (cd) {
Packit dd8086
	      cd->tocent[i].filename = strdup(psz_filename);
Packit dd8086
	      /* To do: do something about reusing existing files. */
Packit dd8086
	      if (!(cd->tocent[i].data_source = cdio_stdio_new (psz_field))) {
Packit dd8086
		cdio_log (log_level,
Packit dd8086
			  "%s line %d: can't open file `%s' for reading",
Packit dd8086
			  psz_cue_name, i_line, psz_field);
Packit dd8086
		free(psz_filename);
Packit dd8086
		free(psz_dirname);
Packit dd8086
		goto err_exit;
Packit dd8086
	      }
Packit dd8086
	    } else {
Packit dd8086
	      CdioDataSource_t *s = cdio_stdio_new (psz_filename);
Packit dd8086
	      if (!s) {
Packit dd8086
		cdio_log (log_level,
Packit dd8086
			  "%s line %d: can't open file `%s' for reading",
Packit dd8086
			  psz_cue_name, i_line, psz_field);
Packit dd8086
		free(psz_filename);
Packit dd8086
		free(psz_dirname);
Packit dd8086
		goto err_exit;
Packit dd8086
	      }
Packit dd8086
	      cdio_stdio_destroy (s);
Packit dd8086
	    }
Packit dd8086
	    free(psz_filename);
Packit dd8086
	    free(psz_dirname);
Packit dd8086
	  }
Packit dd8086
Packit dd8086
	  psz_field = strtok (NULL, " \t\n\r");
Packit dd8086
	  if (psz_field) {
Packit dd8086
	    /* Handle optional #byte-offset */
Packit dd8086
	    if ( psz_field[0] == '#') {
Packit dd8086
	      long int offset;
Packit dd8086
	      psz_field++;
Packit dd8086
	      errno = 0;
Packit dd8086
	      offset = strtol(psz_field, (char **)NULL, 10);
Packit dd8086
	      if ( (LONG_MIN == offset || LONG_MAX == offset)
Packit dd8086
		   && 0 != errno ) {
Packit dd8086
		cdio_log (log_level,
Packit dd8086
			  "%s line %d: can't convert `%s' to byte offset",
Packit dd8086
			  psz_cue_name, i_line, psz_field);
Packit dd8086
		goto err_exit;
Packit dd8086
	      } else {
Packit dd8086
		if (NULL != cd) {
Packit dd8086
		  cd->tocent[i].offset = offset;
Packit dd8086
		}
Packit dd8086
	      }
Packit dd8086
	      psz_field = strtok (NULL, " \t\n\r");
Packit dd8086
	    }
Packit dd8086
	  }
Packit dd8086
	  if (psz_field) {
Packit dd8086
	    /* Handle start-msf */
Packit dd8086
	    lba_t lba = cdio_mmssff_to_lba (psz_field);
Packit dd8086
	    if (CDIO_INVALID_LBA == lba) {
Packit dd8086
	      cdio_log(log_level, "%s line %d: invalid MSF string %s",
Packit dd8086
		       psz_cue_name, i_line, psz_field);
Packit dd8086
	      goto err_exit;
Packit dd8086
	    }
Packit dd8086
	    if (cd) {
Packit dd8086
	      cd->tocent[i].start_lba = lba;
Packit dd8086
	      cdio_lba_to_msf(cd->tocent[i].start_lba,
Packit dd8086
			      &(cd->tocent[i].start_msf));
Packit dd8086
	    }
Packit dd8086
	  } else {
Packit dd8086
	    /* No start-msf. */
Packit dd8086
	    if (cd) {
Packit dd8086
	      if (i) {
Packit dd8086
		uint16_t i_blocksize = cd->tocent[i-1].blocksize;
Packit dd8086
		off_t i_size      =
Packit dd8086
		  cdio_stream_stat(cd->tocent[i-1].data_source);
Packit dd8086
Packit dd8086
		  check_track_is_blocksize_multiple(cd->tocent[i-1].filename,
Packit dd8086
						    i-1, i_size, i_blocksize);
Packit dd8086
		/* Append size of previous datafile. */
Packit dd8086
		cd->tocent[i].start_lba = (lba_t) (cd->tocent[i-1].start_lba +
Packit dd8086
		  (i_size / i_blocksize));
Packit dd8086
	      }
Packit dd8086
	      cd->tocent[i].offset = 0;
Packit dd8086
	      cd->tocent[i].start_lba += CDIO_PREGAP_SECTORS;
Packit dd8086
	      cdio_lba_to_msf(cd->tocent[i].start_lba,
Packit dd8086
			      &(cd->tocent[i].start_msf));
Packit dd8086
	    }
Packit dd8086
	  }
Packit dd8086
Packit dd8086
	} else {
Packit dd8086
	  goto not_in_global_section;
Packit dd8086
	}
Packit dd8086
Packit dd8086
	/* FIFO "<fifo path>" [<length>] */
Packit dd8086
      } else if (0 == strcmp ("FIFO", psz_keyword)) {
Packit dd8086
	goto unimplimented_error;
Packit dd8086
Packit dd8086
	/* START MM:SS:FF */
Packit dd8086
      } else if (0 == strcmp ("START", psz_keyword)) {
Packit dd8086
	if (0 <= i) {
Packit dd8086
	  if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	    /* todo: line is too long! */
Packit dd8086
	    if (NULL != cd) {
Packit dd8086
	      cd->tocent[i].pregap = cd->tocent[i].start_lba;
Packit dd8086
	      cd->tocent[i].start_lba += cdio_mmssff_to_lba (psz_field);
Packit dd8086
	      cdio_lba_to_msf(cd->tocent[i].start_lba,
Packit dd8086
			      &(cd->tocent[i].start_msf));
Packit dd8086
	    }
Packit dd8086
	  }
Packit dd8086
Packit dd8086
	  if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	    goto format_error;
Packit dd8086
	  }
Packit dd8086
	} else {
Packit dd8086
	  goto not_in_global_section;
Packit dd8086
	}
Packit dd8086
Packit dd8086
	/* PREGAP MM:SS:FF */
Packit dd8086
      } else if (0 == strcmp ("PREGAP", psz_keyword)) {
Packit dd8086
	if (0 <= i) {
Packit dd8086
	  if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	    if (NULL != cd)
Packit dd8086
	      cd->tocent[i].pregap = cdio_mmssff_to_lba (psz_field);
Packit dd8086
	  } else {
Packit dd8086
	    goto format_error;
Packit dd8086
	  }
Packit dd8086
	  if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	    goto format_error;
Packit dd8086
	  }
Packit dd8086
	} else {
Packit dd8086
	  goto not_in_global_section;
Packit dd8086
	}
Packit dd8086
Packit dd8086
	  /* INDEX MM:SS:FF */
Packit dd8086
      } else if (0 == strcmp ("INDEX", psz_keyword)) {
Packit dd8086
	if (0 <= i) {
Packit dd8086
	  if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
	    if (NULL != cd) {
Packit dd8086
#if 0
Packit dd8086
	      if (1 == cd->tocent[i].nindex) {
Packit dd8086
		cd->tocent[i].indexes[1] = cd->tocent[i].indexes[0];
Packit dd8086
		cd->tocent[i].nindex++;
Packit dd8086
	      }
Packit dd8086
	      cd->tocent[i].indexes[cd->tocent[i].nindex++] =
Packit dd8086
		cdio_mmssff_to_lba (psz_field) + cd->tocent[i].indexes[0];
Packit dd8086
#else
Packit dd8086
	      ;
Packit dd8086
Packit dd8086
#endif
Packit dd8086
	    }
Packit dd8086
	  } else {
Packit dd8086
	    goto format_error;
Packit dd8086
	  }
Packit dd8086
	  if (NULL != strtok (NULL, " \t\n\r")) {
Packit dd8086
	    goto format_error;
Packit dd8086
	  }
Packit dd8086
	}  else {
Packit dd8086
	  goto not_in_global_section;
Packit dd8086
	}
Packit dd8086
Packit dd8086
  /* CD_TEXT { ... } */
Packit dd8086
  /* todo: opening { must be on same line as CD_TEXT */
Packit dd8086
      } else if (0 == strcmp ("CD_TEXT", psz_keyword)) {
Packit dd8086
        if (NULL == (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
          goto format_error;
Packit dd8086
        }
Packit dd8086
        if ( 0 == strcmp( "{", psz_field ) ) {
Packit dd8086
          i_cdtext_nest++;
Packit dd8086
        } else {
Packit dd8086
          cdio_log (log_level,
Packit dd8086
              "%s line %d: expecting '{'", psz_cue_name, i_line);
Packit dd8086
          goto err_exit;
Packit dd8086
        }
Packit dd8086
Packit dd8086
      // TODO: implement language mapping
Packit dd8086
      } else if (0 == strcmp ("LANGUAGE_MAP", psz_keyword)) {
Packit dd8086
        /* LANGUAGE d { ... } */
Packit dd8086
      } else if (0 == strcmp ("LANGUAGE", psz_keyword)) {
Packit dd8086
        /* Language number */
Packit dd8086
        if (NULL == (psz_field = strtok (NULL, " \t\n\r"))) {
Packit dd8086
          goto format_error;
Packit dd8086
        }
Packit dd8086
        if ( 0 == strcmp( "{", psz_field ) ) {
Packit dd8086
          i_cdtext_nest++;
Packit dd8086
        }
Packit dd8086
      } else if (0 == strcmp ("{", psz_keyword)) {
Packit dd8086
        i_cdtext_nest++;
Packit dd8086
      } else if (0 == strcmp ("}", psz_keyword)) {
Packit dd8086
        if (i_cdtext_nest > 0) i_cdtext_nest--;
Packit dd8086
      } else if ( CDTEXT_FIELD_INVALID !=
Packit dd8086
          (cdtext_key = cdtext_is_field (psz_keyword)) ) {
Packit dd8086
        if (NULL != cd) {
Packit dd8086
          if (NULL == cd->gen.cdtext) {
Packit dd8086
            cd->gen.cdtext = cdtext_init ();
Packit dd8086
            /* until language mapping is implemented ...*/
Packit dd8086
            cd->gen.cdtext->block[cd->gen.cdtext->block_i].language_code = CDTEXT_LANGUAGE_ENGLISH;
Packit dd8086
          }
Packit dd8086
          cdtext_set (cd->gen.cdtext, cdtext_key, (uint8_t*) strtok (NULL, "\"\t\n\r"),
Packit dd8086
              (-1 == i ? 0 : cd->gen.i_first_track + i),
Packit dd8086
              "ISO-8859-1");
Packit dd8086
        }
Packit dd8086
Packit dd8086
	/* unrecognized line */
Packit dd8086
      } else {
Packit dd8086
	cdio_log(log_level, "%s line %d: warning: unrecognized word: %s",
Packit dd8086
		 psz_cue_name, i_line, psz_keyword);
Packit dd8086
	goto err_exit;
Packit dd8086
      }
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (NULL != cd) {
Packit dd8086
    cd->gen.i_tracks = i+1;
Packit dd8086
    cd->gen.toc_init = true;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  fclose (fp);
Packit dd8086
  return true;
Packit dd8086
Packit dd8086
 unimplimented_error:
Packit dd8086
  UNIMPLIMENTED_MSG;
Packit dd8086
  goto err_exit;
Packit dd8086
Packit dd8086
 format_error:
Packit dd8086
  cdio_log(log_level, "%s line %d after word %s",
Packit dd8086
	   psz_cue_name, i_line, psz_keyword);
Packit dd8086
  goto err_exit;
Packit dd8086
Packit dd8086
 not_in_global_section:
Packit dd8086
  cdio_log(log_level, "%s line %d: word %s only allowed in global section",
Packit dd8086
	   psz_cue_name, i_line, psz_keyword);
Packit dd8086
Packit dd8086
 err_exit:
Packit dd8086
  fclose (fp);
Packit dd8086
  return false;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
   Reads a single audio sector from CD device into data starting
Packit dd8086
   from lsn. Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
_read_audio_sectors_cdrdao (void *user_data, void *data, lsn_t lsn,
Packit dd8086
			  unsigned int nblocks)
Packit dd8086
{
Packit dd8086
  _img_private_t *env = user_data;
Packit dd8086
  int ret;
Packit dd8086
Packit dd8086
  ret = cdio_stream_seek (env->tocent[0].data_source,
Packit dd8086
            lsn * CDIO_CD_FRAMESIZE_RAW, SEEK_SET);
Packit dd8086
  if (ret!=0) return ret;
Packit dd8086
Packit dd8086
  ret = cdio_stream_read (env->tocent[0].data_source, data,
Packit dd8086
            CDIO_CD_FRAMESIZE_RAW, nblocks);
Packit dd8086
Packit dd8086
  /* ret is number of bytes if okay, but we need to return 0 okay. */
Packit dd8086
  return ret == 0;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
   Reads a single mode2 sector from cd device into data starting
Packit dd8086
   from lsn. Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
_read_mode1_sector_cdrdao (void *user_data, void *data, lsn_t lsn,
Packit dd8086
			 bool b_form2)
Packit dd8086
{
Packit dd8086
  _img_private_t *env = user_data;
Packit dd8086
  int ret;
Packit dd8086
  char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
Packit dd8086
Packit dd8086
  ret = cdio_stream_seek (env->tocent[0].data_source,
Packit dd8086
			  lsn * CDIO_CD_FRAMESIZE_RAW, SEEK_SET);
Packit dd8086
  if (ret!=0) return ret;
Packit dd8086
Packit dd8086
  /* FIXME: Not completely sure the below is correct. */
Packit dd8086
  ret = cdio_stream_read (env->tocent[0].data_source, buf,
Packit dd8086
			  CDIO_CD_FRAMESIZE_RAW, 1);
Packit dd8086
  if (ret==0) return ret;
Packit dd8086
Packit dd8086
  memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE,
Packit dd8086
	  b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE);
Packit dd8086
Packit dd8086
  return DRIVER_OP_SUCCESS;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
   Reads nblocks of mode1 sectors from cd device into data starting
Packit dd8086
   from lsn.
Packit dd8086
   Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static int
Packit dd8086
_read_mode1_sectors_cdrdao (void *user_data, void *data, lsn_t lsn,
Packit dd8086
			    bool b_form2, unsigned int nblocks)
Packit dd8086
{
Packit dd8086
  _img_private_t *env = user_data;
Packit dd8086
  int i;
Packit dd8086
  int retval;
Packit dd8086
  unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE;
Packit dd8086
Packit dd8086
  for (i = 0; i < nblocks; i++) {
Packit dd8086
    if ( (retval = _read_mode1_sector_cdrdao (env,
Packit dd8086
					    ((char *)data) + (blocksize * i),
Packit dd8086
					    lsn + i, b_form2)) )
Packit dd8086
      return retval;
Packit dd8086
  }
Packit dd8086
  return DRIVER_OP_SUCCESS;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
   Reads a single mode1 sector from cd device into data starting
Packit dd8086
   from lsn. Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
_read_mode2_sector_cdrdao (void *user_data, void *data, lsn_t lsn,
Packit dd8086
			 bool b_form2)
Packit dd8086
{
Packit dd8086
  _img_private_t *env = user_data;
Packit dd8086
  int ret;
Packit dd8086
  char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
Packit dd8086
  long unsigned int i_off = lsn * CDIO_CD_FRAMESIZE_RAW;
Packit dd8086
Packit dd8086
  /* For sms's VCD's (mwc1.toc) it is more like this:
Packit dd8086
     if (i_off > 272) i_off -= 272;
Packit dd8086
     There is that magic 272 that we find in read_audio_sectors_cdrdao again.
Packit dd8086
  */
Packit dd8086
Packit dd8086
  /* NOTE: The logic below seems a bit wrong and convoluted
Packit dd8086
     to me, but passes the regression tests. (Perhaps it is why we get
Packit dd8086
     valgrind errors in vcdxrip). Leave it the way it was for now.
Packit dd8086
     Review this sector 2336 stuff later.
Packit dd8086
  */
Packit dd8086
Packit dd8086
  ret = cdio_stream_seek (env->tocent[0].data_source, i_off, SEEK_SET);
Packit dd8086
  if (ret!=0) return ret;
Packit dd8086
Packit dd8086
  ret = cdio_stream_read (env->tocent[0].data_source, buf,
Packit dd8086
			  CDIO_CD_FRAMESIZE_RAW, 1);
Packit dd8086
  if (ret==0) return ret;
Packit dd8086
Packit dd8086
Packit dd8086
  /* See NOTE above. */
Packit dd8086
  if (b_form2)
Packit dd8086
    memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE,
Packit dd8086
	    M2RAW_SECTOR_SIZE);
Packit dd8086
  else
Packit dd8086
    memcpy (data, buf + CDIO_CD_XA_SYNC_HEADER, CDIO_CD_FRAMESIZE);
Packit dd8086
Packit dd8086
  return DRIVER_OP_SUCCESS;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
   Reads nblocks of mode2 sectors from cd device into data starting
Packit dd8086
   from lsn.
Packit dd8086
   Returns 0 if no error.
Packit dd8086
 */
Packit dd8086
static driver_return_code_t
Packit dd8086
_read_mode2_sectors_cdrdao (void *user_data, void *data, lsn_t lsn,
Packit dd8086
			    bool b_form2, unsigned int nblocks)
Packit dd8086
{
Packit dd8086
  _img_private_t *env = user_data;
Packit dd8086
  int i;
Packit dd8086
  int retval;
Packit dd8086
Packit dd8086
  for (i = 0; i < nblocks; i++) {
Packit dd8086
    if ( (retval = _read_mode2_sector_cdrdao (env,
Packit dd8086
					    ((char *)data) + (CDIO_CD_FRAMESIZE * i),
Packit dd8086
					    lsn + i, b_form2)) )
Packit dd8086
      return retval;
Packit dd8086
  }
Packit dd8086
  return 0;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return an array of strings giving possible TOC disk images.
Packit dd8086
 */
Packit dd8086
char **
Packit dd8086
cdio_get_devices_cdrdao (void)
Packit dd8086
{
Packit dd8086
  char **drives = NULL;
Packit dd8086
  unsigned int num_files=0;
Packit dd8086
#ifdef HAVE_GLOB_H
Packit dd8086
  unsigned int i;
Packit dd8086
  glob_t globbuf;
Packit dd8086
  globbuf.gl_offs = 0;
Packit dd8086
  glob("*.toc", GLOB_DOOFFS, NULL, &globbuf);
Packit dd8086
  for (i=0; i
Packit dd8086
    cdio_add_device_list(&drives, globbuf.gl_pathv[i], &num_files);
Packit dd8086
  }
Packit dd8086
  globfree(&globbuf);
Packit dd8086
#else
Packit dd8086
  cdio_add_device_list(&drives, DEFAULT_CDIO_DEVICE, &num_files);
Packit dd8086
#endif /*HAVE_GLOB_H*/
Packit dd8086
  cdio_add_device_list(&drives, NULL, &num_files);
Packit dd8086
  return drives;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return a string containing the default CD device.
Packit dd8086
 */
Packit dd8086
char *
Packit dd8086
cdio_get_default_device_cdrdao(void)
Packit dd8086
{
Packit dd8086
  char **drives = cdio_get_devices_nrg();
Packit dd8086
  char *drive = (drives[0] == NULL) ? NULL : strdup(drives[0]);
Packit dd8086
  cdio_free_device_list(drives);
Packit dd8086
  return drive;
Packit dd8086
}
Packit dd8086
Packit dd8086
static bool
Packit dd8086
get_hwinfo_cdrdao ( const CdIo_t *p_cdio, /*out*/ cdio_hwinfo_t *hw_info)
Packit dd8086
{
Packit dd8086
  strncpy(hw_info->psz_vendor, "libcdio",
Packit dd8086
	 sizeof(hw_info->psz_vendor)-1);
Packit dd8086
  hw_info->psz_vendor[sizeof(hw_info->psz_vendor)-1] = '\0';
Packit dd8086
  strncpy(hw_info->psz_model, "cdrdao",
Packit dd8086
	 sizeof(hw_info->psz_model)-1);
Packit dd8086
  hw_info->psz_model[sizeof(hw_info->psz_model)-1] = '\0';
Packit dd8086
  strncpy(hw_info->psz_revision, CDIO_VERSION,
Packit dd8086
	  sizeof(hw_info->psz_revision)-1);
Packit dd8086
  hw_info->psz_revision[sizeof(hw_info->psz_revision)-1] = '\0';
Packit dd8086
  return true;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return the number of tracks in the current medium.
Packit dd8086
  CDIO_INVALID_TRACK is returned on error.
Packit dd8086
*/
Packit dd8086
static track_format_t
Packit dd8086
_get_track_format_cdrdao(void *p_user_data, track_t i_track)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
Packit dd8086
  if (!p_env->gen.init) return TRACK_FORMAT_ERROR;
Packit dd8086
Packit dd8086
  if (i_track > p_env->gen.i_tracks || i_track == 0)
Packit dd8086
    return TRACK_FORMAT_ERROR;
Packit dd8086
Packit dd8086
  return p_env->tocent[i_track-p_env->gen.i_first_track].track_format;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return true if we have XA data (green, mode2 form1) or
Packit dd8086
  XA data (green, mode2 form2). That is track begins:
Packit dd8086
  sync - header - subheader
Packit dd8086
  12     4      -  8
Packit dd8086
Packit dd8086
  FIXME: there's gotta be a better design for this and get_track_format?
Packit dd8086
*/
Packit dd8086
static bool
Packit dd8086
_get_track_green_cdrdao(void *user_data, track_t i_track)
Packit dd8086
{
Packit dd8086
  _img_private_t *env = user_data;
Packit dd8086
Packit dd8086
  if (!env->gen.init) _init_cdrdao(env);
Packit dd8086
Packit dd8086
  if (i_track > env->gen.i_tracks || i_track == 0)
Packit dd8086
    return false;
Packit dd8086
Packit dd8086
  return env->tocent[i_track-env->gen.i_first_track].track_green;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Return the starting LSN track number
Packit dd8086
  i_track in obj.  Track numbers start at 1.
Packit dd8086
  The "leadout" track is specified either by
Packit dd8086
  using i_track CDIO_CDROM_LEADOUT_TRACK or the total tracks+1.
Packit dd8086
  False is returned if there is no track entry.
Packit dd8086
*/
Packit dd8086
static lba_t
Packit dd8086
_get_lba_track_cdrdao(void *p_user_data, track_t i_track)
Packit dd8086
{
Packit dd8086
  _img_private_t *p_env = p_user_data;
Packit dd8086
  _init_cdrdao (p_env);
Packit dd8086
Packit dd8086
  if (i_track == CDIO_CDROM_LEADOUT_TRACK)
Packit dd8086
    i_track = p_env->gen.i_tracks+1;
Packit dd8086
Packit dd8086
  if (i_track <= p_env->gen.i_tracks+1 && i_track != 0) {
Packit dd8086
    return p_env->tocent[i_track-1].start_lba;
Packit dd8086
  } else
Packit dd8086
    return CDIO_INVALID_LBA;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Check that a TOC file is valid. We parse the entire file.
Packit dd8086
Packit dd8086
*/
Packit dd8086
bool
Packit dd8086
cdio_is_tocfile(const char *psz_cue_name)
Packit dd8086
{
Packit dd8086
  int   i;
Packit dd8086
Packit dd8086
  if (psz_cue_name == NULL) return false;
Packit dd8086
Packit dd8086
  i=strlen(psz_cue_name)-strlen("toc");
Packit dd8086
Packit dd8086
  if (i>0) {
Packit dd8086
    if ( (psz_cue_name[i]=='t' && psz_cue_name[i+1]=='o' && psz_cue_name[i+2]=='c')
Packit dd8086
	 || (psz_cue_name[i]=='T' && psz_cue_name[i+1]=='O' && psz_cue_name[i+2]=='C') ) {
Packit dd8086
      return parse_tocfile(NULL, psz_cue_name);
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
  return false;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Initialization routine. This is the only thing that doesn't
Packit dd8086
  get called via a function pointer. In fact *we* are the
Packit dd8086
  ones to set that up.
Packit dd8086
 */
Packit dd8086
CdIo_t *
Packit dd8086
cdio_open_am_cdrdao (const char *psz_source_name, const char *psz_access_mode)
Packit dd8086
{
Packit dd8086
  if (psz_access_mode != NULL && strcmp(psz_access_mode, "image"))
Packit dd8086
    cdio_warn ("there is only one access mode, 'image' for cdrdao. Arg %s ignored",
Packit dd8086
	       psz_access_mode);
Packit dd8086
  return cdio_open_cdrdao(psz_source_name);
Packit dd8086
}
Packit dd8086
Packit dd8086
/*!
Packit dd8086
  Initialization routine. This is the only thing that doesn't
Packit dd8086
  get called via a function pointer. In fact *we* are the
Packit dd8086
  ones to set that up.
Packit dd8086
 */
Packit dd8086
CdIo_t *
Packit dd8086
cdio_open_cdrdao (const char *psz_cue_name)
Packit dd8086
{
Packit dd8086
  CdIo_t *ret;
Packit dd8086
  _img_private_t *p_data;
Packit dd8086
Packit dd8086
  cdio_funcs_t _funcs;
Packit dd8086
Packit dd8086
  memset( &_funcs, 0, sizeof(_funcs) );
Packit dd8086
Packit dd8086
  _funcs.eject_media           = _eject_media_image;
Packit dd8086
  _funcs.free                  = _free_image;
Packit dd8086
  _funcs.get_arg               = _get_arg_image;
Packit dd8086
  _funcs.get_cdtext            = _get_cdtext_image;
Packit dd8086
  _funcs.get_cdtext_raw        = NULL;
Packit dd8086
  _funcs.get_devices           = cdio_get_devices_cdrdao;
Packit dd8086
  _funcs.get_default_device    = cdio_get_default_device_cdrdao;
Packit dd8086
  _funcs.get_disc_last_lsn     = get_disc_last_lsn_cdrdao;
Packit dd8086
  _funcs.get_discmode          = _get_discmode_image;
Packit dd8086
  _funcs.get_drive_cap         = _get_drive_cap_image;
Packit dd8086
  _funcs.get_first_track_num   = _get_first_track_num_image;
Packit dd8086
  _funcs.get_hwinfo            = get_hwinfo_cdrdao;
Packit dd8086
  _funcs.get_media_changed     = get_media_changed_image;
Packit dd8086
  _funcs.get_mcn               = _get_mcn_image;
Packit dd8086
  _funcs.get_num_tracks        = _get_num_tracks_image;
Packit dd8086
  _funcs.get_track_channels    = get_track_channels_image;
Packit dd8086
  _funcs.get_track_copy_permit = get_track_copy_permit_image;
Packit dd8086
  _funcs.get_track_format      = _get_track_format_cdrdao;
Packit dd8086
  _funcs.get_track_green       = _get_track_green_cdrdao;
Packit dd8086
  _funcs.get_track_lba         = _get_lba_track_cdrdao;
Packit dd8086
  _funcs.get_track_msf         = _get_track_msf_image;
Packit dd8086
  _funcs.get_track_preemphasis = get_track_preemphasis_image;
Packit dd8086
  _funcs.get_track_pregap_lba  = get_track_pregap_lba_image;
Packit dd8086
  _funcs.get_track_isrc        = get_track_isrc_image;
Packit dd8086
  _funcs.lseek                 = _lseek_cdrdao;
Packit dd8086
  _funcs.read                  = _read_cdrdao;
Packit dd8086
  _funcs.read_audio_sectors    = _read_audio_sectors_cdrdao;
Packit dd8086
  _funcs.read_data_sectors     = read_data_sectors_image;
Packit dd8086
  _funcs.read_mode1_sector     = _read_mode1_sector_cdrdao;
Packit dd8086
  _funcs.read_mode1_sectors    = _read_mode1_sectors_cdrdao;
Packit dd8086
  _funcs.read_mode2_sector     = _read_mode2_sector_cdrdao;
Packit dd8086
  _funcs.read_mode2_sectors    = _read_mode2_sectors_cdrdao;
Packit dd8086
  _funcs.run_mmc_cmd           = NULL;
Packit dd8086
  _funcs.set_arg               = _set_arg_image;
Packit dd8086
  _funcs.set_speed             = cdio_generic_unimplemented_set_speed;
Packit dd8086
  _funcs.set_blocksize         = cdio_generic_unimplemented_set_blocksize;
Packit dd8086
Packit dd8086
  if (NULL == psz_cue_name) return NULL;
Packit dd8086
Packit dd8086
  p_data                  = calloc(1, sizeof (_img_private_t));
Packit dd8086
  p_data->gen.init        = false;
Packit dd8086
  p_data->psz_cue_name    = NULL;
Packit dd8086
  p_data->gen.data_source = NULL;
Packit dd8086
  p_data->gen.source_name = NULL;
Packit dd8086
Packit dd8086
  ret = cdio_new ((void *)p_data, &_funcs);
Packit dd8086
Packit dd8086
  if (ret == NULL) {
Packit dd8086
    free(p_data);
Packit dd8086
    return NULL;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  ret->driver_id = DRIVER_CDRDAO;
Packit dd8086
  if (!cdio_is_tocfile(psz_cue_name)) {
Packit dd8086
    cdio_debug ("source name %s is not recognized as a TOC file",
Packit dd8086
		psz_cue_name);
Packit dd8086
    free(p_data);
Packit dd8086
    free(ret);
Packit dd8086
    return NULL;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  _set_arg_image (p_data, "cue", psz_cue_name);
Packit dd8086
  _set_arg_image (p_data, "source", psz_cue_name);
Packit dd8086
  _set_arg_image (p_data, "access-mode", "cdrdao");
Packit dd8086
Packit dd8086
  if (_init_cdrdao(p_data)) {
Packit dd8086
    return ret;
Packit dd8086
  } else {
Packit dd8086
    _free_image(p_data);
Packit dd8086
    free(ret);
Packit dd8086
    return NULL;
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
bool
Packit dd8086
cdio_have_cdrdao (void)
Packit dd8086
{
Packit dd8086
  return true;
Packit dd8086
}