diff options
Diffstat (limited to 'contrib/libcdio')
62 files changed, 24563 insertions, 0 deletions
diff --git a/contrib/libcdio/FreeBSD/freebsd.c b/contrib/libcdio/FreeBSD/freebsd.c new file mode 100644 index 000000000..daea9b3f5 --- /dev/null +++ b/contrib/libcdio/FreeBSD/freebsd.c @@ -0,0 +1,638 @@ +/* + $Id: freebsd.c,v 1.1 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file contains FreeBSD-specific code and implements low-level + control of the CD drive. Culled initially I think from xine's or + mplayer's FreeBSD code with lots of modifications. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: freebsd.c,v 1.1 2005/01/01 02:43:57 rockyb Exp $"; + +#include "freebsd.h" + +#ifdef HAVE_FREEBSD_CDROM + +#include <cdio/sector.h> + +static access_mode_t +str_to_access_mode_freebsd(const char *psz_access_mode) +{ + const access_mode_t default_access_mode = DEFAULT_FREEBSD_AM; + + if (NULL==psz_access_mode) return default_access_mode; + + if (!strcmp(psz_access_mode, "ioctl")) + return _AM_IOCTL; + else if (!strcmp(psz_access_mode, "CAM")) + return _AM_CAM; + else { + cdio_warn ("unknown access type: %s. Default ioctl used.", + psz_access_mode); + return default_access_mode; + } +} + +static void +_free_freebsd (void *obj) +{ + _img_private_t *env = obj; + + if (NULL == env) return; + + if (NULL != env->device) free(env->device); + + if (_AM_CAM == env->access_mode) + return free_freebsd_cam(env); + else + return cdio_generic_free(obj); +} + +/* Check a drive to see if it is a CD-ROM + Return 1 if a CD-ROM. 0 if it exists but isn't a CD-ROM drive + and -1 if no device exists . +*/ +static bool +cdio_is_cdrom(char *drive, char *mnttype) +{ + return cdio_is_cdrom_freebsd_ioctl(drive, mnttype); +} + +/*! + Reads nblocks of audio sectors from cd device into data starting from lsn. + Returns 0 if no error. + */ +static int +_read_audio_sectors_freebsd (void *user_data, void *data, lsn_t lsn, + unsigned int nblocks) +{ + return read_audio_sectors_freebsd_ioctl(user_data, data, lsn, nblocks); +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_read_mode2_sector_freebsd (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *env = user_data; + + if ( env->access_mode == _AM_CAM ) + return read_mode2_sector_freebsd_cam(env, data, lsn, b_form2); + else + return read_mode2_sector_freebsd_ioctl(env, data, lsn, b_form2); +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode2_sectors_freebsd (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *env = user_data; + + if ( env->access_mode == _AM_CAM && b_form2) { + /* We have a routine that covers this case without looping. */ + return read_mode2_sectors_freebsd_cam(env, data, lsn, nblocks); + } else { + unsigned int i; + unsigned int i_blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + /* For each frame, pick out the data part we need */ + for (i = 0; i < nblocks; i++) { + int retval = _read_mode2_sector_freebsd (env, + ((char *)data) + + (i_blocksize * i), + lsn + i, b_form2); + if (retval) return retval; + } + } + return 0; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static uint32_t +_stat_size_freebsd (void *obj) +{ + _img_private_t *env = obj; + + if (NULL == env) return CDIO_INVALID_LBA; + + if (_AM_CAM == env->access_mode) + return stat_size_freebsd_cam(env); + else + return stat_size_freebsd_ioctl(env); +} + +/*! + Set the key "arg" to "value" in source device. +*/ +static int +_set_arg_freebsd (void *user_data, const char key[], const char value[]) +{ + _img_private_t *env = user_data; + + if (!strcmp (key, "source")) + { + if (!value) + return -2; + + free (env->gen.source_name); + + env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + env->access_mode = str_to_access_mode_freebsd(value); + if (env->access_mode == _AM_CAM && !env->b_cam_init) + return init_freebsd_cam(env) ? 1 : -3; + return 0; + } + else + return -1; + + return 0; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return false if unsuccessful; +*/ +static bool +read_toc_freebsd (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + track_t i, j; + + /* read TOC header */ + if ( ioctl(p_env->gen.fd, CDIOREADTOCHEADER, &p_env->tochdr) == -1 ) { + cdio_warn("error in ioctl(CDIOREADTOCHEADER): %s\n", strerror(errno)); + return false; + } + + p_env->gen.i_first_track = p_env->tochdr.starting_track; + p_env->gen.i_tracks = p_env->tochdr.ending_track - + p_env->gen.i_first_track + 1; + + j=0; + for (i=p_env->gen.i_first_track; i<=p_env->gen.i_tracks; i++, j++) { + p_env->tocent[j].track = i; + p_env->tocent[j].address_format = CD_LBA_FORMAT; + + if ( ioctl(p_env->gen.fd, CDIOREADTOCENTRY, &(p_env->tocent[j]) ) ) { + cdio_warn("%s %d: %s\n", + "error in ioctl CDROMREADTOCENTRY for track", + i, strerror(errno)); + return false; + } + } + + p_env->tocent[j].track = CDIO_CDROM_LEADOUT_TRACK; + p_env->tocent[j].address_format = CD_LBA_FORMAT; + if ( ioctl(p_env->gen.fd, CDIOREADTOCENTRY, &(p_env->tocent[j]) ) ){ + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCENTRY for leadout track", + strerror(errno)); + return false; + } + + p_env->gen.toc_init = true; + return true; +} + +/*! + Eject media. Return 1 if successful, 0 otherwise. + */ +static int +_eject_media_freebsd (void *user_data) +{ + _img_private_t *p_env = user_data; + + return (p_env->access_mode == _AM_IOCTL) + ? eject_media_freebsd_ioctl(p_env) + : eject_media_freebsd_cam(p_env); +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +_get_arg_freebsd (void *user_data, const char key[]) +{ + _img_private_t *env = user_data; + + if (!strcmp (key, "source")) { + return env->gen.source_name; + } else if (!strcmp (key, "access-mode")) { + switch (env->access_mode) { + case _AM_IOCTL: + return "ioctl"; + case _AM_CAM: + return "CAM"; + case _AM_NONE: + return "no access method"; + } + } + return NULL; +} + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + FIXME: This is just a guess. + + */ +static char * +_get_mcn_freebsd (const void *p_user_data) { + + const _img_private_t *p_env = p_user_data; + + return (p_env->access_mode == _AM_IOCTL) + ? get_mcn_freebsd_ioctl(p_env) + : scsi_mmc_get_mcn(p_env->gen.cdio); + +} + +static void +get_drive_cap_freebsd (const void *p_user_data, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap) +{ + const _img_private_t *p_env = p_user_data; + + if (p_env->access_mode == _AM_CAM) + scsi_mmc_get_drive_cap_generic (p_user_data, p_read_cap, p_write_cap, + p_misc_cap); + +} + +/*! + Run a SCSI MMC command. + + p_user_data internal CD structure. + i_timeout_ms time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + i_cdb Size of p_cdb + p_cdb CDB bytes. + e_direction direction the transfer is to go. + i_buf Size of buffer + p_buf Buffer for data, both sending and receiving + + Return 0 if no error. + */ +static int +run_scsi_cmd_freebsd( const void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const scsi_mmc_cdb_t *p_cdb, + scsi_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + + if (p_env->access_mode == _AM_CAM) + return run_scsi_cmd_freebsd_cam( p_user_data, i_timeout_ms, i_cdb, p_cdb, + e_direction, i_buf, p_buf ); + else + return 2; +} + +/*! + Get format of track. + + FIXME: We're just guessing this from the GNU/Linux code. + +*/ +static track_format_t +_get_track_format_freebsd(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if (!p_env->gen.toc_init) read_toc_freebsd (p_user_data) ; + + if (i_track > TOTAL_TRACKS || i_track == 0) + return TRACK_FORMAT_ERROR; + + i_track -= FIRST_TRACK_NUM; + + /* This is pretty much copied from the "badly broken" cdrom_count_tracks + in linux/cdrom.c. + */ + if (p_env->tocent[i_track].entry.control & CDIO_CDROM_DATA_TRACK) { + if (p_env->tocent[i_track].address_format == CDIO_CDROM_CDI_TRACK) + return TRACK_FORMAT_CDI; + else if (p_env->tocent[i_track].address_format == CDIO_CDROM_XA_TRACK) + return TRACK_FORMAT_XA; + else + return TRACK_FORMAT_DATA; + } else + return TRACK_FORMAT_AUDIO; + +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +static bool +_get_track_green_freebsd(void *user_data, track_t i_track) +{ + _img_private_t *p_env = user_data; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = TOTAL_TRACKS+1; + + if (i_track > TOTAL_TRACKS+1 || i_track == 0) + return false; + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cdinfo for a while. + */ + return ((p_env->tocent[i_track-FIRST_TRACK_NUM].entry.control & 2) != 0); +} + +/*! + Return the starting LSN track number + i_track in obj. Track numbers start at 1. + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + CDIO_INVALID_LBA is returned if there is no track entry. +*/ +static lba_t +_get_track_lba_freebsd(void *user_data, track_t i_track) +{ + _img_private_t *p_env = user_data; + + if (!p_env->gen.toc_init) read_toc_freebsd (p_env) ; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = TOTAL_TRACKS+1; + + if (i_track > TOTAL_TRACKS+1 || i_track == 0 || !p_env->gen.toc_init) { + return CDIO_INVALID_LBA; + } else { + return cdio_lsn_to_lba(ntohl(p_env->tocent[i_track-FIRST_TRACK_NUM].entry.addr.lba)); + } +} + +#endif /* HAVE_FREEBSD_CDROM */ + +/*! + Return an array of strings giving possible CD devices. + */ +char ** +cdio_get_devices_freebsd (void) +{ +#ifndef HAVE_FREEBSD_CDROM + return NULL; +#else + char drive[40]; + char **drives = NULL; + unsigned int num_drives=0; + bool exists=true; + char c; + + /* Scan the system for CD-ROM drives. + */ + +#ifdef USE_ETC_FSTAB + + struct fstab *fs; + setfsent(); + + /* Check what's in /etc/fstab... */ + while ( (fs = getfsent()) ) + { + if (strncmp(fs->fs_spec, "/dev/sr", 7)) + cdio_add_device_list(&drives, fs->fs_spec, &num_drives); + } + +#endif + + /* Scan the system for CD-ROM drives. + Not always 100% reliable, so use the USE_MNTENT code above first. + */ + + /* Scan SCSI and CAM devices */ + for ( c='0'; exists && c <='9'; c++ ) { + sprintf(drive, "/dev/cd%c%s", c, DEVICE_POSTFIX); + exists = cdio_is_cdrom(drive, NULL); + if ( exists ) { + cdio_add_device_list(&drives, drive, &num_drives); + } + } + + /* Scan are ATAPI devices */ + for ( c='0'; exists && c <='9'; c++ ) { + sprintf(drive, "/dev/acd%c%s", c, DEVICE_POSTFIX); + exists = cdio_is_cdrom(drive, NULL); + if ( exists ) { + cdio_add_device_list(&drives, drive, &num_drives); + } + } + cdio_add_device_list(&drives, NULL, &num_drives); + return drives; +#endif /*HAVE_FREEBSD_CDROM*/ +} + +/*! + Return a string containing the default CD device if none is specified. + */ +char * +cdio_get_default_device_freebsd() +{ +#ifndef HAVE_FREEBSD_CDROM + return NULL; +#else + char drive[40]; + bool exists=true; + char c; + + /* Scan the system for CD-ROM drives. + */ + +#ifdef USE_ETC_FSTAB + + struct fstab *fs; + setfsent(); + + /* Check what's in /etc/fstab... */ + while ( (fs = getfsent()) ) + { + if (strncmp(fs->fs_spec, "/dev/sr", 7)) + return strdup(fs->fs_spec); + } + +#endif + + /* Scan the system for CD-ROM drives. + Not always 100% reliable, so use the USE_MNTENT code above first. + */ + + /* Scan SCSI and CAM devices */ + for ( c='0'; exists && c <='9'; c++ ) { + sprintf(drive, "/dev/cd%c%s", c, DEVICE_POSTFIX); + exists = cdio_is_cdrom(drive, NULL); + if ( exists ) { + return strdup(drive); + } + } + + /* Scan are ATAPI devices */ + for ( c='0'; exists && c <='9'; c++ ) { + sprintf(drive, "/dev/acd%c%s", c, DEVICE_POSTFIX); + exists = cdio_is_cdrom(drive, NULL); + if ( exists ) { + return strdup(drive); + } + } + return NULL; +#endif /*HAVE_FREEBSD_CDROM*/ +} + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_freebsd (const char *psz_source_name) +{ + return cdio_open_am_freebsd(psz_source_name, NULL); +} + + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_am_freebsd (const char *psz_orig_source_name, + const char *psz_access_mode) +{ + +#ifdef HAVE_FREEBSD_CDROM + CdIo *ret; + _img_private_t *_data; + char *psz_source_name; + + cdio_funcs _funcs = { + .eject_media = _eject_media_freebsd, + .free = _free_freebsd, + .get_arg = _get_arg_freebsd, + .get_cdtext = get_cdtext_generic, + .get_default_device = cdio_get_default_device_freebsd, + .get_devices = cdio_get_devices_freebsd, + .get_discmode = get_discmode_generic, + .get_drive_cap = get_drive_cap_freebsd, + .get_first_track_num= get_first_track_num_generic, + .get_mcn = _get_mcn_freebsd, + .get_num_tracks = get_num_tracks_generic, + .get_track_format = _get_track_format_freebsd, + .get_track_green = _get_track_green_freebsd, + .get_track_lba = _get_track_lba_freebsd, + .get_track_msf = NULL, + .lseek = cdio_generic_lseek, + .read = cdio_generic_read, + .read_audio_sectors = _read_audio_sectors_freebsd, + .read_mode2_sector = _read_mode2_sector_freebsd, + .read_mode2_sectors = _read_mode2_sectors_freebsd, + .read_toc = read_toc_freebsd, + .run_scsi_mmc_cmd = run_scsi_cmd_freebsd, + .set_arg = _set_arg_freebsd, + .stat_size = _stat_size_freebsd + }; + + _data = _cdio_malloc (sizeof (_img_private_t)); + _data->access_mode = str_to_access_mode_freebsd(psz_access_mode); + _data->gen.init = false; + _data->gen.fd = -1; + _data->gen.toc_init = false; + _data->gen.b_cdtext_init = false; + _data->gen.b_cdtext_error = false; + + if (NULL == psz_orig_source_name) { + psz_source_name=cdio_get_default_device_freebsd(); + if (NULL == psz_source_name) return NULL; + _data->device = psz_source_name; + _set_arg_freebsd(_data, "source", psz_source_name); + } else { + if (cdio_is_device_generic(psz_orig_source_name)) { + _set_arg_freebsd(_data, "source", psz_orig_source_name); + _data->device = strdup(psz_orig_source_name); + } else { + /* The below would be okay if all device drivers worked this way. */ +#if 0 + cdio_info ("source %s is a not a device", psz_orig_source_name); +#endif + return NULL; + } + } + + ret = cdio_new ((void *)_data, &_funcs); + if (ret == NULL) return NULL; + + if (cdio_generic_init(_data)) + if ( _data->access_mode == _AM_IOCTL ) { + return ret; + } else { + if (init_freebsd_cam(_data)) + return ret; + else { + cdio_generic_free (_data); + return NULL; + } + } + else { + cdio_generic_free (_data); + return NULL; + } + +#else + return NULL; +#endif /* HAVE_FREEBSD_CDROM */ + +} + +bool +cdio_have_freebsd (void) +{ +#ifdef HAVE_FREEBSD_CDROM + return true; +#else + return false; +#endif /* HAVE_FREEBSD_CDROM */ +} diff --git a/contrib/libcdio/FreeBSD/freebsd.h b/contrib/libcdio/FreeBSD/freebsd.h new file mode 100644 index 000000000..50894c912 --- /dev/null +++ b/contrib/libcdio/FreeBSD/freebsd.h @@ -0,0 +1,232 @@ +/* + $Id: freebsd.h,v 1.1 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file contains FreeBSD-specific code and implements low-level + control of the CD drive. Culled initially I think from xine's or + mplayer's FreeBSD code with lots of modifications. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cdio/sector.h> +#include "cdio_assert.h" +#include "cdio_private.h" + +/*! + For ioctl access /dev/acd0c is preferred over /dev/cd0c. + For cam access /dev/cd0c is preferred. DEFAULT_CDIO_DEVICE and + DEFAULT_FREEBSD_AM should be consistent. + */ + +#ifndef DEFAULT_CDIO_DEVICE +#define DEFAULT_CDIO_DEVICE "/dev/cd0c" +#endif + +#ifndef DEFUALT_FREEBSD_AM +#define DEFAULT_FREEBSD_AM _AM_CAM +#endif + +#include <string.h> + +#ifdef HAVE_FREEBSD_CDROM + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#ifdef HAVE_SYS_CDIO_H +# include <sys/cdio.h> +#endif + +#ifndef CDIOCREADAUDIO +struct ioc_read_audio +{ + u_char address_format; + union msf_lba address; + int nframes; + u_char* buffer; +}; + +#define CDIOCREADAUDIO _IOWR('c',31,struct ioc_read_audio) +#endif + +#include <sys/cdrio.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/param.h> /* for __FreeBSD_version */ + +#if __FreeBSD_version < 500000 +#define DEVICE_POSTFIX "c" +#else +#define DEVICE_POSTFIX "" +#endif + +#define HAVE_FREEBSD_CAM +#ifdef HAVE_FREEBSD_CAM +#include <camlib.h> + +#include <cam/scsi/scsi_message.h> +#include <cam/scsi/scsi_pass.h> +#include <errno.h> +#define ERRCODE(s) ((((s)[2]&0x0F)<<16)|((s)[12]<<8)|((s)[13])) +#define EMEDIUMTYPE EINVAL +#define ENOMEDIUM ENODEV +#define CREAM_ON_ERRNO(s) do { \ + switch ((s)[12]) \ + { case 0x04: errno=EAGAIN; break; \ + case 0x20: errno=ENODEV; break; \ + case 0x21: if ((s)[13]==0) errno=ENOSPC; \ + else errno=EINVAL; \ + break; \ + case 0x30: errno=EMEDIUMTYPE; break; \ + case 0x3A: errno=ENOMEDIUM; break; \ + } \ +} while(0) +#endif /*HAVE_FREEBSD_CAM*/ + +#include <cdio/util.h> + +#define TOTAL_TRACKS ( p_env->tochdr.ending_track \ + - p_env->tochdr.starting_track + 1) +#define FIRST_TRACK_NUM (p_env->tochdr.starting_track) + +typedef enum { + _AM_NONE, + _AM_IOCTL, + _AM_CAM +} access_mode_t; + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + +#ifdef HAVE_FREEBSD_CAM + char *device; + struct cam_device *cam; + union ccb ccb; +#endif + + access_mode_t access_mode; + + bool b_ioctl_init; + bool b_cam_init; + + /* Track information */ + struct ioc_toc_header tochdr; + + /* Entry info for each track. Add 1 for leadout. */ + struct ioc_read_toc_single_entry tocent[CDIO_CD_MAX_TRACKS+1]; + +} _img_private_t; + +bool cdio_is_cdrom_freebsd_ioctl(char *drive, char *mnttype); + +track_format_t get_track_format_freebsd_ioctl(const _img_private_t *env, + track_t i_track); +bool get_track_green_freebsd_ioctl(const _img_private_t *env, + track_t i_track); + +int eject_media_freebsd_ioctl (_img_private_t *env); +int eject_media_freebsd_cam (_img_private_t *env); + +void get_drive_cap_freebsd_cam (const _img_private_t *p_env, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap); + +char *get_mcn_freebsd_ioctl (const _img_private_t *p_env); + +void free_freebsd_cam (void *obj); + +/*! + Using the ioctl method, r nblocks of audio sectors from cd device + into data starting from lsn. Returns 0 if no error. + */ +int read_audio_sectors_freebsd_ioctl (_img_private_t *env, void *data, + lsn_t lsn, unsigned int nblocks); +/*! + Using the CAM method, reads nblocks of mode2 sectors from + cd device using into data starting from lsn. Returns 0 if no + error. +*/ +int read_mode2_sector_freebsd_cam (_img_private_t *env, void *data, + lsn_t lsn, bool b_form2); + +/*! + Using the ioctl method, reads nblocks of mode2 sectors from + cd device using into data starting from lsn. Returns 0 if no + error. +*/ +int read_mode2_sector_freebsd_ioctl (_img_private_t *env, void *data, + lsn_t lsn, bool b_form2); + +/*! + Using the CAM method, reads nblocks of mode2 form2 sectors from + cd device using into data starting from lsn. Returns 0 if no + error. + + Note: if you want form1 sectors, the caller has to pick out the + appropriate piece. +*/ +int read_mode2_sectors_freebsd_cam (_img_private_t *env, void *buf, + lsn_t lsn, unsigned int nblocks); + +bool read_toc_freebsd_ioctl (_img_private_t *env); + +/*! + Run a SCSI MMC command. + + p_user_data internal CD structure. + i_timeout time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + i_cdb Size of p_cdb + p_cdb CDB bytes. + e_direction direction the transfer is to go. + i_buf Size of buffer + p_buf Buffer for data, both sending and receiving + + Return 0 if no error. + */ +int run_scsi_cmd_freebsd_cam( const void *p_user_data, + unsigned int i_timeout_ms, + unsigned int i_cdb, + const scsi_mmc_cdb_t *p_cdb, + scsi_mmc_direction_t e_direction, + unsigned int i_buf, + /*in/out*/ void *p_buf ); + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +uint32_t stat_size_freebsd_cam (_img_private_t *env); +uint32_t stat_size_freebsd_ioctl (_img_private_t *env); + +bool init_freebsd_cam (_img_private_t *env); +void free_freebsd_cam (void *user_data); + +#endif /*HAVE_FREEBSD_CDROM*/ diff --git a/contrib/libcdio/FreeBSD/freebsd_cam.c b/contrib/libcdio/FreeBSD/freebsd_cam.c new file mode 100644 index 000000000..68e38ccae --- /dev/null +++ b/contrib/libcdio/FreeBSD/freebsd_cam.c @@ -0,0 +1,346 @@ +/* + $Id: freebsd_cam.c,v 1.1 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file contains FreeBSD-specific code and implements low-level + control of the CD drive via SCSI emulation. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: freebsd_cam.c,v 1.1 2005/01/01 02:43:57 rockyb Exp $"; + +#ifdef HAVE_FREEBSD_CDROM + +#include "freebsd.h" +#include <cdio/scsi_mmc.h> + +/* Default value in seconds we will wait for a command to + complete. */ +#define DEFAULT_TIMEOUT_MSECS 10000 + +/*! + Run a SCSI MMC command. + + p_user_data internal CD structure. + i_timeout_ms time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + i_cdb Size of p_cdb + p_cdb CDB bytes. + e_direction direction the transfer is to go. + i_buf Size of buffer + p_buf Buffer for data, both sending and receiving + + Return 0 if no error. + */ +int +run_scsi_cmd_freebsd_cam( const void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const scsi_mmc_cdb_t *p_cdb, + scsi_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + int i_status; + int direction = CAM_DEV_QFRZDIS; + union ccb ccb; + + if (!p_env || !p_env->cam) return -2; + + memset(&ccb, 0, sizeof(ccb)); + + ccb.ccb_h.path_id = p_env->cam->path_id; + ccb.ccb_h.target_id = p_env->cam->target_id; + ccb.ccb_h.target_lun = p_env->cam->target_lun; + ccb.ccb_h.timeout = i_timeout_ms; + + if (!i_cdb) + direction |= CAM_DIR_NONE; + else + direction |= (e_direction == SCSI_MMC_DATA_READ)?CAM_DIR_IN : CAM_DIR_OUT; + cam_fill_csio (&(ccb.csio), 1, NULL, + direction | CAM_DEV_QFRZDIS, MSG_SIMPLE_Q_TAG, p_buf, i_buf, + sizeof(ccb.csio.sense_data), 0, 30*1000); + + memcpy(ccb.csio.cdb_io.cdb_bytes, p_cdb, i_cdb); + + ccb.csio.cdb_len = + scsi_mmc_get_cmd_len(ccb.csio.cdb_io.cdb_bytes[0]); + + if ((i_status = cam_send_ccb(p_env->cam, &ccb)) < 0) + { + cdio_warn ("transport failed: %d", i_status); + return -1; + } + if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) + { + return 0; + } + errno = EIO; + i_status = ERRCODE(((unsigned char *)&ccb.csio.sense_data)); + if (i_status == 0) + i_status = -1; + else + CREAM_ON_ERRNO(((unsigned char *)&ccb.csio.sense_data)); + cdio_warn ("transport failed: %d", i_status); + return i_status; +} + +bool +init_freebsd_cam (_img_private_t *p_env) +{ + char pass[100]; + + p_env->cam=NULL; + memset (&p_env->ccb, 0, sizeof(p_env->ccb)); + p_env->ccb.ccb_h.func_code = XPT_GDEVLIST; + + if (-1 == p_env->gen.fd) + p_env->gen.fd = open (p_env->device, O_RDONLY, 0); + + if (p_env->gen.fd < 0) + { + cdio_warn ("open (%s): %s", p_env->device, strerror (errno)); + return false; + } + + if (ioctl (p_env->gen.fd, CAMGETPASSTHRU, &p_env->ccb) < 0) + { + cdio_warn ("open: %s", strerror (errno)); + return false; + } + sprintf (pass,"/dev/%.15s%u", + p_env->ccb.cgdl.periph_name, + p_env->ccb.cgdl.unit_number); + p_env->cam = cam_open_pass (pass,O_RDWR,NULL); + if (!p_env->cam) return false; + + p_env->gen.init = true; + p_env->b_cam_init = true; + return true; +} + +void +free_freebsd_cam (void *user_data) +{ + _img_private_t *p_env = user_data; + + if (NULL == p_env) return; + + if (p_env->gen.fd > 0) + close (p_env->gen.fd); + p_env->gen.fd = -1; + + if(p_env->cam) + cam_close_device(p_env->cam); + + free (p_env); +} + +static int +_set_bsize (_img_private_t *p_env, unsigned int bsize) +{ + scsi_mmc_cdb_t cdb = {{0, }}; + + struct + { + uint8_t reserved1; + uint8_t medium; + uint8_t reserved2; + uint8_t block_desc_length; + uint8_t density; + uint8_t number_of_blocks_hi; + uint8_t number_of_blocks_med; + uint8_t number_of_blocks_lo; + uint8_t reserved3; + uint8_t block_length_hi; + uint8_t block_length_med; + uint8_t block_length_lo; + } mh; + + memset (&mh, 0, sizeof (mh)); + mh.block_desc_length = 0x08; + mh.block_length_hi = (bsize >> 16) & 0xff; + mh.block_length_med = (bsize >> 8) & 0xff; + mh.block_length_lo = (bsize >> 0) & 0xff; + + memset (&cdb, 0, sizeof (cdb)); + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_MODE_SELECT_6); + + cdb.field[1] = 1 << 4; + cdb.field[4] = 12; + + return run_scsi_cmd_freebsd_cam (p_env, DEFAULT_TIMEOUT_MSECS, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_WRITE, + sizeof(mh), &mh); +} + +int +read_mode2_sector_freebsd_cam (_img_private_t *p_env, void *data, lsn_t lsn, + bool b_form2) +{ + if ( b_form2 ) + return read_mode2_sectors_freebsd_cam(p_env, data, lsn, 1); + else { + /* Need to pick out the data portion from a mode2 form2 frame */ + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + int retval = read_mode2_sectors_freebsd_cam(p_env, buf, lsn, 1); + if ( retval ) return retval; + memcpy (((char *)data), buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE); + return 0; + } +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +int +read_mode2_sectors_freebsd_cam (_img_private_t *p_env, void *p_buf, + lsn_t lsn, unsigned int nblocks) +{ + scsi_mmc_cdb_t cdb = {{0, }}; + + bool b_read_10 = false; + + CDIO_MMC_SET_READ_LBA(cdb.field, lsn); + + if (b_read_10) { + int retval; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_10); + CDIO_MMC_SET_READ_LENGTH16(cdb.field, nblocks); + if ((retval = _set_bsize (p_env, M2RAW_SECTOR_SIZE))) + return retval; + + if ((retval = run_scsi_cmd_freebsd_cam (p_env, 0, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, + SCSI_MMC_DATA_READ, + M2RAW_SECTOR_SIZE * nblocks, + p_buf))) + { + _set_bsize (p_env, CDIO_CD_FRAMESIZE); + return retval; + } + + if ((retval = _set_bsize (p_env, CDIO_CD_FRAMESIZE))) + return retval; + } else + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_CD); + CDIO_MMC_SET_READ_LENGTH24(cdb.field, nblocks); + cdb.field[1] = 0; /* sector size mode2 */ + cdb.field[9] = 0x58; /* 2336 mode2 */ + return run_scsi_cmd_freebsd_cam (p_env, 0, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, + SCSI_MMC_DATA_READ, + M2RAW_SECTOR_SIZE * nblocks, p_buf); + + return 0; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +uint32_t +stat_size_freebsd_cam (_img_private_t *p_env) +{ + scsi_mmc_cdb_t cdb = {{0, }}; + uint8_t buf[12] = { 0, }; + + uint32_t retval; + int i_status; + + /* Operation code */ + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC); + + cdb.field[1] = 0; /* lba; msf: 0x2 */ + + /* Format */ + cdb.field[2] = CDIO_MMC_READTOC_FMT_TOC; + + CDIO_MMC_SET_START_TRACK(cdb.field, CDIO_CDROM_LEADOUT_TRACK); + + CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(buf)); + + p_env->ccb.csio.data_ptr = buf; + p_env->ccb.csio.dxfer_len = sizeof (buf); + + i_status = run_scsi_cmd_freebsd_cam(p_env, DEFAULT_TIMEOUT_MSECS, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + sizeof(buf), buf); + if (0 != i_status) + return 0; + + { + int i; + + retval = 0; + for (i = 8; i < 12; i++) + { + retval <<= 8; + retval += buf[i]; + } + } + + return retval; +} + +/*! + * Eject using SCSI MMC commands. Return 0 if successful. + */ +int +eject_media_freebsd_cam (_img_private_t *p_env) +{ + int i_status; + scsi_mmc_cdb_t cdb = {{0, }}; + uint8_t buf[1]; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_ALLOW_MEDIUM_REMOVAL); + + i_status = run_scsi_cmd_freebsd_cam (p_env, DEFAULT_TIMEOUT_MSECS, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_WRITE, 0, &buf); + if (0 != i_status) + return i_status; + + cdb.field[4] = 1; + i_status = run_scsi_cmd_freebsd_cam (p_env, DEFAULT_TIMEOUT_MSECS, + scsi_mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_WRITE, 0, &buf); + if (0 != i_status) + return i_status; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_START_STOP); + cdb.field[4] = 2; /* eject */ + + return run_scsi_cmd_freebsd_cam (p_env, DEFAULT_TIMEOUT_MSECS, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, + SCSI_MMC_DATA_WRITE, 0, &buf); +} + +#endif /* HAVE_FREEBSD_CDROM */ diff --git a/contrib/libcdio/FreeBSD/freebsd_ioctl.c b/contrib/libcdio/FreeBSD/freebsd_ioctl.c new file mode 100644 index 000000000..be9835c95 --- /dev/null +++ b/contrib/libcdio/FreeBSD/freebsd_ioctl.c @@ -0,0 +1,267 @@ +/* + $Id: freebsd_ioctl.c,v 1.1 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file contains FreeBSD-specific code and implements low-level + control of the CD drive. Culled initially I think from xine's or + mplayer's FreeBSD code with lots of modifications. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: freebsd_ioctl.c,v 1.1 2005/01/01 02:43:57 rockyb Exp $"; + +#ifdef HAVE_FREEBSD_CDROM + +#include "freebsd.h" + +/* Check a drive to see if it is a CD-ROM + Return 1 if a CD-ROM. 0 if it exists but isn't a CD-ROM drive + and -1 if no device exists . +*/ +bool +cdio_is_cdrom_freebsd_ioctl(char *drive, char *mnttype) +{ + bool is_cd=false; + int cdfd; + struct ioc_toc_header tochdr; + + /* If it doesn't exist, return -1 */ + if ( !cdio_is_device_quiet_generic(drive) ) { + return(false); + } + + /* If it does exist, verify that it's an available CD-ROM */ + cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0); + + /* Should we want to test the condition in more detail: + ENOENT is the error for /dev/xxxxx does not exist; + ENODEV means there's no drive present. */ + + if ( cdfd >= 0 ) { + if ( ioctl(cdfd, CDIOREADTOCHEADER, &tochdr) != -1 ) { + is_cd = true; + } + close(cdfd); + } + /* Even if we can't read it, it might be mounted */ + else if ( mnttype && (strcmp(mnttype, "iso9660") == 0) ) { + is_cd = true; + } + return(is_cd); +} + +/*! + Reads a single mode2 sector from cd device into data starting from lsn. + Returns 0 if no error. + */ +int +read_audio_sectors_freebsd_ioctl (_img_private_t *_obj, void *data, lsn_t lsn, + unsigned int nblocks) +{ + unsigned char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + struct ioc_read_audio cdda; + + cdda.address.lba = lsn; + cdda.buffer = buf; + cdda.nframes = nblocks; + cdda.address_format = CDIO_CDROM_LBA; + + /* read a frame */ + if(ioctl(_obj->gen.fd, CDIOCREADAUDIO, &cdda) < 0) { + perror("CDIOCREADAUDIO"); + return 1; + } + memcpy (data, buf, CDIO_CD_FRAMESIZE_RAW); + + return 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +int +read_mode2_sector_freebsd_ioctl (_img_private_t *env, void *data, lsn_t lsn, + bool b_form2) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + int retval; + + if ( !b_form2 ) + return cdio_generic_read_form1_sector (env, buf, lsn); + + if ( (retval = read_audio_sectors_freebsd_ioctl (env, buf, lsn, 1)) ) + return retval; + + memcpy (data, buf + CDIO_CD_XA_SYNC_HEADER, M2RAW_SECTOR_SIZE); + + return 0; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +uint32_t +stat_size_freebsd_ioctl (_img_private_t *_obj) +{ + struct ioc_read_toc_single_entry tocent; + uint32_t size; + + tocent.track = CDIO_CDROM_LEADOUT_TRACK; + tocent.address_format = CDIO_CDROM_LBA; + if (ioctl (_obj->gen.fd, CDIOREADTOCENTRY, &tocent) == -1) + { + perror ("ioctl(CDROMREADTOCENTRY)"); + exit (EXIT_FAILURE); + } + + size = tocent.entry.addr.lba; + + return size; +} + +/*! + Eject media. Return 1 if successful, 0 otherwise. + */ +int +eject_media_freebsd_ioctl (_img_private_t *env) +{ + _img_private_t *_obj = env; + int ret=2; + int fd; + + if ((fd = open(_obj->gen.source_name, O_RDONLY|O_NONBLOCK)) > -1) { + ret = 1; + if (ioctl(fd, CDIOCALLOW) == -1) { + cdio_warn("ioctl(fd, CDIOCALLOW) failed: %s\n", strerror(errno)); + } else if (ioctl(fd, CDIOCEJECT) == -1) { + cdio_warn("ioctl(CDIOCEJECT) failed: %s\n", strerror(errno)); + } else { + ret = 0; + } + close(fd); + } + + return ret; +} + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + FIXME: This is just a guess. + + */ +char * +get_mcn_freebsd_ioctl (const _img_private_t *env) { + + struct ioc_read_subchannel subchannel; + struct cd_sub_channel_info subchannel_info; + + subchannel.address_format = CDIO_CDROM_MSF; + subchannel.data_format = CDIO_SUBCHANNEL_MEDIA_CATALOG; + subchannel.track = 0; + subchannel.data_len = sizeof(subchannel_info); + subchannel.data = &subchannel_info; + + if(ioctl(env->gen.fd, CDIOCREADSUBCHANNEL, &subchannel) < 0) { + perror("CDIOCREADSUBCHANNEL"); + return NULL; + } + + /* Probably need a loop over tracks rather than give up if we + can't find in track 0. + */ + if (subchannel_info.what.media_catalog.mc_valid) + return strdup(subchannel_info.what.media_catalog.mc_number); + else + return NULL; +} + +/*! + Get format of track. + + FIXME: We're just guessing this from the GNU/Linux code. + +*/ +track_format_t +get_track_format_freebsd_ioctl(const _img_private_t *env, track_t i_track) +{ + struct ioc_read_subchannel subchannel; + struct cd_sub_channel_info subchannel_info; + + subchannel.address_format = CDIO_CDROM_LBA; + subchannel.data_format = CDIO_SUBCHANNEL_CURRENT_POSITION; + subchannel.track = i_track; + subchannel.data_len = 1; + subchannel.data = &subchannel_info; + + if(ioctl(env->gen.fd, CDIOCREADSUBCHANNEL, &subchannel) < 0) { + perror("CDIOCREADSUBCHANNEL"); + return 1; + } + + if (subchannel_info.what.position.control == 0x04) { + if (subchannel_info.what.position.data_format == 0x10) + return TRACK_FORMAT_CDI; + else if (subchannel_info.what.position.data_format == 0x20) + return TRACK_FORMAT_XA; + else + return TRACK_FORMAT_DATA; + } else + return TRACK_FORMAT_AUDIO; +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +bool +get_track_green_freebsd_ioctl(const _img_private_t *env, track_t i_track) +{ + struct ioc_read_subchannel subchannel; + struct cd_sub_channel_info subchannel_info; + + subchannel.address_format = CDIO_CDROM_LBA; + subchannel.data_format = CDIO_SUBCHANNEL_CURRENT_POSITION; + subchannel.track = i_track; + subchannel.data_len = 1; + subchannel.data = &subchannel_info; + + if(ioctl(env->gen.fd, CDIOCREADSUBCHANNEL, &subchannel) < 0) { + perror("CDIOCREADSUBCHANNEL"); + return 1; + } + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cdinfo for a while. + */ + return (subchannel_info.what.position.control & 2) != 0; +} + +#endif /* HAVE_FREEBSD_CDROM */ diff --git a/contrib/libcdio/MSWindows/Makefile.am b/contrib/libcdio/MSWindows/Makefile.am new file mode 100644 index 000000000..61089cf69 --- /dev/null +++ b/contrib/libcdio/MSWindows/Makefile.am @@ -0,0 +1,3 @@ +include $(top_srcdir)/misc/Makefile.common + +EXTRA_DIST = aspi32.c aspi32.h win32.c win32.h win32_ioctl.c diff --git a/contrib/libcdio/MSWindows/aspi32.c b/contrib/libcdio/MSWindows/aspi32.c new file mode 100644 index 000000000..238a4b4e9 --- /dev/null +++ b/contrib/libcdio/MSWindows/aspi32.c @@ -0,0 +1,805 @@ +/* + $Id: aspi32.c,v 1.2 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file contains Win32-specific code and implements low-level + control of the CD drive via the ASPI API. + Inspired by vlc's cdrom.h code +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: aspi32.c,v 1.2 2005/01/01 02:43:58 rockyb Exp $"; + +#include <cdio/cdio.h> +#include <cdio/sector.h> +#include <cdio/util.h> +#include <cdio/scsi_mmc.h> +#include "cdio_assert.h" + +#include <string.h> + +#ifdef HAVE_WIN32_CDROM + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#include <windows.h> +#include "win32.h" + +#include <sys/stat.h> +#include <sys/types.h> +#include "aspi32.h" +#include "cdtext_private.h" + +/* Amount of time we are willing to wait for an operation to complete. + 10 seconds? +*/ +#define OP_TIMEOUT_MS 10000 + +static const +char *aspierror(int nErrorCode) +{ + switch (nErrorCode) + { + case SS_PENDING: + return "SRB being processed"; + break; + case SS_COMP: + return "SRB completed without error"; + break; + case SS_ABORTED: + return "SRB aborted"; + break; + case SS_ABORT_FAIL: + return "Unable to abort SRB"; + break; + case SS_ERR: + return "SRB completed with error"; + break; + case SS_INVALID_CMD: + return "Invalid ASPI command"; + break; + case SS_INVALID_HA: + return "Invalid host adapter number"; + break; + case SS_NO_DEVICE: + return "SCSI device not installed"; + break; + case SS_INVALID_SRB: + return "Invalid parameter set in SRB"; + break; + case SS_OLD_MANAGER: + return "ASPI manager doesn't support"; + break; + case SS_ILLEGAL_MODE: + return "Unsupported MS Windows mode"; + break; + case SS_NO_ASPI: + return "No ASPI managers"; + break; + case SS_FAILED_INIT: + return "ASPI for windows failed init"; + break; + case SS_ASPI_IS_BUSY: + return "No resources available to execute command."; + break; + case SS_BUFFER_TOO_BIG: + return "Buffer size is too big to handle."; + break; + case SS_MISMATCHED_COMPONENTS: + return "The DLLs/EXEs of ASPI don't version check"; + break; + case SS_NO_ADAPTERS: + return "No host adapters found"; + break; + case SS_INSUFFICIENT_RESOURCES: + return "Couldn't allocate resources needed to init"; + break; + case SS_ASPI_IS_SHUTDOWN: + return "Call came to ASPI after PROCESS_DETACH"; + break; + case SS_BAD_INSTALL: + return "The DLL or other components are installed wrong."; + break; + default: + return "Unknown ASPI error."; + } +} + +/* General ioctl() CD-ROM command function */ +static bool +mciSendCommand_aspi(int id, UINT msg, DWORD flags, void *arg) +{ + MCIERROR mci_error; + + mci_error = mciSendCommand(id, msg, flags, (DWORD)arg); + if ( mci_error ) { + char error[256]; + + mciGetErrorString(mci_error, error, 256); + cdio_warn("mciSendCommand() error: %s", error); + } + return(mci_error == 0); +} + +/* + See if the ASPI DLL is loadable. If so pointers are returned + and we return true. Return false if there was a problem. + */ +static bool +have_aspi( HMODULE *hASPI, + long (**lpGetSupport)( void ), + long (**lpSendCommand)( void* ) ) +{ + /* check if aspi is available */ + *hASPI = LoadLibrary( "wnaspi32.dll" ); + + if( *hASPI == NULL ) { + cdio_debug("Unable to load ASPI DLL"); + return false; + } + + (FARPROC) *lpGetSupport = GetProcAddress( *hASPI, + "GetASPI32SupportInfo" ); + (FARPROC) *lpSendCommand = GetProcAddress( *hASPI, + "SendASPI32Command" ); + + /* make sure that we've got both function addresses */ + if( *lpGetSupport == NULL || *lpSendCommand == NULL ) { + cdio_debug("Unable to get ASPI function pointers"); + FreeLibrary( *hASPI ); + return false; + } + + return true; +} + +/*! + Get disc type associated with cd object. +*/ +discmode_t +get_discmode_aspi (_img_private_t *p_env) +{ + track_t i_track; + discmode_t discmode=CDIO_DISC_MODE_NO_INFO; + + /* See if this is a DVD. */ + cdio_dvd_struct_t dvd; /* DVD READ STRUCT for layer 0. */ + + dvd.physical.type = CDIO_DVD_STRUCT_PHYSICAL; + dvd.physical.layer_num = 0; + if (0 == scsi_mmc_get_dvd_struct_physical_private (p_env, + &run_scsi_cmd_aspi, + &dvd)) { + switch(dvd.physical.layer[0].book_type) { + case CDIO_DVD_BOOK_DVD_ROM: return CDIO_DISC_MODE_DVD_ROM; + case CDIO_DVD_BOOK_DVD_RAM: return CDIO_DISC_MODE_DVD_RAM; + case CDIO_DVD_BOOK_DVD_R: return CDIO_DISC_MODE_DVD_R; + case CDIO_DVD_BOOK_DVD_RW: return CDIO_DISC_MODE_DVD_RW; + case CDIO_DVD_BOOK_DVD_PR: return CDIO_DISC_MODE_DVD_PR; + case CDIO_DVD_BOOK_DVD_PRW: return CDIO_DISC_MODE_DVD_PRW; + default: return CDIO_DISC_MODE_DVD_OTHER; + } + } + + if (!p_env->gen.toc_init) + read_toc_aspi (p_env); + + if (!p_env->gen.toc_init) + return CDIO_DISC_MODE_NO_INFO; + + for (i_track = p_env->gen.i_first_track; + i_track < p_env->gen.i_first_track + p_env->gen.i_tracks ; + i_track ++) { + track_format_t track_fmt=get_track_format_aspi(p_env, i_track); + + switch(track_fmt) { + case TRACK_FORMAT_AUDIO: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DA; + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_XA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_DATA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DATA; + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_ERROR: + default: + discmode = CDIO_DISC_MODE_ERROR; + } + } + return discmode; +} + +const char * +is_cdrom_aspi(const char drive_letter) +{ + static char psz_win32_drive[7]; + HMODULE hASPI = NULL; + long (*lpGetSupport)( void ) = NULL; + long (*lpSendCommand)( void* ) = NULL; + DWORD dwSupportInfo; + int i_adapter, i_hostadapters; + char c_drive; + int i_rc; + + if ( !have_aspi(&hASPI, &lpGetSupport, &lpSendCommand) ) + return NULL; + + /* ASPI support seems to be there. */ + + dwSupportInfo = lpGetSupport(); + + i_rc = HIBYTE( LOWORD ( dwSupportInfo ) ); + + if( SS_COMP != i_rc ) { + cdio_debug("ASPI: %s", aspierror(i_rc)); + FreeLibrary( hASPI ); + return NULL; + } + + i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) ); + if( i_hostadapters == 0 ) { + FreeLibrary( hASPI ); + return NULL; + } + + c_drive = toupper(drive_letter) - 'A'; + + for( i_adapter = 0; i_adapter < i_hostadapters; i_adapter++ ) { + struct SRB_GetDiskInfo srbDiskInfo; + int i_target; + SRB_HAInquiry srbInquiry; + + srbInquiry.SRB_Cmd = SC_HA_INQUIRY; + srbInquiry.SRB_HaId = i_adapter; + + lpSendCommand( (void*) &srbInquiry ); + + if( srbInquiry.SRB_Status != SS_COMP ) continue; + if( !srbInquiry.HA_Unique[3]) srbInquiry.HA_Unique[3]=8; + + for(i_target=0; i_target < srbInquiry.HA_Unique[3]; i_target++) + { + int i_lun; + for( i_lun=0; i_lun<8; i_lun++) + { + srbDiskInfo.SRB_Cmd = SC_GET_DISK_INFO; + srbDiskInfo.SRB_Flags = 0; + srbDiskInfo.SRB_Hdr_Rsvd = 0; + srbDiskInfo.SRB_HaId = i_adapter; + srbDiskInfo.SRB_Target = i_target; + srbDiskInfo.SRB_Lun = i_lun; + + lpSendCommand( (void*) &srbDiskInfo ); + + if( (srbDiskInfo.SRB_Status == SS_COMP) && + (srbDiskInfo.SRB_Int13HDriveInfo == c_drive) ) { + /* Make sure this is a CD-ROM device. */ + struct SRB_GDEVBlock srbGDEVBlock; + + memset( &srbGDEVBlock, 0, sizeof(struct SRB_GDEVBlock) ); + srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE; + srbDiskInfo.SRB_HaId = i_adapter; + srbGDEVBlock.SRB_Target = i_target; + srbGDEVBlock.SRB_Lun = i_lun; + + lpSendCommand( (void*) &srbGDEVBlock ); + + if( ( srbGDEVBlock.SRB_Status == SS_COMP ) && + ( srbGDEVBlock.SRB_DeviceType == DTYPE_CDROM ) ) { + sprintf( psz_win32_drive, "%c:", drive_letter ); + FreeLibrary( hASPI ); + return(psz_win32_drive); + } + } + } + } + } + FreeLibrary( hASPI ); + return NULL; +} + +/*! + Initialize CD device. + */ +bool +init_aspi (_img_private_t *env) +{ + HMODULE hASPI = NULL; + long (*lpGetSupport)( void ) = NULL; + long (*lpSendCommand)( void* ) = NULL; + DWORD dwSupportInfo; + int i_adapter, i_hostadapters; + char c_drive; + int i_rc; + + if (2 == strlen(env->gen.source_name) && isalpha(env->gen.source_name[0]) ) + { + c_drive = env->gen.source_name[0]; + } else if ( 6 == strlen(env->gen.source_name) + && isalpha(env->gen.source_name[4] )) { + c_drive = env->gen.source_name[4]; + } else { + c_drive = 'C'; + } + + if ( !have_aspi(&hASPI, &lpGetSupport, &lpSendCommand) ) + return false; + + /* ASPI support seems to be there. */ + + dwSupportInfo = lpGetSupport(); + + i_rc = HIBYTE( LOWORD ( dwSupportInfo ) ); + + if( SS_COMP != i_rc ) { + cdio_info("ASPI: %s", aspierror(i_rc)); + FreeLibrary( hASPI ); + return false; + } + + i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) ); + if( i_hostadapters == 0 ) { + FreeLibrary( hASPI ); + return false; + } + + c_drive = toupper(c_drive) - 'A'; + + for( i_adapter = 0; i_adapter < i_hostadapters; i_adapter++ ) { + struct SRB_GetDiskInfo srbDiskInfo; + int i_target; + SRB_HAInquiry srbInquiry; + + srbInquiry.SRB_Cmd = SC_HA_INQUIRY; + srbInquiry.SRB_HaId = i_adapter; + + lpSendCommand( (void*) &srbInquiry ); + + if( srbInquiry.SRB_Status != SS_COMP ) continue; + if( !srbInquiry.HA_Unique[3]) srbInquiry.HA_Unique[3]=8; + + for(i_target=0; i_target < srbInquiry.HA_Unique[3]; i_target++) + { + int i_lun; + for (i_lun = 0; i_lun < 8; i_lun++ ) { + srbDiskInfo.SRB_Cmd = SC_GET_DISK_INFO; + srbDiskInfo.SRB_Flags = 0; + srbDiskInfo.SRB_Hdr_Rsvd = 0; + srbDiskInfo.SRB_HaId = i_adapter; + srbDiskInfo.SRB_Target = i_target; + srbDiskInfo.SRB_Lun = i_lun; + + lpSendCommand( (void*) &srbDiskInfo ); + + if( (srbDiskInfo.SRB_Status == SS_COMP) ) { + + if (srbDiskInfo.SRB_Int13HDriveInfo != c_drive) + { + continue; + } else { + /* Make sure this is a CD-ROM device. */ + struct SRB_GDEVBlock srbGDEVBlock; + + memset( &srbGDEVBlock, 0, sizeof(struct SRB_GDEVBlock) ); + srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE; + srbGDEVBlock.SRB_HaId = i_adapter; + srbGDEVBlock.SRB_Target = i_target; + + lpSendCommand( (void*) &srbGDEVBlock ); + + if( ( srbGDEVBlock.SRB_Status == SS_COMP ) && + ( srbGDEVBlock.SRB_DeviceType == DTYPE_CDROM ) ) { + env->i_sid = MAKEWORD( i_adapter, i_target ); + env->hASPI = (long)hASPI; + env->lpSendCommand = lpSendCommand; + env->b_aspi_init = true; + env->i_lun = i_lun; + cdio_debug("Using ASPI layer"); + + return true; + } else { + FreeLibrary( hASPI ); + cdio_debug( "%c: is not a CD-ROM drive", + env->gen.source_name[0] ); + return false; + } + } + } + } + } + } + + FreeLibrary( hASPI ); + cdio_debug( "Unable to get HaId and target (ASPI)" ); + return false; +} + +/*! + Run a SCSI MMC command. + + env private CD structure + i_timeout_ms time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + We return 0 if command completed successfully. + */ +int +run_scsi_cmd_aspi( const void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const scsi_mmc_cdb_t * p_cdb, + scsi_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + HANDLE hEvent; + struct SRB_ExecSCSICmd ssc; + + /* Create the transfer completion event */ + hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + if( hEvent == NULL ) { + cdio_info("CreateEvent failed"); + return 1; + } + + memset( &ssc, 0, sizeof( ssc ) ); + + ssc.SRB_Cmd = SC_EXEC_SCSI_CMD; + + ssc.SRB_Flags = SCSI_MMC_DATA_READ == e_direction ? + SRB_DIR_IN | SRB_EVENT_NOTIFY : SRB_DIR_OUT | SRB_EVENT_NOTIFY; + + ssc.SRB_HaId = LOBYTE( p_env->i_sid ); + ssc.SRB_Target = HIBYTE( p_env->i_sid ); + ssc.SRB_Lun = p_env->i_lun; + ssc.SRB_SenseLen = SENSE_LEN; + + ssc.SRB_PostProc = (LPVOID) hEvent; + ssc.SRB_CDBLen = i_cdb; + + /* Result buffer */ + ssc.SRB_BufPointer = p_buf; + ssc.SRB_BufLen = i_buf; + + memcpy( ssc.CDBByte, p_cdb, i_cdb ); + + ResetEvent( hEvent ); + p_env->lpSendCommand( (void*) &ssc ); + + /* If the command has still not been processed, wait until it's + * finished */ + if( ssc.SRB_Status == SS_PENDING ) { + WaitForSingleObject( hEvent, msecs2secs(i_timeout_ms) ); + } + CloseHandle( hEvent ); + + /* check that the transfer went as planned */ + if( ssc.SRB_Status != SS_COMP ) { + cdio_info("ASPI: %s", aspierror(ssc.SRB_Status)); + return 2; + } + + return 0; +} + + +/*! + Reads nblocks sectors from cd device into data starting from lsn. + Returns 0 if no error. + */ +static int +read_sectors_aspi (const _img_private_t *env, void *data, lsn_t lsn, + int sector_type, unsigned int nblocks) +{ + scsi_mmc_cdb_t cdb = {{0, }}; + unsigned int i_buf; + + int sync = 0; + int header_code = 2; + int i_user_data = 1; + int edc_ecc = 0; + int error_field = 0; + +#if 0 + sector_type = 0; /*all types */ +#endif + + /* Set up passthrough command */ + CDIO_MMC_SET_COMMAND (cdb.field, CDIO_MMC_GPCMD_READ_CD); + CDIO_MMC_SET_READ_TYPE (cdb.field, sector_type); + CDIO_MMC_SET_READ_LBA (cdb.field, lsn); + CDIO_MMC_SET_READ_LENGTH24(cdb.field, nblocks); + +#if 1 + cdb.field[ 9 ] = (sync << 7) | + (header_code << 5) | + (i_user_data << 4) | + (edc_ecc << 3) | + (error_field << 1); + /* ssc.CDBByte[ 9 ] = READ_CD_USERDATA_MODE2; */ +#else + CDIO_MMC_SET_MAIN_CHANNEL_SELECTION_BITS(cmd, + CDIO_MMC_MCSB_ALL_HEADERS); +#endif + + switch (sector_type) { + case CDIO_MMC_READ_TYPE_ANY: + case CDIO_MMC_READ_TYPE_CDDA: + i_buf = CDIO_CD_FRAMESIZE_RAW; + break; + case CDIO_MMC_READ_TYPE_M2F1: + i_buf = CDIO_CD_FRAMESIZE; + break; + case CDIO_MMC_READ_TYPE_M2F2: + i_buf = 2324; + break; + case CDIO_MMC_READ_TYPE_MODE1: + i_buf = CDIO_CD_FRAMESIZE; + break; + default: + i_buf = CDIO_CD_FRAMESIZE_RAW; + } + + return run_scsi_cmd_aspi(env, OP_TIMEOUT_MS, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, i_buf*nblocks, data); +} + +/*! + Reads an audio device into data starting from lsn. + Returns 0 if no error. + */ +int +read_audio_sectors_aspi (_img_private_t *env, void *data, lsn_t lsn, + unsigned int nblocks) +{ + if (read_sectors_aspi(env, data, lsn, CDIO_MMC_READ_TYPE_CDDA, 1)) { + return read_sectors_aspi(env, data, lsn, CDIO_MMC_READ_TYPE_ANY, 1); + } + return 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +int +read_mode2_sector_aspi (const _img_private_t *env, void *data, lsn_t lsn, + bool b_form2) +{ + return read_sectors_aspi(env, data, lsn, b_form2 + ? CDIO_MMC_READ_TYPE_M2F2 + : CDIO_MMC_READ_TYPE_M2F1, + 1); +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +int +read_mode1_sector_aspi (const _img_private_t *env, void *data, lsn_t lsn, + bool b_form2) +{ + return read_sectors_aspi(env, data, lsn, CDIO_MMC_READ_TYPE_MODE1, 1); +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return true if successful or false if an error. +*/ +bool +read_toc_aspi (_img_private_t *p_env) +{ + scsi_mmc_cdb_t cdb = {{0, }}; + unsigned char tocheader[ 4 ]; + int i_status; + + /* Operation code */ + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC); + + /* Format */ + cdb.field[ 2 ] = CDIO_MMC_READTOC_FMT_TOC; + + /* Starting track */ + CDIO_MMC_SET_START_TRACK(cdb.field, 0); + + CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(tocheader)); + + i_status = run_scsi_cmd_aspi (p_env, OP_TIMEOUT_MS, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + sizeof(tocheader), &tocheader); + + if (0 != i_status) return false; + + p_env->gen.i_first_track = tocheader[2]; + p_env->gen.i_tracks = tocheader[3] - tocheader[2] + 1; + + { + int i, i_toclength; + unsigned char *p_fulltoc; + + i_toclength = 4 /* header */ + tocheader[0] + + ((unsigned int) tocheader[1] << 8); + + p_fulltoc = malloc( i_toclength ); + + if( p_fulltoc == NULL ) { + cdio_error( "out of memory" ); + return false; + } + + CDIO_MMC_SET_READ_LENGTH16(cdb.field, i_toclength); + + i_status = run_scsi_cmd_aspi (p_env, OP_TIMEOUT_MS, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + i_toclength, p_fulltoc); + if( 0 != i_status ) { + p_env->gen.i_tracks = 0; + } + + for( i = 0 ; i <= p_env->gen.i_tracks ; i++ ) { + int i_index = 8 + 8 * i; + p_env->tocent[ i ].start_lsn = ((int)p_fulltoc[ i_index ] << 24) + + ((int)p_fulltoc[ i_index+1 ] << 16) + + ((int)p_fulltoc[ i_index+2 ] << 8) + + (int)p_fulltoc[ i_index+3 ]; + p_env->tocent[ i ].Control = (UCHAR)p_fulltoc[ 1 + 8 * i ]; + + cdio_debug( "p_sectors: %i %lu", + i, (unsigned long int) p_env->tocent[i].start_lsn ); + } + + free( p_fulltoc ); + } + + p_env->gen.toc_init = true; + return true; +} + +/* Eject media will eventually get removed from _cdio_win32.c */ +#if 0 +/*! + Eject media. Return 1 if successful, 0 otherwise. + */ +int +wnaspi32_eject_media (void *user_data) { + + _img_private_t *env = user_data; + + + MCI_OPEN_PARMS op; + MCI_STATUS_PARMS st; + DWORD i_flags; + char psz_drive[4]; + int ret; + + memset( &op, 0, sizeof(MCI_OPEN_PARMS) ); + op.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO; + strcpy( psz_drive, "X:" ); + psz_drive[0] = env->gen.source_name[0]; + op.lpstrElementName = psz_drive; + + /* Set the flags for the device type */ + i_flags = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | + MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE; + + if( mciSendCommand_aspi( 0, MCI_OPEN, i_flags, &op ) ) { + st.dwItem = MCI_STATUS_READY; + /* Eject disc */ + ret = mciSendCommand_aspi( op.wDeviceID, MCI_SET, + MCI_SET_DOOR_OPEN, 0 ) != 0; + /* Release access to the device */ + mciSendCommand_aspi( op.wDeviceID, MCI_CLOSE, MCI_WAIT, 0 ); + } else + ret = 0; + + return ret; +} +#endif + +/*! + Get format of track. +*/ +track_format_t +get_track_format_aspi(const _img_private_t *p_env, track_t track_num) +{ + MCI_OPEN_PARMS op; + MCI_STATUS_PARMS st; + DWORD i_flags; + int ret; + + memset( &op, 0, sizeof(MCI_OPEN_PARMS) ); + op.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO; + op.lpstrElementName = p_env->gen.source_name; + + /* Set the flags for the device type */ + i_flags = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | + MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE; + + if( mciSendCommand_aspi( 0, MCI_OPEN, i_flags, &op ) ) { + st.dwItem = MCI_CDA_STATUS_TYPE_TRACK; + st.dwTrack = track_num; + i_flags = MCI_TRACK | MCI_STATUS_ITEM ; + ret = mciSendCommand_aspi( op.wDeviceID, MCI_STATUS, i_flags, &st ); + + /* Release access to the device */ + mciSendCommand_aspi( op.wDeviceID, MCI_CLOSE, MCI_WAIT, 0 ); + + switch(st.dwReturn) { + case MCI_CDA_TRACK_AUDIO: + return TRACK_FORMAT_AUDIO; + case MCI_CDA_TRACK_OTHER: + return TRACK_FORMAT_DATA; + default: + return TRACK_FORMAT_XA; + } + } + return TRACK_FORMAT_ERROR; +} + +#endif /* HAVE_WIN32_CDROM */ diff --git a/contrib/libcdio/MSWindows/aspi32.h b/contrib/libcdio/MSWindows/aspi32.h new file mode 100644 index 000000000..8742c0794 --- /dev/null +++ b/contrib/libcdio/MSWindows/aspi32.h @@ -0,0 +1,249 @@ +/* Win32 aspi specific */ +/* + $Id: aspi32.h,v 1.2 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#define ASPI_HAID 0 +#define ASPI_TARGET 0 +#define DTYPE_CDROM 0x05 + +#define SENSE_LEN 0x0E +#define SC_HA_INQUIRY 0x00 +#define SC_GET_DEV_TYPE 0x01 +#define SC_EXEC_SCSI_CMD 0x02 +#define SC_GET_DISK_INFO 0x06 + +//***************************************************************************** +// %%% SRB Status %%% +//***************************************************************************** + +#define SS_PENDING 0x00 // SRB being processed +#define SS_COMP 0x01 // SRB completed without error +#define SS_ABORTED 0x02 // SRB aborted +#define SS_ABORT_FAIL 0x03 // Unable to abort SRB +#define SS_ERR 0x04 // SRB completed with error + +#define SS_INVALID_CMD 0x80 // Invalid ASPI command +#define SS_INVALID_HA 0x81 // Invalid host adapter number +#define SS_NO_DEVICE 0x82 // SCSI device not installed + +#define SS_INVALID_SRB 0xE0 // Invalid parameter set in SRB +#define SS_OLD_MANAGER 0xE1 // ASPI manager doesn't support Windows +#define SS_BUFFER_ALIGN 0xE1 // Buffer not aligned (replaces + // OLD_MANAGER in Win32) +#define SS_ILLEGAL_MODE 0xE2 // Unsupported Windows mode +#define SS_NO_ASPI 0xE3 // No ASPI managers resident +#define SS_FAILED_INIT 0xE4 // ASPI for windows failed init +#define SS_ASPI_IS_BUSY 0xE5 // No resources available to execute + // cmd +#define SS_BUFFER_TOO_BIG 0xE6 // Buffer size to big to handle! +#define SS_MISMATCHED_COMPONENTS 0xE7 // The DLLs/EXEs of ASPI don't version + // check +#define SS_NO_ADAPTERS 0xE8 // No host adapters to manage +#define SS_INSUFFICIENT_RESOURCES 0xE9 // Couldn't allocate resources needed + // to init +#define SS_ASPI_IS_SHUTDOWN 0xEA // Call came to ASPI after + // PROCESS_DETACH +#define SS_BAD_INSTALL 0xEB // The DLL or other components are installed wrong + +//***************************************************************************** +// %%% Host Adapter Status %%% +//***************************************************************************** + +#define HASTAT_OK 0x00 // Host adapter did not detect an + // error +#define HASTAT_SEL_TO 0x11 // Selection Timeout +#define HASTAT_DO_DU 0x12 // Data overrun data underrun +#define HASTAT_BUS_FREE 0x13 // Unexpected bus free +#define HASTAT_PHASE_ERR 0x14 // Target bus phase sequence + // failure +#define HASTAT_TIMEOUT 0x09 // Timed out while SRB was + // waiting to beprocessed. +#define HASTAT_COMMAND_TIMEOUT 0x0B // Adapter timed out processing SRB. +#define HASTAT_MESSAGE_REJECT 0x0D // While processing SRB, the + // adapter received a MESSAGE +#define HASTAT_BUS_RESET 0x0E // A bus reset was detected. +#define HASTAT_PARITY_ERROR 0x0F // A parity error was detected. +#define HASTAT_REQUEST_SENSE_FAILED 0x10 // The adapter failed in issuing +#define SS_NO_ADAPTERS 0xE8 +#define SRB_DIR_IN 0x08 +#define SRB_DIR_OUT 0x10 +#define SRB_EVENT_NOTIFY 0x40 + +#define SECTOR_TYPE_MODE2 0x14 +#define READ_CD_USERDATA_MODE2 0x10 + +#define READ_TOC 0x43 +#define READ_TOC_FORMAT_TOC 0x0 + +#pragma pack(1) + +struct SRB_GetDiskInfo +{ + unsigned char SRB_Cmd; + unsigned char SRB_Status; + unsigned char SRB_HaId; + unsigned char SRB_Flags; + unsigned long SRB_Hdr_Rsvd; + unsigned char SRB_Target; + unsigned char SRB_Lun; + unsigned char SRB_DriveFlags; + unsigned char SRB_Int13HDriveInfo; + unsigned char SRB_Heads; + unsigned char SRB_Sectors; + unsigned char SRB_Rsvd1[22]; +}; + +struct SRB_GDEVBlock +{ + unsigned char SRB_Cmd; + unsigned char SRB_Status; + unsigned char SRB_HaId; + unsigned char SRB_Flags; + unsigned long SRB_Hdr_Rsvd; + unsigned char SRB_Target; + unsigned char SRB_Lun; + unsigned char SRB_DeviceType; + unsigned char SRB_Rsvd1; +}; + +struct SRB_ExecSCSICmd +{ + unsigned char SRB_Cmd; + unsigned char SRB_Status; + unsigned char SRB_HaId; + unsigned char SRB_Flags; + unsigned long SRB_Hdr_Rsvd; + unsigned char SRB_Target; + unsigned char SRB_Lun; + unsigned short SRB_Rsvd1; + unsigned long SRB_BufLen; + unsigned char *SRB_BufPointer; + unsigned char SRB_SenseLen; + unsigned char SRB_CDBLen; + unsigned char SRB_HaStat; + unsigned char SRB_TargStat; + unsigned long *SRB_PostProc; + unsigned char SRB_Rsvd2[20]; + unsigned char CDBByte[16]; + unsigned char SenseArea[SENSE_LEN+2]; +}; + +/***************************************************************************** + %%% SRB - HOST ADAPTER INQUIRY - SC_HA_INQUIRY (0) %%% +*****************************************************************************/ + +typedef struct // Offset +{ // HX/DEC + BYTE SRB_Cmd; // 00/000 ASPI command code = SC_HA_INQUIRY + BYTE SRB_Status; // 01/001 ASPI command status byte + BYTE SRB_HaId; // 02/002 ASPI host adapter number + BYTE SRB_Flags; // 03/003 ASPI request flags + DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0 + BYTE HA_Count; // 08/008 Number of host adapters present + BYTE HA_SCSI_ID; // 09/009 SCSI ID of host adapter + BYTE HA_ManagerId[16]; // 0A/010 String describing the manager + BYTE HA_Identifier[16]; // 1A/026 String describing the host adapter + BYTE HA_Unique[16]; // 2A/042 Host Adapter Unique parameters + WORD HA_Rsvd1; // 3A/058 Reserved, MUST = 0 +} +SRB_HAInquiry; + +/*! + Get disc type associated with cd object. +*/ +discmode_t get_discmode_aspi (_img_private_t *p_env); + +/*! + Return the the kind of drive capabilities of device. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +char * get_mcn_aspi (const _img_private_t *env); + +/*! + Get the format (XA, DATA, AUDIO) of a track. +*/ +track_format_t get_track_format_aspi(const _img_private_t *env, + track_t i_track); + +/*! + Initialize internal structures for CD device. + */ +bool init_aspi (_img_private_t *env); + +/* + Read cdtext information for a CdIo object . + + return true on success, false on error or CD-TEXT information does + not exist. +*/ +bool init_cdtext_aspi (_img_private_t *env); + +const char *is_cdrom_aspi(const char drive_letter); + +/*! + Reads an audio device using the DeviceIoControl method into data + starting from lsn. Returns 0 if no error. + */ +int read_audio_sectors_aspi (_img_private_t *obj, void *data, lsn_t lsn, + unsigned int nblocks); +/*! + Reads a single mode1 sector using the DeviceIoControl method into + data starting from lsn. Returns 0 if no error. + */ +int read_mode1_sector_aspi (const _img_private_t *env, void *data, + lsn_t lsn, bool b_form2); +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +int read_mode2_sector_aspi (const _img_private_t *env, void *data, lsn_t lsn, + bool b_form2); + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return true if successful or false if an error. +*/ +bool read_toc_aspi (_img_private_t *env); + +/*! + Run a SCSI MMC command. + + env private CD structure + i_timeout time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + Return 0 if command completed successfully. + */ +int run_scsi_cmd_aspi( const void *p_user_data, + unsigned int i_timeout, + unsigned int i_cdb, + const scsi_mmc_cdb_t * p_cdb, + scsi_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ); + diff --git a/contrib/libcdio/MSWindows/win32.c b/contrib/libcdio/MSWindows/win32.c new file mode 100644 index 000000000..1c9d9b93d --- /dev/null +++ b/contrib/libcdio/MSWindows/win32.c @@ -0,0 +1,805 @@ +/* + $Id: win32.c,v 1.3 2006/09/26 22:10:24 dgp85 Exp $ + + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file contains Win32-specific code and implements low-level + control of the CD drive. Inspired by vlc's cdrom.h code +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: win32.c,v 1.3 2006/09/26 22:10:24 dgp85 Exp $"; + +#include <cdio/cdio.h> +#include <cdio/sector.h> +#include <cdio/util.h> +#include <cdio/scsi_mmc.h> +#include "cdio_assert.h" +#include "cdio_private.h" /* protoype for cdio_is_device_win32 */ + +#include <string.h> + +#ifdef HAVE_WIN32_CDROM + +#include <ctype.h> +#include <stdio.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#include <windows.h> +#include <winioctl.h> +#include "win32.h" + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#if defined (MSVC) || defined (_XBOX) +#undef IN +#else +#include "aspi32.h" +#endif + +#ifdef _XBOX +#include "stdint.h" +#include <xtl.h> +#define WIN_NT 1 +#else +#define WIN_NT ( GetVersion() < 0x80000000 ) +#endif + +/* General ioctl() CD-ROM command function */ +static bool +_cdio_mciSendCommand(int id, UINT msg, DWORD flags, void *arg) +{ +#ifdef _XBOX + return false; +#else + MCIERROR mci_error; + + mci_error = mciSendCommand(id, msg, flags, (DWORD)arg); + if ( mci_error ) { + char error[256]; + + mciGetErrorString(mci_error, error, 256); + cdio_warn("mciSendCommand() error: %s", error); + } + return(mci_error == 0); +#endif +} + +static access_mode_t +str_to_access_mode_win32(const char *psz_access_mode) +{ + const access_mode_t default_access_mode = + WIN_NT ? _AM_IOCTL : _AM_ASPI; + + if (NULL==psz_access_mode) return default_access_mode; + + if (!strcmp(psz_access_mode, "ioctl")) + return _AM_IOCTL; + else if (!strcmp(psz_access_mode, "ASPI")) { +#ifdef _XBOX + return _AM_ASPI; +#else + cdio_warn ("XBOX doesn't support access type: %s. Default used instead.", + psz_access_mode); + return default_access_mode; +#endif + } else { + cdio_warn ("unknown access type: %s. Default used instead.", + psz_access_mode); + return default_access_mode; + } +} + +static discmode_t +get_discmode_win32(void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + if (p_env->hASPI) { + return get_discmode_aspi (p_env); + } else { + return get_discmode_win32ioctl (p_env); + } +} + +static const char * +is_cdrom_win32(const char drive_letter) { + if ( WIN_NT ) { + return is_cdrom_win32ioctl (drive_letter); + } else { + return is_cdrom_aspi(drive_letter); + } +} + +/*! + Run a SCSI MMC command. + + env private CD structure + i_timeout_ms time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + Return 0 if command completed successfully. + */ +static int +run_scsi_cmd_win32( const void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const scsi_mmc_cdb_t *p_cdb, + scsi_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + + if (p_env->hASPI) { + return run_scsi_cmd_aspi( p_env, i_timeout_ms, i_cdb, p_cdb, + e_direction, i_buf, p_buf ); + } else { + return run_scsi_cmd_win32ioctl( p_env, i_timeout_ms, i_cdb, p_cdb, + e_direction, i_buf, p_buf ); + } +} + +/*! + Initialize CD device. + */ +static bool +_cdio_init_win32 (void *user_data) +{ + _img_private_t *p_env = user_data; + if (p_env->gen.init) { + cdio_error ("init called more than once"); + return false; + } + + p_env->gen.init = true; + p_env->gen.toc_init = false; + p_env->gen.b_cdtext_init = false; + p_env->gen.b_cdtext_error = false; + + /* Initializations */ + p_env->h_device_handle = NULL; + p_env->i_sid = 0; + p_env->hASPI = 0; + p_env->lpSendCommand = 0; + p_env->b_aspi_init = false; + p_env->b_ioctl_init = false; + + if ( _AM_IOCTL == p_env->access_mode ) { + return init_win32ioctl(p_env); + } else { + return init_aspi(p_env); + } +} + +/*! + Release and free resources associated with cd. + */ +static void +_free_win32 (void *user_data) +{ + _img_private_t *p_env = user_data; + + if (NULL == p_env) return; + free (p_env->gen.source_name); + + if( p_env->h_device_handle ) + CloseHandle( p_env->h_device_handle ); + if( p_env->hASPI ) + FreeLibrary( (HMODULE)p_env->hASPI ); + + free (p_env); +} + +/*! + Reads an audio device into data starting from lsn. + Returns 0 if no error. + */ +static int +_cdio_read_audio_sectors (void *user_data, void *data, lsn_t lsn, + unsigned int nblocks) +{ + _img_private_t *p_env = user_data; + if ( p_env->hASPI ) { + return read_audio_sectors_aspi( p_env, data, lsn, nblocks ); + } else { + return read_audio_sectors_win32ioctl( p_env, data, lsn, nblocks ); + } +} + +/*! + Reads a single mode1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_cdio_read_mode1_sector (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *p_env = user_data; + + if (p_env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged < 75 + || (p_env->gen.ioctls_debugged < (30 * 75) + && p_env->gen.ioctls_debugged % 75 == 0) + || p_env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %lu", (unsigned long int) lsn); + + p_env->gen.ioctls_debugged++; + + if ( p_env->hASPI ) { + return read_mode1_sector_aspi( p_env, data, lsn, b_form2 ); + } else { + return read_mode1_sector_win32ioctl( p_env, data, lsn, b_form2 ); + } +} + +/*! + Reads nblocks of mode1 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_cdio_read_mode1_sectors (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *p_env = user_data; + int i; + int retval; + + for (i = 0; i < nblocks; i++) { + if (b_form2) { + if ( (retval = _cdio_read_mode1_sector (p_env, + ((char *)data) + (M2RAW_SECTOR_SIZE * i), + lsn + i, true)) ) + return retval; + } else { + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + if ( (retval = _cdio_read_mode1_sector (p_env, buf, lsn + i, false)) ) + return retval; + + memcpy (((char *)data) + (CDIO_CD_FRAMESIZE * i), + buf, CDIO_CD_FRAMESIZE); + } + } + return 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_cdio_read_mode2_sector (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + _img_private_t *p_env = user_data; + + if (p_env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged < 75 + || (p_env->gen.ioctls_debugged < (30 * 75) + && p_env->gen.ioctls_debugged % 75 == 0) + || p_env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %lu", (unsigned long int) lsn); + + p_env->gen.ioctls_debugged++; + + if ( p_env->hASPI ) { + int ret; + ret = read_mode2_sector_aspi(user_data, buf, lsn, 1); + if( ret != 0 ) return ret; + if (b_form2) + memcpy (data, buf, M2RAW_SECTOR_SIZE); + else + memcpy (((char *)data), buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE); + return 0; + } else { + return read_mode2_sector_win32ioctl( p_env, data, lsn, b_form2 ); + } +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_cdio_read_mode2_sectors (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _cdio_read_mode2_sector (user_data, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static uint32_t +stat_size_win32 (void *user_data) +{ + _img_private_t *p_env = user_data; + + return p_env->tocent[p_env->gen.i_tracks].start_lsn; +} + +/*! + Set the key "arg" to "value" in source device. +*/ +static int +set_arg_win32 (void *user_data, const char key[], const char value[]) +{ + _img_private_t *p_env = user_data; + + if (!strcmp (key, "source")) + { + if (!value) + return -2; + + free (p_env->gen.source_name); + + p_env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + p_env->access_mode = str_to_access_mode_win32(value); + if (p_env->access_mode == _AM_ASPI && !p_env->b_aspi_init) + return init_aspi(p_env) ? 1 : -3; + else if (p_env->access_mode == _AM_IOCTL && !p_env->b_ioctl_init) + return init_win32ioctl(p_env) ? 1 : -3; + else + return -4; + return 0; + } + else + return -1; + + return 0; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return true if successful or false if an error. +*/ +static bool +read_toc_win32 (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + bool ret; + if( p_env->hASPI ) { + ret = read_toc_aspi( p_env ); + } else { + ret = read_toc_win32ioctl( p_env ); + } + if (ret) p_env->gen.toc_init = true ; + return true; +} + +/*! + Eject media. Return 1 if successful, 0 otherwise. + */ +static int +_cdio_eject_media (void *user_data) { +#ifdef _XBOX + return -1; +#else + _img_private_t *env = user_data; + + + MCI_OPEN_PARMS op; + MCI_STATUS_PARMS st; + DWORD i_flags; + char psz_drive[4]; + int ret; + + memset( &op, 0, sizeof(MCI_OPEN_PARMS) ); + op.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO; + strcpy( psz_drive, "X:" ); + psz_drive[0] = env->gen.source_name[0]; + op.lpstrElementName = psz_drive; + + /* Set the flags for the device type */ + i_flags = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | + MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE; + + if( _cdio_mciSendCommand( 0, MCI_OPEN, i_flags, &op ) ) { + st.dwItem = MCI_STATUS_READY; + /* Eject disc */ + ret = _cdio_mciSendCommand( op.wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, 0 ) != 0; + /* Release access to the device */ + _cdio_mciSendCommand( op.wDeviceID, MCI_CLOSE, MCI_WAIT, 0 ); + } else + ret = 0; + + return ret; +#endif +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +_get_arg_win32 (void *user_data, const char key[]) +{ + _img_private_t *env = user_data; + + if (!strcmp (key, "source")) { + return env->gen.source_name; + } else if (!strcmp (key, "access-mode")) { + if (env->hASPI) + return "ASPI"; + else + return "ioctl"; + } + return NULL; +} + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +static char * +_cdio_get_mcn (const void *p_user_data) { + const _img_private_t *p_env = p_user_data; + + if( p_env->hASPI ) { + return scsi_mmc_get_mcn( p_env->gen.cdio ); + } else { + return get_mcn_win32ioctl(p_env); + } +} + +/*! + Get format of track. +*/ +static track_format_t +_cdio_get_track_format(void *p_obj, track_t i_track) +{ + _img_private_t *p_env = p_obj; + + if ( !p_env ) return TRACK_FORMAT_ERROR; + + if (!p_env->gen.toc_init) read_toc_win32 (p_env) ; + + if ( i_track < p_env->gen.i_first_track + || i_track >= p_env->gen.i_tracks + p_env->gen.i_first_track ) + return TRACK_FORMAT_ERROR; + + if( p_env->hASPI ) { + return get_track_format_aspi(p_env, i_track); + } else { + return get_track_format_win32ioctl(p_env, i_track); + } +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +static bool +_cdio_get_track_green(void *obj, track_t i_track) +{ + _img_private_t *p_env = obj; + + switch (_cdio_get_track_format(p_env, i_track)) { + case TRACK_FORMAT_XA: + return true; + case TRACK_FORMAT_ERROR: + case TRACK_FORMAT_CDI: + case TRACK_FORMAT_AUDIO: + return false; + case TRACK_FORMAT_DATA: + if (_AM_ASPI == p_env->access_mode ) + return ((p_env->tocent[i_track-p_env->gen.i_first_track].Control & 8) != 0); + default: + break; + } + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cd-info for a while. + */ + return ((p_env->tocent[i_track-p_env->gen.i_first_track].Control & 2) != 0); +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + i_tracks in obj. Track numbers start at 1. + The "leadout" track is specified either by + using i_tracks LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static bool +_cdio_get_track_msf(void *p_user_data, track_t i_tracks, msf_t *msf) +{ + _img_private_t *p_env = p_user_data; + + if (NULL == msf) return false; + + if (!p_env->gen.toc_init) read_toc_win32 (p_env) ; + + if (i_tracks == CDIO_CDROM_LEADOUT_TRACK) i_tracks = p_env->gen.i_tracks+1; + + if (i_tracks > p_env->gen.i_tracks+1 || i_tracks == 0) { + return false; + } else { + cdio_lsn_to_msf(p_env->tocent[i_tracks-1].start_lsn, msf); + return true; + } +} + +#endif /* HAVE_WIN32_CDROM */ + +/*! + Return an array of strings giving possible CD devices. + */ +char ** +cdio_get_devices_win32 (void) +{ +#ifndef HAVE_WIN32_CDROM + return NULL; +#else + char **drives = NULL; + unsigned int num_drives=0; + char drive_letter; + + /* Scan the system for CD-ROM drives. + */ + +#if FINISHED + /* Now check the currently mounted CD drives */ + if (NULL != (ret_drive = cdio_check_mounts("/etc/mtab"))) { + cdio_add_device_list(&drives, drive, &num_drives); + } + + /* Finally check possible mountable drives in /etc/fstab */ + if (NULL != (ret_drive = cdio_check_mounts("/etc/fstab"))) { + cdio_add_device_list(&drives, drive, &num_drives); + } +#endif + + /* Scan the system for CD-ROM drives. + Not always 100% reliable, so use the USE_MNTENT code above first. + */ + for (drive_letter='A'; drive_letter <= 'Z'; drive_letter++) { + const char *drive_str=is_cdrom_win32(drive_letter); + if (drive_str != NULL) { + cdio_add_device_list(&drives, drive_str, &num_drives); + } + } + cdio_add_device_list(&drives, NULL, &num_drives); + return drives; +#endif /*HAVE_WIN32_CDROM*/ +} + +/*! + Return a string containing the default CD device if none is specified. + if CdIo is NULL (we haven't initialized a specific device driver), + then find a suitable one and return the default device for that. + + NULL is returned if we couldn't get a default device. +*/ +char * +cdio_get_default_device_win32(void) +{ + +#ifdef HAVE_WIN32_CDROM + char drive_letter; + + for (drive_letter='A'; drive_letter <= 'Z'; drive_letter++) { + const char *drive_str=is_cdrom_win32(drive_letter); + if (drive_str != NULL) { + return strdup(drive_str); + } + } +#endif + return NULL; +} + +/*! + Return true if source_name could be a device containing a CD-ROM. +*/ +bool +cdio_is_device_win32(const char *source_name) +{ + unsigned int len; + + if (NULL == source_name) return false; + len = strlen(source_name); + +#ifdef HAVE_WIN32_CDROM + if ((len == 2) && isalpha(source_name[0]) + && (source_name[len-1] == ':')) + return true; + + if ( ! WIN_NT ) return false; + + /* Test to see if of form: \\.\x: */ + return ( (len == 6) + && source_name[0] == '\\' && source_name[1] == '\\' + && source_name[2] == '.' && source_name[3] == '\\' + && isalpha(source_name[len-2]) + && (source_name[len-1] == ':') ); +#else + return false; +#endif +} + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_win32 (const char *psz_source_name) +{ +#ifdef HAVE_WIN32_CDROM + if ( WIN_NT ) { + return cdio_open_am_win32(psz_source_name, "ioctl"); + } else { + return cdio_open_am_win32(psz_source_name, "ASPI"); + } +#else + return NULL; +#endif /* HAVE_WIN32_CDROM */ +} + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_am_win32 (const char *psz_orig_source, const char *psz_access_mode) +{ + +#ifdef HAVE_WIN32_CDROM + CdIo *ret; + _img_private_t *_data; + char *psz_source; + + cdio_funcs _funcs; + + memset( &_funcs, 0, sizeof(_funcs) ); + + _funcs.eject_media = _cdio_eject_media; + _funcs.free = _free_win32; + _funcs.get_arg = _get_arg_win32; + _funcs.get_cdtext = get_cdtext_generic; + _funcs.get_default_device = cdio_get_default_device_win32; + _funcs.get_devices = cdio_get_devices_win32; + _funcs.get_discmode = get_discmode_win32; + _funcs.get_drive_cap = scsi_mmc_get_drive_cap_generic; + _funcs.get_first_track_num= get_first_track_num_generic; + _funcs.get_hwinfo = NULL; + _funcs.get_mcn = _cdio_get_mcn; + _funcs.get_num_tracks = get_num_tracks_generic; + _funcs.get_track_format = _cdio_get_track_format; + _funcs.get_track_green = _cdio_get_track_green; + _funcs.get_track_lba = NULL; /* This could be implemented if need be. */ + _funcs.get_track_msf = _cdio_get_track_msf; + _funcs.lseek = NULL; + _funcs.read = NULL; + _funcs.read_audio_sectors = _cdio_read_audio_sectors; + _funcs.read_mode1_sector = _cdio_read_mode1_sector; + _funcs.read_mode1_sectors = _cdio_read_mode1_sectors; + _funcs.read_mode2_sector = _cdio_read_mode2_sector; + _funcs.read_mode2_sectors = _cdio_read_mode2_sectors; + _funcs.read_toc = &read_toc_win32; + _funcs.run_scsi_mmc_cmd = &run_scsi_cmd_win32; + _funcs.set_arg = set_arg_win32; + _funcs.stat_size = stat_size_win32; + + _data = _cdio_malloc (sizeof (_img_private_t)); + _data->access_mode = str_to_access_mode_win32(psz_access_mode); + _data->gen.init = false; + _data->gen.fd = -1; + + if (NULL == psz_orig_source) { + psz_source=cdio_get_default_device_win32(); + if (NULL == psz_source) return NULL; + set_arg_win32(_data, "source", psz_source); + free(psz_source); + } else { + if (cdio_is_device_win32(psz_orig_source)) + set_arg_win32(_data, "source", psz_orig_source); + else { + /* The below would be okay if all device drivers worked this way. */ +#if 0 + cdio_info ("source %s is a not a device", psz_orig_source); +#endif + return NULL; + } + } + + ret = cdio_new ((void *)_data, &_funcs); + if (ret == NULL) return NULL; + + if (_cdio_init_win32(_data)) + return ret; + else { + _free_win32 (_data); + return NULL; + } + +#else + return NULL; +#endif /* HAVE_WIN32_CDROM */ + +} + +bool +cdio_have_win32 (void) +{ +#ifdef HAVE_WIN32_CDROM + return true; +#else + return false; +#endif /* HAVE_WIN32_CDROM */ +} diff --git a/contrib/libcdio/MSWindows/win32.h b/contrib/libcdio/MSWindows/win32.h new file mode 100644 index 000000000..84f3b67b3 --- /dev/null +++ b/contrib/libcdio/MSWindows/win32.h @@ -0,0 +1,170 @@ +/*
+ $Id: win32.h,v 1.2 2005/01/01 02:43:58 rockyb Exp $
+
+ Copyright (C) 2004 Rocky Bernstein <rocky@panix.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "cdio_private.h"
+
+#pragma pack()
+
+typedef struct {
+ lsn_t start_lsn;
+ UCHAR Control : 4;
+ UCHAR Format;
+ cdtext_t cdtext; /* CD-TEXT */
+} track_info_t;
+
+typedef enum {
+ _AM_NONE,
+ _AM_IOCTL,
+ _AM_ASPI,
+} access_mode_t;
+
+typedef struct {
+ /* Things common to all drivers like this.
+ This must be first. */
+ generic_img_private_t gen;
+
+ access_mode_t access_mode;
+
+ /* Some of the more OS specific things. */
+ /* Entry info for each track, add 1 for leadout. */
+ track_info_t tocent[CDIO_CD_MAX_TRACKS+1];
+
+ HANDLE h_device_handle; /* device descriptor */
+ long hASPI;
+ short i_sid;
+ short i_lun;
+ long (*lpSendCommand)( void* );
+
+ bool b_ioctl_init;
+ bool b_aspi_init;
+
+} _img_private_t;
+
+/*!
+ Get disc type associated with cd object.
+*/
+discmode_t get_discmode_win32ioctl (_img_private_t *p_env);
+
+/*!
+ Reads an audio device using the DeviceIoControl method into data
+ starting from lsn. Returns 0 if no error.
+*/
+int read_audio_sectors_win32ioctl (_img_private_t *obj, void *data, lsn_t lsn,
+ unsigned int nblocks);
+/*!
+ Reads a single mode2 sector using the DeviceIoControl method into
+ data starting from lsn. Returns 0 if no error.
+ */
+int read_mode2_sector_win32ioctl (const _img_private_t *env, void *data,
+ lsn_t lsn, bool b_form2);
+
+/*!
+ Reads a single mode1 sector using the DeviceIoControl method into
+ data starting from lsn. Returns 0 if no error.
+ */
+int read_mode1_sector_win32ioctl (const _img_private_t *env, void *data,
+ lsn_t lsn, bool b_form2);
+
+const char *is_cdrom_win32ioctl (const char drive_letter);
+
+/*!
+ Run a SCSI MMC command.
+
+ env private CD structure
+ i_timeout_ms time in milliseconds we will wait for the command
+ to complete. If this value is -1, use the default
+ time-out value.
+ p_buf Buffer for data, both sending and receiving
+ i_buf Size of buffer
+ e_direction direction the transfer is to go.
+ cdb CDB bytes. All values that are needed should be set on
+ input. We'll figure out what the right CDB length should be.
+
+ Return 0 if command completed successfully.
+ */
+int run_scsi_cmd_win32ioctl( const void *p_user_data,
+ unsigned int i_timeout,
+ unsigned int i_cdb,
+ const scsi_mmc_cdb_t * p_cdb,
+ scsi_mmc_direction_t e_direction,
+ unsigned int i_buf, /*in/out*/ void *p_buf );
+
+/*!
+ Initialize internal structures for CD device.
+ */
+bool init_win32ioctl (_img_private_t *env);
+
+/*!
+ Read and cache the CD's Track Table of Contents and track info.
+ Return true if successful or false if an error.
+*/
+bool read_toc_win32ioctl (_img_private_t *env);
+
+/*!
+ Return the media catalog number MCN.
+
+ Note: string is malloc'd so caller should free() then returned
+ string when done with it.
+
+ */
+char *get_mcn_win32ioctl (const _img_private_t *env);
+
+/*
+ Read cdtext information for a CdIo object .
+
+ return true on success, false on error or CD-TEXT information does
+ not exist.
+*/
+bool init_cdtext_win32ioctl (_img_private_t *env);
+
+/*!
+ Return the the kind of drive capabilities of device.
+
+ Note: string is malloc'd so caller should free() then returned
+ string when done with it.
+
+ */
+void get_drive_cap_aspi (const _img_private_t *env,
+ cdio_drive_read_cap_t *p_read_cap,
+ cdio_drive_write_cap_t *p_write_cap,
+ cdio_drive_misc_cap_t *p_misc_cap);
+
+/*!
+ Return the the kind of drive capabilities of device.
+
+ Note: string is malloc'd so caller should free() then returned
+ string when done with it.
+
+ */
+void get_drive_cap_win32ioctl (const _img_private_t *env,
+ cdio_drive_read_cap_t *p_read_cap,
+ cdio_drive_write_cap_t *p_write_cap,
+ cdio_drive_misc_cap_t *p_misc_cap);
+
+/*!
+ Get the format (XA, DATA, AUDIO) of a track.
+*/
+track_format_t get_track_format_win32ioctl(const _img_private_t *env,
+ track_t i_track);
+
+void set_cdtext_field_win32(void *user_data, track_t i_track,
+ track_t i_first_track,
+ cdtext_field_t e_field, const char *psz_value);
+
diff --git a/contrib/libcdio/MSWindows/win32_ioctl.c b/contrib/libcdio/MSWindows/win32_ioctl.c new file mode 100644 index 000000000..dec24e785 --- /dev/null +++ b/contrib/libcdio/MSWindows/win32_ioctl.c @@ -0,0 +1,739 @@ +/* + $Id: win32_ioctl.c,v 1.1 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file contains Win32-specific code using the DeviceIoControl + access method. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: win32_ioctl.c,v 1.1 2005/01/01 02:43:58 rockyb Exp $"; + +#ifdef HAVE_WIN32_CDROM + +#if defined (_XBOX) +#include "inttypes.h" +#include "NtScsi.h" +#include "undocumented.h" +#define FORMAT_ERROR(i_err, psz_msg) \ + psz_msg=(char *)LocalAlloc(LMEM_ZEROINIT, 255); \ + sprintf(psz_msg, "error file %s: line %d (%s) %d\n", + _FILE__, __LINE__, __PRETTY_FUNCTION__, i_err) +#else +#include <ddk/ntddstor.h> +#include <ddk/ntddscsi.h> +#include <ddk/scsi.h> +#define FORMAT_ERROR(i_err, psz_msg) \ + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, \ + NULL, i_err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), \ + (LPSTR) psz_msg, 0, NULL) +#endif + +#ifdef WIN32 +#include <windows.h> +#endif + +#include <stdio.h> +#include <stddef.h> /* offsetof() macro */ +#include <sys/stat.h> +#include <errno.h> +#include <sys/types.h> + +#include <cdio/cdio.h> +#include <cdio/sector.h> +#include "cdio_assert.h" +#include <cdio/scsi_mmc.h> +#include "cdtext_private.h" +#include "cdio/logging.h" + +/* Win32 DeviceIoControl specifics */ +/***** FIXME: #include ntddcdrm.h from Wine, but probably need to + modify it a little. +*/ + +#ifndef IOCTL_CDROM_BASE +# define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM +#endif +#ifndef IOCTL_CDROM_READ_TOC +#define IOCTL_CDROM_READ_TOC \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS) +#endif +#ifndef IOCTL_CDROM_RAW_READ +#define IOCTL_CDROM_RAW_READ CTL_CODE(IOCTL_CDROM_BASE, 0x000F, \ + METHOD_OUT_DIRECT, FILE_READ_ACCESS) +#endif + +#ifndef IOCTL_CDROM_READ_Q_CHANNEL +#define IOCTL_CDROM_READ_Q_CHANNEL \ + CTL_CODE(IOCTL_CDROM_BASE, 0x000B, METHOD_BUFFERED, FILE_READ_ACCESS) +#endif + +typedef struct { + SCSI_PASS_THROUGH Spt; + ULONG Filler; + UCHAR SenseBuf[32]; + UCHAR DataBuf[512]; +} SCSI_PASS_THROUGH_WITH_BUFFERS; + +typedef struct _TRACK_DATA { + UCHAR Format; + UCHAR Control : 4; + UCHAR Adr : 4; + UCHAR TrackNumber; + UCHAR Reserved1; + UCHAR Address[4]; +} TRACK_DATA, *PTRACK_DATA; + +typedef struct _CDROM_TOC { + UCHAR Length[2]; + UCHAR FirstTrack; + UCHAR LastTrack; + TRACK_DATA TrackData[CDIO_CD_MAX_TRACKS+1]; +} CDROM_TOC, *PCDROM_TOC; + +typedef struct _TRACK_DATA_FULL { + UCHAR SessionNumber; + UCHAR Control : 4; + UCHAR Adr : 4; + UCHAR TNO; + UCHAR POINT; /* Tracknumber (of session?) or lead-out/in (0xA0, 0xA1, 0xA2) */ + UCHAR Min; /* Only valid if disctype is CDDA ? */ + UCHAR Sec; /* Only valid if disctype is CDDA ? */ + UCHAR Frame; /* Only valid if disctype is CDDA ? */ + UCHAR Zero; /* Always zero */ + UCHAR PMIN; /* start min, if POINT is a track; if lead-out/in 0xA0: First Track */ + UCHAR PSEC; + UCHAR PFRAME; +} TRACK_DATA_FULL, *PTRACK_DATA_FULL; + +typedef struct _CDROM_TOC_FULL { + UCHAR Length[2]; + UCHAR FirstSession; + UCHAR LastSession; + TRACK_DATA_FULL TrackData[CDIO_CD_MAX_TRACKS+3]; +} CDROM_TOC_FULL, *PCDROM_TOC_FULL; + +typedef enum _TRACK_MODE_TYPE { + YellowMode2, + XAForm2, + CDDA +} TRACK_MODE_TYPE, *PTRACK_MODE_TYPE; + +typedef struct __RAW_READ_INFO { + LARGE_INTEGER DiskOffset; + ULONG SectorCount; + TRACK_MODE_TYPE TrackMode; +} RAW_READ_INFO, *PRAW_READ_INFO; + +typedef struct _CDROM_SUB_Q_DATA_FORMAT { + UCHAR Format; + UCHAR Track; +} CDROM_SUB_Q_DATA_FORMAT, *PCDROM_SUB_Q_DATA_FORMAT; + +typedef struct _SUB_Q_HEADER { + UCHAR Reserved; + UCHAR AudioStatus; + UCHAR DataLength[2]; +} SUB_Q_HEADER, *PSUB_Q_HEADER; + +typedef struct _SUB_Q_MEDIA_CATALOG_NUMBER { + SUB_Q_HEADER Header; + UCHAR FormatCode; + UCHAR Reserved[3]; + UCHAR Reserved1 : 7; + UCHAR Mcval :1; + UCHAR MediaCatalog[15]; +} SUB_Q_MEDIA_CATALOG_NUMBER, *PSUB_Q_MEDIA_CATALOG_NUMBER; + +#include "win32.h" + +#define OP_TIMEOUT_MS 60 + +/*! + Run a SCSI MMC command. + + env private CD structure + i_timeout time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + Return 0 if command completed successfully. + */ +int +run_scsi_cmd_win32ioctl( const void *p_user_data, + unsigned int i_timeout_ms, + unsigned int i_cdb, const scsi_mmc_cdb_t * p_cdb, + scsi_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + SCSI_PASS_THROUGH_DIRECT sptd; + bool success; + DWORD dwBytesReturned; + + sptd.Length = sizeof(sptd); + sptd.PathId = 0; /* SCSI card ID will be filled in automatically */ + sptd.TargetId= 0; /* SCSI target ID will also be filled in */ + sptd.Lun=0; /* SCSI lun ID will also be filled in */ + sptd.CdbLength = i_cdb; + sptd.SenseInfoLength = 0; /* Don't return any sense data */ + sptd.DataIn = SCSI_MMC_DATA_READ == e_direction ? + SCSI_IOCTL_DATA_IN : SCSI_IOCTL_DATA_OUT; + sptd.DataTransferLength= i_buf; + sptd.TimeOutValue = msecs2secs(i_timeout_ms); + sptd.DataBuffer = (void *) p_buf; + sptd.SenseInfoOffset = 0; + + memcpy(sptd.Cdb, p_cdb, i_cdb); + + /* Send the command to drive */ + success=DeviceIoControl(p_env->h_device_handle, + IOCTL_SCSI_PASS_THROUGH_DIRECT, + (void *)&sptd, + (DWORD)sizeof(SCSI_PASS_THROUGH_DIRECT), + NULL, 0, + &dwBytesReturned, + NULL); + + if(! success) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + FORMAT_ERROR(i_err, psz_msg); + cdio_info("Error: %s", psz_msg); + LocalFree(psz_msg); + return 1; + } + return 0; +} + +/*! + Get disc type associated with cd object. +*/ +discmode_t +get_discmode_win32ioctl (_img_private_t *p_env) +{ + track_t i_track; + discmode_t discmode=CDIO_DISC_MODE_NO_INFO; + + /* See if this is a DVD. */ + cdio_dvd_struct_t dvd; /* DVD READ STRUCT for layer 0. */ + + dvd.physical.type = CDIO_DVD_STRUCT_PHYSICAL; + dvd.physical.layer_num = 0; + if (0 == scsi_mmc_get_dvd_struct_physical_private (p_env, + &run_scsi_cmd_win32ioctl, + &dvd)) { + switch(dvd.physical.layer[0].book_type) { + case CDIO_DVD_BOOK_DVD_ROM: return CDIO_DISC_MODE_DVD_ROM; + case CDIO_DVD_BOOK_DVD_RAM: return CDIO_DISC_MODE_DVD_RAM; + case CDIO_DVD_BOOK_DVD_R: return CDIO_DISC_MODE_DVD_R; + case CDIO_DVD_BOOK_DVD_RW: return CDIO_DISC_MODE_DVD_RW; + case CDIO_DVD_BOOK_DVD_PR: return CDIO_DISC_MODE_DVD_PR; + case CDIO_DVD_BOOK_DVD_PRW: return CDIO_DISC_MODE_DVD_PRW; + default: return CDIO_DISC_MODE_DVD_OTHER; + } + } + + if (!p_env->gen.toc_init) + read_toc_win32ioctl (p_env); + + if (!p_env->gen.toc_init) + return CDIO_DISC_MODE_NO_INFO; + + for (i_track = p_env->gen.i_first_track; + i_track < p_env->gen.i_first_track + p_env->gen.i_tracks ; + i_track ++) { + track_format_t track_fmt=get_track_format_win32ioctl(p_env, i_track); + + switch(track_fmt) { + case TRACK_FORMAT_AUDIO: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DA; + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_XA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_DATA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DATA; + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_ERROR: + default: + discmode = CDIO_DISC_MODE_ERROR; + } + } + return discmode; +} + +/* + Returns a string that can be used in a CreateFile call if + c_drive letter is a character. If not NULL is returned. + */ + +const char * +is_cdrom_win32ioctl(const char c_drive_letter) +{ +#ifdef _XBOX + char sz_win32_drive_full[] = "\\\\.\\X:"; + sz_win32_drive_full[4] = c_drive_letter; + return strdup(sz_win32_drive_full); +#else + UINT uDriveType; + char sz_win32_drive[4]; + + sz_win32_drive[0]= c_drive_letter; + sz_win32_drive[1]=':'; + sz_win32_drive[2]='\\'; + sz_win32_drive[3]='\0'; + + uDriveType = GetDriveType(sz_win32_drive); + + switch(uDriveType) { + case DRIVE_CDROM: { + char sz_win32_drive_full[] = "\\\\.\\X:"; + sz_win32_drive_full[4] = c_drive_letter; + return strdup(sz_win32_drive_full); + } + default: + cdio_debug("Drive %c is not a CD-ROM", c_drive_letter); + return NULL; + } +#endif +} + +/*! + Reads an audio device using the DeviceIoControl method into data + starting from lsn. Returns 0 if no error. + */ +int +read_audio_sectors_win32ioctl (_img_private_t *env, void *data, lsn_t lsn, + unsigned int nblocks) +{ + DWORD dwBytesReturned; + RAW_READ_INFO cdrom_raw; + + /* Initialize CDROM_RAW_READ structure */ + cdrom_raw.DiskOffset.QuadPart = CDIO_CD_FRAMESIZE_RAW * lsn; + cdrom_raw.SectorCount = nblocks; + cdrom_raw.TrackMode = CDDA; + + if( DeviceIoControl( env->h_device_handle, + IOCTL_CDROM_RAW_READ, &cdrom_raw, + sizeof(RAW_READ_INFO), data, + CDIO_CD_FRAMESIZE_RAW * nblocks, + &dwBytesReturned, NULL ) == 0 ) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + FORMAT_ERROR(i_err, psz_msg); + cdio_info("Error reading audio-mode %lu\n%s)", + (long unsigned int) lsn, psz_msg); + LocalFree(psz_msg); + return 1; + } + return 0; +} + +/*! + Reads a single raw sector using the DeviceIoControl method into + data starting from lsn. Returns 0 if no error. + */ +static int +read_raw_sector (const _img_private_t *p_env, void *p_buf, lsn_t lsn) +{ + scsi_mmc_cdb_t cdb = {{0, }}; + + /* ReadCD CDB12 command. The values were taken from MMC1 draft paper. */ + CDIO_MMC_SET_COMMAND (cdb.field, CDIO_MMC_GPCMD_READ_CD); + CDIO_MMC_SET_READ_LBA (cdb.field, lsn); + CDIO_MMC_SET_READ_LENGTH24(cdb.field, 1); + + cdb.field[9]=0xF8; /* Raw read, 2352 bytes per sector */ + + return run_scsi_cmd_win32ioctl(p_env, OP_TIMEOUT_MS, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + CDIO_CD_FRAMESIZE_RAW, p_buf); +} + +/*! + Reads a single mode2 sector using the DeviceIoControl method into + data starting from lsn. Returns 0 if no error. + */ +int +read_mode2_sector_win32ioctl (const _img_private_t *p_env, void *p_data, + lsn_t lsn, bool b_form2) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + int ret = read_raw_sector (p_env, buf, lsn); + + if ( 0 != ret) return ret; + + memcpy (p_data, + buf + CDIO_CD_SYNC_SIZE + CDIO_CD_XA_HEADER, + b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE); + + return 0; + +} + +/*! + Reads a single mode2 sector using the DeviceIoControl method into + data starting from lsn. Returns 0 if no error. + */ +int +read_mode1_sector_win32ioctl (const _img_private_t *env, void *data, + lsn_t lsn, bool b_form2) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + int ret = read_raw_sector (env, buf, lsn); + + if ( 0 != ret) return ret; + + memcpy (data, + buf + CDIO_CD_SYNC_SIZE+CDIO_CD_HEADER_SIZE, + b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE); + + return 0; + +} + +/*! + Initialize internal structures for CD device. + */ +bool +init_win32ioctl (_img_private_t *env) +{ +#ifdef WIN32 + OSVERSIONINFO ov; +#endif + +#ifdef _XBOX + ANSI_STRING filename; + OBJECT_ATTRIBUTES attributes; + IO_STATUS_BLOCK status; + HANDLE hDevice; + NTSTATUS error; +#else + unsigned int len=strlen(env->gen.source_name); + char psz_win32_drive[7]; + DWORD dw_access_flags; +#endif + + cdio_debug("using winNT/2K/XP ioctl layer"); + +#ifdef WIN32 + memset(&ov,0,sizeof(OSVERSIONINFO)); + ov.dwOSVersionInfoSize=sizeof(OSVERSIONINFO); + GetVersionEx(&ov); + + if((ov.dwPlatformId==VER_PLATFORM_WIN32_NT) && + (ov.dwMajorVersion>4)) + dw_access_flags = GENERIC_READ|GENERIC_WRITE; /* add gen write on W2k/XP */ + else dw_access_flags = GENERIC_READ; +#endif + + if (cdio_is_device_win32(env->gen.source_name)) + { +#ifdef _XBOX + // Use XBOX cdrom, no matter what drive letter is given. + RtlInitAnsiString(&filename,"\\Device\\Cdrom0"); + InitializeObjectAttributes(&attributes, &filename, OBJ_CASE_INSENSITIVE, + NULL); + error = NtCreateFile( &hDevice, + GENERIC_READ |SYNCHRONIZE | FILE_READ_ATTRIBUTES, + &attributes, + &status, + NULL, + 0, + FILE_SHARE_READ, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE + | FILE_SYNCHRONOUS_IO_NONALERT ); + + if (!NT_SUCCESS(error)) + { + return false; + } + env->h_device_handle = hDevice; +#else + sprintf( psz_win32_drive, "\\\\.\\%c:", env->gen.source_name[len-2] ); + + env->h_device_handle = CreateFile( psz_win32_drive, + dw_access_flags, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if( env->h_device_handle == INVALID_HANDLE_VALUE ) + { + /* No good. try toggle write. */ + dw_access_flags ^= GENERIC_WRITE; + env->h_device_handle = CreateFile( psz_win32_drive, + dw_access_flags, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + if (env->h_device_handle == NULL) + return false; + } +#endif + env->b_ioctl_init = true; + return true; + } + return false; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + via a SCSI MMC READ_TOC (FULTOC). Return true if successful or + false if an error. +*/ +static bool +read_fulltoc_win32mmc (_img_private_t *p_env) +{ + scsi_mmc_cdb_t cdb = {{0, }}; + CDROM_TOC_FULL cdrom_toc_full; + int i_status, i, i_track_format, i_seen_flag; + + /* Operation code */ + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC); + + cdb.field[1] = 0x00; + + /* Format */ + cdb.field[2] = CDIO_MMC_READTOC_FMT_FULTOC; + + memset(&cdrom_toc_full, 0, sizeof(cdrom_toc_full)); + + /* Setup to read header, to get length of data */ + CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(cdrom_toc_full)); + + i_status = run_scsi_cmd_win32ioctl (p_env, 1000*60*3, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + sizeof(cdrom_toc_full), &cdrom_toc_full); + + if ( 0 != i_status ) { + cdio_debug ("SCSI MMC READ_TOC failed\n"); + return false; + } + + i_seen_flag=0; + for( i = 0 ; i <= CDIO_CD_MAX_TRACKS+3; i++ ) { + + if ( 0xA0 == cdrom_toc_full.TrackData[i].POINT ) { + /* First track number */ + p_env->gen.i_first_track = cdrom_toc_full.TrackData[i].PMIN; + i_track_format = cdrom_toc_full.TrackData[i].PSEC; + i_seen_flag|=0x01; + } + + if ( 0xA1 == cdrom_toc_full.TrackData[i].POINT ) { + /* Last track number */ + p_env->gen.i_tracks = + cdrom_toc_full.TrackData[i].PMIN - p_env->gen.i_first_track + 1; + i_seen_flag|=0x02; + } + + if ( 0xA2 == cdrom_toc_full.TrackData[i].POINT ) { + /* Start position of the lead out */ + p_env->tocent[ p_env->gen.i_tracks ].start_lsn = + cdio_msf3_to_lba( + cdrom_toc_full.TrackData[i].PMIN, + cdrom_toc_full.TrackData[i].PSEC, + cdrom_toc_full.TrackData[i].PFRAME ); + p_env->tocent[ p_env->gen.i_tracks ].Control + = cdrom_toc_full.TrackData[i].Control; + p_env->tocent[ p_env->gen.i_tracks ].Format = i_track_format; + i_seen_flag|=0x04; + } + + if (cdrom_toc_full.TrackData[i].POINT > 0 + && cdrom_toc_full.TrackData[i].POINT <= p_env->gen.i_tracks) { + p_env->tocent[ cdrom_toc_full.TrackData[i].POINT - 1 ].start_lsn = + cdio_msf3_to_lba( + cdrom_toc_full.TrackData[i].PMIN, + cdrom_toc_full.TrackData[i].PSEC, + cdrom_toc_full.TrackData[i].PFRAME ); + p_env->tocent[ cdrom_toc_full.TrackData[i].POINT - 1 ].Control = + cdrom_toc_full.TrackData[i].Control; + p_env->tocent[ cdrom_toc_full.TrackData[i].POINT - 1 ].Format = + i_track_format; + + cdio_debug("p_sectors: %i, %lu", i, + (unsigned long int) (p_env->tocent[i].start_lsn)); + + if (cdrom_toc_full.TrackData[i].POINT == p_env->gen.i_tracks) + i_seen_flag|=0x08; + } + + if ( 0x0F == i_seen_flag ) break; + } + if ( 0x0F == i_seen_flag ) { + p_env->gen.toc_init = true; + return true; + } + return false; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return true if successful or false if an error. +*/ +bool +read_toc_win32ioctl (_img_private_t *p_env) +{ + CDROM_TOC cdrom_toc; + DWORD dwBytesReturned; + unsigned int i; + + if ( ! p_env ) return false; + + if ( read_fulltoc_win32mmc(p_env) ) return true; + + /* SCSI-MMC READ_TOC (FULTOC) read failed. Try reading TOC via + DeviceIoControl instead */ + if( DeviceIoControl( p_env->h_device_handle, + IOCTL_CDROM_READ_TOC, + NULL, 0, &cdrom_toc, sizeof(CDROM_TOC), + &dwBytesReturned, NULL ) == 0 ) { + char *psz_msg = NULL; + long int i_err = GetLastError(); + FORMAT_ERROR(i_err, psz_msg); + if (psz_msg) { + cdio_warn("could not read TOC (%ld): %s", i_err, psz_msg); + LocalFree(psz_msg); + } else + cdio_warn("could not read TOC (%ld)", i_err); + return false; + } + + p_env->gen.i_first_track = cdrom_toc.FirstTrack; + p_env->gen.i_tracks = cdrom_toc.LastTrack - cdrom_toc.FirstTrack + 1; + + for( i = 0 ; i <= p_env->gen.i_tracks ; i++ ) { + p_env->tocent[ i ].start_lsn = + cdio_msf3_to_lba( cdrom_toc.TrackData[i].Address[1], + cdrom_toc.TrackData[i].Address[2], + cdrom_toc.TrackData[i].Address[3] ); + p_env->tocent[ i ].Control = cdrom_toc.TrackData[i].Control; + p_env->tocent[ i ].Format = cdrom_toc.TrackData[i].Format; + cdio_debug("p_sectors: %i, %lu", i, + (unsigned long int) (p_env->tocent[i].start_lsn)); + } + p_env->gen.toc_init = true; + return true; +} + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +char * +get_mcn_win32ioctl (const _img_private_t *env) { + + DWORD dwBytesReturned; + SUB_Q_MEDIA_CATALOG_NUMBER mcn; + CDROM_SUB_Q_DATA_FORMAT q_data_format; + + memset( &mcn, 0, sizeof(mcn) ); + + q_data_format.Format = CDIO_SUBCHANNEL_MEDIA_CATALOG; + + q_data_format.Track=1; + + if( DeviceIoControl( env->h_device_handle, + IOCTL_CDROM_READ_Q_CHANNEL, + &q_data_format, sizeof(q_data_format), + &mcn, sizeof(mcn), + &dwBytesReturned, NULL ) == 0 ) { + cdio_warn( "could not read Q Channel at track %d", 1); + } else if (mcn.Mcval) + return strdup(mcn.MediaCatalog); + return NULL; +} + +/*! + Get the format (XA, DATA, AUDIO) of a track. +*/ +track_format_t +get_track_format_win32ioctl(const _img_private_t *env, track_t i_track) +{ + /* This is pretty much copied from the "badly broken" cdrom_count_tracks + in linux/cdrom.c. + */ + + if (env->tocent[i_track - env->gen.i_first_track].Control & 0x04) { + if (env->tocent[i_track - env->gen.i_first_track].Format == 0x10) + return TRACK_FORMAT_CDI; + else if (env->tocent[i_track - env->gen.i_first_track].Format == 0x20) + return TRACK_FORMAT_XA; + else + return TRACK_FORMAT_DATA; + } else + return TRACK_FORMAT_AUDIO; +} + +#endif /*HAVE_WIN32_CDROM*/ diff --git a/contrib/libcdio/Makefile.am b/contrib/libcdio/Makefile.am new file mode 100644 index 000000000..a79525c8f --- /dev/null +++ b/contrib/libcdio/Makefile.am @@ -0,0 +1,62 @@ +include $(top_srcdir)/misc/Makefile.common + +AM_CFLAGS = $(DEFAULT_OCFLAGS) $(VISIBILITY_FLAG) +AM_LDFLAGS = $(xineplug_ldflags) + +SUBDIRS = cdio MSWindows image + +INCLUDES = $(LIBCDIO_CFLAGS) -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/lib -I$(top_builddir)/lib + +noinst_HEADERS = \ + cdio_assert.h \ + _cdio_stdio.h \ + scsi_mmc.h \ + cdio_private.h \ + _cdio_stream.h \ + iso9660_private.h \ + portable.h + +noinst_LTLIBRARIES = libcdio.la libiso9660.la + +libcdio_la_SOURCES = \ + _cdio_bsdi.c \ + _cdio_generic.c \ + _cdio_linux.c \ + _cdio_osx.c \ + _cdio_stdio.c \ + _cdio_stdio.h \ + _cdio_stream.c \ + _cdio_stream.h \ + _cdio_sunos.c \ + cd_types.c \ + cdio.c \ + cdtext.c \ + cdtext_private.h \ + ds.c \ + FreeBSD/freebsd.c \ + FreeBSD/freebsd.h \ + FreeBSD/freebsd_cam.c \ + FreeBSD/freebsd_ioctl.c \ + generic.h \ + image.h \ + image/bincue.c \ + image/cdrdao.c \ + image_common.h \ + image/nrg.c \ + image/nrg.h \ + MSWindows/aspi32.c \ + MSWindows/aspi32.h \ + MSWindows/win32_ioctl.c \ + MSWindows/win32.c \ + MSWindows/win32.h \ + logging.c \ + scsi_mmc.c \ + scsi_mmc_private.h \ + sector.c \ + util.c + +libiso9660_la_SOURCES = \ + iso9660.c \ + iso9660_private.h \ + iso9660_fs.c \ + xa.c diff --git a/contrib/libcdio/_cdio_bsdi.c b/contrib/libcdio/_cdio_bsdi.c new file mode 100644 index 000000000..aef535752 --- /dev/null +++ b/contrib/libcdio/_cdio_bsdi.c @@ -0,0 +1,846 @@ +/* + $Id: _cdio_bsdi.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2002, 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file contains BSDI-specific code and implements low-level + control of the CD drive. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: _cdio_bsdi.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $"; + +#include <cdio/logging.h> +#include <cdio/sector.h> +#include <cdio/util.h> +#include "cdio_assert.h" +#include "cdio_private.h" + +#define DEFAULT_CDIO_DEVICE "/dev/rsr0c" +#include <string.h> + +#ifdef HAVE_BSDI_CDROM + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +/*#define USE_ETC_FSTAB*/ +#ifdef USE_ETC_FSTAB +#include <fstab.h> +#endif + +#include <dvd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include </sys/dev/scsi/scsi.h> +#include </sys/dev/scsi/scsi_ioctl.h> +#include "cdtext_private.h" + +typedef enum { + _AM_NONE, + _AM_IOCTL, +} access_mode_t; + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + + access_mode_t access_mode; + + /* Some of the more OS specific things. */ + /* Track information */ + struct cdrom_tochdr tochdr; + struct cdrom_tocentry tocent[CDIO_CD_MAX_TRACKS+1]; + +} _img_private_t; + +/* Define the Cdrom Generic Command structure */ +typedef struct cgc +{ + scsi_mmc_cdb_t cdb; + u_char *buf; + int buflen; + int rw; + unsigned int timeout; + scsi_user_sense_t *sus; +} cgc_t; + + +/* + This code adapted from Steven M. Schultz's libdvd +*/ +static int +run_scsi_cmd_bsdi(const void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const scsi_mmc_cdb_t *p_cdb, + scsi_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + int i_status, i_asc; + struct scsi_user_cdb suc; + struct scsi_sense *sp; + + again: + suc.suc_flags = SCSI_MMC_DATA_READ == e_direction ? + SUC_READ : SUC_WRITE; + suc.suc_cdblen = i_cdb; + memcpy(suc.suc_cdb, p_cdb, i_cdb); + suc.suc_data = p_buf; + suc.suc_datalen = i_buf; + suc.suc_timeout = msecs2secs(i_timeout_ms); + if (ioctl(p_env->gen.fd, SCSIRAWCDB, &suc) == -1) + return(errno); + i_status = suc.suc_sus.sus_status; + +#if 0 + /* + * If the device returns a scsi sense error and debugging is enabled print + * some hopefully useful information on stderr. + */ + if (i_status && debug) + { + unsigned char *cp; + int i; + cp = suc.suc_sus.sus_sense; + fprintf(stderr,"i_status = %x cdb =", + i_status); + for (i = 0; i < cdblen; i++) + fprintf(stderr, " %x", cgc->cdb[i]); + fprintf(stderr, "\nsense ="); + for (i = 0; i < 16; i++) + fprintf(stderr, " %x", cp[i]); + fprintf(stderr, "\n"); + } +#endif + + /* + * HACK! Some drives return a silly "medium changed" on the first + * command AND a non-zero i_status which gets turned into a fatal + * (EIO) error even though the operation was a success. Retrying + * the operation clears the media changed status and gets the + * answer. */ + + sp = (struct scsi_sense *)&suc.suc_sus.sus_sense; + i_asc = XSENSE_ASC(sp); + if (i_status == STS_CHECKCOND && i_asc == 0x28) + goto again; +#if 0 + if (cgc->sus) + memcpy(cgc->sus, &suc.suc_sus, sizeof (struct scsi_user_sense)); +#endif + + return(i_status); +} + + + +/* Check a drive to see if it is a CD-ROM + Return 1 if a CD-ROM. 0 if it exists but isn't a CD-ROM drive + and -1 if no device exists . +*/ +static bool +cdio_is_cdrom(char *drive, char *mnttype) +{ + bool is_cd=false; + int cdfd; + struct cdrom_tochdr tochdr; + + /* If it doesn't exist, return -1 */ + if ( !cdio_is_device_quiet_generic(drive) ) { + return(false); + } + + /* If it does exist, verify that it's an available CD-ROM */ + cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0); + + /* Should we want to test the condition in more detail: + ENOENT is the error for /dev/xxxxx does not exist; + ENODEV means there's no drive present. */ + + if ( cdfd >= 0 ) { + if ( ioctl(cdfd, CDROMREADTOCHDR, &tochdr) != -1 ) { + is_cd = true; + } + close(cdfd); + } + /* Even if we can't read it, it might be mounted */ + else if ( mnttype && (strcmp(mnttype, "cd9660") == 0) ) { + is_cd = true; + } + return(is_cd); +} + +/*! + Initialize CD device. + */ +static bool +_cdio_init (_img_private_t *p_env) +{ + if (p_env->gen.init) { + cdio_warn ("init called more than once"); + return false; + } + + p_env->gen.fd = open (p_env->gen.source_name, O_RDONLY, 0); + + if (p_env->gen.fd < 0) + { + cdio_warn ("open (%s): %s", p_env->gen.source_name, strerror (errno)); + return false; + } + + p_env->gen.init = true; + p_env->gen.toc_init = false; + return true; +} + +/* Read audio sectors +*/ +static int +_read_audio_sectors_bsdi (void *user_data, void *data, lsn_t lsn, + unsigned int nblocks) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + struct cdrom_msf *msf = (struct cdrom_msf *) &buf; + msf_t _msf; + + _img_private_t *p_env = user_data; + + cdio_lba_to_msf (cdio_lsn_to_lba(lsn), &_msf); + msf->cdmsf_min0 = cdio_from_bcd8(_msf.m); + msf->cdmsf_sec0 = cdio_from_bcd8(_msf.s); + msf->cdmsf_frame0 = cdio_from_bcd8(_msf.f); + + if (p_env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged < 75 + || (p_env->gen.ioctls_debugged < (30 * 75) + && p_env->gen.ioctls_debugged % 75 == 0) + || p_env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %2.2d:%2.2d:%2.2d", + msf->cdmsf_min0, msf->cdmsf_sec0, msf->cdmsf_frame0); + + p_env->gen.ioctls_debugged++; + + switch (p_env->access_mode) { + case _AM_NONE: + cdio_warn ("no way to read audio"); + return 1; + break; + + case _AM_IOCTL: { + unsigned int i; + for (i=0; i < nblocks; i++) { + if (ioctl (p_env->gen.fd, CDROMREADRAW, &buf) == -1) { + perror ("ioctl()"); + return 1; + /* exit (EXIT_FAILURE); */ + } + memcpy (((char *)data) + (CDIO_CD_FRAMESIZE_RAW * i), buf, + CDIO_CD_FRAMESIZE_RAW); + } + break; + } + } + + return 0; +} + +/*! + Reads a single mode1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_read_mode1_sector_bsdi (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + +#if FIXED + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + do something here. +#else + return cdio_generic_read_form1_sector(user_data, data, lsn); +#endif + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode1_sectors_bsdi (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *p_env = user_data; + unsigned int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode1_sector_bsdi (p_env, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_read_mode2_sector_bsdi (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + struct cdrom_msf *msf = (struct cdrom_msf *) &buf; + msf_t _msf; + + _img_private_t *p_env = user_data; + + cdio_lba_to_msf (cdio_lsn_to_lba(lsn), &_msf); + msf->cdmsf_min0 = cdio_from_bcd8(_msf.m); + msf->cdmsf_sec0 = cdio_from_bcd8(_msf.s); + msf->cdmsf_frame0 = cdio_from_bcd8(_msf.f); + + if (p_env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged < 75 + || (p_env->gen.ioctls_debugged < (30 * 75) + && p_env->gen.ioctls_debugged % 75 == 0) + || p_env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %2.2d:%2.2d:%2.2d", + msf->cdmsf_min0, msf->cdmsf_sec0, msf->cdmsf_frame0); + + p_env->gen.ioctls_debugged++; + + switch (p_env->access_mode) + { + case _AM_NONE: + cdio_warn ("no way to read mode2"); + return 1; + break; + + case _AM_IOCTL: + if (ioctl (p_env->gen.fd, CDROMREADMODE2, &buf) == -1) + { + perror ("ioctl()"); + return 1; + /* exit (EXIT_FAILURE); */ + } + break; + } + + if (b_form2) + memcpy (data, buf, M2RAW_SECTOR_SIZE); + else + memcpy (((char *)data), buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode2_sectors_bsdi (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *p_env = user_data; + unsigned int i; + unsigned int i_blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + /* For each frame, pick out the data part we need */ + for (i = 0; i < nblocks; i++) { + int retval = _read_mode2_sector_bsdi(p_env, + ((char *)data) + + (i_blocksize * i), + lsn + i, b_form2); + if (retval) return retval; + } + return 0; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static uint32_t +_stat_size_bsdi (void *user_data) +{ + _img_private_t *p_env = user_data; + + struct cdrom_tocentry tocent; + uint32_t size; + + tocent.cdte_track = CDIO_CDROM_LEADOUT_TRACK; + tocent.cdte_format = CDROM_LBA; + if (ioctl (p_env->gen.fd, CDROMREADTOCENTRY, &tocent) == -1) + { + perror ("ioctl(CDROMREADTOCENTRY)"); + exit (EXIT_FAILURE); + } + + size = tocent.cdte_addr.lba; + + return size; +} + +/*! + Set the key "arg" to "value" in source device. +*/ +static int +_set_arg_bsdi (void *user_data, const char key[], const char value[]) +{ + _img_private_t *p_env = user_data; + + if (!strcmp (key, "source")) + { + if (!value) + return -2; + + free (p_env->gen.source_name); + + p_env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + if (!strcmp(value, "IOCTL")) + p_env->access_mode = _AM_IOCTL; + else + cdio_warn ("unknown access type: %s. ignored.", value); + } + else + return -1; + + return 0; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return false if successful or true if an error. +*/ +static bool +read_toc_bsdi (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + int i; + + /* read TOC header */ + if ( ioctl(p_env->gen.fd, CDROMREADTOCHDR, &p_env->tochdr) == -1 ) { + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCHDR", strerror(errno)); + return false; + } + + p_env->gen.i_first_track = p_env->tochdr.cdth_trk0; + p_env->gen.i_tracks = p_env->tochdr.cdth_trk1; + + /* read individual tracks */ + for (i= p_env->gen.i_first_track; i<=p_env->gen.i_tracks; i++) { + p_env->tocent[i-1].cdte_track = i; + p_env->tocent[i-1].cdte_format = CDROM_MSF; + if (ioctl(p_env->gen.fd, CDROMREADTOCENTRY, &p_env->tocent[i-1]) == -1) { + cdio_warn("%s %d: %s\n", + "error in ioctl CDROMREADTOCENTRY for track", + i, strerror(errno)); + return false; + } + /**** + struct cdrom_msf0 *msf= &p_env->tocent[i-1].cdte_addr.msf; + + fprintf (stdout, "--- track# %d (msf %2.2x:%2.2x:%2.2x)\n", + i, msf->minute, msf->second, msf->frame); + ****/ + + } + + /* read the lead-out track */ + p_env->tocent[p_env->gen.i_tracks].cdte_track = CDIO_CDROM_LEADOUT_TRACK; + p_env->tocent[p_env->gen.i_tracks].cdte_format = CDROM_MSF; + + if (ioctl(p_env->gen.fd, CDROMREADTOCENTRY, + &p_env->tocent[p_env->gen.i_tracks]) == -1 ) { + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCENTRY for lead-out", + strerror(errno)); + return false; + } + + /* + struct cdrom_msf0 *msf= &p_env->tocent[p_env->gen.i_tracks].cdte_addr.msf; + + fprintf (stdout, "--- track# %d (msf %2.2x:%2.2x:%2.2x)\n", + i, msf->minute, msf->second, msf->frame); + */ + + p_env->gen.toc_init = true; + return true; +} + +/*! + Eject media in CD drive. If successful, as a side effect we + also free obj. + */ +static int +_eject_media_bsdi (void *user_data) { + + _img_private_t *p_env = user_data; + int ret=2; + int status; + int fd; + + close(p_env->gen.fd); + p_env->gen.fd = -1; + if ((fd = open (p_env->gen.source_name, O_RDONLY|O_NONBLOCK)) > -1) { + if((status = ioctl(fd, CDROM_DRIVE_STATUS, (void *) CDSL_CURRENT)) > 0) { + switch(status) { + case CDS_TRAY_OPEN: + if((ret = ioctl(fd, CDROMCLOSETRAY, 0)) != 0) { + cdio_warn ("ioctl CDROMCLOSETRAY failed: %s\n", strerror(errno)); + } + break; + case CDS_DISC_OK: + if((ret = ioctl(fd, CDROMEJECT, 0)) != 0) { + cdio_warn("ioctl CDROMEJECT failed: %s\n", strerror(errno)); + } + break; + } + ret=0; + } else { + cdio_warn ("CDROM_DRIVE_STATUS failed: %s\n", strerror(errno)); + ret=1; + } + close(fd); + } + return 2; +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +_get_arg_bsdi (void *user_data, const char key[]) +{ + _img_private_t *p_env = user_data; + + if (!strcmp (key, "source")) { + return p_env->gen.source_name; + } else if (!strcmp (key, "access-mode")) { + switch (p_env->access_mode) { + case _AM_IOCTL: + return "ioctl"; + case _AM_NONE: + return "no access method"; + } + } + return NULL; +} + +/*! + Return the media catalog number MCN. + Note: string is malloc'd so caller should free() then returned + string when done with it. + */ +static char * +_get_mcn_bsdi (const void *user_data) { + + struct cdrom_mcn mcn; + const _img_private_t *p_env = user_data; + if (ioctl(p_env->gen.fd, CDROM_GET_MCN, &mcn) != 0) + return NULL; + return strdup(mcn.medium_catalog_number); +} + +/*! + Get format of track. +*/ +static track_format_t +get_track_format_bsdi(void *user_data, track_t i_track) +{ + _img_private_t *p_env = user_data; + + if (!p_env->gen.toc_init) read_toc_bsdi (p_env) ; + + if (i_track > p_env->gen.i_tracks || i_track == 0) + return TRACK_FORMAT_ERROR; + + i_track -= p_env->gen.i_first_track; + + /* This is pretty much copied from the "badly broken" cdrom_count_tracks + in linux/cdrom.c. + */ + if (p_env->tocent[i_track].cdte_ctrl & CDROM_DATA_TRACK) { + if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_CDI_TRACK) + return TRACK_FORMAT_CDI; + else if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_XA_TRACK) + return TRACK_FORMAT_XA; + else + return TRACK_FORMAT_DATA; + } else + return TRACK_FORMAT_AUDIO; + +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +static bool +_get_track_green_bsdi(void *user_data, track_t i_track) +{ + _img_private_t *p_env = user_data; + + if (!p_env->gen.toc_init) read_toc_bsdi (p_env) ; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = p_env->gen.i_tracks+1; + + if (i_track > p_env->gen.i_tracks+1 || i_track == 0) + return false; + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cdinfo for a while. + */ + return ((p_env->tocent[i_track-1].cdte_ctrl & 2) != 0); +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + i_track in obj. Track numbers start at 1. + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static bool +_get_track_msf_bsdi(void *user_data, track_t i_track, msf_t *msf) +{ + _img_private_t *p_env = user_data; + + if (NULL == msf) return false; + + if (!p_env->gen.toc_init) read_toc_bsdi (p_env) ; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = p_env->gen.i_tracks+1; + + if (i_track > p_env->gen.i_tracks+1 || i_track == 0) { + return false; + } + + i_track -= p_env->gen.i_first_track; + + { + struct cdrom_msf0 *msf0= &p_env->tocent[i_track].cdte_addr.msf; + msf->m = cdio_to_bcd8(msf0->minute); + msf->s = cdio_to_bcd8(msf0->second); + msf->f = cdio_to_bcd8(msf0->frame); + return true; + } +} + +#endif /* HAVE_BSDI_CDROM */ + +/*! + Return an array of strings giving possible CD devices. + */ +char ** +cdio_get_devices_bsdi (void) +{ +#ifndef HAVE_BSDI_CDROM + return NULL; +#else + char drive[40]; + char **drives = NULL; + unsigned int num_drives=0; + bool exists=true; + char c; + + /* Scan the system for CD-ROM drives. + */ + +#ifdef USE_ETC_FSTAB + + struct fstab *fs; + setfsent(); + + /* Check what's in /etc/fstab... */ + while ( (fs = getfsent()) ) + { + if (strncmp(fs->fs_spec, "/dev/sr", 7)) + cdio_add_device_list(&drives, fs->fs_spec, &num_drives); + } + +#endif + + /* Scan the system for CD-ROM drives. + Not always 100% reliable, so use the USE_MNTENT code above first. + */ + for ( c='0'; exists && c <='9'; c++ ) { + sprintf(drive, "/dev/rsr%cc", c); + exists = cdio_is_cdrom(drive, NULL); + if ( exists ) { + cdio_add_device_list(&drives, drive, &num_drives); + } + } + cdio_add_device_list(&drives, NULL, &num_drives); + return drives; +#endif /*HAVE_BSDI_CDROM*/ +} + +/*! + Return a string containing the default CD device if none is specified. + */ +char * +cdio_get_default_device_bsdi(void) +{ + return strdup(DEFAULT_CDIO_DEVICE); +} + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_am_bsdi (const char *psz_source_name, const char *psz_access_mode) +{ + if (psz_access_mode != NULL) + cdio_warn ("there is only one access mode for bsdi. Arg %s ignored", + psz_access_mode); + return cdio_open_bsdi(psz_source_name); +} + + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_bsdi (const char *psz_orig_source) +{ + +#ifdef HAVE_BSDI_CDROM + CdIo *ret; + _img_private_t *_data; + char *psz_source; + + cdio_funcs _funcs = { + .eject_media = _eject_media_bsdi, + .free = cdio_generic_free, + .get_arg = _get_arg_bsdi, + .get_cdtext = get_cdtext_generic, + .get_default_device = cdio_get_default_device_bsdi, + .get_devices = cdio_get_devices_bsdi, + .get_drive_cap = scsi_mmc_get_drive_cap_generic, + .get_discmode = get_discmode_generic, + .get_first_track_num= get_first_track_num_generic, + .get_hwinfo = NULL, + .get_mcn = _get_mcn_bsdi, + .get_num_tracks = get_num_tracks_generic, + .get_track_format = get_track_format_bsdi, + .get_track_green = _get_track_green_bsdi, + .get_track_lba = NULL, /* This could be implemented if need be. */ + .get_track_msf = _get_track_msf_bsdi, + .lseek = cdio_generic_lseek, + .read = cdio_generic_read, + .read_audio_sectors = _read_audio_sectors_bsdi, + .read_mode1_sector = _read_mode1_sector_bsdi, + .read_mode1_sectors = _read_mode1_sectors_bsdi, + .read_mode2_sector = _read_mode2_sector_bsdi, + .read_mode2_sectors = _read_mode2_sectors_bsdi, + .read_toc = &read_toc_bsdi, + .run_scsi_mmc_cmd = &run_scsi_cmd_bsdi, + .set_arg = _set_arg_bsdi, + .stat_size = _stat_size_bsdi + }; + + _data = _cdio_malloc (sizeof (_img_private_t)); + _data->access_mode = _AM_IOCTL; + _data->gen.init = false; + _data->gen.fd = -1; + _data->gen.toc_init = false; + _data->gen.b_cdtext_init = false; + _data->gen.b_cdtext_error = false; + + if (NULL == psz_orig_source) { + psz_source=cdio_get_default_device_linux(); + if (NULL == psz_source) return NULL; + _set_arg_bsdi(_data, "source", psz_source); + free(psz_source); + } else { + if (cdio_is_device_generic(psz_orig_source)) + _set_arg_bsdi(_data, "source", psz_orig_source); + else { + /* The below would be okay if all device drivers worked this way. */ +#if 0 + cdio_info ("source %s is not a device", psz_orig_source); +#endif + return NULL; + } + } + + ret = cdio_new ( (void *) _data, &_funcs); + if (ret == NULL) return NULL; + + if (_cdio_init(_data)) + return ret; + else { + cdio_generic_free (_data); + return NULL; + } + +#else + return NULL; +#endif /* HAVE_BSDI_CDROM */ + +} + +bool +cdio_have_bsdi (void) +{ +#ifdef HAVE_BSDI_CDROM + return true; +#else + return false; +#endif /* HAVE_BSDI_CDROM */ +} diff --git a/contrib/libcdio/_cdio_generic.c b/contrib/libcdio/_cdio_generic.c new file mode 100644 index 000000000..fb3ed5a9c --- /dev/null +++ b/contrib/libcdio/_cdio_generic.c @@ -0,0 +1,426 @@ +/* + $Id: _cdio_generic.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file contains generic implementations of device-dirver routines. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: _cdio_generic.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $"; + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /*HAVE_UNISTD_H*/ + +#include <fcntl.h> + +#include <sys/stat.h> +#include <sys/types.h> + +#include <cdio/sector.h> +#include <cdio/util.h> +#include <cdio/logging.h> +#include "cdio_assert.h" +#include "cdio_private.h" +#include "_cdio_stdio.h" +#include "portable.h" + +/*! + Eject media -- there's nothing to do here. We always return 2. + Should we also free resources? + */ +int +cdio_generic_bogus_eject_media (void *user_data) { + /* Sort of a stub here. Perhaps log a message? */ + return 2; +} + + +/*! + Release and free resources associated with cd. + */ +void +cdio_generic_free (void *p_user_data) +{ + generic_img_private_t *p_env = p_user_data; + track_t i_track; + + if (NULL == p_env) return; + free (p_env->source_name); + + for (i_track=0; i_track < p_env->i_tracks; i_track++) { + cdtext_destroy(&(p_env->cdtext_track[i_track])); + } + + if (p_env->fd >= 0) + close (p_env->fd); + + free (p_env); +} + +/*! + Initialize CD device. + */ +bool +cdio_generic_init (void *user_data) +{ + generic_img_private_t *p_env = user_data; + if (p_env->init) { + cdio_warn ("init called more than once"); + return false; + } + + p_env->fd = open (p_env->source_name, O_RDONLY, 0); + + if (p_env->fd < 0) + { + cdio_warn ("open (%s): %s", p_env->source_name, strerror (errno)); + return false; + } + + p_env->init = true; + p_env->toc_init = false; + p_env->b_cdtext_init = false; + p_env->b_cdtext_error = false; + p_env->i_joliet_level = 0; /* Assume no Joliet extensions initally */ + return true; +} + +/*! + Reads a single form1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +int +cdio_generic_read_form1_sector (void * user_data, void *data, lsn_t lsn) +{ + if (0 > cdio_generic_lseek(user_data, CDIO_CD_FRAMESIZE*lsn, SEEK_SET)) + return -1; + if (0 > cdio_generic_read(user_data, data, CDIO_CD_FRAMESIZE)) + return -1; + return 0; +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + Is in fact libc's lseek(). +*/ +off_t +cdio_generic_lseek (void *user_data, off_t offset, int whence) +{ + generic_img_private_t *p_env = user_data; + return lseek(p_env->fd, offset, whence); +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + Is in fact libc's read(). +*/ +ssize_t +cdio_generic_read (void *user_data, void *buf, size_t size) +{ + generic_img_private_t *p_env = user_data; + return read(p_env->fd, buf, size); +} + +/*! + Release and free resources associated with stream or disk image. +*/ +void +cdio_generic_stdio_free (void *user_data) +{ + generic_img_private_t *p_env = user_data; + + if (NULL == p_env) return; + if (NULL != p_env->source_name) + free (p_env->source_name); + + if (p_env->data_source) + cdio_stdio_destroy (p_env->data_source); +} + + +/*! + Return true if source_name could be a device containing a CD-ROM. +*/ +bool +cdio_is_device_generic(const char *source_name) +{ + struct stat buf; + if (0 != stat(source_name, &buf)) { + cdio_warn ("Can't get file status for %s:\n%s", source_name, + strerror(errno)); + return false; + } + return (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)); +} + +/*! + Like above, but don't give a warning device doesn't exist. +*/ +bool +cdio_is_device_quiet_generic(const char *source_name) +{ + struct stat buf; + if (0 != stat(source_name, &buf)) { + return false; + } + return (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)); +} + +/*! + Add/allocate a drive to the end of drives. + Use cdio_free_device_list() to free this device_list. +*/ +void +cdio_add_device_list(char **device_list[], const char *drive, + unsigned int *num_drives) +{ + if (NULL != drive) { + unsigned int j; + + /* Check if drive is already in list. */ + for (j=0; j<*num_drives; j++) { + if (strcmp((*device_list)[j], drive) == 0) break; + } + + if (j==*num_drives) { + /* Drive not in list. Add it. */ + (*num_drives)++; + if (*device_list) { + *device_list = realloc(*device_list, (*num_drives) * sizeof(char *)); + } else { + /* num_drives should be 0. Add assert? */ + *device_list = malloc((*num_drives) * sizeof(char *)); + } + + (*device_list)[*num_drives-1] = strdup(drive); + } + } else { + (*num_drives)++; + if (*device_list) { + *device_list = realloc(*device_list, (*num_drives) * sizeof(char *)); + } else { + *device_list = malloc((*num_drives) * sizeof(char *)); + } + (*device_list)[*num_drives-1] = NULL; + } +} + + +/*! + Get cdtext information for a CdIo object . + + @param obj the CD object that may contain CD-TEXT information. + @return the CD-TEXT object or NULL if obj is NULL + or CD-TEXT information does not exist. +*/ +const cdtext_t * +get_cdtext_generic (void *p_user_data, track_t i_track) +{ + generic_img_private_t *p_env = p_user_data; + + if ( NULL == p_env || + (0 != i_track + && i_track >= p_env->i_tracks+p_env->i_first_track ) ) + return NULL; + + if (!p_env->b_cdtext_init) + init_cdtext_generic(p_env); + if (!p_env->b_cdtext_init) return NULL; + + if (0 == i_track) + return &(p_env->cdtext); + else + return &(p_env->cdtext_track[i_track-p_env->i_first_track]); + +} + +/*! + Get disc type associated with cd object. +*/ +discmode_t +get_discmode_generic (void *p_user_data ) +{ + generic_img_private_t *p_env = p_user_data; + + /* See if this is a DVD. */ + cdio_dvd_struct_t dvd; /* DVD READ STRUCT for layer 0. */ + + dvd.physical.type = CDIO_DVD_STRUCT_PHYSICAL; + dvd.physical.layer_num = 0; + if (0 == scsi_mmc_get_dvd_struct_physical (p_env->cdio, &dvd)) { + switch(dvd.physical.layer[0].book_type) { + case CDIO_DVD_BOOK_DVD_ROM: return CDIO_DISC_MODE_DVD_ROM; + case CDIO_DVD_BOOK_DVD_RAM: return CDIO_DISC_MODE_DVD_RAM; + case CDIO_DVD_BOOK_DVD_R: return CDIO_DISC_MODE_DVD_R; + case CDIO_DVD_BOOK_DVD_RW: return CDIO_DISC_MODE_DVD_RW; + case CDIO_DVD_BOOK_DVD_PR: return CDIO_DISC_MODE_DVD_PR; + case CDIO_DVD_BOOK_DVD_PRW: return CDIO_DISC_MODE_DVD_PRW; + default: return CDIO_DISC_MODE_DVD_OTHER; + } + } + + return get_discmode_cd_generic(p_user_data); +} + +/*! + Get disc type associated with cd object. +*/ +discmode_t +get_discmode_cd_generic (void *p_user_data ) +{ + generic_img_private_t *p_env = p_user_data; + track_t i_track; + discmode_t discmode=CDIO_DISC_MODE_NO_INFO; + + if (!p_env->toc_init) + p_env->cdio->op.read_toc (p_user_data); + + if (!p_env->toc_init) + return CDIO_DISC_MODE_NO_INFO; + + for (i_track = p_env->i_first_track; + i_track < p_env->i_first_track + p_env->i_tracks ; + i_track ++) { + track_format_t track_fmt = + p_env->cdio->op.get_track_format(p_env, i_track); + + switch(track_fmt) { + case TRACK_FORMAT_AUDIO: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DA; + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_XA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_DATA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DATA; + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_ERROR: + default: + discmode = CDIO_DISC_MODE_ERROR; + } + } + return discmode; +} + +/*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. +*/ +track_t +get_first_track_num_generic(void *p_user_data) +{ + generic_img_private_t *p_env = p_user_data; + + if (!p_env->toc_init) + p_env->cdio->op.read_toc (p_user_data); + + return p_env->toc_init ? p_env->i_first_track : CDIO_INVALID_TRACK; +} + + +/*! + Return the number of tracks in the current medium. +*/ + track_t +get_num_tracks_generic(void *p_user_data) +{ + generic_img_private_t *p_env = p_user_data; + + if (!p_env->toc_init) + p_env->cdio->op.read_toc (p_user_data); + + return p_env->toc_init ? p_env->i_tracks : CDIO_INVALID_TRACK; +} + +void +set_cdtext_field_generic(void *user_data, track_t i_track, + track_t i_first_track, + cdtext_field_t e_field, const char *psz_value) +{ + char **pp_field; + generic_img_private_t *env = user_data; + + if( i_track == 0 ) + pp_field = &(env->cdtext.field[e_field]); + + else + pp_field = &(env->cdtext_track[i_track-i_first_track].field[e_field]); + + *pp_field = strdup(psz_value); +} + +/*! + Read CD-Text information for a CdIo object . + + return true on success, false on error or CD-TEXT information does + not exist. +*/ +bool +init_cdtext_generic (generic_img_private_t *p_env) +{ + return scsi_mmc_init_cdtext_private( p_env, + p_env->cdio->op.run_scsi_mmc_cmd, + set_cdtext_field_generic + ); +} + diff --git a/contrib/libcdio/_cdio_linux.c b/contrib/libcdio/_cdio_linux.c new file mode 100644 index 000000000..ea70d262c --- /dev/null +++ b/contrib/libcdio/_cdio_linux.c @@ -0,0 +1,1197 @@ +/* + $Id: _cdio_linux.c,v 1.4 2006/09/26 22:18:44 dgp85 Exp $ + + Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2002, 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file contains Linux-specific code and implements low-level + control of the CD drive. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static const char _rcsid[] = "$Id: _cdio_linux.c,v 1.4 2006/09/26 22:18:44 dgp85 Exp $"; + +#include <string.h> + +#include <cdio/sector.h> +#include <cdio/util.h> +#include <cdio/types.h> +#include <cdio/scsi_mmc.h> +#include <cdio/cdtext.h> +#include "cdtext_private.h" +#include "cdio_assert.h" +#include "cdio_private.h" + +#ifdef HAVE_LINUX_CDROM + +#if defined(HAVE_LINUX_VERSION_H) +# include <linux/version.h> +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,16) +# define __CDIO_LINUXCD_BUILD +# else +# error "You need a kernel greater than 2.2.16 to have CDROM support" +# endif +#else +# error "You need <linux/version.h> to have CDROM support" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <mntent.h> + +#include <linux/cdrom.h> +#include <scsi/scsi.h> +#include <scsi/sg.h> +#include <scsi/scsi_ioctl.h> +#include <sys/mount.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/ioctl.h> + +typedef enum { + _AM_NONE, + _AM_IOCTL, + _AM_READ_CD, + _AM_READ_10 +} access_mode_t; + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + + access_mode_t access_mode; + + /* Some of the more OS specific things. */ + /* Entry info for each track, add 1 for leadout. */ + struct cdrom_tocentry tocent[CDIO_CD_MAX_TRACKS+1]; + + struct cdrom_tochdr tochdr; + +} _img_private_t; + +/* Some ioctl() errno values which occur when the tray is empty */ +#define ERRNO_TRAYEMPTY(errno) \ + ((errno == EIO) || (errno == ENOENT) || (errno == EINVAL)) + +/**** prototypes for static functions ****/ +static bool is_cdrom_linux(const char *drive, char *mnttype); +static bool read_toc_linux (void *p_user_data); +static int run_scsi_cmd_linux( const void *p_user_data, + unsigned int i_timeout, + unsigned int i_cdb, + const scsi_mmc_cdb_t *p_cdb, + scsi_mmc_direction_t e_direction, + unsigned int i_buf, + /*in/out*/ void *p_buf ); +static access_mode_t + +str_to_access_mode_linux(const char *psz_access_mode) +{ + const access_mode_t default_access_mode = _AM_IOCTL; + + if (NULL==psz_access_mode) return default_access_mode; + + if (!strcmp(psz_access_mode, "IOCTL")) + return _AM_IOCTL; + else if (!strcmp(psz_access_mode, "READ_CD")) + return _AM_READ_CD; + else if (!strcmp(psz_access_mode, "READ_10")) + return _AM_READ_10; + else { + cdio_warn ("unknown access type: %s. Default IOCTL used.", + psz_access_mode); + return default_access_mode; + } +} + +static char * +check_mounts_linux(const char *mtab) +{ + FILE *mntfp; + struct mntent *mntent; + + mntfp = setmntent(mtab, "r"); + if ( mntfp != NULL ) { + char *tmp; + char *mnt_type; + char *mnt_dev; + + while ( (mntent=getmntent(mntfp)) != NULL ) { + mnt_type = malloc(strlen(mntent->mnt_type) + 1); + if (mnt_type == NULL) + continue; /* maybe you'll get lucky next time. */ + + mnt_dev = malloc(strlen(mntent->mnt_fsname) + 1); + if (mnt_dev == NULL) { + free(mnt_type); + continue; + } + + strcpy(mnt_type, mntent->mnt_type); + strcpy(mnt_dev, mntent->mnt_fsname); + + /* Handle "supermount" filesystem mounts */ + if ( strcmp(mnt_type, "supermount") == 0 ) { + tmp = strstr(mntent->mnt_opts, "fs="); + if ( tmp ) { + free(mnt_type); + mnt_type = strdup(tmp + strlen("fs=")); + if ( mnt_type ) { + tmp = strchr(mnt_type, ','); + if ( tmp ) { + *tmp = '\0'; + } + } + } + tmp = strstr(mntent->mnt_opts, "dev="); + if ( tmp ) { + free(mnt_dev); + mnt_dev = strdup(tmp + strlen("dev=")); + if ( mnt_dev ) { + tmp = strchr(mnt_dev, ','); + if ( tmp ) { + *tmp = '\0'; + } + } + } + } + if ( strcmp(mnt_type, "iso9660") == 0 ) { + if (is_cdrom_linux(mnt_dev, mnt_type) > 0) { + free(mnt_type); + endmntent(mntfp); + return mnt_dev; + } + } + free(mnt_dev); + free(mnt_type); + } + endmntent(mntfp); + } + return NULL; +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +get_arg_linux (void *env, const char key[]) +{ + _img_private_t *_obj = env; + + if (!strcmp (key, "source")) { + return _obj->gen.source_name; + } else if (!strcmp (key, "access-mode")) { + switch (_obj->access_mode) { + case _AM_IOCTL: + return "ioctl"; + case _AM_READ_CD: + return "READ_CD"; + case _AM_READ_10: + return "READ_10"; + case _AM_NONE: + return "no access method"; + } + } + return NULL; +} + +#undef USE_LINUX_CAP +#ifdef USE_LINUX_CAP +/*! + Return the the kind of drive capabilities of device. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +static void +get_drive_cap_linux (const void *p_user_data, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap) +{ + const _img_private_t *p_env = p_user_data; + int32_t i_drivetype; + + i_drivetype = ioctl (p_env->gen.fd, CDROM_GET_CAPABILITY, CDSL_CURRENT); + + if (i_drivetype < 0) { + *p_read_cap = CDIO_DRIVE_CAP_ERROR; + *p_write_cap = CDIO_DRIVE_CAP_ERROR; + *p_misc_cap = CDIO_DRIVE_CAP_ERROR; + return; + } + + *p_read_cap = 0; + *p_write_cap = 0; + *p_misc_cap = 0; + + /* Reader */ + if (i_drivetype & CDC_PLAY_AUDIO) + *p_read_cap |= CDIO_DRIVE_CAP_READ_AUDIO; + if (i_drivetype & CDC_CD_R) + *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_R; + if (i_drivetype & CDC_CD_RW) + *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_RW; + if (i_drivetype & CDC_DVD) + *p_read_cap |= CDIO_DRIVE_CAP_READ_DVD_ROM; + + /* Writer */ + if (i_drivetype & CDC_CD_RW) + *p_read_cap |= CDIO_DRIVE_CAP_WRITE_CD_RW; + if (i_drivetype & CDC_DVD_R) + *p_read_cap |= CDIO_DRIVE_CAP_WRITE_DVD_R; + if (i_drivetype & CDC_DVD_RAM) + *p_read_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RAM; + + /* Misc */ + if (i_drivetype & CDC_CLOSE_TRAY) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_CLOSE_TRAY; + if (i_drivetype & CDC_OPEN_TRAY) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_EJECT; + if (i_drivetype & CDC_LOCK) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_LOCK; + if (i_drivetype & CDC_SELECT_SPEED) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_SELECT_SPEED; + if (i_drivetype & CDC_SELECT_DISC) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_SELECT_DISC; + if (i_drivetype & CDC_MULTI_SESSION) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_MULTI_SESSION; + if (i_drivetype & CDC_MEDIA_CHANGED) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED; + if (i_drivetype & CDC_RESET) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_RESET; +} +#endif + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +static char * +get_mcn_linux (const void *p_user_data) { + + struct cdrom_mcn mcn; + const _img_private_t *p_env = p_user_data; + memset(&mcn, 0, sizeof(mcn)); + if (ioctl(p_env->gen.fd, CDROM_GET_MCN, &mcn) != 0) + return NULL; + return strdup(mcn.medium_catalog_number); +} + +/*! + Get format of track. +*/ +static track_format_t +get_track_format_linux(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if ( !p_env ) return TRACK_FORMAT_ERROR; + + if (!p_env->gen.toc_init) read_toc_linux (p_user_data) ; + + if (i_track > (p_env->gen.i_tracks+p_env->gen.i_first_track) + || i_track < p_env->gen.i_first_track) + return TRACK_FORMAT_ERROR; + + i_track -= p_env->gen.i_first_track; + + /* This is pretty much copied from the "badly broken" cdrom_count_tracks + in linux/cdrom.c. + */ + if (p_env->tocent[i_track].cdte_ctrl & CDIO_CDROM_DATA_TRACK) { + if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_CDI_TRACK) + return TRACK_FORMAT_CDI; + else if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_XA_TRACK) + return TRACK_FORMAT_XA; + else + return TRACK_FORMAT_DATA; + } else + return TRACK_FORMAT_AUDIO; + +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +static bool +get_track_green_linux(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if (!p_env->gen.toc_init) read_toc_linux (p_user_data) ; + + if (i_track >= (p_env->gen.i_tracks+p_env->gen.i_first_track) + || i_track < p_env->gen.i_first_track) + return false; + + i_track -= p_env->gen.i_first_track; + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cd-info for a while. + */ + return ((p_env->tocent[i_track].cdte_ctrl & 2) != 0); +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in obj. Track numbers usually start at something + greater than 0, usually 1. + + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static bool +get_track_msf_linux(void *p_user_data, track_t i_track, msf_t *msf) +{ + _img_private_t *p_env = p_user_data; + + if (NULL == msf) return false; + + if (!p_env->gen.toc_init) read_toc_linux (p_user_data) ; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) + i_track = p_env->gen.i_tracks + p_env->gen.i_first_track; + + if (i_track > (p_env->gen.i_tracks+p_env->gen.i_first_track) + || i_track < p_env->gen.i_first_track) { + return false; + } else { + struct cdrom_msf0 *msf0= + &p_env->tocent[i_track-p_env->gen.i_first_track].cdte_addr.msf; + msf->m = cdio_to_bcd8(msf0->minute); + msf->s = cdio_to_bcd8(msf0->second); + msf->f = cdio_to_bcd8(msf0->frame); + return true; + } +} + +/*! + Eject media in CD drive. + Return 0 if success and 1 for failure, and 2 if no routine. + */ +static int +eject_media_linux (void *p_user_data) { + + _img_private_t *p_env = p_user_data; + int ret=2; + int status; + int fd; + + if ((fd = open (p_env->gen.source_name, O_RDONLY|O_NONBLOCK)) > -1) { + if((status = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) > 0) { + switch(status) { + case CDS_TRAY_OPEN: + if((ret = ioctl(fd, CDROMCLOSETRAY)) != 0) { + cdio_warn ("ioctl CDROMCLOSETRAY failed: %s\n", strerror(errno)); + ret = 1; + } + break; + case CDS_DISC_OK: + if((ret = ioctl(fd, CDROMEJECT)) != 0) { + int eject_error = errno; + /* Try ejecting the MMC way... */ + ret = scsi_mmc_eject_media(p_env->gen.cdio); + if (0 != ret) { + cdio_warn("ioctl CDROMEJECT failed: %s\n", + strerror(eject_error)); + ret = 1; + } + } + /* force kernel to reread partition table when new disc inserted */ + ret = ioctl(p_env->gen.fd, BLKRRPART); + break; + default: + cdio_warn ("Unknown CD-ROM (%d)\n", status); + ret = 1; + } + } else { + cdio_warn ("CDROM_DRIVE_STATUS failed: %s\n", strerror(errno)); + ret=1; + } + close(fd); + } else + ret = 2; + close(p_env->gen.fd); + p_env->gen.fd = -1; + return ret; +} + +/*! + Get disc type associated with the cd object. +*/ +static discmode_t +get_discmode_linux (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + int32_t i_discmode; + + /* See if this is a DVD. */ + cdio_dvd_struct_t dvd; /* DVD READ STRUCT for layer 0. */ + + dvd.physical.type = CDIO_DVD_STRUCT_PHYSICAL; + dvd.physical.layer_num = 0; + if (0 == ioctl (p_env->gen.fd, DVD_READ_STRUCT, &dvd)) { + switch(dvd.physical.layer[0].book_type) { + case CDIO_DVD_BOOK_DVD_ROM: return CDIO_DISC_MODE_DVD_ROM; + case CDIO_DVD_BOOK_DVD_RAM: return CDIO_DISC_MODE_DVD_RAM; + case CDIO_DVD_BOOK_DVD_R: return CDIO_DISC_MODE_DVD_R; + case CDIO_DVD_BOOK_DVD_RW: return CDIO_DISC_MODE_DVD_RW; + case CDIO_DVD_BOOK_DVD_PR: return CDIO_DISC_MODE_DVD_PR; + case CDIO_DVD_BOOK_DVD_PRW: return CDIO_DISC_MODE_DVD_PRW; + default: return CDIO_DISC_MODE_DVD_OTHER; + } + } + + i_discmode = ioctl (p_env->gen.fd, CDROM_DISC_STATUS); + + if (i_discmode < 0) return CDIO_DISC_MODE_ERROR; + + /* FIXME Need to add getting DVD types. */ + switch(i_discmode) { + case CDS_AUDIO: + return CDIO_DISC_MODE_CD_DA; + case CDS_DATA_1: + case CDS_DATA_2: + return CDIO_DISC_MODE_CD_DATA; + case CDS_MIXED: + return CDIO_DISC_MODE_CD_MIXED; + case CDS_XA_2_1: + case CDS_XA_2_2: + return CDIO_DISC_MODE_CD_XA; + case CDS_NO_INFO: + return CDIO_DISC_MODE_NO_INFO; + default: + return CDIO_DISC_MODE_ERROR; + } +} + +/* Check a drive to see if it is a CD-ROM + Return 1 if a CD-ROM. 0 if it exists but isn't a CD-ROM drive + and -1 if no device exists . +*/ +static bool +is_cdrom_linux(const char *drive, char *mnttype) +{ + bool is_cd=false; + int cdfd; + struct cdrom_tochdr tochdr; + + /* If it doesn't exist, return -1 */ + if ( !cdio_is_device_quiet_generic(drive) ) { + return(false); + } + + /* If it does exist, verify that it's an available CD-ROM */ + cdfd = open(drive, (O_RDONLY|O_NONBLOCK), 0); + if ( cdfd >= 0 ) { + if ( ioctl(cdfd, CDROMREADTOCHDR, &tochdr) != -1 ) { + is_cd = true; + } + close(cdfd); + } + /* Even if we can't read it, it might be mounted */ + else if ( mnttype && (strcmp(mnttype, "iso9660") == 0) ) { + is_cd = true; + } + return(is_cd); +} + +/* MMC driver to read audio sectors. + Can read only up to 25 blocks. +*/ +static int +_read_audio_sectors_linux (void *p_user_data, void *buf, lsn_t lsn, + unsigned int nblocks) +{ + _img_private_t *p_env = p_user_data; + return scsi_mmc_read_sectors( p_env->gen.cdio, buf, lsn, + CDIO_MMC_READ_TYPE_CDDA, nblocks); +} + +/* Packet driver to read mode2 sectors. + Can read only up to 25 blocks. +*/ +static int +_read_mode2_sectors_mmc (_img_private_t *p_env, void *p_buf, lba_t lba, + unsigned int nblocks, bool b_read_10) +{ + scsi_mmc_cdb_t cdb = {{0, }}; + + CDIO_MMC_SET_READ_LBA(cdb.field, lba); + + if (b_read_10) { + int retval; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_10); + CDIO_MMC_SET_READ_LENGTH16(cdb.field, nblocks); + + if ((retval = scsi_mmc_set_blocksize (p_env->gen.cdio, M2RAW_SECTOR_SIZE))) + return retval; + + if ((retval = run_scsi_cmd_linux (p_env, 0, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, + SCSI_MMC_DATA_READ, + M2RAW_SECTOR_SIZE * nblocks, + p_buf))) + { + scsi_mmc_set_blocksize (p_env->gen.cdio, CDIO_CD_FRAMESIZE); + return retval; + } + + if ((retval = scsi_mmc_set_blocksize (p_env->gen.cdio, CDIO_CD_FRAMESIZE))) + return retval; + } else + + cdb.field[1] = 0; /* sector size mode2 */ + cdb.field[9] = 0x58; /* 2336 mode2 */ + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_CD); + CDIO_MMC_SET_READ_LENGTH24(cdb.field, nblocks); + + return run_scsi_cmd_linux (p_env, 0, + scsi_mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_READ, + M2RAW_SECTOR_SIZE * nblocks, p_buf); + + return 0; +} + +static int +_read_mode2_sectors (_img_private_t *p_env, void *p_buf, lba_t lba, + unsigned int nblocks, bool b_read_10) +{ + unsigned int l = 0; + int retval = 0; + + while (nblocks > 0) + { + const unsigned nblocks2 = (nblocks > 25) ? 25 : nblocks; + void *p_buf2 = ((char *)p_buf ) + (l * M2RAW_SECTOR_SIZE); + + retval |= _read_mode2_sectors_mmc (p_env, p_buf2, lba + l, + nblocks2, b_read_10); + + if (retval) + break; + + nblocks -= nblocks2; + l += nblocks2; + } + + return retval; +} + +/*! + Reads a single mode1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_read_mode1_sector_linux (void *p_user_data, void *p_data, lsn_t lsn, + bool b_form2) +{ + +#if FIXED + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + struct cdrom_msf *p_msf = (struct cdrom_msf *) &buf; + msf_t _msf; + + _img_private_t *p_env = p_user_data; + + cdio_lba_to_msf (cdio_lsn_to_lba(lsn), &_msf); + msf->cdmsf_min0 = cdio_from_bcd8(_msf.m); + msf->cdmsf_sec0 = cdio_from_bcd8(_msf.s); + msf->cdmsf_frame0 = cdio_from_bcd8(_msf.f); + + retry: + switch (p_env->access_mode) + { + case _AM_NONE: + cdio_warn ("no way to read mode1"); + return 1; + break; + + case _AM_IOCTL: + if (ioctl (p_env->gen.fd, CDROMREADMODE1, &buf) == -1) + { + perror ("ioctl()"); + return 1; + /* exit (EXIT_FAILURE); */ + } + break; + + case _AM_READ_CD: + case _AM_READ_10: + if (_read_mode2_sectors (p_env->gen.fd, buf, lsn, 1, + (p_env->access_mode == _AM_READ_10))) + { + perror ("ioctl()"); + if (p_env->access_mode == _AM_READ_CD) + { + cdio_info ("READ_CD failed; switching to READ_10 mode..."); + p_env->access_mode = _AM_READ_10; + goto retry; + } + else + { + cdio_info ("READ_10 failed; switching to ioctl(CDROMREADMODE2) mode..."); + p_env->access_mode = _AM_IOCTL; + goto retry; + } + return 1; + } + break; + } + + memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE); + +#else + return cdio_generic_read_form1_sector(p_user_data, p_data, lsn); +#endif + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode1_sectors_linux (void *p_user_data, void *p_data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *p_env = p_user_data; + unsigned int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode1_sector_linux (p_env, + ((char *)p_data) + (blocksize*i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_read_mode2_sector_linux (void *p_user_data, void *p_data, lsn_t lsn, + bool b_form2) +{ + char buf[M2RAW_SECTOR_SIZE] = { 0, }; + struct cdrom_msf *msf = (struct cdrom_msf *) &buf; + msf_t _msf; + + _img_private_t *p_env = p_user_data; + + cdio_lba_to_msf (cdio_lsn_to_lba(lsn), &_msf); + msf->cdmsf_min0 = cdio_from_bcd8(_msf.m); + msf->cdmsf_sec0 = cdio_from_bcd8(_msf.s); + msf->cdmsf_frame0 = cdio_from_bcd8(_msf.f); + + retry: + switch (p_env->access_mode) + { + case _AM_NONE: + cdio_warn ("no way to read mode2"); + return 1; + break; + + case _AM_IOCTL: + if (ioctl (p_env->gen.fd, CDROMREADMODE2, &buf) == -1) + { + perror ("ioctl()"); + return 1; + /* exit (EXIT_FAILURE); */ + } + break; + + case _AM_READ_CD: + case _AM_READ_10: + if (_read_mode2_sectors (p_env, buf, lsn, 1, + (p_env->access_mode == _AM_READ_10))) + { + perror ("ioctl()"); + if (p_env->access_mode == _AM_READ_CD) + { + cdio_info ("READ_CD failed; switching to READ_10 mode..."); + p_env->access_mode = _AM_READ_10; + goto retry; + } + else + { + cdio_info ("READ_10 failed; switching to ioctl(CDROMREADMODE2) mode..."); + p_env->access_mode = _AM_IOCTL; + goto retry; + } + return 1; + } + break; + } + + if (b_form2) + memcpy (p_data, buf, M2RAW_SECTOR_SIZE); + else + memcpy (((char *)p_data), buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode2_sectors_linux (void *p_user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *p_env = p_user_data; + unsigned int i; + unsigned int i_blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + /* For each frame, pick out the data part we need */ + for (i = 0; i < nblocks; i++) { + int retval; + if ( (retval = _read_mode2_sector_linux (p_env, + ((char *)data) + (i_blocksize*i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return false if successful or true if an error. +*/ +static bool +read_toc_linux (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + int i; + + /* read TOC header */ + if ( ioctl(p_env->gen.fd, CDROMREADTOCHDR, &p_env->tochdr) == -1 ) { + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCHDR", strerror(errno)); + return false; + } + + p_env->gen.i_first_track = p_env->tochdr.cdth_trk0; + p_env->gen.i_tracks = p_env->tochdr.cdth_trk1; + + /* read individual tracks */ + for (i= p_env->gen.i_first_track; i<=p_env->gen.i_tracks; i++) { + p_env->tocent[i-p_env->gen.i_first_track].cdte_track = i; + p_env->tocent[i-p_env->gen.i_first_track].cdte_format = CDROM_MSF; + if ( ioctl(p_env->gen.fd, CDROMREADTOCENTRY, + &p_env->tocent[i-p_env->gen.i_first_track]) == -1 ) { + cdio_warn("%s %d: %s\n", + "error in ioctl CDROMREADTOCENTRY for track", + i, strerror(errno)); + return false; + } + /**** + struct cdrom_msf0 *msf= &env->tocent[i-1].cdte_addr.msf; + + fprintf (stdout, "--- track# %d (msf %2.2x:%2.2x:%2.2x)\n", + i, msf->minute, msf->second, msf->frame); + ****/ + + } + + /* read the lead-out track */ + p_env->tocent[p_env->gen.i_tracks].cdte_track = CDIO_CDROM_LEADOUT_TRACK; + p_env->tocent[p_env->gen.i_tracks].cdte_format = CDROM_MSF; + + if (ioctl(p_env->gen.fd, CDROMREADTOCENTRY, + &p_env->tocent[p_env->gen.i_tracks]) == -1 ) { + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCENTRY for lead-out", + strerror(errno)); + return false; + } + + /* + struct cdrom_msf0 *msf= &env->tocent[p_env->gen.i_tracks].cdte_addr.msf; + + fprintf (stdout, "--- track# %d (msf %2.2x:%2.2x:%2.2x)\n", + i, msf->minute, msf->second, msf->frame); + */ + + p_env->gen.toc_init = true; + return true; +} + +/*! + Run a SCSI MMC command. + + cdio CD structure set by cdio_open(). + i_timeout time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + We return true if command completed successfully and false if not. + */ +static int +run_scsi_cmd_linux( const void *p_user_data, + unsigned int i_timeout_ms, + unsigned int i_cdb, const scsi_mmc_cdb_t *p_cdb, + scsi_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + struct cdrom_generic_command cgc; + memset (&cgc, 0, sizeof (struct cdrom_generic_command)); + memcpy(&cgc.cmd, p_cdb, i_cdb); + cgc.buflen = i_buf; + cgc.buffer = p_buf; + cgc.data_direction = (SCSI_MMC_DATA_READ == cgc.data_direction) + ? CGC_DATA_READ : CGC_DATA_WRITE; + +#ifdef HAVE_LINUX_CDROM_TIMEOUT + cgc.timeout = i_timeout_ms; +#endif + + return ioctl (p_env->gen.fd, CDROM_SEND_PACKET, &cgc); +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static uint32_t +stat_size_linux (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + + struct cdrom_tocentry tocent; + uint32_t size; + + tocent.cdte_track = CDIO_CDROM_LEADOUT_TRACK; + tocent.cdte_format = CDROM_LBA; + if (ioctl (p_env->gen.fd, CDROMREADTOCENTRY, &tocent) == -1) + { + perror ("ioctl(CDROMREADTOCENTRY)"); + exit (EXIT_FAILURE); + } + + size = tocent.cdte_addr.lba; + + return size; +} + +/*! + Set the arg "key" with "value" in the source device. + Currently "source" and "access-mode" are valid keys. + "source" sets the source device in I/O operations + "access-mode" sets the the method of CD access + + 0 is returned if no error was found, and nonzero if there as an error. +*/ +static int +set_arg_linux (void *p_user_data, const char key[], const char value[]) +{ + _img_private_t *p_env = p_user_data; + + if (!strcmp (key, "source")) + { + if (!value) + return -2; + + free (p_env->gen.source_name); + + p_env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + return str_to_access_mode_linux(value); + } + else + return -1; + + return 0; +} + +/* checklist: /dev/cdrom, /dev/dvd /dev/hd?, /dev/scd? /dev/sr? */ +static char checklist1[][40] = { + {"cdrom"}, {"dvd"}, {""} +}; +static char checklist2[][40] = { + {"?a hd?"}, {"?0 scd?"}, {"?0 sr?"}, {""} +}; + +#endif /* HAVE_LINUX_CDROM */ + +/*! + Return an array of strings giving possible CD devices. + */ +char ** +cdio_get_devices_linux (void) +{ +#ifndef HAVE_LINUX_CDROM + return NULL; +#else + unsigned int i; + char drive[40]; + char *ret_drive; + bool exists; + char **drives = NULL; + unsigned int num_drives=0; + + /* Scan the system for CD-ROM drives. + */ + for ( i=0; strlen(checklist1[i]) > 0; ++i ) { + sprintf(drive, "/dev/%s", checklist1[i]); + if ( (exists=is_cdrom_linux(drive, NULL)) > 0 ) { + cdio_add_device_list(&drives, drive, &num_drives); + } + } + + /* Now check the currently mounted CD drives */ + if (NULL != (ret_drive = check_mounts_linux("/etc/mtab"))) { + cdio_add_device_list(&drives, ret_drive, &num_drives); + free(ret_drive); + } + + /* Finally check possible mountable drives in /etc/fstab */ + if (NULL != (ret_drive = check_mounts_linux("/etc/fstab"))) { + cdio_add_device_list(&drives, ret_drive, &num_drives); + free(ret_drive); + } + + /* Scan the system for CD-ROM drives. + Not always 100% reliable, so use the USE_MNTENT code above first. + */ + for ( i=0; strlen(checklist2[i]) > 0; ++i ) { + unsigned int j; + char *insert; + exists = true; + for ( j=checklist2[i][1]; exists; ++j ) { + sprintf(drive, "/dev/%s", &checklist2[i][3]); + insert = strchr(drive, '?'); + if ( insert != NULL ) { + *insert = j; + } + if ( (exists=is_cdrom_linux(drive, NULL)) > 0 ) { + cdio_add_device_list(&drives, drive, &num_drives); + } + } + } + cdio_add_device_list(&drives, NULL, &num_drives); + return drives; +#endif /*HAVE_LINUX_CDROM*/ +} + +/*! + Return a string containing the default CD device. + */ +char * +cdio_get_default_device_linux(void) +{ +#ifndef HAVE_LINUX_CDROM + return NULL; + +#else + unsigned int i; + char drive[40]; + bool exists; + char *ret_drive; + + /* Scan the system for CD-ROM drives. + */ + for ( i=0; strlen(checklist1[i]) > 0; ++i ) { + sprintf(drive, "/dev/%s", checklist1[i]); + if ( (exists=is_cdrom_linux(drive, NULL)) > 0 ) { + return strdup(drive); + } + } + + /* Now check the currently mounted CD drives */ + if (NULL != (ret_drive = check_mounts_linux("/etc/mtab"))) + return ret_drive; + + /* Finally check possible mountable drives in /etc/fstab */ + if (NULL != (ret_drive = check_mounts_linux("/etc/fstab"))) + return ret_drive; + + /* Scan the system for CD-ROM drives. + Not always 100% reliable, so use the USE_MNTENT code above first. + */ + for ( i=0; strlen(checklist2[i]) > 0; ++i ) { + unsigned int j; + char *insert; + exists = true; + for ( j=checklist2[i][1]; exists; ++j ) { + sprintf(drive, "/dev/%s", &checklist2[i][3]); + insert = strchr(drive, '?'); + if ( insert != NULL ) { + *insert = j; + } + if ( (exists=is_cdrom_linux(drive, NULL)) > 0 ) { + return(strdup(drive)); + } + } + } + return NULL; +#endif /*HAVE_LINUX_CDROM*/ +} +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_linux (const char *psz_source_name) +{ + return cdio_open_am_linux(psz_source_name, NULL); +} + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_am_linux (const char *psz_orig_source, const char *access_mode) +{ + +#ifdef HAVE_LINUX_CDROM + CdIo *ret; + _img_private_t *_data; + char *psz_source; + + cdio_funcs _funcs = { + .eject_media = eject_media_linux, + .free = cdio_generic_free, + .get_arg = get_arg_linux, + .get_cdtext = get_cdtext_generic, + .get_default_device = cdio_get_default_device_linux, + .get_devices = cdio_get_devices_linux, + .get_discmode = get_discmode_linux, +#if USE_LINUX_CAP + .get_drive_cap = get_drive_cap_linux, +#else + .get_drive_cap = scsi_mmc_get_drive_cap_generic, +#endif + .get_first_track_num= get_first_track_num_generic, + .get_hwinfo = NULL, + .get_mcn = get_mcn_linux, + .get_num_tracks = get_num_tracks_generic, + .get_track_format = get_track_format_linux, + .get_track_green = get_track_green_linux, + .get_track_lba = NULL, /* This could be implemented if need be. */ + .get_track_msf = get_track_msf_linux, + .lseek = cdio_generic_lseek, + .read = cdio_generic_read, + .read_audio_sectors = _read_audio_sectors_linux, + .read_mode1_sector = _read_mode1_sector_linux, + .read_mode1_sectors = _read_mode1_sectors_linux, + .read_mode2_sector = _read_mode2_sector_linux, + .read_mode2_sectors = _read_mode2_sectors_linux, + .read_toc = read_toc_linux, + .run_scsi_mmc_cmd = run_scsi_cmd_linux, + .set_arg = set_arg_linux, + .stat_size = stat_size_linux + }; + + _data = _cdio_malloc (sizeof (_img_private_t)); + + _data->access_mode = str_to_access_mode_linux(access_mode); + _data->gen.init = false; + _data->gen.toc_init = false; + _data->gen.fd = -1; + _data->gen.b_cdtext_init = false; + _data->gen.b_cdtext_error = false; + + if (NULL == psz_orig_source) { + psz_source=cdio_get_default_device_linux(); + if (NULL == psz_source) { + free(_data); + return NULL; + } + set_arg_linux(_data, "source", psz_source); + free(psz_source); + } else { + if (cdio_is_device_generic(psz_orig_source)) + set_arg_linux(_data, "source", psz_orig_source); + else { + /* The below would be okay if all device drivers worked this way. */ +#if 0 + cdio_info ("source %s is not a device", psz_orig_source); +#endif + free(_data); + return NULL; + } + } + + ret = cdio_new ((void *)_data, &_funcs); + if (ret == NULL) return NULL; + + if (cdio_generic_init(_data)) { + return ret; + } else { + cdio_generic_free (_data); + return NULL; + } + +#else + return NULL; +#endif /* HAVE_LINUX_CDROM */ + +} + +bool +cdio_have_linux (void) +{ +#ifdef HAVE_LINUX_CDROM + return true; +#else + return false; +#endif /* HAVE_LINUX_CDROM */ +} diff --git a/contrib/libcdio/_cdio_osx.c b/contrib/libcdio/_cdio_osx.c new file mode 100644 index 000000000..f754933c6 --- /dev/null +++ b/contrib/libcdio/_cdio_osx.c @@ -0,0 +1,1470 @@ +/* + $Id: _cdio_osx.c,v 1.4 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + from vcdimager code: + Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org> + and VideoLAN code Copyright (C) 1998-2001 VideoLAN + Authors: Johan Bilien <jobi@via.ecp.fr> + Gildas Bazin <gbazin@netcourrier.com> + Jon Lech Johansen <jon-vl@nanocrew.net> + Derk-Jan Hartman <hartman at videolan.org> + Justin F. Hallett <thesin@southofheaven.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file contains OSX-specific code and implements low-level + control of the CD drive. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +static const char _rcsid[] = "$Id: _cdio_osx.c,v 1.4 2005/01/01 02:43:57 rockyb Exp $"; + +#include <cdio/logging.h> +#include <cdio/sector.h> +#include <cdio/util.h> +#include "cdio_assert.h" +#include "cdio_private.h" + +#include <string.h> + +#ifdef HAVE_DARWIN_CDROM +#undef VERSION + +#include <CoreFoundation/CoreFoundation.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/storage/IOStorageDeviceCharacteristics.h> + +#include <mach/mach.h> +#include <Carbon/Carbon.h> +#include <IOKit/scsi-commands/SCSITaskLib.h> +#include <IOKit/IOCFPlugIn.h> +#include <mach/mach_error.h> + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/ioctl.h> + + +#include <paths.h> +#include <CoreFoundation/CoreFoundation.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/IOBSD.h> +#include <IOKit/scsi-commands/IOSCSIMultimediaCommandsDevice.h> +#include <IOKit/storage/IOCDTypes.h> +#include <IOKit/storage/IODVDTypes.h> +#include <IOKit/storage/IOMedia.h> +#include <IOKit/storage/IOCDMedia.h> +#include <IOKit/storage/IODVDMedia.h> +#include <IOKit/storage/IOCDMediaBSDClient.h> +#include <IOKit/storage/IODVDMediaBSDClient.h> +#include <IOKit/storage/IOStorageDeviceCharacteristics.h> + +#define kIOCDBlockStorageDeviceClassString "IOCDBlockStorageDevice" + +/* Note leadout is normally defined 0xAA, But on OSX 0xA0 is "lead in" while + 0xA2 is "lead out". Don't ask me why. */ +#define OSX_CDROM_LEADOUT_TRACK 0xA2 + +#define TOTAL_TRACKS (p_env->i_last_track - p_env->gen.i_first_track + 1) + +#define CDROM_CDI_TRACK 0x1 +#define CDROM_XA_TRACK 0x2 + +typedef enum { + _AM_NONE, + _AM_OSX, +} access_mode_t; + +#define MAX_SERVICE_NAME 1000 +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + + access_mode_t access_mode; + + /* Track information */ + CDTOC *pTOC; + int i_descriptors; + track_t i_last_track; /* highest track number */ + track_t i_last_session; /* highest session number */ + track_t i_first_session; /* first session number */ + lsn_t *pp_lba; + io_service_t MediaClass_service; + char psz_MediaClass_service[MAX_SERVICE_NAME]; + SCSITaskDeviceInterface **pp_scsiTaskDeviceInterface; + +} _img_private_t; + +static bool read_toc_osx (void *p_user_data); + +/**** + * GetRegistryEntryProperties - Gets the registry entry properties for + * an io_service_t. + *****/ + +static CFMutableDictionaryRef +GetRegistryEntryProperties ( io_service_t service ) +{ + IOReturn err = kIOReturnSuccess; + CFMutableDictionaryRef dict = 0; + + err = IORegistryEntryCreateCFProperties (service, &dict, kCFAllocatorDefault, 0); + if ( err != kIOReturnSuccess ) + cdio_warn( "IORegistryEntryCreateCFProperties: 0x%08x", err ); + + return dict; +} + + +static bool +init_osx(_img_private_t *p_env) { + mach_port_t port; + char *psz_devname; + kern_return_t ret; + io_iterator_t iterator; + + p_env->gen.fd = open( p_env->gen.source_name, O_RDONLY | O_NONBLOCK ); + if (-1 == p_env->gen.fd) { + cdio_warn("Failed to open %s: %s", p_env->gen.source_name, + strerror(errno)); + return false; + } + + /* get the device name */ + psz_devname = strrchr( p_env->gen.source_name, '/'); + if( NULL != psz_devname ) + ++psz_devname; + else + psz_devname = p_env->gen.source_name; + + /* unraw the device name */ + if( *psz_devname == 'r' ) + ++psz_devname; + + /* get port for IOKit communication */ + ret = IOMasterPort( MACH_PORT_NULL, &port ); + + if( ret != KERN_SUCCESS ) + { + cdio_warn( "IOMasterPort: 0x%08x", ret ); + return false; + } + + ret = IOServiceGetMatchingServices( port, + IOBSDNameMatching(port, 0, psz_devname), + &iterator ); + + /* get service iterator for the device */ + if( ret != KERN_SUCCESS ) + { + cdio_warn( "IOServiceGetMatchingServices: 0x%08x", ret ); + return false; + } + + /* first service */ + p_env->MediaClass_service = IOIteratorNext( iterator ); + IOObjectRelease( iterator ); + + /* search for kIOCDMediaClass or kIOCDVDMediaClass */ + while( p_env->MediaClass_service && + (!IOObjectConformsTo(p_env->MediaClass_service, kIOCDMediaClass)) && + (!IOObjectConformsTo(p_env->MediaClass_service, kIODVDMediaClass)) ) + { + + ret = IORegistryEntryGetParentIterator( p_env->MediaClass_service, + kIOServicePlane, + &iterator ); + if( ret != KERN_SUCCESS ) + { + cdio_warn( "IORegistryEntryGetParentIterator: 0x%08x", ret ); + IOObjectRelease( p_env->MediaClass_service ); + return false; + } + + IOObjectRelease( p_env->MediaClass_service ); + p_env->MediaClass_service = IOIteratorNext( iterator ); + IOObjectRelease( iterator ); + } + + if ( 0 == p_env->MediaClass_service ) { + cdio_warn( "search for kIOCDMediaClass/kIODVDMediaClass came up empty" ); + return false; + } + + /* Save the name so we can compare against this in case we have to do + another scan. FIXME: this is hoaky and there's got to be a better + variable to test or way to do. + */ + IORegistryEntryGetPath(p_env->MediaClass_service, kIOServicePlane, + p_env->psz_MediaClass_service); + return true; +} + +/*! + Run a SCSI MMC command. + + cdio CD structure set by cdio_open(). + i_timeout time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + p_buf Buffer for data, both sending and receiving + i_buf Size of buffer + e_direction direction the transfer is to go. + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + We return true if command completed successfully and false if not. + */ +static int +run_scsi_cmd_osx( const void *p_user_data, + unsigned int i_timeout_ms, + unsigned int i_cdb, const scsi_mmc_cdb_t *p_cdb, + scsi_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + +#ifndef SCSI_MMC_FIXED + return 2; +#else + const _img_private_t *p_env = p_user_data; + SCSITaskDeviceInterface **sc; + SCSITaskInterface **cmd = NULL; + IOVirtualRange iov; + SCSI_Sense_Data senseData; + SCSITaskStatus status; + UInt64 bytesTransferred; + IOReturn ioReturnValue; + int ret = 0; + + if (NULL == p_user_data) return 2; + + /* Make sure pp_scsiTaskDeviceInterface is initialized. FIXME: The code + should probably be reorganized better for this. */ + if (!p_env->gen.toc_init) read_toc_osx (p_user_data) ; + + sc = p_env->pp_scsiTaskDeviceInterface; + + if (NULL == sc) return 3; + + cmd = (*sc)->CreateSCSITask(sc); + if (cmd == NULL) { + cdio_warn("Failed to create SCSI task"); + return -1; + } + + iov.address = (IOVirtualAddress) p_buf; + iov.length = i_buf; + + ioReturnValue = (*cmd)->SetCommandDescriptorBlock(cmd, (UInt8 *) p_cdb, + i_cdb); + if (ioReturnValue != kIOReturnSuccess) { + cdio_warn("SetCommandDescriptorBlock failed with status %x", + ioReturnValue); + return -1; + } + + ioReturnValue = (*cmd)->SetScatterGatherEntries(cmd, &iov, 1, i_buf, + (SCSI_MMC_DATA_READ == e_direction ) ? + kSCSIDataTransfer_FromTargetToInitiator : + kSCSIDataTransfer_FromInitiatorToTarget); + if (ioReturnValue != kIOReturnSuccess) { + cdio_warn("SetScatterGatherEntries failed with status %x", ioReturnValue); + return -1; + } + + ioReturnValue = (*cmd)->SetTimeoutDuration(cmd, i_timeout_ms ); + if (ioReturnValue != kIOReturnSuccess) { + cdio_warn("SetTimeoutDuration failed with status %x", ioReturnValue); + return -1; + } + + memset(&senseData, 0, sizeof(senseData)); + + ioReturnValue = (*cmd)->ExecuteTaskSync(cmd,&senseData, &status, & + bytesTransferred); + + if (ioReturnValue != kIOReturnSuccess) { + cdio_warn("Command execution failed with status %x", ioReturnValue); + return -1; + } + + if (cmd != NULL) { + (*cmd)->Release(cmd); + } + + return (ret); +#endif +} + +/*************************************************************************** + * GetDeviceIterator - Gets an io_iterator_t for our class type + ***************************************************************************/ + +static io_iterator_t +GetDeviceIterator ( const char * deviceClass ) +{ + + IOReturn err = kIOReturnSuccess; + io_iterator_t iterator = MACH_PORT_NULL; + + err = IOServiceGetMatchingServices ( kIOMasterPortDefault, + IOServiceMatching ( deviceClass ), + &iterator ); + check ( err == kIOReturnSuccess ); + + return iterator; + +} + +/*************************************************************************** + * GetFeaturesFlagsForDrive -Gets the bitfield which represents the + * features flags. + ***************************************************************************/ + +static bool +GetFeaturesFlagsForDrive ( CFDictionaryRef dict, + uint32_t *i_cdFlags, + uint32_t *i_dvdFlags ) +{ + CFDictionaryRef propertiesDict = 0; + CFNumberRef flagsNumberRef = 0; + + *i_cdFlags = 0; + *i_dvdFlags= 0; + + propertiesDict = ( CFDictionaryRef ) + CFDictionaryGetValue ( dict, + CFSTR ( kIOPropertyDeviceCharacteristicsKey ) ); + + if ( propertiesDict == 0 ) return false; + + /* Get the CD features */ + flagsNumberRef = ( CFNumberRef ) + CFDictionaryGetValue ( propertiesDict, + CFSTR ( kIOPropertySupportedCDFeatures ) ); + if ( flagsNumberRef != 0 ) { + CFNumberGetValue ( flagsNumberRef, kCFNumberLongType, i_cdFlags ); + } + + /* Get the DVD features */ + flagsNumberRef = ( CFNumberRef ) + CFDictionaryGetValue ( propertiesDict, + CFSTR ( kIOPropertySupportedDVDFeatures ) ); + if ( flagsNumberRef != 0 ) { + CFNumberGetValue ( flagsNumberRef, kCFNumberLongType, i_dvdFlags ); + } + + return true; +} + +/*! + Get disc type associated with the cd object. +*/ +static discmode_t +get_discmode_osx (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + char str[10]; + int32_t i_discmode = CDIO_DISC_MODE_ERROR; + CFDictionaryRef propertiesDict = 0; + CFStringRef data; + + propertiesDict = GetRegistryEntryProperties ( p_env->MediaClass_service ); + + if ( propertiesDict == 0 ) return i_discmode; + + data = ( CFStringRef ) + CFDictionaryGetValue ( propertiesDict, CFSTR ( kIODVDMediaTypeKey ) ); + + if( CFStringGetCString( data, str, sizeof(str), + kCFStringEncodingASCII ) ) { + if (0 == strncmp(str, "DVD+R", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_DVD_PR; + else if (0 == strncmp(str, "DVD+RW", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_DVD_PRW; + else if (0 == strncmp(str, "DVD-R", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_DVD_R; + else if (0 == strncmp(str, "DVD-RW", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_DVD_RW; + else if (0 == strncmp(str, "DVD-ROM", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_DVD_ROM; + else if (0 == strncmp(str, "DVD-RAM", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_DVD_RAM; + else if (0 == strncmp(str, "CD-ROM", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_CD_DATA; + else if (0 == strncmp(str, "CDR", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_CD_DATA; + else if (0 == strncmp(str, "CDRW", strlen(str)) ) + i_discmode = CDIO_DISC_MODE_CD_DATA; + //?? Handled by below? CFRelease( data ); + } + CFRelease( propertiesDict ); + if (CDIO_DISC_MODE_CD_DATA == i_discmode) { + /* Need to do more classification */ + return get_discmode_cd_generic(p_user_data); + } + return i_discmode; + +} + +static io_service_t +get_drive_service_osx(const _img_private_t *p_env) +{ + io_service_t service; + io_iterator_t service_iterator; + + service_iterator = GetDeviceIterator ( kIOCDBlockStorageDeviceClassString ); + + if( service_iterator == MACH_PORT_NULL ) return 0; + + service = IOIteratorNext( service_iterator ); + if( service == 0 ) return 0; + + do + { + char psz_service[MAX_SERVICE_NAME]; + IORegistryEntryGetPath(service, kIOServicePlane, psz_service); + psz_service[MAX_SERVICE_NAME-1] = '\0'; + + /* FIXME: This is all hoaky. Here we need info from a parent class, + psz_service of what we opened above. We are relying on the + fact that the name will be a substring of the name we + openned with. + */ + if (0 == strncmp(psz_service, p_env->psz_MediaClass_service, + strlen(psz_service))) { + /* Found our device */ + IOObjectRelease( service_iterator ); + return service; + } + + IOObjectRelease( service ); + + } while( ( service = IOIteratorNext( service_iterator ) ) != 0 ); + + IOObjectRelease( service_iterator ); + return service; +} + +static void +get_drive_cap_osx(const void *p_user_data, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap) +{ + const _img_private_t *p_env = p_user_data; + uint32_t i_cdFlags; + uint32_t i_dvdFlags; + + io_service_t service = get_drive_service_osx(p_env); + + if( service == 0 ) goto err_exit; + + /* Found our device */ + { + CFDictionaryRef properties = GetRegistryEntryProperties ( service ); + + if (! GetFeaturesFlagsForDrive ( properties, &i_cdFlags, + &i_dvdFlags ) ) { + IOObjectRelease( service ); + goto err_exit; + } + + /* Reader */ + + if ( 0 != (i_cdFlags & kCDFeaturesAnalogAudioMask) ) + *p_read_cap |= CDIO_DRIVE_CAP_READ_AUDIO; + + if ( 0 != (i_cdFlags & kCDFeaturesWriteOnceMask) ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_R; + + if ( 0 != (i_cdFlags & kCDFeaturesCDDAStreamAccurateMask) ) + *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_DA; + + if ( 0 != (i_dvdFlags & kDVDFeaturesReadStructuresMask) ) + *p_read_cap |= CDIO_DRIVE_CAP_READ_DVD_ROM; + + if ( 0 != (i_cdFlags & kCDFeaturesReWriteableMask) ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_RW; + + if ( 0 != (i_dvdFlags & kDVDFeaturesWriteOnceMask) ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_R; + + if ( 0 != (i_dvdFlags & kDVDFeaturesRandomWriteableMask) ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RAM; + + if ( 0 != (i_dvdFlags & kDVDFeaturesReWriteableMask) ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RW; + + /*** + if ( 0 != (i_dvdFlags & kDVDFeaturesPlusRMask) ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_PR; + + if ( 0 != (i_dvdFlags & kDVDFeaturesPlusRWMask ) + *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_PRW; + ***/ + + /* FIXME: fill out. For now assume CD-ROM is relatively modern. */ + *p_misc_cap = ( + CDIO_DRIVE_CAP_MISC_CLOSE_TRAY + | CDIO_DRIVE_CAP_MISC_EJECT + | CDIO_DRIVE_CAP_MISC_LOCK + | CDIO_DRIVE_CAP_MISC_SELECT_SPEED + | CDIO_DRIVE_CAP_MISC_MULTI_SESSION + | CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED + | CDIO_DRIVE_CAP_MISC_RESET + | CDIO_DRIVE_CAP_MCN + | CDIO_DRIVE_CAP_ISRC + ); + + IOObjectRelease( service ); + } + + return; + + err_exit: + *p_misc_cap = *p_write_cap = *p_read_cap = CDIO_DRIVE_CAP_UNKNOWN; + return; +} + +#if 1 +/**************************************************************************** + * GetDriveDescription - Gets drive description. + ****************************************************************************/ + +static bool +get_hwinfo_osx ( const CdIo *p_cdio, /*out*/ cdio_hwinfo_t *hw_info) +{ + _img_private_t *p_env = (_img_private_t *) p_cdio->env; + io_service_t service = get_drive_service_osx(p_env); + + if ( service == 0 ) return false; + + /* Found our device */ + { + CFStringRef vendor = NULL; + CFStringRef product = NULL; + CFStringRef revision = NULL; + + CFDictionaryRef properties = GetRegistryEntryProperties ( service ); + CFDictionaryRef deviceDict = ( CFDictionaryRef ) + CFDictionaryGetValue ( properties, + CFSTR ( kIOPropertyDeviceCharacteristicsKey ) ); + + if ( deviceDict == 0 ) return false; + + vendor = ( CFStringRef ) + CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertyVendorNameKey ) ); + + if ( CFStringGetCString( vendor, + (char *) &(hw_info->psz_vendor), + sizeof(hw_info->psz_vendor), + kCFStringEncodingASCII ) ) + CFRelease( vendor ); + + product = ( CFStringRef ) + CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertyProductNameKey ) ); + + if ( CFStringGetCString( product, + (char *) &(hw_info->psz_model), + sizeof(hw_info->psz_model), + kCFStringEncodingASCII ) ) + CFRelease( product ); + + revision = ( CFStringRef ) + CFDictionaryGetValue ( deviceDict, + CFSTR ( kIOPropertyProductRevisionLevelKey ) ); + + if ( CFStringGetCString( product, + (char *) &(hw_info->psz_revision), + sizeof(hw_info->psz_revision), + kCFStringEncodingASCII ) ) + CFRelease( revision ); + } + return true; + +} +#endif + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +static const cdtext_t * +get_cdtext_osx (void *p_user_data, track_t i_track) +{ + return NULL; +} + +static void +_free_osx (void *p_user_data) { + _img_private_t *p_env = p_user_data; + if (NULL == p_env) return; + cdio_generic_free(p_env); + if (NULL != p_env->pp_lba) free((void *) p_env->pp_lba); + if (NULL != p_env->pTOC) free((void *) p_env->pTOC); + IOObjectRelease( p_env->MediaClass_service ); + + if (NULL != p_env->pp_scsiTaskDeviceInterface) + ( *(p_env->pp_scsiTaskDeviceInterface) )-> + Release ( (p_env->pp_scsiTaskDeviceInterface) ); + +} + +/*! + Reads nblocks of mode2 form2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_get_read_mode1_sectors_osx (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *env = user_data; + dk_cd_read_t cd_read; + + memset( &cd_read, 0, sizeof(cd_read) ); + + cd_read.sectorArea = kCDSectorAreaUser; + cd_read.buffer = data; + cd_read.sectorType = kCDSectorTypeMode1; + + if (b_form2) { + cd_read.offset = lsn * kCDSectorSizeMode2; + cd_read.bufferLength = kCDSectorSizeMode2 * nblocks; + } else { + cd_read.offset = lsn * kCDSectorSizeMode1; + cd_read.bufferLength = kCDSectorSizeMode1 * nblocks; + } + + if( ioctl( env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 ) + { + cdio_info( "could not read block %d, %s", lsn, strerror(errno) ); + return -1; + } + return 0; +} + + +/*! + Reads nblocks of mode2 form2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_get_read_mode2_sectors_osx (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *env = user_data; + dk_cd_read_t cd_read; + + memset( &cd_read, 0, sizeof(cd_read) ); + + cd_read.sectorArea = kCDSectorAreaUser; + cd_read.buffer = data; + + if (b_form2) { + cd_read.offset = lsn * kCDSectorSizeMode2Form2; + cd_read.sectorType = kCDSectorTypeMode2Form2; + cd_read.bufferLength = kCDSectorSizeMode2Form2 * nblocks; + } else { + cd_read.offset = lsn * kCDSectorSizeMode2Form1; + cd_read.sectorType = kCDSectorTypeMode2Form1; + cd_read.bufferLength = kCDSectorSizeMode2Form1 * nblocks; + } + + if( ioctl( env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 ) + { + cdio_info( "could not read block %d, %s", lsn, strerror(errno) ); + return -1; + } + return 0; +} + + +/*! + Reads a single audio sector from CD device into data starting from lsn. + Returns 0 if no error. + */ +static int +_get_read_audio_sectors_osx (void *user_data, void *data, lsn_t lsn, + unsigned int nblocks) +{ + _img_private_t *env = user_data; + dk_cd_read_t cd_read; + + memset( &cd_read, 0, sizeof(cd_read) ); + + cd_read.offset = lsn * kCDSectorSizeCDDA; + cd_read.sectorArea = kCDSectorAreaUser; + cd_read.sectorType = kCDSectorTypeCDDA; + + cd_read.buffer = data; + cd_read.bufferLength = kCDSectorSizeCDDA * nblocks; + + if( ioctl( env->gen.fd, DKIOCCDREAD, &cd_read ) == -1 ) + { + cdio_info( "could not read block %d", lsn ); + return -1; + } + return 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_get_read_mode1_sector_osx (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + return _get_read_mode1_sectors_osx(user_data, data, lsn, b_form2, 1); +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_get_read_mode2_sector_osx (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + return _get_read_mode2_sectors_osx(user_data, data, lsn, b_form2, 1); +} + +/*! + Set the key "arg" to "value" in source device. +*/ +static int +_set_arg_osx (void *user_data, const char key[], const char value[]) +{ + _img_private_t *env = user_data; + + if (!strcmp (key, "source")) + { + if (!value) + return -2; + + free (env->gen.source_name); + + env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + if (!strcmp(value, "OSX")) + env->access_mode = _AM_OSX; + else + cdio_warn ("unknown access type: %s. ignored.", value); + } + else + return -1; + + return 0; +} + +#if 0 +static void TestDevice(_img_private_t *p_env, io_service_t service) +{ + SInt32 score; + HRESULT herr; + kern_return_t err; + IOCFPlugInInterface **plugInInterface = NULL; + MMCDeviceInterface **mmcInterface = NULL; + + /* Create the IOCFPlugIn interface so we can query it. */ + + err = IOCreatePlugInInterfaceForService ( service, + kIOMMCDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugInInterface, + &score ); + if ( err != noErr ) { + printf("IOCreatePlugInInterfaceForService returned %d\n", err); + return; + } + + /* Query the interface for the MMCDeviceInterface. */ + + herr = ( *plugInInterface )->QueryInterface ( plugInInterface, + CFUUIDGetUUIDBytes ( kIOMMCDeviceInterfaceID ), + ( LPVOID ) &mmcInterface ); + + if ( herr != S_OK ) { + printf("QueryInterface returned %ld\n", herr); + return; + } + + p_env->pp_scsiTaskDeviceInterface = + ( *mmcInterface )->GetSCSITaskDeviceInterface ( mmcInterface ); + + if ( NULL == p_env->pp_scsiTaskDeviceInterface ) { + printf("GetSCSITaskDeviceInterface returned NULL\n"); + return; + } + + ( *mmcInterface )->Release ( mmcInterface ); + IODestroyPlugInInterface ( plugInInterface ); +} +#endif + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return false if successful or true if an error. +*/ +static bool +read_toc_osx (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + CFDictionaryRef propertiesDict = 0; + CFDataRef data; + + /* create a CF dictionary containing the TOC */ + propertiesDict = GetRegistryEntryProperties( p_env->MediaClass_service ); + + if ( 0 == propertiesDict ) { + return false; + } + + /* get the TOC from the dictionary */ + data = (CFDataRef) CFDictionaryGetValue( propertiesDict, + CFSTR(kIOCDMediaTOCKey) ); + if ( data != NULL ) { + CFRange range; + CFIndex buf_len; + + buf_len = CFDataGetLength( data ) + 1; + range = CFRangeMake( 0, buf_len ); + + if( ( p_env->pTOC = (CDTOC *)malloc( buf_len ) ) != NULL ) { + CFDataGetBytes( data, range, (u_char *) p_env->pTOC ); + } else { + cdio_warn( "Trouble allocating CDROM TOC" ); + return false; + } + } else { + cdio_warn( "Trouble reading TOC" ); + return false; + } + + /* TestDevice(p_env, service); */ + CFRelease( propertiesDict ); + + p_env->i_descriptors = CDTOCGetDescriptorCount ( p_env->pTOC ); + + /* Read in starting sectors. There may be non-tracks mixed in with + the real tracks. So find the first and last track number by + scanning. Also find the lead-out track position. + */ + { + int i, i_leadout = -1; + + CDTOCDescriptor *pTrackDescriptors; + + p_env->pp_lba = malloc( p_env->i_descriptors * sizeof(int) ); + if( p_env->pp_lba == NULL ) + { + cdio_warn("Out of memory in allocating track starting LSNs" ); + free( p_env->pTOC ); + return false; + } + + pTrackDescriptors = p_env->pTOC->descriptors; + + p_env->gen.i_first_track = CDIO_CD_MAX_TRACKS+1; + p_env->i_last_track = CDIO_CD_MIN_TRACK_NO; + p_env->i_first_session = CDIO_CD_MAX_TRACKS+1; + p_env->i_last_session = CDIO_CD_MIN_TRACK_NO; + + for( i = 0; i <= p_env->i_descriptors; i++ ) + { + track_t i_track = pTrackDescriptors[i].point; + session_t i_session = pTrackDescriptors[i].session; + + cdio_debug( "point: %d, tno: %d, session: %d, adr: %d, control:%d, " + "address: %d:%d:%d, p: %d:%d:%d", + i_track, + pTrackDescriptors[i].tno, i_session, + pTrackDescriptors[i].adr, pTrackDescriptors[i].control, + pTrackDescriptors[i].address.minute, + pTrackDescriptors[i].address.second, + pTrackDescriptors[i].address.frame, + pTrackDescriptors[i].p.minute, + pTrackDescriptors[i].p.second, + pTrackDescriptors[i].p.frame ); + + /* track information has adr = 1 */ + if ( 0x01 != pTrackDescriptors[i].adr ) + continue; + + if( i_track == OSX_CDROM_LEADOUT_TRACK ) + i_leadout = i; + + if( i_track > CDIO_CD_MAX_TRACKS || i_track < CDIO_CD_MIN_TRACK_NO ) + continue; + + if (p_env->gen.i_first_track > i_track) + p_env->gen.i_first_track = i_track; + + if (p_env->i_last_track < i_track) + p_env->i_last_track = i_track; + + if (p_env->i_first_session > i_session) + p_env->i_first_session = i_session; + + if (p_env->i_last_session < i_session) + p_env->i_last_session = i_session; + } + + /* Now that we know what the first track number is, we can make sure + index positions are ordered starting at 0. + */ + for( i = 0; i <= p_env->i_descriptors; i++ ) + { + track_t i_track = pTrackDescriptors[i].point; + + if( i_track > CDIO_CD_MAX_TRACKS || i_track < CDIO_CD_MIN_TRACK_NO ) + continue; + + /* Note what OSX calls a LBA we call an LSN. So below re we + really have have MSF -> LSN -> LBA. + */ + p_env->pp_lba[i_track - p_env->gen.i_first_track] = + cdio_lsn_to_lba(CDConvertMSFToLBA( pTrackDescriptors[i].p )); + } + + if( i_leadout == -1 ) + { + cdio_warn( "CD leadout not found" ); + free( p_env->pp_lba ); + free( (void *) p_env->pTOC ); + return false; + } + + /* Set leadout sector. + Note what OSX calls a LBA we call an LSN. So below re we + really have have MSF -> LSN -> LBA. + */ + p_env->pp_lba[TOTAL_TRACKS] = + cdio_lsn_to_lba(CDConvertMSFToLBA( pTrackDescriptors[i_leadout].p )); + p_env->gen.i_tracks = TOTAL_TRACKS; + } + + p_env->gen.toc_init = true; + + return( true ); + +} + +/*! + Return the starting LSN track number + i_track in obj. Track numbers start at 1. + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static lsn_t +get_track_lba_osx(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if (!p_env->gen.toc_init) read_toc_osx (p_env) ; + if (!p_env->gen.toc_init) return CDIO_INVALID_LSN; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = p_env->i_last_track+1; + + if (i_track > p_env->i_last_track + 1 || i_track < p_env->gen.i_first_track) { + return CDIO_INVALID_LSN; + } else { + return p_env->pp_lba[i_track - p_env->gen.i_first_track]; + } +} + +/*! + Eject media . Return 1 if successful, 0 otherwise. + + The only way to cleanly unmount the disc under MacOS X is to use the + 'disktool' command line utility. It uses the non-public Disk + Arbitration API, which can not be used by Cocoa or Carbon + applications. + + */ + +static int +_eject_media_osx (void *user_data) { + + _img_private_t *p_env = user_data; + + FILE *p_eject; + char *psz_disk; + char sz_cmd[32]; + + if( ( psz_disk = (char *)strstr( p_env->gen.source_name, "disk" ) ) != NULL && + strlen( psz_disk ) > 4 ) + { +#define EJECT_CMD "/usr/sbin/hdiutil eject %s" + snprintf( sz_cmd, sizeof(sz_cmd), EJECT_CMD, psz_disk ); +#undef EJECT_CMD + + if( ( p_eject = popen( sz_cmd, "r" ) ) != NULL ) + { + char psz_result[0x200]; + int i_ret = fread( psz_result, 1, sizeof(psz_result) - 1, p_eject ); + + if( i_ret == 0 && ferror( p_eject ) != 0 ) + { + pclose( p_eject ); + return 0; + } + + pclose( p_eject ); + + psz_result[ i_ret ] = 0; + + if( strstr( psz_result, "Disk Ejected" ) != NULL ) + { + return 1; + } + } + } + + return 0; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static uint32_t +_stat_size_osx (void *user_data) +{ + return get_track_lba_osx(user_data, CDIO_CDROM_LEADOUT_TRACK); +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +_get_arg_osx (void *user_data, const char key[]) +{ + _img_private_t *p_env = user_data; + + if (!strcmp (key, "source")) { + return p_env->gen.source_name; + } else if (!strcmp (key, "access-mode")) { + switch (p_env->access_mode) { + case _AM_OSX: + return "OS X"; + case _AM_NONE: + return "no access method"; + } + } + return NULL; +} + +/*! + Return the media catalog number MCN. + */ +static char * +get_mcn_osx (const void *user_data) { + const _img_private_t *p_env = user_data; + dk_cd_read_mcn_t cd_read; + + memset( &cd_read, 0, sizeof(cd_read) ); + + if( ioctl( p_env->gen.fd, DKIOCCDREADMCN, &cd_read ) < 0 ) + { + cdio_debug( "could not read MCN, %s", strerror(errno) ); + return NULL; + } + return strdup((char*)cd_read.mcn); +} + + +/*! + Get format of track. +*/ +static track_format_t +get_track_format_osx(void *user_data, track_t i_track) +{ + _img_private_t *p_env = user_data; + dk_cd_read_track_info_t cd_read; + CDTrackInfo a_track; + + if (!p_env->gen.toc_init) read_toc_osx (p_env) ; + + if (i_track > p_env->i_last_track || i_track < p_env->gen.i_first_track) + return TRACK_FORMAT_ERROR; + + memset( &cd_read, 0, sizeof(cd_read) ); + + cd_read.address = i_track; + cd_read.addressType = kCDTrackInfoAddressTypeTrackNumber; + + cd_read.buffer = &a_track; + cd_read.bufferLength = sizeof(CDTrackInfo); + + if( ioctl( p_env->gen.fd, DKIOCCDREADTRACKINFO, &cd_read ) == -1 ) + { + cdio_warn( "could not read trackinfo for track %d", i_track ); + return TRACK_FORMAT_ERROR; + } + + cdio_debug( "%d: trackinfo trackMode: %x dataMode: %x", i_track, a_track.trackMode, a_track.dataMode ); + + if (a_track.trackMode == CDIO_CDROM_DATA_TRACK) { + if (a_track.dataMode == CDROM_CDI_TRACK) { + return TRACK_FORMAT_CDI; + } else if (a_track.dataMode == CDROM_XA_TRACK) { + return TRACK_FORMAT_XA; + } else { + return TRACK_FORMAT_DATA; + } + } else { + return TRACK_FORMAT_AUDIO; + } + +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +static bool +get_track_green_osx(void *user_data, track_t i_track) +{ + _img_private_t *p_env = user_data; + CDTrackInfo a_track; + + if (!p_env->gen.toc_init) read_toc_osx (p_env) ; + + if ( i_track > p_env->i_last_track || i_track < p_env->gen.i_first_track ) + return false; + + else { + + dk_cd_read_track_info_t cd_read; + + memset( &cd_read, 0, sizeof(cd_read) ); + + cd_read.address = i_track; + cd_read.addressType = kCDTrackInfoAddressTypeTrackNumber; + + cd_read.buffer = &a_track; + cd_read.bufferLength = sizeof(CDTrackInfo); + + if( ioctl( p_env->gen.fd, DKIOCCDREADTRACKINFO, &cd_read ) == -1 ) { + cdio_warn( "could not read trackinfo for track %d", i_track ); + return false; + } + return ((a_track.trackMode & CDIO_CDROM_DATA_TRACK) != 0); + } +} + +#endif /* HAVE_DARWIN_CDROM */ + +/*! + Return a string containing the default CD device if none is specified. + */ +char ** +cdio_get_devices_osx(void) +{ +#ifndef HAVE_DARWIN_CDROM + return NULL; +#else + io_object_t next_media; + mach_port_t master_port; + kern_return_t kern_result; + io_iterator_t media_iterator; + CFMutableDictionaryRef classes_to_match; + char **drives = NULL; + unsigned int num_drives=0; + + kern_result = IOMasterPort( MACH_PORT_NULL, &master_port ); + if( kern_result != KERN_SUCCESS ) + { + return( NULL ); + } + + classes_to_match = IOServiceMatching( kIOCDMediaClass ); + if( classes_to_match == NULL ) + { + return( NULL ); + } + + CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaEjectableKey), + kCFBooleanTrue ); + + kern_result = IOServiceGetMatchingServices( master_port, + classes_to_match, + &media_iterator ); + if( kern_result != KERN_SUCCESS ) + { + return( NULL ); + } + + next_media = IOIteratorNext( media_iterator ); + if( next_media != 0 ) + { + char psz_buf[0x32]; + size_t dev_path_length; + CFTypeRef str_bsd_path; + + do + { + str_bsd_path = IORegistryEntryCreateCFProperty( next_media, + CFSTR( kIOBSDNameKey ), + kCFAllocatorDefault, + 0 ); + if( str_bsd_path == NULL ) + { + IOObjectRelease( next_media ); + continue; + } + + snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' ); + dev_path_length = strlen( psz_buf ); + + if( CFStringGetCString( str_bsd_path, + (char*)&psz_buf + dev_path_length, + sizeof(psz_buf) - dev_path_length, + kCFStringEncodingASCII ) ) + { + CFRelease( str_bsd_path ); + IOObjectRelease( next_media ); + IOObjectRelease( media_iterator ); + cdio_add_device_list(&drives, strdup(psz_buf), &num_drives); + } + + CFRelease( str_bsd_path ); + IOObjectRelease( next_media ); + + } while( ( next_media = IOIteratorNext( media_iterator ) ) != 0 ); + } + IOObjectRelease( media_iterator ); + cdio_add_device_list(&drives, NULL, &num_drives); + return drives; +#endif /* HAVE_DARWIN_CDROM */ +} + +/*! + Return a string containing the default CD device if none is specified. + */ +char * +cdio_get_default_device_osx(void) +{ +#ifndef HAVE_DARWIN_CDROM + return NULL; +#else + io_object_t next_media; + mach_port_t master_port; + kern_return_t kern_result; + io_iterator_t media_iterator; + CFMutableDictionaryRef classes_to_match; + + kern_result = IOMasterPort( MACH_PORT_NULL, &master_port ); + if( kern_result != KERN_SUCCESS ) + { + return( NULL ); + } + + classes_to_match = IOServiceMatching( kIOCDMediaClass ); + if( classes_to_match == NULL ) + { + return( NULL ); + } + + CFDictionarySetValue( classes_to_match, CFSTR(kIOMediaEjectableKey), + kCFBooleanTrue ); + + kern_result = IOServiceGetMatchingServices( master_port, + classes_to_match, + &media_iterator ); + if( kern_result != KERN_SUCCESS ) + { + return( NULL ); + } + + next_media = IOIteratorNext( media_iterator ); + if( next_media != 0 ) + { + char psz_buf[0x32]; + size_t dev_path_length; + CFTypeRef str_bsd_path; + + do + { + str_bsd_path = IORegistryEntryCreateCFProperty( next_media, + CFSTR( kIOBSDNameKey ), + kCFAllocatorDefault, + 0 ); + if( str_bsd_path == NULL ) + { + IOObjectRelease( next_media ); + continue; + } + + snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' ); + dev_path_length = strlen( psz_buf ); + + if( CFStringGetCString( str_bsd_path, + (char*)&psz_buf + dev_path_length, + sizeof(psz_buf) - dev_path_length, + kCFStringEncodingASCII ) ) + { + CFRelease( str_bsd_path ); + IOObjectRelease( next_media ); + IOObjectRelease( media_iterator ); + return strdup( psz_buf ); + } + + CFRelease( str_bsd_path ); + IOObjectRelease( next_media ); + + } while( ( next_media = IOIteratorNext( media_iterator ) ) != 0 ); + } + IOObjectRelease( media_iterator ); + return NULL; +#endif /* HAVE_DARWIN_CDROM */ +} + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_am_osx (const char *psz_source_name, const char *psz_access_mode) +{ + + if (psz_access_mode != NULL) + cdio_warn ("there is only one access mode for OS X. Arg %s ignored", + psz_access_mode); + return cdio_open_osx(psz_source_name); +} + + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_osx (const char *psz_orig_source) +{ + +#ifdef HAVE_DARWIN_CDROM + CdIo *ret; + _img_private_t *_data; + char *psz_source; + + cdio_funcs _funcs = { + .eject_media = _eject_media_osx, + .free = _free_osx, + .get_arg = _get_arg_osx, + .get_cdtext = get_cdtext_osx, + .get_default_device = cdio_get_default_device_osx, + .get_devices = cdio_get_devices_osx, + .get_discmode = get_discmode_osx, + .get_drive_cap = get_drive_cap_osx, + .get_first_track_num= get_first_track_num_generic, + .get_hwinfo = get_hwinfo_osx, + .get_mcn = get_mcn_osx, + .get_num_tracks = get_num_tracks_generic, + .get_track_format = get_track_format_osx, + .get_track_green = get_track_green_osx, + .get_track_lba = get_track_lba_osx, + .get_track_msf = NULL, + .lseek = cdio_generic_lseek, + .read = cdio_generic_read, + .read_audio_sectors = _get_read_audio_sectors_osx, + .read_mode1_sector = _get_read_mode1_sector_osx, + .read_mode1_sectors = _get_read_mode1_sectors_osx, + .read_mode2_sector = _get_read_mode2_sector_osx, + .read_mode2_sectors = _get_read_mode2_sectors_osx, + .read_toc = read_toc_osx, + .run_scsi_mmc_cmd = run_scsi_cmd_osx, + .set_arg = _set_arg_osx, + .stat_size = _stat_size_osx + }; + + _data = _cdio_malloc (sizeof (_img_private_t)); + _data->access_mode = _AM_OSX; + _data->MediaClass_service = 0; + _data->gen.init = false; + _data->gen.fd = -1; + _data->gen.toc_init = false; + _data->gen.b_cdtext_init = false; + _data->gen.b_cdtext_error = false; + + if (NULL == psz_orig_source) { + psz_source=cdio_get_default_device_osx(); + if (NULL == psz_source) return NULL; + _set_arg_osx(_data, "source", psz_source); + free(psz_source); + } else { + if (cdio_is_device_generic(psz_orig_source)) + _set_arg_osx(_data, "source", psz_orig_source); + else { + /* The below would be okay if all device drivers worked this way. */ +#if 0 + cdio_info ("source %s is a not a device", psz_orig_source); +#endif + return NULL; + } + } + + ret = cdio_new ((void *)_data, &_funcs); + if (ret == NULL) return NULL; + + if (cdio_generic_init(_data) && init_osx(_data)) + return ret; + else { + cdio_generic_free (_data); + return NULL; + } + +#else + return NULL; +#endif /* HAVE_DARWIN_CDROM */ + +} + +bool +cdio_have_osx (void) +{ +#ifdef HAVE_DARWIN_CDROM + return true; +#else + return false; +#endif /* HAVE_DARWIN_CDROM */ +} diff --git a/contrib/libcdio/_cdio_stdio.c b/contrib/libcdio/_cdio_stdio.c new file mode 100644 index 000000000..0083b2194 --- /dev/null +++ b/contrib/libcdio/_cdio_stdio.c @@ -0,0 +1,222 @@ +/* + $Id: _cdio_stdio.c,v 1.2 2004/04/11 12:20:31 miguelfreitas Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <errno.h> + +#include <cdio/logging.h> +#include <cdio/util.h> +#include "_cdio_stream.h" +#include "_cdio_stdio.h" + +static const char _rcsid[] = "$Id: _cdio_stdio.c,v 1.2 2004/04/11 12:20:31 miguelfreitas Exp $"; + +#define CDIO_STDIO_BUFSIZE (128*1024) + +typedef struct { + char *pathname; + FILE *fd; + char *fd_buf; + off_t st_size; /* used only for source */ +} _UserData; + +static int +_stdio_open (void *user_data) +{ + _UserData *const ud = user_data; + + if ((ud->fd = fopen (ud->pathname, "rb"))) + { + ud->fd_buf = _cdio_malloc (CDIO_STDIO_BUFSIZE); + setvbuf (ud->fd, ud->fd_buf, _IOFBF, CDIO_STDIO_BUFSIZE); + } + + return (ud->fd == NULL); +} + +static int +_stdio_close(void *user_data) +{ + _UserData *const ud = user_data; + + if (fclose (ud->fd)) + cdio_error ("fclose (): %s", strerror (errno)); + + ud->fd = NULL; + + free (ud->fd_buf); + ud->fd_buf = NULL; + + return 0; +} + +static void +_stdio_free(void *user_data) +{ + _UserData *const ud = user_data; + + if (ud->pathname) + free(ud->pathname); + + if (ud->fd) /* should be NULL anyway... */ + _stdio_close(user_data); + + free(ud); +} + +/*! + Like fseek(3) and in fact may be the same. + + This function sets the file position indicator for the stream + pointed to by stream. The new position, measured in bytes, is obtained + by adding offset bytes to the position specified by whence. If whence + is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is relative to + the start of the file, the current position indicator, or end-of-file, + respectively. A successful call to the fseek function clears the end- + of-file indicator for the stream and undoes any effects of the + ungetc(3) function on the same stream. + + RETURN VALUE + Upon successful completion, return 0, + Otherwise, -1 is returned and the global variable errno is set to indi- + cate the error. +*/ +static long +_stdio_seek(void *user_data, long offset, int whence) +{ + _UserData *const ud = user_data; + + if ( (offset=fseek (ud->fd, offset, whence)) ) { + cdio_error ("fseek (): %s", strerror (errno)); + } + + return offset; +} + +static long int +_stdio_stat(void *user_data) +{ + const _UserData *const ud = user_data; + + return ud->st_size; +} + +/*! + Like fread(3) and in fact is about the same. + + DESCRIPTION: + The function fread reads nmemb elements of data, each size bytes long, + from the stream pointed to by stream, storing them at the location + given by ptr. + + RETURN VALUE: + return the number of items successfully read or written (i.e., + not the number of characters). If an error occurs, or the + end-of-file is reached, the return value is a short item count + (or zero). + + We do not distinguish between end-of-file and error, and callers + must use feof(3) and ferror(3) to determine which occurred. + */ +static long +_stdio_read(void *user_data, void *buf, long int count) +{ + _UserData *const ud = user_data; + long read; + + read = fread(buf, 1, count, ud->fd); + + if (read != count) + { /* fixme -- ferror/feof */ + if (feof (ud->fd)) + { + cdio_debug ("fread (): EOF encountered"); + clearerr (ud->fd); + } + else if (ferror (ud->fd)) + { + cdio_error ("fread (): %s", strerror (errno)); + clearerr (ud->fd); + } + else + cdio_debug ("fread (): short read and no EOF?!?"); + } + + return read; +} + +/*! + Deallocate resources assocaited with obj. After this obj is unusable. +*/ +void +cdio_stdio_destroy(CdioDataSource *obj) +{ + cdio_stream_destroy(obj); +} + +CdioDataSource* +cdio_stdio_new(const char pathname[]) +{ + CdioDataSource *new_obj = NULL; + cdio_stream_io_functions funcs = { 0, }; + _UserData *ud = NULL; + struct stat statbuf; + + if (stat (pathname, &statbuf) == -1) + { + cdio_warn ("could not retrieve file info for `%s': %s", + pathname, strerror (errno)); + return NULL; + } + + ud = _cdio_malloc (sizeof (_UserData)); + + ud->pathname = strdup(pathname); + ud->st_size = statbuf.st_size; /* let's hope it doesn't change... */ + + funcs.open = _stdio_open; + funcs.seek = _stdio_seek; + funcs.stat = _stdio_stat; + funcs.read = _stdio_read; + funcs.close = _stdio_close; + funcs.free = _stdio_free; + + new_obj = cdio_stream_new(ud, &funcs); + + return new_obj; +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/_cdio_stdio.h b/contrib/libcdio/_cdio_stdio.h new file mode 100644 index 000000000..f5e79c41c --- /dev/null +++ b/contrib/libcdio/_cdio_stdio.h @@ -0,0 +1,52 @@ +/* + $Id: _cdio_stdio.h,v 1.2 2004/04/11 12:20:31 miguelfreitas Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef __CDIO_STDIO_H__ +#define __CDIO_STDIO_H__ + +#include "_cdio_stream.h" + +/*! + Initialize a new stdio stream reading from pathname. + A pointer to the stream is returned or NULL if there was an error. + + cdio_stream_free should be called on the returned value when you + don't need the stream any more. No other finalization is needed. + */ +CdioDataSource* cdio_stdio_new(const char pathname[]); + +/*! + Deallocate resources assocaited with obj. After this obj is unusable. +*/ +void cdio_stdio_destroy(CdioDataSource *obj); + + +#endif /* __CDIO_STREAM_STDIO_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/_cdio_stream.c b/contrib/libcdio/_cdio_stream.c new file mode 100644 index 000000000..fc1f7fce7 --- /dev/null +++ b/contrib/libcdio/_cdio_stream.c @@ -0,0 +1,200 @@ +/* + $Id: _cdio_stream.c,v 1.2 2004/04/11 12:20:31 miguelfreitas Exp $ + + Copyright (C) 2000, 2004 Herbert Valerio Riedel <hvr@gnu.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include "cdio_assert.h" + +/* #define STREAM_DEBUG */ + +#include <cdio/logging.h> +#include <cdio/util.h> +#include "_cdio_stream.h" + +static const char _rcsid[] = "$Id: _cdio_stream.c,v 1.2 2004/04/11 12:20:31 miguelfreitas Exp $"; + +/* + * DataSource implementations + */ + +struct _CdioDataSource { + void* user_data; + cdio_stream_io_functions op; + int is_open; + long position; +}; + +/* + Open if not already open. + Return false if we hit an error. Errno should be set for that error. +*/ +static bool +_cdio_stream_open_if_necessary(CdioDataSource *obj) +{ + cdio_assert (obj != NULL); + + if (!obj->is_open) { + if (obj->op.open(obj->user_data)) { + cdio_warn ("could not open input stream..."); + return false; + } else { + cdio_debug ("opened source..."); + obj->is_open = 1; + obj->position = 0; + } + } + return true; +} + +/*! + Like 3 fseek and in fact may be the same. + + This function sets the file position indicator for the stream + pointed to by stream. The new position, measured in bytes, is obtained + by adding offset bytes to the position specified by whence. If whence + is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is relative to + the start of the file, the current position indicator, or end-of-file, + respectively. A successful call to the fseek function clears the end- + of-file indicator for the stream and undoes any effects of the + ungetc(3) function on the same stream. + + RETURN VALUE + Upon successful completion, return 0, + Otherwise, -1 is returned and the global variable errno is set to indi- + cate the error. +*/ +long +cdio_stream_seek(CdioDataSource* obj, long offset, int whence) +{ + cdio_assert (obj != NULL); + + if (!_cdio_stream_open_if_necessary(obj)) + /* errno is set by _cdio_stream_open_if necessary. */ + return -1; + + if (obj->position != offset) { +#ifdef STREAM_DEBUG + cdio_warn("had to reposition DataSource from %ld to %ld!", obj->position, offset); +#endif + obj->position = offset; + return obj->op.seek(obj->user_data, offset, whence); + } + + return 0; +} + +CdioDataSource* +cdio_stream_new(void *user_data, const cdio_stream_io_functions *funcs) +{ + CdioDataSource *new_obj; + + new_obj = _cdio_malloc (sizeof (CdioDataSource)); + + new_obj->user_data = user_data; + memcpy(&(new_obj->op), funcs, sizeof(cdio_stream_io_functions)); + + return new_obj; +} + +/*! + Like fread(3) and in fact may be the same. + + DESCRIPTION: + The function fread reads nmemb elements of data, each size bytes long, + from the stream pointed to by stream, storing them at the location + given by ptr. + + RETURN VALUE: + return the number of items successfully read or written (i.e., + not the number of characters). If an error occurs, or the + end-of-file is reached, the return value is a short item count + (or zero). + + We do not distinguish between end-of-file and error, and callers + must use feof(3) and ferror(3) to determine which occurred. +*/ +long +cdio_stream_read(CdioDataSource* obj, void *ptr, long size, long nmemb) +{ + long read_bytes; + + cdio_assert (obj != NULL); + + if (!_cdio_stream_open_if_necessary(obj)) return 0; + + read_bytes = obj->op.read(obj->user_data, ptr, size*nmemb); + obj->position += read_bytes; + + return read_bytes; +} + +/*! + Return whatever size of stream reports, I guess unit size is bytes. + On error return -1; + */ +long int +cdio_stream_stat(CdioDataSource* obj) +{ + cdio_assert (obj != NULL); + + if (!_cdio_stream_open_if_necessary(obj)) return -1; + + return obj->op.stat(obj->user_data); +} + +void +cdio_stream_close(CdioDataSource* obj) +{ + cdio_assert (obj != NULL); + + if (obj->is_open) { + cdio_debug ("closed source..."); + obj->op.close(obj->user_data); + obj->is_open = 0; + obj->position = 0; + } +} + +void +cdio_stream_destroy(CdioDataSource* obj) +{ + cdio_assert (obj != NULL); + + cdio_stream_close(obj); + + obj->op.free(obj->user_data); + + free(obj); +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/_cdio_stream.h b/contrib/libcdio/_cdio_stream.h new file mode 100644 index 000000000..ffbb4098e --- /dev/null +++ b/contrib/libcdio/_cdio_stream.h @@ -0,0 +1,127 @@ +/* + $Id: _cdio_stream.h,v 1.2 2004/04/11 12:20:31 miguelfreitas Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef __CDIO_STREAM_H__ +#define __CDIO_STREAM_H__ + +#include <cdio/types.h> +#include "cdio_private.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* typedef'ed IO functions prototypes */ + + typedef int(*cdio_data_open_t)(void *user_data); + + typedef long(*cdio_data_read_t)(void *user_data, void *buf, long count); + + typedef long(*cdio_data_seek_t)(void *user_data, long offset, int whence); + + typedef long(*cdio_data_stat_t)(void *user_data); + + typedef int(*cdio_data_close_t)(void *user_data); + + typedef void(*cdio_data_free_t)(void *user_data); + + + /* abstract data source */ + + typedef struct { + cdio_data_open_t open; + cdio_data_seek_t seek; + cdio_data_stat_t stat; + cdio_data_read_t read; + cdio_data_close_t close; + cdio_data_free_t free; + } cdio_stream_io_functions; + + CdioDataSource* + cdio_stream_new(void *user_data, const cdio_stream_io_functions *funcs); + + /*! + Like fread(3) and in fact may be the same. + + DESCRIPTION: + The function fread reads nmemb elements of data, each size bytes long, + from the stream pointed to by stream, storing them at the location + given by ptr. + + RETURN VALUE: + return the number of items successfully read or written (i.e., + not the number of characters). If an error occurs, or the + end-of-file is reached, the return value is a short item count + (or zero). + + We do not distinguish between end-of-file and error, and callers + must use feof(3) and ferror(3) to determine which occurred. + */ + long + cdio_stream_read(CdioDataSource* obj, void *ptr, long size, long nmemb); + + /*! + Like fseek(3) and in fact may be the same. + + This function sets the file position indicator for the stream + pointed to by stream. The new position, measured in bytes, is obtained + by adding offset bytes to the position specified by whence. If whence + is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is relative to + the start of the file, the current position indicator, or end-of-file, + respectively. A successful call to the fseek function clears the end- + of-file indicator for the stream and undoes any effects of the + ungetc(3) function on the same stream. + + RETURN VALUE + Upon successful completion, return 0, + Otherwise, -1 is returned and the global variable errno is set to indi- + cate the error. + */ + long int cdio_stream_seek(CdioDataSource* obj, long offset, int whence); + + /*! + Return whatever size of stream reports, I guess unit size is bytes. + On error return -1; + */ + long int cdio_stream_stat(CdioDataSource* obj); + + /*! + Deallocate resources assocaited with obj. After this obj is unusable. + */ + void cdio_stream_destroy(CdioDataSource* obj); + + void cdio_stream_close(CdioDataSource* obj); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_STREAM_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/_cdio_sunos.c b/contrib/libcdio/_cdio_sunos.c new file mode 100644 index 000000000..245d1e319 --- /dev/null +++ b/contrib/libcdio/_cdio_sunos.c @@ -0,0 +1,917 @@ +/* + $Id: _cdio_sunos.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2002, 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include <cdio/logging.h> +#include <cdio/sector.h> +#include <cdio/util.h> +#include <cdio/scsi_mmc.h> +#include "cdio_assert.h" +#include "cdio_private.h" + +#define DEFAULT_CDIO_DEVICE "/vol/dev/aliases/cdrom0" + +#ifdef HAVE_SOLARIS_CDROM + +static const char _rcsid[] = "$Id: _cdio_sunos.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $"; + +#ifdef HAVE_GLOB_H +#include <glob.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#ifdef HAVE_SYS_CDIO_H +# include <sys/cdio.h> /* CDIOCALLOW etc... */ +#else +#error "You need <sys/cdio.h> to have CDROM support" +#endif + +#include <sys/dkio.h> +#include <sys/scsi/generic/commands.h> +#include <sys/scsi/impl/uscsi.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include "cdtext_private.h" + +/* not defined in dkio.h yet */ +#define DK_DVDRW 0x13 + +/* reader */ + +typedef enum { + _AM_NONE, + _AM_SUN_CTRL_ATAPI, + _AM_SUN_CTRL_SCSI +#if FINISHED + _AM_READ_CD, + _AM_READ_10 +#endif +} access_mode_t; + + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + + access_mode_t access_mode; + + /* Some of the more OS specific things. */ + /* Entry info for each track, add 1 for leadout. */ + struct cdrom_tocentry tocent[CDIO_CD_MAX_TRACKS+1]; + + /* Track information */ + struct cdrom_tochdr tochdr; +} _img_private_t; + +static track_format_t get_track_format_solaris(void *p_user_data, + track_t i_track); + +static access_mode_t +str_to_access_mode_sunos(const char *psz_access_mode) +{ + const access_mode_t default_access_mode = _AM_SUN_CTRL_SCSI; + + if (NULL==psz_access_mode) return default_access_mode; + + if (!strcmp(psz_access_mode, "ATAPI")) + return _AM_SUN_CTRL_SCSI; /* force ATAPI to be SCSI */ + else if (!strcmp(psz_access_mode, "SCSI")) + return _AM_SUN_CTRL_SCSI; + else { + cdio_warn ("unknown access type: %s. Default SCSI used.", + psz_access_mode); + return default_access_mode; + } +} + + +/*! + Initialize CD device. + */ +static bool +init_solaris (_img_private_t *p_env) +{ + + if (!cdio_generic_init(p_env)) return false; + + p_env->access_mode = _AM_SUN_CTRL_SCSI; + + return true; +} + +/*! + Run a SCSI MMC command. + + p_user_data internal CD structure. + i_timeout_ms time in milliseconds we will wait for the command + to complete. + i_cdb Size of p_cdb + p_cdb CDB bytes. + e_direction direction the transfer is to go. + i_buf Size of buffer + p_buf Buffer for data, both sending and receiving + + Return 0 if no error. + */ +static int +run_scsi_cmd_solaris( const void *p_user_data, unsigned int i_timeout_ms, + unsigned int i_cdb, const scsi_mmc_cdb_t *p_cdb, + scsi_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ) +{ + const _img_private_t *p_env = p_user_data; + struct uscsi_cmd cgc; + + memset (&cgc, 0, sizeof (struct uscsi_cmd)); + cgc.uscsi_cdb = (caddr_t) p_cdb; + + cgc.uscsi_flags = SCSI_MMC_DATA_READ == e_direction ? + USCSI_READ : USCSI_WRITE; + + cgc.uscsi_timeout = msecs2secs(i_timeout_ms); + cgc.uscsi_bufaddr = p_buf; + cgc.uscsi_buflen = i_buf; + cgc.uscsi_cdblen = i_cdb; + + return ioctl(p_env->gen.fd, USCSICMD, &cgc); +} + +/*! + Reads audio sectors from CD device into data starting from lsn. + Returns 0 if no error. + + May have to check size of nblocks. There may be a limit that + can be read in one go, e.g. 25 blocks. +*/ + +static int +_read_audio_sectors_solaris (void *p_user_data, void *data, lsn_t lsn, + unsigned int nblocks) +{ + struct cdrom_msf solaris_msf; + msf_t _msf; + struct cdrom_cdda cdda; + + _img_private_t *p_env = p_user_data; + + cdio_lba_to_msf (cdio_lsn_to_lba(lsn), &_msf); + solaris_msf.cdmsf_min0 = cdio_from_bcd8(_msf.m); + solaris_msf.cdmsf_sec0 = cdio_from_bcd8(_msf.s); + solaris_msf.cdmsf_frame0 = cdio_from_bcd8(_msf.f); + + if (p_env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged < 75 + || (p_env->gen.ioctls_debugged < (30 * 75) + && p_env->gen.ioctls_debugged % 75 == 0) + || p_env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %d", lsn); + + p_env->gen.ioctls_debugged++; + + cdda.cdda_addr = lsn; + cdda.cdda_length = nblocks; + cdda.cdda_data = (caddr_t) data; + cdda.cdda_subcode = CDROM_DA_NO_SUBCODE; + + if (ioctl (p_env->gen.fd, CDROMCDDA, &cdda) == -1) { + perror ("ioctl(..,CDROMCDDA,..)"); + return 1; + /* exit (EXIT_FAILURE); */ + } + + return 0; +} + +/*! + Reads a single mode1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_read_mode1_sector_solaris (void *env, void *data, lsn_t lsn, + bool b_form2) +{ + +#if FIXED + do something here. +#else + return cdio_generic_read_form1_sector(env, data, lsn); +#endif +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode1_sectors_solaris (void *p_user_data, void *p_data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *p_env = p_user_data; + unsigned int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode1_sector_solaris (p_env, + ((char *)p_data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting from lsn. + Returns 0 if no error. + */ +static int +_read_mode2_sector_solaris (void *p_user_data, void *p_data, lsn_t lsn, + bool b_form2) +{ + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + struct cdrom_msf solaris_msf; + msf_t _msf; + int offset = 0; + struct cdrom_cdxa cd_read; + + _img_private_t *p_env = p_user_data; + + cdio_lba_to_msf (cdio_lsn_to_lba(lsn), &_msf); + solaris_msf.cdmsf_min0 = cdio_from_bcd8(_msf.m); + solaris_msf.cdmsf_sec0 = cdio_from_bcd8(_msf.s); + solaris_msf.cdmsf_frame0 = cdio_from_bcd8(_msf.f); + + if (p_env->gen.ioctls_debugged == 75) + cdio_debug ("only displaying every 75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged == 30 * 75) + cdio_debug ("only displaying every 30*75th ioctl from now on"); + + if (p_env->gen.ioctls_debugged < 75 + || (p_env->gen.ioctls_debugged < (30 * 75) + && p_env->gen.ioctls_debugged % 75 == 0) + || p_env->gen.ioctls_debugged % (30 * 75) == 0) + cdio_debug ("reading %2.2d:%2.2d:%2.2d", + solaris_msf.cdmsf_min0, solaris_msf.cdmsf_sec0, + solaris_msf.cdmsf_frame0); + + p_env->gen.ioctls_debugged++; + + /* Using CDROMXA ioctl will actually use the same uscsi command + * as ATAPI, except we don't need to be root + */ + offset = CDIO_CD_XA_SYNC_HEADER; + cd_read.cdxa_addr = lsn; + cd_read.cdxa_data = buf; + cd_read.cdxa_length = 1; + cd_read.cdxa_format = CDROM_XA_SECTOR_DATA; + if (ioctl (p_env->gen.fd, CDROMCDXA, &cd_read) == -1) { + perror ("ioctl(..,CDROMCDXA,..)"); + return 1; + /* exit (EXIT_FAILURE); */ + } + + if (b_form2) + memcpy (p_data, buf + (offset-CDIO_CD_SUBHEADER_SIZE), M2RAW_SECTOR_SIZE); + else + memcpy (((char *)p_data), buf + offset, CDIO_CD_FRAMESIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode2_sectors_solaris (void *p_user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *env = p_user_data; + unsigned int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode2_sector_solaris (env, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static uint32_t +_cdio_stat_size (void *p_user_data) +{ + _img_private_t *env = p_user_data; + + struct cdrom_tocentry tocent; + uint32_t size; + + tocent.cdte_track = CDIO_CDROM_LEADOUT_TRACK; + tocent.cdte_format = CDIO_CDROM_LBA; + if (ioctl (env->gen.fd, CDROMREADTOCENTRY, &tocent) == -1) + { + perror ("ioctl(CDROMREADTOCENTRY)"); + exit (EXIT_FAILURE); + } + + size = tocent.cdte_addr.lba; + + return size; +} + +/*! + Set the arg "key" with "value" in the source device. + Currently "source" and "access-mode" are valid keys. + "source" sets the source device in I/O operations + "access-mode" sets the the method of CD access + + 0 is returned if no error was found, and nonzero if there as an error. +*/ +static int +_set_arg_solaris (void *p_user_data, const char key[], const char value[]) +{ + _img_private_t *env = p_user_data; + + if (!strcmp (key, "source")) + { + if (!value) + return -2; + + free (env->gen.source_name); + + env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "access-mode")) + { + env->access_mode = str_to_access_mode_sunos(key); + } + else + return -1; + + return 0; +} + +/*! + Read and cache the CD's Track Table of Contents and track info. + Return true if successful or false if an error. +*/ +static bool +read_toc_solaris (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + int i; + + /* read TOC header */ + if ( ioctl(p_env->gen.fd, CDROMREADTOCHDR, &p_env->tochdr) == -1 ) { + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCHDR", strerror(errno)); + return false; + } + + p_env->gen.i_first_track = p_env->tochdr.cdth_trk0; + p_env->gen.i_tracks = p_env->tochdr.cdth_trk1; + + /* read individual tracks */ + for (i=p_env->gen.i_first_track; i<=p_env->gen.i_tracks; i++) { + p_env->tocent[i-1].cdte_track = i; + p_env->tocent[i-1].cdte_format = CDIO_CDROM_MSF; + if ( ioctl(p_env->gen.fd, CDROMREADTOCENTRY, &p_env->tocent[i-1]) == -1 ) { + cdio_warn("%s %d: %s\n", + "error in ioctl CDROMREADTOCENTRY for track", + i, strerror(errno)); + return false; + } + } + + /* read the lead-out track */ + p_env->tocent[p_env->tochdr.cdth_trk1].cdte_track = CDIO_CDROM_LEADOUT_TRACK; + p_env->tocent[p_env->tochdr.cdth_trk1].cdte_format = CDIO_CDROM_MSF; + + if (ioctl(p_env->gen.fd, CDROMREADTOCENTRY, + &p_env->tocent[p_env->tochdr.cdth_trk1]) == -1 ) { + cdio_warn("%s: %s\n", + "error in ioctl CDROMREADTOCENTRY for lead-out", + strerror(errno)); + return false; + } + + p_env->gen.toc_init = true; + return true; +} + +/*! + Eject media in CD drive. If successful, as a side effect we + also free obj. + */ +static int +eject_media_solaris (void *p_user_data) { + + _img_private_t *env = p_user_data; + int ret; + + close(env->gen.fd); + env->gen.fd = -1; + if (env->gen.fd > -1) { + if ((ret = ioctl(env->gen.fd, CDROMEJECT)) != 0) { + cdio_generic_free((void *) env); + cdio_warn ("CDROMEJECT failed: %s\n", strerror(errno)); + return 1; + } else { + return 0; + } + } + return 2; +} + + +static void * +_cdio_malloc_and_zero(size_t size) { + void *ptr; + + if( !size ) size++; + + if((ptr = malloc(size)) == NULL) { + cdio_warn("malloc() failed: %s", strerror(errno)); + return NULL; + } + + memset(ptr, 0, size); + return ptr; +} + +/*! + Return the value associated with the key "arg". +*/ +static const char * +get_arg_solaris (void *p_user_data, const char key[]) +{ + _img_private_t *env = p_user_data; + + if (!strcmp (key, "source")) { + return env->gen.source_name; + } else if (!strcmp (key, "access-mode")) { + switch (env->access_mode) { + case _AM_SUN_CTRL_ATAPI: + return "ATAPI"; + case _AM_SUN_CTRL_SCSI: + return "SCSI"; + case _AM_NONE: + return "no access method"; + } + } + return NULL; +} + +/*! + Return a string containing the default CD device if none is specified. + */ +char * +cdio_get_default_device_solaris(void) +{ + char *volume_device; + char *volume_name; + char *volume_action; + char *device; + struct stat stb; + + if ((volume_device = getenv("VOLUME_DEVICE")) != NULL && + (volume_name = getenv("VOLUME_NAME")) != NULL && + (volume_action = getenv("VOLUME_ACTION")) != NULL && + strcmp(volume_action, "insert") == 0) { + + device = _cdio_malloc_and_zero(strlen(volume_device) + + strlen(volume_name) + 2); + if (device == NULL) + return strdup(DEFAULT_CDIO_DEVICE); + sprintf(device, "%s/%s", volume_device, volume_name); + if (stat(device, &stb) != 0 || !S_ISCHR(stb.st_mode)) { + free(device); + return strdup(DEFAULT_CDIO_DEVICE); + } + return device; + } + /* Check if it could be a Solaris media*/ + if((stat(DEFAULT_CDIO_DEVICE, &stb) == 0) && S_ISDIR(stb.st_mode)) { + device = _cdio_malloc_and_zero(strlen(DEFAULT_CDIO_DEVICE) + 4); + sprintf(device, "%s/s0", DEFAULT_CDIO_DEVICE); + return device; + } + return strdup(DEFAULT_CDIO_DEVICE); +} + +/*! + Get disc type associated with cd object. +*/ + +static discmode_t +get_discmode_solaris (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + track_t i_track; + discmode_t discmode=CDIO_DISC_MODE_NO_INFO; + struct dk_minfo media; + int ret; + + /* Get the media info */ + if((ret = ioctl(p_env->gen.fd, DKIOCGMEDIAINFO, &media)) != 0) { + cdio_warn ("DKIOCGMEDIAINFO failed: %s\n", strerror(errno)); + return CDIO_DISC_MODE_NO_INFO; + } + switch(media.dki_media_type) { + case DK_CDROM: + case DK_CDR: + case DK_CDRW: + /* Do cdrom detection */ + break; + case DK_DVDROM: return CDIO_DISC_MODE_DVD_ROM; + case DK_DVDR: discmode = CDIO_DISC_MODE_DVD_R; + break; + case DK_DVDRAM: discmode = CDIO_DISC_MODE_DVD_RAM; + break; + case DK_DVDRW: + case DK_DVDRW+1: discmode = CDIO_DISC_MODE_DVD_RW; + break; + default: /* no valid match */ + return CDIO_DISC_MODE_NO_INFO; + } + + if((discmode == CDIO_DISC_MODE_DVD_RAM || + discmode == CDIO_DISC_MODE_DVD_RW || + discmode == CDIO_DISC_MODE_DVD_R)) { + /* Fallback to uscsi if we can */ + if(geteuid() == 0) + return get_discmode_solaris(p_user_data); + return discmode; + } + + if (!p_env->gen.toc_init) + read_toc_solaris (p_env); + + if (!p_env->gen.toc_init) + return CDIO_DISC_MODE_NO_INFO; + + for (i_track = p_env->gen.i_first_track; + i_track < p_env->gen.i_first_track + p_env->tochdr.cdth_trk1 ; + i_track ++) { + track_format_t track_fmt=get_track_format_solaris(p_env, i_track); + + switch(track_fmt) { + case TRACK_FORMAT_AUDIO: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DA; + break; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_XA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_XA; + break; + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_DATA: + switch(discmode) { + case CDIO_DISC_MODE_NO_INFO: + discmode = CDIO_DISC_MODE_CD_DATA; + break; + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_ERROR: + /* No change*/ + break; + default: + discmode = CDIO_DISC_MODE_CD_MIXED; + } + break; + case TRACK_FORMAT_ERROR: + default: + discmode = CDIO_DISC_MODE_ERROR; + } + } + return discmode; +} + +/*! + Get format of track. +*/ +static track_format_t +get_track_format_solaris(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if ( !p_env ) return TRACK_FORMAT_ERROR; + if (!p_env->gen.init) init_solaris(p_env); + if (!p_env->gen.toc_init) read_toc_solaris (p_user_data) ; + + if ( (i_track > p_env->gen.i_tracks+p_env->gen.i_first_track) + || i_track < p_env->gen.i_first_track) + return TRACK_FORMAT_ERROR; + + i_track -= p_env->gen.i_first_track; + + /* This is pretty much copied from the "badly broken" cdrom_count_tracks + in linux/cdrom.c. + */ + if (p_env->tocent[i_track].cdte_ctrl & CDROM_DATA_TRACK) { + if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_CDI_TRACK) + return TRACK_FORMAT_CDI; + else if (p_env->tocent[i_track].cdte_format == CDIO_CDROM_XA_TRACK) + return TRACK_FORMAT_XA; + else + return TRACK_FORMAT_DATA; + } else + return TRACK_FORMAT_AUDIO; + +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +static bool +_cdio_get_track_green(void *p_user_data, track_t i_track) +{ + _img_private_t *p_env = p_user_data; + + if ( !p_env ) return false; + if (!p_env->gen.init) init_solaris(p_env); + if (!p_env->gen.toc_init) read_toc_solaris (p_env) ; + + if (i_track >= p_env->gen.i_tracks+p_env->gen.i_first_track + || i_track < p_env->gen.i_first_track) + return false; + + i_track -= p_env->gen.i_first_track; + + /* FIXME: Dunno if this is the right way, but it's what + I was using in cd-info for a while. + */ + return ((p_env->tocent[i_track].cdte_ctrl & 2) != 0); +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in obj. Track numbers usually start at something + greater than 0, usually 1. + + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + False is returned if there is no entry. +*/ +static bool +_cdio_get_track_msf(void *p_user_data, track_t i_track, msf_t *msf) +{ + _img_private_t *p_env = p_user_data; + + if (NULL == msf) return false; + + if (!p_env->gen.init) init_solaris(p_env); + if (!p_env->gen.toc_init) read_toc_solaris (p_env) ; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) + i_track = p_env->gen.i_tracks + p_env->gen.i_first_track; + + if (i_track > (p_env->gen.i_tracks+p_env->gen.i_first_track) + || i_track < p_env->gen.i_first_track) { + return false; + } else { + struct cdrom_tocentry *msf0 = &p_env->tocent[i_track-1]; + msf->m = cdio_to_bcd8(msf0->cdte_addr.msf.minute); + msf->s = cdio_to_bcd8(msf0->cdte_addr.msf.second); + msf->f = cdio_to_bcd8(msf0->cdte_addr.msf.frame); + return true; + } +} + +#else +/*! + Return a string containing the default VCD device if none is specified. + */ +char * +cdio_get_default_device_solaris(void) +{ + return strdup(DEFAULT_CDIO_DEVICE); +} + +#endif /* HAVE_SOLARIS_CDROM */ + +/*! + Return an array of strings giving possible CD devices. + */ +char ** +cdio_get_devices_solaris (void) +{ +#ifndef HAVE_SOLARIS_CDROM + return NULL; +#else + char volpath[256]; + struct stat st; + char **drives = NULL; + unsigned int i_files=0; +#ifdef HAVE_GLOB_H + unsigned int i; + glob_t globbuf; + + globbuf.gl_offs = 0; + glob("/vol/dev/aliases/cdrom*", GLOB_DOOFFS, NULL, &globbuf); + for (i=0; i<globbuf.gl_pathc; i++) { + if(stat(globbuf.gl_pathv[i], &st) < 0) + continue; + + /* Check if this is a directory, if so it's probably Solaris media */ + if(S_ISDIR(st.st_mode)) { + sprintf(volpath, "%s/s0", globbuf.gl_pathv[i]); + if(stat(volpath, &st) == 0) + cdio_add_device_list(&drives, volpath, &i_files); + }else + cdio_add_device_list(&drives, globbuf.gl_pathv[i], &i_files); + } + globfree(&globbuf); +#else + if(stat(DEFAULT_CDIO_DEVICE, &st) == 0) { + /* Check if this is a directory, if so it's probably Solaris media */ + if(S_ISDIR(st.st_mode)) { + sprintf(volpath, "%s/s0", DEFAULT_CDIO_DEVICE); + if(stat(volpath, &st) == 0) + cdio_add_device_list(&drives, volpath, &i_files); + }else + cdio_add_device_list(&drives, DEFAULT_CDIO_DEVICE, &i_files); + } +#endif /*HAVE_GLOB_H*/ + cdio_add_device_list(&drives, NULL, &i_files); + return drives; +#endif /*HAVE_SOLARIS_CDROM*/ +} + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_solaris (const char *psz_source_name) +{ + return cdio_open_am_solaris(psz_source_name, NULL); +} + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_am_solaris (const char *psz_orig_source, const char *access_mode) +{ + +#ifdef HAVE_SOLARIS_CDROM + CdIo *ret; + _img_private_t *_data; + char *psz_source; + + cdio_funcs _funcs; + + _funcs.eject_media = eject_media_solaris; + _funcs.free = cdio_generic_free; + _funcs.get_arg = get_arg_solaris; + _funcs.get_cdtext = get_cdtext_generic; + _funcs.get_default_device = cdio_get_default_device_solaris; + _funcs.get_devices = cdio_get_devices_solaris; + _funcs.get_discmode = get_discmode_solaris; + _funcs.get_drive_cap = scsi_mmc_get_drive_cap_generic; + _funcs.get_first_track_num= get_first_track_num_generic; + _funcs.get_hwinfo = NULL; + _funcs.get_mcn = scsi_mmc_get_mcn_generic, + _funcs.get_num_tracks = get_num_tracks_generic; + _funcs.get_track_format = get_track_format_solaris; + _funcs.get_track_green = _cdio_get_track_green; + _funcs.get_track_lba = NULL; /* This could be implemented if need be. */ + _funcs.get_track_msf = _cdio_get_track_msf; + _funcs.lseek = cdio_generic_lseek; + _funcs.read = cdio_generic_read; + _funcs.read_audio_sectors = _read_audio_sectors_solaris; + _funcs.read_mode1_sector = _read_mode1_sector_solaris; + _funcs.read_mode1_sectors = _read_mode1_sectors_solaris; + _funcs.read_mode2_sector = _read_mode2_sector_solaris; + _funcs.read_mode2_sectors = _read_mode2_sectors_solaris; + _funcs.read_toc = read_toc_solaris; + _funcs.run_scsi_mmc_cmd = run_scsi_cmd_solaris; + _funcs.stat_size = _cdio_stat_size; + _funcs.set_arg = _set_arg_solaris; + + _data = _cdio_malloc (sizeof (_img_private_t)); + + _data->access_mode = _AM_SUN_CTRL_SCSI; + _data->gen.init = false; + _data->gen.fd = -1; + _data->gen.toc_init = false; + _data->gen.b_cdtext_init = false; + _data->gen.b_cdtext_error = false; + + if (NULL == psz_orig_source) { + psz_source = cdio_get_default_device_solaris(); + if (NULL == psz_source) return NULL; + _set_arg_solaris(_data, "source", psz_source); + free(psz_source); + } else { + if (cdio_is_device_generic(psz_orig_source)) + _set_arg_solaris(_data, "source", psz_orig_source); + else { + /* The below would be okay if all device drivers worked this way. */ +#if 0 + cdio_info ("source %s is not a device", psz_orig_source); +#endif + return NULL; + } + } + + ret = cdio_new ( (void *) _data, &_funcs ); + if (ret == NULL) return NULL; + + if (init_solaris(_data)) + return ret; + else { + cdio_generic_free (_data); + return NULL; + } + +#else + return NULL; +#endif /* HAVE_SOLARIS_CDROM */ + +} + +bool +cdio_have_solaris (void) +{ +#ifdef HAVE_SOLARIS_CDROM + return true; +#else + return false; +#endif /* HAVE_SOLARIS_CDROM */ +} diff --git a/contrib/libcdio/cd_types.c b/contrib/libcdio/cd_types.c new file mode 100644 index 000000000..b983f9899 --- /dev/null +++ b/contrib/libcdio/cd_types.c @@ -0,0 +1,358 @@ +/* + $Id: cd_types.c,v 1.4 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* + This tries to determine what kind of CD-image or filesystems on a + track we've got. +*/ +#include "config.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include <cdio/cdio.h> +#include <cdio/iso9660.h> +#include <cdio/logging.h> +#include <cdio/util.h> +#include <cdio/cd_types.h> + +/* +Subject: -65- How can I read an IRIX (EFS) CD-ROM on a machine which + doesn't use EFS? +Date: 18 Jun 1995 00:00:01 EST + + You want 'efslook', at + ftp://viz.tamu.edu/pub/sgi/software/efslook.tar.gz. + +and +! Robert E. Seastrom <rs@access4.digex.net>'s software (with source +! code) for using an SGI CD-ROM on a Macintosh is at +! ftp://bifrost.seastrom.com/pub/mac/CDROM-Jumpstart.sit151.hqx. + +*/ + +static char buffer[6][CDIO_CD_FRAMESIZE_RAW]; /* for CD-Data */ + +/* Some interesting sector numbers stored in the above buffer. */ +#define ISO_SUPERBLOCK_SECTOR 16 /* buffer[0] */ +#define UFS_SUPERBLOCK_SECTOR 4 /* buffer[2] */ +#define BOOT_SECTOR 17 /* buffer[3] */ +#define VCD_INFO_SECTOR 150 /* buffer[4] */ +#define XISO_SECTOR 32 /* buffer[4] */ +#define UDFX_SECTOR 32 /* buffer[4] */ +#define UDF_ANCHOR_SECTOR 256 /* buffer[5] */ + + +typedef struct signature +{ + unsigned int buf_num; + unsigned int offset; + const char *sig_str; + const char *description; +} signature_t; + +static signature_t sigs[] = + { +/*buffer[x] off look for description */ + {0, 0, "MICROSOFT*XBOX*MEDIA", "XBOX CD"}, + {0, 1, "BEA01", "UDF"}, + {0, 1, ISO_STANDARD_ID, "ISO 9660"}, + {0, 1, "CD-I", "CD-I"}, + {0, 8, "CDTV", "CDTV"}, + {0, 8, "CD-RTOS", "CD-RTOS"}, + {0, 9, "CDROM", "HIGH SIERRA"}, + {0, 16, "CD-BRIDGE", "BRIDGE"}, + {0, ISO_XA_MARKER_OFFSET, ISO_XA_MARKER_STRING, "XA"}, + {1, 64, "PPPPHHHHOOOOTTTTOOOO____CCCCDDDD", "PHOTO CD"}, + {1, 0x438, "\x53\xef", "EXT2 FS"}, + {2, 1372, "\x54\x19\x01\x0", "UFS"}, + {3, 7, "EL TORITO", "BOOTABLE"}, + {4, 0, "VIDEO_CD", "VIDEO CD"}, + {4, 0, "SUPERVCD", "SVCD or Chaoji VCD"}, + { 0 } + }; + + +/* The below index into the above sigs array. Make sure things match. */ +#define INDEX_XISO 0 /* Microsoft X-BOX filesystem */ +#define INDEX_UDF 1 +#define INDEX_ISOFS 2 +#define INDEX_CD_I 3 +#define INDEX_CDTV 4 +#define INDEX_CD_RTOS 5 +#define INDEX_HS 6 +#define INDEX_BRIDGE 7 +#define INDEX_XA 8 +#define INDEX_PHOTO_CD 9 +#define INDEX_EXT2 10 +#define INDEX_UFS 11 +#define INDEX_BOOTABLE 12 +#define INDEX_VIDEO_CD 13 /* Video CD */ +#define INDEX_SVCD 14 /* CVD *or* SVCD */ + + +/* + Read a particular block into the global array to be used for further + analysis later. +*/ +static int +_cdio_read_block(const CdIo *cdio, int superblock, uint32_t offset, + uint8_t bufnum, track_t i_track) +{ + unsigned int track_sec_count = cdio_get_track_sec_count(cdio, i_track); + memset(buffer[bufnum], 0, CDIO_CD_FRAMESIZE); + + if ( track_sec_count < superblock) { + cdio_debug("reading block %u skipped track %d has only %u sectors\n", + superblock, i_track, track_sec_count); + return -1; + } + + cdio_debug("about to read sector %lu\n", + (long unsigned int) offset+superblock); + + if (cdio_get_track_green(cdio, i_track)) { + if (0 > cdio_read_mode2_sector(cdio, buffer[bufnum], + offset+superblock, false)) + return -1; + } else { + if (0 > cdio_read_mode1_sector(cdio, buffer[bufnum], + offset+superblock, false)) + return -1; + } + + return 0; +} + +/* + Return true if the previously read-in buffer contains a "signature" that + matches index "num". + */ +static bool +_cdio_is_it(int num) +{ + signature_t *sigp=&sigs[num]; + int len=strlen(sigp->sig_str); + + /* TODO: check that num < largest sig. */ + return 0 == memcmp(&buffer[sigp->buf_num][sigp->offset], sigp->sig_str, len); +} + +static int +_cdio_is_hfs(void) +{ + return (0 == memcmp(&buffer[1][512],"PM",2)) || + (0 == memcmp(&buffer[1][512],"TS",2)) || + (0 == memcmp(&buffer[1][1024], "BD",2)); +} + +static int +_cdio_is_3do(void) +{ + return (0 == memcmp(&buffer[1][0],"\x01\x5a\x5a\x5a\x5a\x5a\x01", 7)) && + (0 == memcmp(&buffer[1][40], "CD-ROM", 6)); +} + +static int +_cdio_is_joliet(void) +{ + return 2 == buffer[3][0] && buffer[3][88] == 0x25 && buffer[3][89] == 0x2f; +} + +static int +_cdio_is_UDF(void) +{ + return 2 == ((uint16_t)buffer[5][0] | ((uint16_t)buffer[5][1] << 8)); +} + +/* ISO 9660 volume space in M2F1_SECTOR_SIZE byte units */ +static int +_cdio_get_iso9660_fs_sec_count(void) +{ + return ((buffer[0][80] & 0xff) | + ((buffer[0][81] & 0xff) << 8) | + ((buffer[0][82] & 0xff) << 16) | + ((buffer[0][83] & 0xff) << 24)); +} + +static int +_cdio_get_joliet_level( void ) +{ + switch (buffer[3][90]) { + case 0x40: return 1; + case 0x43: return 2; + case 0x45: return 3; + } + return 0; +} + +/* + Try to determine what kind of CD-image and/or filesystem we + have at track i_track. Return information about the CD image + is returned in cdio_analysis and the return value. +*/ +cdio_fs_anal_t +cdio_guess_cd_type(const CdIo *cdio, int start_session, track_t i_track, + /*out*/ cdio_iso_analysis_t *iso_analysis) +{ + int ret = CDIO_FS_UNKNOWN; + bool sector0_read_ok; + + if (TRACK_FORMAT_AUDIO == cdio_get_track_format(cdio, i_track)) + return CDIO_FS_AUDIO; + + if ( _cdio_read_block(cdio, ISO_PVD_SECTOR, start_session, + 0, i_track) < 0 ) + return CDIO_FS_UNKNOWN; + + if ( _cdio_is_it(INDEX_XISO) ) + return CDIO_FS_ANAL_XISO; + + if (_cdio_read_block(cdio, ISO_SUPERBLOCK_SECTOR, start_session, 0, + i_track) < 0) + return ret; + + if ( _cdio_is_it(INDEX_UDF) ) { + /* Detect UDF version + Test if we have a valid version of UDF the xbox can read natively */ + if (_cdio_read_block(cdio, 35, start_session, 5, i_track) < 0) + return CDIO_FS_UNKNOWN; + + iso_analysis->UDFVerMinor=(unsigned int)buffer[5][240]; + iso_analysis->UDFVerMajor=(unsigned int)buffer[5][241]; + /* Read disc label */ + if (_cdio_read_block(cdio, 32, start_session, 5, i_track) < 0) + return CDIO_FS_UDF; + + strncpy(iso_analysis->iso_label, buffer[5]+25, 33); + iso_analysis->iso_label[32] = '\0'; + return CDIO_FS_UDF; + } + + /* We have something that smells of a filesystem. */ + if (_cdio_is_it(INDEX_CD_I) && _cdio_is_it(INDEX_CD_RTOS) + && !_cdio_is_it(INDEX_BRIDGE) && !_cdio_is_it(INDEX_XA)) { + return CDIO_FS_INTERACTIVE; + } else { + /* read sector 0 ONLY, when NO greenbook CD-I !!!! */ + + sector0_read_ok = + _cdio_read_block(cdio, 0, start_session, 1, i_track) == 0; + + if (_cdio_is_it(INDEX_HS)) + ret |= CDIO_FS_HIGH_SIERRA; + else if (_cdio_is_it(INDEX_ISOFS)) { + if (_cdio_is_it(INDEX_CD_RTOS) && _cdio_is_it(INDEX_BRIDGE)) + ret = CDIO_FS_ISO_9660_INTERACTIVE; + else if (_cdio_is_hfs()) + ret = CDIO_FS_ISO_HFS; + else + ret = CDIO_FS_ISO_9660; + iso_analysis->isofs_size = _cdio_get_iso9660_fs_sec_count(); + strncpy(iso_analysis->iso_label, buffer[0]+40,33); + iso_analysis->iso_label[32] = '\0'; + + if ( _cdio_read_block(cdio, UDF_ANCHOR_SECTOR, start_session, 5, + i_track) < 0) + return ret; + + /* Maybe there is an UDF anchor in IOS session + so its ISO/UDF session and we prefere UDF */ + if ( _cdio_is_UDF() ) { + /* Detect UDF version. + Test if we have a valid version of UDF the xbox can read natively */ + if ( _cdio_read_block(cdio, 35, start_session, 5, i_track) < 0) + return ret; + + iso_analysis->UDFVerMinor=(unsigned int)buffer[5][240]; + iso_analysis->UDFVerMajor=(unsigned int)buffer[5][241]; +#if 0 + /* We are using ISO/UDF cd's as iso, + no need to get UDF disc label */ + if (_cdio_read_block(cdio, 32, start_session, 5, i_track) < 0) + return ret; + stnrcpy(iso_analysis->iso_label, buffer[5]+25, 33); + iso_analysis->iso_label[32] = '\0'; +#endif + ret=CDIO_FS_ISO_UDF; + } + +#if 0 + if (_cdio_is_rockridge()) + ret |= CDIO_FS_ANAL_ROCKRIDGE; +#endif + + if (_cdio_read_block(cdio, BOOT_SECTOR, start_session, 3, i_track) < 0) + return ret; + + if (_cdio_is_joliet()) { + iso_analysis->joliet_level = _cdio_get_joliet_level(); + ret |= CDIO_FS_ANAL_JOLIET; + } + if (_cdio_is_it(INDEX_BOOTABLE)) + ret |= CDIO_FS_ANAL_BOOTABLE; + + if ( _cdio_is_it(INDEX_XA) && _cdio_is_it(INDEX_ISOFS) + && !(sector0_read_ok && _cdio_is_it(INDEX_PHOTO_CD)) ) { + + if ( _cdio_read_block(cdio, VCD_INFO_SECTOR, start_session, 4, + i_track) < 0 ) + return ret; + + if (_cdio_is_it(INDEX_BRIDGE) && _cdio_is_it(INDEX_CD_RTOS)) { + if (_cdio_is_it(INDEX_VIDEO_CD)) ret |= CDIO_FS_ANAL_VIDEOCD; + else if (_cdio_is_it(INDEX_SVCD)) ret |= CDIO_FS_ANAL_SVCD; + } else if (_cdio_is_it(INDEX_SVCD)) ret |= CDIO_FS_ANAL_CVD; + + } + } + else if (_cdio_is_hfs()) ret |= CDIO_FS_HFS; + else if (sector0_read_ok && _cdio_is_it(INDEX_EXT2)) ret |= CDIO_FS_EXT2; + else if (_cdio_is_3do()) ret |= CDIO_FS_3DO; + else { + if ( _cdio_read_block(cdio, UFS_SUPERBLOCK_SECTOR, start_session, 2, + i_track) < 0 ) + return ret; + + if (sector0_read_ok && _cdio_is_it(INDEX_UFS)) + ret |= CDIO_FS_UFS; + else + ret |= CDIO_FS_UNKNOWN; + } + } + + /* other checks */ + if (_cdio_is_it(INDEX_XA)) ret |= CDIO_FS_ANAL_XA; + if (_cdio_is_it(INDEX_PHOTO_CD)) ret |= CDIO_FS_ANAL_PHOTO_CD; + if (_cdio_is_it(INDEX_CDTV)) ret |= CDIO_FS_ANAL_CDTV; + return ret; +} diff --git a/contrib/libcdio/cdio.c b/contrib/libcdio/cdio.c new file mode 100644 index 000000000..fc18add91 --- /dev/null +++ b/contrib/libcdio/cdio.c @@ -0,0 +1,1125 @@ +/* + $Id: cdio.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <errno.h> +#include <string.h> + +#include "cdio_assert.h" +#include <cdio/cdio.h> +#include <cdio/cd_types.h> +#include <cdio/util.h> +#include <cdio/logging.h> +#include "cdio_private.h" + +static const char _rcsid[] = "$Id: cdio.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $"; + + +const char *track_format2str[6] = + { + "audio", "CD-i", "XA", "data", "PSX", "error" + }; + +/* Must match discmode enumeration */ +const char *discmode2str[] = { + "CD-DA", + "CD-DATA Form 1", + "CD DATA Form 2", + "CD-ROM Mixed", + "DVD-ROM", + "DVD-RAM", + "DVD-R", + "DVD-RW", + "DVD+R", + "DVD+RW", + "Unknown/unclassified DVD", + "No information", + "Error in getting information" +}; + + +/* The below array gives of the drivers that are currently available for + on a particular host. */ + +CdIo_driver_t CdIo_driver[CDIO_MAX_DRIVER] = { {0} }; + +/* The last valid entry of Cdio_driver. + -1 or (CDIO_DRIVER_UNINIT) means uninitialzed. + -2 means some sort of error. +*/ + +#define CDIO_DRIVER_UNINIT -1 +int CdIo_last_driver = CDIO_DRIVER_UNINIT; + +#ifdef HAVE_BSDI_CDROM +const driver_id_t cdio_os_driver = DRIVER_BSDI; +#elif HAVE_FREEBSD_CDROM +const driver_id_t cdio_os_driver = DRIVER_FREEBSD; +#elif HAVE_LINUX_CDROM +const driver_id_t cdio_os_driver = DRIVER_LINUX; +#elif HAVE_DARWIN_CDROM +const driver_id_t cdio_os_driver = DRIVER_OSX; +#elif HAVE_DARWIN_SOLARIS +const driver_id_t cdio_os_driver = DRIVER_SOLARIS; +#elif HAVE_DARWIN_WIN32 +const driver_id_t cdio_os_driver = DRIVER_WIN32; +#else +const driver_id_t cdio_os_driver = DRIVER_UNKNOWN; +#endif + +static bool +cdio_have_false(void) +{ + return false; +} + +/* The below array gives all drivers that can possibly appear. + on a particular host. */ + +CdIo_driver_t CdIo_all_drivers[CDIO_MAX_DRIVER+1] = { + {DRIVER_UNKNOWN, + 0, + "Unknown", + "No driver", + &cdio_have_false, + NULL, + NULL, + NULL, + NULL, + NULL + }, + + {DRIVER_BSDI, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK|CDIO_SRC_IS_SCSI_MASK, + "BSDI", + "BSDI ATAPI and SCSI driver", + &cdio_have_bsdi, + &cdio_open_bsdi, + &cdio_open_am_bsdi, + &cdio_get_default_device_bsdi, + &cdio_is_device_generic, + &cdio_get_devices_bsdi + }, + + {DRIVER_FREEBSD, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK|CDIO_SRC_IS_SCSI_MASK, + "FreeBSD", + "FreeBSD driver", + &cdio_have_freebsd, + &cdio_open_freebsd, + &cdio_open_am_freebsd, + &cdio_get_default_device_freebsd, + &cdio_is_device_generic, + NULL + }, + + {DRIVER_LINUX, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK, + "GNU/Linux", + "GNU/Linux ioctl and MMC driver", + &cdio_have_linux, + &cdio_open_linux, + &cdio_open_am_linux, + &cdio_get_default_device_linux, + &cdio_is_device_generic, + &cdio_get_devices_linux + }, + + {DRIVER_SOLARIS, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK|CDIO_SRC_IS_SCSI_MASK, + "Solaris", + "Solaris ATAPI and SCSI driver", + &cdio_have_solaris, + &cdio_open_solaris, + &cdio_open_am_solaris, + &cdio_get_default_device_solaris, + &cdio_is_device_generic, + &cdio_get_devices_solaris + }, + + {DRIVER_OSX, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK|CDIO_SRC_IS_SCSI_MASK, + "OS X", + "Apple Darwin OS X driver", + &cdio_have_osx, + &cdio_open_osx, + &cdio_open_am_osx, + &cdio_get_default_device_osx, + &cdio_is_device_generic, + &cdio_get_devices_osx + }, + + {DRIVER_WIN32, + CDIO_SRC_IS_DEVICE_MASK|CDIO_SRC_IS_NATIVE_MASK|CDIO_SRC_IS_SCSI_MASK, + "WIN32", + "MS Windows ASPI and ioctl driver", + &cdio_have_win32, + &cdio_open_win32, + &cdio_open_am_win32, + &cdio_get_default_device_win32, + &cdio_is_device_win32, + &cdio_get_devices_win32 + }, + + {DRIVER_CDRDAO, + CDIO_SRC_IS_DISK_IMAGE_MASK, + "CDRDAO", + "cdrdao (TOC) disk image driver", + &cdio_have_cdrdao, + &cdio_open_cdrdao, + &cdio_open_am_cdrdao, + &cdio_get_default_device_cdrdao, + NULL, + &cdio_get_devices_cdrdao + }, + + {DRIVER_BINCUE, + CDIO_SRC_IS_DISK_IMAGE_MASK, + "BIN/CUE", + "bin/cuesheet disk image driver", + &cdio_have_bincue, + &cdio_open_bincue, + &cdio_open_am_bincue, + &cdio_get_default_device_bincue, + NULL, + &cdio_get_devices_bincue + }, + + {DRIVER_NRG, + CDIO_SRC_IS_DISK_IMAGE_MASK, + "NRG", + "Nero NRG disk image driver", + &cdio_have_nrg, + &cdio_open_nrg, + &cdio_open_am_nrg, + &cdio_get_default_device_nrg, + NULL, + &cdio_get_devices_nrg + } + +}; + +static CdIo * +scan_for_driver(driver_id_t start, driver_id_t end, + const char *psz_source, const char *access_mode) +{ + driver_id_t driver_id; + + for (driver_id=start; driver_id<=end; driver_id++) { + if ((*CdIo_all_drivers[driver_id].have_driver)()) { + CdIo *ret= + (*CdIo_all_drivers[driver_id].driver_open_am)(psz_source, access_mode); + if (ret != NULL) { + ret->driver_id = driver_id; + return ret; + } + } + } + return NULL; +} + +const char * +cdio_driver_describe(driver_id_t driver_id) +{ + return CdIo_all_drivers[driver_id].describe; +} + +/*! + Eject media in CD drive if there is a routine to do so. + Return 0 if success and 1 for failure, and 2 if no routine. + If the CD is ejected *obj is freed and obj set to NULL. + */ +int +cdio_eject_media (CdIo **obj) +{ + + if ((obj == NULL) || (*obj == NULL)) return 1; + + if ((*obj)->op.eject_media) { + int ret = (*obj)->op.eject_media ((*obj)->env); + if (0 == ret) { + cdio_destroy(*obj); + *obj = NULL; + } + return ret; + } else { + cdio_destroy(*obj); + *obj = NULL; + return 2; + } +} + +/*! + Free device list returned by cdio_get_devices or + cdio_get_devices_with_cap. +*/ +void cdio_free_device_list (char * device_list[]) +{ + if (NULL == device_list) return; + for ( ; *device_list != NULL ; device_list++ ) + free(*device_list); +} + + +/*! + Return the value associatied with key. NULL is returned if obj is NULL + or "key" does not exist. + */ +const char * +cdio_get_arg (const CdIo *obj, const char key[]) +{ + if (obj == NULL) return NULL; + + if (obj->op.get_arg) { + return obj->op.get_arg (obj->env, key); + } else { + return NULL; + } +} + +/*! + Get cdtext information for a CdIo object . + + @param obj the CD object that may contain CD-TEXT information. + @return the CD-TEXT object or NULL if obj is NULL + or CD-TEXT information does not exist. +*/ +const cdtext_t * +cdio_get_cdtext (CdIo *obj, track_t i_track) +{ + if (obj == NULL) return NULL; + + if (obj->op.get_cdtext) { + return obj->op.get_cdtext (obj->env, i_track); + } else { + return NULL; + } +} + +/*! + Return a string containing the default CD device if none is specified. + if CdIo is NULL (we haven't initialized a specific device driver), + then find a suitable one and return the default device for that. + + NULL is returned if we couldn't get a default device. + */ +char * +cdio_get_default_device (const CdIo *obj) +{ + if (obj == NULL) { + driver_id_t driver_id; + /* Scan for driver */ + for (driver_id=DRIVER_UNKNOWN; driver_id<=CDIO_MAX_DRIVER; driver_id++) { + if ( (*CdIo_all_drivers[driver_id].have_driver)() && + *CdIo_all_drivers[driver_id].get_default_device ) { + return (*CdIo_all_drivers[driver_id].get_default_device)(); + } + } + return NULL; + } + + if (obj->op.get_default_device) { + return obj->op.get_default_device (); + } else { + return NULL; + } +} + +/*!Return an array of device names. If you want a specific + devices, dor a driver give that device, if you want hardware + devices, give DRIVER_DEVICE and if you want all possible devices, + image drivers and hardware drivers give DRIVER_UNKNOWN. + + NULL is returned if we couldn't return a list of devices. +*/ +char ** +cdio_get_devices (driver_id_t driver_id) +{ + /* Probably could get away with &driver_id below. */ + driver_id_t driver_id_temp = driver_id; + return cdio_get_devices_ret (&driver_id_temp); +} + +char ** +cdio_get_devices_ret (/*in/out*/ driver_id_t *p_driver_id) +{ + CdIo *p_cdio; + + switch (*p_driver_id) { + /* FIXME: spit out unknown to give image drivers as well. */ + case DRIVER_UNKNOWN: + case DRIVER_DEVICE: + p_cdio = scan_for_driver(DRIVER_UNKNOWN, CDIO_MAX_DRIVER, NULL, NULL); + *p_driver_id = cdio_get_driver_id(p_cdio); + break; + default: + return (*CdIo_all_drivers[*p_driver_id].get_devices)(); + } + + if (p_cdio == NULL) return NULL; + if (p_cdio->op.get_devices) { + char **devices = p_cdio->op.get_devices (); + cdio_destroy(p_cdio); + return devices; + } else { + return NULL; + } +} + +/*! + Return an array of device names in search_devices that have at + least the capabilities listed by cap. If search_devices is NULL, + then we'll search all possible CD drives. + + If "any" is set false then every capability listed in the extended + portion of capabilities (i.e. not the basic filesystem) must be + satisified. If "any" is set true, then if any of the capabilities + matches, we call that a success. + + To find a CD-drive of any type, use the mask CDIO_FS_MATCH_ALL. + + NULL is returned if we couldn't get a default device. + It is also possible to return a non NULL but after dereferencing the + the value is NULL. This also means nothing was found. +*/ +char ** +cdio_get_devices_with_cap (/*out*/ char* search_devices[], + cdio_fs_anal_t capabilities, bool any) +{ + driver_id_t p_driver_id; + return cdio_get_devices_with_cap_ret (search_devices, capabilities, any, + &p_driver_id); +} + +char ** +cdio_get_devices_with_cap_ret (/*out*/ char* search_devices[], + cdio_fs_anal_t capabilities, bool any, + /*out*/ driver_id_t *p_driver_id) +{ + char **drives=search_devices; + char **drives_ret=NULL; + unsigned int i_drives=0; + + *p_driver_id = DRIVER_DEVICE; + + if (NULL == drives) drives=cdio_get_devices_ret(p_driver_id); + if (NULL == drives) return NULL; + + if (capabilities == CDIO_FS_MATCH_ALL) { + /* Duplicate drives into drives_ret. */ + char **d = drives; + + for( ; *d != NULL; d++ ) { + cdio_add_device_list(&drives_ret, *d, &i_drives); + } + } else { + cdio_fs_anal_t got_fs=0; + cdio_fs_anal_t need_fs = CDIO_FSTYPE(capabilities); + cdio_fs_anal_t need_fs_ext; + char **d = drives; + need_fs_ext = capabilities & ~CDIO_FS_MASK; + + for( ; *d != NULL; d++ ) { + CdIo *cdio = cdio_open(*d, *p_driver_id); + + if (NULL != cdio) { + track_t first_track = cdio_get_first_track_num(cdio); + cdio_iso_analysis_t cdio_iso_analysis; + got_fs = cdio_guess_cd_type(cdio, 0, first_track, + &cdio_iso_analysis); + /* Match on fs and add */ + if ( (CDIO_FS_UNKNOWN == need_fs || CDIO_FSTYPE(got_fs) == need_fs) ) + { + bool doit = any + ? (got_fs & need_fs_ext) != 0 + : (got_fs | ~need_fs_ext) == -1; + if (doit) + cdio_add_device_list(&drives_ret, *d, &i_drives); + } + + cdio_destroy(cdio); + } + } + } + cdio_add_device_list(&drives_ret, NULL, &i_drives); + cdio_free_device_list(drives); + free(drives); + return drives_ret; +} + +/*! + Get medium associated with cd_obj. +*/ +discmode_t +cdio_get_discmode (CdIo *cd_obj) +{ + if (cd_obj == NULL) return CDIO_DISC_MODE_ERROR; + + if (cd_obj->op.get_discmode) { + return cd_obj->op.get_discmode (cd_obj->env); + } else { + return CDIO_DISC_MODE_NO_INFO; + } +} + +/*! + Return the the kind of drive capabilities of device. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +void +cdio_get_drive_cap (const CdIo *p_cdio, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap) +{ + /* This seems like a safe bet. */ + *p_read_cap = CDIO_DRIVE_CAP_UNKNOWN; + *p_write_cap = CDIO_DRIVE_CAP_UNKNOWN; + *p_misc_cap = CDIO_DRIVE_CAP_UNKNOWN; + + if (p_cdio && p_cdio->op.get_drive_cap) { + p_cdio->op.get_drive_cap(p_cdio->env, p_read_cap, p_write_cap, p_misc_cap); + } +} + +/*! + Return the the kind of drive capabilities of device. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +void +cdio_get_drive_cap_dev (const char *device, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap) +{ + /* This seems like a safe bet. */ + CdIo *cdio=scan_for_driver(CDIO_MIN_DRIVER, CDIO_MAX_DRIVER, + device, NULL); + if (cdio) { + cdio_get_drive_cap(cdio, p_read_cap, p_write_cap, p_misc_cap); + cdio_destroy(cdio); + } else { + *p_read_cap = CDIO_DRIVE_CAP_UNKNOWN; + *p_write_cap = CDIO_DRIVE_CAP_UNKNOWN; + *p_misc_cap = CDIO_DRIVE_CAP_UNKNOWN; + } +} + + +/*! + Return a string containing the name of the driver in use. + if CdIo is NULL (we haven't initialized a specific device driver), + then return NULL. +*/ +const char * +cdio_get_driver_name (const CdIo *cdio) +{ + if (NULL==cdio) return NULL; + return CdIo_all_drivers[cdio->driver_id].name; +} + + /*! + Return the driver id. + if CdIo is NULL (we haven't initialized a specific device driver), + then return DRIVER_UNKNOWN. + */ +driver_id_t +cdio_get_driver_id (const CdIo *cdio) +{ + if (NULL==cdio) return DRIVER_UNKNOWN; + return cdio->driver_id; +} + + +/*! + Return the number of the first track. + CDIO_INVALID_TRACK is returned on error. +*/ +track_t +cdio_get_first_track_num (const CdIo *p_cdio) +{ + if (NULL == p_cdio) return CDIO_INVALID_TRACK; + + if (p_cdio->op.get_first_track_num) { + return p_cdio->op.get_first_track_num (p_cdio->env); + } else { + return CDIO_INVALID_TRACK; + } +} + +/*! + Return a string containing the name of the driver in use. + if CdIo is NULL (we haven't initialized a specific device driver), + then return NULL. +*/ +bool +cdio_get_hwinfo (const CdIo *p_cdio, cdio_hwinfo_t *hw_info) +{ + if (!p_cdio) return false; + if (p_cdio->op.get_hwinfo) { + return p_cdio->op.get_hwinfo (p_cdio, hw_info); + } else { + /* Perhaps driver forgot to initialize. We are no worse off Using + scsi_mmc than returning false here. */ + return scsi_mmc_get_hwinfo(p_cdio, hw_info); + } +} + +/*! + Return a string containing the name of the driver in use. + if CdIo is NULL (we haven't initialized a specific device driver), + then return NULL. +*/ +char * +cdio_get_mcn (const CdIo *p_cdio) +{ + if (p_cdio->op.get_mcn) { + return p_cdio->op.get_mcn (p_cdio->env); + } else { + return NULL; + } +} + +/*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. +*/ +track_t +cdio_get_num_tracks (const CdIo *p_cdio) +{ + if (p_cdio == NULL) return CDIO_INVALID_TRACK; + + if (p_cdio->op.get_num_tracks) { + return p_cdio->op.get_num_tracks (p_cdio->env); + } else { + return CDIO_INVALID_TRACK; + } +} + +/*! + Get format of track. +*/ +track_format_t +cdio_get_track_format(const CdIo *p_cdio, track_t i_track) +{ + cdio_assert (p_cdio != NULL); + + if (p_cdio->op.get_track_format) { + return p_cdio->op.get_track_format (p_cdio->env, i_track); + } else { + return TRACK_FORMAT_ERROR; + } +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +bool +cdio_get_track_green(const CdIo *cdio, track_t track_num) +{ + cdio_assert (cdio != NULL); + + if (cdio->op.get_track_green) { + return cdio->op.get_track_green (cdio->env, track_num); + } else { + return false; + } +} + +/*! + Return the starting LBA for track number + track_num in cdio. Tracks numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + CDIO_INVALID_LBA is returned on error. +*/ +lba_t +cdio_get_track_lba(const CdIo *cdio, track_t track_num) +{ + if (cdio == NULL) return CDIO_INVALID_LBA; + + if (cdio->op.get_track_lba) { + return cdio->op.get_track_lba (cdio->env, track_num); + } else { + msf_t msf; + if (cdio->op.get_track_msf) + if (cdio_get_track_msf(cdio, track_num, &msf)) + return cdio_msf_to_lba(&msf); + return CDIO_INVALID_LBA; + } +} + +/*! + Return the starting LSN for track number + track_num in cdio. Tracks numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + CDIO_INVALID_LBA is returned on error. +*/ +lsn_t +cdio_get_track_lsn(const CdIo *cdio, track_t track_num) +{ + if (cdio == NULL) return CDIO_INVALID_LBA; + + if (cdio->op.get_track_lba) { + return cdio_lba_to_lsn(cdio->op.get_track_lba (cdio->env, track_num)); + } else { + msf_t msf; + if (cdio_get_track_msf(cdio, track_num, &msf)) + return cdio_msf_to_lsn(&msf); + return CDIO_INVALID_LSN; + } +} + +/*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in cdio. Track numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +bool +cdio_get_track_msf(const CdIo *cdio, track_t track_num, /*out*/ msf_t *msf) +{ + cdio_assert (cdio != NULL); + + if (cdio->op.get_track_msf) { + return cdio->op.get_track_msf (cdio->env, track_num, msf); + } else if (cdio->op.get_track_lba) { + lba_t lba = cdio->op.get_track_lba (cdio->env, track_num); + if (lba == CDIO_INVALID_LBA) return false; + cdio_lba_to_msf(lba, msf); + return true; + } else { + return false; + } +} + +/*! + Return the number of sectors between this track an the next. This + includes any pregap sectors before the start of the next track. + Tracks start at 1. + 0 is returned if there is an error. +*/ +unsigned int +cdio_get_track_sec_count(const CdIo *cdio, track_t track_num) +{ + track_t num_tracks = cdio_get_num_tracks(cdio); + + if (track_num >=1 && track_num <= num_tracks) + return ( cdio_get_track_lba(cdio, track_num+1) + - cdio_get_track_lba(cdio, track_num) ); + return 0; +} + +bool +cdio_have_driver(driver_id_t driver_id) +{ + return (*CdIo_all_drivers[driver_id].have_driver)(); +} + +/*! + Return the Joliet level recognized for p_cdio. +*/ +uint8_t +cdio_get_joliet_level(const CdIo *p_cdio) +{ + if (!p_cdio) return 0; + { + const generic_img_private_t *p_env + = (generic_img_private_t *) (p_cdio->env); + return p_env->i_joliet_level; + } +} + +bool +cdio_is_device(const char *psz_source, driver_id_t driver_id) +{ + if (CdIo_all_drivers[driver_id].is_device == NULL) return false; + return (*CdIo_all_drivers[driver_id].is_device)(psz_source); +} + + +/*! + Initialize CD Reading and control routines. Should be called first. + May be implicitly called by other routines if not called first. +*/ +bool +cdio_init(void) +{ + + CdIo_driver_t *all_dp; + CdIo_driver_t *dp = CdIo_driver; + driver_id_t driver_id; + + if (CdIo_last_driver != CDIO_DRIVER_UNINIT) { + cdio_warn ("Init routine called more than once."); + return false; + } + + for (driver_id=DRIVER_UNKNOWN; driver_id<=CDIO_MAX_DRIVER; driver_id++) { + all_dp = &CdIo_all_drivers[driver_id]; + if ((*CdIo_all_drivers[driver_id].have_driver)()) { + *dp++ = *all_dp; + CdIo_last_driver++; + } + } + + return true; +} + +CdIo * +cdio_new (generic_img_private_t *p_env, cdio_funcs *p_funcs) +{ + CdIo *p_new_cdio = _cdio_malloc (sizeof (CdIo)); + + if (NULL == p_new_cdio) return NULL; + + p_new_cdio->env = p_env; /* This is the private "environment" that + driver-dependent routines use. */ + p_new_cdio->op = *p_funcs; + p_env->cdio = p_new_cdio; /* A way for the driver-dependent routines + to access the higher-level general cdio + object. */ + return p_new_cdio; +} + +/*! + Free any resources associated with cdio. +*/ +void +cdio_destroy (CdIo *cdio) +{ + CdIo_last_driver = CDIO_DRIVER_UNINIT; + if (cdio == NULL) return; + + if (cdio->op.free != NULL) + cdio->op.free (cdio->env); + free (cdio); +} + +/*! + lseek - reposition read/write file offset + Returns (off_t) -1 on error. + Similar to (if not the same as) libc's lseek() +*/ +off_t +cdio_lseek (const CdIo *cdio, off_t offset, int whence) +{ + if (cdio == NULL) return -1; + + if (cdio->op.lseek) + return cdio->op.lseek (cdio->env, offset, whence); + return -1; +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + Similar to (if not the same as) libc's read() +*/ +ssize_t +cdio_read (const CdIo *p_cdio, void *buf, size_t size) +{ + if (p_cdio == NULL) return -1; + + if (p_cdio->op.read) + return p_cdio->op.read (p_cdio->env, buf, size); + return -1; +} + +/*! + Reads an audio sector from cd device into data starting + from lsn. Returns 0 if no error. +*/ +int +cdio_read_audio_sector (const CdIo *p_cdio, void *buf, lsn_t lsn) +{ + + if (NULL == p_cdio || NULL == buf || CDIO_INVALID_LSN == lsn ) + return 0; + + if (p_cdio->op.read_audio_sectors != NULL) + return p_cdio->op.read_audio_sectors (p_cdio->env, buf, lsn, 1); + return -1; +} + +/*! + Reads audio sectors from cd device into data starting + from lsn. Returns 0 if no error. +*/ +int +cdio_read_audio_sectors (const CdIo *p_cdio, void *buf, lsn_t lsn, + unsigned int nblocks) +{ + if ( NULL == p_cdio || NULL == buf || CDIO_INVALID_LSN == lsn ) + return 0; + + if (p_cdio->op.read_audio_sectors != NULL) + return p_cdio->op.read_audio_sectors (p_cdio->env, buf, lsn, nblocks); + return -1; +} + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +/*! + Reads a single mode1 form1 or form2 sector from cd device + into data starting from lsn. Returns 0 if no error. + */ +int +cdio_read_mode1_sector (const CdIo *p_cdio, void *data, lsn_t lsn, + bool b_form2) +{ + uint32_t size = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE ; + + if (NULL == p_cdio || NULL == data || CDIO_INVALID_LSN == lsn ) + return 0; + + if (p_cdio->op.read_mode1_sector) { + return p_cdio->op.read_mode1_sector(p_cdio->env, data, lsn, b_form2); + } else if (p_cdio->op.lseek && p_cdio->op.read) { + char buf[CDIO_CD_FRAMESIZE] = { 0, }; + if (0 > cdio_lseek(p_cdio, CDIO_CD_FRAMESIZE*lsn, SEEK_SET)) + return -1; + if (0 > cdio_read(p_cdio, buf, CDIO_CD_FRAMESIZE)) + return -1; + memcpy (data, buf, size); + return 0; + } + + return 1; + +} + +int +cdio_read_mode1_sectors (const CdIo *cdio, void *buf, lsn_t lsn, + bool b_form2, unsigned int num_sectors) +{ + + if (NULL == cdio || NULL == buf || CDIO_INVALID_LSN == lsn ) + return 0; + + cdio_assert (cdio->op.read_mode1_sectors != NULL); + + return cdio->op.read_mode1_sectors (cdio->env, buf, lsn, b_form2, + num_sectors); +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +int +cdio_read_mode2_sector (const CdIo *cdio, void *buf, lsn_t lsn, + bool b_form2) +{ + if (NULL == cdio || NULL == buf || CDIO_INVALID_LSN == lsn ) + return 0; + + cdio_assert (cdio->op.read_mode2_sector != NULL + || cdio->op.read_mode2_sectors != NULL); + + if (cdio->op.read_mode2_sector) + return cdio->op.read_mode2_sector (cdio->env, buf, lsn, b_form2); + + /* fallback */ + if (cdio->op.read_mode2_sectors != NULL) + return cdio_read_mode2_sectors (cdio, buf, lsn, b_form2, 1); + return 1; +} + +int +cdio_read_mode2_sectors (const CdIo *cdio, void *buf, lsn_t lsn, + bool b_form2, unsigned int num_sectors) +{ + + if (NULL == cdio || NULL == buf || CDIO_INVALID_LSN == lsn ) + return 0; + + cdio_assert (cdio->op.read_mode2_sectors != NULL); + + return cdio->op.read_mode2_sectors (cdio->env, buf, lsn, + b_form2, num_sectors); +} + +uint32_t +cdio_stat_size (const CdIo *cdio) +{ + cdio_assert (cdio != NULL); + + return cdio->op.stat_size (cdio->env); +} + +/*! + Set the arg "key" with "value" in the source device. +*/ +int +cdio_set_arg (CdIo *cdio, const char key[], const char value[]) +{ + cdio_assert (cdio != NULL); + cdio_assert (cdio->op.set_arg != NULL); + cdio_assert (key != NULL); + + return cdio->op.set_arg (cdio->env, key, value); +} + +/*! Sets up to read from place specified by source_name and + driver_id. This should be called before using any other routine, + except cdio_init. This will call cdio_init, if that hasn't been + done previously. + + NULL is returned on error. +*/ +CdIo * +cdio_open (const char *orig_source_name, driver_id_t driver_id) +{ + return cdio_open_am(orig_source_name, driver_id, NULL); +} + +/*! Sets up to read from place specified by source_name and + driver_id. This should be called before using any other routine, + except cdio_init. This will call cdio_init, if that hasn't been + done previously. + + NULL is returned on error. +*/ +CdIo * +cdio_open_am (const char *psz_orig_source, driver_id_t driver_id, + const char *psz_access_mode) +{ + char *psz_source; + + if (CdIo_last_driver == -1) cdio_init(); + + if (NULL == psz_orig_source || strlen(psz_orig_source)==0) + psz_source = cdio_get_default_device(NULL); + else + psz_source = strdup(psz_orig_source); + + switch (driver_id) { + case DRIVER_UNKNOWN: + { + CdIo *cdio=scan_for_driver(CDIO_MIN_DRIVER, CDIO_MAX_DRIVER, + psz_source, psz_access_mode); + free(psz_source); + return cdio; + } + case DRIVER_DEVICE: + { + /* Scan for a driver. */ + CdIo *ret = cdio_open_am_cd(psz_source, psz_access_mode); + free(psz_source); + return ret; + } + break; + case DRIVER_BSDI: + case DRIVER_FREEBSD: + case DRIVER_LINUX: + case DRIVER_SOLARIS: + case DRIVER_WIN32: + case DRIVER_OSX: + case DRIVER_NRG: + case DRIVER_BINCUE: + case DRIVER_CDRDAO: + if ((*CdIo_all_drivers[driver_id].have_driver)()) { + CdIo *ret = + (*CdIo_all_drivers[driver_id].driver_open_am)(psz_source, + psz_access_mode); + if (ret) ret->driver_id = driver_id; + free(psz_source); + return ret; + } + } + + free(psz_source); + return NULL; +} + + +/*! + Set up CD-ROM for reading. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no driver for a some sort of hardware CD-ROM. +*/ +CdIo * +cdio_open_cd (const char *psz_source) +{ + return cdio_open_am_cd(psz_source, NULL); +} + +/*! + Set up CD-ROM for reading. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no driver for a some sort of hardware CD-ROM. +*/ +/* In the future we'll have more complicated code to allow selection + of an I/O routine as well as code to find an appropriate default + routine among the "registered" routines. Possibly classes too + disk-based, SCSI-based, native-based, vendor (e.g. Sony, or + Plextor) based + + For now though, we'll start more simply... +*/ +CdIo * +cdio_open_am_cd (const char *psz_source, const char *psz_access_mode) +{ + if (CdIo_last_driver == -1) cdio_init(); + + /* Scan for a driver. */ + return scan_for_driver(CDIO_MIN_DEVICE_DRIVER, CDIO_MAX_DEVICE_DRIVER, + psz_source, psz_access_mode); +} + + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/cdio/Makefile.am b/contrib/libcdio/cdio/Makefile.am new file mode 100644 index 000000000..0910d60e5 --- /dev/null +++ b/contrib/libcdio/cdio/Makefile.am @@ -0,0 +1,19 @@ +include $(top_srcdir)/misc/Makefile.common + +noinst_HEADERS = \ + bytesex.h \ + bytesex_asm.h \ + cdio.h \ + cdtext.h \ + cdtext.h \ + cd_types.h \ + ds.h \ + dvd.h \ + iso9660.h \ + logging.h \ + sector.h \ + scsi_mmc.h \ + types.h \ + util.h \ + version.h \ + xa.h diff --git a/contrib/libcdio/cdio/bytesex.h b/contrib/libcdio/cdio/bytesex.h new file mode 100644 index 000000000..c40e44729 --- /dev/null +++ b/contrib/libcdio/cdio/bytesex.h @@ -0,0 +1,196 @@ +/* + $Id: bytesex.h,v 1.1 2005/01/02 00:51:38 rockyb Exp $ + + Copyright (C) 2000, 2004 Herbert Valerio Riedel <hvr@gnu.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CDIO_BYTESEX_H__ +#define __CDIO_BYTESEX_H__ + +#include <cdio/types.h> +#include <cdio/bytesex_asm.h> +#include <cdio/logging.h> + +/* generic byteswap routines */ + +#define UINT16_SWAP_LE_BE_C(val) ((uint16_t) ( \ + (((uint16_t) (val) & (uint16_t) 0x00ffU) << 8) | \ + (((uint16_t) (val) & (uint16_t) 0xff00U) >> 8))) + +#define UINT32_SWAP_LE_BE_C(val) ((uint32_t) ( \ + (((uint32_t) (val) & (uint32_t) 0x000000ffU) << 24) | \ + (((uint32_t) (val) & (uint32_t) 0x0000ff00U) << 8) | \ + (((uint32_t) (val) & (uint32_t) 0x00ff0000U) >> 8) | \ + (((uint32_t) (val) & (uint32_t) 0xff000000U) >> 24))) + +#define UINT64_SWAP_LE_BE_C(val) ((uint64_t) ( \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x00000000000000ff)) << 56) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x000000000000ff00)) << 40) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x0000000000ff0000)) << 24) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x00000000ff000000)) << 8) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x000000ff00000000)) >> 8) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x0000ff0000000000)) >> 24) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0x00ff000000000000)) >> 40) | \ + (((uint64_t) (val) & (uint64_t) UINT64_C(0xff00000000000000)) >> 56))) + +#ifndef UINT16_SWAP_LE_BE +# define UINT16_SWAP_LE_BE UINT16_SWAP_LE_BE_C +#endif + +#ifndef UINT32_SWAP_LE_BE +# define UINT32_SWAP_LE_BE UINT32_SWAP_LE_BE_C +#endif + +#ifndef UINT64_SWAP_LE_BE +# define UINT64_SWAP_LE_BE UINT64_SWAP_LE_BE_C +#endif + +inline static +uint16_t uint16_swap_le_be (const uint16_t val) +{ + return UINT16_SWAP_LE_BE (val); +} + +inline static +uint32_t uint32_swap_le_be (const uint32_t val) +{ + return UINT32_SWAP_LE_BE (val); +} + +inline static +uint64_t uint64_swap_le_be (const uint64_t val) +{ + return UINT64_SWAP_LE_BE (val); +} + +# define UINT8_TO_BE(val) ((uint8_t) (val)) +# define UINT8_TO_LE(val) ((uint8_t) (val)) +#ifdef WORDS_BIGENDIAN +# define UINT16_TO_BE(val) ((uint16_t) (val)) +# define UINT16_TO_LE(val) ((uint16_t) UINT16_SWAP_LE_BE(val)) + +# define UINT32_TO_BE(val) ((uint32_t) (val)) +# define UINT32_TO_LE(val) ((uint32_t) UINT32_SWAP_LE_BE(val)) + +# define UINT64_TO_BE(val) ((uint64_t) (val)) +# define UINT64_TO_LE(val) ((uint64_t) UINT64_SWAP_LE_BE(val)) +#else +# define UINT16_TO_BE(val) ((uint16_t) UINT16_SWAP_LE_BE(val)) +# define UINT16_TO_LE(val) ((uint16_t) (val)) + +# define UINT32_TO_BE(val) ((uint32_t) UINT32_SWAP_LE_BE(val)) +# define UINT32_TO_LE(val) ((uint32_t) (val)) + +# define UINT64_TO_BE(val) ((uint64_t) UINT64_SWAP_LE_BE(val)) +# define UINT64_TO_LE(val) ((uint64_t) (val)) +#endif + +/* symmetric conversions */ +#define UINT8_FROM_BE(val) (UINT8_TO_BE (val)) +#define UINT8_FROM_LE(val) (UINT8_TO_LE (val)) +#define UINT16_FROM_BE(val) (UINT16_TO_BE (val)) +#define UINT16_FROM_LE(val) (UINT16_TO_LE (val)) +#define UINT32_FROM_BE(val) (UINT32_TO_BE (val)) +#define UINT32_FROM_LE(val) (UINT32_TO_LE (val)) +#define UINT64_FROM_BE(val) (UINT64_TO_BE (val)) +#define UINT64_FROM_LE(val) (UINT64_TO_LE (val)) + +/* converter function template */ +#define CVT_TO_FUNC(bits) \ + static inline uint ## bits ## _t \ + uint ## bits ## _to_be (uint ## bits ## _t val) \ + { return UINT ## bits ## _TO_BE (val); } \ + static inline uint ## bits ## _t \ + uint ## bits ## _to_le (uint ## bits ## _t val) \ + { return UINT ## bits ## _TO_LE (val); } \ + +CVT_TO_FUNC(8) +CVT_TO_FUNC(16) +CVT_TO_FUNC(32) +CVT_TO_FUNC(64) + +#undef CVT_TO_FUNC + +#define uint8_from_be(val) (uint8_to_be (val)) +#define uint8_from_le(val) (uint8_to_le (val)) +#define uint16_from_be(val) (uint16_to_be (val)) +#define uint16_from_le(val) (uint16_to_le (val)) +#define uint32_from_be(val) (uint32_to_be (val)) +#define uint32_from_le(val) (uint32_to_le (val)) +#define uint64_from_be(val) (uint64_to_be (val)) +#define uint64_from_le(val) (uint64_to_le (val)) + +/* ISO9660 related stuff */ + +#define to_711(i) uint8_to_le(i) +#define from_711(i) uint8_from_le(i) + +#define to_721(i) uint16_to_le(i) +#define from_721(i) uint16_from_le(i) + +#define to_721(i) uint16_to_le(i) +#define from_721(i) uint16_from_le(i) + +#define to_722(i) uint16_to_be(i) +#define from_722(i) uint16_from_be(i) + +static inline uint32_t +to_723(uint16_t i) +{ + return uint32_swap_le_be(i) | i; +} + +static inline uint16_t +from_723 (uint32_t p) +{ + if (uint32_swap_le_be (p) != p) + cdio_warn ("from_723: broken byte order"); + + return (0xFFFF & p); +} + +#define to_731(i) uint32_to_le(i) +#define from_731(i) uint32_from_le(i) + +#define to_732(i) uint32_to_be(i) +#define from_732(i) uint32_from_be(i) + +static inline uint64_t +to_733(uint32_t i) +{ + return uint64_swap_le_be(i) | i; +} + +static inline uint32_t +from_733 (uint64_t p) +{ + if (uint64_swap_le_be (p) != p) + cdio_warn ("from_733: broken byte order"); + + return (UINT32_C(0xFFFFFFFF) & p); +} + +#endif /* __CDIO_BYTESEX_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/cdio/bytesex_asm.h b/contrib/libcdio/cdio/bytesex_asm.h new file mode 100644 index 000000000..4291563ec --- /dev/null +++ b/contrib/libcdio/cdio/bytesex_asm.h @@ -0,0 +1,123 @@ +/* + $Id: bytesex_asm.h,v 1.1 2005/01/02 00:51:38 rockyb Exp $ + + Copyright (C) 2001 Sven Ottemann <ac-logic@freenet.de> + 2001, 2004 Herbert Valerio Riedel <hvr@gnu.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CDIO_BYTESEX_ASM_H__ +#define __CDIO_BYTESEX_ASM_H__ +#if !defined(DISABLE_ASM_OPTIMIZE) + +#include <cdio/types.h> + +#if defined(__powerpc__) && defined(__GNUC__) + +inline static +uint32_t uint32_swap_le_be_asm(const uint32_t a) +{ + uint32_t b; + + __asm__ ("lwbrx %0,0,%1" + :"=r"(b) + :"r"(&a), "m"(a)); + + return b; +} + +inline static +uint16_t uint16_swap_le_be_asm(const uint16_t a) +{ + uint32_t b; + + __asm__ ("lhbrx %0,0,%1" + :"=r"(b) + :"r"(&a), "m"(a)); + + return b; +} + +#define UINT16_SWAP_LE_BE uint16_swap_le_be_asm +#define UINT32_SWAP_LE_BE uint32_swap_le_be_asm + +#elif defined(__mc68000__) && defined(__STORMGCC__) + +inline static +uint32_t uint32_swap_le_be_asm(uint32_t a __asm__("d0")) +{ + /* __asm__("rolw #8,%0; swap %0; rolw #8,%0" : "=d" (val) : "0" (val)); */ + + __asm__("move.l %1,d0;rol.w #8,d0;swap d0;rol.w #8,d0;move.l d0,%0" + :"=r"(a) + :"r"(a)); + + return(a); +} + +inline static +uint16_t uint16_swap_le_be_asm(uint16_t a __asm__("d0")) +{ + __asm__("move.l %1,d0;rol.w #8,d0;move.l d0,%0" + :"=r"(a) + :"r"(a)); + + return(a); +} + +#define UINT16_SWAP_LE_BE uint16_swap_le_be_asm +#define UINT32_SWAP_LE_BE uint32_swap_le_be_asm + +#elif 0 && defined(__i386__) && defined(__GNUC__) + +inline static +uint32_t uint32_swap_le_be_asm(uint32_t a) +{ + __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */ + "rorl $16,%0\n\t" /* swap words */ + "xchgb %b0,%h0" /* swap higher bytes */ + :"=q" (a) + : "0" (a)); + + return(a); +} + +inline static +uint16_t uint16_swap_le_be_asm(uint16_t a) +{ + __asm__("xchgb %b0,%h0" /* swap bytes */ + : "=q" (a) + : "0" (a)); + + return(a); +} + +#define UINT16_SWAP_LE_BE uint16_swap_le_be_asm +#define UINT32_SWAP_LE_BE uint32_swap_le_be_asm + +#endif + +#endif /* !defined(DISABLE_ASM_OPTIMIZE) */ +#endif /* __CDIO_BYTESEX_ASM_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/cdio/cd_types.h b/contrib/libcdio/cdio/cd_types.h new file mode 100644 index 000000000..9f4a73f67 --- /dev/null +++ b/contrib/libcdio/cdio/cd_types.h @@ -0,0 +1,155 @@ +/* + $Id: cd_types.h,v 1.3 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2003 Rocky Bernstein <rocky@panix.com> + Copyright (C) 1996,1997,1998 Gerd Knorr <kraxel@bytesex.org> + and Heiko Eißfeldt <heiko@hexco.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** \file cd_types.h + * \brief Header for routines which automatically determine the Compact Disc + * format and possibly filesystem on the CD. + * + */ + +#ifndef __CDIO_CD_TYPES_H__ +#define __CDIO_CD_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Filesystem types we understand. The highest-numbered fs type should + * be less than CDIO_FS_MASK defined below. + */ +#define CDIO_FS_AUDIO 1 /**< audio only - not really a + filesystem */ +#define CDIO_FS_HIGH_SIERRA 2 +#define CDIO_FS_ISO_9660 3 /**< ISO 9660 filesystem */ +#define CDIO_FS_INTERACTIVE 4 +#define CDIO_FS_HFS 5 /**< file system used on the Macintosh + system in MacOS 6 through MacOS 9 + and depricated in OSX. */ +#define CDIO_FS_UFS 6 /**< Generic Unix file system derived + from the Berkeley fast file + system. */ + +/** + * EXT2 was the GNU/Linux native filesystem for early kernels. Newer + * GNU/Linux OS's may use EXT3 which EXT2 with a journal. + */ +#define CDIO_FS_EXT2 7 + +#define CDIO_FS_ISO_HFS 8 /**< both HFS & ISO-9660 filesystem */ +#define CDIO_FS_ISO_9660_INTERACTIVE 9 /**< both CD-RTOS and ISO filesystem */ + + +/** + * The 3DO is, technically, a set of specifications created by the 3DO + * company. These specs are for making a 3DO Interactive Multiplayer + * which uses a CD-player. Panasonic in the early 90's was the first + * company to manufacture and market a 3DO player. + */ +#define CDIO_FS_3DO 10 + +/** + Microsoft X-BOX CD. + */ +#define CDIO_FS_XISO 11 +#define CDIO_FS_UDFX 12 +#define CDIO_FS_UDF 13 +#define CDIO_FS_ISO_UDF 14 + + +#define CDIO_FS_MASK 15 /**< Note: this should be 2**n-1 and + and greater than the highest + CDIO_FS number above */ +#define CDIO_FS_UNKNOWN CDIO_FS_MASK + +/** + * Macro to extract just the FS type portion defined above +*/ +#define CDIO_FSTYPE(fs) (fs & CDIO_FS_MASK) + +/** + * Bit masks for the classes of CD-images. These are generally + * higher-level than the fs-type information above and may be determined + * based of the fs type information. + */ +#define CDIO_FS_ANAL_XA 0x0010 /**< eXtended Architecture format */ +#define CDIO_FS_ANAL_MULTISESSION 0x0020 /**< CD has multisesion */ +#define CDIO_FS_ANAL_PHOTO_CD 0x0040 /**< Is a Kodak Photo CD */ +#define CDIO_FS_ANAL_HIDDEN_TRACK 0x0080 /**< Hidden track at the + beginning of the CD */ +#define CDIO_FS_ANAL_CDTV 0x0100 +#define CDIO_FS_ANAL_BOOTABLE 0x0200 /**< CD is bootable */ +#define CDIO_FS_ANAL_VIDEOCD 0x0400 /**< VCD 1.1 */ +#define CDIO_FS_ANAL_ROCKRIDGE 0x0800 /**< Has Rock Ridge Extensions to + ISO 9660 */ +#define CDIO_FS_ANAL_JOLIET 0x1000 /**< Microsoft Joliet extensions + to ISO 9660 */ +#define CDIO_FS_ANAL_SVCD 0x2000 /**< Super VCD or Choiji Video CD */ +#define CDIO_FS_ANAL_CVD 0x4000 /**< Choiji Video CD */ +#define CDIO_FS_ANAL_XISO 0x8000 /**< XBOX CD */ + +/** + * Pattern which can be used by cdio_get_devices to specify matching + * any sort of CD. + */ +#define CDIO_FS_MATCH_ALL (cdio_fs_anal_t) (~CDIO_FS_MASK) + + +/*! + \brief The type used to return analysis information from + cdio_guess_cd_type. + + These fields make sense only for when an ISO-9660 filesystem is used. + */ +typedef struct +{ + unsigned int joliet_level; /**< If has Joliet extensions, this is the + associated level number (i.e. 1, 2, or 3). */ + char iso_label[33]; /**< This is 32 + 1 for null byte at the end in + formatting the string */ + unsigned int isofs_size; + uint8_t UDFVerMinor; /**< For UDF filesystems only */ + uint8_t UDFVerMajor; /**< For UDF filesystems only */ +} cdio_iso_analysis_t; + +/** + * Try to determine what kind of CD-image and/or filesystem we + * have at track track_num. Return information about the CD image + * is returned in iso_analysis and the return value. + */ +cdio_fs_anal_t cdio_guess_cd_type(const CdIo *cdio, int start_session, + track_t track_num, + /*out*/ cdio_iso_analysis_t *iso_analysis); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_CD_TYPES_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/cdio/cdio.h b/contrib/libcdio/cdio/cdio.h new file mode 100644 index 000000000..f9e2a38e3 --- /dev/null +++ b/contrib/libcdio/cdio/cdio.h @@ -0,0 +1,980 @@ +/* -*- c -*- + $Id: cdio.h,v 1.4 2005/04/27 23:28:42 rockyb Exp $ + + Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** \file cdio.h + * + * \brief The top-level header for libcdio: the CD Input and Control + * library. Applications include this for anything regarding libcdio. + */ + + +#ifndef __CDIO_H__ +#define __CDIO_H__ + +/** Application Interface or Protocol version number. If the public + * interface changes, we increase this number. + */ +#define CDIO_API_VERSION 2 + +#include <cdio/version.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <cdio/types.h> +#include <cdio/sector.h> + +/**! Flags specifying the category of device to open or is opened. */ + +#define CDIO_SRC_IS_DISK_IMAGE_MASK 0x0001 /**< Read source is a CD image. */ +#define CDIO_SRC_IS_DEVICE_MASK 0x0002 /**< Read source is a CD device. */ +#define CDIO_SRC_IS_SCSI_MASK 0x0004 /**< Read source SCSI device. */ +#define CDIO_SRC_IS_NATIVE_MASK 0x0008 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*! Size of fields returned by an INQUIRY command */ +#define CDIO_MMC_HW_VENDOR_LEN 8 /**< length of vendor field */ +#define CDIO_MMC_HW_MODEL_LEN 16 /**< length of model field */ +#define CDIO_MMC_HW_REVISION_LEN 4 /**< length of revision field */ + + /*! \brief Structure to return CD vendor, model, and revision-level + strings obtained via the INQUIRY command */ + typedef struct cdio_hwinfo + { + char psz_vendor [CDIO_MMC_HW_VENDOR_LEN+1]; + char psz_model [CDIO_MMC_HW_MODEL_LEN+1]; + char psz_revision[CDIO_MMC_HW_REVISION_LEN+1]; + } cdio_hwinfo_t; + +/* For compatability. */ +#define CdIo CdIo_t + + /** This is an opaque structure for the CD object. */ + typedef struct _CdIo CdIo_t; + + /** This is an opaque structure for the CD-Text object. */ + typedef struct cdtext cdtext_t; + + /** The driver_id_t enumerations may be used to tag a specific driver + * that is opened or is desired to be opened. Note that this is + * different than what is available on a given host. + * + * Order is a little significant since the order is used in scans. + * We have to start with DRIVER_UNKNOWN and devices should come before + * disk-image readers. By putting something towards the top (a lower + * enumeration number), in an iterative scan we prefer that to + * something with a higher enumeration number. + * + * NOTE: IF YOU MODIFY ENUM MAKE SURE INITIALIZATION IN CDIO.C AGREES. + * + */ + typedef enum { + DRIVER_UNKNOWN, /**< Used as input when we don't care what kind + of driver to use. */ + DRIVER_BSDI, /**< BSDI driver */ + DRIVER_FREEBSD, /**< FreeBSD driver - includes CAM and ioctl access */ + DRIVER_LINUX, /**< GNU/Linux Driver */ + DRIVER_SOLARIS, /**< Sun Solaris Driver */ + DRIVER_OSX, /**< Apple OSX Driver */ + DRIVER_WIN32, /**< Microsoft Windows Driver. Includes ASPI and + ioctl acces. */ + DRIVER_CDRDAO, /**< cdrdao format CD image. This is listed + before BIN/CUE, to make the code prefer cdrdao + over BIN/CUE when both exist. */ + DRIVER_BINCUE, /**< CDRWIN BIN/CUE format CD image. This is + listed before NRG, to make the code prefer + BIN/CUE over NRG when both exist. */ + DRIVER_NRG, /**< Nero NRG format CD image. */ + DRIVER_DEVICE /**< Is really a set of the above; should come last */ + } driver_id_t; + + /** There will generally be only one hardware for a given + build/platform from the list above. You can use the variable + below to determine which you've got. If the build doesn't make an + hardware driver, then the value will be DRIVER_UNKNOWN. + */ + extern const driver_id_t cdio_os_driver; + + +/** Make sure what's listed for CDIO_MIN_DRIVER is the last + enumeration in driver_id_t. Since we have a bogus (but useful) 0th + entry above we don't have to add one. +*/ +#define CDIO_MIN_DRIVER DRIVER_BSDI +#define CDIO_MIN_DEVICE_DRIVER CDIO_MIN_DRIVER +#define CDIO_MAX_DRIVER DRIVER_NRG +#define CDIO_MAX_DEVICE_DRIVER DRIVER_WIN32 + + typedef enum { + TRACK_FORMAT_AUDIO, /**< Audio track, e.g. CD-DA */ + TRACK_FORMAT_CDI, /**< CD-i. How this is different from DATA below? */ + TRACK_FORMAT_XA, /**< Mode2 of some sort */ + TRACK_FORMAT_DATA, /**< Mode1 of some sort */ + TRACK_FORMAT_PSX, /**< Playstation CD. Like audio but only 2336 bytes + * of user data. + */ + TRACK_FORMAT_ERROR /**< Dunno what is, or some other error. */ + } track_format_t; + + extern const char *discmode2str[]; + + /*! Printable tags for track_format_t enumeration. */ + extern const char *track_format2str[6]; + + /*! + Eject media in CD drive if there is a routine to do so. + + @param p_cdio the CD object to be acted upon. + @return 0 if success and 1 for failure, and 2 if no routine. + If the CD is ejected *p_cdio is freed and p_cdio set to NULL. + */ + int cdio_eject_media (CdIo_t **p_cdio); + + /*! + Free any resources associated with p_cdio. Call this when done using p_cdio + and using CD reading/control operations. + + @param p_cdio the CD object to eliminated. + */ + void cdio_destroy (CdIo_t *p_cdio); + + /*! + Free device list returned by cdio_get_devices or + cdio_get_devices_with_cap. + + @param device_list list returned by cdio_get_devices or + cdio_get_devices_with_cap + + @see cdio_get_devices, cdio_get_devices_with_cap + + */ + void cdio_free_device_list (char * device_list[]); + + /*! + Get the value associatied with key. + + @param p_cdio the CD object queried + @param key the key to retrieve + @return the value associatd with "key" or NULL if p_cdio is NULL + or "key" does not exist. + */ + const char * cdio_get_arg (const CdIo_t *p_cdio, const char key[]); + + /*! + Get CD-Text information for a CdIo object. + + @param p_cdio the CD object that may contain CD-Text information. + @param i_track track for which we are requesting CD-Text information. + @return the CD-Text object or NULL if obj is NULL + or CD-Text information does not exist. + + If i_track is 0 or CDIO_CDROM_LEADOUT_TRACK the track returned + is the information assocated with the CD. + */ + const cdtext_t *cdio_get_cdtext (CdIo_t *p_cdio, track_t i_track); + + /*! + Get the default CD device. + if p_cdio is NULL (we haven't initialized a specific device driver), + then find a suitable one and return the default device for that. + + @param p_cdio the CD object queried + @return a string containing the default CD device or NULL is + if we couldn't get a default device. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + char * cdio_get_default_device (const CdIo_t *p_cdio); + + /*! Return an array of device names. If you want a specific + devices for a driver, give that device. If you want hardware + devices, give DRIVER_DEVICE and if you want all possible devices, + image drivers and hardware drivers give DRIVER_UNKNOWN. + + NULL is returned if we couldn't return a list of devices. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + char ** cdio_get_devices (driver_id_t driver_id); + + /*! + Get an array of device names in search_devices that have at least + the capabilities listed by the capabities parameter. If + search_devices is NULL, then we'll search all possible CD drives. + + If "b_any" is set false then every capability listed in the + extended portion of capabilities (i.e. not the basic filesystem) + must be satisified. If "any" is set true, then if any of the + capabilities matches, we call that a success. + + To find a CD-drive of any type, use the mask CDIO_FS_MATCH_ALL. + + @return the array of device names or NULL if we couldn't get a + default device. It is also possible to return a non NULL but + after dereferencing the the value is NULL. This also means nothing + was found. + */ + char ** cdio_get_devices_with_cap (char* ppsz_search_devices[], + cdio_fs_anal_t capabilities, bool b_any); + + /*! + Like cdio_get_devices_with_cap but we return the driver we found + as well. This is because often one wants to search for kind of drive + and then *open* it afterwards. Giving the driver back facilitates this, + and speeds things up for libcdio as well. + */ + char ** cdio_get_devices_with_cap_ret (/*out*/ char* ppsz_search_devices[], + cdio_fs_anal_t capabilities, + bool b_any, + /*out*/ driver_id_t *p_driver_id); + + /*! Like cdio_get_devices, but we may change the p_driver_id if we + were given DRIVER_DEVICE or DRIVER_UNKNOWN. This is because + often one wants to get a drive name and then *open* it + afterwards. Giving the driver back facilitates this, and speeds + things up for libcdio as well. + */ + + char ** cdio_get_devices_ret (/*in/out*/ driver_id_t *p_driver_id); + + /*! + Get disc mode - the kind of CD (CD-DA, CD-ROM mode 1, CD-MIXED, etc. + that we've got. The notion of "CD" is extended a little to include + DVD's. + */ + discmode_t cdio_get_discmode (CdIo_t *p_cdio); + + /*! + Get the what kind of device we've got. + + @param p_cdio the CD object queried + @param p_read_cap pointer to return read capabilities + @param p_write_cap pointer to return write capabilities + @param p_misc_cap pointer to return miscellaneous other capabilities + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + void cdio_get_drive_cap (const CdIo_t *p_cdio, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap); + + /*! + Get the drive capabilities for a specified device. + + @return a list of device capabilities. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + void cdio_get_drive_cap_dev (const char *device, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap); + + /*! + Get a string containing the name of the driver in use. + + @return a string with driver name or NULL if CdIo is NULL (we + haven't initialized a specific device. + */ + const char * cdio_get_driver_name (const CdIo_t *p_cdio); + + /*! + Get the driver id. + if CdIo is NULL (we haven't initialized a specific device driver), + then return DRIVER_UNKNOWN. + + @return the driver id.. + */ + driver_id_t cdio_get_driver_id (const CdIo_t *p_cdio); + + /*! + Get the number of the first track. + + @return the track number or CDIO_INVALID_TRACK + on error. + */ + track_t cdio_get_first_track_num(const CdIo_t *p_cdio); + + /*! + Get the CD-ROM hardware info via a SCSI MMC INQUIRY command. + False is returned if we had an error getting the information. + */ + bool cdio_get_hwinfo ( const CdIo_t *p_cdio, + /* out*/ cdio_hwinfo_t *p_hw_info ); + + + /*! + Return the Joliet level recognized for p_cdio. + */ + uint8_t cdio_get_joliet_level(const CdIo_t *p_cdio); + + /*! + Get the media catalog number (MCN) from the CD. + + @return the media catalog number r NULL if there is none or we + don't have the ability to get it. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. + + */ + char * cdio_get_mcn (const CdIo_t *p_cdio); + + /*! + Get the number of tracks on the CD. + + @return the number of tracks, or CDIO_INVALID_TRACK if there is + an error. + */ + track_t cdio_get_num_tracks (const CdIo_t *p_cdio); + + /*! + Get the format (audio, mode2, mode1) of track. + */ + track_format_t cdio_get_track_format(const CdIo_t *p_cdio, track_t i_track); + + /*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? + */ + bool cdio_get_track_green(const CdIo_t *p_cdio, track_t i_track); + + /*! + Get the starting LBA for track number + i_track in p_cdio. Track numbers usually start at something + greater than 0, usually 1. + + The "leadout" track is specified either by + using i_track CDIO_CDROM_LEADOUT_TRACK or the total tracks+1. + + @param p_cdio object to get information from + @param i_track the track number we want the LSN for + @return the starting LBA or CDIO_INVALID_LBA on error. + */ + lba_t cdio_get_track_lba(const CdIo_t *p_cdio, track_t i_track); + + /*! + Return the starting MSF (minutes/secs/frames) for track number + i_track in p_cdio. Track numbers usually start at something + greater than 0, usually 1. + + The "leadout" track is specified either by + using i_track CDIO_CDROM_LEADOUT_TRACK or the total tracks+1. + + @param p_cdio object to get information from + @param i_track the track number we want the LSN for + @return the starting LSN or CDIO_INVALID_LSN on error. + */ + lsn_t cdio_get_track_lsn(const CdIo_t *p_cdio, track_t i_track); + + /*! + Return the starting MSF (minutes/secs/frames) for track number + i_track in p_cdio. Track numbers usually start at something + greater than 0, usually 1. + + The "leadout" track is specified either by + using i_track CDIO_CDROM_LEADOUT_TRACK or the total tracks+1. + + @return true if things worked or false if there is no track entry. + */ + bool cdio_get_track_msf(const CdIo_t *p_cdio, track_t i_track, + /*out*/ msf_t *msf); + + /*! + Get the number of sectors between this track an the next. This + includes any pregap sectors before the start of the next track. + Track numbers usually start at something + greater than 0, usually 1. + + @return the number of sectors or 0 if there is an error. + */ + unsigned int cdio_get_track_sec_count(const CdIo_t *p_cdio, track_t i_track); + + /*! + Reposition read offset + Similar to (if not the same as) libc's lseek() + + @param p_cdio object to get information from + @param offset amount to seek + @param whence like corresponding parameter in libc's lseek, e.g. + SEEK_SET or SEEK_END. + @return (off_t) -1 on error. + */ + off_t cdio_lseek(const CdIo_t *p_cdio, off_t offset, int whence); + + /*! + Reads into buf the next size bytes. + Similar to (if not the same as) libc's read() + + @return (ssize_t) -1 on error. + */ + ssize_t cdio_read(const CdIo_t *p_cdio, void *buf, size_t size); + + /*! + Read an audio sector + + @param p_cdio object to read from + @param buf place to read data into + @param lsn sector to read + + @return 0 if no error, nonzero otherwise. + */ + int cdio_read_audio_sector (const CdIo_t *p_cdio, void *buf, lsn_t lsn); + + /*! + Reads audio sectors + + @param p_cdio object to read from + @param buf place to read data into + @param lsn sector to read + @param i_sectors number of sectors to read + + @return 0 if no error, nonzero otherwise. + */ + int cdio_read_audio_sectors (const CdIo_t *p_cdio, void *buf, lsn_t lsn, + unsigned int i_sectors); + + /*! + Reads a mode1 sector + + @param p_cdio object to read from + @param buf place to read data into + @param lsn sector to read + @param b_form2 true for reading mode1 form2 sectors or false for + mode1 form1 sectors. + + @return 0 if no error, nonzero otherwise. + */ + int cdio_read_mode1_sector (const CdIo_t *p_cdio, void *buf, lsn_t lsn, + bool b_form2); + + /*! + Reads mode1 sectors + + @param p_cdio object to read from + @param buf place to read data into + @param lsn sector to read + @param b_form2 true for reading mode1 form2 sectors or false for + mode1 form1 sectors. + @param i_sectors number of sectors to read + + @return 0 if no error, nonzero otherwise. + */ + int cdio_read_mode1_sectors (const CdIo_t *p_cdio, void *buf, lsn_t lsn, + bool b_form2, unsigned int i_sectors); + + /*! + Reads a mode1 sector + + @param p_cdio object to read from + @param buf place to read data into + @param lsn sector to read + @param b_form2 true for reading mode1 form2 sectors or false for + mode1 form1 sectors. + + @return 0 if no error, nonzero otherwise. + */ + int cdio_read_mode2_sector (const CdIo_t *p_cdio, void *buf, lsn_t lsn, + bool b_form2); + + /*! + Reads mode2 sectors + + @param p_cdio object to read from + @param buf place to read data into + @param lsn sector to read + @param b_form2 true for reading mode1 form2 sectors or false for + mode1 form1 sectors. + @param i_sectors number of sectors to read + + @return 0 if no error, nonzero otherwise. + */ + int cdio_read_mode2_sectors (const CdIo_t *p_cdio, void *buf, lsn_t lsn, + bool b_form2, unsigned int i_sectors); + + /*! + Set the arg "key" with "value" in "obj". + + @param p_cdio the CD object to set + @param key the key to set + @param value the value to assocaiate with key + @return 0 if no error was found, and nonzero otherwise. + */ + int cdio_set_arg (CdIo_t *p_cdio, const char key[], const char value[]); + + /*! + Get the size of the CD in logical block address (LBA) units. + + @param p_cdio the CD object queried + @return the size + */ + uint32_t cdio_stat_size (const CdIo_t *p_cdio); + + /*! + Initialize CD Reading and control routines. Should be called first. + */ + bool cdio_init(void); + + /* True if xxx driver is available. where xxx=linux, solaris, nrg, ... + */ + + /*! True if BSDI driver is available. */ + bool cdio_have_bsdi (void); + + /*! True if FreeBSD driver is available. */ + bool cdio_have_freebsd (void); + + /*! True if GNU/Linux driver is available. */ + bool cdio_have_linux (void); + + /*! True if Sun Solaris driver is available. */ + bool cdio_have_solaris (void); + + /*! True if Apple OSX driver is available. */ + bool cdio_have_osx (void); + + /*! True if Microsoft Windows driver is available. */ + bool cdio_have_win32 (void); + + /*! True if Nero driver is available. */ + bool cdio_have_nrg (void); + + /*! True if BIN/CUE driver is available. */ + bool cdio_have_bincue (void); + + /*! True if cdrdao CDRDAO driver is available. */ + bool cdio_have_cdrdao (void); + + /*! Like cdio_have_xxx but uses an enumeration instead. */ + bool cdio_have_driver (driver_id_t driver_id); + + /*! + Get a string decribing driver_id. + + @param driver_id the driver you want the description for + @return a sring of driver description + */ + const char *cdio_driver_describe (driver_id_t driver_id); + + /*! Sets up to read from place specified by source_name and + driver_id. This or cdio_open_* should be called before using any + other routine, except cdio_init. This will call cdio_init, if + that hasn't been done previously. to call one of the specific + cdio_open_xxx routines. + + @return the cdio object or NULL on error or no device. + */ + CdIo_t * cdio_open (const char *source_name, driver_id_t driver_id); + + /*! Sets up to read from place specified by source_name, driver_id + and access mode. This or cdio_open should be called before using + any other routine, except cdio_init. This will call cdio_init, if + that hasn't been done previously. to call one of the specific + cdio_open_xxx routines. + + @return the cdio object or NULL on error or no device. + */ + CdIo_t * cdio_open_am (const char *psz_source_name, + driver_id_t driver_id, const char *psz_access_mode); + + /*! Set up BIN/CUE CD disk-image for reading. Source is the .bin or + .cue file + + @return the cdio object or NULL on error or no device. + */ + CdIo_t * cdio_open_bincue (const char *psz_cue_name); + + /*! Set up BIN/CUE CD disk-image for reading. Source is the .bin or + .cue file + + @return the cdio object or NULL on error or no device.. + */ + CdIo_t * cdio_open_am_bincue (const char *psz_cue_name, + const char *psz_access_mode); + + /*! Set up cdrdao CD disk-image for reading. Source is the .toc file + + @return the cdio object or NULL on error or no device. + */ + CdIo_t * cdio_open_cdrdao (const char *psz_toc_name); + + /*! Set up cdrdao CD disk-image for reading. Source is the .toc file + + @return the cdio object or NULL on error or no device.. + */ + CdIo_t * cdio_open_am_cdrdao (const char *psz_toc_name, + const char *psz_access_mode); + + /*! Return a string containing the default CUE file that would + be used when none is specified. + + @return the cdio object or NULL on error or no device. + */ + char * cdio_get_default_device_bincue(void); + + char **cdio_get_devices_bincue(void); + + /*! Return a string containing the default CUE file that would + be used when none is specified. + + NULL is returned on error or there is no device. + */ + char * cdio_get_default_device_cdrdao(void); + + char **cdio_get_devices_cdrdao(void); + + /*! Set up CD-ROM for reading. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no driver for a some sort of hardware CD-ROM. + */ + CdIo_t * cdio_open_cd (const char *device_name); + + /*! Set up CD-ROM for reading. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no driver for a some sort of hardware CD-ROM. + */ + CdIo_t * cdio_open_am_cd (const char *psz_device, + const char *psz_access_mode); + + /*! CDRWIN BIN/CUE CD disc-image routines. Source is the .cue file + + @return the cdio object for subsequent operations. + NULL on error. + */ + CdIo_t * cdio_open_cue (const char *cue_name); + + /*! Set up CD-ROM for reading using the BSDI driver. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no BSDI driver. + + @see cdio_open + */ + CdIo_t * cdio_open_bsdi (const char *psz_source_name); + + /*! Set up CD-ROM for reading using the BSDI driver. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no BSDI driver. + + @see cdio_open + */ + CdIo_t * cdio_open_am_bsdi (const char *psz_source_name, + const char *psz_access_mode); + + /*! Return a string containing the default device name that the + BSDI driver would use when none is specified. + + @return the cdio object for subsequent operations. + NULL on error or there is no BSDI driver. + + @see cdio_open_cd, cdio_open + */ + char * cdio_get_default_device_bsdi(void); + + /*! Return a list of all of the CD-ROM devices that the BSDI driver + can find. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + char **cdio_get_devices_bsdi(void); + + /*! Set up CD-ROM for reading using the FreeBSD driver. The device_name is + the some sort of device name. + + NULL is returned on error or there is no FreeBSD driver. + + @see cdio_open_cd, cdio_open + */ + CdIo_t * cdio_open_freebsd (const char *paz_source_name); + + /*! Set up CD-ROM for reading using the FreeBSD driver. The device_name is + the some sort of device name. + + NULL is returned on error or there is no FreeBSD driver. + + @see cdio_open_cd, cdio_open + */ + CdIo_t * cdio_open_am_freebsd (const char *psz_source_name, + const char *psz_access_mode); + + /*! Return a string containing the default device name that the + FreeBSD driver would use when none is specified. + + NULL is returned on error or there is no CD-ROM device. + */ + char * cdio_get_default_device_freebsd(void); + + /*! Return a list of all of the CD-ROM devices that the FreeBSD driver + can find. + */ + char **cdio_get_devices_freebsd(void); + + /*! Set up CD-ROM for reading using the GNU/Linux driver. The device_name is + the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no GNU/Linux driver. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + CdIo_t * cdio_open_linux (const char *source_name); + + /*! Set up CD-ROM for reading using the GNU/Linux driver. The + device_name is the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no GNU/Linux driver. + */ + CdIo_t * cdio_open_am_linux (const char *source_name, + const char *access_mode); + + /*! Return a string containing the default device name that the + GNU/Linux driver would use when none is specified. A scan is made + for CD-ROM drives with CDs in them. + + NULL is returned on error or there is no CD-ROM device. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + + @see cdio_open_cd, cdio_open + */ + char * cdio_get_default_device_linux(void); + + /*! Return a list of all of the CD-ROM devices that the GNU/Linux driver + can find. + */ + char **cdio_get_devices_linux(void); + + /*! Set up CD-ROM for reading using the Sun Solaris driver. The + device_name is the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no Solaris driver. + */ + CdIo_t * cdio_open_solaris (const char *source_name); + + /*! Set up CD-ROM for reading using the Sun Solaris driver. The + device_name is the some sort of device name. + + @return the cdio object for subsequent operations. + NULL on error or there is no Solaris driver. + */ + CdIo_t * cdio_open_am_solaris (const char *psz_source_name, + const char *psz_access_mode); + + /*! Return a string containing the default device name that the + Solaris driver would use when none is specified. A scan is made + for CD-ROM drives with CDs in them. + + NULL is returned on error or there is no CD-ROM device. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + + @see cdio_open_cd, cdio_open + */ + char * cdio_get_default_device_solaris(void); + + /*! Return a list of all of the CD-ROM devices that the Solaris driver + can find. + */ + char **cdio_get_devices_solaris(void); + + /*! Set up CD-ROM for reading using the Apple OSX driver. The + device_name is the some sort of device name. + + NULL is returned on error or there is no OSX driver. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + + @see cdio_open_cd, cdio_open + */ + CdIo_t * cdio_open_osx (const char *psz_source_name); + + /*! Set up CD-ROM for reading using the Apple OSX driver. The + device_name is the some sort of device name. + + NULL is returned on error or there is no OSX driver. + + @see cdio_open_cd, cdio_open + */ + CdIo_t * cdio_open_am_osx (const char *psz_source_name, + const char *psz_access_mode); + + /*! Return a string containing the default device name that the + OSX driver would use when none is specified. A scan is made + for CD-ROM drives with CDs in them. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + char * cdio_get_default_device_osx(void); + + /*! Return a list of all of the CD-ROM devices that the OSX driver + can find. + */ + char **cdio_get_devices_osx(void); + + /*! Set up CD-ROM for reading using the Microsoft Windows driver. The + device_name is the some sort of device name. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + */ + CdIo_t * cdio_open_win32 (const char *source_name); + + /*! Set up CD-ROM for reading using the Microsoft Windows driver. The + device_name is the some sort of device name. + + NULL is returned on error or there is no Microsof Windows driver. + */ + CdIo_t * cdio_open_am_win32 (const char *psz_source_name, + const char *psz_access_mode); + + /*! Return a string containing the default device name that the + Win32 driver would use when none is specified. A scan is made + for CD-ROM drives with CDs in them. + + In some situations of drivers or OS's we can't find a CD device if + there is no media in it and it is possible for this routine to return + NULL even though there may be a hardware CD-ROM. + + @see cdio_open_cd, cdio_open + */ + char * cdio_get_default_device_win32(void); + + char **cdio_get_devices_win32(void); + + /*! Set up CD-ROM for reading using the Nero driver. The + device_name is the some sort of device name. + + @return true on success; NULL on error or there is no Nero driver. + */ + CdIo_t * cdio_open_nrg (const char *source_name); + + /*! Set up CD-ROM for reading using the Nero driver. The + device_name is the some sort of device name. + + @return true on success; NULL on error or there is no Nero driver. + */ + CdIo_t * cdio_open_am_nrg (const char *psz_source_name, + const char *psz_access_mode); + + /*! Return a string containing the default device name that the + NRG driver would use when none is specified. A scan is made + for NRG disk images in the current directory.. + + NULL is returned on error or there is no CD-ROM device. + */ + char * cdio_get_default_device_nrg(void); + + char **cdio_get_devices_nrg(void); + + /*! + + Determine if bin_name is the bin file part of a CDRWIN CD disk image. + + @param bin_name location of presumed CDRWIN bin image file. + @return the corresponding CUE file if bin_name is a BIN file or + NULL if not a BIN file. + */ + char *cdio_is_binfile(const char *bin_name); + + /*! + Determine if cue_name is the cue sheet for a CDRWIN CD disk image. + + @return corresponding BIN file if cue_name is a CDRWIN cue file or + NULL if not a CUE file. + */ + char *cdio_is_cuefile(const char *cue_name); + + /*! + Determine if psg_nrg is a Nero CD disk image. + + @param psz_nrg location of presumed NRG image file. + @return true if psz_nrg is a Nero NRG image or false + if not a NRG image. + */ + bool cdio_is_nrg(const char *psz_nrg); + + /*! + Determine if psg_toc is a TOC file for a cdrdao CD disk image. + + @param psz_toc location of presumed TOC image file. + @return true if toc_name is a cdrdao TOC file or false + if not a TOC file. + */ + bool cdio_is_tocfile(const char *psz_toc); + + /*! + Determine if source_name refers to a real hardware CD-ROM. + + @param source_name location name of object + @param driver_id driver for reading object. Use DRIVER_UNKNOWN if you + don't know what driver to use. + @return true if source_name is a device; If false is returned we + could have a CD disk image. + */ + bool cdio_is_device(const char *source_name, driver_id_t driver_id); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_H__ */ diff --git a/contrib/libcdio/cdio/cdtext.h b/contrib/libcdio/cdio/cdtext.h new file mode 100644 index 000000000..4b397a3ff --- /dev/null +++ b/contrib/libcdio/cdio/cdtext.h @@ -0,0 +1,108 @@ +/* + $Id: cdtext.h,v 1.1 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + adapted from cuetools + Copyright (C) 2003 Svend Sanjay Sorensen <ssorensen@fastmail.fm> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*! + * \file cdtext.h + * \brief Header CD-Text information +*/ + + +#ifndef __CDIO_CDTEXT_H__ +#define __CDIO_CDTEXT_H__ + +#include <cdio/cdio.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MAX_CDTEXT_FIELDS 13 + + /*! \brief structure for holding CD-Text information + + @see cdtext_init, cdtext_destroy, cdtext_get, and cdtext_set. + */ + struct cdtext { + char *field[MAX_CDTEXT_FIELDS]; + }; + + /*! \brief A list of all of the CD-Text fields */ + typedef enum { + CDTEXT_ARRANGER = 0, /**< name(s) of the arranger(s) */ + CDTEXT_COMPOSER = 1, /**< name(s) of the composer(s) */ + CDTEXT_DISCID = 2, /**< disc identification information */ + CDTEXT_GENRE = 3, /**< genre identification and genre information */ + CDTEXT_MESSAGE = 4, /**< ISRC code of each track */ + CDTEXT_ISRC = 5, /**< message(s) from the content provider or artist */ + CDTEXT_PERFORMER = 6, /**< name(s) of the performer(s) */ + CDTEXT_SIZE_INFO = 7, /**< size information of the block */ + CDTEXT_SONGWRITER = 8, /**< name(s) of the songwriter(s) */ + CDTEXT_TITLE = 9, /**< title of album name or track titles */ + CDTEXT_TOC_INFO = 10, /**< table of contents information */ + CDTEXT_TOC_INFO2 = 11, /**< second table of contents information */ + CDTEXT_UPC_EAN = 12, + CDTEXT_INVALID = MAX_CDTEXT_FIELDS + } cdtext_field_t; + + /*! Return string representation of the enum values above */ + const char *cdtext_field2str (cdtext_field_t i); + + /*! Initialize a new cdtext structure. + When the structure is no longer needed, release the + resources using cdtext_delete. + */ + void cdtext_init (cdtext_t *cdtext); + + /*! Free memory assocated with cdtext*/ + void cdtext_destroy (cdtext_t *cdtext); + + /*! returns the string associated with the given field. NULL is + returned if key is CDTEXT_INVALID or the field is not set. + + @see cdio_get_cdtext to retrieve the cdtext structure used as + input here. + */ + const char *cdtext_get (cdtext_field_t key, const cdtext_t *cdtext); + + /*! + returns enum of keyword if key is a CD-Text keyword, + returns MAX_CDTEXT_FIELDS non-zero otherwise. + */ + cdtext_field_t cdtext_is_keyword (const char *key); + + /*! + sets cdtext's keyword entry to field + */ + void cdtext_set (cdtext_field_t key, const char *value, cdtext_t *cdtext); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_CDTEXT_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/cdio/ds.h b/contrib/libcdio/cdio/ds.h new file mode 100644 index 000000000..c811cadd4 --- /dev/null +++ b/contrib/libcdio/cdio/ds.h @@ -0,0 +1,73 @@ +/* + $Id: ds.h,v 1.1 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CDIO_DS_H__ +#define __CDIO_DS_H__ + +#include <cdio/types.h> + +/* opaque... */ +typedef struct _CdioList CdioList; +typedef struct _CdioListNode CdioListNode; + +typedef int (*_cdio_list_cmp_func) (void *data1, void *data2); + +typedef int (*_cdio_list_iterfunc) (void *data, void *user_data); + +/* methods */ +CdioList *_cdio_list_new (void); + +void _cdio_list_free (CdioList *list, int free_data); + +unsigned _cdio_list_length (const CdioList *list); + +void _cdio_list_prepend (CdioList *list, void *data); + +void _cdio_list_append (CdioList *list, void *data); + +void _cdio_list_foreach (CdioList *list, _cdio_list_iterfunc func, void *user_data); + +CdioListNode *_cdio_list_find (CdioList *list, _cdio_list_iterfunc cmp_func, void *user_data); + +#define _CDIO_LIST_FOREACH(node, list) \ + for (node = _cdio_list_begin (list); node; node = _cdio_list_node_next (node)) + +/* node ops */ + +CdioListNode *_cdio_list_begin (const CdioList *list); + +CdioListNode *_cdio_list_end (CdioList *list); + +CdioListNode *_cdio_list_node_next (CdioListNode *node); + +void _cdio_list_node_free (CdioListNode *node, int free_data); + +void *_cdio_list_node_data (CdioListNode *node); + +#endif /* __CDIO_DS_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ + diff --git a/contrib/libcdio/cdio/dvd.h b/contrib/libcdio/cdio/dvd.h new file mode 100644 index 000000000..df58c4322 --- /dev/null +++ b/contrib/libcdio/cdio/dvd.h @@ -0,0 +1,113 @@ +/* + $Id: dvd.h,v 1.1 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + Modeled after GNU/Linux definitions in linux/cdrom.h + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*! + \file dvd.h + \brief Definitions for DVD access. +*/ + +#ifndef __CDIO_DVD_H__ +#define __CDIO_DVD_H__ + +#include <cdio/types.h> + +/*! Values used in a READ DVD STRUCTURE */ + +#define CDIO_DVD_STRUCT_PHYSICAL 0x00 +#define CDIO_DVD_STRUCT_COPYRIGHT 0x01 +#define CDIO_DVD_STRUCT_DISCKEY 0x02 +#define CDIO_DVD_STRUCT_BCA 0x03 +#define CDIO_DVD_STRUCT_MANUFACT 0x04 + +/*! Media definitions for "Book Type" */ +#define CDIO_DVD_BOOK_DVD_ROM 0 +#define CDIO_DVD_BOOK_DVD_RAM 1 +#define CDIO_DVD_BOOK_DVD_R 2 /**< DVD-R */ +#define CDIO_DVD_BOOK_DVD_RW 3 /**< DVD-RW */ +#define CDIO_DVD_BOOK_DVD_PR 8 /**< DVD+R */ +#define CDIO_DVD_BOOK_DVD_PRW 9 /**< DVD+RW */ + +typedef struct cdio_dvd_layer { + uint8_t book_version : 4; + uint8_t book_type : 4; + uint8_t min_rate : 4; + uint8_t disc_size : 4; + uint8_t layer_type : 4; + uint8_t track_path : 1; + uint8_t nlayers : 2; + uint8_t track_density : 4; + uint8_t linear_density: 4; + uint8_t bca : 1; + uint32_t start_sector; + uint32_t end_sector; + uint32_t end_sector_l0; +} cdio_dvd_layer_t; + +/*! Maximum number of layers in a DVD. */ +#define CDIO_DVD_MAX_LAYERS 4 + +typedef struct cdio_dvd_physical { + uint8_t type; + uint8_t layer_num; + cdio_dvd_layer_t layer[CDIO_DVD_MAX_LAYERS]; +} cdio_dvd_physical_t; + +typedef struct cdio_dvd_copyright { + uint8_t type; + + uint8_t layer_num; + uint8_t cpst; + uint8_t rmi; +} cdio_dvd_copyright_t; + +typedef struct cdio_dvd_disckey { + uint8_t type; + + unsigned agid : 2; + uint8_t value[2048]; +} cdio_dvd_disckey_t; + +typedef struct cdio_dvd_bca { + uint8_t type; + + int len; + uint8_t value[188]; +} cdio_dvd_bca_t; + +typedef struct cdio_dvd_manufact { + uint8_t type; + + uint8_t layer_num; + int len; + uint8_t value[2048]; +} cdio_dvd_manufact_t; + +typedef union { + uint8_t type; + + cdio_dvd_physical_t physical; + cdio_dvd_copyright_t copyright; + cdio_dvd_disckey_t disckey; + cdio_dvd_bca_t bca; + cdio_dvd_manufact_t manufact; +} cdio_dvd_struct_t; + +#endif /* __SCSI_MMC_H__ */ diff --git a/contrib/libcdio/cdio/iso9660.h b/contrib/libcdio/cdio/iso9660.h new file mode 100644 index 000000000..104b6ba62 --- /dev/null +++ b/contrib/libcdio/cdio/iso9660.h @@ -0,0 +1,786 @@ +/* + $Id: iso9660.h,v 1.3 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + See also iso9660.h by Eric Youngdale (1993). + + Copyright 1993 Yggdrasil Computing, Incorporated + Copyright (c) 1999,2000 J. Schilling + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*! + * \file iso9660.h + * \brief Header for libiso9660: the ISO-9660 filesystem library. +*/ + + +#ifndef __CDIO_ISO9660_H__ +#define __CDIO_ISO9660_H__ + +#include <cdio/cdio.h> +#include <cdio/ds.h> +#include <cdio/xa.h> + +#include <time.h> + +#define _delta(from, to) ((to) - (from) + 1) + +#define MIN_TRACK_SIZE 4*75 +#define MIN_ISO_SIZE MIN_TRACK_SIZE + +/*! + An ISO filename is: "abcde.eee;1" -> <filename> '.' <ext> ';' <version #> + + For ISO-9660 Level 1, the maximum needed string length is: + +\verbatim + 30 chars (filename + ext) + + 2 chars ('.' + ';') + + 5 chars (strlen("32767")) + + 1 null byte + ================================ + = 38 chars +\endverbatim +*/ + +/*! size in bytes of the filename portion + null byte */ +#define LEN_ISONAME 31 + +/*! Max # characters in the entire ISO 9660 filename. */ +#define MAX_ISONAME 37 + +/*! Max # characters in the entire ISO 9660 filename. */ +#define MAX_ISOPATHNAME 255 + +/*! Max # characters in an perparer id. */ +#define ISO_MAX_PREPARER_ID 128 + +/*! Max # characters in an publisher id. */ +#define ISO_MAX_PUBLISHER_ID 128 + +/*! Max # characters in an application id. */ +#define ISO_MAX_APPLICATION_ID 128 + +/*! Max # characters in an system id. */ +#define ISO_MAX_SYSTEM_ID 32 + +/*! Max # characters in an volume id. */ +#define ISO_MAX_VOLUME_ID 32 + +/*! Max # characters in an volume-set id. */ +#define ISO_MAX_VOLUMESET_ID 128 + +/**! ISO 9660 directory flags. */ +#define ISO_FILE 0 /**< Not really a flag... */ +#define ISO_EXISTENCE 1 /**< Do not make existence known (hidden) */ +#define ISO_DIRECTORY 2 /**< This file is a directory */ +#define ISO_ASSOCIATED 4 /**< This file is an associated file */ +#define ISO_RECORD 8 /**< Record format in extended attr. != 0 */ +#define ISO_PROTECTION 16 /**< No read/execute perm. in ext. attr. */ +#define ISO_DRESERVED1 32 /**< Reserved bit 5 */ +#define ISO_DRESERVED2 64 /**< Reserved bit 6 */ +#define ISO_MULTIEXTENT 128 /**< Not final entry of a mult. ext. file */ + +/**! Volume descriptor types */ +#define ISO_VD_PRIMARY 1 +#define ISO_VD_SUPPLEMENTARY 2 /**< Used by Joliet */ +#define ISO_VD_END 255 + +/*! Sector of Primary Volume Descriptor */ +#define ISO_PVD_SECTOR 16 + +/*! Sector of End Volume Descriptor */ +#define ISO_EVD_SECTOR 17 + +/*! String inside track identifying an ISO 9660 filesystem. */ +#define ISO_STANDARD_ID "CD001" + + +/*! Number of bytes in an ISO 9660 block */ +#define ISO_BLOCKSIZE 2048 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +enum strncpy_pad_check { + ISO9660_NOCHECK = 0, + ISO9660_7BIT, + ISO9660_ACHARS, + ISO9660_DCHARS +}; + +#ifndef EMPTY_ARRAY_SIZE +#define EMPTY_ARRAY_SIZE 0 +#endif + +PRAGMA_BEGIN_PACKED + +/*! + \brief ISO-9660 shorter-format time structure. + + @see iso9660_dtime + */ +struct iso9660_dtime { + uint8_t dt_year; + uint8_t dt_month; /**< Has value in range 1..12. Note starts + at 1, not 0 like a tm struct. */ + uint8_t dt_day; + uint8_t dt_hour; + uint8_t dt_minute; + uint8_t dt_second; + int8_t dt_gmtoff; /**< GMT values -48 .. + 52 in 15 minute + intervals */ +} GNUC_PACKED; + +typedef struct iso9660_dtime iso9660_dtime_t; + +/*! + \brief ISO-9660 longer-format time structure. + + @see iso9660_ltime + */ +struct iso9660_ltime { + char lt_year [_delta( 1, 4)]; /**< Add 1900 to value + for the Julian + year */ + char lt_month [_delta( 5, 6)]; /**< Has value in range + 1..12. Note starts + at 1, not 0 like a + tm struct. */ + char lt_day [_delta( 7, 8)]; + char lt_hour [_delta( 9, 10)]; + char lt_minute [_delta( 11, 12)]; + char lt_second [_delta( 13, 14)]; + char lt_hsecond [_delta( 15, 16)]; /**<! The value is in + units of 1/100's of + a second */ + int8_t lt_gmtoff [_delta( 17, 17)]; +} GNUC_PACKED; + +typedef struct iso9660_ltime iso9660_ltime_t; + +/*! \brief Format of an ISO-9660 directory record + + This structure may have an odd length depending on how many + characters there are in the filename! Some compilers (e.g. on + Sun3/mc68020) pad the structures to an even length. For this reason, + we cannot use sizeof (struct iso_path_table) or sizeof (struct + iso_directory_record) to compute on disk sizes. Instead, we use + offsetof(..., name) and add the name size. See mkisofs.h of the + cdrtools package. + + @see iso9660_stat +*/ +struct iso9660_dir { + uint8_t length; /*! 711 encoded */ + uint8_t xa_length; /*! 711 encoded */ + uint64_t extent; /*! 733 encoded */ + uint64_t size; /*! 733 encoded */ + iso9660_dtime_t recording_time; /*! 7 711-encoded units */ + uint8_t file_flags; + uint8_t file_unit_size; /*! 711 encoded */ + uint8_t interleave_gap; /*! 711 encoded */ + uint32_t volume_sequence_number; /*! 723 encoded */ + uint8_t filename_len; /*! 711 encoded */ + char filename[EMPTY_ARRAY_SIZE]; +} GNUC_PACKED; + +typedef struct iso9660_dir iso9660_dir_t; + +/*! + \brief ISO-9660 Primary Volume Descriptor. + */ +struct iso9660_pvd { + uint8_t type; /**< 711 encoded */ + char id[5]; + uint8_t version; /**< 711 encoded */ + char unused1[1]; + char system_id[ISO_MAX_SYSTEM_ID]; /**< each char is an achar */ + char volume_id[ISO_MAX_VOLUME_ID]; /**< each char is a dchar */ + char unused2[8]; + uint64_t volume_space_size; /**< 733 encoded */ + char unused3[32]; + uint32_t volume_set_size; /**< 723 encoded */ + uint32_t volume_sequence_number; /**< 723 encoded */ + uint32_t logical_block_size; /**< 723 encoded */ + uint64_t path_table_size; /**< 733 encoded */ + uint32_t type_l_path_table; /**< 731 encoded */ + uint32_t opt_type_l_path_table; /**< 731 encoded */ + uint32_t type_m_path_table; /**< 732 encoded */ + uint32_t opt_type_m_path_table; /**< 732 encoded */ + iso9660_dir_t root_directory_record; /**< See section 9.1 of + ISO 9660 spec. */ + char root_directory_filename; /**< Is \0 */ + char volume_set_id[ISO_MAX_VOLUMESET_ID]; /**< dchars */ + char publisher_id[ISO_MAX_PUBLISHER_ID]; /**< achars */ + char preparer_id[ISO_MAX_PREPARER_ID]; /**< achars */ + char application_id[ISO_MAX_APPLICATION_ID]; /**< achars */ + char copyright_file_id[37]; /**< See section 7.5 of + ISO 9660 spec. Each char is + a dchar */ + char abstract_file_id[37]; /**< See section 7.5 of + ISO 9660 spec. Each char is + a dchar */ + char bibliographic_file_id[37]; /**< See section 7.5 of + ISO 9660 spec. Each char is + a dchar. */ + iso9660_ltime_t creation_date; /**< See section 8.4.26.1 of + ISO 9660 spec. */ + iso9660_ltime_t modification_date; /**< See section 8.4.26.1 of + ISO 9660 spec. */ + iso9660_ltime_t expiration_date; /**< See section 8.4.26.1 of + ISO 9660 spec. */ + iso9660_ltime_t effective_date; /**< See section 8.4.26.1 of + ISO 9660 spec. */ + uint8_t file_structure_version; /**< 711 encoded */ + char unused4[1]; + char application_data[512]; + char unused5[653]; +} GNUC_PACKED; + +typedef struct iso9660_pvd iso9660_pvd_t; + +/*! + \brief ISO-9660 Supplementary Volume Descriptor. + + This is used for Joliet Extentions and is almost the same as the + the primary descriptor but two unused fields, "unused1" and "unused3 + become "flags and "escape_sequences" respectively. +*/ +struct iso9660_svd { + uint8_t type; /**< 711 encoded */ + char id[5]; + uint8_t version; /**< 711 encoded */ + char flags; /**< 853 */ + char system_id[ISO_MAX_SYSTEM_ID]; /**< each char is an achar */ + char volume_id[ISO_MAX_VOLUME_ID]; /**< each char is a dchar */ + char unused2[8]; + uint64_t volume_space_size; /**< 733 encoded */ + char escape_sequences[32]; /**< 856 */ + uint32_t volume_set_size; /**< 723 encoded */ + uint32_t volume_sequence_number; /**< 723 encoded */ + uint32_t logical_block_size; /**< 723 encoded */ + uint64_t path_table_size; /**< 733 encoded */ + uint32_t type_l_path_table; /**< 731 encoded */ + uint32_t opt_type_l_path_table; /**< 731 encoded */ + uint32_t type_m_path_table; /**< 732 encoded */ + uint32_t opt_type_m_path_table; /**< 732 encoded */ + iso9660_dir_t root_directory_record; /**< See section 9.1 of + ISO 9660 spec. */ + char volume_set_id[ISO_MAX_VOLUMESET_ID]; /**< dchars */ + char publisher_id[ISO_MAX_PUBLISHER_ID]; /**< achars */ + char preparer_id[ISO_MAX_PREPARER_ID]; /**< achars */ + char application_id[ISO_MAX_APPLICATION_ID]; /**< achars */ + char copyright_file_id[37]; /**< See section 7.5 of + ISO 9660 spec. Each char is + a dchar */ + char abstract_file_id[37]; /**< See section 7.5 of + ISO 9660 spec. Each char is + a dchar */ + char bibliographic_file_id[37]; /**< See section 7.5 of + ISO 9660 spec. Each char is + a dchar. */ + iso9660_ltime_t creation_date; /**< See section 8.4.26.1 of + ISO 9660 spec. */ + iso9660_ltime_t modification_date; /**< See section 8.4.26.1 of + ISO 9660 spec. */ + iso9660_ltime_t expiration_date; /**< See section 8.4.26.1 of + ISO 9660 spec. */ + iso9660_ltime_t effective_date; /**< See section 8.4.26.1 of + ISO 9660 spec. */ + uint8_t file_structure_version; /**< 711 encoded */ + char unused4[1]; + char application_data[512]; + char unused5[653]; +} GNUC_PACKED; + +typedef struct iso9660_svd iso9660_svd_t; + +PRAGMA_END_PACKED + +/*! \brief Unix stat-like version of iso9660_dir + + The iso9660_stat structure is not part of the ISO-9660 + specification. We use it for our to communicate information + in a C-library friendly way, e.g struct tm time structures and + a C-style filename string. + + @see iso9660_dir +*/ +struct iso9660_stat { /* big endian!! */ + struct tm tm; /**< time on entry */ + lsn_t lsn; /**< start logical sector number */ + uint32_t size; /**< total size in bytes */ + uint32_t secsize; /**< number of sectors allocated */ + iso9660_xa_t xa; /**< XA attributes */ + enum { _STAT_FILE = 1, _STAT_DIR = 2 } type; + char filename[EMPTY_ARRAY_SIZE]; /**< filename */ +}; + +typedef struct iso9660_stat iso9660_stat_t; + + +/** A mask used in iso9660_ifs_read_vd which allows what kinds + of extensions we allow, eg. Joliet, Rock Ridge, etc. */ +typedef uint8_t iso_extension_mask_t; + +#define ISO_EXTENSION_JOLIET_LEVEL1 0x01 +#define ISO_EXTENSION_JOLIET_LEVEL2 0x02 +#define ISO_EXTENSION_JOLIET_LEVEL3 0x04 +#define ISO_EXTENSION_ROCK_RIDGE 0x08 +#define ISO_EXTENSION_HIGH_SIERRA 0x10 + +#define ISO_EXTENSION_ALL 0xFF +#define ISO_EXTENSION_NONE 0x00 +#define ISO_EXTENSION_JOLIET \ + (ISO_EXTENSION_JOLIET_LEVEL1 | \ + ISO_EXTENSION_JOLIET_LEVEL2 | \ + ISO_EXTENSION_JOLIET_LEVEL3 ) + + +/** This is an opaque structure. */ +typedef struct _iso9660 iso9660_t; + +/*! + Open an ISO 9660 image for reading. Maybe in the future we will have + a mode. NULL is returned on error. +*/ + iso9660_t *iso9660_open (const char *psz_pathname /*flags, mode */); + +/*! + Open an ISO 9660 image for reading allowing various ISO 9660 + extensions. Maybe in the future we will have a mode. NULL is + returned on error. +*/ + iso9660_t *iso9660_open_ext (const char *psz_pathname, + iso_extension_mask_t iso_extension_mask); + +/*! + Close previously opened ISO 9660 image. + True is unconditionally returned. If there was an error false would + be returned. +*/ + bool iso9660_close (iso9660_t * p_iso); + + +/*! + Seek to a position and then read n bytes. Size read is returned. +*/ + long int iso9660_iso_seek_read (const iso9660_t *p_iso, void *ptr, + lsn_t start, long int i_size); + +/*! + Read the Primary Volume Descriptor for a CD. + True is returned if read, and false if there was an error. +*/ + bool iso9660_fs_read_pvd ( const CdIo *p_cdio, + /*out*/ iso9660_pvd_t *p_pvd ); + +/*! + Read the Primary Volume Descriptor for an ISO 9660 image. + True is returned if read, and false if there was an error. +*/ + bool iso9660_ifs_read_pvd (const iso9660_t *p_iso, + /*out*/ iso9660_pvd_t *p_pvd); + +/*! + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume + Descriptor if (Joliet) extensions are acceptable. +*/ + bool iso9660_fs_read_superblock (CdIo *p_cdio, + iso_extension_mask_t iso_extension_mask); + +/*! + Read the Supper block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume + Descriptor if (Joliet) extensions are acceptable. +*/ + bool iso9660_ifs_read_superblock (iso9660_t *p_iso, + iso_extension_mask_t iso_extension_mask); + + +/*==================================================== + Time conversion + ====================================================*/ +/*! + Set time in format used in ISO 9660 directory index record + from a Unix time structure. */ + void iso9660_set_dtime (const struct tm *tm, + /*out*/ iso9660_dtime_t *idr_date); + + +/*! + Set "long" time in format used in ISO 9660 primary volume descriptor + from a Unix time structure. */ + void iso9660_set_ltime (const struct tm *_tm, + /*out*/ iso9660_ltime_t *p_pvd_date); + +/*! + Get Unix time structure from format use in an ISO 9660 directory index + record. Even though tm_wday and tm_yday fields are not explicitly in + idr_date, they are calculated from the other fields. + + If tm is to reflect the localtime, set "use_localtime" true, otherwise + tm will reported in GMT. +*/ + void iso9660_get_dtime (const iso9660_dtime_t *idr_date, bool use_localtime, + /*out*/ struct tm *tm); + + +/*==================================================== + Characters used in file and directory and manipulation + ====================================================*/ +/*! + Return true if c is a DCHAR - a character that can appear in an an + ISO-9600 level 1 directory name. These are the ASCII capital + letters A-Z, the digits 0-9 and an underscore. +*/ +bool iso9660_isdchar (int c); + +/*! + Return true if c is an ACHAR - + These are the DCHAR's plus some ASCII symbols including the space + symbol. +*/ +bool iso9660_isachar (int c); + +/*! + Convert ISO-9660 file name that stored in a directory entry into + what's usually listed as the file name in a listing. + Lowercase name, and remove trailing ;1's or .;1's and + turn the other ;'s into version numbers. + + The length of the translated string is returned. +*/ +int iso9660_name_translate(const char *psz_oldname, char *psz_newname); + +/*! + Convert ISO-9660 file name that stored in a directory entry into + what's usually listed as the file name in a listing. Lowercase + name if not using Joliet extension. Remove trailing ;1's or .;1's and + turn the other ;'s into version numbers. + + The length of the translated string is returned. +*/ +int iso9660_name_translate_ext(const char *old, char *new, + uint8_t i_joliet_level); + +/*! + Pad string src with spaces to size len and copy this to dst. If + len is less than the length of src, dst will be truncated to the + first len characters of src. + + src can also be scanned to see if it contains only ACHARs, DCHARs, + 7-bit ASCII chars depending on the enumeration _check. + + In addition to getting changed, dst is the return value. + Note: this string might not be NULL terminated. + */ +char *iso9660_strncpy_pad(char dst[], const char src[], size_t len, + enum strncpy_pad_check _check); + +/*===================================================================== + file/dirname's +======================================================================*/ + +/*! + Check that pathname is a valid ISO-9660 directory name. + + A valid directory name should not start out with a slash (/), + dot (.) or null byte, should be less than 37 characters long, + have no more than 8 characters in a directory component + which is separated by a /, and consist of only DCHARs. + + True is returned if pathname is valid. + */ +bool iso9660_dirname_valid_p (const char pathname[]); + +/*! + Take pathname and a version number and turn that into a ISO-9660 + pathname. (That's just the pathname followd by ";" and the version + number. For example, mydir/file.ext -> MYDIR/FILE.EXT;1 for version + 1. The resulting ISO-9660 pathname is returned. +*/ +char *iso9660_pathname_isofy (const char pathname[], uint16_t i_version); + +/*! + Check that pathname is a valid ISO-9660 pathname. + + A valid pathname contains a valid directory name, if one appears and + the filename portion should be no more than 8 characters for the + file prefix and 3 characters in the extension (or portion after a + dot). There should be exactly one dot somewhere in the filename + portion and the filename should be composed of only DCHARs. + + True is returned if pathname is valid. + */ +bool iso9660_pathname_valid_p (const char pathname[]); + +/*===================================================================== + directory tree +======================================================================*/ + +void +iso9660_dir_init_new (void *dir, uint32_t self, uint32_t ssize, + uint32_t parent, uint32_t psize, + const time_t *dir_time); + +void +iso9660_dir_init_new_su (void *dir, uint32_t self, uint32_t ssize, + const void *ssu_data, unsigned int ssu_size, + uint32_t parent, uint32_t psize, + const void *psu_data, unsigned int psu_size, + const time_t *dir_time); + +void +iso9660_dir_add_entry_su (void *dir, const char filename[], uint32_t extent, + uint32_t size, uint8_t file_flags, + const void *su_data, + unsigned int su_size, const time_t *entry_time); + +unsigned int +iso9660_dir_calc_record_size (unsigned int namelen, unsigned int su_len); + +/*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + Returns stat_t of entry if we found lsn, or NULL otherwise. + */ +iso9660_stat_t *iso9660_find_fs_lsn(CdIo *p_cdio, lsn_t i_lsn); + + +/*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + Returns stat_t of entry if we found lsn, or NULL otherwise. + */ +iso9660_stat_t *iso9660_find_ifs_lsn(const iso9660_t *p_iso, lsn_t i_lsn); + + +/*! + Get file status for pathname into stat. NULL is returned on error. + */ +iso9660_stat_t *iso9660_fs_stat (CdIo *p_cdio, const char pathname[]); + + +/*! + Get file status for pathname into stat. NULL is returned on error. + pathname version numbers in the ISO 9660 + name are dropped, i.e. ;1 is removed and if level 1 ISO-9660 names + are lowercased. + */ +iso9660_stat_t *iso9660_fs_stat_translate (CdIo *p_cdio, + const char pathname[], + bool b_mode2); + +/*! + Get file status for pathname into stat. NULL is returned on error. + */ +iso9660_stat_t *iso9660_ifs_stat (iso9660_t *p_iso, const char pathname[]); + + +/*! + Get file status for pathname into stat. NULL is returned on error. + pathname version numbers in the ISO 9660 + name are dropped, i.e. ;1 is removed and if level 1 ISO-9660 names + are lowercased. + */ +iso9660_stat_t *iso9660_ifs_stat_translate (iso9660_t *p_iso, + const char pathname[]); + +/*! + Read pathname (a directory) and return a list of iso9660_stat_t + of the files inside that. The caller must free the returned result. +*/ +CdioList * iso9660_fs_readdir (CdIo *p_cdio, const char pathname[], + bool b_mode2); + +/*! + Read pathname (a directory) and return a list of iso9660_stat_t + of the files inside that. The caller must free the returned result. +*/ +CdioList * iso9660_ifs_readdir (iso9660_t *p_iso, const char pathname[]); + +/*! + Return the PVD's application ID. + NULL is returned if there is some problem in getting this. +*/ +char * iso9660_get_application_id(iso9660_pvd_t *p_pvd); + +/*! + Get the application ID. psz_app_id is set to NULL if there + is some problem in getting this and false is returned. +*/ +bool iso9660_ifs_get_application_id(iso9660_t *p_iso, + /*out*/ char **p_psz_app_id); + +/*! + Return the Joliet level recognized for p_iso. +*/ +uint8_t iso9660_ifs_get_joliet_level(iso9660_t *p_iso); + +uint8_t iso9660_get_dir_len(const iso9660_dir_t *p_idr); + +#if FIXME +uint8_t iso9660_get_dir_size(const iso9660_dir_t *p_idr); + +lsn_t iso9660_get_dir_extent(const iso9660_dir_t *p_idr); +#endif + +/*! + Return the directory name stored in the iso9660_dir_t + + A string is allocated: the caller must deallocate. +*/ +char * iso9660_dir_to_name (const iso9660_dir_t *p_iso9660_dir); + +/*! + Return a string containing the preparer id with trailing + blanks removed. +*/ +char *iso9660_get_preparer_id(const iso9660_pvd_t *p_pvd); + +/*! + Get the preparer ID. psz_preparer_id is set to NULL if there + is some problem in getting this and false is returned. +*/ +bool iso9660_ifs_get_preparer_id(iso9660_t *p_iso, + /*out*/ char **p_psz_preparer_id); + +/*! + Return a string containing the PVD's publisher id with trailing + blanks removed. +*/ +char *iso9660_get_publisher_id(const iso9660_pvd_t *p_pvd); + +/*! + Get the publisher ID. psz_publisher_id is set to NULL if there + is some problem in getting this and false is returned. +*/ +bool iso9660_ifs_get_publisher_id(iso9660_t *p_iso, + /*out*/ char **p_psz_publisher_id); + +uint8_t iso9660_get_pvd_type(const iso9660_pvd_t *p_pvd); + +const char * iso9660_get_pvd_id(const iso9660_pvd_t *p_pvd); + +int iso9660_get_pvd_space_size(const iso9660_pvd_t *p_pvd); + +int iso9660_get_pvd_block_size(const iso9660_pvd_t *p_pvd) ; + +/*! Return the primary volume id version number (of pvd). + If there is an error 0 is returned. + */ +int iso9660_get_pvd_version(const iso9660_pvd_t *pvd) ; + +/*! + Return a string containing the PVD's system id with trailing + blanks removed. +*/ +char *iso9660_get_system_id(const iso9660_pvd_t *p_pvd); + +/*! + Get the system ID. psz_system_id is set to NULL if there + is some problem in getting this and false is returned. +*/ +bool iso9660_ifs_get_system_id(iso9660_t *p_iso, + /*out*/ char **p_psz_system_id); + + +/*! Return the LSN of the root directory for pvd. + If there is an error CDIO_INVALID_LSN is returned. + */ +lsn_t iso9660_get_root_lsn(const iso9660_pvd_t *p_pvd); + +/*! + Return the PVD's volume ID. +*/ +char *iso9660_get_volume_id(const iso9660_pvd_t *p_pvd); + +/*! + Get the system ID. psz_system_id is set to NULL if there + is some problem in getting this and false is returned. +*/ +bool iso9660_ifs_get_volume_id(iso9660_t *p_iso, + /*out*/ char **p_psz_volume_id); + +/*! + Return the PVD's volumeset ID. + NULL is returned if there is some problem in getting this. +*/ +char *iso9660_get_volumeset_id(const iso9660_pvd_t *p_pvd); + +/*! + Get the systemset ID. psz_systemset_id is set to NULL if there + is some problem in getting this and false is returned. +*/ +bool iso9660_ifs_get_volumeset_id(iso9660_t *p_iso, + /*out*/ char **p_psz_volumeset_id); + +/* pathtable */ + +/*! Zero's out pathable. Do this first. */ +void iso9660_pathtable_init (void *pt); + +unsigned int iso9660_pathtable_get_size (const void *pt); + +uint16_t +iso9660_pathtable_l_add_entry (void *pt, const char name[], uint32_t extent, + uint16_t parent); + +uint16_t +iso9660_pathtable_m_add_entry (void *pt, const char name[], uint32_t extent, + uint16_t parent); + +/*===================================================================== + Volume Descriptors +======================================================================*/ + +void +iso9660_set_pvd (void *pd, const char volume_id[], const char application_id[], + const char publisher_id[], const char preparer_id[], + uint32_t iso_size, const void *root_dir, + uint32_t path_table_l_extent, uint32_t path_table_m_extent, + uint32_t path_table_size, const time_t *pvd_time); + +void +iso9660_set_evd (void *pd); + +/*! + Return true if ISO 9660 image has extended attrributes (XA). +*/ +bool iso9660_ifs_is_xa (const iso9660_t * p_iso); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_ISO9660_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/cdio/logging.h b/contrib/libcdio/cdio/logging.h new file mode 100644 index 000000000..8c78259ea --- /dev/null +++ b/contrib/libcdio/cdio/logging.h @@ -0,0 +1,137 @@ +/* + $Id: logging.h,v 1.3 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2000, Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** \file logging.h + * \brief Header to control logging and level of detail of output. + * + */ + +#ifndef __LOGGING_H__ +#define __LOGGING_H__ + +#include <cdio/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The different log levels supported. + */ +typedef enum { + CDIO_LOG_DEBUG = 1, /**< Debug-level messages - helps debug what's up. */ + CDIO_LOG_INFO, /**< Informational - indicates perhaps something of + interest. */ + CDIO_LOG_WARN, /**< Warning conditions - something that looks funny. */ + CDIO_LOG_ERROR, /**< Error conditions - may terminate program. */ + CDIO_LOG_ASSERT /**< Critical conditions - may abort program. */ +} cdio_log_level_t; + +/** + * The place to save the preference concerning how much verbosity + * is desired. This is used by the internal default log handler, but + * it could be use by applications which provide their own log handler. + */ +extern cdio_log_level_t cdio_loglevel_default; + +/** + * This type defines the signature of a log handler. For every + * message being logged, the handler will receive the log level and + * the message string. + * + * @see cdio_log_set_handler + * @see cdio_log_level_t + * + * @param level The log level. + * @param message The log message. + */ +typedef void (*cdio_log_handler_t) (cdio_log_level_t level, + const char message[]); + +/** + * Set a custom log handler for libcdio. The return value is the log + * handler being replaced. If the provided parameter is NULL, then + * the handler will be reset to the default handler. + * + * @see cdio_log_handler_t + * + * @param new_handler The new log handler. + * @return The previous log handler. + */ +cdio_log_handler_t cdio_log_set_handler (cdio_log_handler_t new_handler); + +/** + * Handle an message with the given log level. + * + * @see cdio_debug + * @see cdio_info + * @see cdio_warn + * @see cdio_error + + * @param level The log level. + * @param format printf-style format string + * @param ... remaining arguments needed by format string + */ +void cdio_log (cdio_log_level_t level, + const char format[], ...) GNUC_PRINTF(2, 3); + +/** + * Handle a debugging message. + * + * @see cdio_log for a more generic routine + */ +void cdio_debug (const char format[], ...) GNUC_PRINTF(1,2); + +/** + * Handle an informative message. + * + * @see cdio_log for a more generic routine + */ +void cdio_info (const char format[], ...) GNUC_PRINTF(1,2); + +/** + * Handle a warning message. + * + * @see cdio_log for a more generic routine + */ +void cdio_warn (const char format[], ...) GNUC_PRINTF(1,2); + +/** + * Handle an error message. Execution is terminated. + * + * @see cdio_log for a more generic routine. + */ +void cdio_error (const char format[], ...) GNUC_PRINTF(1,2); + +#ifdef __cplusplus +} +#endif + +#endif /* __LOGGING_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/cdio/scsi_mmc.h b/contrib/libcdio/cdio/scsi_mmc.h new file mode 100644 index 000000000..12860247e --- /dev/null +++ b/contrib/libcdio/cdio/scsi_mmc.h @@ -0,0 +1,415 @@ +/* + $Id: scsi_mmc.h,v 1.1 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*! + \file scsi_mmc.h + \brief Common definitions for SCSI MMC (Multi-Media Commands). +*/ + +#ifndef __SCSI_MMC_H__ +#define __SCSI_MMC_H__ + +#include <cdio/cdio.h> +#include <cdio/types.h> +#include <cdio/dvd.h> + +/*! The generic packet command opcodes for CD/DVD Logical Units. */ + +#define CDIO_MMC_GPCMD_INQUIRY 0x12 +#define CDIO_MMC_GPCMD_MODE_SELECT_6 0x15 +#define CDIO_MMC_GPCMD_MODE_SENSE 0x1a +#define CDIO_MMC_GPCMD_START_STOP 0x1b +#define CDIO_MMC_GPCMD_ALLOW_MEDIUM_REMOVAL 0x1e +#define CDIO_MMC_GPCMD_READ_10 0x28 + +/*! + Group 2 Commands + */ +#define CDIO_MMC_GPCMD_READ_SUBCHANNEL 0x42 +#define CDIO_MMC_GPCMD_READ_TOC 0x43 +#define CDIO_MMC_GPCMD_READ_HEADER 0x44 +#define CDIO_MMC_GPCMD_PLAY_AUDIO_10 0x45 +#define CDIO_MMC_GPCMD_GET_CONFIGURATION 0x46 +#define CDIO_MMC_GPCMD_PLAY_AUDIO_MSF 0x47 +#define CDIO_MMC_GPCMD_PLAY_AUDIO_TI 0x48 +#define CDIO_MMC_GPCMD_PLAY_TRACK_REL_10 0x49 +#define CDIO_MMC_GPCMD_PAUSE_RESUME 0x4b + +#define CDIO_MMC_GPCMD_READ_DISC_INFO 0x51 +#define CDIO_MMC_GPCMD_MODE_SELECT 0x55 +#define CDIO_MMC_GPCMD_MODE_SENSE_10 0x5a + +/*! + Group 5 Commands + */ +#define CDIO_MMC_GPCMD_PLAY_AUDIO_12 0xa5 +#define CDIO_MMC_GPCMD_READ_12 0xa8 +#define CDIO_MMC_GPCMD_PLAY_TRACK_REL_12 0xa9 +#define CDIO_MMC_GPCMD_READ_DVD_STRUCTURE 0xad +#define CDIO_MMC_GPCMD_READ_CD 0xbe +#define CDIO_MMC_GPCMD_READ_MSF 0xb9 + +/*! + Group 6 Commands + */ + +#define CDIO_MMC_GPCMD_CD_PLAYBACK_STATUS 0xc4 /**< SONY unique command */ +#define CDIO_MMC_GPCMD_PLAYBACK_CONTROL 0xc9 /**< SONY unique command */ +#define CDIO_MMC_GPCMD_READ_CDDA 0xd8 /**< Vendor unique command */ +#define CDIO_MMC_GPCMD_READ_CDXA 0xdb /**< Vendor unique command */ +#define CDIO_MMC_GPCMD_READ_ALL_SUBCODES 0xdf /**< Vendor unique command */ + + + +/*! Level values that can go into READ_CD */ +#define CDIO_MMC_READ_TYPE_ANY 0 /**< All types */ +#define CDIO_MMC_READ_TYPE_CDDA 1 /**< Only CD-DA sectors */ +#define CDIO_MMC_READ_TYPE_MODE1 2 /**< mode1 sectors (user data = 2048) */ +#define CDIO_MMC_READ_TYPE_MODE2 3 /**< mode2 sectors form1 or form2 */ +#define CDIO_MMC_READ_TYPE_M2F1 4 /**< mode2 sectors form1 */ +#define CDIO_MMC_READ_TYPE_M2F2 5 /**< mode2 sectors form2 */ + +/*! Format values for READ_TOC */ +#define CDIO_MMC_READTOC_FMT_TOC 0 +#define CDIO_MMC_READTOC_FMT_SESSION 1 +#define CDIO_MMC_READTOC_FMT_FULTOC 2 +#define CDIO_MMC_READTOC_FMT_PMA 3 /**< Q subcode data */ +#define CDIO_MMC_READTOC_FMT_ATIP 4 /**< includes media type */ +#define CDIO_MMC_READTOC_FMT_CDTEXT 5 /**< CD-TEXT info */ + +/*! Page codes for MODE SENSE and MODE SET. */ +#define CDIO_MMC_R_W_ERROR_PAGE 0x01 +#define CDIO_MMC_WRITE_PARMS_PAGE 0x05 +#define CDIO_MMC_AUDIO_CTL_PAGE 0x0e +#define CDIO_MMC_CDR_PARMS_PAGE 0x0d +#define CDIO_MMC_POWER_PAGE 0x1a +#define CDIO_MMC_FAULT_FAIL_PAGE 0x1c +#define CDIO_MMC_TO_PROTECT_PAGE 0x1d +#define CDIO_MMC_CAPABILITIES_PAGE 0x2a +#define CDIO_MMC_ALL_PAGES 0x3f + +/*! Return type codes for GET_CONFIGURATION. */ +#define CDIO_MMC_GET_CONF_ALL_FEATURES 0 /**< all features without regard + to currency. */ +#define CDIO_MMC_GET_CONF_CURRENT_FEATURES 1 /**< features which are currently + in effect (e.g. based on + medium inserted). */ +#define CDIO_MMC_GET_CONF_NAMED_FEATURE 2 /**< just the feature named in + the GET_CONFIGURATION + cdb. */ + +/*! FEATURE codes used in GET CONFIGURATION. */ + +#define CDIO_MMC_FEATURE_PROFILE_LIST 0x000 /**< Profile List Feature */ +#define CDIO_MMC_FEATURE_CORE 0x001 +#define CDIO_MMC_FEATURE_REMOVABLE_MEDIUM 0x002 /**< Removable Medium + Feature */ +#define CDIO_MMC_FEATURE_WRITE_PROTECT 0x003 /**< Write Protect + Feature */ +#define CDIO_MMC_FEATURE_RANDOM_READABLE 0x010 /**< Random Readable + Feature */ +#define CDIO_MMC_FEATURE_MULTI_READ 0x01D /**< Multi-Read + Feature */ +#define CDIO_MMC_FEATURE_CD_READ 0x01E /**< CD Read + Feature */ +#define CDIO_MMC_FEATURE_DVD_READ 0x01F /**< DVD Read + Feature */ +#define CDIO_MMC_FEATURE_RANDOM_WRITABLE 0x020 /**< Random Writable + Feature */ +#define CDIO_MMC_FEATURE_INCR_WRITE 0x021 /**< Incremental + Streaming Writable + Feature */ +#define CDIO_MMC_FEATURE_SECTOR_ERASE 0x022 /**< Sector Erasable + Feature */ +#define CDIO_MMC_FEATURE_FORMATABLE 0x023 /**< Formattable + Feature */ +#define CDIO_MMC_FEATURE_DEFECT_MGMT 0x024 /**< Management + Ability of the + Logical Unit/media + system to provide + an apparently + defect-free + space.*/ +#define CDIO_MMC_FEATURE_WRITE_ONCE 0x025 /**< Write Once + Feature */ +#define CDIO_MMC_FEATURE_RESTRICT_OVERW 0x026 /**< Restricted + Overwrite + Feature */ +#define CDIO_MMC_FEATURE_CD_RW_CAV 0x027 /**< CD-RW CAV Write + Feature */ +#define CDIO_MMC_FEATURE_MRW 0x028 /**< MRW Feature */ +#define CDIO_MMC_FEATURE_DVD_PRW 0x02A /**< DVD+RW Feature */ +#define CDIO_MMC_FEATURE_DVD_PR 0x02B /**< DVD+R Feature */ +#define CDIO_MMC_FEATURE_CD_TAO 0x02D +#define CDIO_MMC_FEATURE_CD_SAO 0x02E +#define CDIO_MMC_FEATURE_POWER_MGMT 0x100 /**< Initiator and + device directed + power management */ +#define CDIO_MMC_FEATURE_CDDA_EXT_PLAY 0x103 /**< Ability to play + audio CDs via the + Logical Unit s own + analog output */ +#define CDIO_MMC_FEATURE_MCODE_UPGRADE 0x104 /* Ability for the + device to accept + new microcode via + the interface */ +#define CDIO_MMC_FEATURE_TIME_OUT 0x105 /**< Ability to + respond to all + commands within a + specific time */ +#define CDIO_MMC_FEATURE_DVD_CSS 0x106 /**< Ability to + perform DVD + CSS/CPPM + authentication and + RPC */ +#define CDIO_MMC_FEATURE_RT_STREAMING 0x107 /**< Ability to read + and write using + Initiator requested + performance + parameters + */ +#define CDIO_MMC_FEATURE_LU_SN 0x108 /**< The Logical Unit + has a unique + identifier. */ +#define CDIO_MMC_FEATURE_FIRMWARE_DATE 0x1FF /**< Firmware creation + date report */ + +/*! Profile codes used in GET_CONFIGURATION - PROFILE LIST. */ +#define CDIO_MMC_FEATURE_PROF_NON_REMOVABLE 0x0001 /**< Re-writable + disk, capable of + changing + behavior */ +#define CDIO_MMC_FEATURE_PROF_REMOVABLE 0x0002 /**< disk + Re-writable; + with removable + media */ +#define CDIO_MMC_FEATURE_PROF_MO_ERASABLE 0x0003 /**< Erasable + Magneto-Optical + disk with sector + erase + capability */ +#define CDIO_MMC_FEATURE_PROF_MO_WRITE_ONCE 0x0004 /**< Write Once + Magneto-Optical + write once */ +#define CDIO_MMC_FEATURE_PROF_AS_MO 0x0005 /**< Advance + Storage + Magneto-Optical */ +#define CDIO_MMC_FEATURE_PROF_CD_ROM 0x0008 /**< Read only + Compact Disc + capable */ +#define CDIO_MMC_FEATURE_PROF_CD_R 0x0009 /**< Write once + Compact Disc + capable */ +#define CDIO_MMC_FEATURE_PROF_CD_RW 0x000A /**< CD-RW + Re-writable + Compact Disc + capable */ +#define CDIO_MMC_FEATURE_PROF_DVD_ROM 0x0010 /**< Read only + DVD */ +#define CDIO_MMC_FEATURE_PROF_DVD_R_SEQ 0x0011 /**< Re-recordable + DVD using + Sequential + recording */ +#define CDIO_MMC_FEATURE_PROF_DVD_RAM 0x0012 /**< Re-writable + DVD */ +#define CDIO_MMC_FEATURE_PROF_DVD_RW_RO 0x0013 /**< Re-recordable + DVD using + Restricted + Overwrite */ +#define CDIO_MMC_FEATURE_PROF_DVD_RW_SEQ 0x0014 /**< Re-recordable + DVD using + Sequential + recording */ +#define CDIO_MMC_FEATURE_PROF_DVD_PRW 0x001A /**< DVD+RW - DVD + ReWritable */ +#define CDIO_MMC_FEATURE_PROF_DVD_PR 0x001B /**< DVD+R - DVD + Recordable */ +#define CDIO_MMC_FEATURE_PROF_DDCD_ROM 0x0020 /**< Read only + DDCD */ +#define CDIO_MMC_FEATURE_PROF_DDCD_R 0x0021 /**< DDCD-R Write + only DDCD */ +#define CDIO_MMC_FEATURE_PROF_DDCD_RW 0x0022 /**< Re-Write only + DDCD */ +#define CDIO_MMC_FEATURE_PROF_NON_CONFORM 0xFFFF /**< The Logical + Unit does not + conform to any + Profile. */ + +/*! This is listed as optional in ATAPI 2.6, but is (curiously) + missing from Mt. Fuji, Table 57. It _is_ mentioned in Mt. Fuji + Table 377 as an MMC command for SCSi devices though... Most ATAPI + drives support it. */ +#define CDIO_MMC_GPCMD_SET_SPEED 0xbb + + +/*! The largest Command Descriptor Buffer (CDB) size. + The possible sizes are 6, 10, and 12 bytes. + */ +#define MAX_CDB_LEN 12 + +/*! \brief A Command Descriptor Buffer (CDB) used in sending SCSI MMC + commands. + */ +typedef struct scsi_mmc_cdb { + uint8_t field[MAX_CDB_LEN]; +} scsi_mmc_cdb_t; + +/*! \brief Format of header block in data returned from a SCSI-MMC + GET_CONFIGURATION command. + */ +typedef struct scsi_mmc_feature_list_header { + unsigned char length_msb; + unsigned char length_1sb; + unsigned char length_2sb; + unsigned char length_lsb; + unsigned char reserved1; + unsigned char reserved2; + unsigned char profile_msb; + unsigned char profile_lsb; +} scs_mmc_feature_list_header_t; + +/*! An enumeration indicating whether a SCSI MMC command is sending + data or getting data. + */ +typedef enum scsi_mmc_direction { + SCSI_MMC_DATA_READ, + SCSI_MMC_DATA_WRITE +} scsi_mmc_direction_t; + +#define CDIO_MMC_SET_COMMAND(cdb, command) \ + cdb[0] = command + +#define CDIO_MMC_SET_READ_TYPE(cdb, sector_type) \ + cdb[1] = (sector_type << 2) + +#define CDIO_MMC_GET_LEN16(p) \ + (p[0]<<8) + p[1] + +#define CDIO_MMC_GET_LEN32(p) \ + (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; + +#define CDIO_MMC_SET_LEN16(cdb, pos, len) \ + cdb[pos ] = (len >> 8) & 0xff; \ + cdb[pos+1] = (len ) & 0xff + +#define CDIO_MMC_SET_READ_LBA(cdb, lba) \ + cdb[2] = (lba >> 24) & 0xff; \ + cdb[3] = (lba >> 16) & 0xff; \ + cdb[4] = (lba >> 8) & 0xff; \ + cdb[5] = (lba ) & 0xff + +#define CDIO_MMC_SET_START_TRACK(cdb, command) \ + cdb[6] = command + +#define CDIO_MMC_SET_READ_LENGTH24(cdb, len) \ + cdb[6] = (len >> 16) & 0xff; \ + cdb[7] = (len >> 8) & 0xff; \ + cdb[8] = (len ) & 0xff + +#define CDIO_MMC_SET_READ_LENGTH16(cdb, len) \ + CDIO_MMC_SET_LEN16(cdb, 7, len) + +#define CDIO_MMC_SET_READ_LENGTH8(cdb, len) \ + cdb[8] = (len ) & 0xff + +#define CDIO_MMC_MCSB_ALL_HEADERS 0x78 + +#define CDIO_MMC_SET_MAIN_CHANNEL_SELECTION_BITS(cdb, val) \ + cdb[9] = val; + +/*! + Return the number of length in bytes of the Command Descriptor + buffer (CDB) for a given SCSI MMC command. The length will be + either 6, 10, or 12. +*/ +uint8_t scsi_mmc_get_cmd_len(uint8_t scsi_cmd); + + +/*! + Run a SCSI MMC command. + + cdio CD structure set by cdio_open(). + i_timeout_ms time in milliseconds we will wait for the command + to complete. + p_cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + e_direction direction the transfer is to go. + i_buf Size of buffer + p_buf Buffer for data, both sending and receiving. + + Returns 0 if command completed successfully. + */ +int scsi_mmc_run_cmd( const CdIo *p_cdio, unsigned int i_timeout_ms, + const scsi_mmc_cdb_t *p_cdb, + scsi_mmc_direction_t e_direction, unsigned int i_buf, + /*in/out*/ void *p_buf ); + +/*! + * Eject using SCSI MMC commands. Return 0 if successful. + */ +int scsi_mmc_eject_media( const CdIo *p_cdio); + +/*! Packet driver to read mode2 sectors. + Can read only up to 25 blocks. +*/ +int scsi_mmc_read_sectors ( const CdIo *p_cdio, void *p_buf, lba_t lba, + int sector_type, unsigned int nblocks); + +/*! + Set the block size for subsequest read requests, via a SCSI MMC + MODE_SELECT 6 command. + */ +int scsi_mmc_set_blocksize ( const CdIo *p_cdio, unsigned int bsize); + +/*! + Return the the kind of drive capabilities of device. + */ +void scsi_mmc_get_drive_cap (const CdIo *p_cdio, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap); + +/*! + Get the DVD type associated with cd object. +*/ +discmode_t scsi_mmc_get_dvd_struct_physical ( const CdIo *p_cdio, + cdio_dvd_struct_t *s); + +/*! + Get the CD-ROM hardware info via a SCSI MMC INQUIRY command. + False is returned if we had an error getting the information. +*/ +bool scsi_mmc_get_hwinfo ( const CdIo *p_cdio, + /* out*/ cdio_hwinfo_t *p_hw_info ); + + +/*! + Get the media catalog number (MCN) from the CD via MMC. + + @return the media catalog number r NULL if there is none or we + don't have the ability to get it. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. + +*/ +char *scsi_mmc_get_mcn ( const CdIo *p_cdio ); + +#endif /* __SCSI_MMC_H__ */ diff --git a/contrib/libcdio/cdio/sector.h b/contrib/libcdio/cdio/sector.h new file mode 100644 index 000000000..826883aea --- /dev/null +++ b/contrib/libcdio/cdio/sector.h @@ -0,0 +1,326 @@ +/* + $Id: sector.h,v 1.3 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*! + \file sector.h + \brief Things related to CD-ROM layout: tracks, sector sizes, MSFs, LBAs. + + A CD-ROM physical sector size is 2048, 2052, 2056, 2324, 2332, 2336, + 2340, or 2352 bytes long. + + Sector types of the standard CD-ROM data formats: + +\verbatim + format sector type user data size (bytes) + ----------------------------------------------------------------------------- + 1 (Red Book) CD-DA 2352 (CDIO_CD_FRAMESIZE_RAW) + 2 (Yellow Book) Mode1 Form1 2048 (CDIO_CD_FRAMESIZE) + 3 (Yellow Book) Mode1 Form2 2336 (M2RAW_SECTOR_SIZE) + 4 (Green Book) Mode2 Form1 2048 (CDIO_CD_FRAMESIZE) + 5 (Green Book) Mode2 Form2 2328 (2324+4 spare bytes) + + + The layout of the standard CD-ROM data formats: + ----------------------------------------------------------------------------- + - audio (red): | audio_sample_bytes | + | 2352 | + + - data (yellow, mode1): | sync - head - data - EDC - zero - ECC | + | 12 - 4 - 2048 - 4 - 8 - 276 | + + - data (yellow, mode2): | sync - head - data | + | 12 - 4 - 2336 | + + - XA data (green, mode2 form1): | sync - head - sub - data - EDC - ECC | + | 12 - 4 - 8 - 2048 - 4 - 276 | + + - XA data (green, mode2 form2): | sync - head - sub - data - Spare | + | 12 - 4 - 8 - 2324 - 4 | +\endverbatim + + +*/ + +#ifndef _CDIO_SECTOR_H_ +#define _CDIO_SECTOR_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include <cdio/types.h> + +/*! track modes (Table 350) + reference: MMC-3 draft revsion - 10g +*/ +typedef enum { + AUDIO, /**< 2352 byte block length */ + MODE1, /**< 2048 byte block length */ + MODE1_RAW, /**< 2352 byte block length */ + MODE2, /**< 2336 byte block length */ + MODE2_FORM1, /**< 2048 byte block length */ + MODE2_FORM2, /**< 2324 byte block length */ + MODE2_FORM_MIX, /**< 2336 byte block length */ + MODE2_RAW /**< 2352 byte block length */ +} trackmode_t; + +/*! disc modes. The first combined from MMC-3 5.29.2.8 (Send CUESHEET) + and GNU/Linux /usr/include/linux/cdrom.h and we've added DVD. + */ +typedef enum { + CDIO_DISC_MODE_CD_DA, /**< CD-DA */ + CDIO_DISC_MODE_CD_DATA, /**< CD-ROM form 1 */ + CDIO_DISC_MODE_CD_XA, /**< CD-ROM XA form2 */ + CDIO_DISC_MODE_CD_MIXED, /**< Some combo of above. */ + CDIO_DISC_MODE_DVD_ROM, /**< DVD ROM (e.g. movies) */ + CDIO_DISC_MODE_DVD_RAM, /**< DVD-RAM */ + CDIO_DISC_MODE_DVD_R, /**< DVD-R */ + CDIO_DISC_MODE_DVD_RW, /**< DVD-RW */ + CDIO_DISC_MODE_DVD_PR, /**< DVD+R */ + CDIO_DISC_MODE_DVD_PRW, /**< DVD+RW */ + CDIO_DISC_MODE_DVD_OTHER, /**< Unknown/unclassified DVD type */ + CDIO_DISC_MODE_NO_INFO, + CDIO_DISC_MODE_ERROR +} discmode_t; + +/*! Information that can be obtained through a Read Subchannel + command. + */ +#define CDIO_SUBCHANNEL_SUBQ_DATA 0 +#define CDIO_SUBCHANNEL_CURRENT_POSITION 1 +#define CDIO_SUBCHANNEL_MEDIA_CATALOG 2 +#define CDIO_SUBCHANNEL_TRACK_ISRC 3 + +/*! track flags + * Q Sub-channel Control Field (4.2.3.3) + */ +typedef enum { + NONE = 0x00, /* no flags set */ + PRE_EMPHASIS = 0x01, /* audio track recorded with pre-emphasis */ + COPY_PERMITTED = 0x02, /* digital copy permitted */ + DATA = 0x04, /* data track */ + FOUR_CHANNEL_AUDIO = 0x08, /* 4 audio channels */ + SCMS = 0x10 /* SCMS (5.29.2.7) */ +} flag_t; + +#define CDIO_PREGAP_SECTORS 150 +#define CDIO_POSTGAP_SECTORS 150 + +/* + Some generally useful CD-ROM information -- mostly based on the above. + This is from linux.h - not to slight other OS's. This was the first + place I came across such useful stuff. +*/ +#define CDIO_CD_MINS 74 /**< max. minutes per CD, not really + a limit */ +#define CDIO_CD_SECS_PER_MIN 60 /**< seconds per minute */ +#define CDIO_CD_FRAMES_PER_SEC 75 /**< frames per second */ +#define CDIO_CD_SYNC_SIZE 12 /**< 12 sync bytes per raw data frame */ +#define CDIO_CD_CHUNK_SIZE 24 /**< lowest-level "data bytes piece" */ +#define CDIO_CD_NUM_OF_CHUNKS 98 /**< chunks per frame */ +#define CDIO_CD_FRAMESIZE_SUB 96 /**< subchannel data "frame" size */ +#define CDIO_CD_HEADER_SIZE 4 /**< header (address) bytes per raw + data frame */ +#define CDIO_CD_SUBHEADER_SIZE 8 /**< subheader bytes per raw XA data + frame */ +#define CDIO_CD_EDC_SIZE 4 /**< bytes EDC per most raw data + frame types */ +#define CDIO_CD_M1F1_ZERO_SIZE 8 /**< bytes zero per yellow book mode + 1 frame */ +#define CDIO_CD_ECC_SIZE 276 /**< bytes ECC per most raw data frame + types */ +#define CDIO_CD_FRAMESIZE 2048 /**< bytes per frame, "cooked" mode */ +#define CDIO_CD_FRAMESIZE_RAW 2352 /**< bytes per frame, "raw" mode */ +#define CDIO_CD_FRAMESIZE_RAWER 2646 /**< The maximum possible returned + bytes */ +#define CDIO_CD_FRAMESIZE_RAW1 (CDIO_CD_CD_FRAMESIZE_RAW-CDIO_CD_SYNC_SIZE) /*2340*/ +#define CDIO_CD_FRAMESIZE_RAW0 (CDIO_CD_FRAMESIZE_RAW-CDIO_CD_SYNC_SIZE-CDIO_CD__HEAD_SIZE) /*2336*/ + +/*! "before data" part of raw XA (green, mode2) frame */ +#define CDIO_CD_XA_HEADER (CDIO_CD_HEADER_SIZE+CDIO_CD_SUBHEADER_SIZE) + +/*! "after data" part of raw XA (green, mode2 form1) frame */ +#define CDIO_CD_XA_TAIL (CDIO_CD_EDC_SIZE+CDIO_CD_ECC_SIZE) + +/*! "before data" sync bytes + header of XA (green, mode2) frame */ +#define CDIO_CD_XA_SYNC_HEADER (CDIO_CD_SYNC_SIZE+CDIO_CD_XA_HEADER) + +/*! CD-ROM address types (GNU/Linux e.g. cdrom_tocentry.cdte_format) */ +#define CDIO_CDROM_LBA 0x01 /**< "logical block": first frame is #0 */ +#define CDIO_CDROM_MSF 0x02 /**< "minute-second-frame": binary, not + BCD here! */ + +/*! CD-ROM track format types (GNU/Linux cdte_ctrl) */ +#define CDIO_CDROM_DATA_TRACK 0x04 +#define CDIO_CDROM_CDI_TRACK 0x10 +#define CDIO_CDROM_XA_TRACK 0x20 + +/*! The leadout track is always 0xAA, regardless of # of tracks on + disc, or what value may be used internally. For example although + OS X uses a different value for the lead-out track internally than + given below, programmers should use CDIO_CDROM_LEADOUT_TRACK and + not worry about this. + */ +#define CDIO_CDROM_LEADOUT_TRACK 0xAA + +#define M2F2_SECTOR_SIZE 2324 +#define M2SUB_SECTOR_SIZE 2332 +#define M2RAW_SECTOR_SIZE 2336 + +/*! Largest CD track number */ +#define CDIO_CD_MAX_TRACKS 99 +/*! Smallest CD track number */ +#define CDIO_CD_MIN_TRACK_NO 1 + +/*! Largest CD session number */ +#define CDIO_CD_MAX_SESSIONS 99 +/*! Smallest CD session number */ +#define CDIO_CD_MIN_SESSION_NO 1 + +/*! Largest LSN in a CD */ +#define CDIO_CD_MAX_LSN 450150 +/*! Smallest LSN in a CD */ +#define CDIO_CD_MIN_LSN -450150 + + +#define CDIO_CD_FRAMES_PER_MIN \ + (CDIO_CD_FRAMES_PER_SEC*CDIO_CD_SECS_PER_MIN) + +#define CDIO_CD_74MIN_SECTORS (UINT32_C(74)*CDIO_CD_FRAMES_PER_MIN) +#define CDIO_CD_80MIN_SECTORS (UINT32_C(80)*CDIO_CD_FRAMES_PER_MIN) +#define CDIO_CD_90MIN_SECTORS (UINT32_C(90)*CDIO_CD_FRAMES_PER_MIN) + +#define CDIO_CD_MAX_SECTORS \ + (UINT32_C(100)*CDIO_CD_FRAMES_PER_MIN-CDIO_PREGAP_SECTORS) + +#define msf_t_SIZEOF 3 + +/*! + Convert an LBA into a string representation of the MSF. + \warning cdio_lba_to_msf_str returns new allocated string */ +char *cdio_lba_to_msf_str (lba_t lba); + +/*! + Convert an MSF into a string representation of the MSF. + \warning cdio_msf_to_msf_str returns new allocated string */ +char *cdio_msf_to_str (const msf_t *msf); + +/*! + Convert an LBA into the corresponding LSN. +*/ +lba_t cdio_lba_to_lsn (lba_t lba); + +/*! + Convert an LBA into the corresponding MSF. +*/ +void cdio_lba_to_msf(lba_t lba, msf_t *msf); + +/*! + Convert an LSN into the corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. +*/ +lba_t cdio_lsn_to_lba (lsn_t lsn); + +/*! + Convert an LSN into the corresponding MSF. +*/ +void cdio_lsn_to_msf (lsn_t lsn, msf_t *msf); + +/*! + Convert a MSF into the corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. +*/ +lba_t cdio_msf_to_lba (const msf_t *msf); + +/*! + Convert a MSF into the corresponding LSN. + CDIO_INVALID_LSN is returned if there is an error. +*/ +lsn_t cdio_msf_to_lsn (const msf_t *msf); + +/*! + Convert a MSF - broken out as 3 integer components into the + corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. +*/ +lba_t cdio_msf3_to_lba (unsigned int minutes, unsigned int seconds, + unsigned int frames); + +/*! + Convert a string of the form MM:SS:FF into the corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. +*/ +lba_t cdio_mmssff_to_lba (const char *psz_mmssff); + +/*! + Return true if discmode is some sort of CD. +*/ +bool cdio_is_discmode_cdrom (discmode_t discmode); + +/*! + Return true if discmode is some sort of DVD. +*/ +bool cdio_is_discmode_dvd (discmode_t discmode); + + +#ifdef __cplusplus + } +#endif + +static inline bool discmode_is_cd(discmode_t discmode) +{ + switch (discmode) { + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + return true; + default: + return false; + } +} + +static inline bool discmode_is_dvd(discmode_t discmode) +{ + switch (discmode) { + case CDIO_DISC_MODE_DVD_ROM: + case CDIO_DISC_MODE_DVD_RAM: + case CDIO_DISC_MODE_DVD_R: + case CDIO_DISC_MODE_DVD_RW: + case CDIO_DISC_MODE_DVD_PR: + case CDIO_DISC_MODE_DVD_PRW: + case CDIO_DISC_MODE_DVD_OTHER: + return true; + default: + return false; + } +} + + +#endif /* _CDIO_SECTOR_H_ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/cdio/types.h b/contrib/libcdio/cdio/types.h new file mode 100644 index 000000000..ec84a142b --- /dev/null +++ b/contrib/libcdio/cdio/types.h @@ -0,0 +1,379 @@ +/* + $Id: types.h,v 1.3 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2002, 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** \file types.h + * \brief Common type definitions used pervasively in libcdio. + */ + + +#ifndef __CDIO_TYPES_H__ +#define __CDIO_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* provide some C99 definitions */ + +#if defined(HAVE_SYS_TYPES_H) +#include <sys/types.h> +#endif + +#if defined(HAVE_STDINT_H) +# include <stdint.h> +#elif defined(HAVE_INTTYPES_H) +# include <inttypes.h> +#elif defined(AMIGA) || defined(__linux__) + typedef u_int8_t uint8_t; + typedef u_int16_t uint16_t; + typedef u_int32_t uint32_t; + typedef u_int64_t uint64_t; +#else + /* warning ISO/IEC 9899:1999 <stdint.h> was missing and even <inttypes.h> */ + /* fixme */ +#endif /* HAVE_STDINT_H */ + + /* default HP/UX macros are broken */ +#if defined(__hpux__) +# undef UINT16_C +# undef UINT32_C +# undef UINT64_C +# undef INT64_C +#endif + + /* if it's still not defined, take a good guess... should work for + most 32bit and 64bit archs */ + +#ifndef UINT16_C +# define UINT16_C(c) c ## U +#endif + +#ifndef UINT32_C +# if defined (SIZEOF_INT) && SIZEOF_INT == 4 +# define UINT32_C(c) c ## U +# elif defined (SIZEOF_LONG) && SIZEOF_LONG == 4 +# define UINT32_C(c) c ## UL +# else +# define UINT32_C(c) c ## U +# endif +#endif + +#ifndef UINT64_C +# if defined (SIZEOF_LONG) && SIZEOF_LONG == 8 +# define UINT64_C(c) c ## UL +# elif defined (SIZEOF_INT) && SIZEOF_INT == 8 +# define UINT64_C(c) c ## U +# else +# define UINT64_C(c) c ## ULL +# endif +#endif + +#ifndef INT64_C +# if defined (SIZEOF_LONG) && SIZEOF_LONG == 8 +# define INT64_C(c) c ## L +# elif defined (SIZEOF_INT) && SIZEOF_INT == 8 +# define INT64_C(c) c +# else +# define INT64_C(c) c ## LL +# endif +#endif + +#if defined(HAVE_STDBOOL_H) +#include <stdbool.h> +#else + /* ISO/IEC 9899:1999 <stdbool.h> missing -- enabling workaround */ + +# ifndef __cplusplus + typedef enum + { + false = 0, + true = 1 + } _cdio_Bool; + +# define false false +# define true true +# define bool _cdio_Bool +# endif +#endif + + /* some GCC optimizations -- gcc 2.5+ */ + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +#define GNUC_PRINTF( format_idx, arg_idx ) \ + __attribute__((format (printf, format_idx, arg_idx))) +#define GNUC_SCANF( format_idx, arg_idx ) \ + __attribute__((format (scanf, format_idx, arg_idx))) +#define GNUC_FORMAT( arg_idx ) \ + __attribute__((format_arg (arg_idx))) +#define GNUC_NORETURN \ + __attribute__((noreturn)) +#define GNUC_CONST \ + __attribute__((const)) +#define GNUC_UNUSED \ + __attribute__((unused)) +#define GNUC_PACKED \ + __attribute__((packed)) +#else /* !__GNUC__ */ +#define GNUC_PRINTF( format_idx, arg_idx ) +#define GNUC_SCANF( format_idx, arg_idx ) +#define GNUC_FORMAT( arg_idx ) +#define GNUC_NORETURN +#define GNUC_CONST +#define GNUC_UNUSED +#define GNUC_PACKED +#endif /* !__GNUC__ */ + +#if defined(__GNUC__) + /* for GCC we try to use GNUC_PACKED */ +# define PRAGMA_BEGIN_PACKED +# define PRAGMA_END_PACKED +#elif defined(HAVE_ISOC99_PRAGMA) + /* should work with most EDG-frontend based compilers */ +# define PRAGMA_BEGIN_PACKED _Pragma("pack(1)") +# define PRAGMA_END_PACKED _Pragma("pack()") +#else /* neither gcc nor _Pragma() available... */ + /* ...so let's be naive and hope the regression testsuite is run... */ +# define PRAGMA_BEGIN_PACKED +# define PRAGMA_END_PACKED +#endif + + /* + * user directed static branch prediction gcc 2.96+ + */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 95) +# define GNUC_LIKELY(x) __builtin_expect((x),true) +# define GNUC_UNLIKELY(x) __builtin_expect((x),false) +#else +# define GNUC_LIKELY(x) (x) +# define GNUC_UNLIKELY(x) (x) +#endif + +#ifndef NULL +# define NULL ((void*) 0) +#endif + + /* our own offsetof()-like macro */ +#define __cd_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + + /*! + \brief MSF (minute/second/frame) structure + + One CD-ROMs addressing scheme especially used in audio formats + (Red Book) is an address by minute, sector and frame which + BCD-encoded in three bytes. An alternative format is an lba_t. + + @see lba_t + */ + PRAGMA_BEGIN_PACKED + struct msf_rec { + uint8_t m, s, f; + } GNUC_PACKED; + PRAGMA_END_PACKED + + typedef struct msf_rec msf_t; + +#define msf_t_SIZEOF 3 + + /* type used for bit-fields in structs (1 <= bits <= 8) */ +#if defined(__GNUC__) + /* this is strict ISO C99 which allows only 'unsigned int', 'signed + int' and '_Bool' explicitly as bit-field type */ + typedef unsigned int bitfield_t; +#else + /* other compilers might increase alignment requirements to match the + 'unsigned int' type -- fixme: find out how unalignment accesses can + be pragma'ed on non-gcc compilers */ + typedef uint8_t bitfield_t; +#endif + + /*! The type of a Logical Block Address. We allow for an lba to be + negative to be consistent with an lba, although I'm not sure this + this is possible. + + */ + typedef int32_t lba_t; + + /*! The type of a Logical Sector Number. Note that an lba lsn be negative + and the MMC3 specs allow for a conversion of a negative lba + + @see msf_t + */ + typedef int32_t lsn_t; + + /*! The type of a track number 0..99. */ + typedef uint8_t track_t; + + /*! + Constant for invalid track number + */ +#define CDIO_INVALID_TRACK 0xFF + + /*! The type of a session number 0..99. */ + typedef uint8_t session_t; + + /*! + Constant for invalid session number + */ +#define CDIO_INVALID_SESSION 0xFF + + /*! + Constant for invalid LBA. It is 151 less than the most negative + LBA -45150. This provide slack for the 150-frame offset in + LBA to LSN 150 conversions + */ +#define CDIO_INVALID_LBA -45301 + + /*! + Constant for invalid LSN + */ +#define CDIO_INVALID_LSN CDIO_INVALID_LBA + + /*! + Number of ASCII bytes in a media catalog number (MCN). + */ +#define CDIO_MCN_SIZE 13 + + /*! + Type to hold ASCII bytes in a media catalog number (MCN). + We include an extra 0 byte so these can be used as C strings. + */ + typedef char cdio_mcn_t[CDIO_MCN_SIZE+1]; + + + /*! + Number of ASCII bytes in International Standard Recording Codes (ISRC) + */ +#define CDIO_ISRC_SIZE 12 + + /*! + Type to hold ASCII bytes in a media catalog number (MCN). + We include an extra 0 byte so these can be used as C strings. + */ + typedef char cdio_isrc_t[CDIO_ISRC_SIZE+1]; + + typedef int cdio_fs_anal_t; + + /*! The type of an drive capability bit mask. See below for values*/ + typedef uint32_t cdio_drive_read_cap_t; + typedef uint32_t cdio_drive_write_cap_t; + typedef uint32_t cdio_drive_misc_cap_t; + + /*! + \brief Drive types returned by cdio_get_drive_cap() + + NOTE: Setting a bit here means the presence of a capability. + */ + +#define CDIO_DRIVE_CAP_ERROR 0x40000 /**< Error */ +#define CDIO_DRIVE_CAP_UNKNOWN 0x80000 /**< Dunno. It can be on if we + have only partial information + or are not completely certain + */ + +#define CDIO_DRIVE_CAP_MISC_CLOSE_TRAY 0x00001 /**< caddy systems can't + close... */ +#define CDIO_DRIVE_CAP_MISC_EJECT 0x00002 /**< but can eject. */ +#define CDIO_DRIVE_CAP_MISC_LOCK 0x00004 /**< disable manual eject */ +#define CDIO_DRIVE_CAP_MISC_SELECT_SPEED 0x00008 /**< programmable speed */ +#define CDIO_DRIVE_CAP_MISC_SELECT_DISC 0x00010 /**< select disc from + juke-box */ +#define CDIO_DRIVE_CAP_MISC_MULTI_SESSION 0x00020 /**< read sessions>1 */ +#define CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED 0x00080 /**< media changed */ +#define CDIO_DRIVE_CAP_MISC_RESET 0x00100 /**< hard reset device */ +#define CDIO_DRIVE_CAP_MCN 0x00200 /**< can read MCN */ +#define CDIO_DRIVE_CAP_ISRC 0x00200 /**< can read ISRC */ +#define CDIO_DRIVE_CAP_MISC_FILE 0x20000 /**< drive is really a file, + i.e a CD file image */ + + /*! Reading masks.. */ +#define CDIO_DRIVE_CAP_READ_AUDIO 0x00001 /**< drive can play CD audio */ +#define CDIO_DRIVE_CAP_READ_CD_DA 0x00002 /**< drive can read CD-DA */ +#define CDIO_DRIVE_CAP_READ_CD_G 0x00004 /**< drive can read CD+G */ +#define CDIO_DRIVE_CAP_READ_CD_R 0x00008 /**< drive can read CD-R */ +#define CDIO_DRIVE_CAP_READ_CD_RW 0x00010 /**< drive can read CD-RW */ +#define CDIO_DRIVE_CAP_READ_DVD_R 0x00020 /**< drive can read DVD-R */ +#define CDIO_DRIVE_CAP_READ_DVD_PR 0x00040 /**< drive can read DVD+R */ +#define CDIO_DRIVE_CAP_READ_DVD_RAM 0x00080 /**< drive can read DVD-RAM */ +#define CDIO_DRIVE_CAP_READ_DVD_ROM 0x00100 /**< drive can read DVD-ROM */ +#define CDIO_DRIVE_CAP_READ_DVD_RW 0x00200 /**< drive can read DVD-RW */ +#define CDIO_DRIVE_CAP_READ_DVD_RPW 0x00400 /**< drive can read DVD+RW */ +#define CDIO_DRIVE_CAP_READ_C2_ERRS 0x00800 /**< has C2 error correction */ + + /*! Writing masks.. */ +#define CDIO_DRIVE_CAP_WRITE_CD_R 0x00001 /**< drive can write CD-R */ +#define CDIO_DRIVE_CAP_WRITE_CD_RW 0x00002 /**< drive can write CD-R */ +#define CDIO_DRIVE_CAP_WRITE_DVD_R 0x00004 /**< drive can write DVD-R */ +#define CDIO_DRIVE_CAP_WRITE_DVD_PR 0x00008 /**< drive can write DVD+R */ +#define CDIO_DRIVE_CAP_WRITE_DVD_RAM 0x00010 /**< drive can write DVD-RAM */ +#define CDIO_DRIVE_CAP_WRITE_DVD_RW 0x00020 /**< drive can write DVD-RW */ +#define CDIO_DRIVE_CAP_WRITE_DVD_RPW 0x00040 /**< drive can write DVD+RW */ +#define CDIO_DRIVE_CAP_WRITE_MT_RAINIER 0x00080 /**< Mount Rainier */ +#define CDIO_DRIVE_CAP_WRITE_BURN_PROOF 0x00100 /**< burn proof */ + +/**< Masks derived from above... */ +#define CDIO_DRIVE_CAP_WRITE_CD ( \ + CDIO_DRIVE_CAP_WRITE_CD_R \ + | CDIO_DRIVE_CAP_WRITE_CD_RW \ + ) +/**< Has some sort of CD writer ability */ + +/**< Masks derived from above... */ +#define CDIO_DRIVE_CAP_WRITE_DVD ( \ + | CDIO_DRIVE_CAP_WRITE_DVD_R \ + | CDIO_DRIVE_CAP_WRITE_DVD_PR \ + | CDIO_DRIVE_CAP_WRITE_DVD_RAM \ + | CDIO_DRIVE_CAP_WRITE_DVD_RW \ + | CDIO_DRIVE_CAP_WRITE_DVD_RPW \ + ) +/**< Has some sort of DVD writer ability */ + +#define CDIO_DRIVE_CAP_WRITE \ + (CDIO_DRIVE_CAP_WRITE_CD | CDIO_DRIVE_CAP_WRITE_DVD) +/**< Has some sort of DVD or CD writing ability */ + + /*! + track flags + Q Sub-channel Control Field (4.2.3.3) + */ + typedef enum { + CDIO_TRACK_FLAG_NONE = 0x00, /**< no flags set */ + CDIO_TRACK_FLAG_PRE_EMPHASIS = 0x01, /**< audio track recorded with + pre-emphasis */ + CDIO_TRACK_FLAG_COPY_PERMITTED = 0x02, /**< digital copy permitted */ + CDIO_TRACK_FLAG_DATA = 0x04, /**< data track */ + CDIO_TRACK_FLAG_FOUR_CHANNEL_AUDIO = 0x08, /**< 4 audio channels */ + CDIO_TRACK_FLAG_SCMS = 0x10 /**< SCMS (5.29.2.7) */ +} cdio_track_flag; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_TYPES_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/cdio/util.h b/contrib/libcdio/cdio/util.h new file mode 100644 index 000000000..3cea313b4 --- /dev/null +++ b/contrib/libcdio/cdio/util.h @@ -0,0 +1,136 @@ +/* + $Id: util.h,v 1.3 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CDIO_UTIL_H__ +#define __CDIO_UTIL_H__ + +/*! + \file util.h + \brief Miscellaneous utility functions. + + Warning: this will probably get removed/replaced by using glib.h +*/ +#include <stdlib.h> + +#undef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#undef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#undef IN +#define IN(x, low, high) ((x) >= (low) && (x) <= (high)) + +#undef CLAMP +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +static inline unsigned +_cdio_len2blocks (unsigned len, int blocksize) +{ + unsigned blocks; + + blocks = len / blocksize; + if (len % blocksize) + blocks++; + + return blocks; +} + +/* round up to next block boundary */ +static inline unsigned +_cdio_ceil2block (unsigned offset, int blocksize) +{ + return _cdio_len2blocks (offset, blocksize) * blocksize; +} + +static inline unsigned +_cdio_ofs_add (unsigned offset, unsigned length, int blocksize) +{ + if (blocksize - (offset % blocksize) < length) + offset = _cdio_ceil2block (offset, blocksize); + + offset += length; + + return offset; +} + +static inline const char * +_cdio_bool_str (bool b) +{ + return b ? "yes" : "no"; +} + +#ifdef __cplusplus +extern "C" { +#endif + +void * +_cdio_malloc (size_t size); + +void * +_cdio_memdup (const void *mem, size_t count); + +char * +_cdio_strdup_upper (const char str[]); + +void +_cdio_strfreev(char **strv); + +char * +_cdio_strjoin (char *strv[], unsigned count, const char delim[]); + +size_t +_cdio_strlenv(char **str_array); + +char ** +_cdio_strsplit(const char str[], char delim); + +uint8_t cdio_to_bcd8(uint8_t n); +uint8_t cdio_from_bcd8(uint8_t p); + +#if defined(__GNUC__) && __GNUC__ >= 3 +static inline __attribute__((deprecated)) +uint8_t to_bcd8(uint8_t n) { + return cdio_to_bcd8(n); +} +static inline __attribute__((deprecated)) +uint8_t from_bcd8(uint8_t p) { + return cdio_from_bcd8(p); +} +#else +#define to_bcd8 cdio_to_bcd8 +#define from_bcd8 cdio_from_bcd8 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __CDIO_UTIL_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/cdio/version.h b/contrib/libcdio/cdio/version.h new file mode 100644 index 000000000..345924cab --- /dev/null +++ b/contrib/libcdio/cdio/version.h @@ -0,0 +1,10 @@ +/* $Id: version.h,v 1.2 2004/04/11 12:20:31 miguelfreitas Exp $ */ +/** \file version.h + * \brief A file simply containing the library version number. + */ + +/*! CDIO_VERSION can as a string in programs to show what version is used. */ +#define CDIO_VERSION "0.68" + +/*! LIBCDIO_VERSION_NUM can be used for testing in the C preprocessor */ +#define LIBCDIO_VERSION_NUM 68 diff --git a/contrib/libcdio/cdio/xa.h b/contrib/libcdio/cdio/xa.h new file mode 100644 index 000000000..3af27eab5 --- /dev/null +++ b/contrib/libcdio/cdio/xa.h @@ -0,0 +1,151 @@ +/* + $Id: xa.h,v 1.3 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + See also iso9660.h by Eric Youngdale (1993) and in cdrtools. These + are + + Copyright 1993 Yggdrasil Computing, Incorporated + Copyright (c) 1999,2000 J. Schilling + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*! + \file xa.h + \brief Things related to the ISO-9660 XA (Extended Attributes) format +*/ + + +#ifndef __CDIO_XA_H__ +#define __CDIO_XA_H__ + +#include <cdio/types.h> + +#define ISO_XA_MARKER_STRING "CD-XA001" +#define ISO_XA_MARKER_OFFSET 1024 + +/* XA attribute definitions */ +#define XA_PERM_RSYS 0x0001 /**< System Group Read */ +#define XA_PERM_XSYS 0x0004 /**< System Group Execute */ + +#define XA_PERM_RUSR 0x0010 /**< User (owner) Read */ +#define XA_PERM_XUSR 0x0040 /**< User (owner) Execute */ + +#define XA_PERM_RGRP 0x0100 /**< Group Read */ +#define XA_PERM_XGRP 0x0400 /**< Group Execute */ + +#define XA_PERM_ROTH 0x1000 /**< Other (world) Read */ +#define XA_PERM_XOTH 0x4000 /**< Other (world) Execute */ + +#define XA_ATTR_MODE2FORM1 (1 << 11) +#define XA_ATTR_MODE2FORM2 (1 << 12) +#define XA_ATTR_INTERLEAVED (1 << 13) +#define XA_ATTR_CDDA (1 << 14) +#define XA_ATTR_DIRECTORY (1 << 15) + +/* some aggregations */ +#define XA_PERM_ALL_READ (XA_PERM_RUSR | XA_PERM_RSYS | XA_PERM_RGRP) +#define XA_PERM_ALL_EXEC (XA_PERM_XUSR | XA_PERM_XSYS | XA_PERM_XGRP) +#define XA_PERM_ALL_ALL (XA_PERM_ALL_READ | XA_PERM_ALL_EXEC) + +#define XA_FORM1_DIR (XA_ATTR_DIRECTORY | XA_ATTR_MODE2FORM1 | XA_PERM_ALL_ALL) +#define XA_FORM1_FILE (XA_ATTR_MODE2FORM1 | XA_PERM_ALL_ALL) +#define XA_FORM2_FILE (XA_ATTR_MODE2FORM2 | XA_PERM_ALL_ALL) + +/*! \brief "Extended Architecture according to the Philips Yellow Book. + +CD-ROM EXtended Architecture is a modification to the CD-ROM +specification that defines two new types of sectors. CD-ROM XA was +developed jointly by Sony, Philips, and Microsoft, and announced in +August 1988. Its specifications were published in an extension to the +Yellow Book. CD-i, Photo CD, Video CD and CD-EXTRA have all +subsequently been based on CD-ROM XA. + +CD-XA defines another way of formatting sectors on a CD-ROM, including +headers in the sectors that describe the type (audio, video, data) and +some additional info (markers, resolution in case of a video or audio +sector, file numbers, etc). + +The data written on a CD-XA is consistent with and can be in ISO-9660 +file system format and therefore be readable by ISO-9660 file system +translators. But also a CD-I player can also read CD-XA discs even if +its own `Green Book' file system only resembles ISO 9660 and isn't +fully compatible. + + Note structure is big-endian. +*/ +typedef struct iso9660_xa +{ + uint16_t group_id; /**< 0 */ + uint16_t user_id; /**< 0 */ + uint16_t attributes; /**< XA_ATTR_ */ + uint8_t signature[2]; /**< { 'X', 'A' } */ + uint8_t filenum; /**< file number, see also XA subheader */ + uint8_t reserved[5]; /**< zero */ +} GNUC_PACKED iso9660_xa_t; + + +/*! + Returns a string which interpreting the extended attribute xa_attr. + For example: + \verbatim + d---1xrxrxr + ---2--r-r-r + -a--1xrxrxr + \endverbatim + + A description of the characters in the string follows + The 1st character is either "d" if the entry is a directory, or "-" if not + The 2nd character is either "a" if the entry is CDDA (audio), or "-" if not + The 3rd character is either "i" if the entry is interleaved, or "-" if not + The 4th character is either "2" if the entry is mode2 form2 or "-" if not + The 5th character is either "1" if the entry is mode2 form1 or "-" if not + Note that an entry will either be in mode2 form1 or mode form2. That + is you will either see "2-" or "-1" in the 4th & 5th positions. + + The 6th and 7th characters refer to permissions for a user while the + the 8th and 9th characters refer to permissions for a group while, and + the 10th and 11th characters refer to permissions for everyone. + + In each of these pairs the first character (6, 8, 10) is "x" if the + entry is executable. For a directory this means the directory is + allowed to be listed or "searched". + The second character of a pair (7, 9, 11) is "r" if the entry is allowed + to be read. +*/ +const char * +iso9660_get_xa_attr_str (uint16_t xa_attr); + +/*! + Allocates and initalizes a new iso9600_xa_t variable and returns + it. The caller should free the returned result. + + @see iso9660_xa +*/ +iso9660_xa_t * +iso9660_xa_init (iso9660_xa_t *_xa, uint16_t uid, uint16_t gid, uint16_t attr, + uint8_t filenum); + +#endif /* __CDIO_XA_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/cdio_assert.h b/contrib/libcdio/cdio_assert.h new file mode 100644 index 000000000..2433bf0c7 --- /dev/null +++ b/contrib/libcdio/cdio_assert.h @@ -0,0 +1,59 @@ +/* + $Id: cdio_assert.h,v 1.2 2004/04/11 12:20:31 miguelfreitas Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CDIO_ASSERT_H__ +#define __CDIO_ASSERT_H__ + +#if defined(__GNUC__) + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cdio/types.h> +#include <cdio/logging.h> + +#define cdio_assert(expr) \ + { \ + if (GNUC_UNLIKELY (!(expr))) cdio_log (CDIO_LOG_ASSERT, \ + "file %s: line %d (%s): assertion failed: (%s)", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ + } + +#define cdio_assert_not_reached() \ + { \ + cdio_log (CDIO_LOG_ASSERT, \ + "file %s: line %d (%s): should not be reached", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + } + +#else /* non GNU C */ + +#include <assert.h> + +#define cdio_assert(expr) \ + assert(expr) + +#define cdio_assert_not_reached() \ + assert(0) + +#endif + +#endif /* __CDIO_ASSERT_H__ */ diff --git a/contrib/libcdio/cdio_private.h b/contrib/libcdio/cdio_private.h new file mode 100644 index 000000000..b1e2777ed --- /dev/null +++ b/contrib/libcdio/cdio_private.h @@ -0,0 +1,315 @@ +/* + $Id: cdio_private.h,v 1.3 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Internal routines for CD I/O drivers. */ + + +#ifndef __CDIO_PRIVATE_H__ +#define __CDIO_PRIVATE_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cdio/cdio.h> +#include <cdio/cdtext.h> +#include "scsi_mmc_private.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* Opaque type */ + typedef struct _CdioDataSource CdioDataSource; + +#ifdef __cplusplus +} + +#endif /* __cplusplus */ + +#include "generic.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + typedef struct { + + /*! + Eject media in CD drive. If successful, as a side effect we + also free obj. Return 0 if success and 1 for failure. + */ + int (*eject_media) (void *env); + + /*! + Release and free resources associated with cd. + */ + void (*free) (void *env); + + /*! + Return the value associated with the key "arg". + */ + const char * (*get_arg) (void *env, const char key[]); + + /*! + Get cdtext information for a CdIo object. + + @param obj the CD object that may contain CD-TEXT information. + @return the CD-TEXT object or NULL if obj is NULL + or CD-TEXT information does not exist. + + If i_track is 0 or CDIO_CDROM_LEADOUT_TRACK the track returned + is the information assocated with the CD. + */ + const cdtext_t * (*get_cdtext) (void *env, track_t i_track); + + /*! + Return an array of device names. if CdIo is NULL (we haven't + initialized a specific device driver), then find a suitable device + driver. + + NULL is returned if we couldn't return a list of devices. + */ + char ** (*get_devices) (void); + + /*! + Return a string containing the default CD device if none is specified. + */ + char * (*get_default_device)(void); + + /*! + Get disc mode associated with cd_obj. + */ + discmode_t (*get_discmode) (void *p_env); + + /*! + Return the what kind of device we've got. + + See cd_types.h for a list of bitmasks for the drive type; + */ + void (*get_drive_cap) (const void *env, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap); + /*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. + */ + track_t (*get_first_track_num) (void *p_env); + + /*! + Get the CD-ROM hardware info via a SCSI MMC INQUIRY command. + False is returned if we had an error getting the information. + */ + bool (*get_hwinfo) ( const CdIo *p_cdio, + /* out*/ cdio_hwinfo_t *p_hw_info ); + + /*! + Return the media catalog number MCN from the CD or NULL if + there is none or we don't have the ability to get it. + */ + char * (*get_mcn) (const void *env); + + /*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. + */ + track_t (*get_num_tracks) (void *env); + + /*! + Return the starting LBA for track number + track_num in obj. Tracks numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + CDIO_INVALID_LBA is returned on error. + */ + lba_t (*get_track_lba) (void *env, track_t track_num); + + /*! + Get format of track. + */ + track_format_t (*get_track_format) (void *env, track_t track_num); + + /*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? + */ + bool (*get_track_green) (void *env, track_t track_num); + + /*! + Return the starting MSF (minutes/secs/frames) for track number + track_num in obj. Tracks numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + False is returned on error. + */ + bool (*get_track_msf) (void *env, track_t track_num, msf_t *msf); + + /*! + lseek - reposition read/write file offset + Returns (off_t) -1 on error. + Similar to libc's lseek() + */ + off_t (*lseek) (void *env, off_t offset, int whence); + + /*! + Reads into buf the next size bytes. + Returns -1 on error. + Similar to libc's read() + */ + ssize_t (*read) (void *env, void *buf, size_t size); + + /*! + Reads a single mode2 sector from cd device into buf starting + from lsn. Returns 0 if no error. + */ + int (*read_audio_sectors) (void *env, void *buf, lsn_t lsn, + unsigned int nblocks); + + /*! + Reads a single mode2 sector from cd device into buf starting + from lsn. Returns 0 if no error. + */ + int (*read_mode2_sector) (void *env, void *buf, lsn_t lsn, + bool mode2_form2); + + /*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ + int (*read_mode2_sectors) (void *p_env, void *p_buf, lsn_t lsn, + bool mode2_form2, unsigned int nblocks); + + /*! + Reads a single mode1 sector from cd device into buf starting + from lsn. Returns 0 if no error. + */ + int (*read_mode1_sector) (void *p_env, void *p_buf, lsn_t lsn, + bool mode1_form2); + + /*! + Reads nblocks of mode1 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ + int (*read_mode1_sectors) (void *p_env, void *p_buf, lsn_t lsn, + bool mode1_form2, unsigned int nblocks); + + bool (*read_toc) ( void *p_env ) ; + + /*! + Run a SCSI MMC command. + + cdio CD structure set by cdio_open(). + i_timeout_ms time in milliseconds we will wait for the command + to complete. + cdb_len number of bytes in cdb (6, 10, or 12). + cdb CDB bytes. All values that are needed should be set on + input. + b_return_data TRUE if the command expects data to be returned in + the buffer + len Size of buffer + buf Buffer for data, both sending and receiving + + Returns 0 if command completed successfully. + */ + scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd; + + /*! + Set the arg "key" with "value" in the source device. + */ + int (*set_arg) (void *env, const char key[], const char value[]); + + /*! + Return the size of the CD in logical block address (LBA) units. + */ + uint32_t (*stat_size) (void *env); + + } cdio_funcs; + + + /*! Implementation of CdIo type */ + struct _CdIo { + driver_id_t driver_id; /**< Particular driver opened. */ + cdio_funcs op; /**< driver-specific routines handling + implementation*/ + void *env; /**< environment. Passed to routine above. */ + }; + + /* This is used in drivers that must keep their own internal + position pointer for doing seeks. Stream-based drivers (like bincue, + nrg, toc, network) would use this. + */ + typedef struct + { + off_t buff_offset; /* buffer offset in disk-image seeks. */ + track_t index; /* Current track index in tocent. */ + lba_t lba; /* Current LBA */ + } internal_position_t; + + CdIo * cdio_new (generic_img_private_t *p_env, cdio_funcs *funcs); + + /* The below structure describes a specific CD Input driver */ + typedef struct + { + driver_id_t id; + unsigned int flags; + const char *name; + const char *describe; + bool (*have_driver) (void); + CdIo *(*driver_open) (const char *psz_source_name); + CdIo *(*driver_open_am) (const char *psz_source_name, + const char *psz_access_mode); + char *(*get_default_device) (void); + bool (*is_device) (const char *psz_source_name); + char **(*get_devices) (void); + } CdIo_driver_t; + + /* The below array gives of the drivers that are currently available for + on a particular host. */ + extern CdIo_driver_t CdIo_driver[CDIO_MAX_DRIVER]; + + /* The last valid entry of Cdio_driver. -1 means uninitialzed. -2 + means some sort of error. + */ + extern int CdIo_last_driver; + + /* The below array gives all drivers that can possibly appear. + on a particular host. */ + extern CdIo_driver_t CdIo_all_drivers[CDIO_MAX_DRIVER+1]; + + /*! + Add/allocate a drive to the end of drives. + Use cdio_free_device_list() to free this device_list. + */ + void cdio_add_device_list(char **device_list[], const char *drive, + unsigned int *i_drives); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_PRIVATE_H__ */ diff --git a/contrib/libcdio/cdtext.c b/contrib/libcdio/cdtext.c new file mode 100644 index 000000000..5842641f7 --- /dev/null +++ b/contrib/libcdio/cdtext.c @@ -0,0 +1,228 @@ +/* + $Id: cdtext.c,v 1.1 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + toc reading routine adapted from cuetools + Copyright (C) 2003 Svend Sanjay Sorensen <ssorensen@fastmail.fm> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cdio/cdtext.h> +#include <cdio/logging.h> +#include "cdtext_private.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +/*! Note: the order and number items (except CDTEXT_INVALID) should + match the cdtext_field_t enumeration. */ +const char *cdtext_keywords[] = + { + "ARRANGER", + "COMPOSER", + "DISC_ID", + "GENRE", + "ISRC", + "MESSAGE", + "PERFORMER", + "SIZE_INFO", + "SONGWRITER", + "TITLE", + "TOC_INFO", + "TOC_INFO2", + "UPC_EAN", + }; + + +/*! Return string representation of the enum values above */ +const char * +cdtext_field2str (cdtext_field_t i) +{ + if (i >= MAX_CDTEXT_FIELDS) + return "Invalid CDTEXT field index"; + else + return cdtext_keywords[i]; +} + + +/*! Free memory assocated with cdtext*/ +void +cdtext_destroy (cdtext_t *cdtext) +{ + cdtext_field_t i; + + for (i=0; i < MAX_CDTEXT_FIELDS; i++) { + if (cdtext->field[i]) free(cdtext->field[i]); + } +} + +/*! + returns the CDTEXT value associated with key. NULL is returned + if key is CDTEXT_INVALID or the field is not set. + */ +const char * +cdtext_get (cdtext_field_t key, const cdtext_t *cdtext) +{ + if (key == CDTEXT_INVALID) return NULL; + return cdtext->field[key]; +} + +/*! Initialize a new cdtext structure. + When the structure is no longer needed, release the + resources using cdtext_delete. +*/ +void +cdtext_init (cdtext_t *cdtext) +{ + cdtext_field_t i; + + for (i=0; i < MAX_CDTEXT_FIELDS; i++) { + cdtext->field[i] = NULL; + } +} + +/*! + returns 0 if field is a CD-TEXT keyword, returns non-zero otherwise +*/ +cdtext_field_t +cdtext_is_keyword (const char *key) +{ +#if 0 + char *item; + + item = bsearch(key, + cdtext_keywords, 12, + sizeof (char *), + (int (*)(const void *, const void *)) + strcmp); + return (NULL != item) ? 0 : 1; +#else + unsigned int i; + + for (i = 0; i < 13 ; i++) + if (0 == strcmp (cdtext_keywords[i], key)) { + return i; + } + return CDTEXT_INVALID; +#endif +} + +/*! sets cdtext's keyword entry to field. + */ +void +cdtext_set (cdtext_field_t key, const char *value, cdtext_t *cdtext) +{ + if (NULL == value || key == CDTEXT_INVALID) return; + + if (cdtext->field[key]) free (cdtext->field[key]); + cdtext->field[key] = strdup (value); + +} + +#define SET_CDTEXT_FIELD(FIELD) \ + (*set_cdtext_field_fn)(user_data, i_track, i_first_track, FIELD, buffer); + +/* + parse all CD-TEXT data retrieved. +*/ +bool +cdtext_data_init(void *user_data, track_t i_first_track, + unsigned char *wdata, + set_cdtext_field_fn_t set_cdtext_field_fn) +{ + CDText_data_t *pdata; + int i; + int j; + char buffer[256]; + int idx; + int i_track; + bool b_ret = false; + + memset( buffer, 0x00, sizeof(buffer) ); + idx = 0; + + pdata = (CDText_data_t *) (&wdata[4]); + for( i=0; i < CDIO_CDTEXT_MAX_PACK_DATA; i++ ) { + +#if TESTED + if ( pdata->bDBC ) { + cdio_warn("Double-byte characters not supported"); + return false; + } +#endif + + if( pdata->seq != i ) + break; + + if( (pdata->type >= 0x80) + && (pdata->type <= 0x85) && (pdata->block == 0) ) { + i_track = pdata->i_track; + + for( j=0; j < CDIO_CDTEXT_MAX_TEXT_DATA; j++ ) { + if( pdata->text[j] == 0x00 ) { + bool b_field_set=true; + switch( pdata->type) { + case CDIO_CDTEXT_TITLE: + SET_CDTEXT_FIELD(CDTEXT_TITLE); + break; + case CDIO_CDTEXT_PERFORMER: + SET_CDTEXT_FIELD(CDTEXT_PERFORMER); + break; + case CDIO_CDTEXT_SONGWRITER: + SET_CDTEXT_FIELD(CDTEXT_SONGWRITER); + break; + case CDIO_CDTEXT_COMPOSER: + SET_CDTEXT_FIELD(CDTEXT_COMPOSER); + break; + case CDIO_CDTEXT_ARRANGER: + SET_CDTEXT_FIELD(CDTEXT_ARRANGER); + break; + case CDIO_CDTEXT_MESSAGE: + SET_CDTEXT_FIELD(CDTEXT_MESSAGE); + break; + case CDIO_CDTEXT_DISCID: + SET_CDTEXT_FIELD(CDTEXT_DISCID); + break; + case CDIO_CDTEXT_GENRE: + SET_CDTEXT_FIELD(CDTEXT_GENRE); + break; + default : b_field_set = false; + } + if (b_field_set) { + b_ret = true; + i_track++; + idx = 0; + } + } else { + buffer[idx++] = pdata->text[j]; + } + buffer[idx] = 0x00; + } + } + pdata++; + } + return b_ret; +} + diff --git a/contrib/libcdio/cdtext_private.h b/contrib/libcdio/cdtext_private.h new file mode 100644 index 000000000..03a9c4945 --- /dev/null +++ b/contrib/libcdio/cdtext_private.h @@ -0,0 +1,119 @@ +/* + $Id: cdtext_private.h,v 1.1 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CDIO_CDTEXT_PRIVATE_H__ +#define __CDIO_CDTEXT_PRIVATE_H__ + +#include <cdio/cdio.h> +#include <cdio/cdtext.h> + +#define CDIO_CDTEXT_MAX_PACK_DATA 255 +#define CDIO_CDTEXT_MAX_TEXT_DATA 12 + +/* From table J.2 - Pack Type Indicator Definitions from + Working Draft NCITS XXX T10/1364-D Revision 10G. November 12, 2001. +*/ +/* Title of Alubm name (ID=0) or Track Titles (ID != 0) */ +#define CDIO_CDTEXT_TITLE 0x80 + +/* Name(s) of the performer(s) in ASCII */ +#define CDIO_CDTEXT_PERFORMER 0x81 + +/* Name(s) of the songwriter(s) in ASCII */ +#define CDIO_CDTEXT_SONGWRITER 0x82 + +/* Name(s) of the Composers in ASCII */ +#define CDIO_CDTEXT_COMPOSER 0x83 + +/* Name(s) of the Arrangers in ASCII */ +#define CDIO_CDTEXT_ARRANGER 0x84 + +/* Message(s) from content provider and/or artist in ASCII */ +#define CDIO_CDTEXT_MESSAGE 0x85 + +/* Disc Identificatin information */ +#define CDIO_CDTEXT_DISCID 0x86 + +/* Genre Identification and Genre Information */ +#define CDIO_CDTEXT_GENRE 0x87 + +/* Table of Content Information */ +#define CDIO_CDTEXT_TOC 0x88 + +/* Second Table of Content Information */ +#define CDIO_CDTEXT_TOC2 0x89 + +/* 0x8A, 0x8B, 0x8C are reserved + 0x8D Reserved for content provider only. + */ + +/* UPC/EAN code of the album and ISRC code of each track */ +#define CDIO_CDTEXT_UPC 0x8E + +/* Size information of the Block */ +#define CDIO_CDTEXT_BLOCKSIZE 0x8F + + +PRAGMA_BEGIN_PACKED + +struct CDText_data +{ + uint8_t type; + track_t i_track; + uint8_t seq; +#ifdef WORDS_BIGENDIAN + uint8_t bDBC: 1; /* double byte character */ + uint8_t block: 3; /* block number 0..7 */ + uint8_t characterPosition:4; /* character position */ +#else + uint8_t characterPosition:4; /* character position */ + uint8_t block :3; /* block number 0..7 */ + uint8_t bDBC :1; /* double byte character */ +#endif + char text[CDIO_CDTEXT_MAX_TEXT_DATA]; + uint8_t crc[2]; +} GNUC_PACKED; + +PRAGMA_END_PACKED + +typedef struct CDText_data CDText_data_t; + +typedef void (*set_cdtext_field_fn_t) (void *user_data, track_t i_track, + track_t i_first_track, + cdtext_field_t field, + const char *buffer); + +/* + Internal routine to parse all CD-TEXT data retrieved. +*/ +bool cdtext_data_init(void *user_data, track_t i_first_track, + unsigned char *wdata, + set_cdtext_field_fn_t set_cdtext_field_fn); + + +#endif /* __CDIO_CDTEXT_PRIVATE_H__ */ + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/ds.c b/contrib/libcdio/ds.c new file mode 100644 index 000000000..381c0f00c --- /dev/null +++ b/contrib/libcdio/ds.c @@ -0,0 +1,249 @@ +/* + $Id: ds.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include <cdio/ds.h> +#include <cdio/util.h> +#include <cdio/types.h> +#include "cdio_assert.h" + +static const char _rcsid[] = "$Id: ds.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $"; + +struct _CdioList +{ + unsigned length; + + CdioListNode *begin; + CdioListNode *end; +}; + +struct _CdioListNode +{ + CdioList *list; + + CdioListNode *next; + + void *data; +}; + +/* impl */ + +CdioList * +_cdio_list_new (void) +{ + CdioList *new_obj = _cdio_malloc (sizeof (CdioList)); + + return new_obj; +} + +void +_cdio_list_free (CdioList *list, int free_data) +{ + while (_cdio_list_length (list)) + _cdio_list_node_free (_cdio_list_begin (list), free_data); + + free (list); +} + +unsigned +_cdio_list_length (const CdioList *list) +{ + cdio_assert (list != NULL); + + return list->length; +} + +void +_cdio_list_prepend (CdioList *list, void *data) +{ + CdioListNode *new_node; + + cdio_assert (list != NULL); + + new_node = _cdio_malloc (sizeof (CdioListNode)); + + new_node->list = list; + new_node->next = list->begin; + new_node->data = data; + + list->begin = new_node; + if (list->length == 0) + list->end = new_node; + + list->length++; +} + +void +_cdio_list_append (CdioList *list, void *data) +{ + cdio_assert (list != NULL); + + if (list->length == 0) + { + _cdio_list_prepend (list, data); + } + else + { + CdioListNode *new_node = _cdio_malloc (sizeof (CdioListNode)); + + new_node->list = list; + new_node->next = NULL; + new_node->data = data; + + list->end->next = new_node; + list->end = new_node; + + list->length++; + } +} + +void +_cdio_list_foreach (CdioList *list, _cdio_list_iterfunc func, void *user_data) +{ + CdioListNode *node; + + cdio_assert (list != NULL); + cdio_assert (func != 0); + + for (node = _cdio_list_begin (list); + node != NULL; + node = _cdio_list_node_next (node)) + func (_cdio_list_node_data (node), user_data); +} + +CdioListNode * +_cdio_list_find (CdioList *list, _cdio_list_iterfunc cmp_func, void *user_data) +{ + CdioListNode *node; + + cdio_assert (list != NULL); + cdio_assert (cmp_func != 0); + + for (node = _cdio_list_begin (list); + node != NULL; + node = _cdio_list_node_next (node)) + if (cmp_func (_cdio_list_node_data (node), user_data)) + break; + + return node; +} + +CdioListNode * +_cdio_list_begin (const CdioList *list) +{ + cdio_assert (list != NULL); + + return list->begin; +} + +CdioListNode * +_cdio_list_end (CdioList *list) +{ + cdio_assert (list != NULL); + + return list->end; +} + +CdioListNode * +_cdio_list_node_next (CdioListNode *node) +{ + if (node) + return node->next; + + return NULL; +} + +void +_cdio_list_node_free (CdioListNode *node, int free_data) +{ + CdioList *list; + CdioListNode *prev_node; + + cdio_assert (node != NULL); + + list = node->list; + + cdio_assert (_cdio_list_length (list) > 0); + + if (free_data) + free (_cdio_list_node_data (node)); + + if (_cdio_list_length (list) == 1) + { + cdio_assert (list->begin == list->end); + + list->end = list->begin = NULL; + list->length = 0; + free (node); + return; + } + + cdio_assert (list->begin != list->end); + + if (list->begin == node) + { + list->begin = node->next; + free (node); + list->length--; + return; + } + + for (prev_node = list->begin; prev_node->next; prev_node = prev_node->next) + if (prev_node->next == node) + break; + + cdio_assert (prev_node->next != NULL); + + if (list->end == node) + list->end = prev_node; + + prev_node->next = node->next; + + list->length--; + + free (node); +} + +void * +_cdio_list_node_data (CdioListNode *node) +{ + if (node) + return node->data; + + return NULL; +} + +/* eof */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ + diff --git a/contrib/libcdio/generic.h b/contrib/libcdio/generic.h new file mode 100644 index 000000000..9deb77254 --- /dev/null +++ b/contrib/libcdio/generic.h @@ -0,0 +1,179 @@ +/* + $Id: generic.h,v 1.1 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Internal routines for CD I/O drivers. */ + + +#ifndef __CDIO_GENERIC_H__ +#define __CDIO_GENERIC_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cdio/cdio.h> +#include <cdio/cdtext.h> +#include <cdio/iso9660.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*! + Things common to private device structures. Even though not all + devices may have some of these fields, by listing common ones + we facilitate writing generic routines and even cut-and-paste + code. + */ + typedef struct { + char *source_name; /**< Name used in open. */ + bool init; /**< True if structure has been initialized */ + bool toc_init; /**< True if TOC read in */ + bool b_cdtext_init; /**< True if CD-Text read in */ + bool b_cdtext_error; /**< True if trouble reading CD-Text */ + + int ioctls_debugged; /**< for debugging */ + + /* Only one of data_source or fd is used; fd is for CD-ROM + devices and the data_source for stream reading (bincue, nrg, toc, + network). + */ + CdioDataSource *data_source; + int fd; /**< File descriptor of device */ + track_t i_first_track; /**< The starting track number. */ + track_t i_tracks; /**< The number of tracks. */ + + uint8_t i_joliet_level; /**< 0 = no Joliet extensions. + 1-3: Joliet level. */ + iso9660_pvd_t pvd; + iso9660_svd_t svd; + CdIo *cdio; /**< a way to call general cdio routines. */ + cdtext_t cdtext; /**< CD-Text for disc. */ + cdtext_t cdtext_track[CDIO_CD_MAX_TRACKS+1]; /*CD-TEXT for each track*/ + + } generic_img_private_t; + + /*! + Bogus eject media when there is no ejectable media, e.g. a disk image + We always return 2. Should we also free resources? + */ + int cdio_generic_bogus_eject_media (void *env); + + /*! + Release and free resources associated with cd. + */ + void cdio_generic_free (void *env); + + /*! + Initialize CD device. + */ + bool cdio_generic_init (void *env); + + /*! + Reads into buf the next size bytes. + Returns -1 on error. + Is in fact libc's read(). + */ + off_t cdio_generic_lseek (void *env, off_t offset, int whence); + + /*! + Reads into buf the next size bytes. + Returns -1 on error. + Is in fact libc's read(). + */ + ssize_t cdio_generic_read (void *env, void *buf, size_t size); + + /*! + Reads a single form1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ + int cdio_generic_read_form1_sector (void * user_data, void *data, + lsn_t lsn); + + /*! + Release and free resources associated with stream or disk image. + */ + void cdio_generic_stdio_free (void *env); + + /*! + Return true if source_name could be a device containing a CD-ROM on + Win32 + */ + bool cdio_is_device_win32(const char *source_name); + + + /*! + Return true if source_name could be a device containing a CD-ROM on + most Unix servers with block and character devices. + */ + bool cdio_is_device_generic(const char *source_name); + + + /*! + Like above, but don't give a warning device doesn't exist. + */ + bool cdio_is_device_quiet_generic(const char *source_name); + + /*! + Get cdtext information for a CdIo object . + + @param obj the CD object that may contain CD-TEXT information. + @return the CD-TEXT object or NULL if obj is NULL + or CD-TEXT information does not exist. + */ + const cdtext_t *get_cdtext_generic (void *p_user_data, track_t i_track); + + /*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. + */ + track_t get_first_track_num_generic(void *p_user_data); + + /*! + Return the number of tracks in the current medium. + */ + track_t get_num_tracks_generic(void *p_user_data); + + /*! + Get disc type associated with cd object. + */ + discmode_t get_discmode_generic (void *p_user_data ); + + /*! + Same as above but only handles CD cases + */ + discmode_t get_discmode_cd_generic (void *p_user_data ); + + void set_cdtext_field_generic(void *user_data, track_t i_track, + track_t i_first_track, + cdtext_field_t e_field, const char *psz_value); + /*! + Read cdtext information for a CdIo object . + + return true on success, false on error or CD-Text information does + not exist. + */ + bool init_cdtext_generic (generic_img_private_t *p_env); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CDIO_GENERIC_H__ */ diff --git a/contrib/libcdio/image.h b/contrib/libcdio/image.h new file mode 100644 index 000000000..79c836d32 --- /dev/null +++ b/contrib/libcdio/image.h @@ -0,0 +1,75 @@ +/* + $Id: image.h,v 1.1 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*! + Header for image drivers. In contrast to image_common.h which contains + routines, this header like most C headers does not depend on anything + defined before it is included. +*/ + +#ifndef __CDIO_IMAGE_H__ +#define __CDIO_IMAGE_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cdio/types.h> +#include <cdio/cdtext.h> +#include "cdio_private.h" +#include <cdio/sector.h> + +/*! + The universal format for information about a track for CD image readers + It may be that some fields can be derived from other fields. + Over time this structure may get cleaned up. Possibly this can be + expanded/reused for real CD formats. +*/ + +typedef struct { + track_t track_num; /**< Probably is index+1 */ + msf_t start_msf; + lba_t start_lba; + int start_index; + lba_t length; + lba_t pregap; /**< pre-gap with zero audio data */ + int sec_count; /**< Number of sectors in this track. Does not + include pregap */ + int num_indices; + flag_t flags; /**< "[NO] COPY", "4CH", "[NO] PREMPAHSIS" */ + char *isrc; /**< IRSC Code (5.22.4) exactly 12 bytes */ + char *filename; + CdioDataSource *data_source; + track_format_t track_format; + bool track_green; + cdtext_t cdtext; /**< CD-TEXT */ + + trackmode_t mode; + uint16_t datasize; /**< How much is in the portion we return + back? */ + uint16_t datastart; /**< Offset from begining that data starts */ + uint16_t endsize; /**< How much stuff at the end to skip over. + This stuff may have error correction + (EDC, or ECC).*/ + uint16_t blocksize; /**< total block size = start + size + end */ +} track_info_t; + + +#endif /* __CDIO_IMAGE_H__ */ diff --git a/contrib/libcdio/image/Makefile.am b/contrib/libcdio/image/Makefile.am new file mode 100644 index 000000000..e7a09e980 --- /dev/null +++ b/contrib/libcdio/image/Makefile.am @@ -0,0 +1,3 @@ +include $(top_srcdir)/misc/Makefile.common + +EXTRA_DIST = bincue.c nrg.c diff --git a/contrib/libcdio/image/bincue.c b/contrib/libcdio/image/bincue.c new file mode 100644 index 000000000..56f0e151c --- /dev/null +++ b/contrib/libcdio/image/bincue.c @@ -0,0 +1,1214 @@ +/* + $Id: bincue.c,v 1.2 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2002, 2003, 2004 Rocky Bernstein <rocky@panix.com> + Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org> + cue parsing routine adapted from cuetools + Copyright (C) 2003 Svend Sanjay Sorensen <ssorensen@fastmail.fm> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This code implements low-level access functions for a CD images + residing inside a disk file (*.bin) and its associated cue sheet. + (*.cue). +*/ + +static const char _rcsid[] = "$Id: bincue.c,v 1.2 2005/01/01 02:43:58 rockyb Exp $"; + +#include "image.h" +#include "cdio_assert.h" +#include "cdio_private.h" +#include "_cdio_stdio.h" + +#include <cdio/logging.h> +#include <cdio/util.h> +#include <cdio/version.h> + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_GLOB_H +#include <glob.h> +#endif +#include <ctype.h> + +#include "portable.h" +/* reader */ + +#define DEFAULT_CDIO_DEVICE "videocd.bin" +#define DEFAULT_CDIO_CUE "videocd.cue" + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + internal_position_t pos; + + char *psz_cue_name; + char *psz_mcn; /* Media Catalog Number (5.22.3) + exactly 13 bytes */ + track_info_t tocent[CDIO_CD_MAX_TRACKS+1]; /* entry info for each track + add 1 for leadout. */ + discmode_t disc_mode; +} _img_private_t; + +static uint32_t _stat_size_bincue (void *user_data); +static bool parse_cuefile (_img_private_t *cd, const char *toc_name); + +#define NEED_MEDIA_EJECT_IMAGE +#include "image_common.h" + +/*! + Initialize image structures. + */ +static bool +_init_bincue (_img_private_t *env) +{ + lsn_t lead_lsn; + + if (env->gen.init) + return false; + + if (!(env->gen.data_source = cdio_stdio_new (env->gen.source_name))) { + cdio_warn ("init failed"); + return false; + } + + /* Have to set init before calling _stat_size_bincue() 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)); + + lead_lsn = _stat_size_bincue( (_img_private_t *) env); + + if (-1 == lead_lsn) return false; + + if ((env->psz_cue_name == NULL)) return false; + + /* Read in CUE sheet. */ + if ( !parse_cuefile(env, env->psz_cue_name) ) 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 - env->gen.i_first_track].start_lba); + + return true; +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + Would be libc's seek() but we have to adjust for the extra track header + information in each sector. +*/ +static off_t +_lseek_bincue (void *user_data, off_t offset, int whence) +{ + _img_private_t *env = user_data; + + /* real_offset is the real byte offset inside the disk image + The number below was determined empirically. I'm guessing + the 1st 24 bytes of a bin file are used for something. + */ + off_t real_offset=0; + + unsigned int i; + + env->pos.lba = 0; + for (i=0; i<env->gen.i_tracks; i++) { + track_info_t *this_track=&(env->tocent[i]); + env->pos.index = i; + if ( (this_track->sec_count*this_track->datasize) >= offset) { + int blocks = offset / this_track->datasize; + int rem = offset % this_track->datasize; + int block_offset = blocks * this_track->blocksize; + real_offset += block_offset + rem; + env->pos.buff_offset = rem; + env->pos.lba += blocks; + break; + } + real_offset += this_track->sec_count*this_track->blocksize; + offset -= this_track->sec_count*this_track->datasize; + env->pos.lba += this_track->sec_count; + } + + if (i==env->gen.i_tracks) { + cdio_warn ("seeking outside range of disk image"); + return -1; + } else { + real_offset += env->tocent[i].datastart; + return cdio_stream_seek(env->gen.data_source, real_offset, whence); + } +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + FIXME: + At present we assume a read doesn't cross sector or track + boundaries. +*/ +static ssize_t +_read_bincue (void *user_data, void *data, size_t size) +{ + _img_private_t *env = user_data; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + char *p = data; + ssize_t final_size=0; + ssize_t this_size; + track_info_t *this_track=&(env->tocent[env->pos.index]); + ssize_t skip_size = this_track->datastart + this_track->endsize; + + while (size > 0) { + long int rem = this_track->datasize - env->pos.buff_offset; + if ((long int) size <= rem) { + this_size = cdio_stream_read(env->gen.data_source, buf, size, 1); + final_size += this_size; + memcpy (p, buf, this_size); + break; + } + + /* Finish off reading this sector. */ + cdio_warn ("Reading across block boundaries not finished"); + + size -= rem; + this_size = cdio_stream_read(env->gen.data_source, buf, rem, 1); + final_size += this_size; + memcpy (p, buf, this_size); + p += this_size; + this_size = cdio_stream_read(env->gen.data_source, buf, rem, 1); + + /* Skip over stuff at end of this sector and the beginning of the next. + */ + cdio_stream_read(env->gen.data_source, buf, skip_size, 1); + + /* Get ready to read another sector. */ + env->pos.buff_offset=0; + env->pos.lba++; + + /* Have gone into next track. */ + if (env->pos.lba >= env->tocent[env->pos.index+1].start_lba) { + env->pos.index++; + this_track=&(env->tocent[env->pos.index]); + skip_size = this_track->datastart + this_track->endsize; + } + } + return final_size; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static uint32_t +_stat_size_bincue (void *user_data) +{ + _img_private_t *env = user_data; + long size; + + size = cdio_stream_stat (env->gen.data_source); + + if (size % CDIO_CD_FRAMESIZE_RAW) + { + cdio_warn ("image %s size (%ld) not multiple of blocksize (%d)", + env->gen.source_name, size, CDIO_CD_FRAMESIZE_RAW); + if (size % M2RAW_SECTOR_SIZE == 0) + cdio_warn ("this may be a 2336-type disc image"); + else if (size % CDIO_CD_FRAMESIZE_RAW == 0) + cdio_warn ("this may be a 2352-type disc image"); + /* exit (EXIT_FAILURE); */ + } + + size /= CDIO_CD_FRAMESIZE_RAW; + + return size; +} + +#define MAXLINE 4096 /* maximum line length + 1 */ + +static bool +parse_cuefile (_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; + cdio_log_level_t log_level = (NULL == cd) ? CDIO_LOG_INFO : CDIO_LOG_WARN; + cdtext_field_t cdtext_key; + + /* The below declarations may be unique to this image-parse routine. */ + int start_index; + bool b_first_index_for_track=false; + + if (NULL == psz_cue_name) + return false; + + fp = fopen (psz_cue_name, "r"); + if (fp == NULL) { + cdio_log(log_level, "error opening %s for reading: %s", + psz_cue_name, strerror(errno)); + return false; + } + + if (cd) { + cd->gen.i_tracks=0; + cd->gen.i_first_track=1; + cd->gen.b_cdtext_init = true; + cd->gen.b_cdtext_error = false; + cd->psz_mcn=NULL; + } + + while ((fgets(psz_line, MAXLINE, fp)) != NULL) { + + i_line++; + + if (NULL != (psz_keyword = strtok (psz_line, " \t\n\r"))) { + /* REM remarks ... */ + if (0 == strcmp ("REM", psz_keyword)) { + ; + + /* global section */ + /* CATALOG ddddddddddddd */ + } else if (0 == strcmp ("CATALOG", psz_keyword)) { + if (-1 == i) { + if (NULL == (psz_field = strtok (NULL, " \t\n\r"))) { + cdio_log(log_level, + "%s line %d after word CATALOG: ", + psz_cue_name, i_line); + cdio_log(log_level, + "expecting 13-digit media catalog number, got nothing."); + goto err_exit; + } + if (strlen(psz_field) != 13) { + 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 i; + for (i=0; i<13; i++) { + if (!isdigit(psz_field[i])) { + 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[i], i+1, psz_field); + goto err_exit; + } + } + } + + if (cd) cd->psz_mcn = strdup (psz_field); + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + } else { + goto not_in_global_section; + } + + /* FILE "<filename>" <BINARY|WAVE|other?> */ + } else if (0 == strcmp ("FILE", psz_keyword)) { + if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) { + if (cd) cd->tocent[i + 1].filename = strdup (psz_field); + } else { + goto format_error; + } + + /* TRACK N <mode> */ + } else if (0 == strcmp ("TRACK", psz_keyword)) { + int i_track; + + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + if (1!=sscanf(psz_field, "%d", &i_track)) { + cdio_log(log_level, + "%s line %d after word TRACK:", + psz_cue_name, i_line); + cdio_log(log_level, + "Expecting a track number, got %s", psz_field); + goto err_exit; + } + } + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + track_info_t *this_track=NULL; + + if (cd) { + this_track = &(cd->tocent[cd->gen.i_tracks]); + this_track->track_num = cd->gen.i_tracks; + this_track->num_indices = 0; + b_first_index_for_track = false; + cdtext_init (&(cd->gen.cdtext_track[cd->gen.i_tracks])); + cd->gen.i_tracks++; + } + i++; + + if (0 == strcmp ("AUDIO", psz_field)) { + if (cd) { + this_track->mode = AUDIO; + this_track->blocksize = CDIO_CD_FRAMESIZE_RAW; + this_track->datasize = CDIO_CD_FRAMESIZE_RAW; + this_track->datastart = 0; + this_track->endsize = 0; + this_track->track_format = TRACK_FORMAT_AUDIO; + this_track->track_green = false; + 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/2048", psz_field)) { + if (cd) { + this_track->mode = MODE1; + this_track->blocksize = 2048; + this_track->track_format= TRACK_FORMAT_DATA; + this_track->track_green = false; + /* Is the below correct? */ + this_track->datastart = 0; + this_track->datasize = CDIO_CD_FRAMESIZE; + this_track->endsize = 0; + 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/2352", psz_field)) { + if (cd) { + this_track->blocksize = 2352; + this_track->track_format= TRACK_FORMAT_DATA; + this_track->track_green = false; + this_track->datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE; + this_track->datasize = CDIO_CD_FRAMESIZE; + this_track->endsize = CDIO_CD_EDC_SIZE + + CDIO_CD_M1F1_ZERO_SIZE + CDIO_CD_ECC_SIZE; + this_track->mode = MODE1_RAW; + 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/2336", psz_field)) { + if (cd) { + this_track->blocksize = 2336; + this_track->track_format= TRACK_FORMAT_XA; + this_track->track_green = true; + this_track->mode = MODE2; + this_track->datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE; + this_track->datasize = M2RAW_SECTOR_SIZE; + this_track->endsize = 0; + 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/2048", psz_field)) { + if (cd) { + this_track->blocksize = 2048; + this_track->track_format= TRACK_FORMAT_XA; + this_track->track_green = true; + this_track->mode = MODE2_FORM1; + 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/2324", psz_field)) { + if (cd) { + this_track->blocksize = 2324; + this_track->track_format= TRACK_FORMAT_XA; + this_track->track_green = true; + this_track->mode = MODE2_FORM2; + 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/2336", psz_field)) { + if (cd) { + this_track->blocksize = 2336; + this_track->track_format= TRACK_FORMAT_XA; + this_track->track_green = true; + this_track->mode = MODE2_FORM_MIX; + this_track->datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE; + this_track->datasize = M2RAW_SECTOR_SIZE; + this_track->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/2352", psz_field)) { + if (cd) { + this_track->blocksize = 2352; + this_track->track_format= TRACK_FORMAT_XA; + this_track->track_green = true; + this_track->mode = MODE2_RAW; + this_track->datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE; + this_track->datasize = CDIO_CD_FRAMESIZE; + this_track->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 { + cdio_log(log_level, + "%s line %d after word TRACK:", + psz_cue_name, i_line); + cdio_log(log_level, + "Unknown track mode %s", psz_field); + goto err_exit; + } + } else { + goto format_error; + } + + /* FLAGS flag1 flag2 ... */ + } else if (0 == strcmp ("FLAGS", psz_keyword)) { + if (0 <= i) { + while (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + if (0 == strcmp ("PRE", psz_field)) { + if (cd) cd->tocent[i].flags |= PRE_EMPHASIS; + } else if (0 == strcmp ("DCP", psz_field)) { + if (cd) cd->tocent[i].flags |= COPY_PERMITTED; + } else if (0 == strcmp ("4CH", psz_field)) { + if (cd) cd->tocent[i].flags |= FOUR_CHANNEL_AUDIO; + } else if (0 == strcmp ("SCMS", psz_field)) { + if (cd) cd->tocent[i].flags |= SCMS; + } else { + goto format_error; + } + } + } else { + goto format_error; + } + + /* ISRC CCOOOYYSSSSS */ + } else if (0 == strcmp ("ISRC", psz_keyword)) { + if (0 <= i) { + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + if (cd) cd->tocent[i].isrc = strdup (psz_field); + } else { + goto format_error; + } + } else { + goto 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"))) { + lba_t lba = cdio_lsn_to_lba(cdio_mmssff_to_lba (psz_field)); + if (CDIO_INVALID_LBA == lba) { + cdio_log(log_level, "%s line %d: after word PREGAP:", + psz_cue_name, i_line); + cdio_log(log_level, "Invalid MSF string %s", + psz_field); + goto err_exit; + } + if (cd) { + cd->tocent[i].pregap = lba; + } + } else { + goto format_error; + } if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + } else { + goto 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 (1!=sscanf(psz_field, "%d", &start_index)) { + cdio_log(log_level, + "%s line %d after word INDEX:", + psz_cue_name, i_line); + cdio_log(log_level, + "expecting an index number, got %s", + psz_field); + goto err_exit; + } + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + lba_t lba = cdio_mmssff_to_lba (psz_field); + if (CDIO_INVALID_LBA == lba) { + cdio_log(log_level, "%s line %d: after word INDEX:", + psz_cue_name, i_line); + cdio_log(log_level, "Invalid MSF string %s", + psz_field); + goto err_exit; + } + if (cd) { +#if FIXED_ME + cd->tocent[i].indexes[cd->tocent[i].nindex++] = lba; +#else + track_info_t *this_track= + &(cd->tocent[cd->gen.i_tracks - cd->gen.i_first_track]); + + if (start_index != 0) { + if (!b_first_index_for_track) { + lba += CDIO_PREGAP_SECTORS; + cdio_lba_to_msf(lba, &(this_track->start_msf)); + b_first_index_for_track = true; + this_track->start_lba = lba; + } + + if (cd->gen.i_tracks > 1) { + /* Figure out number of sectors for previous track */ + track_info_t *prev_track=&(cd->tocent[cd->gen.i_tracks-2]); + if ( this_track->start_lba < prev_track->start_lba ) { + cdio_log (log_level, + "track %d at LBA %lu starts before track %d at LBA %lu", + cd->gen.i_tracks, + (unsigned long int) this_track->start_lba, + cd->gen.i_tracks, + (unsigned long int) prev_track->start_lba); + prev_track->sec_count = 0; + } else if ( this_track->start_lba >= prev_track->start_lba + + CDIO_PREGAP_SECTORS ) { + prev_track->sec_count = this_track->start_lba - + prev_track->start_lba - CDIO_PREGAP_SECTORS ; + } else { + cdio_log (log_level, + "%lu fewer than pregap (%d) sectors in track %d", + (long unsigned int) + this_track->start_lba - prev_track->start_lba, + CDIO_PREGAP_SECTORS, + cd->gen.i_tracks); + /* Include pregap portion in sec_count. Maybe the pregap + was omitted. */ + prev_track->sec_count = this_track->start_lba - + prev_track->start_lba; + } + } + this_track->num_indices++; + } + } +#endif + } else { + goto format_error; + } + } else { + goto in_global_section; + } + + /* CD-TEXT */ + } else if ( CDTEXT_INVALID != + (cdtext_key = cdtext_is_keyword (psz_keyword)) ) { + if (-1 == i) { + if (cd) { + cdtext_set (cdtext_key, + strtok (NULL, "\"\t\n\r"), + &(cd->gen.cdtext)); + } + } else { + if (cd) { + cdtext_set (cdtext_key, strtok (NULL, "\"\t\n\r"), + &(cd->gen.cdtext_track[i])); + } + } + + /* unrecognized line */ + } else { + cdio_log(log_level, "%s line %d: warning: unrecognized keyword: %s", + psz_cue_name, i_line, psz_keyword); + goto err_exit; + } + } + } + + if (NULL != cd) { + cd->gen.toc_init = true; + } + + fclose (fp); + return true; + + format_error: + cdio_log(log_level, "%s line %d after word %s", + psz_cue_name, i_line, psz_keyword); + goto err_exit; + + in_global_section: + cdio_log(log_level, "%s line %d: word %s not allowed in global section", + 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; + +} + +/*! + Reads a single audio sector from CD device into data starting + from lsn. Returns 0 if no error. + */ +static int +_read_audio_sectors_bincue (void *user_data, void *data, lsn_t lsn, + unsigned int nblocks) +{ + _img_private_t *env = user_data; + int ret; + + /* Why the adjustment of 272, I don't know. It seems to work though */ + if (lsn != 0) { + ret = cdio_stream_seek (env->gen.data_source, + (lsn * CDIO_CD_FRAMESIZE_RAW) - 272, SEEK_SET); + if (ret!=0) return ret; + + ret = cdio_stream_read (env->gen.data_source, data, + CDIO_CD_FRAMESIZE_RAW, nblocks); + } else { + /* We need to pad out the first 272 bytes with 0's */ + BZERO(data, 272); + + ret = cdio_stream_seek (env->gen.data_source, 0, SEEK_SET); + + if (ret!=0) return ret; + + ret = cdio_stream_read (env->gen.data_source, (uint8_t *) data+272, + CDIO_CD_FRAMESIZE_RAW - 272, nblocks); + } + + /* ret is number of bytes if okay, but we need to return 0 okay. */ + return ret == 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_read_mode1_sector_bincue (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *p_env = user_data; + int ret; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + int blocksize = CDIO_CD_FRAMESIZE_RAW; + + ret = cdio_stream_seek (p_env->gen.data_source, lsn * blocksize, SEEK_SET); + if (ret!=0) return ret; + + /* FIXME: Not completely sure the below is correct. */ + ret = cdio_stream_read (p_env->gen.data_source, buf, CDIO_CD_FRAMESIZE_RAW, 1); + if (ret==0) return ret; + + memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE); + + return 0; +} + +/*! + Reads nblocks of mode1 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode1_sectors_bincue (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *p_env = user_data; + int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode1_sector_bincue (p_env, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/*! + Reads a single mode1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_read_mode2_sector_bincue (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *p_env = user_data; + int ret; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + + /* NOTE: The logic below seems a bit wrong and convoluted + to me, but passes the regression tests. (Perhaps it is why we get + valgrind errors in vcdxrip). Leave it the way it was for now. + Review this sector 2336 stuff later. + */ + + int blocksize = CDIO_CD_FRAMESIZE_RAW; + + ret = cdio_stream_seek (p_env->gen.data_source, lsn * blocksize, SEEK_SET); + if (ret!=0) return ret; + + ret = cdio_stream_read (p_env->gen.data_source, buf, CDIO_CD_FRAMESIZE_RAW, 1); + if (ret==0) return ret; + + + /* See NOTE above. */ + if (b_form2) + memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + M2RAW_SECTOR_SIZE); + else + memcpy (data, buf + CDIO_CD_XA_SYNC_HEADER, CDIO_CD_FRAMESIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode2_sectors_bincue (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *p_env = user_data; + int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode2_sector_bincue (p_env, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/*! + Return an array of strings giving possible BIN/CUE disk images. + */ +char ** +cdio_get_devices_bincue (void) +{ + char **drives = NULL; + unsigned int num_files=0; +#ifdef HAVE_GLOB_H + unsigned int i; + glob_t globbuf; + globbuf.gl_offs = 0; + glob("*.cue", GLOB_DOOFFS, NULL, &globbuf); + for (i=0; i<globbuf.gl_pathc; i++) { + cdio_add_device_list(&drives, globbuf.gl_pathv[i], &num_files); + } + globfree(&globbuf); +#else + cdio_add_device_list(&drives, DEFAULT_CDIO_DEVICE, &num_files); +#endif /*HAVE_GLOB_H*/ + cdio_add_device_list(&drives, NULL, &num_files); + return drives; +} + +/*! + Return a string containing the default CD device. + */ +char * +cdio_get_default_device_bincue(void) +{ + char **drives = cdio_get_devices_nrg(); + char *drive = (drives[0] == NULL) ? NULL : strdup(drives[0]); + cdio_free_device_list(drives); + return drive; +} + +static bool +get_hwinfo_bincue ( const CdIo *p_cdio, /*out*/ cdio_hwinfo_t *hw_info) +{ + strcpy(hw_info->psz_vendor, "libcdio"); + strcpy(hw_info->psz_model, "CDRWIN"); + strcpy(hw_info->psz_revision, CDIO_VERSION); + return true; + +} + +/*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_format_t +_get_track_format_bincue(void *user_data, track_t i_track) +{ + _img_private_t *p_env = user_data; + + if (i_track > p_env->gen.i_tracks || i_track == 0) + return TRACK_FORMAT_ERROR; + + return p_env->tocent[i_track-p_env->gen.i_first_track].track_format; +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +static bool +_get_track_green_bincue(void *user_data, track_t i_track) +{ + _img_private_t *p_env = user_data; + + if ( NULL == p_env || + ( i_track < p_env->gen.i_first_track + || i_track >= p_env->gen.i_tracks + p_env->gen.i_first_track ) ) + return false; + + return p_env->tocent[i_track-p_env->gen.i_first_track].track_green; +} + +/*! + Return the starting LSN track number + i_track in obj. Track numbers start at 1. + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static lba_t +_get_lba_track_bincue(void *user_data, track_t i_track) +{ + _img_private_t *p_env = user_data; + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = p_env->gen.i_tracks+1; + + if (i_track <= p_env->gen.i_tracks + p_env->gen.i_first_track && i_track != 0) { + return p_env->tocent[i_track-p_env->gen.i_first_track].start_lba; + } else + return CDIO_INVALID_LBA; +} + +/*! + Return corresponding BIN file if psz_cue_name is a cue file or NULL + if not a CUE file. +*/ +char * +cdio_is_cuefile(const char *psz_cue_name) +{ + int i; + char *psz_bin_name; + + if (psz_cue_name == NULL) return NULL; + + /* FIXME? Now that we have cue parsing, should we really force + the filename extension requirement or is it enough just to + parse the cuefile? + */ + + psz_bin_name=strdup(psz_cue_name); + i=strlen(psz_bin_name)-strlen("cue"); + + if (i>0) { + if (psz_cue_name[i]=='c' && psz_cue_name[i+1]=='u' && psz_cue_name[i+2]=='e') { + psz_bin_name[i++]='b'; psz_bin_name[i++]='i'; psz_bin_name[i++]='n'; + if (parse_cuefile(NULL, psz_cue_name)) + return psz_bin_name; + else + goto error; + } + else if (psz_cue_name[i]=='C' && psz_cue_name[i+1]=='U' && psz_cue_name[i+2]=='E') { + psz_bin_name[i++]='B'; psz_bin_name[i++]='I'; psz_bin_name[i++]='N'; + if (parse_cuefile(NULL, psz_cue_name)) + return psz_bin_name; + else + goto error; + } + } + error: + free(psz_bin_name); + return NULL; +} + +/*! + Return corresponding CUE file if psz_bin_name is a bin file or NULL + if not a BIN file. +*/ +char * +cdio_is_binfile(const char *psz_bin_name) +{ + int i; + char *psz_cue_name; + + if (psz_bin_name == NULL) return NULL; + + psz_cue_name=strdup(psz_bin_name); + i=strlen(psz_bin_name)-strlen("bin"); + + if (i>0) { + if (psz_bin_name[i]=='b' && psz_bin_name[i+1]=='i' && psz_bin_name[i+2]=='n') { + psz_cue_name[i++]='c'; psz_cue_name[i++]='u'; psz_cue_name[i++]='e'; + return psz_cue_name; + } + else if (psz_bin_name[i]=='B' && psz_bin_name[i+1]=='I' && psz_bin_name[i+2]=='N') { + psz_cue_name[i++]='C'; psz_cue_name[i++]='U'; psz_cue_name[i++]='E'; + return psz_cue_name; + } + } + free(psz_cue_name); + return NULL; +} + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_am_bincue (const char *psz_source_name, const char *psz_access_mode) +{ + if (psz_access_mode != NULL) + cdio_warn ("there is only one access mode for bincue. Arg %s ignored", + psz_access_mode); + return cdio_open_bincue(psz_source_name); +} + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_bincue (const char *source_name) +{ + char *psz_bin_name = cdio_is_cuefile(source_name); + + if (NULL != psz_bin_name) { + free(psz_bin_name); + return cdio_open_cue(source_name); + } else { + char *psz_cue_name = cdio_is_binfile(source_name); + CdIo *cdio = cdio_open_cue(psz_cue_name); + free(psz_cue_name); + return cdio; + } +} + +CdIo * +cdio_open_cue (const char *psz_cue_name) +{ + CdIo *ret; + _img_private_t *_data; + char *psz_bin_name; + + cdio_funcs _funcs; + + memset( &_funcs, 0, sizeof(_funcs) ); + + _funcs.eject_media = _eject_media_image; + _funcs.free = _free_image; + _funcs.get_arg = _get_arg_image; + _funcs.get_cdtext = get_cdtext_generic; + _funcs.get_devices = cdio_get_devices_bincue; + _funcs.get_default_device = cdio_get_default_device_bincue; + _funcs.get_discmode = _get_discmode_image; + _funcs.get_drive_cap = _get_drive_cap_image; + _funcs.get_first_track_num= _get_first_track_num_image; + _funcs.get_hwinfo = get_hwinfo_bincue; + _funcs.get_mcn = _get_mcn_image; + _funcs.get_num_tracks = _get_num_tracks_image; + _funcs.get_track_format = _get_track_format_bincue; + _funcs.get_track_green = _get_track_green_bincue; + _funcs.get_track_lba = _get_lba_track_bincue; + _funcs.get_track_msf = _get_track_msf_image; + _funcs.lseek = _lseek_bincue; + _funcs.read = _read_bincue; + _funcs.read_audio_sectors = _read_audio_sectors_bincue; + _funcs.read_mode1_sector = _read_mode1_sector_bincue; + _funcs.read_mode1_sectors = _read_mode1_sectors_bincue; + _funcs.read_mode2_sector = _read_mode2_sector_bincue; + _funcs.read_mode2_sectors = _read_mode2_sectors_bincue; + _funcs.set_arg = _set_arg_image; + _funcs.stat_size = _stat_size_bincue; + + if (NULL == psz_cue_name) return NULL; + + _data = _cdio_malloc (sizeof (_img_private_t)); + _data->gen.init = false; + _data->psz_cue_name = NULL; + + ret = cdio_new ((void *)_data, &_funcs); + + if (ret == NULL) { + free(_data); + return NULL; + } + + psz_bin_name = cdio_is_cuefile(psz_cue_name); + + if (NULL == psz_bin_name) { + cdio_error ("source name %s is not recognized as a CUE file", + psz_cue_name); + } + + _set_arg_image (_data, "cue", psz_cue_name); + _set_arg_image (_data, "source", psz_bin_name); + free(psz_bin_name); + + if (_init_bincue(_data)) { + return ret; + } else { + _free_image(_data); + free(ret); + return NULL; + } +} + +bool +cdio_have_bincue (void) +{ + return true; +} diff --git a/contrib/libcdio/image/cdrdao.c b/contrib/libcdio/image/cdrdao.c new file mode 100644 index 000000000..828172721 --- /dev/null +++ b/contrib/libcdio/image/cdrdao.c @@ -0,0 +1,1198 @@ +/* + $Id: cdrdao.c,v 1.1 2005/01/01 02:43:58 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + toc reading routine adapted from cuetools + Copyright (C) 2003 Svend Sanjay Sorensen <ssorensen@fastmail.fm> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This code implements low-level access functions for a CD images + residing inside a disk file (*.bin) and its associated cue sheet. + (*.cue). +*/ + +static const char _rcsid[] = "$Id: cdrdao.c,v 1.1 2005/01/01 02:43:58 rockyb Exp $"; + +#include "image.h" +#include "cdio_assert.h" +#include "_cdio_stdio.h" + +#include <cdio/logging.h> +#include <cdio/sector.h> +#include <cdio/util.h> +#include <cdio/version.h> + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#ifdef HAVE_GLOB_H +#include <glob.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include <ctype.h> + +#include "portable.h" + +/* reader */ + +#define DEFAULT_CDIO_DEVICE "videocd.bin" +#define DEFAULT_CDIO_CDRDAO "videocd.toc" + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + internal_position_t pos; + + char *psz_cue_name; + char *psz_mcn; /* Media Catalog Number (5.22.3) + exactly 13 bytes */ + track_info_t tocent[CDIO_CD_MAX_TRACKS+1]; /* entry info for each track + add 1 for leadout. */ + discmode_t disc_mode; +} _img_private_t; + +static uint32_t _stat_size_cdrdao (void *user_data); +static bool parse_tocfile (_img_private_t *cd, const char *toc_name); + +#define NEED_MEDIA_EJECT_IMAGE +#include "image_common.h" + +/*! + 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 _stat_size_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 = _stat_size_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; +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + Would be libc's seek() but we have to adjust for the extra track header + information in each sector. +*/ +static off_t +_lseek_cdrdao (void *user_data, off_t offset, int whence) +{ + _img_private_t *env = user_data; + + /* real_offset is the real byte offset inside the disk image + The number below was determined empirically. I'm guessing + the 1st 24 bytes of a bin file are used for something. + */ + off_t real_offset=0; + + unsigned int i; + + env->pos.lba = 0; + for (i=0; i<env->gen.i_tracks; i++) { + track_info_t *this_track=&(env->tocent[i]); + env->pos.index = i; + if ( (this_track->sec_count*this_track->datasize) >= offset) { + int blocks = offset / this_track->datasize; + int rem = offset % this_track->datasize; + int block_offset = blocks * this_track->blocksize; + real_offset += block_offset + rem; + env->pos.buff_offset = rem; + env->pos.lba += blocks; + break; + } + real_offset += this_track->sec_count*this_track->blocksize; + offset -= this_track->sec_count*this_track->datasize; + env->pos.lba += this_track->sec_count; + } + + if (i==env->gen.i_tracks) { + cdio_warn ("seeking outside range of disk image"); + return -1; + } else { + real_offset += env->tocent[i].datastart; + return cdio_stream_seek(env->tocent[i].data_source, real_offset, whence); + } +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + FIXME: + At present we assume a read doesn't cross sector or track + boundaries. +*/ +static ssize_t +_read_cdrdao (void *user_data, void *data, size_t size) +{ + _img_private_t *env = user_data; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + char *p = data; + ssize_t final_size=0; + ssize_t this_size; + track_info_t *this_track=&(env->tocent[env->pos.index]); + ssize_t skip_size = this_track->datastart + this_track->endsize; + + while (size > 0) { + int rem = this_track->datasize - env->pos.buff_offset; + if (size <= rem) { + this_size = cdio_stream_read(this_track->data_source, buf, size, 1); + final_size += this_size; + memcpy (p, buf, this_size); + break; + } + + /* Finish off reading this sector. */ + cdio_warn ("Reading across block boundaries not finished"); + + size -= rem; + this_size = cdio_stream_read(this_track->data_source, buf, rem, 1); + final_size += this_size; + memcpy (p, buf, this_size); + p += this_size; + this_size = cdio_stream_read(this_track->data_source, buf, rem, 1); + + /* Skip over stuff at end of this sector and the beginning of the next. + */ + cdio_stream_read(this_track->data_source, buf, skip_size, 1); + + /* Get ready to read another sector. */ + env->pos.buff_offset=0; + env->pos.lba++; + + /* Have gone into next track. */ + if (env->pos.lba >= env->tocent[env->pos.index+1].start_lba) { + env->pos.index++; + this_track=&(env->tocent[env->pos.index]); + skip_size = this_track->datastart + this_track->endsize; + } + } + return final_size; +} + +/*! + Return the size of the CD in logical block address (LBA) units. + */ +static uint32_t +_stat_size_cdrdao (void *user_data) +{ + _img_private_t *env = user_data; + long size; + + size = cdio_stream_stat (env->tocent[0].data_source); + + if (size % CDIO_CD_FRAMESIZE_RAW) + { + cdio_warn ("image %s size (%ld) not multiple of blocksize (%d)", + env->tocent[0].filename, size, CDIO_CD_FRAMESIZE_RAW); + if (size % M2RAW_SECTOR_SIZE == 0) + cdio_warn ("this may be a 2336-type disc image"); + else if (size % CDIO_CD_FRAMESIZE_RAW == 0) + cdio_warn ("this may be a 2352-type disc image"); + /* exit (EXIT_FAILURE); */ + } + + size /= CDIO_CD_FRAMESIZE_RAW; + + return size; +} + +#define MAXLINE 512 +#define UNIMPLIMENTED_MSG \ + cdio_log(log_level, "%s line %d: unimplimented keyword: %s", \ + psz_cue_name, i_line, psz_keyword) + + +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; + cdio_log_level_t log_level = (NULL == cd) ? CDIO_LOG_INFO : CDIO_LOG_WARN; + 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; + + fp = fopen (psz_cue_name, "r"); + 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_init = true; + cd->gen.b_cdtext_error = false; + } + + while ((fgets(psz_line, MAXLINE, fp)) != NULL) { + + i_line++; + + /* strip comment from line */ + /* todo: // in quoted strings? */ + /* //comment */ + if (NULL != (psz_field = strstr (psz_line, "//"))) + *psz_field = '\0'; + + if (NULL != (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 i; + for (i=0; i<13; i++) { + if (!isdigit(psz_field[i])) { + 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[i], i+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 != cd) cdtext_init (&(cd->gen.cdtext_track[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 */ + if (0 == strcmp ("RW", psz_field)) + ; + else if (0 == strcmp ("RW_RAW", psz_field)) + ; + } + 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; + goto err_exit; + } + } 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) + cd->tocent[i].flags |= CDIO_TRACK_FLAG_COPY_PERMITTED; + } else if (0 == strcmp ("PRE_EMPHASIS", psz_keyword)) { + if (NULL != cd) + cd->tocent[i].flags |= CDIO_TRACK_FLAG_PRE_EMPHASIS; + /* TWO_CHANNEL_AUDIO */ + } else if (0 == strcmp ("TWO_CHANNEL_AUDIO", psz_keyword)) { + if (NULL != cd) + 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) + 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)) { + UNIMPLIMENTED_MSG; + + /* ZERO <length> */ + } else if (0 == strcmp ("ZERO", psz_keyword)) { + UNIMPLIMENTED_MSG; + + /* [FILE|AUDIOFILE] "<filename>" <start> [<length>] */ + } else if (0 == strcmp ("FILE", psz_keyword) + || 0 == strcmp ("AUDIOFILE", psz_keyword)) { + if (0 <= i) { + if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) { + if (NULL != cd) { + cd->tocent[i].filename = strdup (psz_field); + /* Todo: 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; + } + } + } + + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + lba_t lba = cdio_lsn_to_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 (NULL != cd) { + cd->tocent[i].start_lba = lba; + cdio_lba_to_msf(lba, &(cd->tocent[i].start_msf)); + } + } + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) + if (NULL != cd) + cd->tocent[i].length = cdio_mmssff_to_lba (psz_field); + if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + } else { + goto not_in_global_section; + } + + /* DATAFILE "<filename>" <start> [<length>] */ + } else if (0 == strcmp ("DATAFILE", psz_keyword)) { + goto unimplimented_error; + + /* 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].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 != (psz_field = 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; + } + + } else if (0 == strcmp ("LANGUAGE_MAP", psz_keyword)) { + /* LANGUAGE d { ... } */ + } else if (0 == strcmp ("LANGUAGE", psz_keyword)) { + if (NULL == (psz_field = strtok (NULL, " \t\n\r"))) { + goto format_error; + } + /* 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_INVALID != + (cdtext_key = cdtext_is_keyword (psz_keyword)) ) { + if (-1 == i) { + if (NULL != cd) { + cdtext_set (cdtext_key, + strtok (NULL, "\"\t\n\r"), + &(cd->gen.cdtext)); + } + } else { + if (NULL != cd) { + cdtext_set (cdtext_key, + strtok (NULL, "\"\t\n\r"), + &(cd->gen.cdtext_track[i])); + } + } + + /* 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; +} + +/*! + Reads a single audio sector from CD device into data starting + from lsn. Returns 0 if no error. + */ +static int +_read_audio_sectors_cdrdao (void *user_data, void *data, lsn_t lsn, + unsigned int nblocks) +{ + _img_private_t *env = user_data; + int ret; + + /* Why the adjustment of 272, I don't know. It seems to work though */ + if (lsn != 0) { + ret = cdio_stream_seek (env->tocent[0].data_source, + (lsn * CDIO_CD_FRAMESIZE_RAW) - 272, SEEK_SET); + if (ret!=0) return ret; + + ret = cdio_stream_read (env->tocent[0].data_source, data, + CDIO_CD_FRAMESIZE_RAW, nblocks); + } else { + /* We need to pad out the first 272 bytes with 0's */ + BZERO(data, 272); + + ret = cdio_stream_seek (env->tocent[0].data_source, 0, SEEK_SET); + + if (ret!=0) return ret; + + ret = cdio_stream_read (env->tocent[0].data_source, (uint8_t *) data+272, + CDIO_CD_FRAMESIZE_RAW - 272, nblocks); + } + + /* ret is number of bytes if okay, but we need to return 0 okay. */ + return ret == 0; +} + +/*! + Reads a single mode2 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_read_mode1_sector_cdrdao (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *env = user_data; + int ret; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + + ret = cdio_stream_seek (env->tocent[0].data_source, + lsn * CDIO_CD_FRAMESIZE_RAW, SEEK_SET); + if (ret!=0) return ret; + + /* FIXME: Not completely sure the below is correct. */ + ret = cdio_stream_read (env->tocent[0].data_source, buf, + CDIO_CD_FRAMESIZE_RAW, 1); + if (ret==0) return ret; + + memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE); + + return 0; +} + +/*! + Reads nblocks of mode1 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode1_sectors_cdrdao (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *env = user_data; + int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode1_sector_cdrdao (env, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/*! + Reads a single mode1 sector from cd device into data starting + from lsn. Returns 0 if no error. + */ +static int +_read_mode2_sector_cdrdao (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *env = user_data; + int ret; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + + /* NOTE: The logic below seems a bit wrong and convoluted + to me, but passes the regression tests. (Perhaps it is why we get + valgrind errors in vcdxrip). Leave it the way it was for now. + Review this sector 2336 stuff later. + */ + + ret = cdio_stream_seek (env->tocent[0].data_source, + lsn * CDIO_CD_FRAMESIZE_RAW, SEEK_SET); + if (ret!=0) return ret; + + ret = cdio_stream_read (env->tocent[0].data_source, buf, + CDIO_CD_FRAMESIZE_RAW, 1); + if (ret==0) return ret; + + + /* See NOTE above. */ + if (b_form2) + memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + M2RAW_SECTOR_SIZE); + else + memcpy (data, buf + CDIO_CD_XA_SYNC_HEADER, CDIO_CD_FRAMESIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode2_sectors_cdrdao (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned int nblocks) +{ + _img_private_t *env = user_data; + int i; + int retval; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode2_sector_cdrdao (env, + ((char *)data) + (CDIO_CD_FRAMESIZE * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/*! + Return an array of strings giving possible TOC disk images. + */ +char ** +cdio_get_devices_cdrdao (void) +{ + char **drives = NULL; + unsigned int num_files=0; +#ifdef HAVE_GLOB_H + unsigned int i; + glob_t globbuf; + globbuf.gl_offs = 0; + glob("*.toc", GLOB_DOOFFS, NULL, &globbuf); + for (i=0; i<globbuf.gl_pathc; i++) { + cdio_add_device_list(&drives, globbuf.gl_pathv[i], &num_files); + } + globfree(&globbuf); +#else + cdio_add_device_list(&drives, DEFAULT_CDIO_DEVICE, &num_files); +#endif /*HAVE_GLOB_H*/ + cdio_add_device_list(&drives, NULL, &num_files); + return drives; +} + +/*! + Return a string containing the default CD device. + */ +char * +cdio_get_default_device_cdrdao(void) +{ + char **drives = cdio_get_devices_nrg(); + char *drive = (drives[0] == NULL) ? NULL : strdup(drives[0]); + cdio_free_device_list(drives); + return drive; +} + +static bool +get_hwinfo_cdrdao ( const CdIo *p_cdio, /*out*/ cdio_hwinfo_t *hw_info) +{ + strcpy(hw_info->psz_vendor, "libcdio"); + strcpy(hw_info->psz_model, "cdrdao"); + strcpy(hw_info->psz_revision, CDIO_VERSION); + return true; + +} + +/*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_format_t +_get_track_format_cdrdao(void *user_data, track_t i_track) +{ + _img_private_t *env = user_data; + + if (!env->gen.init) _init_cdrdao(env); + + if (i_track > env->gen.i_tracks || i_track == 0) + return TRACK_FORMAT_ERROR; + + return env->tocent[i_track-env->gen.i_first_track].track_format; +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +static bool +_get_track_green_cdrdao(void *user_data, track_t i_track) +{ + _img_private_t *env = user_data; + + if (!env->gen.init) _init_cdrdao(env); + + if (i_track > env->gen.i_tracks || i_track == 0) + return false; + + return env->tocent[i_track-env->gen.i_first_track].track_green; +} + +/*! + Return the starting LSN track number + i_track in obj. Track numbers start at 1. + The "leadout" track is specified either by + using i_track LEADOUT_TRACK or the total tracks+1. + False is returned if there is no track entry. +*/ +static lba_t +_get_lba_track_cdrdao(void *user_data, track_t i_track) +{ + _img_private_t *env = user_data; + _init_cdrdao (env); + + if (i_track == CDIO_CDROM_LEADOUT_TRACK) + i_track = env->gen.i_tracks+1; + + if (i_track <= env->gen.i_tracks+1 && i_track != 0) { + return env->tocent[i_track-1].start_lba; + } else + return CDIO_INVALID_LBA; +} + +/*! + Check that a TOC file is valid. We parse the entire file. + +*/ +bool +cdio_is_tocfile(const char *psz_cue_name) +{ + int i; + + if (psz_cue_name == NULL) return false; + + i=strlen(psz_cue_name)-strlen("toc"); + + if (i>0) { + if ( (psz_cue_name[i]=='t' && psz_cue_name[i+1]=='o' && psz_cue_name[i+2]=='c') + || (psz_cue_name[i]=='T' && psz_cue_name[i+1]=='O' && psz_cue_name[i+2]=='C') ) { + return parse_tocfile(NULL, psz_cue_name); + } + } + return false; +} + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_am_cdrdao (const char *psz_source_name, const char *psz_access_mode) +{ + if (psz_access_mode != NULL && strcmp(psz_access_mode, "image")) + cdio_warn ("there is only one access mode, 'image' for cdrdao. Arg %s ignored", + psz_access_mode); + return cdio_open_cdrdao(psz_source_name); +} + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_cdrdao (const char *psz_cue_name) +{ + CdIo *ret; + _img_private_t *_data; + + cdio_funcs _funcs; + + memset( &_funcs, 0, sizeof(_funcs) ); + + _funcs.eject_media = _eject_media_image; + _funcs.free = _free_image; + _funcs.get_arg = _get_arg_image; + _funcs.get_cdtext = get_cdtext_generic; + _funcs.get_devices = cdio_get_devices_cdrdao; + _funcs.get_default_device = cdio_get_default_device_cdrdao; + _funcs.get_discmode = _get_discmode_image; + _funcs.get_drive_cap = _get_drive_cap_image; + _funcs.get_first_track_num= _get_first_track_num_image; + _funcs.get_hwinfo = get_hwinfo_cdrdao; + _funcs.get_mcn = _get_mcn_image; + _funcs.get_num_tracks = _get_num_tracks_image; + _funcs.get_track_format = _get_track_format_cdrdao; + _funcs.get_track_green = _get_track_green_cdrdao; + _funcs.get_track_lba = _get_lba_track_cdrdao; + _funcs.get_track_msf = _get_track_msf_image; + _funcs.lseek = _lseek_cdrdao; + _funcs.read = _read_cdrdao; + _funcs.read_audio_sectors = _read_audio_sectors_cdrdao; + _funcs.read_mode1_sector = _read_mode1_sector_cdrdao; + _funcs.read_mode1_sectors = _read_mode1_sectors_cdrdao; + _funcs.read_mode2_sector = _read_mode2_sector_cdrdao; + _funcs.read_mode2_sectors = _read_mode2_sectors_cdrdao; + _funcs.set_arg = _set_arg_image; + _funcs.stat_size = _stat_size_cdrdao; + + if (NULL == psz_cue_name) return NULL; + + _data = _cdio_malloc (sizeof (_img_private_t)); + _data->gen.init = false; + _data->psz_cue_name = NULL; + _data->gen.data_source = NULL; + _data->gen.source_name = NULL; + + ret = cdio_new ((void *)_data, &_funcs); + + if (ret == NULL) { + free(_data); + return NULL; + } + + if (!cdio_is_tocfile(psz_cue_name)) { + cdio_debug ("source name %s is not recognized as a TOC file", + psz_cue_name); + return NULL; + } + + _set_arg_image (_data, "cue", psz_cue_name); + _set_arg_image (_data, "source", psz_cue_name); + + if (_init_cdrdao(_data)) { + return ret; + } else { + _free_image(_data); + free(ret); + return NULL; + } +} + +bool +cdio_have_cdrdao (void) +{ + return true; +} diff --git a/contrib/libcdio/image/nrg.c b/contrib/libcdio/image/nrg.c new file mode 100644 index 000000000..40e5bbbf7 --- /dev/null +++ b/contrib/libcdio/image/nrg.c @@ -0,0 +1,1274 @@ +/* + $Id: nrg.c,v 1.3 2005/05/07 22:07:27 rockyb Exp $ + + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + Copyright (C) 2001, 2003 Herbert Valerio Riedel <hvr@gnu.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*! This code implements low-level access functions for the Nero native + CD-image format residing inside a disk file (*.nrg). +*/ + +#include "image.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_GLOB_H +#include <glob.h> +#endif + +#include <cdio/bytesex.h> +#include <cdio/ds.h> +#include <cdio/logging.h> +#include <cdio/util.h> +#include <cdio/version.h> +#include "cdio_assert.h" +#include "_cdio_stdio.h" +#include "nrg.h" + +static const char _rcsid[] = "$Id: nrg.c,v 1.3 2005/05/07 22:07:27 rockyb Exp $"; + + +/* reader */ + +#define DEFAULT_CDIO_DEVICE "image.nrg" + +/* + Link element of track structure as a linked list. + Possibly redundant with above track_info_t */ +typedef struct { + uint32_t start_lsn; + uint32_t sec_count; /* Number of sectors in track. Does not + include pregap before next entry. */ + uint64_t img_offset; /* Bytes offset from beginning of disk image file.*/ + uint32_t blocksize; /* Number of bytes in a block */ + int flags; /* don't copy, 4 channel, pre-emphasis */ +} _mapping_t; + + +typedef struct { + /* Things common to all drivers like this. + This must be first. */ + generic_img_private_t gen; + internal_position_t pos; + + /* This is common to all image drivers... */ + char *psz_cue_name; + char *psz_mcn; /* Media Catalog Number (5.22.3) */ + + track_info_t tocent[CDIO_CD_MAX_TRACKS+1]; /* entry info for each track + add 1 for leadout. */ + discmode_t disc_mode; + + /* Nero Specific stuff. Note: for the image_free to work, this *must* + be last. */ + bool is_dao; /* True if some of disk at once. False + if some sort of track at once. */ + uint32_t mtyp; /* Value of MTYP (media type?) tag */ + uint8_t dtyp; /* Value of DAOX media type tag */ + + /* This is a hack because I don't really understnad NERO better. */ + bool is_cues; + + CdioList *mapping; /* List of track information */ + uint32_t size; +} _img_private_t; + +static bool parse_nrg (_img_private_t *env, const char *psz_cue_name); +static uint32_t _stat_size_nrg (void *user_data); + +#include "image_common.h" + +/* Updates internal track TOC, so we can later + simulate ioctl(CDROMREADTOCENTRY). + */ +static void +_register_mapping (_img_private_t *env, lsn_t start_lsn, uint32_t sec_count, + uint64_t img_offset, uint32_t blocksize, + track_format_t track_format, bool track_green, + int flags) +{ + const int track_num=env->gen.i_tracks; + track_info_t *this_track=&(env->tocent[env->gen.i_tracks]); + _mapping_t *_map = _cdio_malloc (sizeof (_mapping_t)); + + _map->start_lsn = start_lsn; + _map->sec_count = sec_count; + _map->img_offset = img_offset; + _map->blocksize = blocksize; + _map->flags = flags; + + if (!env->mapping) env->mapping = _cdio_list_new (); + _cdio_list_append (env->mapping, _map); + + env->size = MAX (env->size, (start_lsn + sec_count)); + + /* Update *this_track and track_num. These structures are + in a sense redundant witht the obj->mapping list. Perhaps one + or the other can be eliminated. + */ + + cdio_lba_to_msf (cdio_lsn_to_lba(start_lsn), &(this_track->start_msf)); + this_track->start_lba = cdio_msf_to_lba(&this_track->start_msf); + this_track->track_num = track_num+1; + this_track->blocksize = blocksize; + if (env->is_cues) + this_track->datastart = img_offset; + else + this_track->datastart = 0; + + if (track_green) + this_track->datastart += CDIO_CD_SUBHEADER_SIZE; + + this_track->sec_count = sec_count; + + this_track->track_format= track_format; + this_track->track_green = track_green; + + switch (this_track->track_format) { + case TRACK_FORMAT_AUDIO: + this_track->blocksize = CDIO_CD_FRAMESIZE_RAW; + this_track->datasize = CDIO_CD_FRAMESIZE_RAW; + /*this_track->datastart = 0;*/ + this_track->endsize = 0; + break; + case TRACK_FORMAT_CDI: + this_track->datasize=CDIO_CD_FRAMESIZE; + break; + case TRACK_FORMAT_XA: + if (track_green) { + this_track->blocksize = CDIO_CD_FRAMESIZE; + /*this_track->datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE;*/ + this_track->datasize = M2RAW_SECTOR_SIZE; + this_track->endsize = 0; + } else { + /*this_track->datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE + + CDIO_CD_SUBHEADER_SIZE;*/ + this_track->datasize = CDIO_CD_FRAMESIZE; + this_track->endsize = CDIO_CD_SYNC_SIZE + CDIO_CD_ECC_SIZE; + } + break; + case TRACK_FORMAT_DATA: + if (track_green) { + /*this_track->datastart = CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE;*/ + this_track->datasize = CDIO_CD_FRAMESIZE; + this_track->endsize = CDIO_CD_EDC_SIZE + CDIO_CD_M1F1_ZERO_SIZE + + CDIO_CD_ECC_SIZE; + } else { + /* Is the below correct? */ + /*this_track->datastart = 0;*/ + this_track->datasize = CDIO_CD_FRAMESIZE; + this_track->endsize = 0; + } + break; + default: + /*this_track->datasize=CDIO_CD_FRAMESIZE_RAW;*/ + cdio_warn ("track %d has unknown format %d", + env->gen.i_tracks, this_track->track_format); + } + + env->gen.i_tracks++; + + cdio_debug ("start lsn: %lu sector count: %0lu -> %8ld (%08lx)", + (long unsigned int) start_lsn, + (long unsigned int) sec_count, + (long unsigned int) img_offset, + (long unsigned int) img_offset); +} + + +/* + 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 *env, const char *psz_nrg_name) +{ + long unsigned int footer_start; + long unsigned int size; + char *footer_buf = NULL; + cdio_log_level_t log_level = (NULL == env) ? CDIO_LOG_INFO : CDIO_LOG_WARN; + + size = cdio_stream_stat (env->gen.data_source); + if (-1 == size) return false; + + { + _footer_t buf; + cdio_assert (sizeof (buf) == 12); + + cdio_stream_seek (env->gen.data_source, size - sizeof (buf), SEEK_SET); + cdio_stream_read (env->gen.data_source, (void *) &buf, sizeof (buf), 1); + + if (buf.v50.ID == UINT32_TO_BE (NERO_ID)) { + cdio_info ("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_info ("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 (IN ((size - footer_start), 0, 4096)); + + footer_buf = _cdio_malloc (size - footer_start); + + cdio_stream_seek (env->gen.data_source, footer_start, SEEK_SET); + cdio_stream_read (env->gen.data_source, footer_buf, + 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 (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); + int idx; + + cdio_info ("CUES type image detected" ); + + /* CUES LSN has 150 pregap include at beginning? -/ + cdio_assert (lsn == 0?); + */ + + env->is_cues = true; /* HACK alert. */ + env->gen.i_tracks = 0; + env->gen.i_first_track = 1; + for (idx = 1; idx < entries-1; idx += 2) { + lsn_t sec_count; + int addrtype = _entries[idx].addr_ctrl / 16; + int control = _entries[idx].addr_ctrl % 16; + int flags = 0; + if ( 1 == control ) + flags &= ~CDIO_TRACK_FLAG_COPY_PERMITTED; + + 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 (addrtype) { + 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_warn ("untested (i.e. probably wrong) CUE MSF code"); + break; + } + default: + lsn = CDIO_INVALID_LSN; + cdio_warn("unknown addrtype %d", addrtype); + } + + sec_count = UINT32_FROM_BE (_entries[idx + 1].lsn); + + _register_mapping (env, lsn, sec_count*2, + (lsn+CDIO_PREGAP_SECTORS) * M2RAW_SECTOR_SIZE, + M2RAW_SECTOR_SIZE, TRACK_FORMAT_XA, true, + flags); + } + } else { + lsn_t lsn = UINT32_FROM_BE (_entries[0].lsn); + int idx; + + cdio_info ("CUEX type image detected"); + + /* LSN must start at -150 (LBA 0)? */ + cdio_assert (lsn == -150); + + for (idx = 2; idx < entries; idx += 2) { + lsn_t sec_count; + int addrtype = _entries[idx].addr_ctrl >> 4; + int control = _entries[idx].addr_ctrl & 0xf; + int flags = 0; + if ( 1 == control ) + flags &= ~CDIO_TRACK_FLAG_COPY_PERMITTED; + + /* extractnrg.pl has addrtype for LBA's 0, and + for MSF 1. ??? + + FIXME: Should decode as appropriate for addrtype. + */ + cdio_assert ( addrtype == 0 || addrtype == 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 (env, lsn, sec_count - lsn, + (lsn + CDIO_PREGAP_SECTORS)*M2RAW_SECTOR_SIZE, + M2RAW_SECTOR_SIZE, TRACK_FORMAT_XA, true, + flags); + } + } + break; + } + + case DAOX_ID: /* "DAOX" */ + case DAOI_ID: /* "DAOI" */ + { + track_format_t track_format; + int form2; + + /* We include an extra 0 byte so these can be used as C strings.*/ + env->psz_mcn = _cdio_malloc (CDIO_MCN_SIZE+1); + + if (DAOX_ID == opcode) { + _daox_array_t *_entries = (void *) chunk->data; + form2 = _entries->_unknown[1]; + env->dtyp = _entries->_unknown[19]; + memcpy(env->psz_mcn, &(_entries->psz_mcn), CDIO_MCN_SIZE); + env->psz_mcn[CDIO_MCN_SIZE] = '\0'; + } else { + _daoi_array_t *_entries = (void *) chunk->data; + form2 = _entries->_unknown[1]; + env->dtyp = _entries->_unknown[19]; + memcpy(env->psz_mcn, &(_entries->psz_mcn), CDIO_MCN_SIZE); + env->psz_mcn[CDIO_MCN_SIZE] = '\0'; + } + + env->is_dao = true; + cdio_debug ("DAO%c tag detected, track format %d, form %x\n", + opcode==DAOX_ID ? 'X': 'I', env->dtyp, form2); + switch (env->dtyp) { + case 0: + /* Mode 1 */ + track_format = TRACK_FORMAT_DATA; + env->disc_mode = CDIO_DISC_MODE_CD_DATA; + break; + case 2: + /* Mode 2 form 1 */ + form2 = 0; + track_format = TRACK_FORMAT_XA; + env->disc_mode = CDIO_DISC_MODE_CD_XA; + break; + case 3: + /* Mode 2 */ + track_format = TRACK_FORMAT_XA; + env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ?? */ + break; + case 0x6: + /* Mode2 form mix */ + track_format = TRACK_FORMAT_XA; + env->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + case 0x20: /* ??? Mode2 form 2, Mode2 raw?? */ + track_format = TRACK_FORMAT_XA; + env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ??. */ + break; + case 0x7: + track_format = TRACK_FORMAT_AUDIO; + env->disc_mode = CDIO_DISC_MODE_CD_DA; + break; + default: + cdio_log (log_level, "Unknown track format %x\n", + env->dtyp); + track_format = TRACK_FORMAT_AUDIO; + } + if (0 == form2) { + int i; + for (i=0; i<env->gen.i_tracks; i++) { + cdtext_init (&(env->gen.cdtext_track[i])); + env->tocent[i].track_format= track_format; + env->tocent[i].datastart = 0; + env->tocent[i].track_green = false; + if (TRACK_FORMAT_AUDIO == track_format) { + env->tocent[i].blocksize = CDIO_CD_FRAMESIZE_RAW; + env->tocent[i].datasize = CDIO_CD_FRAMESIZE_RAW; + env->tocent[i].endsize = 0; + } else { + env->tocent[i].datasize = CDIO_CD_FRAMESIZE; + env->tocent[i].datastart = 0; + } + } + } else if (2 == form2) { + int i; + for (i=0; i<env->gen.i_tracks; i++) { + cdtext_init (&(env->gen.cdtext_track[i])); + env->tocent[i].track_green = true; + env->tocent[i].track_format= track_format; + env->tocent[i].datasize = CDIO_CD_FRAMESIZE; + if (TRACK_FORMAT_XA == track_format) { + env->tocent[i].datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE; + env->tocent[i].endsize = CDIO_CD_SYNC_SIZE + + CDIO_CD_ECC_SIZE; + } else { + env->tocent[i].datastart = CDIO_CD_SYNC_SIZE + + CDIO_CD_HEADER_SIZE; + env->tocent[i].endsize = CDIO_CD_EDC_SIZE + + CDIO_CD_M1F1_ZERO_SIZE + CDIO_CD_ECC_SIZE; + + } + } + } else { + cdio_log (log_level, "Don't know if form1 or form2 form2: %x\n", + form2); + } + 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; + break; + + 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 (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_info ("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; + env->disc_mode = CDIO_DISC_MODE_CD_DATA; + break; + case 2: + /* Mode 2 form 1 */ + track_format = TRACK_FORMAT_XA; + track_green = false; /* ?? */ + blocksize = CDIO_CD_FRAMESIZE; + env->disc_mode = CDIO_DISC_MODE_CD_XA; + break; + case 3: + /* Mode 2 */ + track_format = TRACK_FORMAT_XA; + track_green = true; + blocksize = M2RAW_SECTOR_SIZE; + env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ?? */ + break; + case 06: + /* Mode2 form mix */ + track_format = TRACK_FORMAT_XA; + track_green = true; + blocksize = M2RAW_SECTOR_SIZE; + env->disc_mode = CDIO_DISC_MODE_CD_MIXED; + break; + case 0x20: /* ??? Mode2 form 2, Mode2 raw?? */ + track_format = TRACK_FORMAT_XA; + track_green = true; + blocksize = M2RAW_SECTOR_SIZE; + env->disc_mode = CDIO_DISC_MODE_CD_XA; /* ??. */ + break; + case 7: + track_format = TRACK_FORMAT_AUDIO; + track_green = false; + blocksize = CDIO_CD_FRAMESIZE_RAW; + env->disc_mode = CDIO_DISC_MODE_CD_DA; + 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 (env, _start, _len, _start2, blocksize, + track_format, track_green, 0); + + } + } + 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 (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_info ("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 (env, _start, _len, _start2, blocksize, + track_format, track_green, 0); + } + } + break; + } + + case SINF_ID: { /* "SINF" */ + + uint32_t *_sessions = (void *) chunk->data; + + cdio_assert (UINT32_FROM_BE (chunk->len) == 4); + + cdio_debug ("SINF: %lu sessions", + (long unsigned int) UINT32_FROM_BE (*_sessions)); + } + break; + + case MTYP_ID: { /* "MTYP" */ + uint32_t *mtyp_p = (void *) chunk->data; + uint32_t mtyp = UINT32_FROM_BE (*mtyp_p); + + cdio_assert (UINT32_FROM_BE (chunk->len) == 4); + + cdio_debug ("MTYP: %lu", + (long unsigned int) UINT32_FROM_BE (*mtyp_p)); + + if (mtyp != MTYP_AUDIO_CD) { + cdio_log (log_level, + "Unknown MTYP value: %u", (unsigned int) mtyp); + } + env->mtyp = mtyp; + } + break; + + case CDTX_ID: { /* "CD TEXT" */ + + cdio_log (log_level, + "Don't know how to handle CD TEXT yet" ); + 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); + } + } + + /* Fake out leadout track. */ + /* Don't use _stat_size_nrg since that will lead to recursion since + we haven't fully initialized things yet. + */ + cdio_lsn_to_msf (env->size, &env->tocent[env->gen.i_tracks].start_msf); + env->tocent[env->gen.i_tracks].start_lba = cdio_lsn_to_lba(env->size); + env->tocent[env->gen.i_tracks-1].sec_count = + cdio_lsn_to_lba(env->size - env->tocent[env->gen.i_tracks-1].start_lba); + + env->gen.b_cdtext_init = true; + env->gen.b_cdtext_error = false; + env->gen.toc_init = true; + free(footer_buf); + return true; +} + +/*! + Initialize image structures. + */ +static bool +_init_nrg (_img_private_t *env) +{ + if (env->gen.init) { + cdio_error ("init called more than once"); + return false; + } + + if (!(env->gen.data_source = cdio_stdio_new (env->gen.source_name))) { + cdio_warn ("can't open nrg image file %s for reading", + env->gen.source_name); + return false; + } + + env->psz_mcn = NULL; + env->disc_mode = CDIO_DISC_MODE_NO_INFO; + + cdtext_init (&(env->gen.cdtext)); + + if ( !parse_nrg (env, env->gen.source_name) ) { + cdio_warn ("image file %s is not a Nero image", + env->gen.source_name); + return false; + } + + env->gen.init = true; + return true; + +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + Would be libc's seek() but we have to adjust for the extra track header + information in each sector. +*/ +static off_t +_lseek_nrg (void *user_data, off_t offset, int whence) +{ + _img_private_t *env = user_data; + + /* real_offset is the real byte offset inside the disk image + The number below was determined empirically. + */ + off_t real_offset= env->is_dao ? 0x4b000 : 0; + + unsigned int i; + + for (i=0; i<env->gen.i_tracks; i++) { + track_info_t *this_track=&(env->tocent[i]); + env->pos.index = i; + if ( (this_track->sec_count*this_track->datasize) >= offset) { + int blocks = offset / this_track->datasize; + int rem = offset % this_track->datasize; + int block_offset = blocks * this_track->blocksize; + real_offset += block_offset + rem; + env->pos.buff_offset = rem; + env->pos.lba += blocks; + break; + } + real_offset += this_track->sec_count*this_track->blocksize; + offset -= this_track->sec_count*this_track->datasize; + env->pos.lba += this_track->sec_count; + } + + if (i==env->gen.i_tracks) { + cdio_warn ("seeking outside range of disk image"); + return -1; + } else + real_offset += env->tocent[i].datastart; + return cdio_stream_seek(env->gen.data_source, real_offset, whence); +} + +/*! + Reads into buf the next size bytes. + Returns -1 on error. + FIXME: + At present we assume a read doesn't cross sector or track + boundaries. +*/ +static ssize_t +_read_nrg (void *user_data, void *buf, size_t size) +{ + _img_private_t *env = user_data; + return cdio_stream_read(env->gen.data_source, buf, size, 1); +} + +static uint32_t +_stat_size_nrg (void *user_data) +{ + _img_private_t *env = user_data; + + return env->size; +} + +/*! + Reads a single audio sector from CD device into data starting + from LSN. Returns 0 if no error. + */ +static int +_read_audio_sectors_nrg (void *user_data, void *data, lsn_t lsn, + unsigned int nblocks) +{ + _img_private_t *env = user_data; + + CdioListNode *node; + + if (lsn >= env->size) + { + cdio_warn ("trying to read beyond image size (%lu >= %lu)", + (long unsigned int) lsn, (long unsigned int) env->size); + return -1; + } + + _CDIO_LIST_FOREACH (node, env->mapping) { + _mapping_t *_map = _cdio_list_node_data (node); + + if (IN (lsn, _map->start_lsn, (_map->start_lsn + _map->sec_count - 1))) { + int ret; + long int img_offset = _map->img_offset; + + img_offset += (lsn - _map->start_lsn) * CDIO_CD_FRAMESIZE_RAW; + + ret = cdio_stream_seek (env->gen.data_source, img_offset, + SEEK_SET); + if (ret!=0) return ret; + ret = cdio_stream_read (env->gen.data_source, data, + CDIO_CD_FRAMESIZE_RAW, nblocks); + if (ret==0) return ret; + break; + } + } + + if (!node) cdio_warn ("reading into pre gap (lsn %lu)", + (long unsigned int) lsn); + + return 0; +} + +static int +_read_mode1_sector_nrg (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *env = user_data; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + + CdioListNode *node; + + if (lsn >= env->size) + { + cdio_warn ("trying to read beyond image size (%lu >= %lu)", + (long unsigned int) lsn, (long unsigned int) env->size); + return -1; + } + + _CDIO_LIST_FOREACH (node, env->mapping) { + _mapping_t *_map = _cdio_list_node_data (node); + + if (IN (lsn, _map->start_lsn, (_map->start_lsn + _map->sec_count - 1))) { + int ret; + long int img_offset = _map->img_offset; + + img_offset += (lsn - _map->start_lsn) * _map->blocksize; + + ret = cdio_stream_seek (env->gen.data_source, img_offset, + SEEK_SET); + if (ret!=0) return ret; + + /* FIXME: Not completely sure the below is correct. */ + ret = cdio_stream_read (env->gen.data_source, + (M2RAW_SECTOR_SIZE == _map->blocksize) + ? (buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE) + : buf, + _map->blocksize, 1); + if (ret==0) return ret; + break; + } + } + + if (!node) + cdio_warn ("reading into pre gap (lsn %lu)", (long unsigned int) lsn); + + memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode1_sectors_nrg (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned nblocks) +{ + _img_private_t *env = user_data; + int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode1_sector_nrg (env, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +static int +_read_mode2_sector_nrg (void *user_data, void *data, lsn_t lsn, + bool b_form2) +{ + _img_private_t *env = user_data; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + + CdioListNode *node; + + if (lsn >= env->size) + { + cdio_warn ("trying to read beyond image size (%lu >= %lu)", + (long unsigned int) lsn, (long unsigned int) env->size); + return -1; + } + + _CDIO_LIST_FOREACH (node, env->mapping) { + _mapping_t *_map = _cdio_list_node_data (node); + + if (IN (lsn, _map->start_lsn, (_map->start_lsn + _map->sec_count - 1))) { + int ret; + long int img_offset = _map->img_offset; + + img_offset += (lsn - _map->start_lsn) * _map->blocksize; + + ret = cdio_stream_seek (env->gen.data_source, img_offset, + SEEK_SET); + if (ret!=0) return ret; + ret = cdio_stream_read (env->gen.data_source, + (M2RAW_SECTOR_SIZE == _map->blocksize) + ? (buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE) + : buf, + _map->blocksize, 1); + if (ret==0) return ret; + break; + } + } + + if (!node) + cdio_warn ("reading into pre gap (lsn %lu)", (long unsigned int) lsn); + + if (b_form2) + memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE, + M2RAW_SECTOR_SIZE); + else + memcpy (data, buf + CDIO_CD_XA_SYNC_HEADER, CDIO_CD_FRAMESIZE); + + return 0; +} + +/*! + Reads nblocks of mode2 sectors from cd device into data starting + from lsn. + Returns 0 if no error. + */ +static int +_read_mode2_sectors_nrg (void *user_data, void *data, lsn_t lsn, + bool b_form2, unsigned nblocks) +{ + _img_private_t *env = user_data; + int i; + int retval; + unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE; + + for (i = 0; i < nblocks; i++) { + if ( (retval = _read_mode2_sector_nrg (env, + ((char *)data) + (blocksize * i), + lsn + i, b_form2)) ) + return retval; + } + return 0; +} + +/* + Free memory resources associated with NRG object. +*/ +static void +_free_nrg (void *user_data) +{ + _img_private_t *env = user_data; + + if (NULL == env) return; + if (NULL != env->mapping) + _cdio_list_free (env->mapping, true); + + /* The remaining part of the image is like the other image drivers, + so free that in the same way. */ + _free_image(user_data); +} + +/*! + Eject media -- there's nothing to do here except free resources. + We always return 2. + */ +static int +_eject_media_nrg(void *obj) +{ + _free_nrg (obj); + return 2; +} + +/*! + Return an array of strings giving possible NRG disk images. + */ +char ** +cdio_get_devices_nrg (void) +{ + char **drives = NULL; + unsigned int num_files=0; +#ifdef HAVE_GLOB_H + unsigned int i; + glob_t globbuf; + globbuf.gl_offs = 0; + glob("*.nrg", GLOB_DOOFFS, NULL, &globbuf); + for (i=0; i<globbuf.gl_pathc; i++) { + cdio_add_device_list(&drives, globbuf.gl_pathv[i], &num_files); + } + globfree(&globbuf); +#else + cdio_add_device_list(&drives, DEFAULT_CDIO_DEVICE, &num_files); +#endif /*HAVE_GLOB_H*/ + cdio_add_device_list(&drives, NULL, &num_files); + return drives; +} + +/*! + Return a string containing the default CD device. + */ +char * +cdio_get_default_device_nrg(void) +{ + char **drives = cdio_get_devices_nrg(); + char *drive = (drives[0] == NULL) ? NULL : strdup(drives[0]); + cdio_free_device_list(drives); + return drive; +} + +static bool +get_hwinfo_nrg ( const CdIo *p_cdio, /*out*/ cdio_hwinfo_t *hw_info) +{ + strcpy(hw_info->psz_vendor, "libcdio"); + strcpy(hw_info->psz_model, "Nero"); + strcpy(hw_info->psz_revision, CDIO_VERSION); + return true; + +} + +/*! + Return the number of tracks in the current medium. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_format_t +get_track_format_nrg(void *user_data, track_t track_num) +{ + _img_private_t *env = user_data; + + if (track_num > env->gen.i_tracks || track_num == 0) + return TRACK_FORMAT_ERROR; + + if ( env->dtyp != DTYP_INVALID) { + switch (env->dtyp) { + case DTYP_MODE2_XA: + return TRACK_FORMAT_XA; + case DTYP_MODE1: + return TRACK_FORMAT_DATA; + default: ; + } + } + + /*if ( MTYP_AUDIO_CD == env->mtyp) return TRACK_FORMAT_AUDIO; */ + return env->tocent[track_num-1].track_format; +} + +/*! + Return true if we have XA data (green, mode2 form1) or + XA data (green, mode2 form2). That is track begins: + sync - header - subheader + 12 4 - 8 + + FIXME: there's gotta be a better design for this and get_track_format? +*/ +static bool +_get_track_green_nrg(void *user_data, track_t track_num) +{ + _img_private_t *env = user_data; + + if (track_num > env->gen.i_tracks || track_num == 0) + return false; + + if ( MTYP_AUDIO_CD == env->mtyp) return false; + return env->tocent[track_num-1].track_green; +} + +/*! + Check that a NRG file is valid. + +*/ +/* Later we'll probably do better. For now though, this gets us + started for now. +*/ +bool +cdio_is_nrg(const char *psz_nrg) +{ + unsigned int i; + + if (psz_nrg == NULL) return false; + + i=strlen(psz_nrg)-strlen("nrg"); + + if (i>0) { + if (psz_nrg[i]=='n' && psz_nrg[i+1]=='r' && psz_nrg[i+2]=='g') { + return true; + } + else if (psz_nrg[i]=='N' && psz_nrg[i+1]=='R' && psz_nrg[i+2]=='G') { + return true; + } + } + return false; +} + +/*! + Initialization routine. This is the only thing that doesn't + get called via a function pointer. In fact *we* are the + ones to set that up. + */ +CdIo * +cdio_open_am_nrg (const char *psz_source_name, const char *psz_access_mode) +{ + if (psz_access_mode != NULL && strcmp(psz_access_mode, "image")) + cdio_warn ("there is only one access mode for nrg. Arg %s ignored", + psz_access_mode); + return cdio_open_nrg(psz_source_name); +} + + +CdIo * +cdio_open_nrg (const char *psz_source) +{ + CdIo *ret; + _img_private_t *_data; + + cdio_funcs _funcs; + + memset( &_funcs, 0, sizeof(_funcs) ); + + _funcs.eject_media = _eject_media_nrg; + _funcs.free = _free_nrg; + _funcs.get_arg = _get_arg_image; + _funcs.get_cdtext = get_cdtext_generic; + _funcs.get_devices = cdio_get_devices_nrg; + _funcs.get_default_device = cdio_get_default_device_nrg; + _funcs.get_discmode = _get_discmode_image; + _funcs.get_drive_cap = _get_drive_cap_image; + _funcs.get_first_track_num= _get_first_track_num_image; + _funcs.get_hwinfo = get_hwinfo_nrg; + _funcs.get_mcn = _get_mcn_image; + _funcs.get_num_tracks = _get_num_tracks_image; + _funcs.get_track_format = get_track_format_nrg; + _funcs.get_track_green = _get_track_green_nrg; + _funcs.get_track_lba = NULL; /* Will use generic routine via msf */ + _funcs.get_track_msf = _get_track_msf_image; + _funcs.lseek = _lseek_nrg; + _funcs.read = _read_nrg; + _funcs.read_audio_sectors = _read_audio_sectors_nrg; + _funcs.read_mode1_sector = _read_mode1_sector_nrg; + _funcs.read_mode1_sectors = _read_mode1_sectors_nrg; + _funcs.read_mode2_sector = _read_mode2_sector_nrg; + _funcs.read_mode2_sectors = _read_mode2_sectors_nrg; + _funcs.set_arg = _set_arg_image; + _funcs.stat_size = _stat_size_nrg; + + _data = _cdio_malloc (sizeof (_img_private_t)); + _data->gen.init = false; + + _data->gen.i_tracks = 0; + _data->mtyp = 0; + _data->dtyp = DTYP_INVALID; + _data->gen.i_first_track= 1; + _data->is_dao = false; + _data->is_cues = false; /* FIXME: remove is_cues. */ + + ret = cdio_new ((void *)_data, &_funcs); + + if (ret == NULL) { + free(_data); + return NULL; + } + + _set_arg_image(_data, "source", (NULL == psz_source) + ? DEFAULT_CDIO_DEVICE: psz_source); + + _data->psz_cue_name = strdup(_get_arg_image(_data, "source")); + + if (!cdio_is_nrg(_data->psz_cue_name)) { + cdio_debug ("source name %s is not recognized as a NRG image", + _data->psz_cue_name); + _free_nrg(_data); + return NULL; + } + + _set_arg_image (_data, "cue", _data->psz_cue_name); + + if (_init_nrg(_data)) + return ret; + else { + _free_nrg(_data); + free(ret); + return NULL; + } + +} + +bool +cdio_have_nrg (void) +{ + return true; +} diff --git a/contrib/libcdio/image/nrg.h b/contrib/libcdio/image/nrg.h new file mode 100644 index 000000000..2923c78f1 --- /dev/null +++ b/contrib/libcdio/image/nrg.h @@ -0,0 +1,115 @@ +/* + $Id: nrg.h,v 1.1 2005/01/01 02:43:59 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + Copyright (C) 2001, 2003 Herbert Valerio Riedel <hvr@gnu.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* NERO (NRG) file format structures. */ + +/* this ugly image format is typical for lazy win32 programmers... at + least structure were set big endian, so at reverse + engineering wasn't such a big headache... */ + +PRAGMA_BEGIN_PACKED +typedef union { + struct { + uint32_t __x GNUC_PACKED; + uint32_t ID GNUC_PACKED; + uint32_t footer_ofs GNUC_PACKED; + } v50; + struct { + uint32_t ID GNUC_PACKED; + uint64_t footer_ofs GNUC_PACKED; + } v55; +} _footer_t; + +typedef struct { + uint32_t start GNUC_PACKED; + uint32_t length GNUC_PACKED; + uint32_t type GNUC_PACKED; /* 0x0 -> MODE1, 0x2 -> MODE2 form1, + 0x3 -> MIXED_MODE2 2336 blocksize + */ + uint32_t start_lsn GNUC_PACKED; /* does not include any pre-gaps! */ + uint32_t _unknown GNUC_PACKED; /* wtf is this for? -- always zero... */ +} _etnf_array_t; + +/* Finally they realized that 32-bit offsets are a bit outdated for + IA64 *eg* */ +typedef struct { + uint64_t start GNUC_PACKED; + uint64_t length GNUC_PACKED; + uint32_t type GNUC_PACKED; /* 0x0 -> MODE1, 0x2 -> MODE2 form1, + 0x3 -> MIXED_MODE2 2336 blocksize + */ + uint32_t start_lsn GNUC_PACKED; + uint64_t _unknown GNUC_PACKED; /* wtf is this for? -- always zero... */ +} _etn2_array_t; + +typedef struct { + uint8_t type GNUC_PACKED; /* has track copy bit and whether audiofile + or datafile. Is often 0x41 == 'A' */ + uint8_t track GNUC_PACKED; /* binary or BCD?? */ + uint8_t addr_ctrl GNUC_PACKED; /* addresstype: MSF or LBA in lower 4 bits + control in upper 4 bits. + makes 0->1 transitions */ + uint8_t res GNUC_PACKED; /* ?? */ + uint32_t lsn GNUC_PACKED; +} _cuex_array_t; + +typedef struct { + uint32_t _unknown1 GNUC_PACKED; + char psz_mcn[CDIO_MCN_SIZE] GNUC_PACKED; + uint8_t _unknown[64-CDIO_MCN_SIZE-sizeof(uint32_t)] GNUC_PACKED; +} _daox_array_t; + +typedef struct { + uint32_t _unknown1 GNUC_PACKED; + char psz_mcn[CDIO_MCN_SIZE] GNUC_PACKED; + uint8_t _unknown[64-CDIO_MCN_SIZE-sizeof(uint32_t)] GNUC_PACKED; +} _daoi_array_t; + +typedef struct { + uint32_t id GNUC_PACKED; + uint32_t len GNUC_PACKED; + char data[EMPTY_ARRAY_SIZE] GNUC_PACKED; +} _chunk_t; + +PRAGMA_END_PACKED + +/* Nero images are Big Endian. */ +#define CDTX_ID 0x43445458 /* CD TEXT */ +#define CUEX_ID 0x43554558 /* Nero version 5.5.x-6.x */ +#define CUES_ID 0x43554553 /* Nero pre version 5.5.x-6.x */ +#define DAOX_ID 0x44414f58 /* Nero version 5.5.x-6.x */ +#define DAOI_ID 0x44414f49 +#define END1_ID 0x454e4421 +#define ETN2_ID 0x45544e32 +#define ETNF_ID 0x45544e46 +#define NER5_ID 0x4e455235 /* Nero version 5.5.x */ +#define NERO_ID 0x4e45524f /* Nero pre 5.5.x */ +#define SINF_ID 0x53494e46 /* Session information */ +#define MTYP_ID 0x4d545950 /* Disc Media type? */ + +#define MTYP_AUDIO_CD 1 /* This isn't correct. But I don't know the + the right thing is and it sometimes works (and + sometimes is wrong). */ + +/* Disk track type Values gleaned from DAOX */ +#define DTYP_MODE1 0 +#define DTYP_MODE2_XA 2 +#define DTYP_INVALID 255 diff --git a/contrib/libcdio/image_common.h b/contrib/libcdio/image_common.h new file mode 100644 index 000000000..30699c15c --- /dev/null +++ b/contrib/libcdio/image_common.h @@ -0,0 +1,225 @@ +/* + $Id: image_common.h,v 1.1 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*! Common image routines. + + Because _img_private_t may vary over image formats, the routines are + included into the image drivers after _img_private_t is defined. In + order for the below routines to work, there is a large part of + _img_private_t that is common among image drivers. For example, see + image.h +*/ + +#ifndef __CDIO_IMAGE_COMMON_H__ +#define __CDIO_IMAGE_COMMON_H__ + +#define free_if_notnull(obj) \ + if (NULL != obj) { free(obj); obj=NULL; }; + +/*! + We don't need the image any more. Free all memory associated with + it. + */ +static void +_free_image (void *user_data) +{ + _img_private_t *p_env = user_data; + track_t i_track; + + if (NULL == p_env) return; + + for (i_track=0; i_track < p_env->gen.i_tracks; i_track++) { + free_if_notnull(p_env->tocent[i_track].filename); + free_if_notnull(p_env->tocent[i_track].isrc); + cdtext_destroy(&(p_env->tocent[i_track].cdtext)); + } + + free_if_notnull(p_env->psz_mcn); + free_if_notnull(p_env->psz_cue_name); + cdtext_destroy(&(p_env->gen.cdtext)); + cdio_generic_stdio_free(p_env); + free(p_env); +} + +#ifdef NEED_MEDIA_EJECT_IMAGE +/*! + Eject media -- there's nothing to do here except free resources. + We always return 2. + */ +static int +_eject_media_image(void *user_data) +{ + _free_image (user_data); + return 2; +} +#endif + +/*! + Return the value associated with the key "arg". +*/ +static const char * +_get_arg_image (void *user_data, const char key[]) +{ + _img_private_t *p_env = user_data; + + if (!strcmp (key, "source")) { + return p_env->gen.source_name; + } else if (!strcmp (key, "cue")) { + return p_env->psz_cue_name; + } else if (!strcmp(key, "access-mode")) { + return "image"; + } + return NULL; +} + +/*! + Get disc type associated with cd_obj. +*/ +static discmode_t +_get_discmode_image (void *p_user_data) +{ + _img_private_t *p_env = p_user_data; + return p_env->disc_mode; +} + +/*! + Return the media catalog number (MCN) from the CD or NULL if there + is none or we don't have the ability to get it. + + Note: string is malloc'd so caller has to free() the returned + string when done with it. + */ +static char * +_get_mcn_image(const void *user_data) +{ + const _img_private_t *env = user_data; + + if (NULL == env || NULL == env->psz_mcn) return NULL; + return strdup(env->psz_mcn); +} + +/*! + Return the starting MSF (minutes/secs/frames) for the track number + track_num in obj. Tracks numbers start at 1. + The "leadout" track is specified either by + using track_num LEADOUT_TRACK or the total tracks+1. + +*/ +static bool +_get_track_msf_image(void *user_data, track_t track_num, msf_t *msf) +{ + _img_private_t *env = user_data; + + if (NULL == msf) return false; + + if (track_num == CDIO_CDROM_LEADOUT_TRACK) track_num = env->gen.i_tracks+1; + + if (track_num <= env->gen.i_tracks+1 && track_num != 0) { + *msf = env->tocent[track_num-env->gen.i_first_track].start_msf; + return true; + } else + return false; +} + +/*! + Return the number of of the first track. + CDIO_INVALID_TRACK is returned on error. +*/ +static track_t +_get_first_track_num_image(void *user_data) +{ + _img_private_t *env = user_data; + + return env->gen.i_first_track; +} + +/*! + Return the number of tracks. +*/ +static track_t +_get_num_tracks_image(void *user_data) +{ + _img_private_t *env = user_data; + + return env->gen.i_tracks; +} + +/*! + Set the arg "key" with "value" in the source device. + Currently "source" to set the source device in I/O operations + is the only valid key. + + 0 is returned if no error was found, and nonzero if there as an error. +*/ +static int +_set_arg_image (void *user_data, const char key[], const char value[]) +{ + _img_private_t *env = user_data; + + if (!strcmp (key, "source")) + { + free_if_notnull (env->gen.source_name); + + if (!value) + return -2; + + env->gen.source_name = strdup (value); + } + else if (!strcmp (key, "cue")) + { + free_if_notnull (env->psz_cue_name); + + if (!value) + return -2; + + env->psz_cue_name = strdup (value); + } + else + return -1; + + return 0; +} + +/*! + Return the the kind of drive capabilities of device. + + */ +static void +_get_drive_cap_image (const void *user_data, + cdio_drive_read_cap_t *p_read_cap, + cdio_drive_write_cap_t *p_write_cap, + cdio_drive_misc_cap_t *p_misc_cap) +{ + + *p_read_cap = CDIO_DRIVE_CAP_READ_AUDIO + | CDIO_DRIVE_CAP_READ_CD_G + | CDIO_DRIVE_CAP_READ_CD_R + | CDIO_DRIVE_CAP_READ_CD_RW + ; + + *p_write_cap = 0; + + /* In the future we may want to simulate + LOCK, OPEN_TRAY, CLOSE_TRAY, SELECT_SPEED, etc. + */ + *p_misc_cap = CDIO_DRIVE_CAP_MISC_FILE; +} + +#endif /* __CDIO_IMAGE_COMMON_H__ */ diff --git a/contrib/libcdio/iso9660.c b/contrib/libcdio/iso9660.c new file mode 100644 index 000000000..91b89d516 --- /dev/null +++ b/contrib/libcdio/iso9660.c @@ -0,0 +1,1007 @@ +/* + $Id: iso9660.c,v 1.4 2006/09/26 18:13:11 dgp85 Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Private headers */ +#include "iso9660_private.h" +#include "cdio_assert.h" + +/* Public headers */ +#include <cdio/bytesex.h> +#include <cdio/iso9660.h> +#include <cdio/util.h> + +#include <time.h> +#include <ctype.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif + +static const char _rcsid[] = "$Id: iso9660.c,v 1.4 2006/09/26 18:13:11 dgp85 Exp $"; + +/* some parameters... */ +#define SYSTEM_ID "CD-RTOS CD-BRIDGE" +#define VOLUME_SET_ID "" + +/*! + Change trailing blanks in str to nulls. Str has a maximum size of + n characters. +*/ +static char * +strip_trail (const char str[], size_t n) +{ + static char buf[1025]; + int j; + + cdio_assert (n < 1024); + + strncpy (buf, str, n); + buf[n] = '\0'; + + for (j = strlen (buf) - 1; j >= 0; j--) + { + if (buf[j] != ' ') + break; + + buf[j] = '\0'; + } + + return buf; +} + +static void +pathtable_get_size_and_entries(const void *pt, unsigned int *size, + unsigned int *entries); + +/*! + Get time structure from structure in an ISO 9660 directory index + record. Even though tm_wday and tm_yday fields are not explicitly in + idr_date, the are calculated from the other fields. + + If tm is to reflect the localtime set b_localtime true, otherwise + tm will reported in GMT. +*/ +void +iso9660_get_dtime (const iso9660_dtime_t *idr_date, bool b_localtime, + /*out*/ struct tm *p_tm) +{ + time_t t; + struct tm *p_temp_tm; + + if (!idr_date) return; + + memset(p_tm, 0, sizeof(struct tm)); + p_tm->tm_year = idr_date->dt_year; + p_tm->tm_mon = idr_date->dt_month - 1; + p_tm->tm_mday = idr_date->dt_day; + p_tm->tm_hour = idr_date->dt_hour; + p_tm->tm_min = idr_date->dt_minute; + p_tm->tm_sec = idr_date->dt_second; + +#if defined(HAVE_TM_GMTOFF) && defined(HAVE_TZSET) + if (b_localtime) { + tzset(); +#if defined(HAVE_TZNAME) + p_tm->tm_zone = (char *) tzname; +#endif +#if defined(HAVE_DAYLIGHT) + p_tm->tm_isdst = daylight; + p_tm->tm_gmtoff = timezone; +#endif + } +#endif + + /* Recompute tm_wday and tm_yday via mktime. */ + t = mktime(p_tm); + + if (b_localtime) + p_temp_tm = localtime(&t); + else + p_temp_tm = gmtime(&t); + + memcpy(p_tm, p_temp_tm, sizeof(struct tm)); +} + +/*! + Set time in format used in ISO 9660 directory index record + from a Unix time structure. */ +void +iso9660_set_dtime (const struct tm *p_tm, /*out*/ iso9660_dtime_t *p_idr_date) +{ + memset (p_idr_date, 0, 7); + + if (!p_tm) return; + + p_idr_date->dt_year = p_tm->tm_year; + p_idr_date->dt_month = p_tm->tm_mon + 1; + p_idr_date->dt_day = p_tm->tm_mday; + p_idr_date->dt_hour = p_tm->tm_hour; + p_idr_date->dt_minute = p_tm->tm_min; + p_idr_date->dt_second = p_tm->tm_sec; + +#ifdef HAVE_TM_GMTOFF + /* The ISO 9660 timezone is in the range -48..+52 and each unit + represents a 15-minute interval. */ + p_idr_date->dt_gmtoff = p_tm->tm_gmtoff / (15 * 60); + + if (p_tm->tm_isdst) p_idr_date->dt_gmtoff -= 4; + + if (p_idr_date->dt_gmtoff < -48 ) { + + cdio_warn ("Converted ISO 9660 timezone %d is less than -48. Adjusted", + p_idr_date->dt_gmtoff); + p_idr_date->dt_gmtoff = -48; + } else if (p_idr_date->dt_gmtoff > 52) { + cdio_warn ("Converted ISO 9660 timezone %d is over 52. Adjusted", + p_idr_date->dt_gmtoff); + p_idr_date->dt_gmtoff = 52; + } +#else + p_idr_date->dt_gmtoff = 0; +#endif +} + +/*! + Set "long" time in format used in ISO 9660 primary volume descriptor + from a Unix time structure. */ +void +iso9660_set_ltime (const struct tm *_tm, /*out*/ iso9660_ltime_t *pvd_date) +{ + char *_pvd_date = (char *) pvd_date; + + memset (_pvd_date, '0', 16); + _pvd_date[16] = (int8_t) 0; /* tz */ + + if (!_tm) return; + + snprintf(_pvd_date, 17, + "%4.4d%2.2d%2.2d" "%2.2d%2.2d%2.2d" "%2.2d", + _tm->tm_year + 1900, _tm->tm_mon + 1, _tm->tm_mday, + _tm->tm_hour, _tm->tm_min, _tm->tm_sec, + 0 /* 1/100 secs */ ); + + _pvd_date[16] = (int8_t) 0; /* tz */ +} + +/*! + Convert ISO-9660 file name that stored in a directory entry into + what's usually listed as the file name in a listing. + Lowercase name, and remove trailing ;1's or .;1's and + turn the other ;'s into version numbers. + + The length of the translated string is returned. +*/ +int +iso9660_name_translate(const char *old, char *new) +{ + return iso9660_name_translate_ext(old, new, 0); +} + +/*! + Convert ISO-9660 file name that stored in a directory entry into + what's usually listed as the file name in a listing. Lowercase + name if not using Joliet extension. Remove trailing ;1's or .;1's and + turn the other ;'s into version numbers. + + The length of the translated string is returned. +*/ +int +iso9660_name_translate_ext(const char *old, char *new, uint8_t i_joliet_level) +{ + int len = strlen(old); + int i; + + for (i = 0; i < len; i++) { + unsigned char c = old[i]; + if (!c) + break; + + /* Lower case, unless we have Joliet extensions. */ + if (!i_joliet_level && isupper(c)) c = tolower(c); + + /* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */ + if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1') + break; + + /* Drop trailing ';1' */ + if (c == ';' && i == len - 2 && old[i + 1] == '1') + break; + + /* Convert remaining ';' to '.' */ + if (c == ';') + c = '.'; + + new[i] = c; + } + new[i] = '\0'; + return i; +} + +/*! + Pad string src with spaces to size len and copy this to dst. If + len is less than the length of src, dst will be truncated to the + first len characters of src. + + src can also be scanned to see if it contains only ACHARs, DCHARs, + 7-bit ASCII chars depending on the enumeration _check. + + In addition to getting changed, dst is the return value. + Note: this string might not be NULL terminated. + */ +char * +iso9660_strncpy_pad(char dst[], const char src[], size_t len, + enum strncpy_pad_check _check) +{ + size_t rlen; + + cdio_assert (dst != NULL); + cdio_assert (src != NULL); + cdio_assert (len > 0); + + switch (_check) + { + int idx; + case ISO9660_NOCHECK: + break; + + case ISO9660_7BIT: + for (idx = 0; src[idx]; idx++) + if ((int8_t) src[idx] < 0) + { + cdio_warn ("string '%s' fails 7bit constraint (pos = %d)", + src, idx); + break; + } + break; + + case ISO9660_ACHARS: + for (idx = 0; src[idx]; idx++) + if (!iso9660_isachar (src[idx])) + { + cdio_warn ("string '%s' fails a-character constraint (pos = %d)", + src, idx); + break; + } + break; + + case ISO9660_DCHARS: + for (idx = 0; src[idx]; idx++) + if (!iso9660_isdchar (src[idx])) + { + cdio_warn ("string '%s' fails d-character constraint (pos = %d)", + src, idx); + break; + } + break; + + default: + cdio_assert_not_reached (); + break; + } + + rlen = strlen (src); + + if (rlen > len) + cdio_warn ("string '%s' is getting truncated to %d characters", + src, (unsigned int) len); + + strncpy (dst, src, len); + if (rlen < len) + memset(dst+rlen, ' ', len-rlen); + return dst; +} + +/*! + Return true if c is a DCHAR - a valid ISO-9660 level 1 character. + These are the ASCSII capital letters A-Z, the digits 0-9 and an + underscore. +*/ +bool +iso9660_isdchar (int c) +{ + if (!IN (c, 0x30, 0x5f) + || IN (c, 0x3a, 0x40) + || IN (c, 0x5b, 0x5e)) + return false; + + return true; +} + + +/*! + Return true if c is an ACHAR - + These are the DCHAR's plus some ASCII symbols including the space + symbol. +*/ +bool +iso9660_isachar (int c) +{ + if (!IN (c, 0x20, 0x5f) + || IN (c, 0x23, 0x24) + || c == 0x40 + || IN (c, 0x5b, 0x5e)) + return false; + + return true; +} + +void +iso9660_set_evd(void *pd) +{ + struct iso_volume_descriptor ied; + + cdio_assert (sizeof(struct iso_volume_descriptor) == ISO_BLOCKSIZE); + + cdio_assert (pd != NULL); + + memset(&ied, 0, sizeof(ied)); + + ied.type = to_711(ISO_VD_END); + iso9660_strncpy_pad (ied.id, ISO_STANDARD_ID, sizeof(ied.id), ISO9660_DCHARS); + ied.version = to_711(ISO_VERSION); + + memcpy(pd, &ied, sizeof(ied)); +} + +void +iso9660_set_pvd(void *pd, + const char volume_id[], + const char publisher_id[], + const char preparer_id[], + const char application_id[], + uint32_t iso_size, + const void *root_dir, + uint32_t path_table_l_extent, + uint32_t path_table_m_extent, + uint32_t path_table_size, + const time_t *pvd_time + ) +{ + iso9660_pvd_t ipd; + + cdio_assert (sizeof(iso9660_pvd_t) == ISO_BLOCKSIZE); + + cdio_assert (pd != NULL); + cdio_assert (volume_id != NULL); + cdio_assert (application_id != NULL); + + memset(&ipd,0,sizeof(ipd)); /* paranoia? */ + + /* magic stuff ... thatis CD XA marker... */ + strcpy(((char*)&ipd)+ISO_XA_MARKER_OFFSET, ISO_XA_MARKER_STRING); + + ipd.type = to_711(ISO_VD_PRIMARY); + iso9660_strncpy_pad (ipd.id, ISO_STANDARD_ID, 5, ISO9660_DCHARS); + ipd.version = to_711(ISO_VERSION); + + iso9660_strncpy_pad (ipd.system_id, SYSTEM_ID, 32, ISO9660_ACHARS); + iso9660_strncpy_pad (ipd.volume_id, volume_id, 32, ISO9660_DCHARS); + + ipd.volume_space_size = to_733(iso_size); + + ipd.volume_set_size = to_723(1); + ipd.volume_sequence_number = to_723(1); + ipd.logical_block_size = to_723(ISO_BLOCKSIZE); + + ipd.path_table_size = to_733(path_table_size); + ipd.type_l_path_table = to_731(path_table_l_extent); + ipd.type_m_path_table = to_732(path_table_m_extent); + + /* root_directory_record doesn't contain the 1-byte filename, + so we add one for that. */ + cdio_assert (sizeof(ipd.root_directory_record) == 33); + memcpy(&(ipd.root_directory_record), root_dir, + sizeof(ipd.root_directory_record)); + ipd.root_directory_filename='\0'; + ipd.root_directory_record.length = 33+1; + iso9660_strncpy_pad (ipd.volume_set_id, VOLUME_SET_ID, 128, ISO9660_DCHARS); + + iso9660_strncpy_pad (ipd.publisher_id, publisher_id, 128, ISO9660_ACHARS); + iso9660_strncpy_pad (ipd.preparer_id, preparer_id, 128, ISO9660_ACHARS); + iso9660_strncpy_pad (ipd.application_id, application_id, 128, ISO9660_ACHARS); + + iso9660_strncpy_pad (ipd.copyright_file_id , "", 37, ISO9660_DCHARS); + iso9660_strncpy_pad (ipd.abstract_file_id , "", 37, ISO9660_DCHARS); + iso9660_strncpy_pad (ipd.bibliographic_file_id, "", 37, ISO9660_DCHARS); + + iso9660_set_ltime (gmtime (pvd_time), &(ipd.creation_date)); + iso9660_set_ltime (gmtime (pvd_time), &(ipd.modification_date)); + iso9660_set_ltime (NULL, &(ipd.expiration_date)); + iso9660_set_ltime (NULL, &(ipd.effective_date)); + + ipd.file_structure_version = to_711(1); + + /* we leave ipd.application_data = 0 */ + + memcpy(pd, &ipd, sizeof(ipd)); /* copy stuff to arg ptr */ +} + +unsigned int +iso9660_dir_calc_record_size(unsigned int namelen, unsigned int su_len) +{ + unsigned int length; + + length = sizeof(iso9660_dir_t); + length += namelen; + if (length % 2) /* pad to word boundary */ + length++; + length += su_len; + if (length % 2) /* pad to word boundary again */ + length++; + + return length; +} + +void +iso9660_dir_add_entry_su(void *dir, + const char filename[], + uint32_t extent, + uint32_t size, + uint8_t file_flags, + const void *su_data, + unsigned int su_size, + const time_t *entry_time) +{ + iso9660_dir_t *idr = dir; + uint8_t *dir8 = dir; + unsigned int offset = 0; + uint32_t dsize = from_733(idr->size); + int length, su_offset; + cdio_assert (sizeof(iso9660_dir_t) == 33); + + if (!dsize && !idr->length) + dsize = ISO_BLOCKSIZE; /* for when dir lacks '.' entry */ + + cdio_assert (dsize > 0 && !(dsize % ISO_BLOCKSIZE)); + cdio_assert (dir != NULL); + cdio_assert (extent > 17); + cdio_assert (filename != NULL); + cdio_assert (strlen(filename) <= MAX_ISOPATHNAME); + + length = sizeof(iso9660_dir_t); + length += strlen(filename); + length = _cdio_ceil2block (length, 2); /* pad to word boundary */ + su_offset = length; + length += su_size; + length = _cdio_ceil2block (length, 2); /* pad to word boundary again */ + + /* find the last entry's end */ + { + unsigned int ofs_last_rec = 0; + + offset = 0; + while (offset < dsize) + { + if (!dir8[offset]) + { + offset++; + continue; + } + + offset += dir8[offset]; + ofs_last_rec = offset; + } + + cdio_assert (offset == dsize); + + offset = ofs_last_rec; + } + + /* be sure we don't cross sectors boundaries */ + offset = _cdio_ofs_add (offset, length, ISO_BLOCKSIZE); + offset -= length; + + cdio_assert (offset + length <= dsize); + + idr = (iso9660_dir_t *) &dir8[offset]; + + cdio_assert (offset+length < dsize); + + memset(idr, 0, length); + + idr->length = to_711(length); + idr->extent = to_733(extent); + idr->size = to_733(size); + + iso9660_set_dtime (gmtime(entry_time), &(idr->recording_time)); + + idr->file_flags = to_711(file_flags); + + idr->volume_sequence_number = to_723(1); + + idr->filename_len = to_711(strlen(filename) + ? strlen(filename) : 1); /* working hack! */ + + memcpy(idr->filename, filename, from_711(idr->filename_len)); + memcpy(&dir8[offset] + su_offset, su_data, su_size); +} + +void +iso9660_dir_init_new (void *dir, + uint32_t self, + uint32_t ssize, + uint32_t parent, + uint32_t psize, + const time_t *dir_time) +{ + iso9660_dir_init_new_su (dir, self, ssize, NULL, 0, parent, psize, NULL, + 0, dir_time); +} + +void +iso9660_dir_init_new_su (void *dir, + uint32_t self, + uint32_t ssize, + const void *ssu_data, + unsigned int ssu_size, + uint32_t parent, + uint32_t psize, + const void *psu_data, + unsigned int psu_size, + const time_t *dir_time) +{ + cdio_assert (ssize > 0 && !(ssize % ISO_BLOCKSIZE)); + cdio_assert (psize > 0 && !(psize % ISO_BLOCKSIZE)); + cdio_assert (dir != NULL); + + memset (dir, 0, ssize); + + /* "\0" -- working hack due to padding */ + iso9660_dir_add_entry_su (dir, "\0", self, ssize, ISO_DIRECTORY, ssu_data, + ssu_size, dir_time); + + iso9660_dir_add_entry_su (dir, "\1", parent, psize, ISO_DIRECTORY, psu_data, + psu_size, dir_time); +} + +/* Zero's out pathable. Do this first. */ +void +iso9660_pathtable_init (void *pt) +{ + cdio_assert (sizeof (struct iso_path_table) == 8); + + cdio_assert (pt != NULL); + + memset (pt, 0, ISO_BLOCKSIZE); /* fixme */ +} + +static const struct iso_path_table* +pathtable_get_entry (const void *pt, unsigned int entrynum) +{ + const uint8_t *tmp = pt; + unsigned int offset = 0; + unsigned int count = 0; + + cdio_assert (pt != NULL); + + while (from_711 (*tmp)) + { + if (count == entrynum) + break; + + cdio_assert (count < entrynum); + + offset += sizeof (struct iso_path_table); + offset += from_711 (*tmp); + if (offset % 2) + offset++; + tmp = (uint8_t *)pt + offset; + count++; + } + + if (!from_711 (*tmp)) + return NULL; + + return (const void *) tmp; +} + +void +pathtable_get_size_and_entries (const void *pt, + unsigned int *size, + unsigned int *entries) +{ + const uint8_t *tmp = pt; + unsigned int offset = 0; + unsigned int count = 0; + + cdio_assert (pt != NULL); + + while (from_711 (*tmp)) + { + offset += sizeof (struct iso_path_table); + offset += from_711 (*tmp); + if (offset % 2) + offset++; + tmp = (uint8_t *)pt + offset; + count++; + } + + if (size) + *size = offset; + + if (entries) + *entries = count; +} + +unsigned int +iso9660_pathtable_get_size (const void *pt) +{ + unsigned int size = 0; + pathtable_get_size_and_entries (pt, &size, NULL); + return size; +} + +uint16_t +iso9660_pathtable_l_add_entry (void *pt, + const char name[], + uint32_t extent, + uint16_t parent) +{ + struct iso_path_table *ipt = + (struct iso_path_table*)((char *)pt + iso9660_pathtable_get_size (pt)); + size_t name_len = strlen (name) ? strlen (name) : 1; + unsigned int entrynum = 0; + + cdio_assert (iso9660_pathtable_get_size (pt) < ISO_BLOCKSIZE); /*fixme */ + + memset (ipt, 0, sizeof (struct iso_path_table) + name_len); /* paranoia */ + + ipt->name_len = to_711 (name_len); + ipt->extent = to_731 (extent); + ipt->parent = to_721 (parent); + memcpy (ipt->name, name, name_len); + + pathtable_get_size_and_entries (pt, NULL, &entrynum); + + if (entrynum > 1) + { + const struct iso_path_table *ipt2 + = pathtable_get_entry (pt, entrynum - 2); + + cdio_assert (ipt2 != NULL); + + cdio_assert (from_721 (ipt2->parent) <= parent); + } + + return entrynum; +} + +uint16_t +iso9660_pathtable_m_add_entry (void *pt, + const char name[], + uint32_t extent, + uint16_t parent) +{ + struct iso_path_table *ipt = + (struct iso_path_table*)((char *)pt + iso9660_pathtable_get_size (pt)); + size_t name_len = strlen (name) ? strlen (name) : 1; + unsigned int entrynum = 0; + + cdio_assert (iso9660_pathtable_get_size(pt) < ISO_BLOCKSIZE); /* fixme */ + + memset(ipt, 0, sizeof (struct iso_path_table) + name_len); /* paranoia */ + + ipt->name_len = to_711 (name_len); + ipt->extent = to_732 (extent); + ipt->parent = to_722 (parent); + memcpy (ipt->name, name, name_len); + + pathtable_get_size_and_entries (pt, NULL, &entrynum); + + if (entrynum > 1) + { + const struct iso_path_table *ipt2 + = pathtable_get_entry (pt, entrynum - 2); + + cdio_assert (ipt2 != NULL); + + cdio_assert (from_722 (ipt2->parent) <= parent); + } + + return entrynum; +} + +/*! + Check that pathname is a valid ISO-9660 directory name. + + A valid directory name should not start out with a slash (/), + dot (.) or null byte, should be less than 37 characters long, + have no more than 8 characters in a directory component + which is separated by a /, and consist of only DCHARs. + */ +bool +iso9660_dirname_valid_p (const char pathname[]) +{ + const char *p = pathname; + int len; + + cdio_assert (pathname != NULL); + + if (*p == '/' || *p == '.' || *p == '\0') + return false; + + if (strlen (pathname) > MAX_ISOPATHNAME) + return false; + + len = 0; + for (; *p; p++) + if (iso9660_isdchar (*p)) + { + len++; + if (len > 8) + return false; + } + else if (*p == '/') + { + if (!len) + return false; + len = 0; + } + else + return false; /* unexpected char */ + + if (!len) + return false; /* last char may not be '/' */ + + return true; +} + +/*! + Check that pathname is a valid ISO-9660 pathname. + + A valid pathname contains a valid directory name, if one appears and + the filename portion should be no more than 8 characters for the + file prefix and 3 characters in the extension (or portion after a + dot). There should be exactly one dot somewhere in the filename + portion and the filename should be composed of only DCHARs. + + True is returned if pathname is valid. + */ +bool +iso9660_pathname_valid_p (const char pathname[]) +{ + const char *p = NULL; + + cdio_assert (pathname != NULL); + + if ((p = strrchr (pathname, '/'))) + { + bool rc; + char *_tmp = strdup (pathname); + + *strrchr (_tmp, '/') = '\0'; + + rc = iso9660_dirname_valid_p (_tmp); + + free (_tmp); + + if (!rc) + return false; + + p++; + } + else + p = pathname; + + if (strlen (pathname) > (MAX_ISOPATHNAME - 6)) + return false; + + { + int len = 0; + int dots = 0; + + for (; *p; p++) + if (iso9660_isdchar (*p)) + { + len++; + if (dots == 0 ? len > 8 : len > 3) + return false; + } + else if (*p == '.') + { + dots++; + if (dots > 1) + return false; + if (!len) + return false; + len = 0; + } + else + return false; + + if (dots != 1) + return false; + } + + return true; +} + +/*! + Take pathname and a version number and turn that into a ISO-9660 + pathname. (That's just the pathname followd by ";" and the version + number. For example, mydir/file.ext -> mydir/file.ext;1 for version + 1. The resulting ISO-9660 pathname is returned. +*/ +char * +iso9660_pathname_isofy (const char pathname[], uint16_t version) +{ + char tmpbuf[1024] = { 0, }; + + cdio_assert (strlen (pathname) < (sizeof (tmpbuf) - sizeof (";65535"))); + + snprintf (tmpbuf, sizeof(tmpbuf), "%s;%d", pathname, version); + + return strdup (tmpbuf); +} + +/*! + Return the PVD's application ID. + NULL is returned if there is some problem in getting this. +*/ +char * +iso9660_get_application_id(iso9660_pvd_t *p_pvd) +{ + if (NULL==p_pvd) return NULL; + return strdup(strip_trail(p_pvd->application_id, ISO_MAX_APPLICATION_ID)); +} + +#if FIXME +lsn_t +iso9660_get_dir_extent(const iso9660_dir_t *idr) +{ + if (NULL == idr) return 0; + return from_733(idr->extent); +} +#endif + +uint8_t +iso9660_get_dir_len(const iso9660_dir_t *idr) +{ + if (NULL == idr) return 0; + return idr->length; +} + +#if FIXME +uint8_t +iso9660_get_dir_size(const iso9660_dir_t *idr) +{ + if (NULL == idr) return 0; + return from_733(idr->size); +} +#endif + +uint8_t +iso9660_get_pvd_type(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) return 255; + return(pvd->type); +} + +const char * +iso9660_get_pvd_id(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) return "ERR"; + return(pvd->id); +} + +int +iso9660_get_pvd_space_size(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) return 0; + return from_733(pvd->volume_space_size); +} + +int +iso9660_get_pvd_block_size(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) return 0; + return from_723(pvd->logical_block_size); +} + +/*! Return the primary volume id version number (of pvd). + If there is an error 0 is returned. + */ +int +iso9660_get_pvd_version(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) return 0; + return pvd->version; +} + +/*! Return the LSN of the root directory for pvd. + If there is an error CDIO_INVALID_LSN is returned. + */ +lsn_t +iso9660_get_root_lsn(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) + return CDIO_INVALID_LSN; + else { + const iso9660_dir_t *idr = &(pvd->root_directory_record); + if (NULL == idr) return CDIO_INVALID_LSN; + return(from_733 (idr->extent)); + } +} + +/*! + Return a string containing the preparer id with trailing + blanks removed. +*/ +char * +iso9660_get_preparer_id(const iso9660_pvd_t *pvd) +{ + if (NULL==pvd) return NULL; + return strdup(strip_trail(pvd->preparer_id, ISO_MAX_PREPARER_ID)); +} + +/*! + Return a string containing the publisher id with trailing + blanks removed. +*/ +char * +iso9660_get_publisher_id(const iso9660_pvd_t *pvd) +{ + if (NULL==pvd) return NULL; + return strdup(strip_trail(pvd->publisher_id, ISO_MAX_PUBLISHER_ID)); +} + +/*! + Return a string containing the PVD's system id with trailing + blanks removed. +*/ +char * +iso9660_get_system_id(const iso9660_pvd_t *pvd) +{ + if (NULL==pvd) return NULL; + return strdup(strip_trail(pvd->system_id, ISO_MAX_SYSTEM_ID)); +} + +/*! + Return the PVD's volume ID. +*/ +char * +iso9660_get_volume_id(const iso9660_pvd_t *pvd) +{ + if (NULL == pvd) return NULL; + return strdup(strip_trail(pvd->volume_id, ISO_MAX_VOLUME_ID)); +} + +/*! + Return the PVD's volumeset ID. + NULL is returned if there is some problem in getting this. +*/ +char * +iso9660_get_volumeset_id(const iso9660_pvd_t *pvd) +{ + if ( NULL == pvd ) return NULL; + return strdup(strip_trail(pvd->volume_set_id, ISO_MAX_VOLUMESET_ID)); +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/iso9660_fs.c b/contrib/libcdio/iso9660_fs.c new file mode 100644 index 000000000..fff12cd7c --- /dev/null +++ b/contrib/libcdio/iso9660_fs.c @@ -0,0 +1,1260 @@ +/* + $Id: iso9660_fs.c,v 1.7 2006/12/08 16:26:10 mshopf Exp $ + + Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include <string.h> +#endif + +#ifdef HAVE_ERRNO_H +# include <errno.h> +#endif + +#ifdef HAVE_ICONV +# include <iconv.h> +#endif + +#ifdef HAVE_NL_LANGINFO +#include <langinfo.h> +#endif + +#include <cdio/cdio.h> +#include <cdio/bytesex.h> +#include <cdio/iso9660.h> +#include <cdio/util.h> + +/* Private headers */ +#include "cdio_assert.h" +#include "_cdio_stdio.h" +#include "cdio_private.h" + +#include <stdio.h> + +static const char _rcsid[] = "$Id: iso9660_fs.c,v 1.7 2006/12/08 16:26:10 mshopf Exp $"; + +/* Implementation of iso9660_t type */ +struct _iso9660 { + CdioDataSource *stream; /* Stream pointer */ + bool b_xa; /* true if has XA attributes. */ + uint8_t i_joliet_level;/* 0 = no Joliet extensions. + 1-3: Joliet level. */ + iso9660_pvd_t pvd; + iso9660_svd_t svd; + iso_extension_mask_t iso_extension_mask; /* What extensions we + tolerate. */ +}; + +/*! + Open an ISO 9660 image for reading. Maybe in the future we will have + a mode. NULL is returned on error. +*/ +iso9660_t * +iso9660_open (const char *pathname /*, mode*/) +{ + return iso9660_open_ext(pathname, ISO_EXTENSION_NONE); +} + +/*! + Open an ISO 9660 image for reading. Maybe in the future we will have + a mode. NULL is returned on error. +*/ +iso9660_t * +iso9660_open_ext (const char *pathname, + iso_extension_mask_t iso_extension_mask) +{ + iso9660_t *p_iso = (iso9660_t *) _cdio_malloc(sizeof(struct _iso9660)) ; + + if (NULL == p_iso) return NULL; + + p_iso->stream = cdio_stdio_new( pathname ); + if (NULL == p_iso->stream) + goto error; + + if ( !iso9660_ifs_read_superblock(p_iso, iso_extension_mask) ) + goto error; + + /* Determine if image has XA attributes. */ + + p_iso->b_xa = !strncmp ((char *) &(p_iso->pvd) + ISO_XA_MARKER_OFFSET, + ISO_XA_MARKER_STRING, + strlen (ISO_XA_MARKER_STRING)); + p_iso->iso_extension_mask = iso_extension_mask; + return p_iso; + + error: + free(p_iso); + return NULL; +} + + + +/*! + Close previously opened ISO 9660 image. + True is unconditionally returned. If there was an error false would + be returned. +*/ +bool +iso9660_close (iso9660_t *p_iso) +{ + if (NULL != p_iso) { + cdio_stdio_destroy(p_iso->stream); + free(p_iso); + } + return true; +} + +static bool +check_pvd (const iso9660_pvd_t *p_pvd) +{ + if ( ISO_VD_PRIMARY != from_711(p_pvd->type) ) { + cdio_warn ("unexpected PVD type %d", p_pvd->type); + return false; + } + + if (strncmp (p_pvd->id, ISO_STANDARD_ID, strlen (ISO_STANDARD_ID))) + { + cdio_warn ("unexpected ID encountered (expected `" + ISO_STANDARD_ID "', got `%.5s'", p_pvd->id); + return false; + } + return true; +} + +#ifdef HAVE_JOLIET +static bool +ucs2be_to_locale(ICONV_CONST char *psz_ucs2be, size_t i_inlen, + char **p_psz_out, size_t i_outlen) +{ + + iconv_t ic = +#if defined(HAVE_NL_LANGINFO) + iconv_open(nl_langinfo(CODESET), "UCS-2BE"); +#else + iconv_open("ASCII", "UCS-2BE"); +#endif + + int rc; + char *psz_buf = NULL; + char *psz_buf2; + int i_outlen_max = i_outlen; + int i_outlen_actual; + + if (-1 == (size_t) ic) { +#if defined(HAVE_NL_LANGINFO) + cdio_info("Failed to get conversion table for locale, trying ASCII"); + ic = iconv_open("ASCII", "UCS-2BE"); + if (-1 == (size_t) ic) { + cdio_info("Failed to get conversion table for ASCII too"); + return false; + } +#else + cdio_info("Failed to get conversion table for locale"); + return false; +#endif + } + + psz_buf = (char *) realloc(psz_buf, i_outlen); + psz_buf2 = psz_buf; + if (!psz_buf) { + /* XXX: report out of memory error */ + goto error; + } + rc = iconv(ic, &psz_ucs2be, &i_inlen, &psz_buf2, &i_outlen); + iconv_close(ic); + if ((rc == -1) && (errno != E2BIG)) { + /* conversion failed */ + goto error; + } + i_outlen_actual = i_outlen_max - i_outlen; + *p_psz_out = malloc(i_outlen_actual + 1); + memcpy(*p_psz_out, psz_buf, i_outlen_actual); + *(*p_psz_out + i_outlen_actual) = '\0'; + free(psz_buf); + return true; + error: + free(psz_buf); + *p_psz_out = NULL; + return false; +} +#endif /*HAVE_JOLIET*/ + +/*! + Return the application ID. NULL is returned in psz_app_id if there + is some problem in getting this. +*/ +bool +iso9660_ifs_get_application_id(iso9660_t *p_iso, + /*out*/ char **p_psz_app_id) +{ + if (!p_iso) { + *p_psz_app_id = NULL; + return false; + } + +#ifdef HAVE_JOLIET + if (p_iso->i_joliet_level) { + /* TODO: check that we haven't reached the maximum size. + If we have, perhaps we've truncated and if we can get + longer results *and* have the same character using + the PVD, do that. + */ + if ( ucs2be_to_locale(p_iso->svd.application_id, + ISO_MAX_APPLICATION_ID, + p_psz_app_id, + ISO_MAX_APPLICATION_ID)) + return true; + } +#endif /*HAVE_JOLIET*/ + *p_psz_app_id = iso9660_get_application_id( &(p_iso->pvd) ); + return *p_psz_app_id != NULL && strlen(*p_psz_app_id); +} + +/*! + Return the Joliet level recognaized for p_iso. +*/ +uint8_t iso9660_ifs_get_joliet_level(iso9660_t *p_iso) +{ + if (!p_iso) return 0; + return p_iso->i_joliet_level; +} + +/*! + Return a string containing the preparer id with trailing + blanks removed. +*/ +bool +iso9660_ifs_get_preparer_id(iso9660_t *p_iso, + /*out*/ char **p_psz_preparer_id) +{ + if (!p_iso) { + *p_psz_preparer_id = NULL; + return false; + } + +#ifdef HAVE_JOLIET + if (p_iso->i_joliet_level) { + /* TODO: check that we haven't reached the maximum size. + If we have, perhaps we've truncated and if we can get + longer results *and* have the same character using + the PVD, do that. + */ + if ( ucs2be_to_locale(p_iso->svd.preparer_id, ISO_MAX_PREPARER_ID, + p_psz_preparer_id, ISO_MAX_PREPARER_ID) ) + return true; + } +#endif /*HAVE_JOLIET*/ + *p_psz_preparer_id = iso9660_get_preparer_id( &(p_iso->pvd) ); + return *p_psz_preparer_id != NULL && strlen(*p_psz_preparer_id); +} + +/*! + Return a string containing the PVD's publisher id with trailing + blanks removed. +*/ +bool iso9660_ifs_get_publisher_id(iso9660_t *p_iso, + /*out*/ char **p_psz_publisher_id) +{ + if (!p_iso) { + *p_psz_publisher_id = NULL; + return false; + } + +#ifdef HAVE_JOLIET + if (p_iso->i_joliet_level) { + /* TODO: check that we haven't reached the maximum size. + If we have, perhaps we've truncated and if we can get + longer results *and* have the same character using + the PVD, do that. + */ + if( ucs2be_to_locale(p_iso->svd.publisher_id, ISO_MAX_PUBLISHER_ID, + p_psz_publisher_id, ISO_MAX_PUBLISHER_ID) ) + return true; + } +#endif /*HAVE_JOLIET*/ + *p_psz_publisher_id = iso9660_get_publisher_id( &(p_iso->pvd) ); + return *p_psz_publisher_id != NULL && strlen(*p_psz_publisher_id); +} + + +/*! + Return a string containing the PVD's publisher id with trailing + blanks removed. +*/ +bool iso9660_ifs_get_system_id(iso9660_t *p_iso, + /*out*/ char **p_psz_system_id) +{ + if (!p_iso) { + *p_psz_system_id = NULL; + return false; + } + +#ifdef HAVE_JOLIET + if (p_iso->i_joliet_level) { + /* TODO: check that we haven't reached the maximum size. + If we have, perhaps we've truncated and if we can get + longer results *and* have the same character using + the PVD, do that. + */ + if ( ucs2be_to_locale(p_iso->svd.system_id, ISO_MAX_SYSTEM_ID, + p_psz_system_id, ISO_MAX_SYSTEM_ID) ) + return true; + } +#endif /*HAVE_JOLIET*/ + *p_psz_system_id = iso9660_get_system_id( &(p_iso->pvd) ); + return *p_psz_system_id != NULL && strlen(*p_psz_system_id); +} + + +/*! + Return a string containing the PVD's publisher id with trailing + blanks removed. +*/ +bool iso9660_ifs_get_volume_id(iso9660_t *p_iso, + /*out*/ char **p_psz_volume_id) +{ + if (!p_iso) { + *p_psz_volume_id = NULL; + return false; + } + +#ifdef HAVE_JOLIET + if (p_iso->i_joliet_level) { + /* TODO: check that we haven't reached the maximum size. + If we have, perhaps we've truncated and if we can get + longer results *and* have the same character using + the PVD, do that. + */ + if ( ucs2be_to_locale(p_iso->svd.volume_id, ISO_MAX_VOLUME_ID, + p_psz_volume_id, ISO_MAX_VOLUME_ID) ) + return true; + } +#endif /* HAVE_JOLIET */ + *p_psz_volume_id = iso9660_get_volume_id( &(p_iso->pvd) ); + return *p_psz_volume_id != NULL && strlen(*p_psz_volume_id); +} + + +/*! + Return a string containing the PVD's publisher id with trailing + blanks removed. +*/ +bool iso9660_ifs_get_volumeset_id(iso9660_t *p_iso, + /*out*/ char **p_psz_volumeset_id) +{ + if (!p_iso) { + *p_psz_volumeset_id = NULL; + return false; + } + +#ifdef HAVE_JOLIET + if (p_iso->i_joliet_level) { + /* TODO: check that we haven't reached the maximum size. + If we have, perhaps we've truncated and if we can get + longer results *and* have the same character using + the PVD, do that. + */ + if ( ucs2be_to_locale(p_iso->svd.volume_set_id, + ISO_MAX_VOLUMESET_ID, + p_psz_volumeset_id, + ISO_MAX_VOLUMESET_ID) ) + return true; + } +#endif /*HAVE_JOLIET*/ + *p_psz_volumeset_id = iso9660_get_volume_id( &(p_iso->pvd) ); + return *p_psz_volumeset_id != NULL && strlen(*p_psz_volumeset_id); +} + + +/*! + Read the Primary Volume Descriptor for an ISO 9660 image. + True is returned if read, and false if there was an error. +*/ +bool +iso9660_ifs_read_pvd (const iso9660_t *p_iso, /*out*/ iso9660_pvd_t *p_pvd) +{ + if (0 == iso9660_iso_seek_read (p_iso, p_pvd, ISO_PVD_SECTOR, 1)) { + cdio_warn ("error reading PVD sector (%d)", ISO_PVD_SECTOR); + return false; + } + return check_pvd(p_pvd); +} + + +/*! + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume + Descriptor if (Joliet) extensions are acceptable. +*/ +bool +iso9660_ifs_read_superblock (iso9660_t *p_iso, + iso_extension_mask_t iso_extension_mask) +{ + iso9660_svd_t *p_svd; /* Secondary volume descriptor. */ + + if (!p_iso || !iso9660_ifs_read_pvd(p_iso, &(p_iso->pvd))) + return false; + + p_svd = &(p_iso->svd); + p_iso->i_joliet_level = 0; + + if (0 != iso9660_iso_seek_read (p_iso, p_svd, ISO_PVD_SECTOR+1, 1)) { + if ( ISO_VD_SUPPLEMENTARY == from_711(p_svd->type) ) { + if (p_svd->escape_sequences[0] == 0x25 + && p_svd->escape_sequences[1] == 0x2f) { + switch (p_svd->escape_sequences[2]) { + case 0x40: + if (iso_extension_mask & ISO_EXTENSION_JOLIET_LEVEL1) + p_iso->i_joliet_level = 1; + break; + case 0x43: + if (iso_extension_mask & ISO_EXTENSION_JOLIET_LEVEL2) + p_iso->i_joliet_level = 2; + break; + case 0x45: + if (iso_extension_mask & ISO_EXTENSION_JOLIET_LEVEL3) + p_iso->i_joliet_level = 3; + break; + default: + cdio_info("Supplementary Volume Descriptor found, but not Joliet"); + } + if (p_iso->i_joliet_level > 0) { + cdio_info("Found Extension: Joliet Level %d", p_iso->i_joliet_level); + } + } + } + } + + return true; +} + + +/*! + Read the Primary Volume Descriptor for of CD. +*/ +bool +iso9660_fs_read_pvd(const CdIo *p_cdio, /*out*/ iso9660_pvd_t *p_pvd) +{ + /* A bit of a hack, we'll assume track 1 contains ISO_PVD_SECTOR.*/ + bool b_mode2; + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + int i_rc; + + switch(cdio_get_track_format(p_cdio, 1)) { + case TRACK_FORMAT_CDI: + case TRACK_FORMAT_XA: + b_mode2 = true; + break; + case TRACK_FORMAT_DATA: + b_mode2 = false; + break; + case TRACK_FORMAT_AUDIO: + case TRACK_FORMAT_PSX: + case TRACK_FORMAT_ERROR: + default: + return false; + } + + i_rc = b_mode2 + ? cdio_read_mode2_sector (p_cdio, buf, ISO_PVD_SECTOR, false) + : cdio_read_mode1_sector (p_cdio, buf, ISO_PVD_SECTOR, false); + + if (i_rc) { + cdio_warn ("error reading PVD sector (%d)", ISO_PVD_SECTOR); + return false; + } + + /* The size of a PVD or SVD is smaller than a sector. So we + allocated a bigger block above (buf) and now we'll copy just + the part we need to save. + */ + cdio_assert (sizeof(buf) >= sizeof (iso9660_pvd_t)); + memcpy(p_pvd, buf, sizeof(iso9660_pvd_t)); + + return check_pvd(p_pvd); +} + + +/*! + Read the Super block of an ISO 9660 image. This is the + Primary Volume Descriptor (PVD) and perhaps a Supplemental Volume + Descriptor if (Joliet) extensions are acceptable. +*/ +bool +iso9660_fs_read_superblock (CdIo *p_cdio, + iso_extension_mask_t iso_extension_mask) +{ + if (!p_cdio) return false; + + { + generic_img_private_t *p_env = (generic_img_private_t *) p_cdio->env; + iso9660_pvd_t *p_pvd = &(p_env->pvd); + iso9660_svd_t *p_svd = &(p_env->svd); + char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, }; + bool b_mode2; + int i_rc; + + /* A bit of a hack, we'll assume track 1 contains ISO_PVD_SECTOR.*/ + switch(cdio_get_track_format(p_cdio, 1)) { + case TRACK_FORMAT_CDI: + case TRACK_FORMAT_XA: + b_mode2 = true; + break; + case TRACK_FORMAT_DATA: + b_mode2 = false; + break; + case TRACK_FORMAT_AUDIO: + case TRACK_FORMAT_PSX: + case TRACK_FORMAT_ERROR: + default: + return false; + } + + if ( !iso9660_fs_read_pvd(p_cdio, p_pvd) ) + return false; + + p_env->i_joliet_level = 0; + + i_rc = (b_mode2) + ? cdio_read_mode2_sector (p_cdio, buf, ISO_PVD_SECTOR+1, false) + : cdio_read_mode1_sector (p_cdio, buf, ISO_PVD_SECTOR+1, false); + + if (0 == i_rc) { + /* The size of a PVD or SVD is smaller than a sector. So we + allocated a bigger block above (buf) and now we'll copy just + the part we need to save. + */ + cdio_assert (sizeof(buf) >= sizeof (iso9660_svd_t)); + memcpy(p_svd, buf, sizeof(iso9660_svd_t)); + + if ( ISO_VD_SUPPLEMENTARY == from_711(p_svd->type) ) { + if (p_svd->escape_sequences[0] == 0x25 + && p_svd->escape_sequences[1] == 0x2f) { + switch (p_svd->escape_sequences[2]) { + case 0x40: + if (iso_extension_mask & ISO_EXTENSION_JOLIET_LEVEL1) + p_env->i_joliet_level = 1; + break; + case 0x43: + if (iso_extension_mask & ISO_EXTENSION_JOLIET_LEVEL2) + p_env->i_joliet_level = 2; + break; + case 0x45: + if (iso_extension_mask & ISO_EXTENSION_JOLIET_LEVEL3) + p_env->i_joliet_level = 3; + break; + default: + cdio_info("Supplementary Volume Descriptor found, but not Joliet"); + } + if (p_env->i_joliet_level > 0) { + cdio_info("Found Extension: Joliet Level %d", + p_env->i_joliet_level); + } + } + } + } + } + + return true; +} + +/*! + Seek to a position and then read n blocks. Size read is returned. +*/ +long int +iso9660_iso_seek_read (const iso9660_t *p_iso, void *ptr, lsn_t start, + long int size) +{ + long int ret; + if (NULL == p_iso) return 0; + ret = cdio_stream_seek (p_iso->stream, start * ISO_BLOCKSIZE, SEEK_SET); + if (ret!=0) return 0; + return cdio_stream_read (p_iso->stream, ptr, ISO_BLOCKSIZE, size); +} + + +static iso9660_stat_t * +_iso9660_dir_to_statbuf (iso9660_dir_t *p_iso9660_dir, + bool b_mode2, uint8_t i_joliet_level) +{ + iso9660_xa_t *xa_data = NULL; + uint8_t dir_len= iso9660_get_dir_len(p_iso9660_dir); + unsigned int filename_len; + unsigned int stat_len; + iso9660_stat_t *stat; + + if (!dir_len) return NULL; + + filename_len = from_711(p_iso9660_dir->filename_len); + + /* .. string in statbuf is one longer than in p_iso9660_dir's listing '\1' */ + stat_len = sizeof(iso9660_stat_t)+filename_len+2; + + stat = _cdio_malloc(stat_len); + stat->type = (p_iso9660_dir->file_flags & ISO_DIRECTORY) + ? _STAT_DIR : _STAT_FILE; + stat->lsn = from_733 (p_iso9660_dir->extent); + stat->size = from_733 (p_iso9660_dir->size); + stat->secsize = _cdio_len2blocks (stat->size, ISO_BLOCKSIZE); + + if ('\0' == p_iso9660_dir->filename[0] && 1 == filename_len) + strcpy (stat->filename, "."); + else if ('\1' == p_iso9660_dir->filename[0] && 1 == filename_len) + strcpy (stat->filename, ".."); + else { +#ifdef HAVE_JOLIET + if (i_joliet_level) { + int i_inlen = filename_len; + int i_outlen = (i_inlen / 2); + char *p_psz_out = NULL; + ucs2be_to_locale(p_iso9660_dir->filename, i_inlen, + &p_psz_out, i_outlen); + strncpy(stat->filename, p_psz_out, filename_len); + free(p_psz_out); + } else +#endif /*HAVE_JOLIET*/ + strncpy (stat->filename, p_iso9660_dir->filename, filename_len); + } + + + iso9660_get_dtime(&(p_iso9660_dir->recording_time), true, &(stat->tm)); + + cdio_assert (dir_len >= sizeof (iso9660_dir_t)); + + if (b_mode2) { + int su_length = iso9660_get_dir_len(p_iso9660_dir) + - sizeof (iso9660_dir_t); + su_length -= filename_len; + + if (su_length % 2) + su_length--; + + if (su_length < 0 || su_length < sizeof (iso9660_xa_t)) + return stat; + + xa_data = (void *) (((char *) p_iso9660_dir) + + (iso9660_get_dir_len(p_iso9660_dir) - su_length)); + + if (xa_data->signature[0] != 'X' + || xa_data->signature[1] != 'A') + { + cdio_warn ("XA signature not found in ISO9660's system use area;" + " ignoring XA attributes for this file entry."); + cdio_debug ("%d %d %d, '%c%c' (%d, %d)", + iso9660_get_dir_len(p_iso9660_dir), + filename_len, + su_length, + xa_data->signature[0], xa_data->signature[1], + xa_data->signature[0], xa_data->signature[1]); + return stat; + } + stat->xa = *xa_data; + } + return stat; + +} + +/*! + Return the directory name stored in the iso9660_dir_t + + A string is allocated: the caller must deallocate. + */ +char * +iso9660_dir_to_name (const iso9660_dir_t *iso9660_dir) +{ + char namebuf[256] = { 0, }; + uint8_t len=iso9660_get_dir_len(iso9660_dir); + + if (!len) return NULL; + + cdio_assert (len >= sizeof (iso9660_dir_t)); + + /* (iso9660_dir->file_flags & ISO_DIRECTORY) */ + + if (iso9660_dir->filename[0] == '\0') + strcpy (namebuf, "."); + else if (iso9660_dir->filename[0] == '\1') + strcpy (namebuf, ".."); + else + strncpy (namebuf, iso9660_dir->filename, iso9660_dir->filename_len); + + return strdup (namebuf); +} + +/* + Return a pointer to a ISO 9660 stat buffer or NULL if there's an error +*/ +static iso9660_stat_t * +_fs_stat_root (CdIo *p_cdio) +{ + + if (!p_cdio) return NULL; + + { + iso_extension_mask_t iso_extension_mask = ISO_EXTENSION_ALL; + generic_img_private_t *p_env = (generic_img_private_t *) p_cdio->env; + bool b_mode2 = cdio_get_track_green(p_cdio, 1); + iso9660_dir_t *p_iso9660_dir; + iso9660_stat_t *p_stat; + + if (!p_env->i_joliet_level) + iso_extension_mask &= ~ISO_EXTENSION_JOLIET; + + /* FIXME try also with Joliet.*/ + if ( !iso9660_fs_read_superblock (p_cdio, iso_extension_mask) ) { + cdio_warn("Could not read ISO-9660 Superblock."); + return NULL; + } + +#ifdef HAVE_JOLIET + p_iso9660_dir = p_env->i_joliet_level + ? &(p_env->svd.root_directory_record) + : &(p_env->pvd.root_directory_record) ; +#else + p_iso9660_dir = &(p_env->pvd.root_directory_record) ; +#endif + + p_stat = _iso9660_dir_to_statbuf (p_iso9660_dir, b_mode2, + p_env->i_joliet_level); + return p_stat; + } + +} + +static iso9660_stat_t * +_fs_stat_iso_root (iso9660_t *p_iso) +{ + iso9660_stat_t *p_stat; + iso9660_dir_t *p_iso9660_dir; + +#ifdef HAVE_JOLIET + p_iso9660_dir = p_iso->i_joliet_level + ? &(p_iso->svd.root_directory_record) + : &(p_iso->pvd.root_directory_record) ; +#else + p_iso9660_dir = &(p_iso->pvd.root_directory_record) ; +#endif + + p_stat = _iso9660_dir_to_statbuf (p_iso9660_dir, true, + p_iso->i_joliet_level); + return p_stat; +} + +static iso9660_stat_t * +_fs_stat_traverse (const CdIo *p_cdio, const iso9660_stat_t *_root, + char **splitpath, bool b_mode2, bool translate) +{ + unsigned offset = 0; + uint8_t *_dirbuf = NULL; + iso9660_stat_t *p_stat; + generic_img_private_t *p_env = (generic_img_private_t *) p_cdio->env; + + if (!splitpath[0]) + { + unsigned int len=sizeof(iso9660_stat_t) + strlen(_root->filename)+1; + p_stat = _cdio_malloc(len); + memcpy(p_stat, _root, len); + return p_stat; + } + + if (_root->type == _STAT_FILE) + return NULL; + + cdio_assert (_root->type == _STAT_DIR); + + if (_root->size != ISO_BLOCKSIZE * _root->secsize) + { + cdio_warn ("bad size for ISO9660 directory (%ud) should be (%lu)!", + (unsigned) _root->size, + (unsigned long int) ISO_BLOCKSIZE * _root->secsize); + } + + _dirbuf = _cdio_malloc (_root->secsize * ISO_BLOCKSIZE); + + if (b_mode2) { + if (cdio_read_mode2_sectors (p_cdio, _dirbuf, _root->lsn, false, + _root->secsize)) + return NULL; + } else { + if (cdio_read_mode1_sectors (p_cdio, _dirbuf, _root->lsn, false, + _root->secsize)) + return NULL; + } + + while (offset < (_root->secsize * ISO_BLOCKSIZE)) + { + iso9660_dir_t *p_iso9660_dir = (void *) &_dirbuf[offset]; + iso9660_stat_t *p_stat; + int cmp; + + if (!iso9660_get_dir_len(p_iso9660_dir)) + { + offset++; + continue; + } + + p_stat = _iso9660_dir_to_statbuf (p_iso9660_dir, b_mode2, + p_env->i_joliet_level); + + if (translate) { + char *trans_fname = malloc(strlen(p_stat->filename)); + int trans_len; + + if (trans_fname == NULL) { + cdio_warn("can't allocate %lu bytes", + (long unsigned int) strlen(p_stat->filename)); + return NULL; + } + trans_len = iso9660_name_translate_ext(p_stat->filename, trans_fname, + p_env->i_joliet_level); + cmp = strcmp(splitpath[0], trans_fname); + free(trans_fname); + } else { + cmp = strcmp(splitpath[0], p_stat->filename); + } + + if (!cmp) { + iso9660_stat_t *ret_stat + = _fs_stat_traverse (p_cdio, p_stat, &splitpath[1], b_mode2, + translate); + free(p_stat); + free (_dirbuf); + return ret_stat; + } + + free(p_stat); + + offset += iso9660_get_dir_len(p_iso9660_dir); + } + + cdio_assert (offset == (_root->secsize * ISO_BLOCKSIZE)); + + /* not found */ + free (_dirbuf); + return NULL; +} + +static iso9660_stat_t * +_fs_iso_stat_traverse (iso9660_t *p_iso, const iso9660_stat_t *_root, + char **splitpath, bool translate) +{ + unsigned offset = 0; + uint8_t *_dirbuf = NULL; + int ret; + + if (!splitpath[0]) + { + iso9660_stat_t *p_stat; + unsigned int len=sizeof(iso9660_stat_t) + strlen(_root->filename)+1; + p_stat = _cdio_malloc(len); + memcpy(p_stat, _root, len); + return p_stat; + } + + if (_root->type == _STAT_FILE) + return NULL; + + cdio_assert (_root->type == _STAT_DIR); + + if (_root->size != ISO_BLOCKSIZE * _root->secsize) + { + cdio_warn ("bad size for ISO9660 directory (%ud) should be (%lu)!", + (unsigned) _root->size, + (unsigned long int) ISO_BLOCKSIZE * _root->secsize); + } + + _dirbuf = _cdio_malloc (_root->secsize * ISO_BLOCKSIZE); + + ret = iso9660_iso_seek_read (p_iso, _dirbuf, _root->lsn, _root->secsize); + if (ret!=ISO_BLOCKSIZE*_root->secsize) return NULL; + + while (offset < (_root->secsize * ISO_BLOCKSIZE)) + { + iso9660_dir_t *p_iso9660_dir = (void *) &_dirbuf[offset]; + iso9660_stat_t *p_stat; + int cmp; + + if (!iso9660_get_dir_len(p_iso9660_dir)) + { + offset++; + continue; + } + + p_stat = _iso9660_dir_to_statbuf (p_iso9660_dir, true, + p_iso->i_joliet_level); + + if (translate) { + char *trans_fname = malloc(strlen(p_stat->filename)+1); + int trans_len; + + if (trans_fname == NULL) { + cdio_warn("can't allocate %lu bytes", + (long unsigned int) strlen(p_stat->filename)); + return NULL; + } + trans_len = iso9660_name_translate_ext(p_stat->filename, trans_fname, + p_iso->i_joliet_level); + cmp = strcmp(splitpath[0], trans_fname); + free(trans_fname); + } else { + cmp = strcmp(splitpath[0], p_stat->filename); + } + + if (!cmp) { + iso9660_stat_t *ret_stat + = _fs_iso_stat_traverse (p_iso, p_stat, &splitpath[1], translate); + free(p_stat); + free (_dirbuf); + return ret_stat; + } + + free(p_stat); + + offset += iso9660_get_dir_len(p_iso9660_dir); + } + + cdio_assert (offset == (_root->secsize * ISO_BLOCKSIZE)); + + /* not found */ + free (_dirbuf); + return NULL; +} + +/*! + Get file status for pathname into stat. NULL is returned on error. + */ +iso9660_stat_t * +iso9660_fs_stat (CdIo *p_cdio, const char pathname[]) +{ + iso9660_stat_t *p_root; + char **p_psz_splitpath; + iso9660_stat_t *p_stat; + /* A bit of a hack, we'll assume track 1 contains ISO_PVD_SECTOR.*/ + bool b_mode2; + + if (!p_cdio) return NULL; + if (!pathname) return NULL; + + p_root = _fs_stat_root (p_cdio); + if (!p_root) return NULL; + + b_mode2 = cdio_get_track_green(p_cdio, 1); + p_psz_splitpath = _cdio_strsplit (pathname, '/'); + p_stat = _fs_stat_traverse (p_cdio, p_root, p_psz_splitpath, b_mode2, false); + free(p_root); + _cdio_strfreev (p_psz_splitpath); + + return p_stat; +} + +/*! + Get file status for pathname into stat. NULL is returned on error. + pathname version numbers in the ISO 9660 + name are dropped, i.e. ;1 is removed and if level 1 ISO-9660 names + are lowercased. + */ +iso9660_stat_t * +iso9660_fs_stat_translate (CdIo *p_cdio, const char pathname[], + bool b_mode2) +{ + iso9660_stat_t *p_root; + char **p_psz_splitpath; + iso9660_stat_t *p_stat; + + if (!p_cdio) return NULL; + if (pathname) return NULL; + + p_root = _fs_stat_root (p_cdio); + if (!p_root) return NULL; + + p_psz_splitpath = _cdio_strsplit (pathname, '/'); + p_stat = _fs_stat_traverse (p_cdio, p_root, p_psz_splitpath, b_mode2, true); + free(p_root); + _cdio_strfreev (p_psz_splitpath); + + return p_stat; +} + +/*! + Get file status for pathname into stat. NULL is returned on error. + */ +iso9660_stat_t * +iso9660_ifs_stat (iso9660_t *p_iso, const char pathname[]) +{ + iso9660_stat_t *p_root; + char **splitpath; + iso9660_stat_t *stat; + + if (!p_iso) return NULL; + if (!pathname) return NULL; + + p_root = _fs_stat_iso_root (p_iso); + if (!p_root) return NULL; + + splitpath = _cdio_strsplit (pathname, '/'); + stat = _fs_iso_stat_traverse (p_iso, p_root, splitpath, false); + free(p_root); + /*** FIXME _cdio_strfreev (splitpath); ***/ + + return stat; +} + +/*! + Get file status for pathname into stat. NULL is returned on error. + pathname version numbers in the ISO 9660 + name are dropped, i.e. ;1 is removed and if level 1 ISO-9660 names + are lowercased. + */ +iso9660_stat_t * +iso9660_ifs_stat_translate (iso9660_t *p_iso, const char pathname[]) +{ + iso9660_stat_t *p_root; + char **p_psz_splitpath; + iso9660_stat_t *p_stat; + + if (!p_iso) return NULL; + if (!pathname) return NULL; + + p_root = _fs_stat_iso_root (p_iso); + if (NULL == p_root) return NULL; + + p_psz_splitpath = _cdio_strsplit (pathname, '/'); + p_stat = _fs_iso_stat_traverse (p_iso, p_root, p_psz_splitpath, true); + free(p_root); + _cdio_strfreev (p_psz_splitpath); + + return p_stat; +} + +/*! + Read pathname (a directory) and return a list of iso9660_stat_t + of the files inside that. The caller must free the returned result. +*/ +CdioList * +iso9660_fs_readdir (CdIo *p_cdio, const char pathname[], bool b_mode2) +{ + generic_img_private_t *p_env; + iso9660_stat_t *p_stat; + + if (!p_cdio) return NULL; + if (!pathname) return NULL; + + p_env = (generic_img_private_t *) p_cdio->env; + + p_stat = iso9660_fs_stat (p_cdio, pathname); + if (!p_stat) return NULL; + + if (p_stat->type != _STAT_DIR) { + free(p_stat); + return NULL; + } + + { + unsigned offset = 0; + uint8_t *_dirbuf = NULL; + CdioList *retval = _cdio_list_new (); + + if (p_stat->size != ISO_BLOCKSIZE * p_stat->secsize) + { + cdio_warn ("bad size for ISO9660 directory (%ud) should be (%lu)!", + (unsigned) p_stat->size, + (unsigned long int) ISO_BLOCKSIZE * p_stat->secsize); + } + + _dirbuf = _cdio_malloc (p_stat->secsize * ISO_BLOCKSIZE); + + if (b_mode2) { + if (cdio_read_mode2_sectors (p_cdio, _dirbuf, p_stat->lsn, false, + p_stat->secsize)) + cdio_assert_not_reached (); + } else { + if (cdio_read_mode1_sectors (p_cdio, _dirbuf, p_stat->lsn, false, + p_stat->secsize)) + cdio_assert_not_reached (); + } + + while (offset < (p_stat->secsize * ISO_BLOCKSIZE)) + { + iso9660_dir_t *p_iso9660_dir = (void *) &_dirbuf[offset]; + iso9660_stat_t *p_iso9660_stat; + + if (!iso9660_get_dir_len(p_iso9660_dir)) + { + offset++; + continue; + } + + p_iso9660_stat = _iso9660_dir_to_statbuf(p_iso9660_dir, b_mode2, + p_env->i_joliet_level); + _cdio_list_append (retval, p_iso9660_stat); + + offset += iso9660_get_dir_len(p_iso9660_dir); + } + + cdio_assert (offset == (p_stat->secsize * ISO_BLOCKSIZE)); + + free (_dirbuf); + free (p_stat); + return retval; + } +} + +/*! + Read pathname (a directory) and return a list of iso9660_stat_t + of the files inside that. The caller must free the returned result. +*/ +CdioList * +iso9660_ifs_readdir (iso9660_t *p_iso, const char pathname[]) +{ + iso9660_stat_t *p_stat; + + if (!p_iso) return NULL; + if (!pathname) return NULL; + + p_stat = iso9660_ifs_stat (p_iso, pathname); + if (!p_stat) return NULL; + + if (p_stat->type != _STAT_DIR) { + free(p_stat); + return NULL; + } + + { + long int ret; + unsigned offset = 0; + uint8_t *_dirbuf = NULL; + CdioList *retval = _cdio_list_new (); + + if (p_stat->size != ISO_BLOCKSIZE * p_stat->secsize) + { + cdio_warn ("bad size for ISO9660 directory (%ud) should be (%lu)!", + (unsigned int) p_stat->size, + (unsigned long int) ISO_BLOCKSIZE * p_stat->secsize); + } + + _dirbuf = _cdio_malloc (p_stat->secsize * ISO_BLOCKSIZE); + + ret = iso9660_iso_seek_read (p_iso, _dirbuf, p_stat->lsn, p_stat->secsize); + if (ret != ISO_BLOCKSIZE*p_stat->secsize) return NULL; + + while (offset < (p_stat->secsize * ISO_BLOCKSIZE)) + { + iso9660_dir_t *p_iso9660_dir = (void *) &_dirbuf[offset]; + iso9660_stat_t *p_iso9660_stat; + + if (!iso9660_get_dir_len(p_iso9660_dir)) + { + offset++; + continue; + } + + p_iso9660_stat = _iso9660_dir_to_statbuf(p_iso9660_dir, true, + p_iso->i_joliet_level); + _cdio_list_append (retval, p_iso9660_stat); + + offset += iso9660_get_dir_len(p_iso9660_dir); + } + + cdio_assert (offset == (p_stat->secsize * ISO_BLOCKSIZE)); + + free (_dirbuf); + free (p_stat); + return retval; + } +} + +static iso9660_stat_t * +find_fs_lsn_recurse (CdIo *p_cdio, const char pathname[], lsn_t lsn) +{ + CdioList *entlist = iso9660_fs_readdir (p_cdio, pathname, true); + CdioList *dirlist = _cdio_list_new (); + CdioListNode *entnode; + + cdio_assert (entlist != NULL); + + /* iterate over each entry in the directory */ + + _CDIO_LIST_FOREACH (entnode, entlist) + { + iso9660_stat_t *statbuf = _cdio_list_node_data (entnode); + char _fullname[4096] = { 0, }; + char *filename = (char *) statbuf->filename; + + snprintf (_fullname, sizeof (_fullname), "%s%s/", pathname, filename); + + if (statbuf->type == _STAT_DIR + && strcmp ((char *) statbuf->filename, ".") + && strcmp ((char *) statbuf->filename, "..")) + _cdio_list_append (dirlist, strdup (_fullname)); + + if (statbuf->lsn == lsn) { + unsigned int len=sizeof(iso9660_stat_t)+strlen(statbuf->filename)+1; + iso9660_stat_t *ret_stat = _cdio_malloc(len); + memcpy(ret_stat, statbuf, len); + _cdio_list_free (entlist, true); + _cdio_list_free (dirlist, true); + return ret_stat; + } + + } + + _cdio_list_free (entlist, true); + + /* now recurse/descend over directories encountered */ + + _CDIO_LIST_FOREACH (entnode, dirlist) + { + char *_fullname = _cdio_list_node_data (entnode); + iso9660_stat_t *ret_stat = find_fs_lsn_recurse (p_cdio, _fullname, lsn); + + if (NULL != ret_stat) { + _cdio_list_free (dirlist, true); + return ret_stat; + } + } + + _cdio_list_free (dirlist, true); + return NULL; +} + +/*! + Given a directory pointer, find the filesystem entry that contains + lsn and return information about it. + + Returns stat_t of entry if we found lsn, or NULL otherwise. + */ +iso9660_stat_t * +iso9660_find_fs_lsn(CdIo *p_cdio, lsn_t i_lsn) +{ + return find_fs_lsn_recurse (p_cdio, "/", i_lsn); +} + +/*! + Return true if ISO 9660 image has extended attrributes (XA). +*/ +bool +iso9660_ifs_is_xa (const iso9660_t * p_iso) +{ + if (!p_iso) return false; + return p_iso->b_xa; +} diff --git a/contrib/libcdio/iso9660_private.h b/contrib/libcdio/iso9660_private.h new file mode 100644 index 000000000..9a10950aa --- /dev/null +++ b/contrib/libcdio/iso9660_private.h @@ -0,0 +1,85 @@ +/* + $Id: iso9660_private.h,v 1.2 2004/04/11 12:20:31 miguelfreitas Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003 Rocky Bernstein <rocky@panix.com> + + See also iso9660.h by Eric Youngdale (1993). + + Copyright 1993 Yggdrasil Computing, Incorporated + Copyright (c) 1999,2000 J. Schilling + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CDIO_ISO9660_PRIVATE_H__ +#define __CDIO_ISO9660_PRIVATE_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cdio/types.h> + +#define ISO_VERSION 1 + +PRAGMA_BEGIN_PACKED + +struct iso_volume_descriptor { + uint8_t type; /* 711 */ + char id[5]; + uint8_t version; /* 711 */ + char data[2041]; +} GNUC_PACKED; + +#define struct_iso_volume_descriptor_SIZEOF ISO_BLOCKSIZE + +#define struct_iso9660_pvd_SIZEOF ISO_BLOCKSIZE + +/* + * XXX JS: The next structure has an odd length! + * Some compilers (e.g. on Sun3/mc68020) padd the structures to even length. + * For this reason, we cannot use sizeof (struct iso_path_table) or + * sizeof (struct iso_directory_record) to compute on disk sizes. + * Instead, we use offsetof(..., name) and add the name size. + * See mkisofs.h + */ + +/* We use this to help us look up the parent inode numbers. */ + +struct iso_path_table { + uint8_t name_len; /* 711 */ + uint8_t xa_len; /* 711 */ + uint32_t extent; /* 731/732 */ + uint16_t parent; /* 721/722 */ + char name[EMPTY_ARRAY_SIZE]; +} GNUC_PACKED; + +#define struct_iso_path_table_SIZEOF 8 + +#define struct_iso9660_dir_SIZEOF 33 + +PRAGMA_END_PACKED + +#endif /* __CDIO_ISO9660_PRIVATE_H__ */ + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/logging.c b/contrib/libcdio/logging.c new file mode 100644 index 000000000..8d561debe --- /dev/null +++ b/contrib/libcdio/logging.c @@ -0,0 +1,142 @@ +/* + $Id: logging.c,v 1.2 2004/04/11 12:20:31 miguelfreitas Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> + +#include <cdio/logging.h> +#include "cdio_assert.h" + +static const char _rcsid[] = "$Id: logging.c,v 1.2 2004/04/11 12:20:31 miguelfreitas Exp $"; + +cdio_log_level_t cdio_loglevel_default = CDIO_LOG_WARN; + +static void +default_cdio_log_handler (cdio_log_level_t level, const char message[]) +{ + switch (level) + { + case CDIO_LOG_ERROR: + if (level >= cdio_loglevel_default) { + fprintf (stderr, "**ERROR: %s\n", message); + fflush (stderr); + } + exit (EXIT_FAILURE); + break; + case CDIO_LOG_DEBUG: + if (level >= cdio_loglevel_default) { + fprintf (stdout, "--DEBUG: %s\n", message); + } + break; + case CDIO_LOG_WARN: + if (level >= cdio_loglevel_default) { + fprintf (stdout, "++ WARN: %s\n", message); + } + break; + case CDIO_LOG_INFO: + if (level >= cdio_loglevel_default) { + fprintf (stdout, " INFO: %s\n", message); + } + break; + case CDIO_LOG_ASSERT: + if (level >= cdio_loglevel_default) { + fprintf (stderr, "!ASSERT: %s\n", message); + fflush (stderr); + } + abort (); + break; + default: + cdio_assert_not_reached (); + break; + } + + fflush (stdout); +} + +static cdio_log_handler_t _handler = default_cdio_log_handler; + +cdio_log_handler_t +cdio_log_set_handler (cdio_log_handler_t new_handler) +{ + cdio_log_handler_t old_handler = _handler; + + _handler = new_handler; + + return old_handler; +} + +static void +cdio_logv (cdio_log_level_t level, const char format[], va_list args) +{ + char buf[1024] = { 0, }; + static int in_recursion = 0; + + if (in_recursion) + cdio_assert_not_reached (); + + in_recursion = 1; + + vsnprintf(buf, sizeof(buf)-1, format, args); + + _handler(level, buf); + + in_recursion = 0; +} + +void +cdio_log (cdio_log_level_t level, const char format[], ...) +{ + va_list args; + va_start (args, format); + cdio_logv (level, format, args); + va_end (args); +} + +#define CDIO_LOG_TEMPLATE(level, LEVEL) \ +void \ +cdio_ ## level (const char format[], ...) \ +{ \ + va_list args; \ + va_start (args, format); \ + cdio_logv (CDIO_LOG_ ## LEVEL, format, args); \ + va_end (args); \ +} + +CDIO_LOG_TEMPLATE(debug, DEBUG) +CDIO_LOG_TEMPLATE(info, INFO) +CDIO_LOG_TEMPLATE(warn, WARN) +CDIO_LOG_TEMPLATE(error, ERROR) + +#undef CDIO_LOG_TEMPLATE + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/portable.h b/contrib/libcdio/portable.h new file mode 100644 index 000000000..3da436245 --- /dev/null +++ b/contrib/libcdio/portable.h @@ -0,0 +1,74 @@ +/* + $Id: portable.h,v 1.1 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + This file contains definitions to fill in for differences or + deficiencies to OS or compiler irregularities. If this file is + included other routines can be more portable. +*/ + +#ifndef __CDIO_PORTABLE_H__ +#define __CDIO_PORTABLE_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if !defined(HAVE_FTRUNCATE) +# if defined ( WIN32 ) +# define ftruncate chsize +# endif +#endif /*HAVE_FTRUNCATE*/ + +#if !defined(HAVE_SNPRINTF) +# if defined ( MSVC ) +# define snprintf _snprintf +# endif +#endif /*HAVE_SNPRINTF*/ + +#if !defined(HAVE_VSNPRINTF) +# if defined ( MSVC ) +# define snprintf _vsnprintf +# endif +#endif /*HAVE_SNPRINTF*/ + +#ifdef MSVC +# include <io.h> + +# ifndef S_ISBLK +# define _S_IFBLK 0060000 /* Block Special */ +# define S_ISBLK(x) (x & _S_IFBLK) +# endif + +# ifndef S_ISCHR +# define _S_IFCHR 0020000 /* character special */ +# define S_ISCHR(x) (x & _S_IFCHR) +# endif +#endif /*MSVC*/ + +#ifdef HAVE_MEMSET +# define BZERO(ptr, size) memset(ptr, 0, size) +#elif HAVE_BZERO +# define BZERO(ptr, size) bzero(ptr, size) +#else + Error -- you need either memset or bzero +#endif + +#endif /* __CDIO_PORTABLE_H__ */ diff --git a/contrib/libcdio/scsi_mmc.c b/contrib/libcdio/scsi_mmc.c new file mode 100644 index 000000000..9b4a456a5 --- /dev/null +++ b/contrib/libcdio/scsi_mmc.c @@ -0,0 +1,589 @@ +/* Common SCSI Multimedia Command (MMC) routines. + + $Id: scsi_mmc.c,v 1.1 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cdio/cdio.h> +#include <cdio/logging.h> +#include <cdio/scsi_mmc.h> +#include "cdio_private.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +/*! + On input a MODE_SENSE command was issued and we have the results + in p. We interpret this and return a bit mask set according to the + capabilities. + */ +void +scsi_mmc_get_drive_cap_buf(const uint8_t *p, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap) +{ + /* Reader */ + if (p[2] & 0x01) *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_R; + if (p[2] & 0x02) *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_RW; + if (p[2] & 0x08) *p_read_cap |= CDIO_DRIVE_CAP_READ_DVD_ROM; + if (p[4] & 0x01) *p_read_cap |= CDIO_DRIVE_CAP_READ_AUDIO; + if (p[5] & 0x01) *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_DA; + if (p[5] & 0x10) *p_read_cap |= CDIO_DRIVE_CAP_READ_C2_ERRS; + + /* Writer */ + if (p[3] & 0x01) *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_R; + if (p[3] & 0x02) *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_RW; + if (p[3] & 0x10) *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_R; + if (p[3] & 0x20) *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RAM; + if (p[4] & 0x80) *p_misc_cap |= CDIO_DRIVE_CAP_WRITE_BURN_PROOF; + + /* Misc */ + if (p[4] & 0x40) *p_misc_cap |= CDIO_DRIVE_CAP_MISC_MULTI_SESSION; + if (p[6] & 0x01) *p_misc_cap |= CDIO_DRIVE_CAP_MISC_LOCK; + if (p[6] & 0x08) *p_misc_cap |= CDIO_DRIVE_CAP_MISC_EJECT; + if (p[6] >> 5 != 0) + *p_misc_cap |= CDIO_DRIVE_CAP_MISC_CLOSE_TRAY; +} + +/*! + Return the number of length in bytes of the Command Descriptor + buffer (CDB) for a given SCSI MMC command. The length will be + either 6, 10, or 12. +*/ +uint8_t +scsi_mmc_get_cmd_len(uint8_t scsi_cmd) +{ + static const uint8_t scsi_cdblen[8] = {6, 10, 10, 12, 12, 12, 10, 10}; + return scsi_cdblen[((scsi_cmd >> 5) & 7)]; +} + +/*! + Run a SCSI MMC command. + + cdio CD structure set by cdio_open(). + i_timeout time in milliseconds we will wait for the command + to complete. If this value is -1, use the default + time-out value. + buf Buffer for data, both sending and receiving + len Size of buffer + e_direction direction the transfer is to go + cdb CDB bytes. All values that are needed should be set on + input. We'll figure out what the right CDB length should be. + + We return 0 if command completed successfully and 1 if not. + */ +int +scsi_mmc_run_cmd( const CdIo *p_cdio, unsigned int i_timeout_ms, + const scsi_mmc_cdb_t *p_cdb, + scsi_mmc_direction_t e_direction, unsigned int i_buf, + /*in/out*/ void *p_buf ) +{ + if (p_cdio && p_cdio->op.run_scsi_mmc_cmd) { + return p_cdio->op.run_scsi_mmc_cmd(p_cdio->env, i_timeout_ms, + scsi_mmc_get_cmd_len(p_cdb->field[0]), + p_cdb, e_direction, i_buf, p_buf); + } else + return 1; +} + +#define DEFAULT_TIMEOUT_MS 6000 + +/*! + * Eject using SCSI MMC commands. Return 0 if successful. + */ +int +scsi_mmc_eject_media( const CdIo *cdio ) +{ + int i_status; + scsi_mmc_cdb_t cdb = {{0, }}; + uint8_t buf[1]; + scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd; + + if ( ! cdio || ! cdio->op.run_scsi_mmc_cmd ) + return -2; + + run_scsi_mmc_cmd = cdio->op.run_scsi_mmc_cmd; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_ALLOW_MEDIUM_REMOVAL); + + i_status = run_scsi_mmc_cmd (cdio->env, DEFAULT_TIMEOUT_MS, + scsi_mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_WRITE, 0, &buf); + if (0 != i_status) + return i_status; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_START_STOP); + cdb.field[4] = 1; + i_status = run_scsi_mmc_cmd (cdio->env, DEFAULT_TIMEOUT_MS, + scsi_mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_WRITE, 0, &buf); + if (0 != i_status) + return i_status; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_START_STOP); + cdb.field[4] = 2; /* eject */ + + return run_scsi_mmc_cmd (cdio->env, DEFAULT_TIMEOUT_MS, + scsi_mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_WRITE, 0, &buf); + +} + +/*! Packet driver to read mode2 sectors. + Can read only up to 25 blocks. +*/ +int +scsi_mmc_read_sectors ( const CdIo *cdio, void *p_buf, lba_t lba, + int sector_type, unsigned int nblocks ) +{ + scsi_mmc_cdb_t cdb = {{0, }}; + + scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd; + + if ( ! cdio || ! cdio->op.run_scsi_mmc_cmd ) + return -2; + + run_scsi_mmc_cmd = cdio->op.run_scsi_mmc_cmd; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_CD); + CDIO_MMC_SET_READ_TYPE (cdb.field, sector_type); + CDIO_MMC_SET_READ_LBA (cdb.field, lba); + CDIO_MMC_SET_READ_LENGTH24(cdb.field, nblocks); + CDIO_MMC_SET_MAIN_CHANNEL_SELECTION_BITS(cdb.field, + CDIO_MMC_MCSB_ALL_HEADERS); + + return run_scsi_mmc_cmd (cdio->env, DEFAULT_TIMEOUT_MS, + scsi_mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_READ, + CDIO_CD_FRAMESIZE_RAW * nblocks, + p_buf); +} + +int +scsi_mmc_set_blocksize_private ( const void *p_env, + const scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd, + unsigned int bsize) +{ + scsi_mmc_cdb_t cdb = {{0, }}; + + struct + { + uint8_t reserved1; + uint8_t medium; + uint8_t reserved2; + uint8_t block_desc_length; + uint8_t density; + uint8_t number_of_blocks_hi; + uint8_t number_of_blocks_med; + uint8_t number_of_blocks_lo; + uint8_t reserved3; + uint8_t block_length_hi; + uint8_t block_length_med; + uint8_t block_length_lo; + } mh; + + if ( ! p_env || ! run_scsi_mmc_cmd ) + return -2; + + memset (&mh, 0, sizeof (mh)); + mh.block_desc_length = 0x08; + mh.block_length_hi = (bsize >> 16) & 0xff; + mh.block_length_med = (bsize >> 8) & 0xff; + mh.block_length_lo = (bsize >> 0) & 0xff; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_MODE_SELECT_6); + + cdb.field[1] = 1 << 4; + cdb.field[4] = 12; + + return run_scsi_mmc_cmd (p_env, DEFAULT_TIMEOUT_MS, + scsi_mmc_get_cmd_len(cdb.field[0]), &cdb, + SCSI_MMC_DATA_WRITE, sizeof(mh), &mh); +} + +int +scsi_mmc_set_blocksize ( const CdIo *cdio, unsigned int bsize) +{ + if ( ! cdio ) return -2; + return + scsi_mmc_set_blocksize_private (cdio->env, cdio->op.run_scsi_mmc_cmd, + bsize); +} + + +/*! + Return the the kind of drive capabilities of device. + */ +void +scsi_mmc_get_drive_cap_private (const void *p_env, + const scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap) +{ + /* Largest buffer size we use. */ +#define BUF_MAX 2048 + uint8_t buf[BUF_MAX] = { 0, }; + + scsi_mmc_cdb_t cdb = {{0, }}; + int i_status; + uint16_t i_data = BUF_MAX; + + if ( ! p_env || ! run_scsi_mmc_cmd ) + return; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_MODE_SENSE_10); + cdb.field[1] = 0x0; + cdb.field[2] = CDIO_MMC_ALL_PAGES; + + retry: + CDIO_MMC_SET_READ_LENGTH16(cdb.field, 8); + + /* In the first run we run MODE SENSE 10 we are trying to get the + length of the data features. */ + i_status = run_scsi_mmc_cmd (p_env, DEFAULT_TIMEOUT_MS, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + sizeof(buf), &buf); + if (0 == i_status) { + uint16_t i_data_try = (uint16_t) CDIO_MMC_GET_LEN16(buf); + if (i_data_try < BUF_MAX) i_data = i_data_try; + } + + /* Now try getting all features with length set above, possibly + truncated or the default length if we couldn't get the proper + length. */ + CDIO_MMC_SET_READ_LENGTH16(cdb.field, i_data); + + i_status = run_scsi_mmc_cmd (p_env, DEFAULT_TIMEOUT_MS, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + sizeof(buf), &buf); + + if (0 != i_status && CDIO_MMC_CAPABILITIES_PAGE != cdb.field[2]) { + cdb.field[2] = CDIO_MMC_CAPABILITIES_PAGE; + goto retry; + } + + if (0 == i_status) { + uint8_t *p; + uint8_t *p_max = buf + 256; + + *p_read_cap = 0; + *p_write_cap = 0; + *p_misc_cap = 0; + + /* set to first sense mask, and then walk through the masks */ + p = buf + 8; + while( (p < &(buf[2+i_data])) && (p < p_max) ) { + uint8_t which_page; + + which_page = p[0] & 0x3F; + switch( which_page ) + { + case CDIO_MMC_AUDIO_CTL_PAGE: + case CDIO_MMC_R_W_ERROR_PAGE: + case CDIO_MMC_CDR_PARMS_PAGE: + /* Don't handle these yet. */ + break; + case CDIO_MMC_CAPABILITIES_PAGE: + scsi_mmc_get_drive_cap_buf(p, p_read_cap, p_write_cap, p_misc_cap); + break; + default: ; + } + p += (p[1] + 2); + } + } else { + cdio_info("%s: %s\n", "error in MODE_SELECT", strerror(errno)); + *p_read_cap = CDIO_DRIVE_CAP_ERROR; + *p_write_cap = CDIO_DRIVE_CAP_ERROR; + *p_misc_cap = CDIO_DRIVE_CAP_ERROR; + } + return; +} + +void +scsi_mmc_get_drive_cap (const CdIo *p_cdio, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap) +{ + if ( ! p_cdio ) return; + scsi_mmc_get_drive_cap_private (p_cdio->env, + p_cdio->op.run_scsi_mmc_cmd, + p_read_cap, p_write_cap, p_misc_cap); +} + +void +scsi_mmc_get_drive_cap_generic (const void *p_user_data, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap) +{ + const generic_img_private_t *p_env = p_user_data; + scsi_mmc_get_drive_cap( p_env->cdio, + p_read_cap, p_write_cap, p_misc_cap ); +} + + +/*! + Get the DVD type associated with cd object. +*/ +discmode_t +scsi_mmc_get_dvd_struct_physical_private ( void *p_env, const + scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd, + cdio_dvd_struct_t *s) +{ + scsi_mmc_cdb_t cdb = {{0, }}; + unsigned char buf[4 + 4 * 20], *base; + int i_status; + uint8_t layer_num = s->physical.layer_num; + + cdio_dvd_layer_t *layer; + + if ( ! p_env || ! run_scsi_mmc_cmd ) + return -2; + + if (layer_num >= CDIO_DVD_MAX_LAYERS) + return -EINVAL; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_DVD_STRUCTURE); + cdb.field[6] = layer_num; + cdb.field[7] = CDIO_DVD_STRUCT_PHYSICAL; + cdb.field[9] = sizeof(buf) & 0xff; + + i_status = run_scsi_mmc_cmd(p_env, DEFAULT_TIMEOUT_MS, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + sizeof(buf), &buf); + if (0 != i_status) + return CDIO_DISC_MODE_ERROR; + + base = &buf[4]; + layer = &s->physical.layer[layer_num]; + + /* + * place the data... really ugly, but at least we won't have to + * worry about endianess in userspace. + */ + memset(layer, 0, sizeof(*layer)); + layer->book_version = base[0] & 0xf; + layer->book_type = base[0] >> 4; + layer->min_rate = base[1] & 0xf; + layer->disc_size = base[1] >> 4; + layer->layer_type = base[2] & 0xf; + layer->track_path = (base[2] >> 4) & 1; + layer->nlayers = (base[2] >> 5) & 3; + layer->track_density = base[3] & 0xf; + layer->linear_density = base[3] >> 4; + layer->start_sector = base[5] << 16 | base[6] << 8 | base[7]; + layer->end_sector = base[9] << 16 | base[10] << 8 | base[11]; + layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15]; + layer->bca = base[16] >> 7; + + return 0; +} + + +/*! + Get the DVD type associated with cd object. +*/ +discmode_t +scsi_mmc_get_dvd_struct_physical ( const CdIo *p_cdio, cdio_dvd_struct_t *s) +{ + if ( ! p_cdio ) return -2; + return + scsi_mmc_get_dvd_struct_physical_private (p_cdio->env, + p_cdio->op.run_scsi_mmc_cmd, + s); +} + +/*! + Get the CD-ROM hardware info via a SCSI MMC INQUIRY command. + False is returned if we had an error getting the information. +*/ +bool +scsi_mmc_get_hwinfo ( const CdIo *p_cdio, + /*out*/ cdio_hwinfo_t *hw_info ) +{ + int i_status; /* Result of SCSI MMC command */ + char buf[36] = { 0, }; /* Place to hold returned data */ + scsi_mmc_cdb_t cdb = {{0, }}; /* Command Descriptor Block */ + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_INQUIRY); + cdb.field[4] = sizeof(buf); + + if (! p_cdio || ! hw_info ) return false; + + i_status = scsi_mmc_run_cmd(p_cdio, DEFAULT_TIMEOUT_MS, + &cdb, SCSI_MMC_DATA_READ, + sizeof(buf), &buf); + if (i_status == 0) { + + memcpy(hw_info->psz_vendor, + buf + 8, + sizeof(hw_info->psz_vendor)-1); + hw_info->psz_vendor[sizeof(hw_info->psz_vendor)-1] = '\0'; + memcpy(hw_info->psz_model, + buf + 8 + CDIO_MMC_HW_VENDOR_LEN, + sizeof(hw_info->psz_model)-1); + hw_info->psz_model[sizeof(hw_info->psz_model)-1] = '\0'; + memcpy(hw_info->psz_revision, + buf + 8 + CDIO_MMC_HW_VENDOR_LEN + CDIO_MMC_HW_MODEL_LEN, + sizeof(hw_info->psz_revision)-1); + hw_info->psz_revision[sizeof(hw_info->psz_revision)-1] = '\0'; + return true; + } + return false; +} + +/*! + Return the media catalog number MCN. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +char * +scsi_mmc_get_mcn_private ( void *p_env, + const scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd + ) +{ + scsi_mmc_cdb_t cdb = {{0, }}; + char buf[28] = { 0, }; + int i_status; + + if ( ! p_env || ! run_scsi_mmc_cmd ) + return NULL; + + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_SUBCHANNEL); + cdb.field[1] = 0x0; + cdb.field[2] = 0x40; + cdb.field[3] = CDIO_SUBCHANNEL_MEDIA_CATALOG; + CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(buf)); + + i_status = run_scsi_mmc_cmd(p_env, DEFAULT_TIMEOUT_MS, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + sizeof(buf), buf); + if(i_status == 0) { + return strdup(&buf[9]); + } + return NULL; +} + +char * +scsi_mmc_get_mcn ( const CdIo *p_cdio ) +{ + if ( ! p_cdio ) return NULL; + return scsi_mmc_get_mcn_private (p_cdio->env, + p_cdio->op.run_scsi_mmc_cmd ); +} + +char * +scsi_mmc_get_mcn_generic (const void *p_user_data) +{ + const generic_img_private_t *p_env = p_user_data; + return scsi_mmc_get_mcn( p_env->cdio ); +} + +/* + Read cdtext information for a CdIo object . + + return true on success, false on error or CD-Text information does + not exist. +*/ +bool +scsi_mmc_init_cdtext_private ( void *p_user_data, + const scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd, + set_cdtext_field_fn_t set_cdtext_field_fn + ) +{ + + generic_img_private_t *p_env = p_user_data; + scsi_mmc_cdb_t cdb = {{0, }}; + unsigned char wdata[5000] = { 0, }; + int i_status, i_errno; + + if ( ! p_env || ! run_scsi_mmc_cmd || p_env->b_cdtext_error ) + return false; + + /* Operation code */ + CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC); + + cdb.field[1] = CDIO_CDROM_MSF; + /* Format */ + cdb.field[2] = CDIO_MMC_READTOC_FMT_CDTEXT; + + /* Setup to read header, to get length of data */ + CDIO_MMC_SET_READ_LENGTH16(cdb.field, 4); + + errno = 0; + +/* Set read timeout 3 minues. */ +#define READ_TIMEOUT 3*60*1000 + + /* We may need to give CD-Text a little more time to complete. */ + /* First off, just try and read the size */ + i_status = run_scsi_mmc_cmd (p_env, READ_TIMEOUT, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + 4, &wdata); + + if (i_status != 0) { + cdio_info ("CD-Text read failed for header: %s\n", strerror(errno)); + i_errno = errno; + p_env->b_cdtext_error = true; + return false; + } else { + /* Now read the CD-Text data */ + int i_cdtext = CDIO_MMC_GET_LEN16(wdata); + + if (i_cdtext > sizeof(wdata)) i_cdtext = sizeof(wdata); + + CDIO_MMC_SET_READ_LENGTH16(cdb.field, i_cdtext); + i_status = run_scsi_mmc_cmd (p_env, READ_TIMEOUT, + scsi_mmc_get_cmd_len(cdb.field[0]), + &cdb, SCSI_MMC_DATA_READ, + i_cdtext, &wdata); + if (i_status != 0) { + cdio_info ("CD-Text read for text failed: %s\n", strerror(errno)); + i_errno = errno; + p_env->b_cdtext_error = true; + return false; + } + p_env->b_cdtext_init = true; + return cdtext_data_init(p_env, p_env->i_first_track, wdata, + set_cdtext_field_fn); + } +} + diff --git a/contrib/libcdio/scsi_mmc.h b/contrib/libcdio/scsi_mmc.h new file mode 100644 index 000000000..b6ea05b7a --- /dev/null +++ b/contrib/libcdio/scsi_mmc.h @@ -0,0 +1,63 @@ +/* + $Id: scsi_mmc.h,v 1.2 2004/04/11 12:20:31 miguelfreitas Exp $ + + Copyright (C) 2003 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + This file contains common definitions/routines for SCSI MMC + (Multimedia commands). +*/ + +#ifndef __SCSI_MMC_H__ +#define __SCSI_MMC_H__ + +/* Leval values that can go into READ_CD */ +#define CDIO_MMC_READ_TYPE_ANY 0 /* All types */ +#define CDIO_MMC_READ_TYPE_CDDA 1 /* Only CD-DA sectors */ +#define CDIO_MMC_READ_TYPE_MODE1 2 /* Only mode1 sectors (user data = 2048) */ +#define CDIO_MMC_READ_TYPE_MODE2 3 /* mode2 sectors form1 or form2 */ +#define CDIO_MMC_READ_TYPE_M2F1 4 /* mode2 sectors form1 */ +#define CDIO_MMC_READ_TYPE_M2F2 5 /* mode2 sectors form2 */ + +/* The generic packet command opcodes for CD/DVD Logical Units, + * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */ +#define CDIO_MMC_GPCMD_READ_CD 0xbe +#define CDIO_MMC_GPCMD_READ_10 0x28 +#define CDIO_MMC_GPCMD_READ_12 0xa8 + +#define CDIO_MMC_SET_READ_TYPE(rec, sector_type) \ + rec[1] = (sector_type << 2) + + +#define CDIO_MMC_SET_READ_LBA(rec, lba) \ + rec[2] = (lba >> 24) & 0xff; \ + rec[3] = (lba >> 16) & 0xff; \ + rec[4] = (lba >> 8) & 0xff; \ + rec[5] = (lba ) & 0xff + +#define CDIO_MMC_SET_READ_LENGTH(rec, len) \ + rec[6] = (len >> 16) & 0xff; \ + rec[7] = (len >> 8) & 0xff; \ + rec[8] = (len ) & 0xff + +#define CDIO_MMC_MCSB_ALL_HEADERS 0x78 + +#define CDIO_MMC_SET_MAIN_CHANNEL_SELECTION_BITS(rec, val) \ + rec[9] = val; + +#endif /* __SCSI_MMC_H__ */ diff --git a/contrib/libcdio/scsi_mmc_private.h b/contrib/libcdio/scsi_mmc_private.h new file mode 100644 index 000000000..b3f1e7061 --- /dev/null +++ b/contrib/libcdio/scsi_mmc_private.h @@ -0,0 +1,105 @@ +/* private MMC helper routines. + + $Id: scsi_mmc_private.h,v 1.1 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2004 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <cdio/scsi_mmc.h> +#include "cdtext_private.h" + +/*! Convert milliseconds to seconds taking the ceiling value, i.e. + 1002 milliseconds gets rounded to 2 seconds. +*/ +#define SECS2MSECS 1000 +static inline unsigned int +msecs2secs(unsigned int msecs) +{ + return (msecs+(SECS2MSECS-1)) / SECS2MSECS; +} +#undef SECS2MSECS + +typedef +int (*scsi_mmc_run_cmd_fn_t) ( const void *p_user_data, + unsigned int i_timeout_ms, + unsigned int i_cdb, + const scsi_mmc_cdb_t *p_cdb, + scsi_mmc_direction_t e_direction, + unsigned int i_buf, /*in/out*/ void *p_buf ); + +int scsi_mmc_set_blocksize_mmc_private ( const void *p_env, const + scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd, + unsigned int bsize ); + +/*! + Get the DVD type associated with cd object. +*/ +discmode_t +scsi_mmc_get_dvd_struct_physical_private ( void *p_env, const + scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd, + cdio_dvd_struct_t *s ); + + +int +scsi_mmc_set_blocksize_private ( const void *p_env, + const scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd, + unsigned int bsize); + +char *scsi_mmc_get_mcn_private ( void *p_env, + const scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd + ); + +char *scsi_mmc_get_mcn_generic (const void *p_user_data); + +bool scsi_mmc_init_cdtext_private ( void *user_data, const + scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd, + set_cdtext_field_fn_t set_cdtext_field_fn + ); + +/*! + On input a MODE_SENSE command was issued and we have the results + in p. We interpret this and return a bit mask set according to the + capabilities. + */ +void scsi_mmc_get_drive_cap_buf(const uint8_t *p, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap); + +/*! + Return the the kind of drive capabilities of device. + + Note: string is malloc'd so caller should free() then returned + string when done with it. + + */ +void +scsi_mmc_get_drive_cap_private (const void *p_env, + const scsi_mmc_run_cmd_fn_t run_scsi_mmc_cmd, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap); +void +scsi_mmc_get_drive_cap_generic (const void *p_user_data, + /*out*/ cdio_drive_read_cap_t *p_read_cap, + /*out*/ cdio_drive_write_cap_t *p_write_cap, + /*out*/ cdio_drive_misc_cap_t *p_misc_cap); + + + + + diff --git a/contrib/libcdio/sector.c b/contrib/libcdio/sector.c new file mode 100644 index 000000000..da49e9908 --- /dev/null +++ b/contrib/libcdio/sector.c @@ -0,0 +1,289 @@ +/* + $Id: sector.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cdio/sector.h> +#include <cdio/util.h> +#include <cdio/logging.h> +#include "cdio_assert.h" + +#include <stdio.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include <ctype.h> + +static const char _rcsid[] = "$Id: sector.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $"; + +lba_t +cdio_lba_to_lsn (lba_t lba) +{ + if (CDIO_INVALID_LBA == lba) return CDIO_INVALID_LSN; + return lba - CDIO_PREGAP_SECTORS; +} + +/* + The below is adapted from cdparanoia code which claims it is + straight from the MMC3 spec. +*/ + +void +cdio_lsn_to_msf (lsn_t lsn, msf_t *msf) +{ + int m, s, f; + + cdio_assert (msf != 0); + + if ( lsn >= -CDIO_PREGAP_SECTORS ){ + m = (lsn + CDIO_PREGAP_SECTORS) / CDIO_CD_FRAMES_PER_MIN; + lsn -= m * CDIO_CD_FRAMES_PER_MIN; + s = (lsn + CDIO_PREGAP_SECTORS) / CDIO_CD_FRAMES_PER_SEC; + lsn -= s * CDIO_CD_FRAMES_PER_SEC; + f = lsn + CDIO_PREGAP_SECTORS; + } else { + m = (lsn + CDIO_CD_MAX_LSN) / CDIO_CD_FRAMES_PER_MIN; + lsn -= m * (CDIO_CD_FRAMES_PER_MIN); + s = (lsn+CDIO_CD_MAX_LSN) / CDIO_CD_FRAMES_PER_SEC; + lsn -= s * CDIO_CD_FRAMES_PER_SEC; + f = lsn + CDIO_CD_MAX_LSN; + } + + if (m > 99) { + cdio_warn ("number of minutes (%d) truncated to 99.", m); + m = 99; + } + + msf->m = cdio_to_bcd8 (m); + msf->s = cdio_to_bcd8 (s); + msf->f = cdio_to_bcd8 (f); +} + +/*! + Convert an LBA into a string representation of the MSF. + \warning cdio_lba_to_msf_str returns new allocated string */ +char * +cdio_lba_to_msf_str (lba_t lba) +{ + + if (CDIO_INVALID_LBA == lba) { + return strdup("*INVALID"); + } else { + msf_t msf; + msf.m = msf.s = msf.f = 0; + cdio_lba_to_msf (lba, &msf); + return cdio_msf_to_str(&msf); + } +} + +/*! + Convert an LSN into the corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. +*/ +lba_t +cdio_lsn_to_lba (lsn_t lsn) +{ + if (CDIO_INVALID_LSN == lsn) return CDIO_INVALID_LBA; + return lsn + CDIO_PREGAP_SECTORS; +} + +/*! + Convert an LBA into the corresponding MSF. +*/ +void +cdio_lba_to_msf (lba_t lba, msf_t *msf) +{ + cdio_assert (msf != 0); + cdio_lsn_to_msf(cdio_lba_to_lsn(lba), msf); +} + +/*! + Convert a MSF into the corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. +*/ +lba_t +cdio_msf_to_lba (const msf_t *msf) +{ + uint32_t lba = 0; + + cdio_assert (msf != 0); + + lba = cdio_from_bcd8 (msf->m); + lba *= CDIO_CD_SECS_PER_MIN; + + lba += cdio_from_bcd8 (msf->s); + lba *= CDIO_CD_FRAMES_PER_SEC; + + lba += cdio_from_bcd8 (msf->f); + + return lba; +} + +/*! + Convert a MSF into the corresponding LSN. + CDIO_INVALID_LSN is returned if there is an error. +*/ +lba_t +cdio_msf_to_lsn (const msf_t *msf) +{ + return cdio_lba_to_lsn(cdio_msf_to_lba (msf)); +} + +/*! + Convert an LBA into a string representation of the MSF. + \warning cdio_lba_to_msf_str returns new allocated string */ +char * +cdio_msf_to_str (const msf_t *msf) +{ + char buf[16]; + + snprintf (buf, sizeof (buf), "%2.2x:%2.2x:%2.2x", msf->m, msf->s, msf->f); + return strdup (buf); +} + +/*! + Convert a MSF - broken out as 3 integer components into the + corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. +*/ +lba_t +cdio_msf3_to_lba (unsigned int minutes, unsigned int seconds, + unsigned int frames) +{ + return ((minutes * CDIO_CD_SECS_PER_MIN + seconds) * CDIO_CD_FRAMES_PER_SEC + + frames); +} + +/*! + Convert a string of the form MM:SS:FF into the corresponding LBA. + CDIO_INVALID_LBA is returned if there is an error. +*/ +lba_t +cdio_mmssff_to_lba (const char *psz_mmssff) +{ + int psz_field; + lba_t ret; + char c; + + if (0 == strcmp (psz_mmssff, "0")) + return 0; + + c = *psz_mmssff++; + if(c >= '0' && c <= '9') + psz_field = (c - '0'); + else + return CDIO_INVALID_LBA; + while(':' != (c = *psz_mmssff++)) { + if(c >= '0' && c <= '9') + psz_field = psz_field * 10 + (c - '0'); + else + return CDIO_INVALID_LBA; + } + + ret = cdio_msf3_to_lba (psz_field, 0, 0); + + c = *psz_mmssff++; + if(c >= '0' && c <= '9') + psz_field = (c - '0'); + else + return CDIO_INVALID_LBA; + if(':' != (c = *psz_mmssff++)) { + if(c >= '0' && c <= '9') { + psz_field = psz_field * 10 + (c - '0'); + c = *psz_mmssff++; + if(c != ':') + return CDIO_INVALID_LBA; + } + else + return CDIO_INVALID_LBA; + } + + if(psz_field >= CDIO_CD_SECS_PER_MIN) + return CDIO_INVALID_LBA; + + ret += cdio_msf3_to_lba (0, psz_field, 0); + + c = *psz_mmssff++; + if (isdigit(c)) + psz_field = (c - '0'); + else + return -1; + if('\0' != (c = *psz_mmssff++)) { + if (isdigit(c)) { + psz_field = psz_field * 10 + (c - '0'); + c = *psz_mmssff++; + } + else + return CDIO_INVALID_LBA; + } + + if('\0' != c) + return CDIO_INVALID_LBA; + + if(psz_field >= CDIO_CD_FRAMES_PER_SEC) + return CDIO_INVALID_LBA; + + ret += psz_field; + + return ret; +} + +bool +cdio_is_discmode_cdrom(discmode_t discmode) +{ + switch (discmode) { + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_DATA: + case CDIO_DISC_MODE_CD_XA: + case CDIO_DISC_MODE_CD_MIXED: + case CDIO_DISC_MODE_NO_INFO: + return true; + default: + return false; + } +} + +bool +cdio_is_discmode_dvd(discmode_t discmode) +{ + switch (discmode) { + case CDIO_DISC_MODE_DVD_ROM: + case CDIO_DISC_MODE_DVD_RAM: + case CDIO_DISC_MODE_DVD_R: + case CDIO_DISC_MODE_DVD_RW: + case CDIO_DISC_MODE_DVD_PR: + case CDIO_DISC_MODE_DVD_PRW: + return true; + default: + return false; + } +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/util.c b/contrib/libcdio/util.c new file mode 100644 index 000000000..9c646daf6 --- /dev/null +++ b/contrib/libcdio/util.c @@ -0,0 +1,197 @@ +/* + $Id: util.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_INTTYPES_H +#include "inttypes.h" +#endif + +#include "cdio_assert.h" +#include <cdio/types.h> +#include <cdio/util.h> + +static const char _rcsid[] = "$Id: util.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $"; + +size_t +_cdio_strlenv(char **str_array) +{ + size_t n = 0; + + cdio_assert (str_array != NULL); + + while(str_array[n]) + n++; + + return n; +} + +void +_cdio_strfreev(char **strv) +{ + int n; + + cdio_assert (strv != NULL); + + for(n = 0; strv[n]; n++) + free(strv[n]); + + free(strv); +} + +char * +_cdio_strjoin (char *strv[], unsigned count, const char delim[]) +{ + size_t len; + char *new_str; + unsigned n; + + cdio_assert (strv != NULL); + cdio_assert (delim != NULL); + + len = (count-1) * strlen (delim); + + for (n = 0;n < count;n++) + len += strlen (strv[n]); + + len++; + + new_str = _cdio_malloc (len); + new_str[0] = '\0'; + + for (n = 0;n < count;n++) + { + if (n) + strcat (new_str, delim); + strcat (new_str, strv[n]); + } + + return new_str; +} + +char ** +_cdio_strsplit(const char str[], char delim) /* fixme -- non-reentrant */ +{ + int n; + char **strv = NULL; + char *_str, *p; + char _delim[2] = { 0, 0 }; + + cdio_assert (str != NULL); + + _str = strdup(str); + _delim[0] = delim; + + cdio_assert (_str != NULL); + + n = 1; + p = _str; + while(*p) + if (*(p++) == delim) + n++; + + strv = _cdio_malloc (sizeof (char *) * (n+1)); + + n = 0; + while((p = strtok(n ? NULL : _str, _delim)) != NULL) + strv[n++] = strdup(p); + + free(_str); + + return strv; +} + +void * +_cdio_malloc (size_t size) +{ + void *new_mem = malloc (size); + + cdio_assert (new_mem != NULL); + + memset (new_mem, 0, size); + + return new_mem; +} + +void * +_cdio_memdup (const void *mem, size_t count) +{ + void *new_mem = NULL; + + if (mem) + { + new_mem = _cdio_malloc (count); + memcpy (new_mem, mem, count); + } + + return new_mem; +} + +char * +_cdio_strdup_upper (const char str[]) +{ + char *new_str = NULL; + + if (str) + { + char *p; + + p = new_str = strdup (str); + + while (*p) + { + *p = toupper (*p); + p++; + } + } + + return new_str; +} + +uint8_t +cdio_to_bcd8 (uint8_t n) +{ + /*cdio_assert (n < 100);*/ + + return ((n/10)<<4) | (n%10); +} + +uint8_t +cdio_from_bcd8(uint8_t p) +{ + return (0xf & p)+(10*(p >> 4)); +} + + +/* + * Local variables: + * c-file-style: "gnu" + * tab-width: 8 + * indent-tabs-mode: nil + * End: + */ diff --git a/contrib/libcdio/xa.c b/contrib/libcdio/xa.c new file mode 100644 index 000000000..f811f7ebd --- /dev/null +++ b/contrib/libcdio/xa.c @@ -0,0 +1,136 @@ +/* + $Id: xa.c,v 1.3 2005/01/01 02:43:57 rockyb Exp $ + + Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org> + Copyright (C) 2003 Rocky Bernstein <rocky@panix.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +/* Public headers */ +#include <cdio/iso9660.h> +#include <cdio/util.h> +#include <cdio/bytesex.h> + +/* Private headers */ +#include "cdio_assert.h" + +#define BUF_COUNT 16 +#define BUF_SIZE 80 + +/* Return a pointer to a internal free buffer */ +static char * +_getbuf (void) +{ + static char _buf[BUF_COUNT][BUF_SIZE]; + static int _num = -1; + + _num++; + _num %= BUF_COUNT; + + memset (_buf[_num], 0, BUF_SIZE); + + return _buf[_num]; +} + +/*! + Returns a string which interpreting the extended attribute xa_attr. + For example: + \verbatim + d---1xrxrxr + ---2--r-r-r + -a--1xrxrxr + \endverbatim + + A description of the characters in the string follows + The 1st character is either "d" if the entry is a directory, or "-" if not. + The 2nd character is either "a" if the entry is CDDA (audio), or "-" if not. + The 3rd character is either "i" if the entry is interleaved, or "-" if not. + The 4th character is either "2" if the entry is mode2 form2 or "-" if not. + The 5th character is either "1" if the entry is mode2 form1 or "-" if not. + Note that an entry will either be in mode2 form1 or mode form2. That + is you will either see "2-" or "-1" in the 4th & 5th positions. + + The 6th and 7th characters refer to permissions for a user while the + the 8th and 9th characters refer to permissions for a group while, and + the 10th and 11th characters refer to permissions for a others. + + In each of these pairs the first character (6, 8, 10) is "x" if the + entry is executable. For a directory this means the directory is + allowed to be listed or "searched". + The second character of a pair (7, 9, 11) is "r" if the entry is allowed + to be read. +*/ + +const char * +iso9660_get_xa_attr_str (uint16_t xa_attr) +{ + char *result = _getbuf(); + + xa_attr = uint16_from_be (xa_attr); + + result[ 0] = (xa_attr & XA_ATTR_DIRECTORY) ? 'd' : '-'; + result[ 1] = (xa_attr & XA_ATTR_CDDA) ? 'a' : '-'; + result[ 2] = (xa_attr & XA_ATTR_INTERLEAVED) ? 'i' : '-'; + result[ 3] = (xa_attr & XA_ATTR_MODE2FORM2) ? '2' : '-'; + result[ 4] = (xa_attr & XA_ATTR_MODE2FORM1) ? '1' : '-'; + + result[ 5] = (xa_attr & XA_PERM_XUSR) ? 'x' : '-'; + result[ 6] = (xa_attr & XA_PERM_RUSR) ? 'r' : '-'; + + result[ 7] = (xa_attr & XA_PERM_XGRP) ? 'x' : '-'; + result[ 8] = (xa_attr & XA_PERM_RGRP) ? 'r' : '-'; + + /* Hack alert: wonder if this should be ROTH and XOTH? */ + result[ 9] = (xa_attr & XA_PERM_XSYS) ? 'x' : '-'; + result[10] = (xa_attr & XA_PERM_RSYS) ? 'r' : '-'; + + result[11] = '\0'; + + return result; +} + +iso9660_xa_t * +iso9660_xa_init (iso9660_xa_t *_xa, uint16_t uid, uint16_t gid, uint16_t attr, + uint8_t filenum) +{ + cdio_assert (_xa != NULL); + + _xa->user_id = uint16_to_be (uid); + _xa->group_id = uint16_to_be (gid); + _xa->attributes = uint16_to_be (attr); + + _xa->signature[0] = 'X'; + _xa->signature[1] = 'A'; + + _xa->filenum = filenum; + + _xa->reserved[0] + = _xa->reserved[1] + = _xa->reserved[2] + = _xa->reserved[3] + = _xa->reserved[4] = 0x00; + + return _xa; +} |