From 9ec7fac30e232f776a3c8b9d7292aa8916c1e05a Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 17 2020 17:27:52 +0000 Subject: Apply patch cdrkit-1.1.11-paranoiacdda.patch patch_name: cdrkit-1.1.11-paranoiacdda.patch present_in_specfile: true --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 57edba6..b8413f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,3 @@ PROJECT (cdrkit C) -SUBDIRS(include genisoimage wodim libedc libhfs_iso libparanoia icedax libusal librols libunls readom netscsid 3rd-party/dirsplit) +SUBDIRS(include genisoimage wodim libedc libhfs_iso icedax libusal librols libunls readom netscsid 3rd-party/dirsplit) diff --git a/CMakeLists.txt.paranoiacdda b/CMakeLists.txt.paranoiacdda new file mode 100644 index 0000000..57edba6 --- /dev/null +++ b/CMakeLists.txt.paranoiacdda @@ -0,0 +1,3 @@ +PROJECT (cdrkit C) +SUBDIRS(include genisoimage wodim libedc libhfs_iso libparanoia icedax libusal librols libunls readom netscsid 3rd-party/dirsplit) + diff --git a/icedax/CMakeLists.txt b/icedax/CMakeLists.txt index 54c2e7d..66dbb86 100644 --- a/icedax/CMakeLists.txt +++ b/icedax/CMakeLists.txt @@ -1,8 +1,9 @@ PROJECT (icedax C) -INCLUDE_DIRECTORIES(../include ../wodim ../libparanoia ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/include) +INCLUDE_DIRECTORIES(../include ../wodim ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/include) include(../include/AddScgBits.cmake) include(../include/AddSchilyBits.cmake) include(../include/AddNetworkBits.cmake) +INCLUDE (CheckLibraryExists) FIND_FILE (HAVE_SYS_SOUNDCARD_H sys/soundcard.h) IF(HAVE_SYS_SOUNDCARD_H) @@ -18,12 +19,20 @@ IF(HAVE_LIBOSSAUDIO) LIST(APPEND EXTRA_LIBS "ossaudio") ENDIF(HAVE_LIBOSSAUDIO) -LIST(APPEND EXTRA_LIBS paranoia) +CHECK_INCLUDE_FILES(cdda/cdda_interface.h HAVE_CDDA_INTERFACE_H) +CHECK_LIBRARY_EXISTS (cdda_paranoia paranoia_init "" HAVE_LIBPARANOIA) +IF(HAVE_CDDA_INTERFACE_H AND HAVE_LIBPARANOIA) + LIST(APPEND EXTRA_LIBS cdda_paranoia cdda_interface) +ELSE(HAVE_CDDA_INTERFACE_H AND HAVE_LIBPARANOIA) + MESSAGE(FATAL_ERROR "You need cdparanoia library installed") +ENDIF(HAVE_CDDA_INTERFACE_H AND HAVE_LIBPARANOIA) + +LIST(APPEND EXTRA_LIBS) IF (WIN32) LIST(APPEND EXTRA_LIBS winmm) ENDIF (WIN32) -LINK_DIRECTORIES(../librols ../libusal ../libparanoia) +LINK_DIRECTORIES(../librols ../libusal) ADD_EXECUTABLE (icedax aifc.c aiff.c base64.c icedax.c interface.c ioctl.c raw.c resample.c ringbuff.c scsi_cmds.c semshm.c setuid.c sha_func.c sndconfig.c sun.c toc.c wav.c) TARGET_LINK_LIBRARIES(icedax wodimstuff ${EXTRA_LIBS}) SET_TARGET_PROPERTIES(icedax PROPERTIES SKIP_BUILD_RPATH TRUE) diff --git a/icedax/CMakeLists.txt.paranoiacdda b/icedax/CMakeLists.txt.paranoiacdda new file mode 100644 index 0000000..54c2e7d --- /dev/null +++ b/icedax/CMakeLists.txt.paranoiacdda @@ -0,0 +1,34 @@ +PROJECT (icedax C) +INCLUDE_DIRECTORIES(../include ../wodim ../libparanoia ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/include) +include(../include/AddScgBits.cmake) +include(../include/AddSchilyBits.cmake) +include(../include/AddNetworkBits.cmake) + +FIND_FILE (HAVE_SYS_SOUNDCARD_H sys/soundcard.h) +IF(HAVE_SYS_SOUNDCARD_H) + ADD_DEFINITIONS(-DHAVE_SYS_SOUNDCARD_H) +ENDIF(HAVE_SYS_SOUNDCARD_H) +FIND_FILE (HAVE_LINUX_SOUNDCARD_H linux/soundcard.h) +IF(HAVE_LINUX_SOUNDCARD_H) + ADD_DEFINITIONS(-DHAVE_LINUX_SOUNDCARD_H) +ENDIF(HAVE_LINUX_SOUNDCARD_H) + +FIND_LIBRARY(HAVE_LIBOSSAUDIO "ossaudio") +IF(HAVE_LIBOSSAUDIO) + LIST(APPEND EXTRA_LIBS "ossaudio") +ENDIF(HAVE_LIBOSSAUDIO) + +LIST(APPEND EXTRA_LIBS paranoia) +IF (WIN32) + LIST(APPEND EXTRA_LIBS winmm) +ENDIF (WIN32) + +LINK_DIRECTORIES(../librols ../libusal ../libparanoia) +ADD_EXECUTABLE (icedax aifc.c aiff.c base64.c icedax.c interface.c ioctl.c raw.c resample.c ringbuff.c scsi_cmds.c semshm.c setuid.c sha_func.c sndconfig.c sun.c toc.c wav.c) +TARGET_LINK_LIBRARIES(icedax wodimstuff ${EXTRA_LIBS}) +SET_TARGET_PROPERTIES(icedax PROPERTIES SKIP_BUILD_RPATH TRUE) + +INSTALL(TARGETS icedax DESTINATION bin) +INSTALL(PROGRAMS pitchplay readmult cdda2mp3 cdda2ogg DESTINATION bin) +INSTALL(FILES cdda2ogg.1 icedax.1 pitchplay.1 readmult.1 list_audio_tracks.1 DESTINATION ${MANSUBDIR}/man1) + diff --git a/icedax/global.h b/icedax/global.h index fc80c78..47df41f 100644 --- a/icedax/global.h +++ b/icedax/global.h @@ -17,7 +17,8 @@ #include "md5.h" #endif #ifdef USE_PARANOIA -#include "cdda_paranoia.h" +#include +#include #endif typedef struct index_list @@ -102,10 +103,10 @@ typedef struct global struct paranoia_parms_t { - Ucbit disable_paranoia:1; - Ucbit disable_extra_paranoia:1; - Ucbit disable_scratch_detect:1; - Ucbit disable_scratch_repair:1; + char disable_paranoia:1; + char disable_extra_paranoia:1; + char disable_scratch_detect:1; + char disable_scratch_repair:1; int retries; int overlap; int mindynoverlap; diff --git a/icedax/global.h.paranoiacdda b/icedax/global.h.paranoiacdda new file mode 100644 index 0000000..fc80c78 --- /dev/null +++ b/icedax/global.h.paranoiacdda @@ -0,0 +1,133 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)global.h 1.11 04/07/29 Copyright 1998-2004 Heiko Eissfeldt */ +/* Global Variables */ + +#ifdef MD5_SIGNATURES +#include "md5.h" +#endif +#ifdef USE_PARANOIA +#include "cdda_paranoia.h" +#endif + +typedef struct index_list +{ + struct index_list *next; + int frameoffset; +} +index_list; + +typedef struct global +{ + + char *dev_name; /* device name */ + char *aux_name; /* device name */ + char fname_base[200]; + + int have_forked; + int parent_died; + int audio; + struct soundfile *audio_out; + int cooked_fd; + int no_file; + int no_infofile; + int no_cddbfile; + int quiet; + int verbose; + int scsi_silent; + int scsi_verbose; + int scanbus; + int scandevs; + int multiname; + int sh_bits; + int Remainder; + int SkippedSamples; + int OutSampleSize; + int need_big_endian; + int need_hostorder; + int channels; + unsigned long iloop; + unsigned long nSamplesDoneInTrack; + unsigned overlap; + int useroverlap; + unsigned nsectors; + unsigned buffers; + unsigned shmsize; + long pagesize; + int in_lendian; + int outputendianess; + int findminmax; + int maxamp[2]; + int minamp[2]; + unsigned speed; + int userspeed; + int ismono; + int findmono; + int swapchannels; + int deemphasize; + int gui; + long playback_rate; + int target; /* SCSI Id to be used */ + int lun; /* SCSI Lun to be used */ + UINT4 cddb_id; + int cddbp; + char * cddbp_server; + char * cddbp_port; + unsigned cddb_revision; + int cddb_year; + char cddb_genre[60]; + int illleadout_cd; + int reads_illleadout; + unsigned char *cdindex_id; + unsigned char *creator; + unsigned char *copyright_message; + unsigned char *disctitle; + unsigned char *tracktitle[100]; + unsigned char *trackcreator[100]; + index_list *trackindexlist[100]; + + int paranoia_selected; +#ifdef USE_PARANOIA + cdrom_paranoia *cdp; + + struct paranoia_parms_t + { + Ucbit disable_paranoia:1; + Ucbit disable_extra_paranoia:1; + Ucbit disable_scratch_detect:1; + Ucbit disable_scratch_repair:1; + int retries; + int overlap; + int mindynoverlap; + int maxdynoverlap; + } + paranoia_parms; +#endif + + unsigned md5blocksize; +#ifdef MD5_SIGNATURES + int md5count; + MD5_CTX context; + unsigned char MD5_result[16]; +#endif + +#ifdef ECHO_TO_SOUNDCARD + int soundcard_fd; +#endif + int echo; + + int just_the_toc; +} +global_t; + +extern global_t global; diff --git a/icedax/icedax.c b/icedax/icedax.c index e205f02..cd92921 100644 --- a/icedax/icedax.c +++ b/icedax/icedax.c @@ -120,6 +120,10 @@ #ifdef USE_LAME #include "mp3.h" /* mp3 file handling */ #endif +#ifdef USE_PARANOIA +#include +#include +#endif #include "interface.h" /* low level cdrom interfacing */ #include "icedax.h" #include "resample.h" @@ -128,9 +132,6 @@ #include "ringbuff.h" #include "global.h" #include "exitcodes.h" -#ifdef USE_PARANOIA -#include "cdda_paranoia.h" -#endif #include "defaults.h" static void RestrictPlaybackRate(long newrate); @@ -800,7 +801,7 @@ static void init_globals() global.useroverlap = -1; /* amount of overlapping sectors user override */ global.need_hostorder = 0; /* processing needs samples in host endianess */ global.in_lendian = -1; /* input endianess from SetupSCSI() */ - global.outputendianess = NONE; /* user specified output endianess */ + global.outputendianess = NONE_EN; /* user specified output endianess */ global.findminmax = 0; /* flag find extrem amplitudes */ #ifdef HAVE_LIMITS_H global.maxamp[0] = INT_MIN; /* maximum amplitude */ @@ -2418,7 +2419,7 @@ Rate Divider Rate Divider Rate Divider Rate Divider\n\ if (bulk == -1) bulk = 0; global.need_big_endian = global.audio_out->need_big_endian; - if (global.outputendianess != NONE) + if (global.outputendianess != NONE_EN) global.need_big_endian = global.outputendianess == BIG; if (global.no_file) global.fname_base[0] = '\0'; @@ -2602,7 +2603,7 @@ Rate Divider Rate Divider Rate Divider Rate Divider\n\ fputs( ", soundcard", stderr ); #endif #if defined USE_PARANOIA - fputs( ", libparanoia", stderr ); + fputs( ", libcdda_paranoia", stderr ); #endif fputs( " support\n", stderr ); } @@ -2892,8 +2893,28 @@ Rate Divider Rate Divider Rate Divider Rate Divider\n\ #ifdef USE_PARANOIA if (global.paranoia_selected) { long paranoia_mode; + cdrom_drive *tmpdrive; + usal_close(get_scsi_p()); + + tmpdrive = cdda_identify(global.dev_name, 0, NULL); + if (!tmpdrive) + { + fputs("Can't identify disc\n", stderr); + return 1; + } - global.cdp = paranoia_init(get_scsi_p(), global.nsectors); + if(global.nsectors) + { + tmpdrive->nsectors = global.nsectors; + tmpdrive->bigbuff = global.nsectors * CD_FRAMESIZE_RAW; + } + if (cdda_open(tmpdrive) != 0) + { + fputs("Can't open disc\n", stderr); + cdda_close(tmpdrive); + return 1; + } + global.cdp = paranoia_init(tmpdrive); if (global.paranoia_parms.overlap >= 0) { int overlap = global.paranoia_parms.overlap; @@ -2902,17 +2923,7 @@ Rate Divider Rate Divider Rate Divider Rate Divider\n\ overlap = global.nsectors - 1; paranoia_overlapset(global.cdp, overlap); } - /* - * Default to a minimum of dynamic overlapping == 0.5 sectors. - * If we don't do this, we get the default from libparanoia - * which is approx. 0.1. - */ - if (global.paranoia_parms.mindynoverlap < 0) - paranoia_dynoverlapset(global.cdp, CD_FRAMEWORDS/2, -1); - paranoia_dynoverlapset(global.cdp, - global.paranoia_parms.mindynoverlap * CD_FRAMEWORDS, - global.paranoia_parms.maxdynoverlap * CD_FRAMEWORDS); - + paranoia_mode = PARANOIA_MODE_FULL ^ PARANOIA_MODE_NEVERSKIP; if (global.paranoia_parms.disable_paranoia) { diff --git a/icedax/icedax.c.paranoiacdda b/icedax/icedax.c.paranoiacdda new file mode 100644 index 0000000..e205f02 --- /dev/null +++ b/icedax/icedax.c.paranoiacdda @@ -0,0 +1,3059 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)cdda2wav.c 1.64 06/02/19 Copyright 1998-2004 Heiko Eissfeldt, Copyright 2004-2006 J. Schilling */ +#undef DEBUG_BUFFER_ADDRESSES +#undef GPROF +#undef DEBUG_FORKED +#undef DEBUG_CLEANUP +#undef DEBUG_DYN_OVERLAP +#undef DEBUG_READS +#define DEBUG_ILLLEADOUT 0 /* 0 disables, 1 enables */ +/* + * Copyright: GNU Public License 2 applies + * + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +/* + * parts (C) Peter Widow + * parts (C) Thomas Niederreiter + * parts (C) RSA Data Security, Inc. + * + * last changes: + * 18.12.93 - first version, OK + * 01.01.94 - generalized & clean up HE + * 10.06.94 - first linux version HE + * 12.06.94 - wav header alignment problem fixed HE + * 12.08.94 - open the cdrom device O_RDONLY makes more sense :-) + * no more floating point math + * change to sector size 2352 which is more common + * sub-q-channel information per kernel ioctl requested + * doesn't work as well as before + * some new options (-max -i) + * 01.02.95 - async i/o via semaphores and shared memory + * 03.02.95 - overlapped reading on sectors + * 03.02.95 - generalized sample rates. all integral divisors are legal + * 04.02.95 - sun format added + * more divisors: all integral halves >= 1 allowed + * floating point math needed again + * 06.02.95 - bugfix for last track and not d0 + * tested with photo-cd with audio tracks + * tested with xa disk + * 29.01.96 - new options for bulk transfer + * 01.06.96 - tested with enhanced cd + * 01.06.96 - tested with cd-plus + * 02.06.96 - support pipes + * 02.06.96 - support raw format + * 04.02.96 - security hole fixed + * 22.04.97 - large parts rewritten + * 28.04.97 - make file names DOS compatible + * 01.09.97 - add speed control + * 20.10.97 - add find mono option + * Jan/Feb 98 - conversion to use Joerg Schillings SCSI library + * see ChangeLog + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined (HAVE_LIMITS_H) +#include +#endif +#if defined (HAVE_SYS_IOCTL_H) +#include +#endif +#include +#include +#include +#if defined (HAVE_SETPRIORITY) +#include +#endif +#include + +#include + +#ifdef HAVE_AREAS +#include +#endif + +#include "mytype.h" +#include "sndconfig.h" + +#include "semshm.h" /* semaphore functions */ +#include "sndfile.h" +#include "wav.h" /* wav file header structures */ +#include "sun.h" /* sun audio file header structures */ +#include "raw.h" /* raw file handling */ +#include "aiff.h" /* aiff file handling */ +#include "aifc.h" /* aifc file handling */ +#ifdef USE_LAME +#include "mp3.h" /* mp3 file handling */ +#endif +#include "interface.h" /* low level cdrom interfacing */ +#include "icedax.h" +#include "resample.h" +#include "toc.h" +#include "setuid.h" +#include "ringbuff.h" +#include "global.h" +#include "exitcodes.h" +#ifdef USE_PARANOIA +#include "cdda_paranoia.h" +#endif +#include "defaults.h" + +static void RestrictPlaybackRate(long newrate); +static void output_indices(FILE *fp, index_list *p, unsigned trackstart); +static int write_info_file(char *fname_baseval, unsigned int track, + unsigned long SamplesDone, int numbered); +static void CloseAudio(int channels_val, unsigned long nSamples, + struct soundfile *audio_out); +static void CloseAll(void); +static void OpenAudio(char *fname, double rate, long nBitsPerSample, + long channels_val, unsigned long expected_bytes, + struct soundfile*audio_out); +static void set_offset(myringbuff *p, int offset); +static int get_offset(myringbuff *p); +static void usage(void); +static void init_globals(void); +static int is_fifo(char *filename); + + +/* Rules: + * unique parameterless options first, + * unique parametrized option names next, + * ambigious parameterless option names next, + * ambigious string parametrized option names last + */ +static const char *opts = "paranoia,paraopts&,version,help,h,\ +no-write,N,dump-rates,R,bulk,B,alltracks,verbose-scsi+,V+,\ +find-extremes,F,find-mono,G,no-infofile,H,\ +deemphasize,T,info-only,J,silent-scsi,Q,\ +cddbp-server*,cddbp-port*,\ +scanbus,devices,device*,dev*,D*,auxdevice*,A*,interface*,I*,output-format*,O*,\ +output-endianess*,E*,cdrom-endianess*,C*,speed#,S#,\ +playback-realtime#L,p#L,md5#,M#,set-overlap#,P#,sound-device*,K*,\ +cddb#,L#,channels*,c*,bits-per-sample#,b#,rate#,r#,gui,g,\ +divider*,a*,track*,t*,index#,i#,duration*,d*,offset#,o#,\ +sectors-per-request#,n#,verbose-level&,v&,buffers-in-ring#,l#,\ +stereo,s,mono,m,wait,w,echo,e,quiet,q,max,x\ +"; + + +#ifdef NEED_O_BINARY +#include /* for setmode() prototype */ +#endif + +/* global variables */ +global_t global; + +/* static variables */ +static unsigned long nSamplesDone = 0; + +static int child_pid = -2; + +static unsigned long *nSamplesToDo; +static unsigned int current_track; +static int bulk = 0; + +unsigned int get_current_track(void); + +unsigned int get_current_track() +{ + return current_track; +} + +static void RestrictPlaybackRate(long newrate) +{ + global.playback_rate = newrate; + + if ( global.playback_rate < 25 ) global.playback_rate = 25; /* filter out insane values */ + if ( global.playback_rate > 250 ) global.playback_rate = 250; + + if ( global.playback_rate < 100 ) + global.nsectors = (global.nsectors*global.playback_rate)/100; +} + + +long SamplesNeeded(long amount, long undersampling_val) +{ + long retval = ((undersampling_val * 2 + Halved)*amount)/2; + if (Halved && (*nSamplesToDo & 1)) + retval += 2; + return retval; +} + +static int argc2; +static int argc3; +static char **argv2; + +static void reset_name_iterator(void); +static void reset_name_iterator() +{ + argv2 -= argc3 - argc2; + argc2 = argc3; +} + +static char *get_next_name(void); +static char *get_next_name() +{ + if (argc2 > 0) { + argc2--; + return (*argv2++); + } else { + return NULL; + } +} + +static char *cut_extension(char *fname); + +static char *cut_extension(char *fname) +{ + char *pp; + + pp = strrchr(fname, '.'); + + if (pp == NULL) { + pp = fname + strlen(fname); + } + *pp = '\0'; + + return pp; +} + +#ifdef INFOFILES +static void output_indices(FILE *fp, index_list *p, unsigned trackstart) +{ + int ci; + + fprintf(fp, "Index=\t\t"); + + if (p == NULL) { + fprintf(fp, "0\n"); + return; + } + + for (ci = 1; p != NULL; ci++, p = p->next) { + int frameoff = p->frameoffset; + + if (p->next == NULL) + fputs("\nIndex0=\t\t", fp); +#if 0 + else if ( ci > 8 && (ci % 8) == 1) + fputs("\nIndex =\t\t", fp); +#endif + if (frameoff != -1) + fprintf(fp, "%d ", frameoff - trackstart); + else + fprintf(fp, "-1 "); + } + fputs("\n", fp); +} + +/* + * write information before the start of the sampling process + * + * + * uglyfied for Joerg Schillings ultra dumb line parser + */ +static int write_info_file(char *fname_baseval, unsigned int track, + unsigned long int SamplesDone, int numbered) +{ + FILE *info_fp; + char fname[200]; + char datetime[30]; + time_t utc_time; + struct tm *tmptr; + + /* write info file */ + if (!strcmp(fname_baseval,"-")) return 0; + + strncpy(fname, fname_baseval, sizeof(fname) -1); + fname[sizeof(fname) -1] = 0; + if (numbered) + sprintf(cut_extension(fname), "_%02u.inf", track); + else + strcpy(cut_extension(fname), ".inf"); + + info_fp = fopen (fname, "w"); + if (!info_fp) + return -1; + +#if 0 +#ifdef MD5_SIGNATURES + if (global.md5blocksize) + MD5Final (global.MD5_result, &global.context); +#endif +#endif + + utc_time = time(NULL); + tmptr = localtime(&utc_time); + if (tmptr) { + strftime(datetime, sizeof(datetime), "%x %X", tmptr); + } else { + strncpy(datetime, "unknown", sizeof(datetime)); + } + fprintf(info_fp, "#created by icedax %s %s\n#\n", VERSION + , datetime + ); + fprintf(info_fp, +"CDINDEX_DISCID=\t'%s'\n" , global.cdindex_id); + fprintf(info_fp, +"CDDB_DISCID=\t0x%08lx\n\ +MCN=\t\t%s\n\ +ISRC=\t\t%15.15s\n\ +#\n\ +Albumperformer=\t'%s'\n\ +Performer=\t'%s'\n\ +Albumtitle=\t'%s'\n" + , (unsigned long) global.cddb_id + , Get_MCN() + , Get_ISRC(track) + , global.creator != NULL ? global.creator : (const unsigned char *)"" + , global.trackcreator[track] != NULL ? global.trackcreator[track] : + (global.creator != NULL ? global.creator : (const unsigned char *)"") + , global.disctitle != NULL ? global.disctitle : (const unsigned char *)"" + ); + fprintf(info_fp, + "Tracktitle=\t'%s'\n" + , global.tracktitle[track] ? global.tracktitle[track] : (const unsigned char *)"" + ); + fprintf(info_fp, "Tracknumber=\t%u\n" + , track + ); + fprintf(info_fp, + "Trackstart=\t%ld\n" + , Get_AudioStartSector(track) + ); + fprintf(info_fp, + "# track length in sectors (1/75 seconds each), rest samples\nTracklength=\t%ld, %d\n" + , SamplesDone/588L,(int)(SamplesDone%588)); + fprintf(info_fp, + "Pre-emphasis=\t%s\n" + , Get_Preemphasis(track) && (global.deemphasize == 0) ? "yes" : "no"); + fprintf(info_fp, + "Channels=\t%d\n" + , Get_Channels(track) ? 4 : global.channels == 2 ? 2 : 1); + { int cr = Get_Copyright(track); + fputs("Copy_permitted=\t", info_fp); + switch (cr) { + case 0: + fputs("once (copyright protected)\n", info_fp); + break; + case 1: + fputs("no (SCMS first copy)\n", info_fp); + break; + case 2: + fputs("yes (not copyright protected)\n", info_fp); + break; + default: + fputs("unknown\n", info_fp); + } + } + fprintf(info_fp, + "Endianess=\t%s\n" + , global.need_big_endian ? "big" : "little" + ); + fprintf(info_fp, "# index list\n"); + output_indices(info_fp, global.trackindexlist[track], + Get_AudioStartSector(track)); +#if 0 +/* MD5 checksums in info files are currently broken. + * for on-the-fly-recording the generation of info files has been shifted + * before the recording starts, so there is no checksum at that point. + */ +#ifdef MD5_SIGNATURES + fprintf(info_fp, + "#(blocksize) checksum\nMD-5=\t\t(%d) %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n" + , global.md5blocksize + , global.MD5_result[0] + , global.MD5_result[1] + , global.MD5_result[2] + , global.MD5_result[3] + , global.MD5_result[4] + , global.MD5_result[5] + , global.MD5_result[6] + , global.MD5_result[7] + , global.MD5_result[8] + , global.MD5_result[9] + , global.MD5_result[10] + , global.MD5_result[11] + , global.MD5_result[12] + , global.MD5_result[13] + , global.MD5_result[14] + , global.MD5_result[15]); +#endif +#endif + fclose(info_fp); + return 0; +} +#endif + +static void CloseAudio(int channels_val, unsigned long nSamples, + struct soundfile *audio_out) +{ + /* define length */ + audio_out->ExitSound( global.audio, (nSamples-global.SkippedSamples)*global.OutSampleSize*channels_val ); + + close (global.audio); + global.audio = -1; +} + +static unsigned int track = 1; + +/* On terminating: + * define size-related entries in audio file header, update and close file */ +static void CloseAll() +{ + WAIT_T chld_return_status; + int amiparent; + + /* terminate child process first */ + amiparent = child_pid > 0; + + if (global.iloop > 0) { + /* set to zero */ + global.iloop = 0; + } + +#if defined HAVE_FORK_AND_SHAREDMEM +# ifdef DEBUG_CLEANUP + fprintf(stderr, "%s terminating, \n", amiparent ? + "Parent (READER)" : "Child (WRITER)"); +#endif +#else +# ifdef DEBUG_CLEANUP + fprintf(stderr, "icedax single process terminating, \n"); +# endif +#endif + + if (amiparent || child_pid < 0) { + /* switch to original mode and close device */ + EnableCdda (get_scsi_p(), 0, 0); + } + + if (!amiparent) { + /* do general clean up */ + + if (global.audio>=0) { + if (bulk) { + /* finish sample file for this track */ + CloseAudio(global.channels, + global.nSamplesDoneInTrack, global.audio_out); + } else { + /* finish sample file for this track */ + CloseAudio(global.channels, + (unsigned int) *nSamplesToDo, global.audio_out); + } + } + + /* tell minimum and maximum amplitudes, if required */ + if (global.findminmax) { + fprintf(stderr, + "Right channel: minimum amplitude :%d/-32768, maximum amplitude :%d/32767\n", + global.minamp[0], global.maxamp[0]); + fprintf(stderr, + "Left channel: minimum amplitude :%d/-32768, maximum amplitude :%d/32767\n", + global.minamp[1], global.maxamp[1]); + } + + /* tell mono or stereo recording, if required */ + if (global.findmono) { + fprintf(stderr, "Audio samples are originally %s.\n", global.ismono ? "mono" : "stereo"); + } + + return; /* end of child or single process */ + } + + + if (global.have_forked == 1) { +#ifdef DEBUG_CLEANUP + fprintf(stderr, "Parent wait for child death, \n"); +#endif + + /* wait for child to terminate */ + if (0 > wait(&chld_return_status)) { + perror(""); + } else { + if (WIFEXITED(chld_return_status)) { + if (WEXITSTATUS(chld_return_status)) { + fprintf(stderr, "\nW Child exited with %d\n", WEXITSTATUS(chld_return_status)); + } + } + if (WIFSIGNALED(chld_return_status)) { + fprintf(stderr, "\nW Child exited due to signal %d\n", WTERMSIG(chld_return_status)); + } + if (WIFSTOPPED(chld_return_status)) { + fprintf(stderr, "\nW Child is stopped due to signal %d\n", WSTOPSIG(chld_return_status)); + } + } + +#ifdef DEBUG_CLEANUP + fprintf(stderr, "\nW Parent child death, state:%d\n", chld_return_status); +#endif + } + +#ifdef GPROF + rename("gmon.out", "gmon.child"); +#endif +} + + +/* report a usage error and exit */ +#ifdef PROTOTYPES +static void usage2 (const char *szMessage, ...) +#else +static void usage2(const char *szMessage, va_dcl va_alist) +#endif +{ + va_list marker; + +#ifdef PROTOTYPES + va_start(marker, szMessage); +#else + va_start(marker); +#endif + + vfprintf(stderr, szMessage, marker); + + va_end(marker); + fprintf(stderr, "\nPlease use -help or consult the man page for help.\n"); + + exit (1); +} + + +/* report a fatal error, clean up and exit */ +#ifdef PROTOTYPES +void FatalError (const char *szMessage, ...) +#else +void FatalError(const char *szMessage, va_dcl va_alist) +#endif +{ + va_list marker; + +#ifdef PROTOTYPES + va_start(marker, szMessage); +#else + va_start(marker); +#endif + + vfprintf(stderr, szMessage, marker); + + va_end(marker); + + if (child_pid >= 0) { + if (child_pid == 0) { + pid_t ppid; + /* + * Kill the parent too if we are not orphaned. + */ + ppid = getppid(); + if (ppid > 1) + kill(ppid, SIGINT); + } else { + kill(child_pid, SIGINT); + } + } + exit (1); +} + + +/* open the audio output file and prepare the header. + * the header will be defined on terminating (when the size + * is known). So hitting the interrupt key leaves an intact + * file. + */ +static void OpenAudio(char *fname, double rate, long nBitsPerSample, + long channels_val, unsigned long expected_bytes, + struct soundfile *audio_out) +{ + if (global.audio == -1) { + + global.audio = open (fname, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY +#ifdef SYNCHRONOUS_WRITE + | O_SYNC +#endif + , 0666); + if (global.audio == -1) { + if (errno == EAGAIN && is_fifo(fname)) { + FatalError ("Could not open fifo %s. Probably no fifo reader present.\n", fname); + } + perror("open audio sample file"); + FatalError ("Could not open file %s\n", fname); + } + } + global.SkippedSamples = 0; + any_signal = 0; + audio_out->InitSound( global.audio, channels_val, (unsigned long)rate, nBitsPerSample, expected_bytes ); + +#ifdef MD5_SIGNATURES + if (global.md5blocksize) + MD5Init (&global.context); + global.md5count = global.md5blocksize; +#endif +} + +#include "scsi_cmds.h" + +static int RealEnd(SCSI *usalp, UINT4 *buff); + +static int RealEnd(SCSI *usalp, UINT4 *buff) +{ + if (usal_cmd_err(usalp) != 0) { + int c,k,q; + + k = usal_sense_key(usalp); + c = usal_sense_code(usalp); + q = usal_sense_qual(usalp); + if ((k == 0x05 /* ILLEGAL_REQUEST */ && + c == 0x21 /* lba out of range */ && + q == 0x00) || + (k == 0x05 /* ILLEGAL_REQUEST */ && + c == 0x63 /*end of user area encountered on this track*/ && + q == 0x00) || + (k == 0x08 /* BLANK_CHECK */ && + c == 0x64 /* illegal mode for this track */ && + q == 0x00)) { + return 1; + } + } + + if (usal_getresid(usalp) > 16) return 1; + + { + unsigned char *p; + /* Look into the subchannel data */ + buff += CD_FRAMESAMPLES; + p = (unsigned char *)buff; + if (p[0] == 0x21 && p[1] == 0xaa) { + return 1; + } + } + return 0; +} + +static void set_offset(myringbuff *p, int offset) +{ +#ifdef DEBUG_SHM + fprintf(stderr, "Write offset %d at %p\n", offset, &p->offset); +#endif + p->offset = offset; +} + + +static int get_offset(myringbuff *p) +{ +#ifdef DEBUG_SHM + fprintf(stderr, "Read offset %d from %p\n", p->offset, &p->offset); +#endif + return p->offset; +} + + +static void usage() +{ + fputs( +"usage: icedax [OPTIONS ...] [trackfilenames ...]\n\ +OPTIONS:\n\ + [-c chans] [-s] [-m] [-b bits] [-r rate] [-a divider] [-S speed] [-x]\n\ + [-t track[+endtrack]] [-i index] [-o offset] [-d duration] [-F] [-G]\n\ + [-q] [-w] [-v vopts] [-R] [-P overlap] [-B] [-T] [-C input-endianess]\n\ + [-e] [-n sectors] [-N] [-J] [-L cddbp-mode] [-H] [-g] [-l buffers] [-D cd-device]\n\ + [-I interface] [-K sound-device] [-O audiotype] [-E output-endianess]\n\ + [-A auxdevice] [-paranoia] [-cddbp-server=name] [-cddbp-port=port] [-version]\n", stderr); + fputs("\ + (-D) dev=device set the cdrom or scsi device (as Bus,Id,Lun).\n\ + (-A) auxdevice=device set the aux device (typically /dev/cdrom).\n\ + (-K) sound-device=device set the sound device to use for -e (typically /dev/dsp).\n\ + (-I) interface=interface specify the interface for cdrom access.\n\ + (generic_scsi or cooked_ioctl).\n\ + (-c) channels=channels set 1 for mono, 2 or s for stereo (s: channels swapped).\n\ + (-s) -stereo select stereo recording.\n\ + (-m) -mono select mono recording.\n\ + (-x) -max select maximum quality (stereo/16-bit/44.1 KHz).\n\ + (-b) bits=bits set bits per sample per channel (8, 12 or 16 bits).\n\ + (-r) rate=rate set rate in samples per second. -R gives all rates\n\ + (-a) divider=divider set rate to 44100Hz / divider. -R gives all rates\n\ + (-R) -dump-rates dump a table with all available sample rates\n\ + (-S) speed=speedfactor set the cdrom drive to a given speed during reading\n\ + (-P) set-overlap=sectors set amount of overlap sampling (default is 0)\n\ + (-n) sectors-per-request=secs read 'sectors' sectors per request.\n\ + (-l) buffers-in-ring=buffers use a ring buffer with 'buffers' elements.\n\ + (-t) track=track[+end track] select start track (option. end track).\n\ + (-i) index=index select start index.\n\ + (-o) offset=offset start at 'offset' sectors behind start track/index.\n\ + one sector equivalents 1/75 second.\n\ + (-O) output-format=audiotype set to wav, au (sun), cdr (raw), aiff or aifc format.\n\ + (-C) cdrom-endianess=endian set little, big or guess input sample endianess.\n\ + (-E) output-endianess=endian set little or big output sample endianess.\n\ + (-d) duration=seconds set recording time in seconds or 0 for whole track.\n\ + (-w) -wait wait for audio signal, then start recording.\n\ + (-F) -find-extremes find extrem amplitudes in samples.\n\ + (-G) -find-mono find if input samples are mono.\n\ + (-T) -deemphasize undo pre-emphasis in input samples.\n\ + (-e) -echo echo audio data to sound device (see -K) SOUND_DEV.\n\ + (-v) verbose-level=optlist controls verbosity (for a list use -vhelp).\n\ + (-N) -no-write do not create audio sample files.\n\ + (-J) -info-only give disc information only.\n\ + (-L) cddb=cddbpmode do cddbp title lookups.\n\ + resolve multiple entries according to cddbpmode: 0=interactive, 1=first entry\n\ + (-H) -no-infofile no info file generation.\n\ + (-g) -gui generate special output suitable for gui frontends.\n\ + (-Q) -silent-scsi do not print status of erreneous scsi-commands.\n\ + -scanbus scan the SCSI bus and exit\n\ + --devices scan for system devices and print with native names\n\ + (-M) md5=count calculate MD-5 checksum for blocks of 'count' bytes.\n\ + (-q) -quiet quiet operation, no screen output.\n\ + (-p) playback-realtime=perc play (echo) audio pitched at perc percent (50%-200%).\n\ + (-V) -verbose-scsi each option increases verbosity for SCSI commands.\n\ + (-h) -help show this help screen.\n\ + (-B) -alltracks, -bulk record each track into a seperate file.\n\ + -paranoia use the lib paranoia for reading.\n\ + -paraopts=opts set options for lib paranoia (see -paraopts=help).\n\ + -cddbp-server=servername set the cddbp server to use for title lookups.\n\ + -cddbp-port=portnumber set the cddbp port to use for title lookups.\n\ + -version print version information.\n\ +\n\ +Please note: some short options will be phased out soon (disappear)!\n\ +\n\ +parameters: (optional) one or more file names or - for standard output.\n\ +", stderr); + fputs("Version ", stderr); + fputs(VERSION, stderr); + fprintf(stderr, "\n\ +defaults %s, %d bit, %d.%02d Hz, track 1, no offset, one track,\n", + CHANNELS-1?"stereo":"mono", BITS_P_S, + 44100 / UNDERSAMPLING, + (4410000 / UNDERSAMPLING) % 100); + fprintf(stderr, "\ + type %s '%s', don't wait for signal, not quiet,\n", + AUDIOTYPE, FILENAME); + fprintf(stderr, "\ + use %s, device %s, aux %s\n", + DEF_INTERFACE, CD_DEVICE, AUX_DEVICE); + exit( SYNTAX_ERROR ); +} + +static void init_globals() +{ +#ifdef HISTORICAL_JUNK + global.dev_name = CD_DEVICE; /* device name */ +#endif + global.aux_name = AUX_DEVICE;/* auxiliary cdrom device */ + strncpy(global.fname_base, FILENAME, sizeof(global.fname_base));/* auxiliary cdrom device */ + global.have_forked = 0; /* state variable for clean up */ + global.parent_died = 0; /* state variable for clean up */ + global.audio = -1; /* audio file desc */ + global.cooked_fd = -1; /* cdrom file desc */ + global.no_file = 0; /* flag no_file */ + global.no_infofile = 0; /* flag no_infofile */ + global.no_cddbfile = 0; /* flag no_cddbfile */ + global.quiet = 0; /* flag quiet */ + global.verbose = SHOW_TOC + SHOW_SUMMARY + SHOW_STARTPOSITIONS + SHOW_TITLES; /* verbose level */ + global.scsi_silent = 0; + global.scsi_verbose = 0; /* SCSI verbose level */ + global.scanbus = 0; + global.scandevs = 0; + global.multiname = 0; /* multiple file names given */ + global.sh_bits = 0; /* sh_bits: sample bit shift */ + global.Remainder= 0; /* remainder */ + global.iloop = 0; /* todo counter */ + global.SkippedSamples = 0; /* skipped samples */ + global.OutSampleSize = 0; /* output sample size */ + global.channels = CHANNELS; /* output sound channels */ + global.nSamplesDoneInTrack = 0; /* written samples in current track */ + global.buffers = 4; /* buffers to use */ + global.nsectors = NSECTORS; /* sectors to read in one request */ + global.overlap = 1; /* amount of overlapping sectors */ + global.useroverlap = -1; /* amount of overlapping sectors user override */ + global.need_hostorder = 0; /* processing needs samples in host endianess */ + global.in_lendian = -1; /* input endianess from SetupSCSI() */ + global.outputendianess = NONE; /* user specified output endianess */ + global.findminmax = 0; /* flag find extrem amplitudes */ +#ifdef HAVE_LIMITS_H + global.maxamp[0] = INT_MIN; /* maximum amplitude */ + global.maxamp[1] = INT_MIN; /* maximum amplitude */ + global.minamp[0] = INT_MAX; /* minimum amplitude */ + global.minamp[1] = INT_MAX; /* minimum amplitude */ +#else + global.maxamp[0] = -32768; /* maximum amplitude */ + global.maxamp[1] = -32768; /* maximum amplitude */ + global.minamp[0] = 32767; /* minimum amplitude */ + global.minamp[1] = 32767; /* minimum amplitude */ +#endif + global.speed = DEFAULT_SPEED; /* use default */ + global.userspeed = -1; /* speed user override */ + global.findmono = 0; /* flag find if samples are mono */ + global.ismono = 1; /* flag if samples are mono */ + global.swapchannels = 0; /* flag if channels shall be swapped */ + global.deemphasize = 0; /* flag undo pre-emphasis in samples */ + global.playback_rate = 100; /* new fancy selectable sound output rate */ + global.gui = 0; /* flag plain formatting for guis */ + global.cddb_id = 0; /* disc identifying id for CDDB database */ + global.cddb_revision = 0; /* entry revision for CDDB database */ + global.cddb_year = 0; /* disc identifying year for CDDB database */ + global.cddb_genre[0] = '\0'; /* disc identifying genre for CDDB database */ + global.cddbp = 0; /* flag if titles shall be looked up from CDDBP */ + global.cddbp_server = 0; /* user supplied CDDBP server */ + global.cddbp_port = 0; /* user supplied CDDBP port */ + global.illleadout_cd = 0; /* flag if illegal leadout is present */ + global.reads_illleadout = 0; /* flag if cdrom drive reads cds with illegal leadouts */ + global.disctitle = NULL; + global.creator = NULL; + global.copyright_message = NULL; + memset(global.tracktitle, 0, sizeof(global.tracktitle)); + memset(global.trackindexlist, 0, sizeof(global.trackindexlist)); + + global.just_the_toc = 0; +#ifdef USE_PARANOIA + global.paranoia_parms.disable_paranoia = + global.paranoia_parms.disable_extra_paranoia = + global.paranoia_parms.disable_scratch_detect = + global.paranoia_parms.disable_scratch_repair = 0; + global.paranoia_parms.retries = 20; + global.paranoia_parms.overlap = -1; + global.paranoia_parms.mindynoverlap = -1; + global.paranoia_parms.maxdynoverlap = -1; +#endif +} + +#if !defined (HAVE_STRCASECMP) || (HAVE_STRCASECMP != 1) +#include +static int strcasecmp(const char *s1, const char *s2); +static int strcasecmp(const char *s1, const char *s2) +{ + if (s1 && s2) { + while (*s1 && *s2 && (tolower(*s1) - tolower(*s2) == 0)) { + s1++; + s2++; + } + if (*s1 == '\0' && *s2 == '\0') return 0; + if (*s1 == '\0') return -1; + if (*s2 == '\0') return +1; + return tolower(*s1) - tolower(*s2); + } + return -1; +} +#endif + +static int is_fifo(char *filename) +{ +#if defined S_ISFIFO + struct stat statstruct; + + if (stat(filename, &statstruct)) { + /* maybe the output file does not exist. */ + if (errno == ENOENT) + return 0; + else comerr("Error during stat for output file\n"); + } else { + if (S_ISFIFO(statstruct.st_mode)) { + return 1; + } + } + return 0; +#else + return 0; +#endif +} + + +#if !defined (HAVE_STRTOUL) || (HAVE_STRTOUL != 1) +static unsigned int strtoul(const char *s1, char **s2, int base); +static unsigned int strtoul(const char *s1, char **s2, int base) +{ + long retval; + + if (base == 10) { + /* strip zeros in front */ + while (*s1 == '0') + s1++; + } + if (s2 != NULL) { + *s2 = astol(s1, &retval); + } else { + (void) astol(s1, &retval); + } + + return (unsigned long) retval; +} +#endif + +static unsigned long SectorBurst; +#if (SENTINEL > CD_FRAMESIZE_RAW) +error block size for overlap check has to be < sector size +#endif + + +static void +switch_to_realtime_priority(void); + +#ifdef HAVE_SYS_PRIOCNTL_H + +#include +#include +static void switch_to_realtime_priority() +{ + pcinfo_t info; + pcparms_t param; + rtinfo_t rtinfo; + rtparms_t rtparam; + int pid; + + pid = getpid(); + + /* get info */ + strcpy(info.pc_clname, "RT"); + if (-1 == priocntl(P_PID, pid, PC_GETCID, (void *)&info)) { + errmsg("Cannot get priority class id priocntl(PC_GETCID)\n"); + goto prio_done; + } + + memmove(&rtinfo, info.pc_clinfo, sizeof(rtinfo_t)); + + /* set priority not to the max */ + rtparam.rt_pri = rtinfo.rt_maxpri - 2; + rtparam.rt_tqsecs = 0; + rtparam.rt_tqnsecs = RT_TQDEF; + param.pc_cid = info.pc_cid; + memmove(param.pc_clparms, &rtparam, sizeof(rtparms_t)); + priv_on(); + needroot(0); + if (-1 == priocntl(P_PID, pid, PC_SETPARMS, (void *)¶m)) + errmsg("Cannot set priority class parameters priocntl(PC_SETPARMS)\n"); +prio_done: + priv_off(); + dontneedroot(); +} +#else +#if defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING -0 >= 0 +#define USE_POSIX_PRIORITY_SCHEDULING +#endif +#ifdef USE_POSIX_PRIORITY_SCHEDULING +#include + +static void switch_to_realtime_priority() +{ +#ifdef _SC_PRIORITY_SCHEDULING + if (sysconf(_SC_PRIORITY_SCHEDULING) == -1) { + errmsg("WARNING: RR-scheduler not available, disabling.\n"); + } else +#endif + { + int sched_fifo_min, sched_fifo_max; + struct sched_param sched_parms; + + sched_fifo_min = sched_get_priority_min(SCHED_FIFO); + sched_fifo_max = sched_get_priority_max(SCHED_FIFO); + sched_parms.sched_priority = sched_fifo_max - 1; + priv_on(); + needroot(0); + if (-1 == sched_setscheduler(getpid(), SCHED_FIFO, &sched_parms) + && global.quiet != 1) + errmsg("cannot set posix realtime scheduling policy\n"); + priv_off(); + dontneedroot(); + } +} +#else +#if defined(__CYGWIN32__) + +/* + * NOTE: Base.h from Cygwin-B20 has a second typedef for BOOL. + * We define BOOL to make all local code use BOOL + * from Windows.h and use the hidden __SBOOL for + * our global interfaces. + * + * NOTE: windows.h from Cygwin-1.x includes a structure field named sample, + * so me may not define our own 'sample' or need to #undef it now. + * With a few nasty exceptions, Microsoft assumes that any global + * defines or identifiers will begin with an Uppercase letter, so + * there may be more of these problems in the future. + * + * NOTE: windows.h defines interface as an alias for struct, this + * is used by COM/OLE2, I guess it is class on C++ + * We man need to #undef 'interface' + */ +#define BOOL WBOOL /* This is the Win BOOL */ +#define format __format /* Avoid format parameter hides global ... */ +#include +#undef format +#undef interface + +static void switch_to_realtime_priority() +{ + /* set priority class */ + if (FALSE == SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)) { + fprintf(stderr, "No realtime priority possible.\n"); + return; + } + + /* set thread priority */ + if (FALSE == SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST)) { + fprintf(stderr, "Could not set realtime priority.\n"); + } +} +#else +static void switch_to_realtime_priority() +{ +} +#endif +#endif +#endif + +/* SCSI cleanup */ +int on_exitscsi(void *status); + +int on_exitscsi(void *status) +{ + exit((intptr_t)status); + return 0; +} + +/* wrapper for signal handler exit needed for Mac-OS-X */ +static void exit_wrapper(int status); + +static void exit_wrapper(int status) +{ +#if defined DEBUG_CLEANUP + fprintf( stderr, "Exit(%d) for %s\n", status, child_pid == 0 ? "Child" : "Parent"); + fflush(stderr); +#endif + + if (child_pid != 0) { + SCSI *usalp = get_scsi_p(); + if (usalp && usalp->running) { + usalp->cb_fun = on_exitscsi; + usalp->cb_arg = (void *) (uintptr_t) status; + } else { + on_exitscsi((void *) (intptr_t) status); + } + } else { + exit(status); + } +} + +/* signal handler for process communication */ +static void set_nonforked(int status); + +/* ARGSUSED */ +static void set_nonforked(int status) +{ + global.parent_died = 1; +#if defined DEBUG_CLEANUP +fprintf( stderr, "SIGPIPE received from %s\n.", child_pid == 0 ? "Child" : "Parent"); +#endif + if (child_pid == 0) { + pid_t ppid; + /* + * Kill the parent too if we are not orphaned. + */ + ppid = getppid(); + if (ppid > 1) + kill(ppid, SIGINT); + } else { + kill(child_pid, SIGINT); + } + exit(SIGPIPE_ERROR); +} + + + +#ifdef USE_PARANOIA +static struct paranoia_statistics +{ + long c_sector; + long v_sector; + int last_heartbeatstate; + long lasttime; + char heartbeat; + int minoverlap; + int curoverlap; + int maxoverlap; + int slevel; + int slastlevel; + int stimeout; + int rip_smile_level; + unsigned verifies; + unsigned reads; + unsigned fixup_edges; + unsigned fixup_atoms; + unsigned readerrs; + unsigned skips; + unsigned overlaps; + unsigned scratchs; + unsigned drifts; + unsigned fixup_droppeds; + unsigned fixup_dupeds; +} *para_stat; + + +static void paranoia_reset(void); +static void paranoia_reset() +{ + para_stat->c_sector = 0; + para_stat->v_sector = 0; + para_stat->last_heartbeatstate = 0; + para_stat->lasttime = 0; + para_stat->heartbeat = ' '; + para_stat->minoverlap = 0x7FFFFFFF; + para_stat->curoverlap = 0; + para_stat->maxoverlap = 0; + para_stat->slevel = 0; + para_stat->slastlevel = 0; + para_stat->stimeout = 0; + para_stat->rip_smile_level = 0; + para_stat->verifies = 0; + para_stat->reads = 0; + para_stat->readerrs = 0; + para_stat->fixup_edges = 0; + para_stat->fixup_atoms = 0; + para_stat->fixup_droppeds = 0; + para_stat->fixup_dupeds = 0; + para_stat->drifts = 0; + para_stat->scratchs = 0; + para_stat->overlaps = 0; + para_stat->skips = 0; +} + +static void paranoia_callback(long inpos, int function); + +static void paranoia_callback(long inpos, int function) +{ + struct timeval thistime; + long test; + + switch (function) { + case -2: + para_stat->v_sector = inpos / CD_FRAMEWORDS; + return; + case -1: + para_stat->last_heartbeatstate = 8; + para_stat->heartbeat = '*'; + para_stat->slevel = 0; + para_stat->v_sector = inpos / CD_FRAMEWORDS; + break; + case PARANOIA_CB_VERIFY: + if (para_stat->stimeout >= 30) { + if (para_stat->curoverlap > CD_FRAMEWORDS) { + para_stat->slevel = 2; + } else { + para_stat->slevel = 1; + } + } + para_stat->verifies++; + break; + case PARANOIA_CB_READ: + if (inpos / CD_FRAMEWORDS > para_stat->c_sector) { + para_stat->c_sector = inpos / CD_FRAMEWORDS; + } + para_stat->reads++; + break; + case PARANOIA_CB_FIXUP_EDGE: + if (para_stat->stimeout >= 5) { + if (para_stat->curoverlap > CD_FRAMEWORDS) { + para_stat->slevel = 2; + } else { + para_stat->slevel = 1; + } + } + para_stat->fixup_edges++; + break; + case PARANOIA_CB_FIXUP_ATOM: + if (para_stat->slevel < 3 || para_stat->stimeout > 5) { + para_stat->slevel = 3; + } + para_stat->fixup_atoms++; + break; + case PARANOIA_CB_READERR: + para_stat->slevel = 6; + para_stat->readerrs++; + break; + case PARANOIA_CB_SKIP: + para_stat->slevel = 8; + para_stat->skips++; + break; + case PARANOIA_CB_OVERLAP: + para_stat->curoverlap = inpos; + if (inpos > para_stat->maxoverlap) + para_stat->maxoverlap = inpos; + if (inpos < para_stat->minoverlap) + para_stat->minoverlap = inpos; + para_stat->overlaps++; + break; + case PARANOIA_CB_SCRATCH: + para_stat->slevel = 7; + para_stat->scratchs++; + break; + case PARANOIA_CB_DRIFT: + if (para_stat->slevel < 4 || para_stat->stimeout > 5) { + para_stat->slevel = 4; + } + para_stat->drifts++; + break; + case PARANOIA_CB_FIXUP_DROPPED: + para_stat->slevel = 5; + para_stat->fixup_droppeds++; + break; + case PARANOIA_CB_FIXUP_DUPED: + para_stat->slevel = 5; + para_stat->fixup_dupeds++; + break; + } + + gettimeofday(&thistime, NULL); + /* now in tenth of seconds. */ + test = thistime.tv_sec * 10 + thistime.tv_usec / 100000; + + if (para_stat->lasttime != test + || function == -1 + || para_stat->slastlevel != para_stat->slevel) { + + if (function == -1 + || para_stat->slastlevel != para_stat->slevel) { + + static const char hstates[] = " .o0O0o."; + + para_stat->lasttime = test; + para_stat->stimeout++; + + para_stat->last_heartbeatstate++; + if (para_stat->last_heartbeatstate > 7) { + para_stat->last_heartbeatstate = 0; + } + para_stat->heartbeat = hstates[para_stat->last_heartbeatstate]; + + if (function == -1) { + para_stat->heartbeat = '*'; + } + } + + if (para_stat->slastlevel != para_stat->slevel) { + para_stat->stimeout = 0; + } + para_stat->slastlevel = para_stat->slevel; + } + + if (para_stat->slevel < 8) { + para_stat->rip_smile_level = para_stat->slevel; + } else { + para_stat->rip_smile_level = 0; + } +} +#endif + +static long lSector; +static long lSector_p2; +static double rate = 44100.0 / UNDERSAMPLING; +static int bits = BITS_P_S; +static char fname[200]; +static const char *audio_type; +static long BeginAtSample; +static unsigned long SamplesToWrite; +static unsigned minover; +static unsigned maxover; + +static unsigned long calc_SectorBurst(void); +static unsigned long calc_SectorBurst() +{ + unsigned long SectorBurstVal; + + SectorBurstVal = min(global.nsectors, + (global.iloop + CD_FRAMESAMPLES-1) / CD_FRAMESAMPLES); + if ( lSector+(int)SectorBurst-1 >= lSector_p2 ) + SectorBurstVal = lSector_p2 - lSector; + return SectorBurstVal; +} + +/* if PERCENTAGE_PER_TRACK is defined, the percentage message will reach + * 100% every time a track end is reached or the time limit is reached. + * + * Otherwise if PERCENTAGE_PER_TRACK is not defined, the percentage message + * will reach 100% once at the very end of the last track. + */ +#define PERCENTAGE_PER_TRACK + +static int do_read(myringbuff *p, unsigned *total_unsuccessful_retries); +static int do_read(myringbuff *p, unsigned *total_unsuccessful_retries) +{ + unsigned char *newbuf; + int offset; + unsigned int added_size; + + /* how many sectors should be read */ + SectorBurst = calc_SectorBurst(); + +#ifdef USE_PARANOIA + if (global.paranoia_selected) { + int i; + + for (i = 0; i < SectorBurst; i++) { + void *dp; + + dp = paranoia_read_limited(global.cdp, paranoia_callback, + global.paranoia_parms.retries); +/* + { + char *err; + char *msg; + err = cdda_errors(global.cdp); + msg = cdda_messages(global.cdp); + if (err) { + fputs(err, stderr); + free(err); + } + if (msg) { + fputs(msg, stderr); + free(msg); + } + } +*/ + if (dp != NULL) { + memcpy(p->data + i*CD_FRAMESAMPLES, dp, + CD_FRAMESIZE_RAW); + } else { + fputs("E unrecoverable error!", stderr); + exit(READ_ERROR); + } + } + newbuf = (unsigned char *)p->data; + offset = 0; + set_offset(p,offset); + added_size = SectorBurst * CD_FRAMESAMPLES; + global.overlap = 0; + handle_inputendianess(p->data, added_size); + } else +#endif + { + unsigned int retry_count; +#define MAX_READRETRY 12 + + retry_count = 0; + do { + SCSI *usalp = get_scsi_p(); + int retval; +#ifdef DEBUG_READS +fprintf(stderr, "reading from %lu to %lu, overlap %u\n", lSector, lSector + SectorBurst -1, global.overlap); +#endif + +#ifdef DEBUG_BUFFER_ADDRESSES +fprintf(stderr, "%p %l\n", p->data, global.pagesize); +if (((unsigned)p->data) & (global.pagesize -1) != 0) { + fprintf(stderr, "Address %p is NOT page aligned!!\n", p->data); +} +#endif + + if (global.reads_illleadout != 0 && lSector > Get_StartSector(LastTrack())) { + int singles = 0; + UINT4 bufferSub[CD_FRAMESAMPLES + 24]; + + /* we switch to single sector reads, + * in order to handle the remaining sectors. */ + usalp->silent++; + do { + ReadCdRomSub( usalp, bufferSub, lSector+singles, 1 ); + *eorecording = RealEnd( usalp, bufferSub ); + if (*eorecording) { + break; + } + memcpy(p->data+singles*CD_FRAMESAMPLES, bufferSub, CD_FRAMESIZE_RAW); + singles++; + } while (singles < SectorBurst); + usalp->silent--; + + if ( *eorecording ) { + patch_real_end(lSector+singles); + SectorBurst = singles; +#if DEBUG_ILLLEADOUT +fprintf(stderr, "iloop=%11lu, nSamplesToDo=%11lu, end=%lu -->\n", +global.iloop, *nSamplesToDo, lSector+singles); +#endif + + *nSamplesToDo -= global.iloop - SectorBurst*CD_FRAMESAMPLES; + global.iloop = SectorBurst*CD_FRAMESAMPLES; +#if DEBUG_ILLLEADOUT +fprintf(stderr, "iloop=%11lu, nSamplesToDo=%11lu\n\n", +global.iloop, *nSamplesToDo); +#endif + + } + } else { + retval = ReadCdRom( usalp, p->data, lSector, SectorBurst ); + } + handle_inputendianess(p->data, SectorBurst * CD_FRAMESAMPLES); + if (NULL == + (newbuf = synchronize( p->data, SectorBurst*CD_FRAMESAMPLES, + *nSamplesToDo-global.iloop ))) { + /* could not synchronize! + * Try to invalidate the cdrom cache. + * Increase overlap setting, if possible. + */ + /*trash_cache(p->data, lSector, SectorBurst);*/ + if (global.overlap < global.nsectors - 1) { + global.overlap++; + lSector--; + SectorBurst = calc_SectorBurst(); +#ifdef DEBUG_DYN_OVERLAP +fprintf(stderr, "using increased overlap of %u\n", global.overlap); +#endif + } else { + lSector += global.overlap - 1; + global.overlap = 1; + SectorBurst = calc_SectorBurst(); + } + } else + break; + } while (++retry_count < MAX_READRETRY); + + if (retry_count == MAX_READRETRY && newbuf == NULL && global.verbose != 0) { + (*total_unsuccessful_retries)++; + } + + if (newbuf) { + offset = newbuf - ((unsigned char *)p->data); + } else { + offset = global.overlap * CD_FRAMESIZE_RAW; + } + set_offset(p,offset); + + /* how much has been added? */ + added_size = SectorBurst * CD_FRAMESAMPLES - offset/4; + + if (newbuf && *nSamplesToDo != global.iloop) { + minover = min(global.overlap, minover); + maxover = max(global.overlap, maxover); + + + /* should we reduce the overlap setting ? */ + if (offset > CD_FRAMESIZE_RAW && global.overlap > 1) { +#ifdef DEBUG_DYN_OVERLAP +fprintf(stderr, "decreasing overlap from %u to %u (jitter %d)\n", global.overlap, global.overlap-1, offset - (global.overlap)*CD_FRAMESIZE_RAW); +#endif + global.overlap--; + SectorBurst = calc_SectorBurst(); + } + } + } + if (global.iloop >= added_size) { + global.iloop -= added_size; + } else { + global.iloop = 0; + } + + lSector += SectorBurst - global.overlap; + +#if defined PERCENTAGE_PER_TRACK && defined HAVE_FORK_AND_SHAREDMEM + { + int as; + while ((as = Get_StartSector(current_track+1)) != -1 + && lSector >= as) { + current_track++; + } + } +#endif + + return offset; +} + +static void +print_percentage(unsigned *poper, int c_offset); + +static void print_percentage(unsigned *poper, int c_offset) +{ + unsigned per; +#ifdef PERCENTAGE_PER_TRACK + /* Thomas Niederreiter wants percentage per track */ + unsigned start_in_track = max(BeginAtSample, + Get_AudioStartSector(current_track)*CD_FRAMESAMPLES); + + per = min(BeginAtSample + (long)*nSamplesToDo, + Get_StartSector(current_track+1)*CD_FRAMESAMPLES) + - (long)start_in_track; + + per = (BeginAtSample+nSamplesDone + - start_in_track + )/(per/100); + +#else + per = global.iloop ? (nSamplesDone)/(*nSamplesToDo/100) : 100; +#endif + + if (global.overlap > 0) { + fprintf(stderr, "\r%2d/%2d/%2d/%7d %3d%%", + minover, maxover, global.overlap, + c_offset - global.overlap*CD_FRAMESIZE_RAW, + per); + } else if (*poper != per) { + fprintf(stderr, "\r%3d%%", per); + } + *poper = per; + fflush(stderr); +} + +static unsigned long do_write(myringbuff *p); +static unsigned long do_write(myringbuff *p) +{ + int current_offset; + unsigned int InSamples; + static unsigned oper = 200; + + current_offset = get_offset(p); + + /* how many bytes are available? */ + InSamples = global.nsectors*CD_FRAMESAMPLES - current_offset/4; + /* how many samples are wanted? */ + InSamples = min((*nSamplesToDo-nSamplesDone),InSamples); + + /* when track end is reached, close current file and start a new one */ + while ((nSamplesDone < *nSamplesToDo) && (InSamples != 0)) { + long unsigned int how_much = InSamples; + + long int left_in_track; + left_in_track = Get_StartSector(current_track+1)*CD_FRAMESAMPLES + - (int)(BeginAtSample+nSamplesDone); + + if (*eorecording != 0 && current_track == cdtracks+1 && + (*total_segments_read) == (*total_segments_written)+1) { + /* limit, if the actual end of the last track is + * not known from the toc. */ + left_in_track = InSamples; + } + +if (left_in_track < 0) { + fprintf(stderr, "internal error: negative left_in_track:%ld, current_track=%d\n",left_in_track, current_track); +} + + if (bulk) { + how_much = min(how_much, (unsigned long) left_in_track); + } + +#ifdef MD5_SIGNATURES + if (global.md5count) { + MD5Update (&global.context, ((unsigned char *)p->data) +current_offset, min(global.md5count,how_much)); + global.md5count -= min(global.md5count,how_much); + } +#endif + if ( SaveBuffer ( p->data + current_offset/4, + how_much, + &nSamplesDone) ) { + if (global.have_forked == 1) { + pid_t ppid; + /* + * Kill the parent too if we are not orphaned. + */ + ppid = getppid(); + if (ppid > 1) + kill(ppid, SIGINT); + } + exit(WRITE_ERROR); + } + + global.nSamplesDoneInTrack += how_much; + SamplesToWrite -= how_much; + + /* move residual samples upto buffer start */ + if (how_much < InSamples) { + memmove( + (char *)(p->data) + current_offset, + (char *)(p->data) + current_offset + how_much*4, + (InSamples - how_much) * 4); + } + + if ((unsigned long) left_in_track <= InSamples || SamplesToWrite == 0) { + /* the current portion to be handled is + the end of a track */ + + if (bulk) { + /* finish sample file for this track */ + CloseAudio(global.channels, + global.nSamplesDoneInTrack, global.audio_out); + } else if (SamplesToWrite == 0) { + /* finish sample file for this track */ + CloseAudio(global.channels, + (unsigned int) *nSamplesToDo, global.audio_out); + } + + if (global.verbose) { +#ifdef USE_PARANOIA + double f; +#endif + print_percentage(&oper, current_offset); + fputc(' ', stderr); +#ifndef THOMAS_SCHAU_MAL + if ((unsigned long)left_in_track > InSamples) { + fputs(" incomplete", stderr); + } +#endif + if (global.tracktitle[current_track] != NULL) { + fprintf( stderr, + " track %2u '%s' recorded", + current_track, + global.tracktitle[current_track]); + } else { + fprintf( stderr, + " track %2u recorded", + current_track); + } +#ifdef USE_PARANOIA + oper = para_stat->readerrs + para_stat->skips + + para_stat->fixup_edges + para_stat->fixup_atoms + + para_stat->fixup_droppeds + para_stat->fixup_dupeds + + para_stat->drifts; + f = (100.0 * oper) / (((double)global.nSamplesDoneInTrack)/588.0); + + if (para_stat->readerrs) { + fprintf(stderr, " with audible hard errors"); + } else if ((para_stat->skips) > 0) { + fprintf(stderr, " with %sretry/skip errors", + f < 2.0 ? "":"audible "); + } else if (oper > 0) { + oper = f; + + fprintf(stderr, " with "); + if (oper < 2) + fprintf(stderr, "minor"); + else if (oper < 10) + fprintf(stderr, "medium"); + else if (oper < 67) + fprintf(stderr, "noticable audible"); + else if (oper < 100) + fprintf(stderr, "major audible"); + else + fprintf(stderr, "extreme audible"); + fprintf(stderr, " problems"); + } else { + fprintf(stderr, " successfully"); + } + if (f >= 0.1) + fprintf(stderr, " (%.1f%% problem sectors)", f); +#else + fprintf(stderr, " successfully"); +#endif + + if (waitforsignal == 1) { + fprintf(stderr, ". %d silent samples omitted", global.SkippedSamples); + } + fputs("\n", stderr); + + if (global.reads_illleadout && *eorecording == 1) { + fprintf(stderr, "Real lead out at: %ld sectors\n", + (*nSamplesToDo+BeginAtSample)/CD_FRAMESAMPLES); + } +#ifdef USE_PARANOIA + if (global.paranoia_selected) { + oper = 200; /* force new output */ + print_percentage(&oper, current_offset); + if (para_stat->minoverlap == 0x7FFFFFFF) + para_stat->minoverlap = 0; + fprintf(stderr, " %u rderr, %u skip, %u atom, %u edge, %u drop, %u dup, %u drift\n" + ,para_stat->readerrs + ,para_stat->skips + ,para_stat->fixup_atoms + ,para_stat->fixup_edges + ,para_stat->fixup_droppeds + ,para_stat->fixup_dupeds + ,para_stat->drifts); + oper = 200; /* force new output */ + print_percentage(&oper, current_offset); + fprintf(stderr, " %u overlap(%.4g .. %.4g)\n", + para_stat->overlaps, + (float)para_stat->minoverlap / (2352.0/2.0), + (float)para_stat->maxoverlap / (2352.0/2.0)); + paranoia_reset(); + } +#endif + } + + global.nSamplesDoneInTrack = 0; + if ( bulk && SamplesToWrite > 0 ) { + if ( !global.no_file ) { + char *tmp_fname; + + /* build next filename */ + tmp_fname = get_next_name(); + if (tmp_fname != NULL) { + strncpy(global.fname_base, + tmp_fname, + sizeof global.fname_base); + global.fname_base[ + sizeof(global.fname_base)-1] = + '\0'; + } + + tmp_fname = cut_extension(global.fname_base); + tmp_fname[0] = '\0'; + + if (global.multiname == 0) { + sprintf(fname, "%s_%02u.%s", + global.fname_base, + current_track+1, + audio_type); + } else { + sprintf(fname, "%s.%s", + global.fname_base, + audio_type); + } + + OpenAudio( fname, rate, bits, global.channels, + (Get_AudioStartSector(current_track+1) - + Get_AudioStartSector(current_track)) + *CD_FRAMESIZE_RAW, + global.audio_out); + } /* global.nofile */ + } /* if ( bulk && SamplesToWrite > 0 ) */ + current_track++; + + } /* left_in_track <= InSamples */ + InSamples -= how_much; + + } /* end while */ + if (!global.quiet && *nSamplesToDo != nSamplesDone) { + print_percentage(&oper, current_offset); + } + return nSamplesDone; +} + +#define PRINT_OVERLAP_INIT \ + if (global.verbose) { \ + if (global.overlap > 0) \ + fprintf(stderr, "overlap:min/max/cur, jitter, percent_done:\n / / / 0%%"); \ + else \ + fputs("percent_done:\n 0%", stderr); \ + } + +#if defined HAVE_FORK_AND_SHAREDMEM +static void forked_read(void); + +/* This function does all audio cdrom reads + * until there is nothing more to do + */ +static void forked_read() +{ + unsigned total_unsuccessful_retries = 0; + +#if !defined(HAVE_SEMGET) || !defined(USE_SEMAPHORES) + init_child(); +#endif + + minover = global.nsectors; + + PRINT_OVERLAP_INIT + while (global.iloop) { + + do_read(get_next_buffer(), &total_unsuccessful_retries); + + define_buffer(); + + } /* while (global.iloop) */ + + if (total_unsuccessful_retries) { + fprintf(stderr,"%u unsuccessful matches while reading\n",total_unsuccessful_retries); + } +} + +static void forked_write(void); + +static void forked_write() +{ + + /* don't need these anymore. Good security policy says we get rid + of them ASAP */ + priv_off(); + neverneedroot(); + neverneedgroup(); + +#if defined(HAVE_SEMGET) && defined(USE_SEMAPHORES) +#else + init_parent(); +#endif + + for (;nSamplesDone < *nSamplesToDo;) { + if (*eorecording == 1 && (*total_segments_read) == (*total_segments_written)) break; + + /* get oldest buffers */ + + nSamplesDone = do_write(get_oldest_buffer()); + + drop_buffer(); + + } /* end for */ + +} +#endif + +/* This function implements the read and write calls in one loop (in case + * there is no fork/thread_create system call). + * This means reads and writes have to wait for each other to complete. + */ +static void nonforked_loop(void); + +static void nonforked_loop() +{ + unsigned total_unsuccessful_retries = 0; + + minover = global.nsectors; + + PRINT_OVERLAP_INIT + while (global.iloop) { + + do_read(get_next_buffer(), &total_unsuccessful_retries); + + do_write(get_oldest_buffer()); + + } + + if (total_unsuccessful_retries) { + fprintf(stderr,"%u unsuccessful matches while reading\n",total_unsuccessful_retries); + } + +} + +void verbose_usage(void); + +void verbose_usage() +{ + fputs("\ + help lists all verbose options.\n\ + disable disables verbose mode.\n\ + all enables all verbose options.\n\ + toc display the table of contents.\n\ + summary display a summary of track parameters.\n\ + indices retrieve/display index positions.\n\ + catalog retrieve/display media catalog number.\n\ + trackid retrieve/display international standard recording code.\n\ + sectors display the start sectors of each track.\n\ + titles display any known track titles.\n\ +", stderr); +} + +#ifdef USE_PARANOIA +void paranoia_usage(void); + +void paranoia_usage() +{ + fputs("\ + help lists all paranoia options.\n\ + disable disables paranoia mode. Paranoia is still being used.\n\ + no-verify switches verify off, and overlap on.\n\ + retries=amount set the number of maximum retries per sector.\n\ + overlap=amount set the number of sectors used for statical paranoia overlap.\n\ + minoverlap=amt set the min. number of sectors used for dynamic paranoia overlap.\n\ + maxoverlap=amt set the max. number of sectors used for dynamic paranoia overlap.\n\ +", stderr); +} +#endif + +int +handle_verbose_opts(char *optstr, long *flagp); + +int handle_verbose_opts(char *optstr, long *flagp) +{ + char *ep; + char *np; + int optlen; + + *flagp = 0; + while (*optstr) { + if ((ep = strchr(optstr, ',')) != NULL) { + optlen = ep - optstr; + np = ep + 1; + } else { + optlen = strlen(optstr); + np = optstr + optlen; + } + if (strncmp(optstr, "toc", optlen) == 0) { + *flagp |= SHOW_TOC; + } + else if (strncmp(optstr, "summary", optlen) == 0) { + *flagp |= SHOW_SUMMARY; + } + else if (strncmp(optstr, "indices", optlen) == 0) { + *flagp |= SHOW_INDICES; + } + else if (strncmp(optstr, "catalog", optlen) == 0) { + *flagp |= SHOW_MCN; + } + else if (strncmp(optstr, "trackid", optlen) == 0) { + *flagp |= SHOW_ISRC; + } + else if (strncmp(optstr, "sectors", optlen) == 0) { + *flagp |= SHOW_STARTPOSITIONS; + } + else if (strncmp(optstr, "titles", optlen) == 0) { + *flagp |= SHOW_TITLES; + } + else if (strncmp(optstr, "all", optlen) == 0) { + *flagp |= SHOW_MAX; + } + else if (strncmp(optstr, "disable", optlen) == 0) { + *flagp = 0; + } + else if (strncmp(optstr, "help", optlen) == 0) { + verbose_usage(); + exit(NO_ERROR); + } + else { + char *endptr; + unsigned arg = strtoul(optstr, &endptr, 10); + if (optstr != endptr + && arg <= SHOW_MAX) { + *flagp |= arg; + fprintf(stderr, "Warning: numerical parameters for -v are no more supported in the next releases!\n"); + } + else { + fprintf(stderr, "unknown option %s\n", optstr); + verbose_usage(); + exit(SYNTAX_ERROR); + } + } + optstr = np; + } + return 1; +} + + +int +handle_paranoia_opts(char *optstr, long *flagp); + +int handle_paranoia_opts(char *optstr, long *flagp) +{ +#ifdef USE_PARANOIA + char *ep; + char *np; + int optlen; + + while (*optstr) { + if ((ep = strchr(optstr, ',')) != NULL) { + optlen = ep - optstr; + np = ep + 1; + } else { + optlen = strlen(optstr); + np = optstr + optlen; + } + if (strncmp(optstr, "retries=", min(8,optlen)) == 0) { + char *eqp = strchr(optstr, '='); + int rets; + + astoi(eqp+1, &rets); + if (rets >= 0) { + global.paranoia_parms.retries = rets; + } + } + else if (strncmp(optstr, "overlap=", min(8, optlen)) == 0) { + char *eqp = strchr(optstr, '='); + int rets; + + astoi(eqp+1, &rets); + if (rets >= 0) { + global.paranoia_parms.overlap = rets; + } + } + else if (strncmp(optstr, "minoverlap=", min(11, optlen)) == 0) { + char *eqp = strchr(optstr, '='); + int rets; + + astoi(eqp+1, &rets); + if (rets >= 0) { + global.paranoia_parms.mindynoverlap = rets; + } + } + else if (strncmp(optstr, "maxoverlap=", min(11, optlen)) == 0) { + char *eqp = strchr(optstr, '='); + int rets; + + astoi(eqp+1, &rets); + if (rets >= 0) { + global.paranoia_parms.maxdynoverlap = rets; + } + } + else if (strncmp(optstr, "no-verify", optlen) == 0) { + global.paranoia_parms.disable_extra_paranoia = 1; + } + else if (strncmp(optstr, "disable", optlen) == 0) { + global.paranoia_parms.disable_paranoia = 1; + } + else if (strncmp(optstr, "help", optlen) == 0) { + paranoia_usage(); + exit(NO_ERROR); + } + else { + fprintf(stderr, "unknown option %s\n", optstr); + paranoia_usage(); + exit(SYNTAX_ERROR); + } + optstr = np; + } + return 1; +#else + fputs("lib paranoia support is not configured!\n", stderr); + return 0; +#endif +} + + +/* and finally: the MAIN program */ +int main(int argc, char *argv[]) +{ + long lSector_p1; + long sector_offset = 0; + unsigned long endtrack = 1; + double rectime = DURATION; + int cd_index = -1; + double int_part; + int littleendian = -1; + char *int_name; + static char *user_sound_device = ""; + char * env_p; + int tracks_included; + int moreargs; + + int_name = DEF_INTERFACE; + audio_type = AUDIOTYPE; + save_args(argc, argv); + + /* init global variables */ + init_globals(); +{ + int am_i_cdda2wav; + /* When being invoked as list_audio_tracks, just dump a list of + audio tracks. */ + am_i_cdda2wav = !(strlen(argv[0]) >= sizeof("list_audio_tracks")-1 + && !strcmp(argv[0]+strlen(argv[0])+1-sizeof("list_audio_tracks"),"list_audio_tracks")); + if (!am_i_cdda2wav) global.verbose = SHOW_JUSTAUDIOTRACKS; +} + /* Control those set-id privileges... */ + initsecurity(); + + env_p = getenv("CDDA_DEVICE"); + if (env_p != NULL) { + global.dev_name = env_p; + } + + env_p = getenv("CDDBP_SERVER"); + if (env_p != NULL) { + global.cddbp_server = env_p; + } + + env_p = getenv("CDDBP_PORT"); + if (env_p != NULL) { + global.cddbp_port = env_p; + } + +{ + int cac; + char *const*cav; + + BOOL version = FALSE; + BOOL help = FALSE; + char *channels = NULL; + int irate = -1; + char *divider = NULL; + char *trackspec = NULL; + char *duration = NULL; + + char *oendianess = NULL; + char *cendianess = NULL; + int cddbp = -1; + BOOL stereo = FALSE; + BOOL mono = FALSE; + BOOL domax = FALSE; + BOOL dump_rates = FALSE; + int userverbose = -1; + long paraopts = 0; + + cac = argc; + cav = argv; + cac--; + cav++; + if (getargs(&cac, &cav, opts + , &global.paranoia_selected + , handle_paranoia_opts, ¶opts + , &version + , &help, &help + + , &global.no_file, &global.no_file + , &dump_rates, &dump_rates + , &bulk, &bulk, &bulk + , &global.scsi_verbose, &global.scsi_verbose + + , &global.findminmax, &global.findminmax + , &global.findmono, &global.findmono + , &global.no_infofile, &global.no_infofile + + , &global.deemphasize, &global.deemphasize + , &global.just_the_toc, &global.just_the_toc + , &global.scsi_silent, &global.scsi_silent + + , &global.cddbp_server, &global.cddbp_port + , &global.scanbus + , &global.scandevs + , &global.dev_name, &global.dev_name, &global.dev_name + , &global.aux_name, &global.aux_name + , &int_name, &int_name + , &audio_type, &audio_type + + , &oendianess, &oendianess + , &cendianess, &cendianess + , &global.userspeed, &global.userspeed + + , &global.playback_rate, &global.playback_rate + , &global.md5blocksize, &global.md5blocksize + , &global.useroverlap, &global.useroverlap + , &user_sound_device, &user_sound_device + + , &cddbp, &cddbp + , &channels, &channels + , &bits, &bits + , &irate, &irate + , &global.gui, &global.gui + + , ÷r, ÷r + , &trackspec, &trackspec + , &cd_index, &cd_index + , &duration, &duration + , §or_offset, §or_offset + + , &global.nsectors, &global.nsectors + , handle_verbose_opts, &userverbose + , handle_verbose_opts, &userverbose + , &global.buffers, &global.buffers + + , &stereo, &stereo + , &mono, &mono + , &waitforsignal, &waitforsignal + , &global.echo, &global.echo + , &global.quiet, &global.quiet + , &domax, &domax + + ) < 0) { + errmsgno(EX_BAD, "Bad Option: %s.\n", cav[0]); + fputs ("use 'icedax -help' to get more information.\n", stderr); + exit (SYNTAX_ERROR); + } + if (getfiles(&cac, &cav, opts) == 0) + /* No more file type arguments */; + moreargs = cav - argv; + if (version) { + fprintf(stderr, "icedax " CDRKIT_VERSION "\n"); + exit(EXIT_SUCCESS); + } + if (help) { + usage(); + } + if (!global.scanbus) + cdr_defaults(&global.dev_name, NULL, NULL, NULL); + if (dump_rates) { /* list available rates */ + int ii; + + fputs("\ +Available rates are:\n\ +Rate Divider Rate Divider Rate Divider Rate Divider\n\ +" , stderr ); + for (ii = 1; ii <= 44100 / 880 / 2; ii++) { + long i2 = ii; + fprintf(stderr, "%7.1f %2ld %7.1f %2ld.5 ", + 44100.0/i2, i2, 44100.0/(i2+0.5), i2); + i2 += 25; + fprintf(stderr, "%7.1f %2ld %7.1f %2ld.5\n", + 44100.0/i2, i2, 44100.0/(i2+0.5), i2); + i2 -= 25; + } + exit(NO_ERROR); + } + if (channels) { + if (*channels == 's') { + global.channels = 2; + global.swapchannels = 1; + } else { + global.channels = strtol(channels, NULL, 10); + } + } + if (irate >= 0) { + rate = irate; + } + if (divider) { + double divider_d; + divider_d = strtod(divider , NULL); + if (divider_d > 0.0) { + rate = 44100.0 / divider_d; + } else { + fputs("E option -divider requires a nonzero, positive argument.\nSee -dump-rates.", stderr); + exit(SYNTAX_ERROR); + } + } + if (trackspec) { + char * endptr; + char * endptr2; + track = strtoul(trackspec, &endptr, 10 ); + endtrack = strtoul(endptr, &endptr2, 10 ); + if (endptr2 == endptr) { + endtrack = track; + } else if (track == endtrack) { + bulk = -1; + } + } + if (duration) { + char *end_ptr = NULL; + rectime = strtod(duration, &end_ptr ); + if (*end_ptr == 'f') { + rectime = rectime / 75.0; + /* TODO: add an absolute end of recording. */ +#if 0 + } else if (*end_ptr == 'F') { + rectime = rectime / 75.0; +#endif + } else if (*end_ptr != '\0') { + rectime = -1.0; + } + } + if (oendianess) { + if (strcasecmp(oendianess, "little") == 0) { + global.outputendianess = LITTLE; + } else if (strcasecmp(oendianess, "big") == 0) { + global.outputendianess = BIG; + } else { + usage2("wrong parameter '%s' for option -E", oendianess); + } + } + if (cendianess) { + if (strcasecmp(cendianess, "little") == 0) { + littleendian = 1; + } else if (strcasecmp(cendianess, "big") == 0) { + littleendian = 0; + } else if (strcasecmp(cendianess, "guess") == 0) { + littleendian = -2; + } else { + usage2("wrong parameter '%s' for option -C", cendianess); + } + } + if (cddbp >= 0) { + global.cddbp = 1 + cddbp; + } + if (stereo) { + global.channels = 2; + } + if (mono) { + global.channels = 1; + global.need_hostorder = 1; + } + if (global.echo) { +#ifdef ECHO_TO_SOUNDCARD + if (global.playback_rate != 100) { + RestrictPlaybackRate( global.playback_rate ); + } + global.need_hostorder = 1; +#else + fprintf(stderr, "There is no sound support compiled into %s.\n",argv[0]); + global.echo = 0; +#endif + } + if (global.quiet) { + global.verbose = 0; + } + if (domax) { + global.channels = 2; bits = 16; rate = 44100; + } + if (global.findminmax) { + global.need_hostorder = 1; + } + if (global.deemphasize) { + global.need_hostorder = 1; + } + if (global.just_the_toc) { + global.verbose = SHOW_MAX; + bulk = 1; + } + if (global.gui) { +#ifdef Thomas_will_es + global.no_file = 1; + global.no_infofile = 1; + global.verbose = SHOW_MAX; +#endif + global.no_cddbfile = 1; + } + if (global.no_file) { + global.no_infofile = 1; + global.no_cddbfile = 1; + } + if (global.no_infofile) { + global.no_cddbfile = 1; + } + if (global.md5blocksize) { +#ifdef MD5_SIGNATURES + fputs("MD5 signatures are currently broken! Sorry\n", stderr); +#else + fputs("MD5 signatures are currently broken! Sorry\n", stderr); +#endif + } + if (user_sound_device) { +#ifndef ECHO_TO_SOUNDCARD + fputs("There is no sound support configured!\n", stderr); +#endif + } + if (global.paranoia_selected) { + global.useroverlap = 0; + } + if (userverbose >= 0) { + global.verbose = userverbose; + } +} + + /* check all parameters */ + if (global.buffers < 1) { + usage2("Incorrect buffer setting: %d", global.buffers); + } + + if (global.nsectors < 1) { + usage2("Incorrect nsectors setting: %d", global.nsectors); + } + + if (global.verbose < 0 || global.verbose > SHOW_MAX) { + usage2("Incorrect verbose level setting: %d",global.verbose); + } + if (global.verbose == 0) global.quiet = 1; + + if ( rectime < 0.0 ) { + usage2("Incorrect recording time setting: %d.%02d", + (int)rectime, (int)(rectime*100+0.5) % 100); + } + + if ( global.channels != 1 && global.channels != 2 ) { + usage2("Incorrect channel setting: %d",global.channels); + } + + if ( bits != 8 && bits != 12 && bits != 16 ) { + usage2("Incorrect bits_per_sample setting: %d",bits); + } + + if ( rate < 827.0 || rate > 44100.0 ) { + usage2("Incorrect sample rate setting: %d.%02d", + (int)rate, ((int)rate*100) % 100); + } + + int_part = (double)(long) (2*44100.0 / rate); + + if (2*44100.0 / rate - int_part >= 0.5 ) { + int_part += 1.0; + fprintf( stderr, "Nearest available sample rate is %d.%02d Hertz\n", + 2*44100 / (int)int_part, + (2*4410000 / (int)int_part) % 100); + } + Halved = ((int) int_part) & 1; + rate = 2*44100.0 / int_part; + undersampling = (int) int_part / 2.0; + samples_to_do = undersampling; + + if (!strcmp((char *)int_name,"generic_scsi")) + interface = GENERIC_SCSI; + else if (!strcmp((char *)int_name,"cooked_ioctl")) + interface = COOKED_IOCTL; + else { + usage2("Incorrect interface setting: %s",int_name); + } + + /* check * init audio file */ + if (!strncmp(audio_type,"wav",3)) { + global.audio_out = &wavsound; + } else if (!strncmp(audio_type, "sun", 3) || !strncmp(audio_type, "au", 2)) { + /* Enhanced compatibility */ + audio_type = "au"; + global.audio_out = &sunsound; + } else if (!strncmp(audio_type, "cdr", 3) || + !strncmp(audio_type, "raw", 3)) { + global.audio_out = &rawsound; + } else if (!strncmp(audio_type, "aiff", 4)) { + global.audio_out = &aiffsound; + } else if (!strncmp(audio_type, "aifc", 4)) { + global.audio_out = &aifcsound; +#ifdef USE_LAME + } else if (!strncmp(audio_type, "mp3", 3)) { + global.audio_out = &mp3sound; + if (!global.quiet) { + unsigned char Lame_version[20]; + + fetch_lame_version(Lame_version); + fprintf(stderr, "Using LAME version %s.\n", Lame_version); + } + if (bits < 9) { + bits = 16; + fprintf(stderr, "Warning: sample size forced to 16 bit for MP3 format.\n"); + } +#endif /* USE_LAME */ + } else { + usage2("Incorrect audio type setting: %3s", audio_type); + } + + if (bulk == -1) bulk = 0; + + global.need_big_endian = global.audio_out->need_big_endian; + if (global.outputendianess != NONE) + global.need_big_endian = global.outputendianess == BIG; + + if (global.no_file) global.fname_base[0] = '\0'; + + if (!bulk) { + strcat(global.fname_base, "."); + strcat(global.fname_base, audio_type); + } + + /* If we need to calculate with samples or write them to a soundcard, + * we need a conversion to host byte order. + */ + if (global.channels != 2 + || bits != 16 + || rate != 44100) + global.need_hostorder = 1; + + /* Bad hack!! + * Remove for release 2.0 + * this is a bug compatibility feature. + */ + if (global.gui && global.verbose == SHOW_TOC) + global.verbose |= SHOW_STARTPOSITIONS | SHOW_SUMMARY | SHOW_TITLES; + + /* + * all options processed. + * Now a file name per track may follow + */ + argc2 = argc3 = argc - moreargs; + argv2 = argv + moreargs; + if ( moreargs < argc ) { + if (!strcmp(argv[moreargs],"-")) { +#ifdef NEED_O_BINARY + setmode(fileno(stdout), O_BINARY); +#endif + global.audio = dup (fileno(stdout)); + strncpy( global.fname_base, "standard_output", sizeof(global.fname_base) ); + global.fname_base[sizeof(global.fname_base)-1]=0; + } else if (!is_fifo(argv[moreargs])) { + /* we do have at least one argument */ + global.multiname = 1; + } + } + +#define SETSIGHAND(PROC, SIG, SIGNAME) if (signal(SIG, PROC) == SIG_ERR) \ + { fprintf(stderr, "cannot set signal %s handler\n", SIGNAME); exit(SETSIG_ERROR); } + SETSIGHAND(exit_wrapper, SIGINT, "SIGINT") + SETSIGHAND(exit_wrapper, SIGQUIT, "SIGQUIT") + SETSIGHAND(exit_wrapper, SIGTERM, "SIGTERM") + SETSIGHAND(exit_wrapper, SIGHUP, "SIGHUP") + + SETSIGHAND(set_nonforked, SIGPIPE, "SIGPIPE") + + /* setup interface and open cdrom device */ + /* request sychronization facilities and shared memory */ + SetupInterface( ); + + /* use global.useroverlap to set our overlap */ + if (global.useroverlap != -1) + global.overlap = global.useroverlap; + + /* check for more valid option combinations */ + + if (global.nsectors < 1+global.overlap) { + fprintf( stderr, "Warning: Setting #nsectors to minimum of %d, due to jitter correction!\n", global.overlap+1); + global.nsectors = global.overlap+1; + } + + if (global.overlap > 0 && global.buffers < 2) { + fprintf( stderr, "Warning: Setting #buffers to minimum of 2, due to jitter correction!\n"); + global.buffers = 2; + } + + /* Value of 'nsectors' must be defined here */ + + global.shmsize = 0; +#ifdef USE_PARANOIA + while (global.shmsize < sizeof (struct paranoia_statistics)) + global.shmsize += global.pagesize; +#endif + global.shmsize += 10*global.pagesize; /* XXX Der Speicherfehler ist nicht in libparanoia sondern in cdda2wav :-( */ + global.shmsize += HEADER_SIZE + ENTRY_SIZE_PAGE_AL * global.buffers; + +#if defined (HAVE_FORK_AND_SHAREDMEM) + /* + * The (void *) cast is to avoid a GCC warning like: + * warning: dereferencing type-punned pointer will break strict-aliasing rules + * which does not apply to this code. (void *) introduces a compatible + * intermediate type in the cast list. + */ + he_fill_buffer = request_shm_sem(global.shmsize, (unsigned char **)(void *)&he_fill_buffer); + if (he_fill_buffer == NULL) { + fprintf( stderr, "no shared memory available!\n"); + exit(SHMMEM_ERROR); + } +#else /* do not have fork() and shared memory */ + he_fill_buffer = malloc(global.shmsize); + if (he_fill_buffer == NULL) { + fprintf( stderr, "no buffer memory available!\n"); + exit(NOMEM_ERROR); + } +#endif +#ifdef USE_PARANOIA + { + int i = 0; + + para_stat = (struct paranoia_statistics *)he_fill_buffer; + while (i < sizeof (struct paranoia_statistics)) { + i += global.pagesize; + he_fill_buffer += global.pagesize; + global.shmsize -= global.pagesize; + } + } +#endif + + if (global.verbose != 0) + fprintf(stderr, + "%u bytes buffer memory requested, %d buffers, %d sectors\n", + global.shmsize, global.buffers, global.nsectors); + + /* initialize pointers into shared memory segment */ + last_buffer = he_fill_buffer + 1; + total_segments_read = (unsigned long *) (last_buffer + 1); + total_segments_written = total_segments_read + 1; + child_waits = (int *) (total_segments_written + 1); + parent_waits = child_waits + 1; + in_lendian = parent_waits + 1; + eorecording = in_lendian + 1; + *total_segments_read = *total_segments_written = 0; + nSamplesToDo = (unsigned long *)(eorecording + 1); + *eorecording = 0; + *in_lendian = global.in_lendian; + + set_total_buffers(global.buffers, sem_id); + + +#if defined(HAVE_SEMGET) && defined(USE_SEMAPHORES) + atexit ( free_sem ); +#endif + + /* + * set input endian default + */ + if (littleendian != -1) + *in_lendian = littleendian; + + /* get table of contents */ + cdtracks = ReadToc(); + if (cdtracks == 0) { + fprintf(stderr, "No track in table of contents! Aborting...\n"); + exit(MEDIA_ERROR); + } + + calc_cddb_id(); + calc_cdindex_id(); + +#if 1 + Check_Toc(); +#endif + + if (ReadTocText != NULL && FirstAudioTrack () != -1) { + ReadTocText(get_scsi_p()); + handle_cdtext(); + } + if ( global.verbose == SHOW_JUSTAUDIOTRACKS ) { + unsigned int z; + + for (z = 0; z < cdtracks; z++) + if (Get_Datatrack(z) == 0) + printf("%02d\t%06ld\n", Get_Tracknumber(z), Get_AudioStartSector(z)); + exit(NO_ERROR); + } + + if ( global.verbose != 0 ) { + fputs( "#icedax version ", stderr ); + fputs( VERSION, stderr ); +#if defined USE_POSIX_PRIORITY_SCHEDULING || defined HAVE_SYS_PRIOCNTL_H + fputs( ", real time sched.", stderr ); +#endif +#if defined ECHO_TO_SOUNDCARD + fputs( ", soundcard", stderr ); +#endif +#if defined USE_PARANOIA + fputs( ", libparanoia", stderr ); +#endif + fputs( " support\n", stderr ); + } + + FixupTOC(cdtracks + 1); + +#if 0 + if (!global.paranoia_selected) { + fprintf(stderr, "NICE\n"); + /* try to get some extra kicks */ + priv_on(); + needroot(0); +#if defined HAVE_SETPRIORITY + setpriority(PRIO_PROCESS, 0, -20); +#else +# if defined(HAVE_NICE) && (HAVE_NICE == 1) + nice(-20); +# endif +#endif + priv_off(); + dontneedroot(); + } +#endif + + /* switch cdrom to audio mode */ + EnableCdda (get_scsi_p(), 1, CD_FRAMESIZE_RAW); + + atexit ( CloseAll ); + + DisplayToc(); + if ( FirstAudioTrack () == -1 ) { + if (no_disguised_audiotracks()) { + FatalError ( "This disk has no audio tracks\n" ); + } + } + + Read_MCN_ISRC(); + + /* check if start track is in range */ + if ( track < 1 || track > cdtracks ) { + usage2("Incorrect start track setting: %d",track); + } + + /* check if end track is in range */ + if ( endtrack < track || endtrack > cdtracks ) { + usage2("Incorrect end track setting: %ld",endtrack); + } + + do { + lSector = Get_AudioStartSector ( track ); + lSector_p1 = Get_EndSector ( track ) + 1; + + if ( lSector < 0 ) { + if ( bulk == 0 ) { + FatalError ( "Track %d not found\n", track ); + } else { + fprintf(stderr, "Skipping data track %d...\n", track); + if (endtrack == track) endtrack++; + track++; + } + } + } while (bulk != 0 && track <= cdtracks && lSector < 0); + + if ((global.illleadout_cd == 0 || global.reads_illleadout != 0) && cd_index != -1) { + if (global.verbose && !global.quiet) { + global.verbose |= SHOW_INDICES; + } + sector_offset += ScanIndices( track, cd_index, bulk ); + } else { + cd_index = 1; + if (global.deemphasize || (global.verbose & SHOW_INDICES)) { + ScanIndices( track, cd_index, bulk ); + } + } + + lSector += sector_offset; + /* check against end sector of track */ + if ( lSector >= lSector_p1 ) { + fprintf(stderr, "W Sector offset %lu exceeds track size (ignored)\n", sector_offset ); + lSector -= sector_offset; + } + + if ( lSector < 0L ) { + fputs( "Negative start sector! Set to zero.\n", stderr ); + lSector = 0L; + } + + lSector_p2 = Get_LastSectorOnCd( track ); + if (bulk == 1 && track == endtrack && rectime == 0.0) + rectime = 99999.0; + if ( rectime == 0.0 ) { + /* set time to track time */ + *nSamplesToDo = (lSector_p1 - lSector) * CD_FRAMESAMPLES; + rectime = (lSector_p1 - lSector) / 75.0; + if (CheckTrackrange( track, endtrack) == 1) { + lSector_p2 = Get_EndSector ( endtrack ) + 1; + + if (lSector_p2 >= 0) { + rectime = (lSector_p2 - lSector) / 75.0; + *nSamplesToDo = (long)(rectime*44100.0 + 0.5); + } else { + fputs( "End track is no valid audio track (ignored)\n", stderr ); + } + } else { + fputs( "Track range does not consist of audio tracks only (ignored)\n", stderr ); + } + } else { + /* Prepare the maximum recording duration. + * It is defined as the biggest amount of + * adjacent audio sectors beginning with the + * specified track/index/offset. */ + + if ( rectime > (lSector_p2 - lSector) / 75.0 ) { + rectime = (lSector_p2 - lSector) / 75.0; + lSector_p1 = lSector_p2; + } + + /* calculate # of samples to read */ + *nSamplesToDo = (long)(rectime*44100.0 + 0.5); + } + + global.OutSampleSize = (1+bits/12); + if (*nSamplesToDo/undersampling == 0L) { + usage2("Time interval is too short. Choose a duration greater than %d.%02d secs!", + undersampling/44100, (int)(undersampling/44100) % 100); + } + if ( moreargs < argc ) { + if (!strcmp(argv[moreargs],"-") || is_fifo(argv[moreargs])) { + /* + * pipe mode + */ + if (bulk == 1) { + fprintf(stderr, "W Bulk mode is disabled while outputting to a %spipe\n", + is_fifo(argv[moreargs]) ? "named " : ""); + bulk = 0; + } + global.no_cddbfile = 1; + } + } + if (global.no_infofile == 0) { + global.no_infofile = 1; + if (global.channels == 1 || bits != 16 || rate != 44100) { + fprintf(stderr, "W Sample conversions disable generation of info files!\n"); + } else if (waitforsignal == 1) { + fprintf(stderr, "W Option -w 'wait for signal' disables generation of info files!\n"); + } else if (sector_offset != 0) { + fprintf(stderr, "W Using an start offset (option -o) disables generation of info files!\n"); + } else if (!bulk && + !((lSector == Get_AudioStartSector(track)) && + ((long)(lSector + rectime*75.0 + 0.5) == Get_EndSector(track) + 1))) { + fprintf(stderr, "W Duration is not set for complete tracks (option -d), this disables generation\n of info files!\n"); + } else { + global.no_infofile = 0; + } + } + + SamplesToWrite = *nSamplesToDo*2/(int)int_part; + + { + int first = FirstAudioTrack(); + tracks_included = Get_Track( + (unsigned) (lSector + *nSamplesToDo/CD_FRAMESAMPLES -1)) + - max((int)track,first) +1; + } + + if (global.multiname != 0 && moreargs + tracks_included > argc) { + global.multiname = 0; + } + + if ( !waitforsignal ) { + +#ifdef INFOFILES + if (!global.no_infofile) { + int i; + + for (i = track; i < (int)track + tracks_included; i++) { + unsigned minsec, maxsec; + char *tmp_fname; + + /* build next filename */ + + tmp_fname = get_next_name(); + if (tmp_fname != NULL) + strncpy( global.fname_base, tmp_fname, sizeof(global.fname_base)-8 ); + global.fname_base[sizeof(global.fname_base)-1]=0; + minsec = max(lSector, Get_AudioStartSector(i)); + maxsec = min(lSector + rectime*75.0 + 0.5, 1+Get_EndSector(i)); + if ((int)minsec == Get_AudioStartSector(i) && + (int)maxsec == 1+Get_EndSector(i)) { + write_info_file(global.fname_base,i,(maxsec-minsec)*CD_FRAMESAMPLES, bulk && global.multiname == 0); + } else { + fprintf(stderr, + "Partial length copy for track %d, no info file will be generated for this track!\n", i); + } + if (!bulk) break; + } + reset_name_iterator(); + } +#endif + + } + + if (global.just_the_toc) exit(NO_ERROR); + +#ifdef ECHO_TO_SOUNDCARD + if (user_sound_device[0] != '\0') { + set_snd_device(user_sound_device); + } + init_soundcard(rate, bits); +#endif /* ECHO_TO_SOUNDCARD */ + + if (global.userspeed > -1) + global.speed = global.userspeed; + + if (global.speed != 0 && SelectSpeed != NULL) { + SelectSpeed(get_scsi_p(), global.speed); + } + + current_track = track; + + if ( !global.no_file ) { + { + char *myfname; + + myfname = get_next_name(); + + if (myfname != NULL) { + strncpy( global.fname_base, myfname, sizeof(global.fname_base)-8 ); + global.fname_base[sizeof(global.fname_base)-1]=0; + } + } + + /* strip audio_type extension */ + { + char *cp = global.fname_base; + + cp = strrchr(cp, '.'); + if (cp == NULL) { + cp = global.fname_base + strlen(global.fname_base); + } + *cp = '\0'; + } + if (bulk && global.multiname == 0) { + sprintf(fname, "%s_%02u.%s",global.fname_base,current_track,audio_type); + } else { + sprintf(fname, "%s.%s",global.fname_base,audio_type); + } + + OpenAudio( fname, rate, bits, global.channels, + (unsigned)(SamplesToWrite*global.OutSampleSize*global.channels), + global.audio_out); + } + + global.Remainder = (75 % global.nsectors)+1; + + global.sh_bits = 16 - bits; /* shift counter */ + + global.iloop = *nSamplesToDo; + if (Halved && (global.iloop&1)) + global.iloop += 2; + + BeginAtSample = lSector * CD_FRAMESAMPLES; + +#if 1 + if ( (global.verbose & SHOW_SUMMARY) && !global.just_the_toc && + (global.reads_illleadout == 0 || + lSector+*nSamplesToDo/CD_FRAMESAMPLES + <= (unsigned) Get_AudioStartSector(cdtracks-1))) { + + fprintf(stderr, "samplefile size will be %lu bytes.\n", + global.audio_out->GetHdrSize() + + global.audio_out->InSizeToOutSize(SamplesToWrite*global.OutSampleSize*global.channels) ); + fprintf (stderr, "recording %d.%04d seconds %s with %d bits @ %5d.%01d Hz" + ,(int)rectime , (int)(rectime * 10000) % 10000, + global.channels == 1 ? "mono":"stereo", bits, (int)rate, (int)(rate*10)%10); + if (!global.no_file && *global.fname_base) + fprintf(stderr, " ->'%s'...", global.fname_base ); + fputs("\n", stderr); + } +#endif + +#if defined(HAVE_SEMGET) && defined(USE_SEMAPHORES) +#else + init_pipes(); +#endif + +#ifdef USE_PARANOIA + if (global.paranoia_selected) { + long paranoia_mode; + + global.cdp = paranoia_init(get_scsi_p(), global.nsectors); + + if (global.paranoia_parms.overlap >= 0) { + int overlap = global.paranoia_parms.overlap; + + if (overlap > global.nsectors - 1) + overlap = global.nsectors - 1; + paranoia_overlapset(global.cdp, overlap); + } + /* + * Default to a minimum of dynamic overlapping == 0.5 sectors. + * If we don't do this, we get the default from libparanoia + * which is approx. 0.1. + */ + if (global.paranoia_parms.mindynoverlap < 0) + paranoia_dynoverlapset(global.cdp, CD_FRAMEWORDS/2, -1); + paranoia_dynoverlapset(global.cdp, + global.paranoia_parms.mindynoverlap * CD_FRAMEWORDS, + global.paranoia_parms.maxdynoverlap * CD_FRAMEWORDS); + + paranoia_mode = PARANOIA_MODE_FULL ^ PARANOIA_MODE_NEVERSKIP; + + if (global.paranoia_parms.disable_paranoia) { + paranoia_mode = PARANOIA_MODE_DISABLE; + } + if (global.paranoia_parms.disable_extra_paranoia) { + paranoia_mode |= PARANOIA_MODE_OVERLAP; + paranoia_mode &= ~PARANOIA_MODE_VERIFY; + } + /* not yet implemented */ + if (global.paranoia_parms.disable_scratch_detect) { + paranoia_mode &= ~(PARANOIA_MODE_SCRATCH|PARANOIA_MODE_REPAIR); + } + /* not yet implemented */ + if (global.paranoia_parms.disable_scratch_repair) { + paranoia_mode &= ~PARANOIA_MODE_REPAIR; + } + + paranoia_modeset(global.cdp, paranoia_mode); + if (global.verbose) + fprintf(stderr, "using lib paranoia for reading.\n"); + paranoia_seek(global.cdp, lSector, SEEK_SET); + paranoia_reset(); + } +#endif +#if defined(HAVE_FORK_AND_SHAREDMEM) + + /* Everything is set up. Now fork and let one process read cdda sectors + and let the other one store them in a wav file */ + + /* forking */ + child_pid = fork(); + if (child_pid > 0 && global.gui > 0 && global.verbose > 0) + fprintf( stderr, "child pid is %d\n", child_pid); + + /*********************** fork **************************************/ + if (child_pid == 0) { + /* child WRITER section */ + +#ifdef HAVE_AREAS + /* Under BeOS a fork() with shared memory does not work as + * it does under System V Rel. 4. The mapping of the child + * works with copy on write semantics, so changes do not propagate + * back and forth. The existing mapping has to be deleted + * and replaced by an clone without copy on write semantics. + * This is done with clone_area(...,B_CLONE_ADDRESS,...). + * Thanks to file support.c from the postgreSQL project. + */ + area_info inf; + int32 cook = 0; + /* iterate over all mappings to find our shared memory mapping. */ + while (get_next_area_info(0, &cook, &inf) == B_OK) + { + /* check the name of the mapping. */ + if (!strcmp(inf.name, AREA_NAME)) + { + void *area_address; + area_id area_parent; + + /* kill the cow mapping. */ + area_address = inf.address; + if (B_OK != delete_area(inf.area)) + { + fprintf(stderr, "delete_area: no valid area.\n"); + exit(SHMMEM_ERROR); + } + /* get the parent mapping. */ + area_parent = find_area(inf.name); + if (area_parent == B_NAME_NOT_FOUND) + { + fprintf(stderr, "find_area: no such area name.\n"); + exit(SHMMEM_ERROR); + } + /* clone the parent mapping without cow. */ + if (B_OK > clone_area("shm_child", &area_address, B_CLONE_ADDRESS, + B_READ_AREA | B_WRITE_AREA, area_parent)) + { + fprintf(stderr,"clone_area failed\n"); + exit(SHMMEM_ERROR); + } + } + } +#endif +#ifdef __EMX__ + if (DosGetSharedMem(he_fill_buffer, 3)) { + comerr("DosGetSharedMem() failed.\n"); + } +#endif + global.have_forked = 1; + forked_write(); +#ifdef __EMX__ + DosFreeMem(he_fill_buffer); + _exit(NO_ERROR); + /* NOTREACHED */ +#endif + exit_wrapper(NO_ERROR); + /* NOTREACHED */ + } else if (child_pid > 0) { + /* parent READER section */ + + global.have_forked = 1; + switch_to_realtime_priority(); + + forked_read(); +#ifdef HAVE_AREAS + { + area_id aid; + aid = find_area(AREA_NAME); + if (aid < B_OK) { + comerrno(aid, "find_area() failed.\n"); + } + delete_area(aid); + } +#endif +#ifdef __EMX__ + DosFreeMem(he_fill_buffer); +#endif + exit_wrapper(NO_ERROR); + /* NOTREACHED */ + } else + perror("fork error."); + +#endif + /* version without fork */ + { + global.have_forked = 0; +#if 0 + if (!global.paranoia_selected) { + fprintf(stderr, "REAL\n"); + switch_to_realtime_priority(); + } +#endif + fprintf(stderr, "a nonforking version is running...\n"); + nonforked_loop(); + exit_wrapper(NO_ERROR); + /* NOTREACHED */ + } +#ifdef USE_PARANOIA + if (global.paranoia_selected) + paranoia_free(global.cdp); +#endif + + return 0; +} diff --git a/icedax/icedax.h b/icedax/icedax.h index 9795aac..78345dd 100644 --- a/icedax/icedax.h +++ b/icedax/icedax.h @@ -41,7 +41,7 @@ /* Endianess */ #define GUESS (-2) -#define NONE (-1) +#define NONE_EN (-1) #define LITTLE 0 #define BIG 1 diff --git a/icedax/icedax.h.paranoiacdda b/icedax/icedax.h.paranoiacdda new file mode 100644 index 0000000..9795aac --- /dev/null +++ b/icedax/icedax.h.paranoiacdda @@ -0,0 +1,150 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)icedax.h 1.4 00/02/13 Copyright 1998,1999,2000 Heiko Eissfeldt */ +/*** + * CopyPolicy: GNU Public License 2 applies + * Copyright (C) by Heiko Eissfeldt + * + * prototypes from cdda2wav.c + */ + +#ifdef max +#undef max +#endif +#ifdef min +#undef min +#endif +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define min(a,b) ((a) < (b) ? (a) : (b)) + + +/* verbose levels */ +#define SHOW_TOC 1 +#define SHOW_SUMMARY 2 +#define SHOW_INDICES 4 +#define SHOW_MCN 8 +#define SHOW_ISRC 16 +#define SHOW_STARTPOSITIONS 32 +#define SHOW_TITLES 64 +#define SHOW_JUSTAUDIOTRACKS 128 +#define SHOW_MAX 255 + +/* Endianess */ +#define GUESS (-2) +#define NONE (-1) +#define LITTLE 0 +#define BIG 1 + +void FatalError(const char *szMessage, ...); +void AnalyzeQchannel(unsigned frame); +long SamplesNeeded(long amount, long undersampling); +unsigned int get_current_track(void); + +#if defined (sun) && !defined(SVR4) +#define atexit(f) on_exit(f, 0) +#endif + +#ifndef _LINUX_CDROM_H +#define _LINUX_CDROM_H + +/* + * some fix numbers + */ +#define CD_MINS 74 /* max. minutes per CD, not really a limit */ +#define CD_SECS 60 /* seconds per minute */ +#define CD_FRAMES 75 /* frames per second */ + +#define CD_SYNC_SIZE 12 /* 12 sync bytes per raw data frame, not transfered by the drive */ +#define CD_HEAD_SIZE 4 /* header (address) bytes per raw data frame */ +#define CD_SUBHEAD_SIZE 8 /* subheader bytes per raw XA data frame */ +#define CD_XA_HEAD (CD_HEAD_SIZE+CD_SUBHEAD_SIZE) /* "before data" part of raw XA frame */ +#define CD_XA_SYNC_HEAD (CD_SYNC_SIZE+CD_XA_HEAD)/* sync bytes + header of XA frame */ + +#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */ +#define CD_FRAMESIZE_RAW 2352 /* bytes per frame, "raw" mode */ +/* most drives don't deliver everything: */ +#define CD_FRAMESIZE_RAW1 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE) /* 2340 */ +#define CD_FRAMESIZE_RAW0 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE-CD_HEAD_SIZE) /* 2336 */ +/* Optics drive also has a 'read all' mode: */ +#define CD_FRAMESIZE_RAWER 2646 /* bytes per frame */ + +#define CD_EDC_SIZE 4 /* bytes EDC per most raw data frame types */ +#define CD_ZERO_SIZE 8 /* bytes zero per yellow book mode 1 frame */ +#define CD_ECC_SIZE 276 /* bytes ECC per most raw data frame types */ +#define CD_XA_TAIL (CD_EDC_SIZE+CD_ECC_SIZE) /* "after data" part of raw XA frame */ + +#define CD_FRAMESIZE_SUB 96 /* subchannel data "frame" size */ +#define CD_MSF_OFFSET 150 /* MSF numbering offset of first frame */ + +#define CD_CHUNK_SIZE 24 /* lowest-level "data bytes piece" */ +#define CD_NUM_OF_CHUNKS 98 /* chunks per frame */ + +#define CD_FRAMESIZE_XA CD_FRAMESIZE_RAW1 /* obsolete name */ +#define CD_BLOCK_OFFSET CD_MSF_OFFSET /* obsolete name */ + +/* + * the raw frame layout: + * + * - 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 - EDC | + * | 12 - 4 - 8 - 2324 - 4 | + */ + + +/* + * CD-ROM address types (cdrom_tocentry.cdte_format) + */ +#if !defined CDROM_LBA +#define CDROM_LBA 0x01 /* "logical block": first frame is #0 */ +#define CDROM_MSF 0x02 /* "minute-second-frame": binary, not bcd here! */ +#endif +/* + * bit to tell whether track is data or audio (cdrom_tocentry.cdte_ctrl) + */ +#define CDROM_DATA_TRACK 0x04 + +/* + * The leadout track is always 0xAA, regardless of # of tracks on disc + */ +#define CDROM_LEADOUT 0xAA + +/* + * audio states (from SCSI-2, but seen with other drives, too) + */ +#define CDROM_AUDIO_INVALID 0x00 /* audio status not supported */ +#define CDROM_AUDIO_PLAY 0x11 /* audio play operation in progress */ +#define CDROM_AUDIO_PAUSED 0x12 /* audio play operation paused */ +#define CDROM_AUDIO_COMPLETED 0x13 /* audio play successfully completed */ +#define CDROM_AUDIO_ERROR 0x14 /* audio play stopped due to error */ +#define CDROM_AUDIO_NO_STATUS 0x15 /* no current audio status to return */ + +#ifdef FIVETWELVE +#define CDROM_MODE1_SIZE 512 +#else +#define CDROM_MODE1_SIZE 2048 +#endif /*FIVETWELVE*/ +#define CDROM_MODE2_SIZE 2336 + +#endif + diff --git a/icedax/interface.c b/icedax/interface.c index ef7278a..886d574 100644 --- a/icedax/interface.c +++ b/icedax/interface.c @@ -116,7 +116,7 @@ int (*Play_at)(SCSI *usalp, unsigned int from_sector, unsigned int sectors); int (*StopPlay)(SCSI *usalp); void (*trash_cache)(UINT4 *p, unsigned lSector, unsigned SectorBurstVal); -#if defined USE_PARANOIA +#if 0 //defined USE_PARANOIA long cdda_read(void *d, void *buffer, long beginsector, long sectors); long cdda_read(void *d, void *buffer, long beginsector, long sectors) diff --git a/icedax/interface.c.paranoiacdda b/icedax/interface.c.paranoiacdda new file mode 100644 index 0000000..ef7278a --- /dev/null +++ b/icedax/interface.c.paranoiacdda @@ -0,0 +1,1050 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)interface.c 1.40 06/02/19 Copyright 1998-2002 Heiko Eissfeldt, Copyright 2006 J. Schilling */ +/*** + * CopyPolicy: GNU Public License 2 applies + * Copyright (C) 1994-1997 Heiko Eissfeldt heiko@colossus.escape.de + * + * Interface module for cdrom drive access + * + * Two interfaces are possible. + * + * 1. using 'cooked' ioctls() (Linux only) + * : available for atapi, sbpcd and cdu31a drives only. + * + * 2. using the generic scsi device (for details see SCSI Prog. HOWTO). + * NOTE: a bug/misfeature in the kernel requires blocking signal + * SIGINT during SCSI command handling. Once this flaw has + * been removed, the sigprocmask SIG_BLOCK and SIG_UNBLOCK calls + * should removed, thus saving context switches. + * + * For testing purposes I have added a third simulation interface. + * + * Version 0.8: used experiences of Jochen Karrer. + * SparcLinux port fixes + * AlphaLinux port fixes + * + */ +#if 0 +#define SIM_CD +#endif + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#include "mycdrom.h" +#include "lowlevel.h" +/* some include file locations have changed with newer kernels */ +#if defined (__linux__) +# if LINUX_VERSION_CODE > 0x10300 + 97 +# if LINUX_VERSION_CODE < 0x200ff +# include +# include +# endif +# if !defined(CDROM_SELECT_SPEED) +# include +# endif +# endif +#endif + +#include + +#include "mytype.h" +#include "byteorder.h" +#include "interface.h" +#include "icedax.h" +#include "semshm.h" +#include "setuid.h" +#include "ringbuff.h" +#include "toc.h" +#include "global.h" +#include "ioctl.h" +#include "exitcodes.h" +#include "scsi_cmds.h" + +#include +#include +#include "scsi_scan.h" + +unsigned interface; + +int trackindex_disp = 0; + +void priv_init(void); +void priv_on(void); +void priv_off(void); + +void (*EnableCdda)(SCSI *, int Switch, unsigned uSectorsize); +unsigned (*doReadToc)(SCSI *usalp); +void (*ReadTocText)(SCSI *usalp); +unsigned (*ReadLastAudio)(SCSI *usalp); +int (*ReadCdRom)(SCSI *usalp, UINT4 *p, unsigned lSector, + unsigned SectorBurstVal); +int (*ReadCdRomData)(SCSI *usalp, unsigned char *p, unsigned lSector, + unsigned SectorBurstVal); +int (*ReadCdRomSub)(SCSI *usalp, UINT4 *p, unsigned lSector, + unsigned SectorBurstVal); +subq_chnl *(*ReadSubChannels)(SCSI *usalp, unsigned lSector); +subq_chnl *(*ReadSubQ)(SCSI *usalp, unsigned char sq_format, + unsigned char track); +void (*SelectSpeed)(SCSI *usalp, unsigned speed); +int (*Play_at)(SCSI *usalp, unsigned int from_sector, unsigned int sectors); +int (*StopPlay)(SCSI *usalp); +void (*trash_cache)(UINT4 *p, unsigned lSector, unsigned SectorBurstVal); + +#if defined USE_PARANOIA +long cdda_read(void *d, void *buffer, long beginsector, long sectors); + +long cdda_read(void *d, void *buffer, long beginsector, long sectors) +{ + long ret = ReadCdRom(d, buffer, beginsector, sectors); + return ret; +} +#endif + +typedef struct string_len { + char *str; + unsigned int sl; +} mystring; + +static mystring drv_is_not_mmc[] = { + {"DEC RRD47 (C) DEC ",24}, +/* {"SONY CD-ROM CDU625 1.0",28}, */ + {NULL,0} /* must be last entry */ +}; + +static mystring drv_has_mmc_cdda[] = { + {"HITACHI CDR-7930",16}, +/* {"TOSHIBA CD-ROM XM-5402TA3605",28}, */ + {NULL,0} /* must be last entry */ +}; + +static int Is_a_Toshiba3401; + +int Toshiba3401(void); + +int Toshiba3401() +{ + return Is_a_Toshiba3401; +} + +/* hook */ +static void Dummy(void); +static void Dummy() +{ +} + +static SCSI *usalp; + +SCSI *get_scsi_p(void); + +SCSI *get_scsi_p() +{ + return usalp; +} + +#if !defined(SIM_CD) + +static void trash_cache_SCSI(UINT4 *p, unsigned lSector, + unsigned SectorBurstVal); + +static void trash_cache_SCSI(UINT4 *p, unsigned lSector, + unsigned SectorBurstVal) +{ + /* trash the cache */ + ReadCdRom(get_scsi_p(), p, find_an_off_sector(lSector, SectorBurstVal), min(global.nsectors,6)); +} + + + +static void Check_interface_for_device(struct stat *statstruct, + char *pdev_name); +static int OpenCdRom(char *pdev_name); + +static void SetupSCSI(void); + +static void SetupSCSI() +{ + unsigned char *p; + + if (interface != GENERIC_SCSI) { + /* unfortunately we have the wrong interface and are + * not able to change on the fly */ + fprintf(stderr, "The generic SCSI interface and devices are required\n"); + exit(SYNTAX_ERROR); + } + + /* do a test unit ready to 'init' the device. */ + TestForMedium(usalp); + + /* check for the correct type of unit. */ + p = Inquiry(usalp); + +#undef TYPE_ROM +#define TYPE_ROM 5 +#undef TYPE_WORM +#define TYPE_WORM 4 + if (p == NULL) { + fprintf(stderr, "Inquiry command failed. Aborting...\n"); + exit(DEVICE_ERROR); + } + + if ((*p != TYPE_ROM && *p != TYPE_WORM)) { + fprintf(stderr, "this is neither a scsi cdrom nor a worm device\n"); + exit(SYNTAX_ERROR); + } + + if (global.quiet == 0) { + fprintf(stderr, + "Type: %s, Vendor '%8.8s' Model '%16.16s' Revision '%4.4s' ", + *p == TYPE_ROM ? "ROM" : "WORM" + ,p+8 + ,p+16 + ,p+32); + } + /* generic Sony type defaults */ + density = 0x0; + accepts_fua_bit = -1; + EnableCdda = (void (*)(SCSI *, int, unsigned))Dummy; + ReadCdRom = ReadCdda12; + ReadCdRomSub = ReadCddaSubSony; + ReadCdRomData = (int (*)(SCSI *, unsigned char *, unsigned, unsigned))ReadStandardData; + ReadLastAudio = ReadFirstSessionTOCSony; + SelectSpeed = SpeedSelectSCSISony; + Play_at = Play_atSCSI; + StopPlay = StopPlaySCSI; + trash_cache = trash_cache_SCSI; + ReadTocText = ReadTocTextSCSIMMC; + doReadToc = ReadTocSCSI; + ReadSubQ = ReadSubQSCSI; + ReadSubChannels = NULL; + + /* check for brands and adjust special peculiaritites */ + + /* If your drive is not treated correctly, you can adjust some things + here: + + global.in_lendian: should be to 1, if the CDROM drive or CD-Writer + delivers the samples in the native byteorder of the audio cd + (LSB first). + HP CD-Writers need it set to 0. + NOTE: If you get correct wav files when using sox with the '-x' option, + the endianess is wrong. You can use the -C option to specify + the value of global.in_lendian. + + */ + + { + int mmc_code; + + usalp->silent ++; + allow_atapi(usalp, 1); + if (*p == TYPE_ROM) { + mmc_code = heiko_mmc(usalp); + } else { + mmc_code = 0; + } + usalp->silent --; + + /* Exceptions for drives that report incorrect MMC capability */ + if (mmc_code != 0) { + /* these drives are NOT capable of MMC commands */ + mystring *pp = drv_is_not_mmc; + while (pp->str != NULL) { + if (!strncmp(pp->str, (char *)p+8,pp->sl)) { + mmc_code = 0; + break; + } + pp++; + } + } + { + /* these drives flag themselves as non-MMC, but offer CDDA reading + only with a MMC method. */ + mystring *pp = drv_has_mmc_cdda; + while (pp->str != NULL) { + if (!strncmp(pp->str, (char *)p+8,pp->sl)) { + mmc_code = 1; + break; + } + pp++; + } + } + + switch (mmc_code) { + case 2: /* SCSI-3 cdrom drive with accurate audio stream */ + /* fall through */ + case 1: /* SCSI-3 cdrom drive with no accurate audio stream */ + /* fall through */ +lost_toshibas: + global.in_lendian = 1; + if (mmc_code == 2) + global.overlap = 0; + else + global.overlap = 1; + ReadCdRom = ReadCddaFallbackMMC; + ReadCdRomSub = ReadCddaSubSony; + ReadLastAudio = ReadFirstSessionTOCMMC; + SelectSpeed = SpeedSelectSCSIMMC; + ReadTocText = ReadTocTextSCSIMMC; + doReadToc = ReadTocMMC; + ReadSubChannels = ReadSubChannelsFallbackMMC; + if (!memcmp(p+8,"SONY CD-RW CRX100E 1.0", 27)) ReadTocText = NULL; + if (!global.quiet) fprintf(stderr, "MMC+CDDA\n"); + break; + case -1: /* "MMC drive does not support cdda reading, sorry\n." */ + doReadToc = ReadTocMMC; + if (!global.quiet) fprintf(stderr, "MMC-CDDA\n"); + /* FALLTHROUGH */ + case 0: /* non SCSI-3 cdrom drive */ + if (!global.quiet) fprintf(stderr, "no MMC\n"); + ReadLastAudio = NULL; + if (!memcmp(p+8,"TOSHIBA", 7) || + !memcmp(p+8,"IBM", 3) || + !memcmp(p+8,"DEC", 3)) { + /* + * older Toshiba ATAPI drives don't identify themselves as MMC. + * The last digit of the model number is '2' for ATAPI drives. + * These are treated as MMC. + */ + if (!memcmp(p+15, " CD-ROM XM-", 11) && p[29] == '2') { + goto lost_toshibas; + } + density = 0x82; + EnableCdda = EnableCddaModeSelect; + ReadSubChannels = ReadStandardSub; + ReadCdRom = ReadStandard; + SelectSpeed = SpeedSelectSCSIToshiba; + if (!memcmp(p+15, " CD-ROM XM-3401",15)) { + Is_a_Toshiba3401 = 1; + } + global.in_lendian = 1; + } else if (!memcmp(p+8,"IMS",3) || + !memcmp(p+8,"KODAK",5) || + !memcmp(p+8,"RICOH",5) || + !memcmp(p+8,"HP",2) || + !memcmp(p+8,"PHILIPS",7) || + !memcmp(p+8,"PLASMON",7) || + !memcmp(p+8,"GRUNDIG CDR100IPW",17) || + !memcmp(p+8,"MITSUMI CD-R ",13)) { + EnableCdda = EnableCddaModeSelect; + ReadCdRom = ReadStandard; + SelectSpeed = SpeedSelectSCSIPhilipsCDD2600; + + /* treat all of these as bigendian */ + global.in_lendian = 0; + + /* no overlap reading for cd-writers */ + global.overlap = 0; + } else if (!memcmp(p+8,"NRC",3)) { + SelectSpeed = NULL; + } else if (!memcmp(p+8,"YAMAHA",6)) { + EnableCdda = EnableCddaModeSelect; + SelectSpeed = SpeedSelectSCSIYamaha; + + /* no overlap reading for cd-writers */ + global.overlap = 0; + global.in_lendian = 1; + } else if (!memcmp(p+8,"PLEXTOR",7)) { + global.in_lendian = 1; + global.overlap = 0; + ReadLastAudio = ReadFirstSessionTOCSony; + ReadTocText = ReadTocTextSCSIMMC; + doReadToc = ReadTocSony; + ReadSubChannels = ReadSubChannelsSony; + } else if (!memcmp(p+8,"SONY",4)) { + global.in_lendian = 1; + if (!memcmp(p+16, "CD-ROM CDU55E",13)) { + ReadCdRom = ReadCddaMMC12; + } + ReadLastAudio = ReadFirstSessionTOCSony; + ReadTocText = ReadTocTextSCSIMMC; + doReadToc = ReadTocSony; + ReadSubChannels = ReadSubChannelsSony; + } else if (!memcmp(p+8,"NEC",3)) { + ReadCdRom = ReadCdda10; + ReadTocText = NULL; + SelectSpeed = SpeedSelectSCSINEC; + global.in_lendian = 1; + if (!memcmp(p+29,"5022.0r",3)) /* I assume all versions of the 502 require this? */ + global.overlap = 0; /* no overlap reading for NEC CD-ROM 502 */ + } else if (!memcmp(p+8,"MATSHITA",8)) { + ReadCdRom = ReadCdda12Matsushita; + global.in_lendian = 1; + } + } /* switch (get_mmc) */ + } + + + /* look if caddy is loaded */ + if (interface == GENERIC_SCSI) { + usalp->silent++; + while (!wait_unit_ready(usalp, 60)) { + fprintf(stderr,"load cdrom please and press enter"); + getchar(); + } + usalp->silent--; + } +} + +/* Check to see if the device will support SCSI generic commands. A + * better check than simply looking at the device name. Open the + * device, issue an inquiry. If they both succeed, there's a good + * chance that the device works... */ +#if defined(__linux__) +static int check_linux_scsi_interface(char *pdev_name) +{ + SCSI *dev = NULL; + unsigned char *p = NULL; + char errstr[80]; + + dev = usal_open(pdev_name, errstr, sizeof(errstr), 0, 0); + if (NULL == dev) + return EINVAL; + p = Inquiry(dev); + if (p) + { + usal_close(dev); + return 0; + } + usal_close(dev); + return EINVAL; +} +#endif + +/********************** General setup *******************************/ + +/* As the name implies, interfaces and devices are checked. We also + adjust nsectors, overlap, and interface for the first time here. + Any unnecessary privileges (setuid, setgid) are also dropped here. +*/ +static void Check_interface_for_device(struct stat *statstruct, char *pdev_name) +{ +#if defined(__linux__) + int is_scsi = 1; +#endif +#ifndef STAT_MACROS_BROKEN + if (!S_ISCHR(statstruct->st_mode) && + !S_ISBLK(statstruct->st_mode)) { + fprintf(stderr, "%s is not a device\n",pdev_name); + exit(SYNTAX_ERROR); + } +#endif + +/* Check what type of device we have */ +#if defined (__linux__) + if (check_linux_scsi_interface(pdev_name)) + is_scsi = 0; + if (interface == GENERIC_SCSI && !is_scsi) + { + fprintf(stderr, "device %s does not support generic_scsi; falling back to cooked_ioctl instead\n", pdev_name); + interface = COOKED_IOCTL; + } + if ((interface == COOKED_IOCTL) && + is_scsi && + (SCSI_GENERIC_MAJOR == major(statstruct->st_rdev))) + { + fprintf(stderr, "device %s is generic_scsi NOT cooked_ioctl\n", pdev_name); + interface = GENERIC_SCSI; + } +#else + +#if defined (HAVE_ST_RDEV) + switch (major(statstruct->st_rdev)) { +#if defined (__linux__) + case SCSI_GENERIC_MAJOR: /* generic */ +#else + default: /* ??? what is the proper value here */ +#endif +#ifndef STAT_MACROS_BROKEN +#if defined (__linux__) + if (!S_ISCHR(statstruct->st_mode)) { + fprintf(stderr, "%s is not a char device\n",pdev_name); + exit(SYNTAX_ERROR); + } + + if (interface != GENERIC_SCSI) { + fprintf(stderr, "wrong interface (cooked_ioctl) for this device (%s)\nset to generic_scsi\n", pdev_name); + interface = GENERIC_SCSI; + } +#endif +#else + default: /* ??? what is the proper value here */ +#endif + break; + +#if defined (__linux__) || defined (__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#if defined (__linux__) + case SCSI_CDROM_MAJOR: /* scsi cd */ + default: /* for example ATAPI cds */ +#else +#if defined (__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#if __FreeBSD_version >= 600021 + case 0: /* majors abandoned */ + /* FALLTHROUGH */ +#endif +#if __FreeBSD_version >= 501113 + case 4: /* GEOM */ + /* FALLTHROUGH */ +#endif + case 117: /* pre-GEOM atapi cd */ + if (!S_ISCHR(statstruct->st_mode)) { + fprintf(stderr, "%s is not a char device\n",pdev_name); + exit(SYNTAX_ERROR); + } + if (interface != COOKED_IOCTL) { + fprintf(stderr, +"cdrom device (%s) is not of type generic SCSI. \ +Setting interface to cooked_ioctl.\n", pdev_name); + interface = COOKED_IOCTL; + } + break; + case 19: /* first atapi cd */ +#endif +#endif + if (!S_ISBLK(statstruct->st_mode)) { + fprintf(stderr, "%s is not a block device\n",pdev_name); + exit(SYNTAX_ERROR); + } +#if defined (__linux__) +#if LINUX_VERSION_CODE >= 0x20600 + /* In Linux kernel 2.6 it is better to use the SCSI interface + * with the device. + */ + break; +#endif +#endif + if (interface != COOKED_IOCTL) { + fprintf(stderr, +"cdrom device (%s) is not of type generic SCSI. \ +Setting interface to cooked_ioctl.\n", pdev_name); + interface = COOKED_IOCTL; + } + + if (interface == COOKED_IOCTL) { + fprintf(stderr, "\nW: The cooked_ioctl interface is functionally very limited!!\n"); +#if defined (__linux__) + fprintf(stderr, "\nW: For good sampling quality simply use the generic SCSI interface!\n" + "For example dev=ATA:1,0,0\n"); +#endif + } + + break; +#endif + } +#endif +#endif + if (global.overlap >= global.nsectors) + global.overlap = global.nsectors-1; +} + +/* open the cdrom device */ +static int OpenCdRom(char *pdev_name) +{ + int retval = 0; + struct stat fstatstruct; + + /* The device (given by pdevname) can be: + a. an SCSI device specified with a /dev/xxx name, + b. an SCSI device specified with bus,target,lun numbers, + c. a non-SCSI device such as ATAPI or proprietary CDROM devices. + */ +#ifdef HAVE_IOCTL_INTERFACE + struct stat statstruct; + int have_named_device = 0; + + have_named_device = FALSE; + if (pdev_name) { + have_named_device = strchr(pdev_name, ':') == NULL + && memcmp(pdev_name, "/dev/", 5) == 0; + } + + if (have_named_device) { + if (stat(pdev_name, &statstruct)) { + fprintf(stderr, "cannot stat device %s\n", pdev_name); + exit(STAT_ERROR); + } else { + Check_interface_for_device( &statstruct, pdev_name ); + } + } +#endif + + if (interface == GENERIC_SCSI) { + char errstr[80]; + + priv_on(); + needroot(0); + needgroup(0); + /* + * Call usal_remote() to force loading the remote SCSI transport library + * code that is located in librusal instead of the dummy remote routines + * that are located inside libusal. + */ + usal_remote(); + if (pdev_name != NULL && + ((strncmp(pdev_name, "HELP", 4) == 0) || + (strncmp(pdev_name, "help", 4) == 0))) { + usal_help(stderr); + exit(NO_ERROR); + } + /* device name, debug, verboseopen */ + usalp = usal_open(pdev_name, errstr, sizeof(errstr), 0, 0); + + if (usalp == NULL) { + int err = geterrno(); + + errmsgno(err, "%s%sCannot open SCSI driver.\n", errstr, errstr[0]?". ":""); + errmsgno(EX_BAD, "For possible targets try 'wodim -scanbus'.%s\n", + geteuid() ? " Make sure you are root.":""); + priv_off(); + dontneedgroup(); + dontneedroot(); +#if defined(sun) || defined(__sun) + fprintf(stderr, "On SunOS/Solaris make sure you have Joerg Schillings usal SCSI driver installed.\n"); +#endif +#if defined (__linux__) + fprintf(stderr, "Use the script scan_scsi.linux to find out more.\n"); +#endif + fprintf(stderr, "Probably you did not define your SCSI device.\n"); + fprintf(stderr, "Set the CDDA_DEVICE environment variable or use the -D option.\n"); + fprintf(stderr, "You can also define the default device in the Makefile.\n"); + fprintf(stderr, "For possible transport specifiers try 'wodim dev=help'.\n"); + exit(SYNTAX_ERROR); + } + usal_settimeout(usalp, 300); + usal_settimeout(usalp, 60); + usalp->silent = global.scsi_silent; + usalp->verbose = global.scsi_verbose; + + if (global.nsectors > (unsigned) usal_bufsize(usalp, 3*1024*1024)/CD_FRAMESIZE_RAW) + global.nsectors = usal_bufsize(usalp, 3*1024*1024)/CD_FRAMESIZE_RAW; + if (global.overlap >= global.nsectors) + global.overlap = global.nsectors-1; + + /* + * Newer versions of Linux seem to introduce an incompatible change + * and require root privileges or limit RLIMIT_MEMLOCK infinity + * in order to get a SCSI buffer in case we did call mlockall(MCL_FUTURE). + */ + init_scsibuf(usalp, global.nsectors*CD_FRAMESIZE_RAW); + priv_off(); + dontneedgroup(); + dontneedroot(); + + if (global.scandevs) { + list_devices(usalp, stdout, 0); + exit(0); + } + + if (global.scanbus) { + select_target(usalp, stdout); + exit(0); + } + } else { + needgroup(0); + retval = open(pdev_name,O_RDONLY +#ifdef linux + | O_NONBLOCK +#endif + ); + dontneedgroup(); + + if (retval < 0) { + fprintf(stderr, "while opening %s :", pdev_name); + perror(""); + exit(DEVICEOPEN_ERROR); + } + + /* Do final security checks here */ + if (fstat(retval, &fstatstruct)) { + fprintf(stderr, "Could not fstat %s (fd %d): ", pdev_name, retval); + perror(""); + exit(STAT_ERROR); + } + Check_interface_for_device( &fstatstruct, pdev_name ); + +#if defined HAVE_IOCTL_INTERFACE + /* Watch for race conditions */ + if (have_named_device + && (fstatstruct.st_dev != statstruct.st_dev || + fstatstruct.st_ino != statstruct.st_ino)) { + fprintf(stderr,"Race condition attempted in OpenCdRom. Exiting now.\n"); + exit(RACE_ERROR); + } +#endif + /* + * The structure looks like a desaster :-( + * We do this more than once as it is impossible to understand where + * the right place would be to do this.... + */ + if (usalp != NULL) { + usalp->verbose = global.scsi_verbose; + } + } + return retval; +} +#endif /* SIM_CD */ + +/******************* Simulation interface *****************/ +#if defined SIM_CD +#include "toc.h" +static unsigned long sim_pos=0; + +/* read 'SectorBurst' adjacent sectors of audio sectors + * to Buffer '*p' beginning at sector 'lSector' + */ +static int ReadCdRom_sim(SCSI *x, UINT4 *p, unsigned lSector, + unsigned SectorBurstVal); +static int ReadCdRom_sim(SCSI *x, UINT4 *p, unsigned lSector, + unsigned SectorBurstVal) +{ + unsigned int loop=0; + Int16_t *q = (Int16_t *) p; + int joffset = 0; + + if (lSector > g_toc[cdtracks].dwStartSector || lSector + SectorBurstVal > g_toc[cdtracks].dwStartSector + 1) { + fprintf(stderr, "Read request out of bounds: %u - %u (%d - %d allowed)\n", + lSector, lSector + SectorBurstVal, 0, g_toc[cdtracks].dwStartSector); + } +#if 0 + /* jitter with a probability of jprob */ + if (random() <= jprob) { + /* jitter up to jmax samples */ + joffset = random(); + } +#endif + +#ifdef DEBUG_SHM + fprintf(stderr, ", last_b = %p\n", *last_buffer); +#endif + for (loop = lSector*CD_FRAMESAMPLES + joffset; + loop < (lSector+SectorBurstVal)*CD_FRAMESAMPLES + joffset; + loop++) { + *q++ = loop; + *q++ = ~loop; + } +#ifdef DEBUG_SHM + fprintf(stderr, "sim wrote from %p upto %p - 4 (%d), last_b = %p\n", + p, q, SectorBurstVal*CD_FRAMESAMPLES, *last_buffer); +#endif + sim_pos = (lSector+SectorBurstVal)*CD_FRAMESAMPLES + joffset; + return SectorBurstVal; +} + +static int Play_at_sim(SCSI *x, unsigned int from_sector, unsigned int sectors); +static int Play_at_sim(SCSI *x, unsigned int from_sector, unsigned int sectors) +{ + sim_pos = from_sector*CD_FRAMESAMPLES; + return 0; +} + +static unsigned sim_indices; + + +/* read the table of contents (toc) via the ioctl interface */ +static unsigned ReadToc_sim(SCSI *x, TOC *toc); +static unsigned ReadToc_sim(SCSI *x, TOC *toc) +{ + unsigned int scenario; + int scen[12][3] = { + {1,1,500}, + {1,2,500}, + {1,99,150*99}, + {2,1,500}, + {2,2,500}, + {2,99,150*99}, + {2,1,500}, + {5,2,500}, + {5,99,150*99}, + {99,1,1000}, + {99,2,1000}, + {99,99,150*99}, + }; + unsigned int i; + unsigned trcks; +#if 0 + fprintf(stderr, "select one of the following TOCs\n" + "0 : 1 track with 1 index\n" + "1 : 1 track with 2 indices\n" + "2 : 1 track with 99 indices\n" + "3 : 2 tracks with 1 index each\n" + "4 : 2 tracks with 2 indices each\n" + "5 : 2 tracks with 99 indices each\n" + "6 : 2 tracks (data and audio) with 1 index each\n" + "7 : 5 tracks with 2 indices each\n" + "8 : 5 tracks with 99 indices each\n" + "9 : 99 tracks with 1 index each\n" + "10: 99 tracks with 2 indices each\n" + "11: 99 tracks with 99 indices each\n" + ); + + do { + scanf("%u", &scenario); + } while (scenario > sizeof(scen)/2/sizeof(int)); +#else + scenario = 6; +#endif + /* build table of contents */ + +#if 0 + trcks = scen[scenario][0] + 1; + sim_indices = scen[scenario][1]; + + for (i = 0; i < trcks; i++) { + toc[i].bFlags = (scenario == 6 && i == 0) ? 0x40 : 0xb1; + toc[i].bTrack = i + 1; + toc[i].dwStartSector = i * scen[scenario][2]; + toc[i].mins = (toc[i].dwStartSector+150) / (60*75); + toc[i].secs = (toc[i].dwStartSector+150 / 75) % (60); + toc[i].frms = (toc[i].dwStartSector+150) % (75); + } + toc[i].bTrack = 0xaa; + toc[i].dwStartSector = i * scen[scenario][2]; + toc[i].mins = (toc[i].dwStartSector+150) / (60*75); + toc[i].secs = (toc[i].dwStartSector+150 / 75) % (60); + toc[i].frms = (toc[i].dwStartSector+150) % (75); +#else + { + int starts[15] = { 23625, 30115, 39050, 51777, 67507, + 88612, 112962, 116840, 143387, 162662, + 173990, 186427, 188077, 209757, 257120}; + trcks = 14 + 1; + sim_indices = 1; + + for (i = 0; i < trcks; i++) { + toc[i].bFlags = 0x0; + toc[i].bTrack = i + 1; + toc[i].dwStartSector = starts[i]; + toc[i].mins = (starts[i]+150) / (60*75); + toc[i].secs = (starts[i]+150 / 75) % (60); + toc[i].frms = (starts[i]+150) % (75); + } + toc[i].bTrack = 0xaa; + toc[i].dwStartSector = starts[i]; + toc[i].mins = (starts[i]) / (60*75); + toc[i].secs = (starts[i] / 75) % (60); + toc[i].frms = (starts[i]) % (75); + } +#endif + return --trcks; /* without lead-out */ +} + + +static subq_chnl *ReadSubQ_sim(SCSI *usalp, unsigned char sq_format, + unsigned char track); +/* request sub-q-channel information. This function may cause confusion + * for a drive, when called in the sampling process. + */ +static subq_chnl *ReadSubQ_sim(SCSI *usalp, unsigned char sq_format, + unsigned char track) +{ + subq_chnl *SQp = (subq_chnl *) (SubQbuffer); + subq_position *SQPp = (subq_position *) &SQp->data; + unsigned long sim_pos1; + unsigned long sim_pos2; + + if ( sq_format != GET_POSITIONDATA ) return NULL; /* not supported by sim */ + + /* simulate CDROMSUBCHNL ioctl */ + + /* copy to SubQbuffer */ + SQp->audio_status = 0; + SQp->format = 0xff; + SQp->control_adr = 0xff; + sim_pos1 = sim_pos/CD_FRAMESAMPLES; + sim_pos2 = sim_pos1 % 150; + SQp->track = (sim_pos1 / 5000) + 1; + SQp->index = ((sim_pos1 / 150) % sim_indices) + 1; + sim_pos1 += 150; + SQPp->abs_min = sim_pos1 / (75*60); + SQPp->abs_sec = (sim_pos1 / 75) % 60; + SQPp->abs_frame = sim_pos1 % 75; + SQPp->trel_min = sim_pos2 / (75*60); + SQPp->trel_sec = (sim_pos2 / 75) % 60; + SQPp->trel_frame = sim_pos2 % 75; + + return (subq_chnl *)(SubQbuffer); +} + +static void SelectSpeed_sim(SCSI *x, unsigned sp); +/* ARGSUSED */ +static void SelectSpeed_sim(SCSI *x, unsigned sp) +{ +} + +static void trash_cache_sim(UINT4 *p, unsigned lSector, + unsigned SectorBurstVal); + +/* ARGSUSED */ +static void trash_cache_sim(UINT4 *p, unsigned lSector, + unsigned SectorBurstVal) +{ +} + +static void SetupSimCd(void); + +static void SetupSimCd() +{ + EnableCdda = (void (*)(SCSI *, int, unsigned))Dummy; + ReadCdRom = ReadCdRom_sim; + ReadCdRomData = (int (*)(SCSI *, unsigned char *, unsigned, unsigned))ReadCdRom_sim; + doReadToc = ReadToc_sim; + ReadTocText = NULL; + ReadSubQ = ReadSubQ_sim; + ReadSubChannels = NULL; + ReadLastAudio = NULL; + SelectSpeed = SelectSpeed_sim; + Play_at = Play_at_sim; + StopPlay = (int (*)(SCSI *))Dummy; + trash_cache = trash_cache_sim; + +} + +#endif /* def SIM_CD */ + +/* perform initialization depending on the interface used. */ +void SetupInterface() +{ +#if defined SIM_CD + fprintf( stderr, "SIMULATION MODE !!!!!!!!!!!\n"); +#else + /* ensure interface is setup correctly */ + global.cooked_fd = OpenCdRom ( global.dev_name ); +#endif + + global.pagesize = getpagesize(); + + /* request one sector for table of contents */ + bufferTOC = malloc( CD_FRAMESIZE_RAW + 96 ); /* assumes sufficient aligned addresses */ + /* SubQchannel buffer */ + SubQbuffer = malloc( 48 ); /* assumes sufficient aligned addresses */ + cmd = malloc( 18 ); /* assumes sufficient aligned addresses */ + if ( !bufferTOC || !SubQbuffer || !cmd ) { + fprintf( stderr, "Too low on memory. Giving up.\n"); + exit(NOMEM_ERROR); + } + +#if defined SIM_CD + usalp = malloc(sizeof(* usalp)); + if (usalp == NULL) { + FatalError("No memory for SCSI structure.\n"); + } + usalp->silent = 0; + SetupSimCd(); +#else + /* if drive is of type scsi, get vendor name */ + if (interface == GENERIC_SCSI) { + unsigned sector_size; + + SetupSCSI(); + sector_size = get_orig_sectorsize(usalp, &orgmode4, &orgmode10, &orgmode11); + if (!SCSI_emulated_ATAPI_on(usalp)) { + if ( sector_size != 2048 && set_sectorsize(usalp, 2048) ) { + fprintf( stderr, "Could not change sector size from %d to 2048\n", sector_size ); + } + } else { + sector_size = 2048; + } + + /* get cache setting */ + + /* set cache to zero */ + + } else { +#if defined (HAVE_IOCTL_INTERFACE) + usalp = malloc(sizeof(* usalp)); + if (usalp == NULL) { + FatalError("No memory for SCSI structure.\n"); + } + usalp->silent = 0; + SetupCookedIoctl( global.dev_name ); +#else + FatalError("Sorry, there is no known method to access the device.\n"); +#endif + } +#endif /* if def SIM_CD */ + /* + * The structure looks like a desaster :-( + * We do this more than once as it is impossible to understand where + * the right place would be to do this.... + */ + if (usalp != NULL) { + usalp->verbose = global.scsi_verbose; + } +} + +#ifdef HAVE_PRIV_H +#include +#endif + +void +priv_init() +{ +#ifdef HAVE_PRIV_SET + /* + * Give up privs we do not need anymore. + * We no longer need: + * file_dac_read,sys_devices,proc_priocntl,net_privaddr + */ + priv_set(PRIV_OFF, PRIV_EFFECTIVE, + PRIV_FILE_DAC_READ, PRIV_PROC_PRIOCNTL, + PRIV_NET_PRIVADDR, NULL); + priv_set(PRIV_OFF, PRIV_INHERITABLE, + PRIV_FILE_DAC_READ, PRIV_PROC_PRIOCNTL, + PRIV_NET_PRIVADDR, PRIV_SYS_DEVICES, NULL); +#endif +} + +void +priv_on() +{ +#ifdef HAVE_PRIV_SET + /* + * Get back privs we may need now. + * We need: + * file_dac_read,sys_devices,proc_priocntl,net_privaddr + */ + priv_set(PRIV_ON, PRIV_EFFECTIVE, + PRIV_FILE_DAC_READ, PRIV_PROC_PRIOCNTL, + PRIV_NET_PRIVADDR, NULL); +#endif +} + +void +priv_off() +{ +#ifdef HAVE_PRIV_SET + /* + * Give up privs we do not need anymore. + * We no longer need: + * file_dac_read,sys_devices,proc_priocntl,net_privaddr + */ + priv_set(PRIV_OFF, PRIV_EFFECTIVE, + PRIV_FILE_DAC_READ, PRIV_PROC_PRIOCNTL, + PRIV_NET_PRIVADDR, NULL); +#endif +} diff --git a/icedax/toc.c b/icedax/toc.c index b48fb1c..fcb8809 100644 --- a/icedax/toc.c +++ b/icedax/toc.c @@ -379,8 +379,7 @@ static void emit_cddb_form(char *fname_baseval); static void emit_cdindex_form(char *fname_baseval); #endif - -typedef struct TOC { /* structure of table of contents (cdrom) */ +typedef struct TOC_t { /* structure of table of contents (cdrom) */ unsigned char reserved1; unsigned char bFlags; unsigned char bTrack; @@ -391,8 +390,7 @@ typedef struct TOC { /* structure of table of contents (cdrom) */ int frms; unsigned char ISRC[16]; int SCMS; -} TOC; - +} TOC_t; /* Flags contains two fields: bits 7-4 (ADR) @@ -432,7 +430,7 @@ struct iterator { int index; int startindex; void (*reset)(struct iterator *this); - struct TOC *(*getNextTrack)(struct iterator *this); + struct TOC_t *(*getNextTrack)(struct iterator *this); int (*hasNextTrack)(struct iterator *this); }; @@ -518,7 +516,7 @@ unsigned FixupTOC(unsigned no_tracks) InitIterator(&i, 1); while (i.hasNextTrack(&i)) { - struct TOC *p = i.getNextTrack(&i); + struct TOC_t *p = i.getNextTrack(&i); if (IS__AUDIO(p)) count_audio_tracks++; } @@ -733,7 +731,7 @@ static void emit_cddb_form(char *fname_baseval) fprintf( cddb_form, "# Track frame offsets:\n#\n"); while (i.hasNextTrack(&i)) { - struct TOC *p = i.getNextTrack(&i); + struct TOC_t *p = i.getNextTrack(&i); if (GETTRACK(p) == CDROM_LEADOUT) break; fprintf( cddb_form, "# %lu\n", 150 + Get_AudioStartSector(GETTRACK(p))); @@ -765,7 +763,7 @@ static void emit_cddb_form(char *fname_baseval) i.reset(&i); while (i.hasNextTrack(&i)) { - struct TOC *p = i.getNextTrack(&i); + struct TOC_t *p = i.getNextTrack(&i); int ii; ii = GETTRACK(p); @@ -788,7 +786,7 @@ static void emit_cddb_form(char *fname_baseval) i.reset(&i); while (i.hasNextTrack(&i)) { - struct TOC *p = i.getNextTrack(&i); + struct TOC_t *p = i.getNextTrack(&i); int ii; ii = GETTRACK(p); @@ -1495,7 +1493,7 @@ static int IsSingleArtist(void) InitIterator(&i, 1); while (i.hasNextTrack(&i)) { - struct TOC *p = i.getNextTrack(&i); + struct TOC_t *p = i.getNextTrack(&i); int ii; if (IS__DATA(p) || GETTRACK(p) == CDROM_LEADOUT) continue; @@ -1681,7 +1679,7 @@ static void emit_cdindex_form(char *fname_baseval) global.creator ? ascii2html(global.creator) : ""); while (i.hasNextTrack(&i)) { - struct TOC *p = i.getNextTrack(&i); + struct TOC_t *p = i.getNextTrack(&i); int ii = GETTRACK(p); if (ii == CDROM_LEADOUT) break; @@ -1703,7 +1701,7 @@ static void emit_cdindex_form(char *fname_baseval) fprintf( cdindex_form, " \n"); while (i.hasNextTrack(&i)) { - struct TOC *p = i.getNextTrack(&i); + struct TOC_t *p = i.getNextTrack(&i); int ii = GETTRACK(p); if (ii == CDROM_LEADOUT) break; @@ -1942,9 +1940,9 @@ static void DisplayToc_with_gui(unsigned long dw) if ((global.verbose & (SHOW_TOC | SHOW_STARTPOSITIONS | SHOW_SUMMARY | SHOW_TITLES)) != 0 && i.hasNextTrack(&i)) { - TOC *o = i.getNextTrack(&i); + TOC_t *o = i.getNextTrack(&i); while (i.hasNextTrack(&i)) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); int from; from = GETTRACK(o); @@ -2072,9 +2070,9 @@ static void DisplayToc_no_gui(unsigned long dw) count_audio_trks = 0; if (i.hasNextTrack(&i)) { - TOC *o = i.getNextTrack(&i); + TOC_t *o = i.getNextTrack(&i); while (i.hasNextTrack(&i)) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); int from; from = GETTRACK(o); @@ -2131,10 +2129,10 @@ static void DisplayToc_no_gui(unsigned long dw) i.reset(&i); if ((global.verbose & SHOW_TOC) != 0 && i.hasNextTrack(&i)) { - TOC *o = i.getNextTrack(&i); + TOC_t *o = i.getNextTrack(&i); for (; i.hasNextTrack(&i);) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); if ( GETTRACK(o) <= MAXTRK ) { unsigned char brace1, brace2; @@ -2189,9 +2187,9 @@ static void DisplayToc_no_gui(unsigned long dw) ii = 0; i.reset(&i); if (i.hasNextTrack(&i)) { - TOC *o = i.getNextTrack(&i); + TOC_t *o = i.getNextTrack(&i); for ( ; i.hasNextTrack(&i);) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); fprintf ( stderr, " %2u.(%8u)", GETTRACK(o), @@ -2248,7 +2246,7 @@ static void DisplayToc_no_gui(unsigned long dw) i.reset(&i); for ( ; i.hasNextTrack(&i);) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); int jj = GETTRACK(p); if ( global.tracktitle[jj] != NULL ) { @@ -2260,7 +2258,7 @@ static void DisplayToc_no_gui(unsigned long dw) i.reset(&i); for ( ; i.hasNextTrack(&i); ) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); int jj; if (IS__DATA(p)) @@ -2720,7 +2718,7 @@ void Read_MCN_ISRC(void) InitIterator(&i, 1); while (i.hasNextTrack(&i)) { - struct TOC *p = i.getNextTrack(&i); + struct TOC_t *p = i.getNextTrack(&i); unsigned ii = GETTRACK(p); if (ii == CDROM_LEADOUT) break; @@ -3155,7 +3153,7 @@ unsigned ScanIndices(unsigned track, unsigned cd_index, int bulk) while (i.hasNextTrack(&i)) { - struct TOC *p = i.getNextTrack(&i); + struct TOC_t *p = i.getNextTrack(&i); unsigned ii = GETTRACK(p); if ( ii < starttrack || IS__DATA(p) ) @@ -3322,7 +3320,7 @@ unsigned char *Get_MCN(void) } -static TOC g_toc [MAXTRK+1]; /* hidden track + 100 regular tracks */ +static TOC_t g_toc [MAXTRK+1]; /* hidden track + 100 regular tracks */ /*#define IS_AUDIO(i) (!(g_toc[i].bFlags & 0x40))*/ @@ -3397,10 +3395,10 @@ static int patch_cd_extra(unsigned track, unsigned long sector) static int restrict_tracks_illleadout(void) { - struct TOC *o = &g_toc[cdtracks+1]; + struct TOC_t *o = &g_toc[cdtracks+1]; int i; for (i = cdtracks; i >= 0; i--) { - struct TOC *p = &g_toc[i]; + struct TOC_t *p = &g_toc[i]; if (GETSTART(o) > GETSTART(p)) break; } patch_cd_extra(i+1, GETSTART(o)); @@ -3523,7 +3521,7 @@ static void it_reset(struct iterator *this) static int it_hasNextTrack(struct iterator *this); -static struct TOC *it_getNextTrack(struct iterator *this); +static struct TOC_t *it_getNextTrack(struct iterator *this); static int it_hasNextTrack(struct iterator *this) { @@ -3532,7 +3530,7 @@ static int it_hasNextTrack(struct iterator *this) -static struct TOC *it_getNextTrack(struct iterator *this) +static struct TOC_t *it_getNextTrack(struct iterator *this) { /* if ( (*this->hasNextTrack)(this) == 0 ) return NULL; */ if ( this->index > (int)cdtracks+1 ) return NULL; @@ -3583,7 +3581,7 @@ long Get_AudioStartSector(unsigned long p_track) if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT; while (i.hasNextTrack(&i)) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); if (GETTRACK(p) == p_track) { if (IS__DATA(p)) { @@ -3613,7 +3611,7 @@ long Get_StartSector(unsigned long p_track) if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT; while (i.hasNextTrack(&i)) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); if (GETTRACK(p) == p_track) { return GETSTART(p); @@ -3637,7 +3635,7 @@ long Get_EndSector(unsigned long p_track) if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT; while (i.hasNextTrack(&i)) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); if (GETTRACK(p) == p_track) { p = i.getNextTrack(&i); if (p == NULL) { @@ -3669,7 +3667,7 @@ long FirstAudioTrack(void) else i.reset(&i); while (i.hasNextTrack(&i)) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); unsigned ii = GETTRACK(p); if (ii == CDROM_LEADOUT) break; @@ -3687,7 +3685,7 @@ long FirstDataTrack(void) else i.reset(&i); while (i.hasNextTrack(&i)) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); if (IS__DATA(p)) { return GETTRACK(p); } @@ -3708,7 +3706,7 @@ long LastAudioTrack(void) else i.reset(&i); while (i.hasNextTrack(&i)) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); if (IS__AUDIO(p) && (GETTRACK(p) != CDROM_LEADOUT)) { j = GETTRACK(p); } @@ -3730,7 +3728,7 @@ long Get_LastSectorOnCd(unsigned long p_track) if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT; while (i.hasNextTrack(&i)) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); if (GETTRACK(p) < p_track) continue; @@ -3749,9 +3747,9 @@ int Get_Track(unsigned long sector) else i.reset(&i); if (i.hasNextTrack(&i)) { - TOC *o = i.getNextTrack(&i); + TOC_t *o = i.getNextTrack(&i); while (i.hasNextTrack(&i)) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); if ((GETSTART(o) <= sector) && (GETSTART(p) > sector)) { if (IS__DATA(o)) { return -1; @@ -3772,7 +3770,7 @@ int CheckTrackrange(unsigned long from, unsigned long upto) else i.reset(&i); while (i.hasNextTrack(&i)) { - TOC *p = i.getNextTrack(&i); + TOC_t *p = i.getNextTrack(&i); if (GETTRACK(p) < from) continue; @@ -3788,54 +3786,3 @@ int CheckTrackrange(unsigned long from, unsigned long upto) return 0; } -#ifdef USE_PARANOIA -long cdda_disc_firstsector(void *d); - -long cdda_disc_firstsector(void *d) -{ - return Get_StartSector(FirstAudioTrack()); -} - -int cdda_tracks(void *d); - -int cdda_tracks(void *d) -{ - return LastAudioTrack() - FirstAudioTrack() +1; -} - -int cdda_track_audiop(void *d, int track); - -int cdda_track_audiop(void *d, int track) -{ - return Get_Datatrack(track) == 0; -} - -long cdda_track_firstsector(void *d, int track); - -long cdda_track_firstsector(void *d, int track) -{ - return Get_AudioStartSector(track); -} - -long cdda_track_lastsector(void *d, int track); - -long cdda_track_lastsector(void *d, int track) -{ - return Get_EndSector(track); -} - -long cdda_disc_lastsector(void *d); - -long cdda_disc_lastsector(void *d) -{ - return Get_LastSectorOnCd(cdtracks) - 1; -} - -int cdda_sector_gettrack(void *d,long sector); - -int cdda_sector_gettrack(void *d, long sector) -{ - return Get_Track(sector); -} - -#endif diff --git a/icedax/toc.c.paranoiacdda b/icedax/toc.c.paranoiacdda new file mode 100644 index 0000000..b48fb1c --- /dev/null +++ b/icedax/toc.c.paranoiacdda @@ -0,0 +1,3841 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)toc.c 1.57 06/02/19 Copyright 1998-2003 Heiko Eissfeldt */ +/* + * Copyright: GNU Public License 2 applies + * + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * CDDA2WAV (C) Heiko Eissfeldt heiko@hexco.de + * CDDB routines (C) Ti Kan and Steve Scherf + */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include /* sleep */ +#include +#include +#include +#include +#include +#include +#include + +#define CD_TEXT +#define CD_EXTRA +#undef DEBUG_XTRA +#undef DEBUG_CDTEXT +#undef DEBUG_CDDBP + + +#include + +#include "mytype.h" +#include "byteorder.h" +#include "interface.h" +#include "icedax.h" +#include "global.h" +#include "sha.h" +#include "base64.h" +#include "toc.h" +#include "exitcodes.h" +#include "ringbuff.h" + +int Get_Mins(unsigned long p_track); +int Get_Secs(unsigned long p_track); +int Get_Frames(unsigned long p_track); +int Get_Flags(unsigned long p_track); +int Get_SCMS(unsigned long p_track); + + +#if defined USE_REMOTE +/* tcp stuff */ +/* fix OS/2 compilation */ +#ifdef __EMX__ +#define gethostid nogethostid +#endif +#include +#undef gethostid +#include +#if defined(HAVE_NETDB_H) && !defined(HOST_NOT_FOUND) && \ + !defined(_INCL_NETDB_H) +#include +#define _INCL_NETDB_H +#endif +#endif + +int have_CD_text; +int have_multisession; +int have_CD_extra; +int have_CDDB; + +struct iterator; + +static void UpdateTrackData(int p_num); +static void UpdateIndexData(int p_num); +static void UpdateTimeData(int p_min, int p_sec, int p_frm); +static unsigned int is_multisession(void); +static unsigned int get_end_of_last_audio_track(unsigned mult_off); +static int cddb_sum(int n); +static void dump_extra_info(unsigned from); +static int GetIndexOfSector(unsigned sec, unsigned track); +static int patch_cd_extra(unsigned track, unsigned long sector); +static void patch_to_audio(unsigned long p_track); +static int restrict_tracks_illleadout(void); +static void Set_MCN(unsigned char *MCN_arg); +static void Set_ISRC(int track, const unsigned char *ISRC_arg); +static void InitIterator(struct iterator *iter, unsigned long p_track); + +static unsigned char g_track=0xff, g_index=0xff; /* current track, index */ + +/* Conversion function: from logical block adresses to minute,second,frame + */ +int lba_2_msf(long lba, int *m, int *s, int *f) +{ +#ifdef __follow_redbook__ + if (lba >= -150 && lba < 405000) { /* lba <= 404849 */ +#else + if (lba >= -150) { +#endif + lba += 150; + } else if (lba >= -45150 && lba <= -151) { + lba += 450150; + } else + return 1; + + *m = lba / 60 / 75; + lba -= (*m)*60*75; + *s = lba / 75; + lba -= (*s)*75; + *f = lba; + + return 0; +} + +/* print the track currently read */ +static void UpdateTrackData(int p_num) +{ + if (global.quiet == 0) { + fprintf (stderr, "\ntrack: %.2d, ", p_num); fflush(stderr); + } + g_track = (unsigned char) p_num; +} + + +/* print the index currently read */ +static void UpdateIndexData(int p_num) +{ + if (global.quiet == 0) { + fprintf (stderr, "index: %.2d\n", p_num); fflush(stderr); + } + g_index = (unsigned char) p_num; +} + + +/* print the time of track currently read */ +static void UpdateTimeData(int p_min, int p_sec, int p_frm) +{ + if (global.quiet == 0) { + fprintf (stderr, "time: %.2d:%.2d.%.2d\r", p_min, p_sec, p_frm); + fflush(stderr); + } +} + +void AnalyzeQchannel(unsigned frame) +{ + subq_chnl *sub_ch; + + if (trackindex_disp != 0) { + sub_ch = ReadSubQ(get_scsi_p(), GET_POSITIONDATA,0); + /* analyze sub Q-channel data */ + if (sub_ch->track != g_track || + sub_ch->index != g_index) { + UpdateTrackData (sub_ch->track); + UpdateIndexData (sub_ch->index); + } + } + frame += 150; + UpdateTimeData ((unsigned char) (frame / (60*75)), + (unsigned char) ((frame % (60*75)) / 75), + (unsigned char) (frame % 75)); +} + +unsigned cdtracks = 0; + +int no_disguised_audiotracks(void) +{ + /* we can assume no audio tracks according to toc here. */ + /* read a data sector from the first data track */ + unsigned char p[3000]; + int retval; + get_scsi_p()->silent++; + retval = 1 == ReadCdRomData(get_scsi_p(), p, Get_StartSector(1), 1); + get_scsi_p()->silent--; + if (retval == 0) { + int i; +fprintf(stderr, "Warning: wrong track types found: patching to audio...\n"); + for (i = 0; i < cdtracks; i++) + patch_to_audio(i); + } + return retval; +} + + +#undef SIM_ILLLEADOUT +int ReadToc(void) +{ + int retval = (*doReadToc)( get_scsi_p() ); +#if defined SIM_ILLLEADOUT + g_toc[cdtracks+1] = 20*75; +#endif + return retval; +} + +static int can_read_illleadout(void); + +static int can_read_illleadout(void) +{ + SCSI *usalp = get_scsi_p(); + + UINT4 buffer [CD_FRAMESIZE_RAW/4]; + if (global.illleadout_cd == 0) return 0; + + usalp->silent++; + global.reads_illleadout = + ReadCdRom(usalp, buffer, Get_AudioStartSector(CDROM_LEADOUT), 1); + usalp->silent--; + return global.reads_illleadout; +} + + +unsigned find_an_off_sector(unsigned lSector, unsigned SectorBurstVal); + +unsigned find_an_off_sector(unsigned lSector, unsigned SectorBurstVal) +{ + long track_of_start = Get_Track(lSector); + long track_of_end = Get_Track(lSector + SectorBurstVal -1); + long start = Get_AudioStartSector(track_of_start); + long end = Get_EndSector(track_of_end); + + if (lSector - start > end - lSector + SectorBurstVal -1) + return start; + else + return end; +} + +#ifdef CD_TEXT +#include "scsi_cmds.h" +#endif + + +int handle_cdtext(void) +{ +#ifdef CD_TEXT + if (bufferTOC[0] == 0 && bufferTOC[1] == 0) { + have_CD_text = 0; + return have_CD_text; + } + + /* do a quick scan over all pack type indicators */ + { + int i; + int count_fails = 0; + int len = (bufferTOC[0] << 8) | bufferTOC[1]; + + len = min(len, 2048); + for (i = 0; i < len-4; i += 18) { + if (bufferTOC[4+i] < 0x80 || bufferTOC[4+i] > 0x8f) { + count_fails++; + } + } + have_CD_text = len > 4 && count_fails < 3; + } + +#else + have_CD_text = 0; +#endif + return have_CD_text; +} + + +#ifdef CD_TEXT +#include "cd_text.c" +#endif + + +#if defined CDROMMULTISESSION +static int tmp_fd; +#endif + +#ifdef CD_EXTRA +#include "cd_extra.c" +#endif + +static unsigned session_start; +/* + A Cd-Extra is detected, if it is a multisession CD with + only audio tracks in the first session and a data track + in the last session. + */ +static unsigned is_multisession(void) +{ + unsigned mult_off; +#if defined CDROMMULTISESSION + /* + * FIXME: we would have to do a ioctl (CDROMMULTISESSION) + * for the cdrom device associated with the generic device + * not just AUX_DEVICE + */ + struct cdrom_multisession ms_str; + + if (interface == GENERIC_SCSI) + tmp_fd = open (global.aux_name, O_RDONLY); + else + tmp_fd = global.cooked_fd; + + if (tmp_fd != -1) { + int result; + + ms_str.addr_format = CDROM_LBA; + result = ioctl(tmp_fd, CDROMMULTISESSION, &ms_str); + if (result == -1) { + if (global.verbose != 0) + perror("multi session ioctl not supported: "); + } else { +#ifdef DEBUG_XTRA + fprintf(stderr, "current ioctl multisession_offset = %u\n", ms_str.addr.lba); +#endif + if (interface == GENERIC_SCSI) + close (tmp_fd); + if (ms_str.addr.lba > 0) + return ms_str.addr.lba; + } + } +#endif + mult_off = 0; + if (LastAudioTrack() + 1 == FirstDataTrack()) { + mult_off = Get_StartSector(FirstDataTrack()); + } + +#ifdef DEBUG_XTRA + fprintf(stderr, "current guessed multisession_offset = %u\n", mult_off); +#endif + return mult_off; +} + +#define SESSIONSECTORS (152*75) +/* + The solution is to read the Table of Contents of the first + session only (if the drive permits that) and directly use + the start of the leadout. If this is not supported, we subtract + a constant of SESSIONSECTORS sectors (found heuristically). + */ +static unsigned get_end_of_last_audio_track(unsigned mult_off) +{ + unsigned retval; + + /* Try to read the first session table of contents. + This works for Sony and mmc type drives. */ + if (ReadLastAudio && (retval = ReadLastAudio(get_scsi_p())) != 0) { + return retval; + } else { + return mult_off - SESSIONSECTORS; + } +} + +static void dump_cdtext_info(void); + +#if defined CDDB_SUPPORT +static void emit_cddb_form(char *fname_baseval); +#endif + +#if defined CDINDEX_SUPPORT +static void emit_cdindex_form(char *fname_baseval); +#endif + + +typedef struct TOC { /* structure of table of contents (cdrom) */ + unsigned char reserved1; + unsigned char bFlags; + unsigned char bTrack; + unsigned char reserved2; + unsigned int dwStartSector; + int mins; + int secs; + int frms; + unsigned char ISRC[16]; + int SCMS; +} TOC; + + +/* Flags contains two fields: + bits 7-4 (ADR) + : 0 no sub-q-channel information + : 1 sub-q-channel contains current position + : 2 sub-q-channel contains media catalog number + : 3 sub-q-channel contains International Standard + Recording Code ISRC + : other values reserved + bits 3-0 (Control) : + bit 3 : when set indicates there are 4 audio channels else 2 channels + bit 2 : when set indicates this is a data track else an audio track + bit 1 : when set indicates digital copy is permitted else prohibited + bit 0 : when set indicates pre-emphasis is present else not present + */ + +#define GETFLAGS(x) ((x)->bFlags) +#define GETTRACK(x) ((x)->bTrack) +#define GETSTART(x) ((x)->dwStartSector) +#define GETMINS(x) ((x)->mins) +#define GETSECS(x) ((x)->secs) +#define GETFRAMES(x) ((x)->frms) +#define GETISRC(x) ((x)->ISRC) + +#define IS__PREEMPHASIZED(p) ( (GETFLAGS(p) & 0x10) != 0) +#define IS__INCREMENTAL(p) ( (GETFLAGS(p) & 0x10) != 0) +#define IS__COPYRESTRICTED(p) (!(GETFLAGS(p) & 0x20) != 0) +#define IS__COPYRIGHTED(p) (!(GETFLAGS(p) & 0x20) != 0) +#define IS__DATA(p) ( (GETFLAGS(p) & 0x40) != 0) +#define IS__AUDIO(p) (!(GETFLAGS(p) & 0x40) != 0) +#define IS__QUADRO(p) ( (GETFLAGS(p) & 0x80) != 0) + +/* + * Iterator interface inspired from Java + */ +struct iterator { + int index; + int startindex; + void (*reset)(struct iterator *this); + struct TOC *(*getNextTrack)(struct iterator *this); + int (*hasNextTrack)(struct iterator *this); +}; + + + + +/* The Table of Contents needs to be corrected if we + have a CD-Extra. In this case all audio tracks are + followed by a data track (in the second session). + Unlike for single session CDs the end of the last audio + track cannot be set to the start of the following + track, since the lead-out and lead-in would then + errenously be part of the audio track. This would + lead to read errors when trying to read into the + lead-out area. + So the length of the last track in case of Cd-Extra + has to be fixed. + */ +unsigned FixupTOC(unsigned no_tracks) +{ + unsigned mult_off; + unsigned offset = 0; + int j = -1; + unsigned real_end = 2000000; + + /* get the multisession offset in sectors */ + mult_off = is_multisession(); + + /* if the first track address had been the victim of an underflow, + * set it to zero. + */ + if (Get_StartSector(1) > Get_StartSector(LastTrack())) { + fprintf(stderr, "Warning: first track has negative start sector! Setting to zero.\n"); + toc_entry( 1, Get_Flags(1), Get_Tracknumber(1), Get_ISRC(1), 0, 0, 2, 0 ); + } + +#ifdef DEBUG_XTRA + fprintf(stderr, "current multisession_offset = %u\n", mult_off); +#endif + dump_cdtext_info(); + + if (mult_off > 100) { /* the offset has to have a minimum size */ + + /* believe the multisession offset :-) */ + /* adjust end of last audio track to be in the first session */ + real_end = get_end_of_last_audio_track(mult_off); +#ifdef DEBUG_XTRA + fprintf(stderr, "current end = %u\n", real_end); +#endif + + j = FirstDataTrack(); + if (LastAudioTrack() + 1 == j) { + long sj = Get_StartSector(j); + if (sj > (long)real_end) { + session_start = mult_off; + have_multisession = sj; + +#ifdef CD_EXTRA + offset = Read_CD_Extra_Info(sj); + + if (offset != 0) { + have_CD_extra = sj; + dump_extra_info(offset); + } +#endif + } + } + } + if (global.cddbp) { +#if defined USE_REMOTE + if (global.disctitle == NULL) { + have_CDDB = !request_titles(); + } +#else + fprintf(stderr, "Cannot lookup titles: no cddbp support included!\n"); +#endif + } +#if defined CDINDEX_SUPPORT || defined CDDB_SUPPORT + if (have_CD_text || have_CD_extra || have_CDDB) { + unsigned long count_audio_tracks = 0; + static struct iterator i; + if (i.reset == NULL) + InitIterator(&i, 1); + + while (i.hasNextTrack(&i)) { + struct TOC *p = i.getNextTrack(&i); + if (IS__AUDIO(p)) count_audio_tracks++; + } + + if (count_audio_tracks > 0 && global.no_cddbfile == 0) { +#if defined CDINDEX_SUPPORT + emit_cdindex_form(global.fname_base); +#endif +#if defined CDDB_SUPPORT + emit_cddb_form(global.fname_base); +#endif + } + } +#endif + if (have_multisession) { + /* set start of track to beginning of lead-out */ + patch_cd_extra(j, real_end); +#if defined CD_EXTRA && defined DEBUG_XTRA + fprintf(stderr, "setting end of session (track %d) to %u\n", j, real_end); +#endif + } + return offset; +} + +static int cddb_sum(int n) +{ + int ret; + + for (ret = 0; n > 0; n /= 10) { + ret += (n % 10); + } + + return ret; +} + +void calc_cddb_id(void) +{ + UINT4 i; + UINT4 t = 0; + UINT4 n = 0; + + for (i = 1; i <= cdtracks; i++) { + n += cddb_sum(Get_StartSector(i)/75 + 2); + } + + t = Get_StartSector(i)/75 - Get_StartSector(1)/75; + + global.cddb_id = (n % 0xff) << 24 | (t << 8) | cdtracks; +} + + +#undef TESTCDINDEX +#ifdef TESTCDINDEX +void TestGenerateId(void) +{ + SHA_INFO sha; + unsigned char digest[20], *base64; + unsigned long size; + + sha_init(&sha); + sha_update(&sha, (unsigned char *)"0123456789", 10); + sha_final(digest, &sha); + + base64 = rfc822_binary((char *)digest, 20, &size); + if (strncmp((char*) base64, "h6zsF82dzSCnFsws9nQXtxyKcBY-", size)) + { + free(base64); + + fprintf(stderr, "The SHA-1 hash function failed to properly generate the\n"); + fprintf(stderr, "test key.\n"); + exit(INTERNAL_ERROR); + } + free(base64); +} +#endif + +void calc_cdindex_id() +{ + SHA_INFO sha; + unsigned char digest[20], *base64; + unsigned long size; + unsigned i; + char temp[9]; + +#ifdef TESTCDINDEX + TestGenerateId(); + g_toc[1].bTrack = 1; + cdtracks = 15; + g_toc[cdtracks].bTrack = 15; + i = 1; + g_toc[i++].dwStartSector = 0U; + g_toc[i++].dwStartSector = 18641U; + g_toc[i++].dwStartSector = 34667U; + g_toc[i++].dwStartSector = 56350U; + g_toc[i++].dwStartSector = 77006U; + g_toc[i++].dwStartSector = 106094U; + g_toc[i++].dwStartSector = 125729U; + g_toc[i++].dwStartSector = 149785U; + g_toc[i++].dwStartSector = 168885U; + g_toc[i++].dwStartSector = 185910U; + g_toc[i++].dwStartSector = 205829U; + g_toc[i++].dwStartSector = 230142U; + g_toc[i++].dwStartSector = 246659U; + g_toc[i++].dwStartSector = 265614U; + g_toc[i++].dwStartSector = 289479U; + g_toc[i++].dwStartSector = 325732U; +#endif + sha_init(&sha); + sprintf(temp, "%02X", Get_Tracknumber(1)); + sha_update(&sha, (unsigned char *)temp, 2); + sprintf(temp, "%02X", Get_Tracknumber(cdtracks)); + sha_update(&sha, (unsigned char *)temp, 2); + + /* the position of the leadout comes first. */ + sprintf(temp, "%08lX", 150 + Get_StartSector(CDROM_LEADOUT)); + sha_update(&sha, (unsigned char *)temp, 8); + + /* now 99 tracks follow with their positions. */ + for (i = 1; i <= cdtracks; i++) { + sprintf(temp, "%08lX", 150+Get_StartSector(i)); + sha_update(&sha, (unsigned char *)temp, 8); + } + for (i++ ; i <= 100; i++) { + sha_update(&sha, (unsigned char *)"00000000", 8); + } + sha_final(digest, &sha); + + base64 = rfc822_binary((char *)digest, 20, &size); + global.cdindex_id = base64; +} + + +#if defined CDDB_SUPPORT + +#ifdef PROTOTYPES +static void escape_and_split(FILE *channel, const char *args, ...) +#else +/*VARARGS3*/ +static void escape_and_split(FILE *channel, const char *args, va_dcl va_alist) +#endif +{ + va_list marker; + + int prefixlen; + int len; + char *q; + +#ifdef PROTOTYPES + va_start(marker, args); +#else + va_start(marker); +#endif + + prefixlen = strlen(args); + len = prefixlen; + fputs(args, channel); + + q = va_arg(marker, char *); + while (*q != '\0') { + while (*q != '\0') { + len += 2; + if (*q == '\\') + fputs("\\\\", channel); + else if (*q == '\t') + fputs("\\t", channel); + else if (*q == '\n') + fputs("\\n", channel); + else { + fputc(*q, channel); + len--; + } + if (len > 78) { + fputc('\n', channel); + fputs(args, channel); + len = prefixlen; + } + q++; + } + q = va_arg(marker, char *); + } + fputc('\n', channel); + + va_end(marker); +} + +static void emit_cddb_form(char *fname_baseval) +{ + static struct iterator i; + unsigned first_audio; + FILE *cddb_form; + char fname[200]; + char *pp; + + if (fname_baseval == NULL || fname_baseval[0] == 0) + return; + + if (!strcmp(fname_baseval,"standard_output")) return; + InitIterator(&i, 1); + + strncpy(fname, fname_baseval, sizeof(fname) -1); + fname[sizeof(fname) -1] = 0; + pp = strrchr(fname, '.'); + if (pp == NULL) { + pp = fname + strlen(fname); + } + strncpy(pp, ".cddb", sizeof(fname) - 1 - (pp - fname)); + + cddb_form = fopen(fname, "w"); + if (cddb_form == NULL) return; + + first_audio = FirstAudioTrack(); + fprintf( cddb_form, "# xmcd\n#\n"); + fprintf( cddb_form, "# Track frame offsets:\n#\n"); + + while (i.hasNextTrack(&i)) { + struct TOC *p = i.getNextTrack(&i); + if (GETTRACK(p) == CDROM_LEADOUT) break; + fprintf( cddb_form, + "# %lu\n", 150 + Get_AudioStartSector(GETTRACK(p))); + } + + fprintf( cddb_form, "#\n# Disc length: %lu seconds\n#\n", + (150 + Get_StartSector(CDROM_LEADOUT)) / 75); + fprintf( cddb_form, "# Revision: %u\n", global.cddb_revision ); + fprintf( cddb_form, "# Submitted via: icedax " VERSION "\n" ); + + fprintf( cddb_form, "DISCID=%08lx\n", (unsigned long)global.cddb_id); + + if (global.disctitle == NULL && global.creator == NULL) { + fprintf( cddb_form, "DTITLE=\n"); + } else { + if (global.creator == NULL) { + escape_and_split( cddb_form, "DTITLE=", global.disctitle, ""); + } else if (global.disctitle == NULL) { + escape_and_split( cddb_form, "DTITLE=", global.creator, ""); + } else { + escape_and_split( cddb_form, "DTITLE=", global.creator, " / ", global.disctitle, ""); + } + } + if (global.cddb_year != 0) + fprintf( cddb_form, "DYEAR=%4u\n", global.cddb_year); + else + fprintf( cddb_form, "DYEAR=\n"); + fprintf( cddb_form, "DGENRE=%s\n", global.cddb_genre); + + i.reset(&i); + while (i.hasNextTrack(&i)) { + struct TOC *p = i.getNextTrack(&i); + int ii; + + ii = GETTRACK(p); + if (ii == CDROM_LEADOUT) break; + + if (global.tracktitle[ii] != NULL) { + char prefix[10]; + sprintf(prefix, "TTITLE%d=", ii-1); + escape_and_split( cddb_form, prefix, global.tracktitle[ii], ""); + } else { + fprintf( cddb_form, "TTITLE%d=\n", ii-1); + } + } + + if (global.copyright_message == NULL) { + fprintf( cddb_form, "EXTD=\n"); + } else { + escape_and_split( cddb_form, "EXTD=", "Copyright ", global.copyright_message, ""); + } + + i.reset(&i); + while (i.hasNextTrack(&i)) { + struct TOC *p = i.getNextTrack(&i); + int ii; + + ii = GETTRACK(p); + + if (ii == CDROM_LEADOUT) break; + + fprintf( cddb_form, "EXTT%d=\n", ii-1); + } + fprintf( cddb_form, "PLAYORDER=\n"); + fclose( cddb_form ); +} + +#if defined USE_REMOTE +#include + +static int readn(register int fd, register char *ptr, register int nbytes) +{ + int nread; + + nread = read(fd, ptr, nbytes); +#ifdef DEBUG_CDDBP + if (nread > 0) { + fprintf(stderr, "READ :(%d)", nread); + write(2, ptr, nread); + } +#endif + if (nread < 0) { + perror("socket read error: "); + fprintf(stderr, "fd=%d, ptr=%p, nbytes=%d\n", fd, ptr, nbytes); + } + + return nread; +} + +static ssize_t writez(int fd, const char *ptr) +{ + size_t nleft, nbytes; + + nleft = nbytes = strlen(ptr); + + while (nleft > 0) { + ssize_t nwritten = write(fd, ptr, nleft); + if (nwritten <= 0) { + return nwritten; /* return error */ + } +#ifdef DEBUG_CDDBP + fprintf(stderr, "WRITE:%s\n", ptr); +#endif + + nleft -= nwritten; + ptr += nwritten; + } + + return nbytes - nleft; +} + +#define SOCKBUFF 2048 + +static void filter_nonprintable(char *c, size_t l) +{ + size_t i; + for(i = 0; i < l; ++i) { + if(!isprint(c[i]) && !isspace(c[i])) { + c[i] = '_'; + } + } +} + + +int process_cddb_titles(int sock_fd, char *inbuff, int readbytes); +int process_cddb_titles(int sock_fd, char *inbuff, int readbytes) +{ + int finished = 0; + char *p = inbuff; + int ind = 0; + unsigned char ** target = &global.creator; + + do { + while (readbytes > 0) { + /* do we have a complete line in the buffer? */ + p = (char *)memchr(inbuff+ind, '\n', readbytes); + if (p == NULL) break; + + /* look for the terminator first */ + if (!strncmp(".\r\n", inbuff+ind, 3)) { + finished = 1; + break; + } + /* kill carriage return */ + if (p > inbuff+ind && *(p-1) == '\r') { + *(p-1) = '\0'; + } + /* kill line feed */ + *p = '\0'; + + /* handle escaped characters */ + + { + char *q = inbuff+ind; + while (*q) { + if (*q++ == '\\' && *q != '\0') { + if (*q == '\\') { + readbytes--; + p--; + memmove(q, q+1, readbytes - (q-inbuff-ind)); + } else if (*q == 'n') { + *(q-1) = '\n'; + readbytes--; + p--; + memmove(q, q+1, readbytes - (q-inbuff-ind)); + } else if (*q == 't') { + *(q-1) = '\t'; + readbytes--; + p--; + memmove(q, q+1, readbytes - (q-inbuff-ind)); + } + } + } + + } + + /* handle multi line entries concatenate fields */ + +/* TODO if the delimiter is split into two lines, it is not recognized. */ + if (!strncmp(inbuff+ind, "DTITLE=", 7)) { + char *res = strstr(inbuff+ind+7, " / "); + int clen; + char *q; + + if (res == NULL) { + /* no limiter found yet */ + /* copy until the end */ + q = p; + } else { + /* limiter found */ + /* copy until the limiter */ + q = res; + *q = '\0'; + } + + clen = q - (inbuff+ind+7); + if (*target == NULL) { + *target = malloc(clen+1); + if (*target != NULL) + **target = '\0'; + } else { + *target = realloc(*target, strlen((char *)*target) + clen - 1); + } + if (*target != NULL) { + strcat((char *)*target, inbuff+ind+7); + } + + /* handle part after the delimiter, if present */ + if (res != NULL) { + target = (unsigned char **)&global.disctitle; + /* skip the delimiter */ + q += 3; + clen = p - q; + if (*target == NULL) { + *target = malloc(clen+1); + if (*target != NULL) + **target = '\0'; + } + if (*target != NULL) { + strcat((char *)*target, q); + } + } + } else if (!strncmp(inbuff+ind, "TTITLE", 6)) { + char *q = (char *)memchr(inbuff+ind, '=', readbytes); + unsigned tno; + + if (q != NULL) { + *q = '\0'; + tno = (unsigned)atoi(inbuff+ind+6); + tno++; + if (tno < 100) { + if (global.tracktitle[tno] == NULL) { + global.tracktitle[tno] = malloc( p - q + 1 ); + if (global.tracktitle[tno] != NULL) + *(global.tracktitle[tno]) = '\0'; + } else { + global.tracktitle[tno] = realloc(global.tracktitle[tno], strlen((char *)global.tracktitle[tno]) + p - q + 1 ); + } + if (global.tracktitle[tno] != NULL) { + strcat((char *)global.tracktitle[tno], q+1); + } + } + } + } else if (!strncmp(inbuff+ind, "DYEAR", 5)) { + char *q = (char *)memchr(inbuff+ind, '=', readbytes); + if (q++ != NULL) { + sscanf(q, "%d", &global.cddb_year); + } + } else if (!strncmp(inbuff+ind, "DGENRE", 6)) { + char *q = (char *)memchr(inbuff+ind, '=', readbytes); + if (q++ != NULL) { + /* patch from Joe Nuzman, thanks */ + /* might have significant whitespace */ + strncpy(global.cddb_genre, q, sizeof(global.cddb_genre)-1); + /* always have a terminator */ + global.cddb_genre[sizeof(global.cddb_genre)-1] = '\0'; + } + } else if (!strncmp(inbuff+ind, "# Revision: ", 12)) { + char *q = inbuff+ind+11; + sscanf(q, "%d", &global.cddb_revision); + global.cddb_revision++; + } + readbytes -= (p - inbuff -ind) + 1; + ind = (p - inbuff) + 1; + } + if (!finished) { + int newbytes; + memmove(inbuff, inbuff+ind, readbytes); + newbytes = readn(sock_fd, inbuff+readbytes, SOCKBUFF-readbytes); + if (newbytes < 0) { + fprintf(stderr, "Could not read from socket.\n"); + return 0; /* Caller checks for != 1 */ + } + filter_nonprintable(inbuff+readbytes, newbytes); + if (newbytes <= 0) + break; + readbytes += newbytes; + ind = 0; + } + } while (!(finished || readbytes == 0)); + return finished; +} + +static int handle_userchoice(char *p, unsigned size); + +static int handle_userchoice(char *p, unsigned size) +{ + unsigned nr = 0; + unsigned user_choice; + int i; + char *q; + char *o; + + /* count lines. */ + q = p; + while ((q = (char *)memchr(q, '\n', size - (q-p))) != NULL) { + nr++; + q++; + } + if (nr > 1) nr--; + + /* handle escaped characters */ + + { + char *r = p; + while (*r) { + if (*r++ == '\\' && *r != '\0') { + if (*r == '\\') { + size--; + memmove(r, r+1, size - (r-p)); + } else if (*r == 'n') { + *(r-1) = '\n'; + size--; + memmove(r, r+1, size - (r-p)); + } else if (*r == 't') { + *(r-1) = '\t'; + size--; + memmove(r, r+1, size - (r-p)); + } + } + } + } + + /* list entries. */ + q = p; + fprintf(stderr, "%u entries found:\n", nr); + for (q = (char *)memchr(q, '\n', size - (q-p)), o = p, i = 0; i < nr; i++) { + *q = '\0'; + fprintf(stderr, "%02u: %s\n", i, o); + o = q+1; + q = (char *)memchr(q, '\n', size - (q-p)); + } + fprintf(stderr, "%02u: ignore\n", i); + + /* get user response. */ + do { + fprintf(stderr, "please choose one (0-%u): ", nr); + do{int ret;ret=scanf("%u", &user_choice);}while(0); /* FIXME: check return value */ + } while (user_choice > nr); + + if (user_choice == nr) + return -1; + + /* skip to choice. */ + q = p; + for (i = 0; i <= (int)user_choice - 1; i++) { + q = (char *)memchr(q, '\0', size - (q-p)) + 1; + } + return q-p; +} + +/* request disc and track titles from a cddbp server. + * + * return values: + * 0 titles have been found exactly (success) + * -1 some communication error happened. + * 1 titles have not been found. + * 2 multiple fuzzy matches have been found. + */ +int +request_titles(void) +{ + int retval = 0; + int sock_fd; + struct sockaddr_in sa; + struct hostent *he; + struct servent *se; + struct passwd *pw = getpwuid(getuid()); + char hostname[HOST_NAME_MAX]; + char inbuff[SOCKBUFF]; + char outbuff[SOCKBUFF]; + int i; + char category[64]; + unsigned cat_offset; + unsigned disc_id; + ssize_t readbytes; + + sock_fd = socket(AF_INET, SOCK_STREAM, 0); + if (sock_fd < 0) { + perror("cddb socket failed: "); + retval = -1; + goto errout; + } + + /* TODO fallbacks + * freedb.freedb.org + * de.freedb.org + * at.freedb.org + */ + if (global.cddbp_server != NULL) + he = gethostbyname(global.cddbp_server); + else + he = gethostbyname(CDDBHOST /*"freedb.freedb.org"*/); + + if (he == NULL) { + perror("cddb cannot resolve freedb host: "); + he = malloc(sizeof(struct hostent)); + memset(he, 0 , sizeof(struct hostent)); + he->h_length = 4; + he->h_addrtype = AF_INET; + he->h_addr_list = malloc(4); + he->h_addr_list[0] = malloc(4); + ((struct in_addr *)(he->h_addr_list[0]))->s_addr = + /* kingfisher.berlios.de freedb.freedb.de */ + htonl(UINT_C(0xc3254d85)); /*0xc2610412*/ + he->h_name = "freedb.freedb.org"; +#if 0 + retval = -1; + goto errout; +#endif + } + + /* save result data IMMEDIATELY!! */ + memset(&sa, 0 , sizeof(struct sockaddr_in)); + sa.sin_family = he->h_addrtype; /* AF_INET; */ + sa.sin_addr.s_addr = ((struct in_addr *)((he->h_addr_list)[0]))->s_addr; + + se = NULL; + if (global.cddbp_port == NULL) + se = getservbyname("cddbp-alt", "tcp"); + + if (se == NULL) { + if (global.cddbp_port == NULL) { + se = getservbyname("cddbp", "tcp"); + } + if (se == NULL) { + se = malloc(sizeof(struct servent)); + memset(se, 0 , sizeof(struct servent)); + se->s_port = htons(CDDBPORT /*8880*/); +#if 0 + perror("cddb cannot resolve cddbp or cddbp-alt port:\n "); + retval = -1; + goto errout; +#endif + } + } + if (global.cddbp_port != NULL) { + se->s_port = htons(atoi(global.cddbp_port)); + } + + sa.sin_port = se->s_port; + +/* TODO timeout */ + if (0 > connect(sock_fd, (struct sockaddr *)&sa, + sizeof(struct sockaddr_in))) { + perror("cddb connect failed: "); + retval = -1; + goto errout; + } + + /* read banner */ + readbytes = readn(sock_fd, inbuff, sizeof(inbuff)); + if (readbytes < 0) { + fprintf(stderr, "Could not read from socket\n"); + retval = -1; + goto errout; + } + + if (strncmp(inbuff, "200 ", 4) && strncmp(inbuff, "201 ", 4)) { + if(readbytes == sizeof(inbuff)) + --readbytes; + inbuff[readbytes] = '\0'; + filter_nonprintable(inbuff, readbytes); + fprintf(stderr, "bad status from freedb server during sign-on banner: %s\n", inbuff); + retval = -1; + goto errout; + } + + /* say hello */ + hostname[0] = '\0'; + if (0 > gethostname(hostname, sizeof(hostname))) + strcpy(hostname, "unknown_host"); + hostname[sizeof(hostname)-1] = '\0'; + writez(sock_fd, "cddb hello "); + if (pw != NULL) { + BOOL space_err = FALSE; + BOOL ascii_err = FALSE; + /* change spaces to underscores */ + char *q = pw->pw_name; + while (*q != '\0') { + if (*q == ' ') { + if (!space_err) { + space_err = TRUE; + errmsgno(EX_BAD, + "Warning: Space in user name '%s'.\n", + pw->pw_name); + } + *q = '_'; + } + if (*q < ' ' || *q > '~') { + if (!ascii_err) { + ascii_err = TRUE; + errmsgno(EX_BAD, + "Warning: Nonascii character in user name '%s'.\n", + pw->pw_name); + } + *q = '_'; + } + q++; + } + writez(sock_fd, pw->pw_name); + writez(sock_fd, " "); + } else { + writez(sock_fd, "unknown "); + } + + /* change spaces to underscores */ + { + char *q = hostname; + BOOL space_err = FALSE; + BOOL ascii_err = FALSE; + + while (*q != '\0') { + if (*q == ' ') { + if (!space_err) { + space_err = TRUE; + errmsgno(EX_BAD, + "Warning: Space in hostname '%s'.\n", + hostname); + } + *q = '_'; + } + if (*q < ' ' || *q > '~') { + if (!ascii_err) { + ascii_err = TRUE; + errmsgno(EX_BAD, + "Warning: Nonascii character in hostname '%s'.\n", + hostname); + } + *q = '_'; + } + q++; + } + } + + writez(sock_fd, hostname); + writez(sock_fd, " icedax " VERSION "\n"); + + readbytes = readn(sock_fd, inbuff, sizeof(inbuff)); + if (readbytes < 0) { + fprintf(stderr, "Could not read from socket\n"); + retval = -1; + goto errout; + } + if (strncmp(inbuff, "200 ", 4)) { + if(readbytes == sizeof(inbuff)) + --readbytes; + inbuff[readbytes] = '\0'; + filter_nonprintable(inbuff, readbytes); + fprintf(stderr, "bad status from freedb server during hello: %s\n", inbuff); + retval = -1; + goto signoff; + } + + /* enable new protocol variant. Weird command here, no cddb prefix ?!?! */ + writez(sock_fd, "proto\n"); + readbytes = readn(sock_fd, inbuff, sizeof(inbuff)); + if (readbytes < 0) { + fprintf(stderr, "Could not read from socket\n"); + retval = -1; + goto errout; + } + /* check for errors and maximum supported protocol level */ + if (strncmp(inbuff, "201 ", 4) > 0) { + if(readbytes == sizeof(inbuff)) + --readbytes; + inbuff[readbytes] = '\0'; + filter_nonprintable(inbuff, readbytes); + fprintf(stderr, "bad status from freedb server during proto command: %s\n", inbuff); + retval = -1; + goto signoff; + } + + /* check the supported protocol level */ + if (!memcmp(inbuff, "200 CDDB protocol level: current 1, supported ", 46)) { + char *q = strstr(inbuff, " supported "); + unsigned pr_level; + + if (q != NULL) { + q += 11; + sscanf(q, "%u\n", &pr_level); + if (pr_level > 1) { + if (pr_level > 5) + pr_level = 5; + sprintf(inbuff, "proto %1u\n", pr_level); + writez(sock_fd, inbuff); + readbytes = readn(sock_fd, inbuff, sizeof(inbuff)); + if (readbytes < 0) { + fprintf(stderr, "Could not read from socket\n"); + retval = -1; + goto errout; + } + /* check for errors and maximum supported protocol level */ + if (strncmp(inbuff, "201 ", 4) > 0) { + if(readbytes == sizeof(inbuff)) + --readbytes; + inbuff[readbytes] = '\0'; + filter_nonprintable(inbuff, + readbytes); + fprintf(stderr, "bad status from freedb server during proto x: %s\n", inbuff); + retval = -1; + goto signoff; + } + } + } + } + + /* format query string */ + /* query */ +#define CDDPB_INCLUDING_DATATRACKS +#ifdef CDDPB_INCLUDING_DATATRACKS + sprintf(outbuff, "cddb query %08lx %ld ", (unsigned long)global.cddb_id, LastTrack() - FirstTrack() + 1); + /* first all leading datatracks */ + { + int j = FirstAudioTrack(); + if (j < 0) + j = LastTrack() +1; + for (i = FirstTrack(); i < j; i++) { + sprintf(outbuff + strlen(outbuff), "%ld ", 150 + Get_StartSector(i)); + } + } +#else + sprintf(outbuff, "cddb query %08lx %ld ", global.cddb_id, LastAudioTrack() - FirstAudioTrack() + 1); +#endif + /* all audio tracks */ + for (i = FirstAudioTrack(); i != -1 && i <= LastAudioTrack(); i++) { + sprintf(outbuff + strlen(outbuff), "%ld ", 150 + Get_AudioStartSector(i)); + } +#ifdef CDDPB_INCLUDING_DATATRACKS + /* now all trailing datatracks */ + for (; i != -1 && i <= LastTrack(); i++) { + sprintf(outbuff + strlen(outbuff), "%ld ", 150 + Get_StartSector(i)); + } + sprintf(outbuff + strlen(outbuff), "%lu\n", + (150 + Get_StartSector(CDROM_LEADOUT)) / 75); +#else + sprintf(outbuff + strlen(outbuff), "%lu\n", + (150 + Get_LastSectorOnCd(FirstAudioTrack())) / 75); +#endif +/* strcpy(outbuff, "cddb query 9709210c 12 150 12010 33557 50765 65380 81467 93235 109115 124135 137732 152575 166742 2339\n"); */ +/* strcpy(outbuff, "cddb query 03015501 1 296 344\n"); */ + writez(sock_fd, outbuff); + + readbytes = readn(sock_fd, inbuff, sizeof(inbuff) - 1); + if (readbytes < 0) { + fprintf(stderr, "Could not read from socket\n"); + retval = -1; + goto errout; + } + inbuff[readbytes] = '\0'; + filter_nonprintable(inbuff, readbytes); + cat_offset = 4; + if (!strncmp(inbuff, "210 ", 4) + || !strncmp(inbuff, "211 ", 4)) { + /* Check if there are really multiple entries. */ + char *p = (char *)memchr(inbuff, '\n', readbytes-1); + + if (p != NULL) cat_offset = p+1 - inbuff; + /* first entry */ + if (p) p = (char *)memchr(p+1, '\n', inbuff+readbytes - p); + /* second entry */ + if (p) p = (char *)memchr(p+1, '\n', inbuff+readbytes - p); + /* . */ + if (p) p = (char *)memchr(p+1, '\n', inbuff+readbytes - p); + if (p) { + /* multiple entries */ + switch (global.cddbp) { + case 2: /* take the first entry */ + break; + case 1: /* ask user */ + if (!global.gui) { + int userret = handle_userchoice(inbuff+cat_offset, readbytes - cat_offset); + if (userret == -1) { + /* ignore any selection */ + retval = -1; + goto signoff; + } + cat_offset += userret; + } + break; + default: + fprintf(stderr, "multiple entries found: %s\n", inbuff); + retval = 2; + goto signoff; + } + } + + } else if (strncmp(inbuff, "200 ", 4)) { + if (!strncmp(inbuff, "202 ", 4)) { + fprintf(stderr, "no cddb entry found: %s\n", inbuff); + retval = 1; + } else { + fprintf(stderr, "bad status from freedb server during query: %s\n%s", inbuff, outbuff); + retval = -1; + } + goto signoff; + } + sscanf(inbuff + cat_offset, "%s %x", category, &disc_id ); + + + /* read */ + sprintf(inbuff, "cddb read %s %08x\n", category, disc_id); + writez(sock_fd, inbuff); + + /* read status and first buffer size. */ + readbytes = readn(sock_fd, inbuff, sizeof(inbuff)); + if (readbytes < 0) { + fprintf(stderr, "Could not read from socket\n"); + retval = -1; + goto errout; + } + filter_nonprintable(inbuff, readbytes); + if (strncmp(inbuff, "210 ", 4)) { + if(readbytes == sizeof(inbuff)) + --readbytes; + inbuff[readbytes] = '\0'; + fprintf(stderr, "bad status from freedb server during read: %s\n", inbuff); + retval = -1; + goto signoff; + } + + if (1 != process_cddb_titles(sock_fd, inbuff, readbytes)) { + fprintf(stderr, "cddb read finished not correctly!\n"); + } + +signoff: + /* sign-off */ + writez(sock_fd, "quit\n"); + readbytes = readn(sock_fd, inbuff, sizeof(inbuff)); + if (readbytes < 0) { + fprintf(stderr, "Could not read from socket\n"); + retval = -1; + goto errout; + } + if (strncmp(inbuff, "230 ", 4)) { + if(readbytes == sizeof(inbuff)) + --readbytes; + inbuff[readbytes] = '\0'; + filter_nonprintable(inbuff, readbytes); + fprintf(stderr, "bad status from freedb server during quit: %s\n", inbuff); + goto errout; + } + +errout: + close(sock_fd); + return retval; +} +#endif +#endif + +#if defined CDINDEX_SUPPORT + +static int IsSingleArtist(void); + +/* check, if there are more than one track creators */ +static int IsSingleArtist(void) +{ + static struct iterator i; + InitIterator(&i, 1); + + while (i.hasNextTrack(&i)) { + struct TOC *p = i.getNextTrack(&i); + int ii; + + if (IS__DATA(p) || GETTRACK(p) == CDROM_LEADOUT) continue; + + ii = GETTRACK(p); + if (global.creator && global.trackcreator[ii] + && strcmp((char *) global.creator, + (char *) global.trackcreator[ii]) != 0) + return 0; + } + return 1; +} + +static const char *a2h[255-191] = { +"À", +"Á", +"Â", +"Ã", +"Ä", +"Å", +"Æ", +"Ç", +"È", +"É", +"Ê", +"Ë", +"Ì", +"Í", +"Î", +"Ï", +"Ð", +"Ñ", +"Ò", +"Ó", +"Ô", +"Õ", +"Ö", +"×", +"Ø", +"Ù", +"Ú", +"Û", +"Ü", +"Ý", +"Þ", +"ß", +"à", +"á", +"â", +"ã", +"ä", +"å", +"æ", +"ç", +"è", +"é", +"ê", +"ë", +"ì", +"í", +"î", +"ï", +"ð", +"ñ", +"ò", +"ó", +"ô", +"õ", +"ö", +"÷", +"ø", +"ù", +"ú", +"û", +"ü", +"ý", +"þ", +"ÿ", +}; + +static char *ascii2html(unsigned char *inp) +{ + static size_t buflen = 256; + static char *outline = 0; + size_t pos = 0; + size_t l=0; + + /* init */ + if(!outline) { + outline = malloc(buflen); + if(outline == 0) { + fprintf(stderr, "error: memory exhausted\n"); + _exit(EXIT_FAILURE); + } + } + + outline[pos] = '\0'; + + while (*inp != '\0') { + + /* Pick the sequence to insert */ + const char *insert; + char b[2]; + const int c = (unsigned char)*inp; + switch(c) { + case '"': insert = """; break; + case '&': insert = "&"; break; + case '<': insert = "<"; break; + case '>': insert = ">"; break; + case 160: insert = " "; break; + default: { + if(c < 192) { + b[0] = c; + b[1] = '\0'; + insert = b; + } else { + insert = a2h[c - 192]; + } + } + }; + + /* Resize buffer */ + l = strlen(insert); + while(pos + l + 1 >= buflen) { + outline = realloc(outline, buflen *= 2); + if(outline == 0) { + fprintf(stderr, "error: memory exhausted\n"); + _exit(EXIT_FAILURE); + } + } + + /* Copy in */ + strcpy(&outline[pos], insert); + pos += l; + + ++inp; + } + + return outline; +} + +static void emit_cdindex_form(char *fname_baseval) +{ + FILE *cdindex_form; + char fname[200]; + char *pp; + + if (fname_baseval == NULL || fname_baseval[0] == 0) + return; + + strncpy(fname, fname_baseval, sizeof(fname) -1); + fname[sizeof(fname) -1] = 0; + pp = strrchr(fname, '.'); + if (pp == NULL) { + pp = fname + strlen(fname); + } + strncpy(pp, ".cdindex", sizeof(fname) - 1 - (pp - fname)); + + cdindex_form = fopen(fname, "w"); + if (cdindex_form == NULL) return; + +#define CDINDEX_URL "http://www.musicbrainz.org/dtd/CDInfo.dtd" + + /* format XML page according to cdindex DTD (see www.musicbrainz.org) */ + fprintf( cdindex_form, "\n\n\n\n", + CDINDEX_URL); + + fprintf( cdindex_form, " %s\n", + global.disctitle ? ascii2html(global.disctitle) : ""); + /* + * In case of mixed mode and Extra-CD, nonaudio tracks are included! + */ + fprintf( cdindex_form, " %d\n\n", cdtracks); + fprintf( cdindex_form, " \n \n %s\n \n", global.cdindex_id); + + fprintf( cdindex_form, " \n\n"); + + if (IsSingleArtist()) { + static struct iterator i; + InitIterator(&i, 1); + + fprintf( cdindex_form, " \n %s\n", + global.creator ? ascii2html(global.creator) : ""); + + while (i.hasNextTrack(&i)) { + struct TOC *p = i.getNextTrack(&i); + int ii = GETTRACK(p); + + if (ii == CDROM_LEADOUT) break; + if (IS__AUDIO(p)) { + fprintf( cdindex_form, + " \n %s\n \n", + ii, global.tracktitle[ii] ? ascii2html(global.tracktitle[ii]) : ""); + } else { + fprintf( cdindex_form, + " \n data track\n \n", + ii ); + } + } + fprintf( cdindex_form, " \n"); + } else { + static struct iterator i; + InitIterator(&i, 1); + + fprintf( cdindex_form, " \n"); + + while (i.hasNextTrack(&i)) { + struct TOC *p = i.getNextTrack(&i); + int ii = GETTRACK(p); + + if (ii == CDROM_LEADOUT) break; + if (IS__AUDIO(p)) { + fprintf( cdindex_form, " %s\n", + global.trackcreator[ii] ? ascii2html(global.trackcreator[ii]) : ""); + fprintf( cdindex_form, " %s\n \n", + global.tracktitle[ii] ? ascii2html(global.tracktitle[ii]) : ""); + } else { + fprintf( cdindex_form, + " data track\n data track\n \n"); + } + } + fprintf( cdindex_form, " \n"); + } + fprintf( cdindex_form, "\n"); + + fclose( cdindex_form ); +} +#endif + +static void dump_cdtext_info(void) +{ +#ifdef CD_TEXT + /* interpret the contents of CD Text information based on an early draft + of SCSI-3 mmc version 2 from jan 2, 1998 + CD Text information consists of a text group containing up to + 8 language blocks containing up to + 255 Pack data chunks of + 18 bytes each. + So we have at most 36720 bytes to cope with. + */ + { + short int datalength; + unsigned char *p = bufferTOC; + unsigned char lastline[255*12]; + int lastitem = -1; + int itemcount = 0; + int inlinecount = 0; + int outlinecount = 0; + + lastline[0] = '\0'; + datalength = ((p[0] << 8) + p[1]) - 2; + datalength = min(datalength, 2048-4); + p += 4; + for (;datalength > 0; + datalength -= sizeof (cdtextpackdata), + p += sizeof (cdtextpackdata)) { + unsigned char *zeroposition; + + /* handle one packet of CD Text Information Descriptor Pack Data */ + /* this is raw R-W subchannel data converted to 8 bit values. */ + cdtextpackdata *c = (cdtextpackdata *)p; + int dbcc; + int crc_error; + unsigned tracknr; + +#ifdef DEBUG_CDTEXT + fprintf(stderr, "datalength =%d\n", datalength); +#endif + crc_error = !cdtext_crc_ok(c); + + if (lastitem != c->headerfield[0]) { + itemcount = 0; + lastitem = c->headerfield[0]; + } + + tracknr = c->headerfield[1] & 0x7f; + dbcc = ((unsigned)(c->headerfield[3] & 0x80)) >> 7; /* double byte character code */ + +#if defined DEBUG_CDTEXT + { + int extension_flag; + int sequence_number; + int block_number; + int character_position; + + extension_flag = ((unsigned)(c->headerfield[1] & 0x80)) >> 7; + sequence_number = c->headerfield[2]; + block_number = ((unsigned)(c->headerfield[3] & 0x30)) >> 4; /* language */ + character_position = c->headerfield[3] & 0x0f; + + fprintf(stderr, "CDText: ext_fl=%d, trnr=%u, seq_nr=%d, dbcc=%d, block_nr=%d, char_pos=%d\n", + extension_flag, tracknr, sequence_number, dbcc, block_number, character_position); + } +#endif + + /* print ASCII information */ + memcpy(lastline+inlinecount, c->textdatafield, 12); + inlinecount += 12; + zeroposition = (unsigned char *)memchr(lastline+outlinecount, '\0', inlinecount-outlinecount); + while (zeroposition != NULL) { + process_header(c, tracknr, dbcc, lastline+outlinecount); + outlinecount += zeroposition - (lastline+outlinecount) + 1; + +#if defined DEBUG_CDTEXT + fprintf(stderr, "\tin=%d, out=%d, items=%d, trcknum=%u\n", inlinecount, outlinecount, itemcount, tracknr); +{ int q; for (q= outlinecount; q < inlinecount; q++) fprintf (stderr, "%c", lastline[q] ? lastline[q] : '�'); fputs("\n", stderr); } +#else + if (DETAILED) { + if (crc_error) fputs(" ! uncorr. CRC-Error", stderr); + fputs("\n", stderr); + } +#endif + + itemcount++; + if (itemcount > (int)cdtracks + || (c->headerfield[0] == 0x8f + || (c->headerfield[0] <= 0x8d && c->headerfield[0] >= 0x86))) { + outlinecount = inlinecount; + break; + } + tracknr++; + zeroposition = (unsigned char *)memchr(lastline+outlinecount, '\0', inlinecount-outlinecount); + } + } + } +#endif +} + + + +static void dump_extra_info(unsigned int from) +{ +#ifdef CD_EXTRA + unsigned char *p; + unsigned pos, length; + + if (from == 0) return; + + p = Extra_buffer + 48; + while (*p != '\0') { + pos = GET_BE_UINT_FROM_CHARP(p+2); + length = GET_BE_UINT_FROM_CHARP(p+6); + if (pos == (unsigned)-1) { + pos = from+1; + } else { + pos += session_start; + } + +#ifdef DEBUG_XTRA + if (global.gui == 0 && global.verbose != 0) { + fprintf(stderr, "Language: %c%c (as defined by ISO 639)", *p, *(p+1)); + fprintf(stderr, " at sector %u, len=%u (sessionstart=%u)", pos, length, session_start); + fputs("\n", stderr); + } +#endif + /* dump this entry */ + Read_Subinfo(pos, length); + p += 10; + + if (p + 9 > (Extra_buffer + CD_FRAMESIZE)) + break; + } +#endif +} + +static char *quote(unsigned char *string); + +static char *quote(unsigned char *string) +{ + static char result[200]; + unsigned char *p = (unsigned char *)result; + + while (*string) { + if (*string == '\'' || *string == '\\') { + *p++ = '\\'; + } + *p++ = *string++; + } + *p = '\0'; + + return result; +} + + + +static void DisplayToc_with_gui(unsigned long dw); + +static void DisplayToc_with_gui(unsigned long dw) +{ + unsigned mins; + unsigned secnds; + unsigned frames; + int count_audio_trks; + static struct iterator i; + if (i.reset == NULL) InitIterator(&i, 1); + else i.reset(&i); + + mins = dw / ( 60*75 ); + secnds = ( dw % ( 60*75 ) ) / 75; + frames = ( dw % 75 ); + + /* summary */ + count_audio_trks = 0; + + if ((global.verbose & SHOW_STARTPOSITIONS) != 0) { + if (global.illleadout_cd != 0 && have_CD_extra == 0) { + fprintf( stderr, "Tracks:%u > %u:%02u.%02u\n", cdtracks, mins, secnds, frames ); + } else { + fprintf( stderr, "Tracks:%u %u:%02u.%02u\n", cdtracks, mins, secnds, frames ); + } + } + + if (global.quiet == 0) { + fprintf( stderr, "CDINDEX discid: %s\n", global.cdindex_id); + fprintf( stderr, "CDDB discid: 0x%08lx", (unsigned long) global.cddb_id); + + if (have_CDDB != 0) { + fprintf(stderr, " CDDBP titles: resolved\n"); + } else { + fprintf(stderr, "\n"); + } + if (have_CD_text != 0) { + fprintf(stderr, "CD-Text: detected\n"); + dump_cdtext_info(); + } else { + fprintf(stderr, "CD-Text: not detected\n"); + } + if (have_CD_extra != 0) { + fprintf(stderr, "CD-Extra: detected\n"); + dump_extra_info(have_CD_extra); + } else { + fprintf(stderr, "CD-Extra: not detected\n"); + } + + fprintf( stderr, + "Album title: '%s'", (void *)global.disctitle != NULL + ? quote(global.disctitle) : ""); + + fprintf( stderr, " from '%s'\n", (void *)global.creator != NULL + ? quote(global.creator) : ""); + } + count_audio_trks = 0; + + + if ((global.verbose & (SHOW_TOC | SHOW_STARTPOSITIONS | SHOW_SUMMARY | SHOW_TITLES)) != 0 + && i.hasNextTrack(&i)) { + TOC *o = i.getNextTrack(&i); + while (i.hasNextTrack(&i)) { + TOC *p = i.getNextTrack(&i); + int from; + from = GETTRACK(o); + + fprintf(stderr, "T%02d:", from); + + if (IS__DATA(o)) { + /* + * Special case of cd extra + */ + unsigned int real_start = have_CD_extra + ? have_CD_extra : GETSTART(o); + + + dw = (unsigned long) (GETSTART(p) - real_start); + + mins = dw / ( 60*75 ); + secnds = ( dw % ( 60*75 )) / 75; + frames = ( dw % 75 ); + + if ( global.verbose & SHOW_STARTPOSITIONS ) + fprintf(stderr, + " %7u", + real_start + ); + + if ( global.verbose & SHOW_TOC ) + fprintf(stderr, + " %2u:%02u.%02u", + mins, secnds, frames + ); + + if ( global.verbose & SHOW_SUMMARY ) + fprintf(stderr, + " data %s %s N/A", + + /* how recorded */ + IS__INCREMENTAL(o) + ? "incremental" : "uninterrupted", + + /* copy-permission */ + IS__COPYRIGHTED(o) + ? "copydenied" : "copyallowed" + ); + fputs("\n", stderr); + } else { + dw = (unsigned long) (GETSTART(p) - GETSTART(o)); + mins = dw / ( 60*75 ); + secnds = ( dw % ( 60*75 )) / 75; + frames = ( dw % 75 ); + + if ( global.verbose & SHOW_STARTPOSITIONS ) + fprintf(stderr, + " %7u", + GETSTART(o) + ); + + if ( global.verbose & SHOW_TOC ) + fprintf(stderr, + " %2u:%02u.%02u", + mins, secnds, frames + ); + + if ( global.verbose & SHOW_SUMMARY ) + fprintf(stderr, + " audio %s %s %s", + + /* how recorded */ + IS__PREEMPHASIZED(o) + ? "pre-emphasized" : "linear", + + /* copy-permission */ + IS__COPYRIGHTED(o) + ? "copydenied" : "copyallowed", + + /* channels */ + IS__QUADRO(o) + ? "quadro" : "stereo"); + + /* Title */ + if ( global.verbose & SHOW_TITLES ) { + fprintf(stderr, + " title '%s' from ", + + (void *) global.tracktitle[GETTRACK(o)] != NULL + ? quote(global.tracktitle[GETTRACK(o)]) : "" + ); + + fprintf(stderr, + "'%s'", + + (void *) global.trackcreator[GETTRACK(o)] != NULL + ? quote(global.trackcreator[GETTRACK(o)]) : "" + ); + } + fputs("\n", stderr); + count_audio_trks++; + } + o = p; + } /* while */ + if ( global.verbose & SHOW_STARTPOSITIONS ) + if (GETTRACK(o) == CDROM_LEADOUT) { + fprintf(stderr, "Leadout: %7u\n", GETSTART(o)); + } + } /* if */ +} + +static void DisplayToc_no_gui(unsigned long dw); + +static void DisplayToc_no_gui(unsigned long dw) +{ + unsigned mins; + unsigned secnds; + unsigned frames; + int count_audio_trks; + unsigned ii = 0; + static struct iterator i; + if (i.reset == NULL) InitIterator(&i, 1); + else i.reset(&i); + + mins = dw / ( 60*75 ); + secnds = ( dw % ( 60*75 ) ) / 75; + frames = ( dw % 75 ); + + /* summary */ + count_audio_trks = 0; + + if (i.hasNextTrack(&i)) { + TOC *o = i.getNextTrack(&i); + while (i.hasNextTrack(&i)) { + TOC *p = i.getNextTrack(&i); + int from; + from = GETTRACK(o); + + + while ( p != NULL && GETTRACK(p) != CDROM_LEADOUT + && GETFLAGS(o) == GETFLAGS(p) ) { + o = p; + p = i.getNextTrack(&i); + } + if ((global.verbose & SHOW_SUMMARY) == 0) continue; + + if (IS__DATA(o)) { + fputs( " DATAtrack recorded copy-permitted tracktype\n" , stderr); + fprintf(stderr, + " %2d-%2d %13.13s %14.14s data\n", + from, + GETTRACK(o), + /* how recorded */ + IS__INCREMENTAL(o) + ? "incremental" : "uninterrupted", + + /* copy-perm */ + IS__COPYRIGHTED(o) ? "no" : "yes" + ); + } else { + fputs( "AUDIOtrack pre-emphasis copy-permitted tracktype channels\n" , stderr); + fprintf(stderr, + " %2d-%2d %12.12s %14.14s audio %1c\n", + from, + GETTRACK(o), + IS__PREEMPHASIZED(o) + ? "yes" : "no", + IS__COPYRIGHTED(o) ? "no" : "yes", + IS__QUADRO(o) ? '4' : '2' + ); + count_audio_trks++; + } + o = p; + } + } + if ((global.verbose & SHOW_STARTPOSITIONS) != 0) { + if (global.illleadout_cd != 0 && have_multisession == 0) { + + fprintf ( stderr, + "Table of Contents: total tracks:%u, (total time more than %u:%02u.%02u)\n", + cdtracks, mins, secnds, frames ); + } else { + fprintf ( stderr, + "Table of Contents: total tracks:%u, (total time %u:%02u.%02u)\n", + cdtracks, mins, secnds, frames ); + } + } + + i.reset(&i); + if ((global.verbose & SHOW_TOC) != 0 && + i.hasNextTrack(&i)) { + TOC *o = i.getNextTrack(&i); + + for (; i.hasNextTrack(&i);) { + TOC *p = i.getNextTrack(&i); + + if ( GETTRACK(o) <= MAXTRK ) { + unsigned char brace1, brace2; + unsigned trackbeg; + trackbeg = have_multisession && IS__DATA(o) ? have_multisession : GETSTART(o); + + dw = (unsigned long) (GETSTART(p) - trackbeg); + mins = dw / ( 60*75 ); + secnds = ( dw % ( 60*75 )) / 75; + frames = ( dw % 75 ); + + if ( IS__DATA(o) ) { + /* data track display */ + brace1 = '['; + brace2 = ']'; + } else if (have_multisession + && GETTRACK(o) == LastAudioTrack()) { + /* corrected length of + * last audio track in cd extra + */ + brace1 = '|'; + brace2 = '|'; + } else { + /* audio track display */ + brace1 = '('; + brace2 = ')'; + } + fprintf ( stderr, + " %2u.%c%2u:%02u.%02u%c", + GETTRACK(o), + brace1, + mins, secnds, frames, + brace2 + ); + ii++; + + if ( ii % 5 == 0 ) + fputs( ",\n", stderr ); + else if (ii != cdtracks) + fputc ( ',', stderr ); + } + o = p; + } /* for */ + if ( (ii % 5) != 0 ) + fputs( "\n", stderr ); + + } /* if */ + + if ((global.verbose & SHOW_STARTPOSITIONS) != 0) { + fputs ("\nTable of Contents: starting sectors\n", stderr); + + ii = 0; + i.reset(&i); + if (i.hasNextTrack(&i)) { + TOC *o = i.getNextTrack(&i); + for ( ; i.hasNextTrack(&i);) { + TOC *p = i.getNextTrack(&i); + fprintf ( stderr, + " %2u.(%8u)", + GETTRACK(o), + have_multisession + && GETTRACK(o) == FirstDataTrack() + ? have_multisession + : GETSTART(o) +#ifdef DEBUG_CDDB + +150 +#endif + ); + + ii++; + if ( (ii) % 5 == 0 ) + fputs( ",\n", stderr ); + else + fputc ( ',', stderr ); + o = p; + } + fprintf ( stderr, " lead-out(%8u)", GETSTART(o)); + fputs ("\n", stderr); + } + } + if (global.quiet == 0) { + fprintf(stderr, "CDINDEX discid: %s\n", global.cdindex_id); + fprintf( stderr, "CDDB discid: 0x%08lx", (unsigned long) global.cddb_id); + + if (have_CDDB != 0) { + fprintf(stderr, " CDDBP titles: resolved\n"); + } else { + fprintf(stderr, "\n"); + } + if (have_CD_text != 0) { + fprintf(stderr, "CD-Text: detected\n"); + } else { + fprintf(stderr, "CD-Text: not detected\n"); + } + if (have_CD_extra != 0) { + fprintf(stderr, "CD-Extra: detected\n"); + } else { + fprintf(stderr, "CD-Extra: not detected\n"); + } + } + if ((global.verbose & SHOW_TITLES) != 0) { + int maxlen = 0; + + if ( global.disctitle != NULL ) { + fprintf( stderr, "Album title: '%s'", global.disctitle); + if ( global.creator != NULL ) { + fprintf( stderr, "\t[from %s]", global.creator); + } + fputs("\n", stderr); + } + + i.reset(&i); + for ( ; i.hasNextTrack(&i);) { + TOC *p = i.getNextTrack(&i); + int jj = GETTRACK(p); + + if ( global.tracktitle[jj] != NULL ) { + int len = strlen((char *)global.tracktitle[jj]); + maxlen = max(maxlen, len); + } + } + maxlen = (maxlen + 12 + 8 + 7)/8; + + i.reset(&i); + for ( ; i.hasNextTrack(&i); ) { + TOC *p = i.getNextTrack(&i); + int jj; + + if (IS__DATA(p)) + continue; + + jj = GETTRACK(p); + + if (jj == CDROM_LEADOUT) + break; + + if ( maxlen != 3 ) { + if ( global.tracktitle[jj] != NULL ) { + fprintf( stderr, "Track %2u: '%s'", jj, global.tracktitle[jj]); + } else { + fprintf( stderr, "Track %2u: '%s'", jj, ""); + } + if ( global.trackcreator[jj] != NULL + && global.trackcreator[jj][0] != '\0' +#if 1 + && (global.creator == NULL + || 0 != strcmp((char *)global.creator,(char *)global.trackcreator[jj])) +#endif + ) { + int j; + char *o = global.tracktitle[jj] != NULL + ? (char *)global.tracktitle[jj] + : ""; + for ( j = 0; + j < (maxlen - ((int)strlen(o) + 12)/8); + j++) + fprintf(stderr, "\t"); + fprintf( stderr, "[from %s]", global.trackcreator[jj]); + } + fputs("\n", stderr); + } + } + } +} + +void DisplayToc(void) +{ + unsigned long dw; + + /* special handling of pseudo-red-book-audio cds */ + if (cdtracks > 1 + && Get_StartSector(CDROM_LEADOUT) < Get_StartSector(cdtracks)) { + global.illleadout_cd = 1; + can_read_illleadout(); + } + + + /* get total time */ + if (global.illleadout_cd == 0) + dw = (unsigned long) Get_StartSector(CDROM_LEADOUT) - Get_StartSector(1); + else + dw = (unsigned long) Get_StartSector(cdtracks ) - Get_StartSector(1); + + if ( global.gui == 0 ) { + /* table formatting when in cmdline mode */ + DisplayToc_no_gui( dw ); + } else if (global.gui == 1) { + /* line formatting when in gui mode */ + DisplayToc_with_gui( dw ); + } + + if (global.illleadout_cd != 0) { + if (global.quiet == 0) { + fprintf(stderr, "CD with illegal leadout position detected!\n"); + } + + if (global.reads_illleadout == 0) { + /* limit accessible tracks + * to lowered leadout position + */ + restrict_tracks_illleadout(); + + if (global.quiet == 0) { + fprintf(stderr, + "The cdrom drive firmware does not permit access beyond the leadout position!\n"); + } + if (global.verbose & (SHOW_ISRC | SHOW_INDICES)) { + global.verbose &= ~(SHOW_ISRC | SHOW_INDICES); + fprintf(stderr, "Switching index scan and ISRC scan off!\n"); + } + + if (global.quiet == 0) { + fprintf(stderr, + "Audio extraction will be limited to track %ld with maximal %ld sectors...\n", + LastTrack(), + Get_EndSector(LastTrack())+1 + ); + } + } else { + /* The cdrom drive can read beyond the + * indicated leadout. We patch a new leadout + * position to the maximum: + * 99 minutes, 59 seconds, 74 frames + */ + patch_real_end(150 + (99*60+59)*75 + 74); + if (global.quiet == 0) { + fprintf(stderr, + "Restrictions apply, since the size of the last track is unknown!\n"); + } + } + } +} + +static void Read_MCN_toshiba(subq_chnl **sub_ch); + +static void Read_MCN_toshiba(subq_chnl **sub_ch) +{ + if (Toshiba3401() != 0 && global.quiet == 0 + && ((*sub_ch) != 0 + || (((subq_catalog *)(*sub_ch)->data)->mc_valid & 0x80))) { + /* no valid MCN yet. do more searching */ + long h = Get_AudioStartSector(1); + + while (h <= Get_AudioStartSector(1) + 100) { + if (Toshiba3401()) + ReadCdRom(get_scsi_p(), RB_BASE->data, h, global.nsectors); + (*sub_ch) = ReadSubQ(get_scsi_p(), GET_CATALOGNUMBER,0); + if ((*sub_ch) != NULL) { + subq_catalog *subq_cat; + + subq_cat = (subq_catalog *) (*sub_ch)->data; + if ((subq_cat->mc_valid & 0x80) != 0) { + break; + } + } + h += global.nsectors; + } + } +} + +static void Get_Set_MCN(void); + +static void Get_Set_MCN(void) +{ + subq_chnl *sub_ch; + subq_catalog *subq_cat = NULL; + fprintf(stderr, "scanning for MCN..."); + + sub_ch = ReadSubQ(get_scsi_p(), GET_CATALOGNUMBER,0); + +#define EXPLICIT_READ_MCN_ISRC 1 +#if EXPLICIT_READ_MCN_ISRC == 1 /* TOSHIBA HACK */ + Read_MCN_toshiba( &sub_ch ); +#endif + + if (sub_ch != NULL) + subq_cat = (subq_catalog *)sub_ch->data; + + if (sub_ch != NULL + && (subq_cat->mc_valid & 0x80) != 0 + && global.quiet == 0) { + + /* unified format guesser: + * format MCN all digits in bcd + * 1 13 + * A: ab cd ef gh ij kl m0 0 0 0 0 0 0 Plextor 6x Rel. 1.02 + * B: 0a 0b 0c 0d 0e 0f 0g 0h 0i 0j 0k 0l 0m Toshiba 3401 + * C: AS AS AS AS AS AS AS AS AS AS AS AS AS ASCII SCSI-2 Plextor 4.5x and 6x Rel. 1.06 + */ + unsigned char *cp = subq_cat->media_catalog_number; + if (!(cp[8] | cp[9] | cp[10] | cp[11] | cp[12]) + && ((cp[0] & 0xf0) | (cp[1] & 0xf0) + | (cp[2] & 0xf0) | (cp[3] & 0xf0) + | (cp[4] & 0xf0) | (cp[5] & 0xf0) + | (cp[6] & 0xf0))) { + /* reformat A: to B: */ + cp[12] = cp[6] >> 4; cp[11] = cp[5] & 0xf; + cp[10] = cp[5] >> 4; cp[ 9] = cp[4] & 0xf; + cp[ 8] = cp[4] >> 4; cp[ 7] = cp[3] & 0xf; + cp[ 6] = cp[3] >> 4; cp[ 5] = cp[2] & 0xf; + cp[ 4] = cp[2] >> 4; cp[ 3] = cp[1] & 0xf; + cp[ 2] = cp[1] >> 4; cp[ 1] = cp[0] & 0xf; + cp[ 0] = cp[0] >> 4; + } + + if (!isdigit(cp[0]) + && (memcmp(subq_cat->media_catalog_number, + "\0\0\0\0\0\0\0\0\0\0\0\0\0", 13) != 0)) { + sprintf((char *) + subq_cat->media_catalog_number, + "%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X", + subq_cat->media_catalog_number [0], + subq_cat->media_catalog_number [1], + subq_cat->media_catalog_number [2], + subq_cat->media_catalog_number [3], + subq_cat->media_catalog_number [4], + subq_cat->media_catalog_number [5], + subq_cat->media_catalog_number [6], + subq_cat->media_catalog_number [7], + subq_cat->media_catalog_number [8], + subq_cat->media_catalog_number [9], + subq_cat->media_catalog_number [10], + subq_cat->media_catalog_number [11], + subq_cat->media_catalog_number [12] + ); + } + + if (memcmp(subq_cat->media_catalog_number,"0000000000000",13) + != 0) { + Set_MCN(subq_cat->media_catalog_number); + } + } +} + + +static void Read_ISRC_toshiba(subq_chnl **sub_ch, unsigned tr); + +static void Read_ISRC_toshiba(subq_chnl **sub_ch, unsigned tr) +{ + if (Toshiba3401() != 0) { + int j; + j = (Get_AudioStartSector(tr)/100 + 1) * 100; + do { + ReadCdRom(get_scsi_p(), RB_BASE->data, j, global.nsectors); + *sub_ch = ReadSubQ(get_scsi_p(), GET_TRACK_ISRC, Get_Tracknumber(tr)); + if (*sub_ch != NULL) { + subq_track_isrc * subq_tr; + + subq_tr = (subq_track_isrc *) (*sub_ch)->data; + if (subq_tr != NULL && (subq_tr->tc_valid & 0x80) != 0) + break; + } + j += global.nsectors; + } while (j < (Get_AudioStartSector(tr)/100 + 1) * 100 + 100); + } +} + + +static void Get_Set_ISRC(unsigned tr); + +static void Get_Set_ISRC(unsigned tr) +{ + subq_chnl *sub_ch; + subq_track_isrc * subq_tr; + + fprintf(stderr, "\rscanning for ISRCs: %d ...", tr); + + subq_tr = NULL; + sub_ch = ReadSubQ(get_scsi_p(), GET_TRACK_ISRC, tr); + +#if EXPLICIT_READ_MCN_ISRC == 1 /* TOSHIBA HACK */ + Read_ISRC_toshiba( &sub_ch, tr ); +#endif + + if (sub_ch != NULL) + subq_tr = (subq_track_isrc *)sub_ch->data; + + if (sub_ch != NULL && (subq_tr->tc_valid & 0x80) + && global.quiet == 0) { + unsigned char p_start[16]; + unsigned char *p = p_start; + unsigned char *cp = subq_tr->track_isrc; + + /* unified format guesser: + * there are 60 bits and 15 bytes available. + * 5 * 6bit-items + two zero fill bits + 7 * 4bit-items + * + * A: ab cd ef gh ij kl mn o0 0 0 0 0 0 0 0 Plextor 6x Rel. 1.02 + * B: 0a 0b 0c 0d 0e 0f 0g 0h 0i 0j 0k 0l 0m 0n 0o Toshiba 3401 + * C: AS AS AS AS AS AS AS AS AS AS AS AS AS AS AS ASCII SCSI-2 + * eg 'G''B''-''A''0''7''-''6''8''-''0''0''2''7''0' makes most sense + * D: 'G''B''A''0''7''6''8''0''0''2''7''0'0 0 0 Plextor 6x Rel. 1.06 and 4.5x R. 1.01 and 1.04 + */ + + /* Check for format A: */ + if (!(cp[8] | cp[9] | cp[10] | cp[11] | cp[12] | cp[13] | cp[14]) && + ((cp[0] & 0xf0) | (cp[1] & 0xf0) | (cp[2] & 0xf0) | + (cp[3] & 0xf0) | (cp[4] & 0xf0) | (cp[5] & 0xf0) | + (cp[6] & 0xf0) | (cp[7] & 0xf0))) { +#if DEBUG_ISRC + fprintf(stderr, "a!\t"); +#endif + /* reformat A: to B: */ + cp[14] = cp[7] >> 4; cp[13] = cp[6] & 0xf; + cp[12] = cp[6] >> 4; cp[11] = cp[5] & 0xf; + cp[10] = cp[5] >> 4; cp[ 9] = cp[4] & 0xf; + cp[ 8] = cp[4] >> 4; cp[ 7] = cp[3] & 0xf; + cp[ 6] = cp[3] >> 4; cp[ 5] = cp[2] & 0xf; + cp[ 4] = cp[2] >> 4; cp[ 3] = cp[1] & 0xf; + cp[ 2] = cp[1] >> 4; cp[ 1] = cp[0] & 0xf; + cp[ 0] = cp[0] >> 4; +#if DEBUG_ISRC + fprintf(stderr, "a->b: %15.15s\n", cp); +#endif + } + + /* Check for format B: + * If not yet in ASCII format, do the conversion + */ + if (cp[0] < '0' && cp[1] < '0') { + /* coding table for International Standard Recording Code */ + static char bin2ISRC[] = { + '0','1','2','3','4','5','6','7','8','9', /* 10 */ + ':',';','<','=','>','?','@', /* 17 */ + 'A','B','C','D','E','F','G','H','I','J','K', /* 28 */ + 'L','M','N','O','P','Q','R','S','T','U','V', /* 39 */ + 'W','X','Y','Z', /* 43 */ +#if 1 + '[','\\',']','^','_','`', /* 49 */ + 'a','b','c','d','e','f','g','h','i','j','k', /* 60 */ + 'l','m','n','o' /* 64 */ +#endif + }; + + /* build 6-bit vector of coded values */ + unsigned ind; + int bits; + +#if DEBUG_ISRC + fprintf(stderr, "b!\n"); +#endif + ind = (cp[0] << 26) + + (cp[1] << 22) + + (cp[2] << 18) + + (cp[3] << 14) + + (cp[4] << 10) + + (cp[5] << 6) + + (cp[6] << 2) + + (cp[7] >> 2); + + if ((cp[7] & 3) == 3) { + if (global.verbose) { + fprintf(stderr, + "Recorder-ID encountered: "); + for (bits = 0; bits < 30; bits +=6) { + unsigned binval = (ind & (ULONG_C(0x3f) << (24-bits))) + >> (24-bits); + if ((binval < sizeof(bin2ISRC)) && + (binval <= 9 || binval >= 16)) { + fprintf(stderr, "%X", bin2ISRC[binval]); + } + } + + fprintf(stderr, "%.1X%.1X%.1X%.1X%.1X%.1X%.1X", + subq_tr->track_isrc [8] & 0x0f, + subq_tr->track_isrc [9] & 0x0f, + subq_tr->track_isrc [10] & 0x0f, + subq_tr->track_isrc [11] & 0x0f, + subq_tr->track_isrc [12] & 0x0f, + subq_tr->track_isrc [13] & 0x0f, + subq_tr->track_isrc [14] & 0x0f + ); + fprintf(stderr, "\n"); + } + return; + } + if ((cp[7] & 3) != 0) { + fprintf(stderr, "unknown mode 3 entry C1=0x%02x, C2=0x%02x\n", + (cp[7] >> 1) & 1, cp[7] & 1); + return; + } + + /* decode ISRC due to IEC 908 */ + for (bits = 0; bits < 30; bits +=6) { + unsigned binval = (ind & ((unsigned long) 0x3fL << (24L-bits))) >> (24L-bits); + if ((binval >= sizeof(bin2ISRC)) || + (binval > 9 && binval < 16)) { + /* Illegal ISRC, dump and skip */ + int y; + + Get_ISRC(tr)[0] = '\0'; + fprintf(stderr, "\nIllegal ISRC for track %d, skipped: ", tr); + for (y = 0; y < 15; y++) { + fprintf(stderr, "%02x ", cp[y]); + } + fputs("\n", stderr); + return; + } + *p++ = bin2ISRC[binval]; + + /* insert a dash after two country characters for legibility */ + if (bits == 6) + *p++ = '-'; + } + + /* format year and serial number */ + sprintf ((char *)p, "-%.1X%.1X-%.1X%.1X%.1X%.1X%.1X", + subq_tr->track_isrc [8] & 0x0f, + subq_tr->track_isrc [9] & 0x0f, + subq_tr->track_isrc [10] & 0x0f, + subq_tr->track_isrc [11] & 0x0f, + subq_tr->track_isrc [12] & 0x0f, + subq_tr->track_isrc [13] & 0x0f, + subq_tr->track_isrc [14] & 0x0f + ); +#if DEBUG_ISRC + fprintf(stderr, "b: %15.15s!\n", p_start); +#endif + } else { + /* It might be in ASCII, surprise */ + int ii; + for (ii = 0; ii < 12; ii++) { + if (cp[ii] < '0' || cp[ii] > 'Z') { + break; + } + } + if (ii != 12) { + int y; + + Get_ISRC(ii)[0] = '\0'; + fprintf(stderr, "\nIllegal ISRC for track %d, skipped: ", ii+1); + for (y = 0; y < 15; y++) { + fprintf(stderr, "%02x ", cp[y]); + } + fputs("\n", stderr); + return; + } + +#if DEBUG_ISRC + fprintf(stderr, "ascii: %15.15s!\n", cp); +#endif + for (ii = 0; ii < 12; ii++) { +#if 1 + if ((ii == 2 || ii == 5 || ii == 7) && cp[ii] != ' ') + *p++ = '-'; +#endif + *p++ = cp[ii]; + } + if (p - p_start >= 16) + *(p_start + 15) = '\0'; + else + *p = '\0'; + } + + if (memcmp(p_start,"00-000-00-00000",15) != 0) { + Set_ISRC(tr, p_start); + } + } +} + +/* get and display Media Catalog Number ( one per disc ) + * and Track International Standard Recording Codes (for each track) + */ +void Read_MCN_ISRC(void) +{ + if ((global.verbose & SHOW_MCN) != 0) { + + if (Get_MCN()[0] == '\0') { + Get_Set_MCN(); + } + + if (Get_MCN()[0] != '\0') + fprintf(stderr, "\rMedia catalog number: %13.13s\n", Get_MCN()); + else + fprintf(stderr, "\rNo media catalog number present.\n"); + } + + + + if ((global.verbose & SHOW_ISRC) != 0) { + static struct iterator i; + + InitIterator(&i, 1); + + while (i.hasNextTrack(&i)) { + struct TOC *p = i.getNextTrack(&i); + unsigned ii = GETTRACK(p); + + if (ii == CDROM_LEADOUT) break; + + if (!IS__AUDIO(p)) + continue; + + if (GETISRC(p)[0] == '\0') { + Get_Set_ISRC(ii); + } + + if (GETISRC(p)[0] != '\0') { + fprintf (stderr, "\rT: %2d ISRC: %15.15s\n", ii, GETISRC(p)); + fflush(stderr); + } + } /* for all tracks */ + + fputs("\n", stderr); + } /* if SHOW_ISRC */ +} + +static int playing = 0; + +static subq_chnl *ReadSubChannel(unsigned sec); + +static subq_chnl *ReadSubChannel(unsigned sec) +{ + subq_chnl *sub_ch; + + /* + * For modern drives implement a direct method. If the drive supports + * reading of subchannel data, do direct reads. + */ + if (ReadSubChannels != NULL) { + get_scsi_p()->silent++; + sub_ch = ReadSubChannels(get_scsi_p(), sec); + get_scsi_p()->silent--; + if (sub_ch == NULL /*&& (usal_sense_key(get_scsi_p()) == 5)*/) { + /* command is not implemented */ + ReadSubChannels = NULL; +#if defined DEBUG_SUB +fprintf(stderr, "\nCommand not implemented: switching ReadSubChannels off !\n"); +#endif + goto fallback; + } + + /* check the adress mode field */ + if ((sub_ch->control_adr & 0x0f) == 0) { + /* no Q mode information present at all, weird */ + sub_ch->control_adr = 0xAA; + } + + if ((int)(sub_ch->control_adr & 0x0f) > 0x01) { + /* this sector just has no position information. + * we try the one before and then the one after. + */ + if (sec > 1) { + sec -= 1; + sub_ch = ReadSubChannels(get_scsi_p(), sec); + if (sub_ch == NULL) return NULL; + sec += 1; + } + if ((sub_ch->control_adr & 0x0f) != 0x01) { + sec += 2; + sub_ch = ReadSubChannels(get_scsi_p(), sec); + if (sub_ch == NULL) return NULL; + sec -= 2; + } + } + + /* check adress mode field for position information */ + if ((sub_ch->control_adr & 0x0f) == 0x01) { + return sub_ch; + } + ReadSubChannels = NULL; +fprintf(stderr, "\nCould not get position information (%02x) for sectors %d, %d, %d: switching ReadSubChannels off !\n", sub_ch->control_adr &0x0f, sec-1, sec, sec+2); + } + + /* + * We rely on audio sectors here!!! + * The only method that worked even with my antique Toshiba 3401, + * is playing the sector and then request the subchannel afterwards. + */ +fallback: + /* We need a conformed audio track here! */ + + /* Fallback to ancient method */ + if (-1 == Play_at(get_scsi_p(), sec, 1)) { + return NULL; + } + playing = 1; + sub_ch = ReadSubQ(get_scsi_p(), GET_POSITIONDATA,0); + return sub_ch; +} + +static int ReadSubControl(unsigned sec); +static int ReadSubControl(unsigned sec) +{ + subq_chnl *sub_ch = ReadSubChannel(sec); + if (sub_ch == NULL) return -1; + + return sub_ch->control_adr & 0xf0; +} + +static int HaveSCMS(unsigned StartSector); +static int HaveSCMS(unsigned StartSector) +{ + int i; + int cr; + int copy_bits_set = 0; + + for (i = 0; i < 8; i++) { + cr = ReadSubControl(StartSector + i); + if (cr == -1) continue; + (cr & 0x20) ? copy_bits_set++ : 0; + } + return (copy_bits_set >= 1 && copy_bits_set < 8); +} + +void Check_Toc(void) +{ + /* detect layout */ + + /* detect tracks */ +} + +static int GetIndexOfSector(unsigned sec, unsigned track) +{ + subq_chnl *sub_ch = ReadSubChannel(sec); + if (sub_ch == NULL) { + if ((long)sec == Get_EndSector(track)) { + fprintf(stderr, "Driver and/or firmware bug detected! Drive cannot play the very last sector (%u)!\n", sec); + } + return -1; + } + + /* can we trust that these values are hex and NOT bcd? */ + if ((sub_ch->track >= 0x10) && (sub_ch->track - track > 5)) { + /* change all values from bcd to hex */ + sub_ch->track = (sub_ch->track >> 4)*10 + (sub_ch->track & 0x0f); + sub_ch->index = (sub_ch->index >> 4)*10 + (sub_ch->index & 0x0f); + } + +#if 1 + /* compare tracks */ + if (sub_ch->index != 0 && track != sub_ch->track) { + if (global.verbose) fprintf(stderr, "\ntrack mismatch: %1d, in-track subchannel: %1d (index %1d, sector %1d)\n", + track, sub_ch->track, sub_ch->index, sec); + } +#endif + + /* compare control field with the one from the TOC */ + if ((Get_Flags(track) & 0xf0) != (sub_ch->control_adr & 0xf0)) { + int diffbits = (Get_Flags(track) & 0xf0) ^ (sub_ch->control_adr & 0xf0); + if ((diffbits & 0x80) == 0x80) { + /* broadcast difference */ + if (global.verbose) fprintf(stderr, "broadcast type conflict detected -> TOC:%s, subchannel:%s\n", + (sub_ch->control_adr & 0x80) == 0 ? "broadcast" : "nonbroadcast" + ,(sub_ch->control_adr & 0x80) != 0 ? "broadcast" : "nonbroadcast" + ); + } + if ((diffbits & 0x40) == 0x40) { + /* track type difference */ + if (global.verbose) fprintf(stderr, "track type conflict detected -> TOC:%s, subchannel:%s\n", + (sub_ch->control_adr & 0x40) == 0 ? "data" : "audio" + ,(sub_ch->control_adr & 0x40) != 0 ? "data" : "audio" + ); + } + if ((diffbits & 0x20) == 0x20 && !Get_SCMS(track)) { + /* copy permission difference is a sign for SCMS + * and is treated elsewhere. */ + if (global.verbose) fprintf(stderr, "difference: TOC:%s, subchannel:%s\ncorrecting TOC...\n", + (sub_ch->control_adr & 0x20) == 0 ? "unprotected" : "copyright protected", + (sub_ch->control_adr & 0x20) != 0 ? "unprotected" : "copyright protected" + ); + toc_entry(track, + (Get_Flags(track) & 0xDF) | (sub_ch->control_adr & 0x20), + Get_Tracknumber(track), + Get_ISRC(track), + Get_AudioStartSector(track), + Get_Mins(track), + Get_Secs(track), + Get_Frames(track) + ); + } + if ((diffbits & 0x10) == 0x10) { + /* preemphasis difference */ + if (global.verbose) fprintf(stderr, "difference: TOC:%s, subchannel:%s preemphasis\ncorrecting TOC...\n", + (sub_ch->control_adr & 0x10) == 0 ? "with" : "without", + (sub_ch->control_adr & 0x10) != 0 ? "with" : "without" + ); + toc_entry(track, + (Get_Flags(track) & 0xEF) | (sub_ch->control_adr & 0x10), + Get_Tracknumber(track), + Get_ISRC(track), + Get_AudioStartSector(track), + Get_Mins(track), + Get_Secs(track), + Get_Frames(track) + ); + } + + } + + return sub_ch ? sub_ch->index == 244 ? 1 : sub_ch->index : -1; +} + +static int ScanBackwardFrom(unsigned sec, unsigned limit, int *where, + unsigned track); + +static int ScanBackwardFrom(unsigned sec, unsigned limit, int *where, + unsigned track) +{ + unsigned lastindex = 0; + unsigned mysec = sec; + + /* try to find the transition of index n to index 0, + * if the track ends with an index 0. + */ + while ((lastindex = GetIndexOfSector(mysec, track)) == 0) { + if (mysec < limit+75) { + break; + } + mysec -= 75; + } + if (mysec == sec) { + /* there is no pre-gap in this track */ + if (where != NULL) *where = -1; + } else { + /* we have a pre-gap in this track */ + + if (lastindex == 0) { + /* we did not cross the transition yet -> search backward */ + do { + if (mysec < limit+1) { + break; + } + mysec --; + } while ((lastindex = GetIndexOfSector(mysec,track)) == 0); + if (lastindex != 0) { + /* successful */ + mysec ++; + /* register mysec as transition */ + if (where != NULL) *where = (int) mysec; + } else { + /* could not find transition */ + if (!global.quiet) + fprintf(stderr, + "Could not find index transition for pre-gap.\n"); + if (where != NULL) *where = -1; + } + } else { + int myindex = -1; + /* we have crossed the transition -> search forward */ + do { + if (mysec >= sec) { + break; + } + mysec ++; + } while ((myindex = GetIndexOfSector(mysec,track)) != 0); + if (myindex == 0) { + /* successful */ + /* register mysec as transition */ + if (where != NULL) *where = (int) mysec; + } else { + /* could not find transition */ + if (!global.quiet) + fprintf(stderr, + "Could not find index transition for pre-gap.\n"); + if (where != NULL) *where = -1; + } + } + } + return lastindex; +} + +#ifdef USE_LINEAR_SEARCH +static int linear_search(int searchInd, unsigned int Start, unsigned int End, + unsigned track); + +static int linear_search(int searchInd, unsigned int Start, unsigned int End, + unsigned track) +{ + int l = Start; + int r = End; + + for (; l <= r; l++ ) { + int ind; + + ind = GetIndexOfSector(l, track); + if ( searchInd == ind ) { + break; + } + } + if ( l <= r ) { + /* Index found. */ + return l; + } + + return -1; +} +#endif + +#ifndef USE_LINEAR_SEARCH +#undef DEBUG_BINSEARCH +static int binary_search(int searchInd, unsigned int Start, unsigned int End, + unsigned track); + +static int binary_search(int searchInd, unsigned Start, unsigned End, + unsigned track) +{ + int l = Start; + int r = End; + int x = 0; + int ind; + + while ( l <= r ) { + x = ( l + r ) / 2; + /* try to avoid seeking */ + ind = GetIndexOfSector(x, track); + if ( searchInd == ind ) { + break; + } else { + if ( searchInd < ind ) r = x - 1; + else l = x + 1; + } + } +#ifdef DEBUG_BINSEARCH +fprintf(stderr, "(%d,%d,%d > ",l,x,r); +#endif + if ( l <= r ) { + /* Index found. Now find the first position of this index */ + /* l=LastPos x=found r=NextPos */ + r = x; + while ( l < r-1 ) { + x = ( l + r ) / 2; + /* try to avoid seeking */ + ind = GetIndexOfSector(x, track); + if ( searchInd == ind ) { + r = x; + } else { + l = x; + } +#ifdef DEBUG_BINSEARCH +fprintf(stderr, "%d -> ",x); +#endif + } +#ifdef DEBUG_BINSEARCH +fprintf(stderr, "%d,%d)\n",l,r); +#endif + if (searchInd == GetIndexOfSector(l, track)) + return l; + else + return r; + } + + return -1; +} +#endif + + +static void register_index_position(int IndexOffset, + index_list **last_index_entry); + +static void register_index_position(int IndexOffset, + index_list **last_index_entry) +{ + index_list *indexentry; + + /* register higher index entries */ + if (*last_index_entry != NULL) { + indexentry = (index_list *) malloc( sizeof(index_list) ); + } else { + indexentry = NULL; + } + if (indexentry != NULL) { + indexentry->next = NULL; + (*last_index_entry)->next = indexentry; + *last_index_entry = indexentry; + indexentry->frameoffset = IndexOffset; +#if defined INFOFILES + } else { + fprintf( stderr, "No memory for index lists. Index positions\nwill not be written in info file!\n"); +#endif + } +} + +static void Set_SCMS(unsigned long p_track); + +#undef DEBUG_INDLIST +/* experimental code */ +/* search for indices (audio mode required) */ +unsigned ScanIndices(unsigned track, unsigned cd_index, int bulk) +{ + /* scan for indices. */ + /* look at last sector of track. */ + /* when the index is not equal 1 scan by bipartition + * for offsets of all indices */ + + unsigned starttrack, endtrack; + unsigned startindex, endindex; + + unsigned j; + int LastIndex=0; + int n_0_transition; + unsigned StartSector; + unsigned retval = 0; + + index_list *baseindex_pool; + index_list *last_index_entry; + + SCSI *usalp = get_scsi_p(); + + static struct iterator i; + InitIterator(&i, 1); + + EnableCdda(usalp, 0, 0); + EnableCdda(usalp, 1, CD_FRAMESIZE_RAW + 16); + + if (!global.quiet && !(global.verbose & SHOW_INDICES)) + fprintf(stderr, "seeking index start ..."); + + if (bulk != 1) { + starttrack = track; endtrack = track; + } else { + starttrack = 1; endtrack = cdtracks; + } + baseindex_pool = (index_list *) malloc( sizeof(index_list) * (endtrack - starttrack + 1)); +#ifdef DEBUG_INDLIST + fprintf(stderr, "index0-mem-pool %p\n", baseindex_pool); +#endif + + + while (i.hasNextTrack(&i)) { + struct TOC *p = i.getNextTrack(&i); + unsigned ii = GETTRACK(p); + + if ( ii < starttrack || IS__DATA(p) ) + continue; /* skip nonaudio tracks */ + + if ( ii > endtrack ) + break; + + if ( global.verbose & SHOW_INDICES ) { + if (global.illleadout_cd && global.reads_illleadout && ii == endtrack) { + fprintf(stderr, "Analysis of track %d skipped due to unknown length\n", ii); + } + } + if (global.illleadout_cd && global.reads_illleadout + && ii == endtrack) continue; + + StartSector = Get_AudioStartSector(ii); + if (HaveSCMS(StartSector)) { + Set_SCMS(ii); + } + if ( global.verbose & SHOW_INDICES ) { + fprintf( stderr, "\rindex scan: %d...", ii ); + fflush (stderr); + } + LastIndex = ScanBackwardFrom(Get_EndSector(ii), StartSector, &n_0_transition, ii); + if (LastIndex > 99) continue; + + if (baseindex_pool != NULL) { +#ifdef DEBUG_INDLIST +#endif + /* register first index entry for this track */ + baseindex_pool[ii - starttrack].next = NULL; + baseindex_pool[ii - starttrack].frameoffset = StartSector; + global.trackindexlist[ii] = &baseindex_pool[ii - starttrack]; +#ifdef DEBUG_INDLIST +#endif + } else { + global.trackindexlist[ii] = NULL; + } + last_index_entry = global.trackindexlist[ii]; + + if (LastIndex < 2) { + register_index_position(n_0_transition, &last_index_entry); + continue; + } + + if ((global.verbose & SHOW_INDICES) && LastIndex > 1) + fprintf(stderr, "\rtrack %2d has %d indices, index table (pairs of 'index: frame offset')\n", ii, LastIndex); + + startindex = 0; + endindex = LastIndex; + + for (j = startindex; j <= endindex; j++) { + int IndexOffset; + + /* this track has indices */ + +#ifdef USE_LINEAR_SEARCH + /* do a linear search */ + IndexOffset = linear_search(j, StartSector, Get_EndSector(ii), ii); +#else + /* do a binary search */ + IndexOffset = binary_search(j, StartSector, Get_EndSector(ii), ii); +#endif + + if (IndexOffset != -1) { + StartSector = IndexOffset; + } + + if (j == 1) + last_index_entry->frameoffset = IndexOffset; + else if (j > 1) + register_index_position(IndexOffset, &last_index_entry); + + if ( IndexOffset == -1 ) { + if (global.verbose & SHOW_INDICES) { + if (global.gui == 0) { + fprintf(stderr, "%2u: N/A ",j); + if (((j + 1) % 8) == 0) fputs("\n", stderr); + } else { + fprintf(stderr, "\rT%02d I%02u N/A\n",ii,j); + } + } + } else { + if (global.verbose & SHOW_INDICES) { + if (global.gui == 0) { + fprintf(stderr, + "%2u:%6lu ", + j, + IndexOffset-Get_AudioStartSector(ii) + ); + if (((j + 1) % 8) == 0) fputs("\n", stderr); + } else { + fprintf(stderr, + "\rT%02d I%02u %06lu\n", + ii, + j, + IndexOffset-Get_AudioStartSector(ii) + ); + } + } + + if (track == ii && cd_index == j) { + retval = IndexOffset-Get_AudioStartSector(ii); + } + } /* if IndexOffset */ + } /* for index */ + register_index_position(n_0_transition, &last_index_entry); + + /* sanity check. clear all consecutive nonindex entries (frameoffset -1) from the end. */ + { + index_list *ip = global.trackindexlist[ii]; + index_list *iq = NULL; + index_list *lastgood = iq; + + while (ip != NULL) + { + if (ip->frameoffset == -1) + { + /* no index available */ + if (lastgood == NULL) + { + /* if this is the first one in a sequence, store predecessor */ + lastgood = iq; + } + } else { + /* this is a valid index, reset marker */ + lastgood = NULL; + } + + iq = ip; + ip = ip->next; + } + /* terminate chain at the last well defined entry. */ + if (lastgood != NULL) + lastgood->next = NULL; + } + + if (global.gui == 0 && (global.verbose & SHOW_INDICES) + && ii != endtrack) + fputs("\n", stderr); + } /* for tracks */ + if (global.gui == 0 && (global.verbose & SHOW_INDICES)) + fputs("\n", stderr); + if (playing != 0) StopPlay(get_scsi_p()); + + EnableCdda(usalp, 0, 0); + EnableCdda(usalp, 1, CD_FRAMESIZE_RAW); + + return retval; +} + +static unsigned char MCN[14]; + +static void Set_MCN(unsigned char *MCN_arg) +{ + memcpy(MCN, MCN_arg, 14); + MCN[13] = '\0'; +} + +unsigned char *Get_MCN(void) +{ + return MCN; +} + + +static TOC g_toc [MAXTRK+1]; /* hidden track + 100 regular tracks */ + +/*#define IS_AUDIO(i) (!(g_toc[i].bFlags & 0x40))*/ + +int +TOC_entries(unsigned tracks, unsigned char *a, unsigned char *b, int binvalid) +{ + int i; + for (i = 1; i <= (int)tracks; i++) { + unsigned char *p; + unsigned long dwStartSector; + + if (binvalid) { + p = a + 8*(i-1); + + g_toc[i].bFlags = p[1]; + g_toc[i].bTrack = p[2]; + g_toc[i].ISRC[0] = 0; + dwStartSector = a_to_u_4_byte(p+4); + g_toc[i].dwStartSector = dwStartSector; + lba_2_msf((long)dwStartSector, + &g_toc[i].mins, + &g_toc[i].secs, + &g_toc[i].frms); + } else { + p = b + 8*(i-1); + g_toc[i].bFlags = p[1]; + g_toc[i].bTrack = p[2]; + g_toc[i].ISRC[0] = 0; + if ((int)((p[5]*60 + p[6])*75 + p[7]) >= 150) { + g_toc[i].dwStartSector = (p[5]*60 + p[6])*75 + p[7] -150; + } else { + g_toc[i].dwStartSector = 0; + } + g_toc[i].mins = p[5]; + g_toc[i].secs = p[6]; + g_toc[i].frms = p[7]; + } + } + return 0; +} + +void toc_entry(unsigned nr, unsigned flag, unsigned tr, unsigned char *ISRC, + unsigned long lba, int m, int s, int f) +{ + if (nr > MAXTRK) return; + + g_toc[nr].bFlags = flag; + g_toc[nr].bTrack = tr; + if (ISRC) { + strncpy((char *)g_toc[nr].ISRC, (char *)ISRC, + sizeof(g_toc[nr].ISRC) -1); + g_toc[nr].ISRC[sizeof(g_toc[nr].ISRC) -1] = '\0'; + } + g_toc[nr].dwStartSector = lba; + g_toc[nr].mins = m; + g_toc[nr].secs = s; + g_toc[nr].frms = f; +} + +int patch_real_end(unsigned long sector) +{ + g_toc[cdtracks+1].dwStartSector = sector; + return 0; +} + +static int patch_cd_extra(unsigned track, unsigned long sector) +{ + if (track <= cdtracks) + g_toc[track].dwStartSector = sector; + return 0; +} + +static int restrict_tracks_illleadout(void) +{ + struct TOC *o = &g_toc[cdtracks+1]; + int i; + for (i = cdtracks; i >= 0; i--) { + struct TOC *p = &g_toc[i]; + if (GETSTART(o) > GETSTART(p)) break; + } + patch_cd_extra(i+1, GETSTART(o)); + cdtracks = i; + + return 0; +} + +static void Set_ISRC(int track, const unsigned char *ISRC_arg) +{ + if (track <= (int)cdtracks) { + memcpy(Get_ISRC(track), ISRC_arg, 16); + } +} + + +unsigned char *Get_ISRC(unsigned long p_track) +{ + if (p_track <= cdtracks) + return g_toc[p_track].ISRC; + return NULL; +} + +static void patch_to_audio(unsigned long p_track) +{ + if (p_track <= cdtracks) + g_toc[p_track].bFlags &= ~0x40; +} + +int Get_Flags(unsigned long p_track) +{ + if (p_track <= cdtracks) + return g_toc[p_track].bFlags; + return -1; +} + +int Get_Mins(unsigned long p_track) +{ + if (p_track <= cdtracks) + return g_toc[p_track].mins; + return -1; +} + +int Get_Secs(unsigned long p_track) +{ + if (p_track <= cdtracks) + return g_toc[p_track].secs; + return -1; +} + +int Get_Frames(unsigned long p_track) +{ + if (p_track <= cdtracks) + return g_toc[p_track].frms; + return -1; +} + +int Get_Preemphasis(unsigned long p_track) +{ + if (p_track <= cdtracks) + return g_toc[p_track].bFlags & 0x10; + return -1; +} + +static void Set_SCMS(unsigned long p_track) +{ + g_toc[p_track].SCMS = 1; +} + +int Get_SCMS(unsigned long p_track) +{ + if (p_track <= cdtracks) + return g_toc[p_track].SCMS; + return -1; +} + +int Get_Copyright(unsigned long p_track) +{ + if (p_track <= cdtracks) { + if (g_toc[p_track].SCMS) return 1; + return ((int)g_toc[p_track].bFlags & 0x20) >> 4; + } + return -1; +} + +int Get_Datatrack(unsigned long p_track) +{ + if (p_track <= cdtracks) + return g_toc[p_track].bFlags & 0x40; + return -1; +} + +int Get_Channels(unsigned long p_track) +{ + if (p_track <= cdtracks) + return g_toc[p_track].bFlags & 0x80; + return -1; +} + +int Get_Tracknumber(unsigned long p_track) +{ + if (p_track <= cdtracks) + return g_toc[p_track].bTrack; + return -1; +} + +static int useHiddenTrack(void) +{ + return 0; +} + + + +static void it_reset(struct iterator *this); + +static void it_reset(struct iterator *this) +{ + this->index = this->startindex; +} + + +static int it_hasNextTrack(struct iterator *this); +static struct TOC *it_getNextTrack(struct iterator *this); + +static int it_hasNextTrack(struct iterator *this) +{ + return this->index <= (int)cdtracks+1; +} + + + +static struct TOC *it_getNextTrack(struct iterator *this) +{ + /* if ( (*this->hasNextTrack)(this) == 0 ) return NULL; */ + if ( this->index > (int)cdtracks+1 ) return NULL; + + return &g_toc[ this->index++ ]; +} + + +static void InitIterator(struct iterator *iter, unsigned long p_track) +{ + if (iter == NULL) return; + + iter->index = iter->startindex = useHiddenTrack() ? 0 : p_track; + iter->reset = it_reset; + iter->getNextTrack = it_getNextTrack; + iter->hasNextTrack = it_hasNextTrack; +} + +#if 0 +static struct iterator *NewIterator(void); + +static struct iterator *NewIterator () +{ + struct iterator *retval; + + retval = malloc (sizeof(struct iterator)); + if (retval != NULL) { + InitIterator(retval, 1); + } + return retval; +} +#endif + +long Get_AudioStartSector(unsigned long p_track) +{ +#if 1 + if (p_track == CDROM_LEADOUT) + p_track = cdtracks + 1; + + if (p_track <= cdtracks +1 + && IS__AUDIO(&g_toc[p_track])) + return GETSTART(&g_toc[p_track]); +#else + static struct iterator i; + if (i.reset == NULL) InitIterator(&i, p_track); + else i.reset(&i); + + if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT; + + while (i.hasNextTrack(&i)) { + TOC *p = i.getNextTrack(&i); + + if (GETTRACK(p) == p_track) { + if (IS__DATA(p)) { + return -1; + } + return GETSTART(p); + } + } +#endif + return -1; +} + + +long Get_StartSector(unsigned long p_track) +{ +#if 1 + if (p_track == CDROM_LEADOUT) + p_track = cdtracks + 1; + + if (p_track <= cdtracks +1) + return GETSTART(&g_toc[p_track]); +#else + static struct iterator i; + if (i.reset == NULL) InitIterator(&i, p_track); + else i.reset(&i); + + if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT; + + while (i.hasNextTrack(&i)) { + TOC *p = i.getNextTrack(&i); + + if (GETTRACK(p) == p_track) { + return GETSTART(p); + } + } +#endif + return -1; +} + + +long Get_EndSector(unsigned long p_track) +{ +#if 1 + if (p_track <= cdtracks) + return GETSTART(&g_toc[p_track+1])-1; +#else + static struct iterator i; + if (i.reset == NULL) InitIterator(&i, p_track); + else i.reset(&i); + + if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT; + + while (i.hasNextTrack(&i)) { + TOC *p = i.getNextTrack(&i); + if (GETTRACK(p) == p_track) { + p = i.getNextTrack(&i); + if (p == NULL) { + return -1; + } + return GETSTART(p)-1; + } + } +#endif + return -1; +} + +long FirstTrack(void) +{ + static struct iterator i; + if (i.reset == NULL) InitIterator(&i, 1); + else i.reset(&i); + + if (i.hasNextTrack(&i)) { + return GETTRACK(i.getNextTrack(&i)); + } + return -1; +} + +long FirstAudioTrack(void) +{ + static struct iterator i; + if (i.reset == NULL) InitIterator(&i, 1); + else i.reset(&i); + + while (i.hasNextTrack(&i)) { + TOC *p = i.getNextTrack(&i); + unsigned ii = GETTRACK(p); + + if (ii == CDROM_LEADOUT) break; + if (IS__AUDIO(p)) { + return ii; + } + } + return -1; +} + +long FirstDataTrack(void) +{ + static struct iterator i; + if (i.reset == NULL) InitIterator(&i, 1); + else i.reset(&i); + + while (i.hasNextTrack(&i)) { + TOC *p = i.getNextTrack(&i); + if (IS__DATA(p)) { + return GETTRACK(p); + } + } + return -1; +} + +long LastTrack(void) +{ + return g_toc[cdtracks].bTrack; +} + +long LastAudioTrack(void) +{ + long j = -1; + static struct iterator i; + if (i.reset == NULL) InitIterator(&i, 1); + else i.reset(&i); + + while (i.hasNextTrack(&i)) { + TOC *p = i.getNextTrack(&i); + if (IS__AUDIO(p) && (GETTRACK(p) != CDROM_LEADOUT)) { + j = GETTRACK(p); + } + } + return j; +} + +long Get_LastSectorOnCd(unsigned long p_track) +{ + long LastSec = 0; + static struct iterator i; + + if (global.illleadout_cd && global.reads_illleadout) + return 150+(99*60+59)*75+74; + + if (i.reset == NULL) InitIterator(&i, p_track); + else i.reset(&i); + + if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT; + + while (i.hasNextTrack(&i)) { + TOC *p = i.getNextTrack(&i); + + if (GETTRACK(p) < p_track) + continue; + + LastSec = GETSTART(p); + + if (IS__DATA(p)) break; + } + return LastSec; +} + +int Get_Track(unsigned long sector) +{ + static struct iterator i; + if (i.reset == NULL) InitIterator(&i, 1); + else i.reset(&i); + + if (i.hasNextTrack(&i)) { + TOC *o = i.getNextTrack(&i); + while (i.hasNextTrack(&i)) { + TOC *p = i.getNextTrack(&i); + if ((GETSTART(o) <= sector) && (GETSTART(p) > sector)) { + if (IS__DATA(o)) { + return -1; + } else { + return GETTRACK(o); + } + } + o = p; + } + } + return -1; +} + +int CheckTrackrange(unsigned long from, unsigned long upto) +{ + static struct iterator i; + if (i.reset == NULL) InitIterator(&i, from); + else i.reset(&i); + + while (i.hasNextTrack(&i)) { + TOC *p = i.getNextTrack(&i); + + if (GETTRACK(p) < from) + continue; + + if (GETTRACK(p) == upto) + return 1; + + /* data tracks terminate the search */ + if (IS__DATA(p)) + return 0; + } + /* track not found */ + return 0; +} + +#ifdef USE_PARANOIA +long cdda_disc_firstsector(void *d); + +long cdda_disc_firstsector(void *d) +{ + return Get_StartSector(FirstAudioTrack()); +} + +int cdda_tracks(void *d); + +int cdda_tracks(void *d) +{ + return LastAudioTrack() - FirstAudioTrack() +1; +} + +int cdda_track_audiop(void *d, int track); + +int cdda_track_audiop(void *d, int track) +{ + return Get_Datatrack(track) == 0; +} + +long cdda_track_firstsector(void *d, int track); + +long cdda_track_firstsector(void *d, int track) +{ + return Get_AudioStartSector(track); +} + +long cdda_track_lastsector(void *d, int track); + +long cdda_track_lastsector(void *d, int track) +{ + return Get_EndSector(track); +} + +long cdda_disc_lastsector(void *d); + +long cdda_disc_lastsector(void *d) +{ + return Get_LastSectorOnCd(cdtracks) - 1; +} + +int cdda_sector_gettrack(void *d,long sector); + +int cdda_sector_gettrack(void *d, long sector) +{ + return Get_Track(sector); +} + +#endif