/*
 * cdaccess.c
 * System-dependent code to access extended CD information
 *
 * Based on a contribution by Erik Andersen
 *
 * Copyright (c) 2003 Christoph Pfisterer
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */

#include "global.h"


#ifdef USE_IOCTL_LINUX
#include <sys/ioctl.h>
#include <linux/cdrom.h>
#define DO_CDACCESS 1
#endif  /* USE_IOCTL_LINUX */


#ifdef DO_CDACCESS

/*
 * CDDB stuff
 */

static int cddb_sum(int n);

#define LBA_TO_SECS(lba) (((lba) + 150) / 75)


/*
 * CD structure:
 * Tries to get the CD TOC using out-of-band means (i.e. ioctl()),
 * prints the info, and analyzes just the data tracks. Quite
 * system-dependent, but layed out for porability.
 */

int analyze_cdaccess(int fd, SOURCE *s, int level)
{
  int i;
  int first, last, ntracks;
  int cksum, totaltime, seconds;
  u4 diskid;
  u1 ctrl[100];
  u4 lba[100], length;
  char human_readable_size[256];
#ifdef USE_IOCTL_LINUX
  struct cdrom_tochdr tochdr;
  struct cdrom_tocentry tocentry;
#endif

  /* read TOC header */
#ifdef USE_IOCTL_LINUX
  if (ioctl(fd, CDROMREADTOCHDR, &tochdr) < 0) {
    return 0;
  }
  first = tochdr.cdth_trk0;
  last  = tochdr.cdth_trk1;
#endif

  ntracks = last + 1 - first;
  if (ntracks > 99)  /* natural limit */
    return 0;

  /* read per-track data from TOC */
  for (i = 0; i <= ntracks; i++) {
#ifdef USE_IOCTL_LINUX
    if (i == ntracks)
      tocentry.cdte_track = CDROM_LEADOUT;
    else
      tocentry.cdte_track = first + i;
    tocentry.cdte_format = CDROM_LBA;
    if (ioctl(fd, CDROMREADTOCENTRY, &tocentry) < 0) {
      //printf("CDROMREADTOCENTRY: %s: ", strerror(errno));
      return 0;
    }
    ctrl[i] = tocentry.cdte_ctrl;
    lba[i] = tocentry.cdte_addr.lba;
#endif
  }

  /* System-dependent code ends here. From now on, we use the data
     in first, last, ntracks, ctrl[], and lba[]. We also assume
     all systems treat actual data access the same way... */

  /* calculate CDDB disk id */
  cksum = 0;
  for (i = 0; i < ntracks; i++) {
    cksum += cddb_sum(LBA_TO_SECS(lba[i]));
  }
  totaltime = LBA_TO_SECS(lba[ntracks]) - LBA_TO_SECS(lba[0]);
  diskid = (u4)(cksum % 0xff) << 24 | (u4)totaltime << 8 | (u4)ntracks;

  /* print disk info */
  print_line(level, "CD-ROM, %d track%s, CDDB disk ID %08lX",
	     ntracks, (ntracks != 1) ? "s" : "", diskid);

  /* Loop over each track */
  for (i = 0; i < ntracks; i++) {
    /* length of track in sectors */
    length = lba[i+1] - lba[i];

    if ((ctrl[i] & 0x4) == 0) {
      /* Audio track, one sector holds 2352 actual data bytes */
      seconds = length / 75;
      format_size(human_readable_size, (u8)length * 2352);
      print_line(level, "Track %d: Audio track, %s, %3d min %02d sec", 
		 first + i, human_readable_size,
		 seconds / 60, seconds % 60);

    } else {
      /* Data track, one sector holds 2048 actual data bytes */
      format_size(human_readable_size, length * 2048);
      print_line(level, "Track %d: Data track, %s",
		 first + i, human_readable_size);

      /* NOTE: we adjust the length to stay clear of padding or
	 post-gap stuff */
      analyze_source_special(s, level + 1,
			     (u8)lba[i] * 2048, (u8)(length - 250) * 2048);
    }
  }

  return 1;
}

/*
 * helper function: calculate cddb disk id
 */

static int cddb_sum(int n)
{
  /* a number like 2344 becomes 2+3+4+4 (13) */
  int ret = 0;

  while (n > 0) {
    ret = ret + (n % 10);
    n = n / 10;
  }

  return ret;
}


#else   /* DO_CDACCESS */

/*
 * the system is not supported, so use a dummy function
 */

int analyze_cdaccess(int fd, SOURCE *s, int level)
{
  return 0;
}

#endif  /* DO_CDACCESS */


/* EOF */


syntax highlighted by Code2HTML, v. 0.9.1