/* * 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); scanf("%u", &user_choice); /* 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