2 * This file has been modified for the cdrkit suite.
4 * The behaviour and appearence of the program code below can differ to a major
5 * extent from the version distributed by the original author(s).
7 * For details, see Changelog file distributed with the cdrkit package. If you
8 * received this file from another source then ask the distributing person for
9 * a log of modifications.
13 /* @(#)toc.c 1.57 06/02/19 Copyright 1998-2003 Heiko Eissfeldt */
15 * Copyright: GNU Public License 2 applies
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2, or (at your option)
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 * CDDA2WAV (C) Heiko Eissfeldt heiko@hexco.de
32 * CDDB routines (C) Ti Kan and Steve Scherf
41 #include <unixstd.h> /* sleep */
48 #include <sys/ioctl.h>
57 #include <usal/scsitransp.h>
60 #include "byteorder.h"
61 #include "interface.h"
67 #include "exitcodes.h"
70 int Get_Mins(unsigned long p_track);
71 int Get_Secs(unsigned long p_track);
72 int Get_Frames(unsigned long p_track);
73 int Get_Flags(unsigned long p_track);
74 int Get_SCMS(unsigned long p_track);
77 #if defined USE_REMOTE
79 /* fix OS/2 compilation */
81 #define gethostid nogethostid
83 #include <sys/socket.h>
85 #include <netinet/in.h>
86 #if defined(HAVE_NETDB_H) && !defined(HOST_NOT_FOUND) && \
87 !defined(_INCL_NETDB_H)
94 int have_multisession;
100 static void UpdateTrackData(int p_num);
101 static void UpdateIndexData(int p_num);
102 static void UpdateTimeData(int p_min, int p_sec, int p_frm);
103 static unsigned int is_multisession(void);
104 static unsigned int get_end_of_last_audio_track(unsigned mult_off);
105 static int cddb_sum(int n);
106 static void dump_extra_info(unsigned from);
107 static int GetIndexOfSector(unsigned sec, unsigned track);
108 static int patch_cd_extra(unsigned track, unsigned long sector);
109 static void patch_to_audio(unsigned long p_track);
110 static int restrict_tracks_illleadout(void);
111 static void Set_MCN(unsigned char *MCN_arg);
112 static void Set_ISRC(int track, const unsigned char *ISRC_arg);
113 static void InitIterator(struct iterator *iter, unsigned long p_track);
115 static unsigned char g_track=0xff, g_index=0xff; /* current track, index */
117 /* Conversion function: from logical block adresses to minute,second,frame
119 int lba_2_msf(long lba, int *m, int *s, int *f)
121 #ifdef __follow_redbook__
122 if (lba >= -150 && lba < 405000) { /* lba <= 404849 */
127 } else if (lba >= -45150 && lba <= -151) {
141 /* print the track currently read */
142 static void UpdateTrackData(int p_num)
144 if (global.quiet == 0) {
145 fprintf (stderr, "\ntrack: %.2d, ", p_num); fflush(stderr);
147 g_track = (unsigned char) p_num;
151 /* print the index currently read */
152 static void UpdateIndexData(int p_num)
154 if (global.quiet == 0) {
155 fprintf (stderr, "index: %.2d\n", p_num); fflush(stderr);
157 g_index = (unsigned char) p_num;
161 /* print the time of track currently read */
162 static void UpdateTimeData(int p_min, int p_sec, int p_frm)
164 if (global.quiet == 0) {
165 fprintf (stderr, "time: %.2d:%.2d.%.2d\r", p_min, p_sec, p_frm);
170 void AnalyzeQchannel(unsigned frame)
174 if (trackindex_disp != 0) {
175 sub_ch = ReadSubQ(get_scsi_p(), GET_POSITIONDATA,0);
176 /* analyze sub Q-channel data */
177 if (sub_ch->track != g_track ||
178 sub_ch->index != g_index) {
179 UpdateTrackData (sub_ch->track);
180 UpdateIndexData (sub_ch->index);
184 UpdateTimeData ((unsigned char) (frame / (60*75)),
185 (unsigned char) ((frame % (60*75)) / 75),
186 (unsigned char) (frame % 75));
189 unsigned cdtracks = 0;
191 int no_disguised_audiotracks(void)
193 /* we can assume no audio tracks according to toc here. */
194 /* read a data sector from the first data track */
195 unsigned char p[3000];
197 get_scsi_p()->silent++;
198 retval = 1 == ReadCdRomData(get_scsi_p(), p, Get_StartSector(1), 1);
199 get_scsi_p()->silent--;
202 fprintf(stderr, "Warning: wrong track types found: patching to audio...\n");
203 for (i = 0; i < cdtracks; i++)
210 #undef SIM_ILLLEADOUT
213 int retval = (*doReadToc)( get_scsi_p() );
214 #if defined SIM_ILLLEADOUT
215 g_toc[cdtracks+1] = 20*75;
220 static int can_read_illleadout(void);
222 static int can_read_illleadout(void)
224 SCSI *usalp = get_scsi_p();
226 UINT4 buffer [CD_FRAMESIZE_RAW/4];
227 if (global.illleadout_cd == 0) return 0;
230 global.reads_illleadout =
231 ReadCdRom(usalp, buffer, Get_AudioStartSector(CDROM_LEADOUT), 1);
233 return global.reads_illleadout;
237 unsigned find_an_off_sector(unsigned lSector, unsigned SectorBurstVal);
239 unsigned find_an_off_sector(unsigned lSector, unsigned SectorBurstVal)
241 long track_of_start = Get_Track(lSector);
242 long track_of_end = Get_Track(lSector + SectorBurstVal -1);
243 long start = Get_AudioStartSector(track_of_start);
244 long end = Get_EndSector(track_of_end);
246 if (lSector - start > end - lSector + SectorBurstVal -1)
253 #include "scsi_cmds.h"
257 int handle_cdtext(void)
260 if (bufferTOC[0] == 0 && bufferTOC[1] == 0) {
265 /* do a quick scan over all pack type indicators */
269 int len = (bufferTOC[0] << 8) | bufferTOC[1];
271 len = min(len, 2048);
272 for (i = 0; i < len-4; i += 18) {
273 if (bufferTOC[4+i] < 0x80 || bufferTOC[4+i] > 0x8f) {
277 have_CD_text = len > 4 && count_fails < 3;
292 #if defined CDROMMULTISESSION
297 #include "cd_extra.c"
300 static unsigned session_start;
302 A Cd-Extra is detected, if it is a multisession CD with
303 only audio tracks in the first session and a data track
306 static unsigned is_multisession(void)
309 #if defined CDROMMULTISESSION
311 * FIXME: we would have to do a ioctl (CDROMMULTISESSION)
312 * for the cdrom device associated with the generic device
313 * not just AUX_DEVICE
315 struct cdrom_multisession ms_str;
317 if (interface == GENERIC_SCSI)
318 tmp_fd = open (global.aux_name, O_RDONLY);
320 tmp_fd = global.cooked_fd;
325 ms_str.addr_format = CDROM_LBA;
326 result = ioctl(tmp_fd, CDROMMULTISESSION, &ms_str);
328 if (global.verbose != 0)
329 perror("multi session ioctl not supported: ");
332 fprintf(stderr, "current ioctl multisession_offset = %u\n", ms_str.addr.lba);
334 if (interface == GENERIC_SCSI)
336 if (ms_str.addr.lba > 0)
337 return ms_str.addr.lba;
342 if (LastAudioTrack() + 1 == FirstDataTrack()) {
343 mult_off = Get_StartSector(FirstDataTrack());
347 fprintf(stderr, "current guessed multisession_offset = %u\n", mult_off);
352 #define SESSIONSECTORS (152*75)
354 The solution is to read the Table of Contents of the first
355 session only (if the drive permits that) and directly use
356 the start of the leadout. If this is not supported, we subtract
357 a constant of SESSIONSECTORS sectors (found heuristically).
359 static unsigned get_end_of_last_audio_track(unsigned mult_off)
363 /* Try to read the first session table of contents.
364 This works for Sony and mmc type drives. */
365 if (ReadLastAudio && (retval = ReadLastAudio(get_scsi_p())) != 0) {
368 return mult_off - SESSIONSECTORS;
372 static void dump_cdtext_info(void);
374 #if defined CDDB_SUPPORT
375 static void emit_cddb_form(char *fname_baseval);
378 #if defined CDINDEX_SUPPORT
379 static void emit_cdindex_form(char *fname_baseval);
383 typedef struct TOC { /* structure of table of contents (cdrom) */
384 unsigned char reserved1;
385 unsigned char bFlags;
386 unsigned char bTrack;
387 unsigned char reserved2;
388 unsigned int dwStartSector;
392 unsigned char ISRC[16];
397 /* Flags contains two fields:
399 : 0 no sub-q-channel information
400 : 1 sub-q-channel contains current position
401 : 2 sub-q-channel contains media catalog number
402 : 3 sub-q-channel contains International Standard
404 : other values reserved
406 bit 3 : when set indicates there are 4 audio channels else 2 channels
407 bit 2 : when set indicates this is a data track else an audio track
408 bit 1 : when set indicates digital copy is permitted else prohibited
409 bit 0 : when set indicates pre-emphasis is present else not present
412 #define GETFLAGS(x) ((x)->bFlags)
413 #define GETTRACK(x) ((x)->bTrack)
414 #define GETSTART(x) ((x)->dwStartSector)
415 #define GETMINS(x) ((x)->mins)
416 #define GETSECS(x) ((x)->secs)
417 #define GETFRAMES(x) ((x)->frms)
418 #define GETISRC(x) ((x)->ISRC)
420 #define IS__PREEMPHASIZED(p) ( (GETFLAGS(p) & 0x10) != 0)
421 #define IS__INCREMENTAL(p) ( (GETFLAGS(p) & 0x10) != 0)
422 #define IS__COPYRESTRICTED(p) (!(GETFLAGS(p) & 0x20) != 0)
423 #define IS__COPYRIGHTED(p) (!(GETFLAGS(p) & 0x20) != 0)
424 #define IS__DATA(p) ( (GETFLAGS(p) & 0x40) != 0)
425 #define IS__AUDIO(p) (!(GETFLAGS(p) & 0x40) != 0)
426 #define IS__QUADRO(p) ( (GETFLAGS(p) & 0x80) != 0)
429 * Iterator interface inspired from Java
434 void (*reset)(struct iterator *this);
435 struct TOC *(*getNextTrack)(struct iterator *this);
436 int (*hasNextTrack)(struct iterator *this);
442 /* The Table of Contents needs to be corrected if we
443 have a CD-Extra. In this case all audio tracks are
444 followed by a data track (in the second session).
445 Unlike for single session CDs the end of the last audio
446 track cannot be set to the start of the following
447 track, since the lead-out and lead-in would then
448 errenously be part of the audio track. This would
449 lead to read errors when trying to read into the
451 So the length of the last track in case of Cd-Extra
454 unsigned FixupTOC(unsigned no_tracks)
459 unsigned real_end = 2000000;
461 /* get the multisession offset in sectors */
462 mult_off = is_multisession();
464 /* if the first track address had been the victim of an underflow,
467 if (Get_StartSector(1) > Get_StartSector(LastTrack())) {
468 fprintf(stderr, "Warning: first track has negative start sector! Setting to zero.\n");
469 toc_entry( 1, Get_Flags(1), Get_Tracknumber(1), Get_ISRC(1), 0, 0, 2, 0 );
473 fprintf(stderr, "current multisession_offset = %u\n", mult_off);
477 if (mult_off > 100) { /* the offset has to have a minimum size */
479 /* believe the multisession offset :-) */
480 /* adjust end of last audio track to be in the first session */
481 real_end = get_end_of_last_audio_track(mult_off);
483 fprintf(stderr, "current end = %u\n", real_end);
486 j = FirstDataTrack();
487 if (LastAudioTrack() + 1 == j) {
488 long sj = Get_StartSector(j);
489 if (sj > (long)real_end) {
490 session_start = mult_off;
491 have_multisession = sj;
494 offset = Read_CD_Extra_Info(sj);
498 dump_extra_info(offset);
505 #if defined USE_REMOTE
506 if (global.disctitle == NULL) {
507 have_CDDB = !request_titles();
510 fprintf(stderr, "Cannot lookup titles: no cddbp support included!\n");
513 #if defined CDINDEX_SUPPORT || defined CDDB_SUPPORT
514 if (have_CD_text || have_CD_extra || have_CDDB) {
515 unsigned long count_audio_tracks = 0;
516 static struct iterator i;
520 while (i.hasNextTrack(&i)) {
521 struct TOC *p = i.getNextTrack(&i);
522 if (IS__AUDIO(p)) count_audio_tracks++;
525 if (count_audio_tracks > 0 && global.no_cddbfile == 0) {
526 #if defined CDINDEX_SUPPORT
527 emit_cdindex_form(global.fname_base);
529 #if defined CDDB_SUPPORT
530 emit_cddb_form(global.fname_base);
535 if (have_multisession) {
536 /* set start of track to beginning of lead-out */
537 patch_cd_extra(j, real_end);
538 #if defined CD_EXTRA && defined DEBUG_XTRA
539 fprintf(stderr, "setting end of session (track %d) to %u\n", j, real_end);
545 static int cddb_sum(int n)
549 for (ret = 0; n > 0; n /= 10) {
556 void calc_cddb_id(void)
562 for (i = 1; i <= cdtracks; i++) {
563 n += cddb_sum(Get_StartSector(i)/75 + 2);
566 t = Get_StartSector(i)/75 - Get_StartSector(1)/75;
568 global.cddb_id = (n % 0xff) << 24 | (t << 8) | cdtracks;
574 void TestGenerateId(void)
577 unsigned char digest[20], *base64;
581 sha_update(&sha, (unsigned char *)"0123456789", 10);
582 sha_final(digest, &sha);
584 base64 = rfc822_binary((char *)digest, 20, &size);
585 if (strncmp((char*) base64, "h6zsF82dzSCnFsws9nQXtxyKcBY-", size))
589 fprintf(stderr, "The SHA-1 hash function failed to properly generate the\n");
590 fprintf(stderr, "test key.\n");
591 exit(INTERNAL_ERROR);
597 void calc_cdindex_id()
600 unsigned char digest[20], *base64;
609 g_toc[cdtracks].bTrack = 15;
611 g_toc[i++].dwStartSector = 0U;
612 g_toc[i++].dwStartSector = 18641U;
613 g_toc[i++].dwStartSector = 34667U;
614 g_toc[i++].dwStartSector = 56350U;
615 g_toc[i++].dwStartSector = 77006U;
616 g_toc[i++].dwStartSector = 106094U;
617 g_toc[i++].dwStartSector = 125729U;
618 g_toc[i++].dwStartSector = 149785U;
619 g_toc[i++].dwStartSector = 168885U;
620 g_toc[i++].dwStartSector = 185910U;
621 g_toc[i++].dwStartSector = 205829U;
622 g_toc[i++].dwStartSector = 230142U;
623 g_toc[i++].dwStartSector = 246659U;
624 g_toc[i++].dwStartSector = 265614U;
625 g_toc[i++].dwStartSector = 289479U;
626 g_toc[i++].dwStartSector = 325732U;
629 sprintf(temp, "%02X", Get_Tracknumber(1));
630 sha_update(&sha, (unsigned char *)temp, 2);
631 sprintf(temp, "%02X", Get_Tracknumber(cdtracks));
632 sha_update(&sha, (unsigned char *)temp, 2);
634 /* the position of the leadout comes first. */
635 sprintf(temp, "%08lX", 150 + Get_StartSector(CDROM_LEADOUT));
636 sha_update(&sha, (unsigned char *)temp, 8);
638 /* now 99 tracks follow with their positions. */
639 for (i = 1; i <= cdtracks; i++) {
640 sprintf(temp, "%08lX", 150+Get_StartSector(i));
641 sha_update(&sha, (unsigned char *)temp, 8);
643 for (i++ ; i <= 100; i++) {
644 sha_update(&sha, (unsigned char *)"00000000", 8);
646 sha_final(digest, &sha);
648 base64 = rfc822_binary((char *)digest, 20, &size);
649 global.cdindex_id = base64;
653 #if defined CDDB_SUPPORT
656 static void escape_and_split(FILE *channel, const char *args, ...)
659 static void escape_and_split(FILE *channel, const char *args, va_dcl va_alist)
669 va_start(marker, args);
674 prefixlen = strlen(args);
676 fputs(args, channel);
678 q = va_arg(marker, char *);
683 fputs("\\\\", channel);
685 fputs("\\t", channel);
687 fputs("\\n", channel);
693 fputc('\n', channel);
694 fputs(args, channel);
699 q = va_arg(marker, char *);
701 fputc('\n', channel);
706 static void emit_cddb_form(char *fname_baseval)
708 static struct iterator i;
709 unsigned first_audio;
714 if (fname_baseval == NULL || fname_baseval[0] == 0)
717 if (!strcmp(fname_baseval,"standard_output")) return;
720 strncpy(fname, fname_baseval, sizeof(fname) -1);
721 fname[sizeof(fname) -1] = 0;
722 pp = strrchr(fname, '.');
724 pp = fname + strlen(fname);
726 strncpy(pp, ".cddb", sizeof(fname) - 1 - (pp - fname));
728 cddb_form = fopen(fname, "w");
729 if (cddb_form == NULL) return;
731 first_audio = FirstAudioTrack();
732 fprintf( cddb_form, "# xmcd\n#\n");
733 fprintf( cddb_form, "# Track frame offsets:\n#\n");
735 while (i.hasNextTrack(&i)) {
736 struct TOC *p = i.getNextTrack(&i);
737 if (GETTRACK(p) == CDROM_LEADOUT) break;
739 "# %lu\n", 150 + Get_AudioStartSector(GETTRACK(p)));
742 fprintf( cddb_form, "#\n# Disc length: %lu seconds\n#\n",
743 (150 + Get_StartSector(CDROM_LEADOUT)) / 75);
744 fprintf( cddb_form, "# Revision: %u\n", global.cddb_revision );
745 fprintf( cddb_form, "# Submitted via: icedax " VERSION "\n" );
747 fprintf( cddb_form, "DISCID=%08lx\n", (unsigned long)global.cddb_id);
749 if (global.disctitle == NULL && global.creator == NULL) {
750 fprintf( cddb_form, "DTITLE=\n");
752 if (global.creator == NULL) {
753 escape_and_split( cddb_form, "DTITLE=", global.disctitle, "");
754 } else if (global.disctitle == NULL) {
755 escape_and_split( cddb_form, "DTITLE=", global.creator, "");
757 escape_and_split( cddb_form, "DTITLE=", global.creator, " / ", global.disctitle, "");
760 if (global.cddb_year != 0)
761 fprintf( cddb_form, "DYEAR=%4u\n", global.cddb_year);
763 fprintf( cddb_form, "DYEAR=\n");
764 fprintf( cddb_form, "DGENRE=%s\n", global.cddb_genre);
767 while (i.hasNextTrack(&i)) {
768 struct TOC *p = i.getNextTrack(&i);
772 if (ii == CDROM_LEADOUT) break;
774 if (global.tracktitle[ii] != NULL) {
776 sprintf(prefix, "TTITLE%d=", ii-1);
777 escape_and_split( cddb_form, prefix, global.tracktitle[ii], "");
779 fprintf( cddb_form, "TTITLE%d=\n", ii-1);
783 if (global.copyright_message == NULL) {
784 fprintf( cddb_form, "EXTD=\n");
786 escape_and_split( cddb_form, "EXTD=", "Copyright ", global.copyright_message, "");
790 while (i.hasNextTrack(&i)) {
791 struct TOC *p = i.getNextTrack(&i);
796 if (ii == CDROM_LEADOUT) break;
798 fprintf( cddb_form, "EXTT%d=\n", ii-1);
800 fprintf( cddb_form, "PLAYORDER=\n");
804 #if defined USE_REMOTE
807 static int readn(register int fd, register char *ptr, register int nbytes)
811 nread = read(fd, ptr, nbytes);
814 fprintf(stderr, "READ :(%d)", nread);
815 write(2, ptr, nread);
819 perror("socket read error: ");
820 fprintf(stderr, "fd=%d, ptr=%p, nbytes=%d\n", fd, ptr, nbytes);
826 static ssize_t writez(int fd, const char *ptr)
828 size_t nleft, nbytes;
830 nleft = nbytes = strlen(ptr);
833 ssize_t nwritten = write(fd, ptr, nleft);
835 return nwritten; /* return error */
838 fprintf(stderr, "WRITE:%s\n", ptr);
845 return nbytes - nleft;
848 #define SOCKBUFF 2048
850 static void filter_nonprintable(char *c, size_t l)
853 for(i = 0; i < l; ++i) {
854 if(!isprint(c[i]) && !isspace(c[i])) {
861 int process_cddb_titles(int sock_fd, char *inbuff, int readbytes);
862 int process_cddb_titles(int sock_fd, char *inbuff, int readbytes)
867 unsigned char ** target = &global.creator;
870 while (readbytes > 0) {
871 /* do we have a complete line in the buffer? */
872 p = (char *)memchr(inbuff+ind, '\n', readbytes);
873 if (p == NULL) break;
875 /* look for the terminator first */
876 if (!strncmp(".\r\n", inbuff+ind, 3)) {
880 /* kill carriage return */
881 if (p > inbuff+ind && *(p-1) == '\r') {
887 /* handle escaped characters */
890 char *q = inbuff+ind;
892 if (*q++ == '\\' && *q != '\0') {
896 memmove(q, q+1, readbytes - (q-inbuff-ind));
897 } else if (*q == 'n') {
901 memmove(q, q+1, readbytes - (q-inbuff-ind));
902 } else if (*q == 't') {
906 memmove(q, q+1, readbytes - (q-inbuff-ind));
913 /* handle multi line entries concatenate fields */
915 /* TODO if the delimiter is split into two lines, it is not recognized. */
916 if (!strncmp(inbuff+ind, "DTITLE=", 7)) {
917 char *res = strstr(inbuff+ind+7, " / ");
922 /* no limiter found yet */
923 /* copy until the end */
927 /* copy until the limiter */
932 clen = q - (inbuff+ind+7);
933 if (*target == NULL) {
934 *target = malloc(clen+1);
938 *target = realloc(*target, strlen((char *)*target) + clen - 1);
940 if (*target != NULL) {
941 strcat((char *)*target, inbuff+ind+7);
944 /* handle part after the delimiter, if present */
946 target = (unsigned char **)&global.disctitle;
947 /* skip the delimiter */
950 if (*target == NULL) {
951 *target = malloc(clen+1);
955 if (*target != NULL) {
956 strcat((char *)*target, q);
959 } else if (!strncmp(inbuff+ind, "TTITLE", 6)) {
960 char *q = (char *)memchr(inbuff+ind, '=', readbytes);
965 tno = (unsigned)atoi(inbuff+ind+6);
968 if (global.tracktitle[tno] == NULL) {
969 global.tracktitle[tno] = malloc( p - q + 1 );
970 if (global.tracktitle[tno] != NULL)
971 *(global.tracktitle[tno]) = '\0';
973 global.tracktitle[tno] = realloc(global.tracktitle[tno], strlen((char *)global.tracktitle[tno]) + p - q + 1 );
975 if (global.tracktitle[tno] != NULL) {
976 strcat((char *)global.tracktitle[tno], q+1);
980 } else if (!strncmp(inbuff+ind, "DYEAR", 5)) {
981 char *q = (char *)memchr(inbuff+ind, '=', readbytes);
983 sscanf(q, "%d", &global.cddb_year);
985 } else if (!strncmp(inbuff+ind, "DGENRE", 6)) {
986 char *q = (char *)memchr(inbuff+ind, '=', readbytes);
988 /* patch from Joe Nuzman, thanks */
989 /* might have significant whitespace */
990 strncpy(global.cddb_genre, q, sizeof(global.cddb_genre)-1);
991 /* always have a terminator */
992 global.cddb_genre[sizeof(global.cddb_genre)-1] = '\0';
994 } else if (!strncmp(inbuff+ind, "# Revision: ", 12)) {
995 char *q = inbuff+ind+11;
996 sscanf(q, "%d", &global.cddb_revision);
997 global.cddb_revision++;
999 readbytes -= (p - inbuff -ind) + 1;
1000 ind = (p - inbuff) + 1;
1004 memmove(inbuff, inbuff+ind, readbytes);
1005 newbytes = readn(sock_fd, inbuff+readbytes, SOCKBUFF-readbytes);
1007 fprintf(stderr, "Could not read from socket.\n");
1008 return 0; /* Caller checks for != 1 */
1010 filter_nonprintable(inbuff+readbytes, newbytes);
1013 readbytes += newbytes;
1016 } while (!(finished || readbytes == 0));
1020 static int handle_userchoice(char *p, unsigned size);
1022 static int handle_userchoice(char *p, unsigned size)
1025 unsigned user_choice;
1032 while ((q = (char *)memchr(q, '\n', size - (q-p))) != NULL) {
1038 /* handle escaped characters */
1043 if (*r++ == '\\' && *r != '\0') {
1046 memmove(r, r+1, size - (r-p));
1047 } else if (*r == 'n') {
1050 memmove(r, r+1, size - (r-p));
1051 } else if (*r == 't') {
1054 memmove(r, r+1, size - (r-p));
1062 fprintf(stderr, "%u entries found:\n", nr);
1063 for (q = (char *)memchr(q, '\n', size - (q-p)), o = p, i = 0; i < nr; i++) {
1065 fprintf(stderr, "%02u: %s\n", i, o);
1067 q = (char *)memchr(q, '\n', size - (q-p));
1069 fprintf(stderr, "%02u: ignore\n", i);
1071 /* get user response. */
1073 fprintf(stderr, "please choose one (0-%u): ", nr);
1074 scanf("%u", &user_choice); /* FIXME: check return value */
1075 } while (user_choice > nr);
1077 if (user_choice == nr)
1080 /* skip to choice. */
1082 for (i = 0; i <= (int)user_choice - 1; i++) {
1083 q = (char *)memchr(q, '\0', size - (q-p)) + 1;
1088 /* request disc and track titles from a cddbp server.
1091 * 0 titles have been found exactly (success)
1092 * -1 some communication error happened.
1093 * 1 titles have not been found.
1094 * 2 multiple fuzzy matches have been found.
1097 request_titles(void)
1101 struct sockaddr_in sa;
1104 struct passwd *pw = getpwuid(getuid());
1105 char hostname[HOST_NAME_MAX];
1106 char inbuff[SOCKBUFF];
1107 char outbuff[SOCKBUFF];
1110 unsigned cat_offset;
1114 sock_fd = socket(AF_INET, SOCK_STREAM, 0);
1116 perror("cddb socket failed: ");
1126 if (global.cddbp_server != NULL)
1127 he = gethostbyname(global.cddbp_server);
1129 he = gethostbyname(CDDBHOST /*"freedb.freedb.org"*/);
1132 perror("cddb cannot resolve freedb host: ");
1133 he = malloc(sizeof(struct hostent));
1134 memset(he, 0 , sizeof(struct hostent));
1136 he->h_addrtype = AF_INET;
1137 he->h_addr_list = malloc(4);
1138 he->h_addr_list[0] = malloc(4);
1139 ((struct in_addr *)(he->h_addr_list[0]))->s_addr =
1140 /* kingfisher.berlios.de freedb.freedb.de */
1141 htonl(UINT_C(0xc3254d85)); /*0xc2610412*/
1142 he->h_name = "freedb.freedb.org";
1149 /* save result data IMMEDIATELY!! */
1150 memset(&sa, 0 , sizeof(struct sockaddr_in));
1151 sa.sin_family = he->h_addrtype; /* AF_INET; */
1152 sa.sin_addr.s_addr = ((struct in_addr *)((he->h_addr_list)[0]))->s_addr;
1155 if (global.cddbp_port == NULL)
1156 se = getservbyname("cddbp-alt", "tcp");
1159 if (global.cddbp_port == NULL) {
1160 se = getservbyname("cddbp", "tcp");
1163 se = malloc(sizeof(struct servent));
1164 memset(se, 0 , sizeof(struct servent));
1165 se->s_port = htons(CDDBPORT /*8880*/);
1167 perror("cddb cannot resolve cddbp or cddbp-alt port:\n ");
1173 if (global.cddbp_port != NULL) {
1174 se->s_port = htons(atoi(global.cddbp_port));
1177 sa.sin_port = se->s_port;
1180 if (0 > connect(sock_fd, (struct sockaddr *)&sa,
1181 sizeof(struct sockaddr_in))) {
1182 perror("cddb connect failed: ");
1188 readbytes = readn(sock_fd, inbuff, sizeof(inbuff));
1189 if (readbytes < 0) {
1190 fprintf(stderr, "Could not read from socket\n");
1195 if (strncmp(inbuff, "200 ", 4) && strncmp(inbuff, "201 ", 4)) {
1196 if(readbytes == sizeof(inbuff))
1198 inbuff[readbytes] = '\0';
1199 filter_nonprintable(inbuff, readbytes);
1200 fprintf(stderr, "bad status from freedb server during sign-on banner: %s\n", inbuff);
1207 if (0 > gethostname(hostname, sizeof(hostname)))
1208 strcpy(hostname, "unknown_host");
1209 hostname[sizeof(hostname)-1] = '\0';
1210 writez(sock_fd, "cddb hello ");
1212 BOOL space_err = FALSE;
1213 BOOL ascii_err = FALSE;
1214 /* change spaces to underscores */
1215 char *q = pw->pw_name;
1216 while (*q != '\0') {
1221 "Warning: Space in user name '%s'.\n",
1226 if (*q < ' ' || *q > '~') {
1230 "Warning: Nonascii character in user name '%s'.\n",
1237 writez(sock_fd, pw->pw_name);
1238 writez(sock_fd, " ");
1240 writez(sock_fd, "unknown ");
1243 /* change spaces to underscores */
1246 BOOL space_err = FALSE;
1247 BOOL ascii_err = FALSE;
1249 while (*q != '\0') {
1254 "Warning: Space in hostname '%s'.\n",
1259 if (*q < ' ' || *q > '~') {
1263 "Warning: Nonascii character in hostname '%s'.\n",
1272 writez(sock_fd, hostname);
1273 writez(sock_fd, " icedax " VERSION "\n");
1275 readbytes = readn(sock_fd, inbuff, sizeof(inbuff));
1276 if (readbytes < 0) {
1277 fprintf(stderr, "Could not read from socket\n");
1281 if (strncmp(inbuff, "200 ", 4)) {
1282 if(readbytes == sizeof(inbuff))
1284 inbuff[readbytes] = '\0';
1285 filter_nonprintable(inbuff, readbytes);
1286 fprintf(stderr, "bad status from freedb server during hello: %s\n", inbuff);
1291 /* enable new protocol variant. Weird command here, no cddb prefix ?!?! */
1292 writez(sock_fd, "proto\n");
1293 readbytes = readn(sock_fd, inbuff, sizeof(inbuff));
1294 if (readbytes < 0) {
1295 fprintf(stderr, "Could not read from socket\n");
1299 /* check for errors and maximum supported protocol level */
1300 if (strncmp(inbuff, "201 ", 4) > 0) {
1301 if(readbytes == sizeof(inbuff))
1303 inbuff[readbytes] = '\0';
1304 filter_nonprintable(inbuff, readbytes);
1305 fprintf(stderr, "bad status from freedb server during proto command: %s\n", inbuff);
1310 /* check the supported protocol level */
1311 if (!memcmp(inbuff, "200 CDDB protocol level: current 1, supported ", 46)) {
1312 char *q = strstr(inbuff, " supported ");
1317 sscanf(q, "%u\n", &pr_level);
1321 sprintf(inbuff, "proto %1u\n", pr_level);
1322 writez(sock_fd, inbuff);
1323 readbytes = readn(sock_fd, inbuff, sizeof(inbuff));
1324 if (readbytes < 0) {
1325 fprintf(stderr, "Could not read from socket\n");
1329 /* check for errors and maximum supported protocol level */
1330 if (strncmp(inbuff, "201 ", 4) > 0) {
1331 if(readbytes == sizeof(inbuff))
1333 inbuff[readbytes] = '\0';
1334 filter_nonprintable(inbuff,
1336 fprintf(stderr, "bad status from freedb server during proto x: %s\n", inbuff);
1344 /* format query string */
1346 #define CDDPB_INCLUDING_DATATRACKS
1347 #ifdef CDDPB_INCLUDING_DATATRACKS
1348 sprintf(outbuff, "cddb query %08lx %ld ", (unsigned long)global.cddb_id, LastTrack() - FirstTrack() + 1);
1349 /* first all leading datatracks */
1351 int j = FirstAudioTrack();
1354 for (i = FirstTrack(); i < j; i++) {
1355 sprintf(outbuff + strlen(outbuff), "%ld ", 150 + Get_StartSector(i));
1359 sprintf(outbuff, "cddb query %08lx %ld ", global.cddb_id, LastAudioTrack() - FirstAudioTrack() + 1);
1361 /* all audio tracks */
1362 for (i = FirstAudioTrack(); i != -1 && i <= LastAudioTrack(); i++) {
1363 sprintf(outbuff + strlen(outbuff), "%ld ", 150 + Get_AudioStartSector(i));
1365 #ifdef CDDPB_INCLUDING_DATATRACKS
1366 /* now all trailing datatracks */
1367 for (; i != -1 && i <= LastTrack(); i++) {
1368 sprintf(outbuff + strlen(outbuff), "%ld ", 150 + Get_StartSector(i));
1370 sprintf(outbuff + strlen(outbuff), "%lu\n",
1371 (150 + Get_StartSector(CDROM_LEADOUT)) / 75);
1373 sprintf(outbuff + strlen(outbuff), "%lu\n",
1374 (150 + Get_LastSectorOnCd(FirstAudioTrack())) / 75);
1376 /* strcpy(outbuff, "cddb query 9709210c 12 150 12010 33557 50765 65380 81467 93235 109115 124135 137732 152575 166742 2339\n"); */
1377 /* strcpy(outbuff, "cddb query 03015501 1 296 344\n"); */
1378 writez(sock_fd, outbuff);
1380 readbytes = readn(sock_fd, inbuff, sizeof(inbuff) - 1);
1381 if (readbytes < 0) {
1382 fprintf(stderr, "Could not read from socket\n");
1386 inbuff[readbytes] = '\0';
1387 filter_nonprintable(inbuff, readbytes);
1389 if (!strncmp(inbuff, "210 ", 4)
1390 || !strncmp(inbuff, "211 ", 4)) {
1391 /* Check if there are really multiple entries. */
1392 char *p = (char *)memchr(inbuff, '\n', readbytes-1);
1394 if (p != NULL) cat_offset = p+1 - inbuff;
1396 if (p) p = (char *)memchr(p+1, '\n', inbuff+readbytes - p);
1398 if (p) p = (char *)memchr(p+1, '\n', inbuff+readbytes - p);
1400 if (p) p = (char *)memchr(p+1, '\n', inbuff+readbytes - p);
1402 /* multiple entries */
1403 switch (global.cddbp) {
1404 case 2: /* take the first entry */
1406 case 1: /* ask user */
1408 int userret = handle_userchoice(inbuff+cat_offset, readbytes - cat_offset);
1409 if (userret == -1) {
1410 /* ignore any selection */
1414 cat_offset += userret;
1418 fprintf(stderr, "multiple entries found: %s\n", inbuff);
1424 } else if (strncmp(inbuff, "200 ", 4)) {
1425 if (!strncmp(inbuff, "202 ", 4)) {
1426 fprintf(stderr, "no cddb entry found: %s\n", inbuff);
1429 fprintf(stderr, "bad status from freedb server during query: %s\n%s", inbuff, outbuff);
1434 sscanf(inbuff + cat_offset, "%s %x", category, &disc_id );
1438 sprintf(inbuff, "cddb read %s %08x\n", category, disc_id);
1439 writez(sock_fd, inbuff);
1441 /* read status and first buffer size. */
1442 readbytes = readn(sock_fd, inbuff, sizeof(inbuff));
1443 if (readbytes < 0) {
1444 fprintf(stderr, "Could not read from socket\n");
1448 filter_nonprintable(inbuff, readbytes);
1449 if (strncmp(inbuff, "210 ", 4)) {
1450 if(readbytes == sizeof(inbuff))
1452 inbuff[readbytes] = '\0';
1453 fprintf(stderr, "bad status from freedb server during read: %s\n", inbuff);
1458 if (1 != process_cddb_titles(sock_fd, inbuff, readbytes)) {
1459 fprintf(stderr, "cddb read finished not correctly!\n");
1464 writez(sock_fd, "quit\n");
1465 readbytes = readn(sock_fd, inbuff, sizeof(inbuff));
1466 if (readbytes < 0) {
1467 fprintf(stderr, "Could not read from socket\n");
1471 if (strncmp(inbuff, "230 ", 4)) {
1472 if(readbytes == sizeof(inbuff))
1474 inbuff[readbytes] = '\0';
1475 filter_nonprintable(inbuff, readbytes);
1476 fprintf(stderr, "bad status from freedb server during quit: %s\n", inbuff);
1487 #if defined CDINDEX_SUPPORT
1489 static int IsSingleArtist(void);
1491 /* check, if there are more than one track creators */
1492 static int IsSingleArtist(void)
1494 static struct iterator i;
1495 InitIterator(&i, 1);
1497 while (i.hasNextTrack(&i)) {
1498 struct TOC *p = i.getNextTrack(&i);
1501 if (IS__DATA(p) || GETTRACK(p) == CDROM_LEADOUT) continue;
1504 if (global.creator && global.trackcreator[ii]
1505 && strcmp((char *) global.creator,
1506 (char *) global.trackcreator[ii]) != 0)
1512 static const char *a2h[255-191] = {
1579 static char *ascii2html(unsigned char *inp)
1581 static size_t buflen = 256;
1582 static char *outline = 0;
1588 outline = malloc(buflen);
1590 fprintf(stderr, "error: memory exhausted\n");
1591 _exit(EXIT_FAILURE);
1595 outline[pos] = '\0';
1597 while (*inp != '\0') {
1599 /* Pick the sequence to insert */
1602 const int c = (unsigned char)*inp;
1604 case '"': insert = """; break;
1605 case '&': insert = "&"; break;
1606 case '<': insert = "<"; break;
1607 case '>': insert = ">"; break;
1608 case 160: insert = " "; break;
1615 insert = a2h[c - 192];
1622 while(pos + l + 1 >= buflen) {
1623 outline = realloc(outline, buflen *= 2);
1625 fprintf(stderr, "error: memory exhausted\n");
1626 _exit(EXIT_FAILURE);
1631 strcpy(&outline[pos], insert);
1640 static void emit_cdindex_form(char *fname_baseval)
1646 if (fname_baseval == NULL || fname_baseval[0] == 0)
1649 strncpy(fname, fname_baseval, sizeof(fname) -1);
1650 fname[sizeof(fname) -1] = 0;
1651 pp = strrchr(fname, '.');
1653 pp = fname + strlen(fname);
1655 strncpy(pp, ".cdindex", sizeof(fname) - 1 - (pp - fname));
1657 cdindex_form = fopen(fname, "w");
1658 if (cdindex_form == NULL) return;
1660 #define CDINDEX_URL "http://www.musicbrainz.org/dtd/CDInfo.dtd"
1662 /* format XML page according to cdindex DTD (see www.musicbrainz.org) */
1663 fprintf( cdindex_form, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<!DOCTYPE CDInfo SYSTEM \"%s\">\n\n<CDInfo>\n",
1666 fprintf( cdindex_form, " <Title>%s</Title>\n",
1667 global.disctitle ? ascii2html(global.disctitle) : "");
1669 * In case of mixed mode and Extra-CD, nonaudio tracks are included!
1671 fprintf( cdindex_form, " <NumTracks>%d</NumTracks>\n\n", cdtracks);
1672 fprintf( cdindex_form, " <IdInfo>\n <DiskId>\n <Id>%s</Id>\n </DiskId>\n", global.cdindex_id);
1674 fprintf( cdindex_form, " </IdInfo>\n\n");
1676 if (IsSingleArtist()) {
1677 static struct iterator i;
1678 InitIterator(&i, 1);
1680 fprintf( cdindex_form, " <SingleArtistCD>\n <Artist>%s</Artist>\n",
1681 global.creator ? ascii2html(global.creator) : "");
1683 while (i.hasNextTrack(&i)) {
1684 struct TOC *p = i.getNextTrack(&i);
1685 int ii = GETTRACK(p);
1687 if (ii == CDROM_LEADOUT) break;
1689 fprintf( cdindex_form,
1690 " <Track Num=\"%d\">\n <Name>%s</Name>\n </Track>\n",
1691 ii, global.tracktitle[ii] ? ascii2html(global.tracktitle[ii]) : "");
1693 fprintf( cdindex_form,
1694 " <Track Num=\"%d\">\n <Name>data track</Name>\n </Track>\n",
1698 fprintf( cdindex_form, " </SingleArtistCD>\n");
1700 static struct iterator i;
1701 InitIterator(&i, 1);
1703 fprintf( cdindex_form, " <MultipleArtistCD>\n");
1705 while (i.hasNextTrack(&i)) {
1706 struct TOC *p = i.getNextTrack(&i);
1707 int ii = GETTRACK(p);
1709 if (ii == CDROM_LEADOUT) break;
1711 fprintf( cdindex_form, " <Artist>%s</Artist>\n",
1712 global.trackcreator[ii] ? ascii2html(global.trackcreator[ii]) : "");
1713 fprintf( cdindex_form, " <Name>%s</Name>\n </Track>\n",
1714 global.tracktitle[ii] ? ascii2html(global.tracktitle[ii]) : "");
1716 fprintf( cdindex_form,
1717 " <Artist>data track</Artist>\n <Name>data track</Name>\n </Track>\n");
1720 fprintf( cdindex_form, " </MultipleArtistCD>\n");
1722 fprintf( cdindex_form, "</CDInfo>\n");
1724 fclose( cdindex_form );
1728 static void dump_cdtext_info(void)
1731 /* interpret the contents of CD Text information based on an early draft
1732 of SCSI-3 mmc version 2 from jan 2, 1998
1733 CD Text information consists of a text group containing up to
1734 8 language blocks containing up to
1735 255 Pack data chunks of
1737 So we have at most 36720 bytes to cope with.
1740 short int datalength;
1741 unsigned char *p = bufferTOC;
1742 unsigned char lastline[255*12];
1745 int inlinecount = 0;
1746 int outlinecount = 0;
1749 datalength = ((p[0] << 8) + p[1]) - 2;
1750 datalength = min(datalength, 2048-4);
1752 for (;datalength > 0;
1753 datalength -= sizeof (cdtextpackdata),
1754 p += sizeof (cdtextpackdata)) {
1755 unsigned char *zeroposition;
1757 /* handle one packet of CD Text Information Descriptor Pack Data */
1758 /* this is raw R-W subchannel data converted to 8 bit values. */
1759 cdtextpackdata *c = (cdtextpackdata *)p;
1765 fprintf(stderr, "datalength =%d\n", datalength);
1767 crc_error = !cdtext_crc_ok(c);
1769 if (lastitem != c->headerfield[0]) {
1771 lastitem = c->headerfield[0];
1774 tracknr = c->headerfield[1] & 0x7f;
1775 dbcc = ((unsigned)(c->headerfield[3] & 0x80)) >> 7; /* double byte character code */
1777 #if defined DEBUG_CDTEXT
1780 int sequence_number;
1782 int character_position;
1784 extension_flag = ((unsigned)(c->headerfield[1] & 0x80)) >> 7;
1785 sequence_number = c->headerfield[2];
1786 block_number = ((unsigned)(c->headerfield[3] & 0x30)) >> 4; /* language */
1787 character_position = c->headerfield[3] & 0x0f;
1789 fprintf(stderr, "CDText: ext_fl=%d, trnr=%u, seq_nr=%d, dbcc=%d, block_nr=%d, char_pos=%d\n",
1790 extension_flag, tracknr, sequence_number, dbcc, block_number, character_position);
1794 /* print ASCII information */
1795 memcpy(lastline+inlinecount, c->textdatafield, 12);
1797 zeroposition = (unsigned char *)memchr(lastline+outlinecount, '\0', inlinecount-outlinecount);
1798 while (zeroposition != NULL) {
1799 process_header(c, tracknr, dbcc, lastline+outlinecount);
1800 outlinecount += zeroposition - (lastline+outlinecount) + 1;
1802 #if defined DEBUG_CDTEXT
1803 fprintf(stderr, "\tin=%d, out=%d, items=%d, trcknum=%u\n", inlinecount, outlinecount, itemcount, tracknr);
1804 { int q; for (q= outlinecount; q < inlinecount; q++) fprintf (stderr, "%c", lastline[q] ? lastline[q] : 'ß'); fputs("\n", stderr); }
1807 if (crc_error) fputs(" ! uncorr. CRC-Error", stderr);
1808 fputs("\n", stderr);
1813 if (itemcount > (int)cdtracks
1814 || (c->headerfield[0] == 0x8f
1815 || (c->headerfield[0] <= 0x8d && c->headerfield[0] >= 0x86))) {
1816 outlinecount = inlinecount;
1820 zeroposition = (unsigned char *)memchr(lastline+outlinecount, '\0', inlinecount-outlinecount);
1829 static void dump_extra_info(unsigned int from)
1833 unsigned pos, length;
1835 if (from == 0) return;
1837 p = Extra_buffer + 48;
1838 while (*p != '\0') {
1839 pos = GET_BE_UINT_FROM_CHARP(p+2);
1840 length = GET_BE_UINT_FROM_CHARP(p+6);
1841 if (pos == (unsigned)-1) {
1844 pos += session_start;
1848 if (global.gui == 0 && global.verbose != 0) {
1849 fprintf(stderr, "Language: %c%c (as defined by ISO 639)", *p, *(p+1));
1850 fprintf(stderr, " at sector %u, len=%u (sessionstart=%u)", pos, length, session_start);
1851 fputs("\n", stderr);
1854 /* dump this entry */
1855 Read_Subinfo(pos, length);
1858 if (p + 9 > (Extra_buffer + CD_FRAMESIZE))
1864 static char *quote(unsigned char *string);
1866 static char *quote(unsigned char *string)
1868 static char result[200];
1869 unsigned char *p = (unsigned char *)result;
1872 if (*string == '\'' || *string == '\\') {
1884 static void DisplayToc_with_gui(unsigned long dw);
1886 static void DisplayToc_with_gui(unsigned long dw)
1891 int count_audio_trks;
1892 static struct iterator i;
1893 if (i.reset == NULL) InitIterator(&i, 1);
1896 mins = dw / ( 60*75 );
1897 secnds = ( dw % ( 60*75 ) ) / 75;
1898 frames = ( dw % 75 );
1901 count_audio_trks = 0;
1903 if ((global.verbose & SHOW_STARTPOSITIONS) != 0) {
1904 if (global.illleadout_cd != 0 && have_CD_extra == 0) {
1905 fprintf( stderr, "Tracks:%u > %u:%02u.%02u\n", cdtracks, mins, secnds, frames );
1907 fprintf( stderr, "Tracks:%u %u:%02u.%02u\n", cdtracks, mins, secnds, frames );
1911 if (global.quiet == 0) {
1912 fprintf( stderr, "CDINDEX discid: %s\n", global.cdindex_id);
1913 fprintf( stderr, "CDDB discid: 0x%08lx", (unsigned long) global.cddb_id);
1915 if (have_CDDB != 0) {
1916 fprintf(stderr, " CDDBP titles: resolved\n");
1918 fprintf(stderr, "\n");
1920 if (have_CD_text != 0) {
1921 fprintf(stderr, "CD-Text: detected\n");
1924 fprintf(stderr, "CD-Text: not detected\n");
1926 if (have_CD_extra != 0) {
1927 fprintf(stderr, "CD-Extra: detected\n");
1928 dump_extra_info(have_CD_extra);
1930 fprintf(stderr, "CD-Extra: not detected\n");
1934 "Album title: '%s'", (void *)global.disctitle != NULL
1935 ? quote(global.disctitle) : "");
1937 fprintf( stderr, " from '%s'\n", (void *)global.creator != NULL
1938 ? quote(global.creator) : "");
1940 count_audio_trks = 0;
1943 if ((global.verbose & (SHOW_TOC | SHOW_STARTPOSITIONS | SHOW_SUMMARY | SHOW_TITLES)) != 0
1944 && i.hasNextTrack(&i)) {
1945 TOC *o = i.getNextTrack(&i);
1946 while (i.hasNextTrack(&i)) {
1947 TOC *p = i.getNextTrack(&i);
1951 fprintf(stderr, "T%02d:", from);
1955 * Special case of cd extra
1957 unsigned int real_start = have_CD_extra
1958 ? have_CD_extra : GETSTART(o);
1961 dw = (unsigned long) (GETSTART(p) - real_start);
1963 mins = dw / ( 60*75 );
1964 secnds = ( dw % ( 60*75 )) / 75;
1965 frames = ( dw % 75 );
1967 if ( global.verbose & SHOW_STARTPOSITIONS )
1973 if ( global.verbose & SHOW_TOC )
1976 mins, secnds, frames
1979 if ( global.verbose & SHOW_SUMMARY )
1985 ? "incremental" : "uninterrupted",
1987 /* copy-permission */
1989 ? "copydenied" : "copyallowed"
1991 fputs("\n", stderr);
1993 dw = (unsigned long) (GETSTART(p) - GETSTART(o));
1994 mins = dw / ( 60*75 );
1995 secnds = ( dw % ( 60*75 )) / 75;
1996 frames = ( dw % 75 );
1998 if ( global.verbose & SHOW_STARTPOSITIONS )
2004 if ( global.verbose & SHOW_TOC )
2007 mins, secnds, frames
2010 if ( global.verbose & SHOW_SUMMARY )
2015 IS__PREEMPHASIZED(o)
2016 ? "pre-emphasized" : "linear",
2018 /* copy-permission */
2020 ? "copydenied" : "copyallowed",
2024 ? "quadro" : "stereo");
2027 if ( global.verbose & SHOW_TITLES ) {
2029 " title '%s' from ",
2031 (void *) global.tracktitle[GETTRACK(o)] != NULL
2032 ? quote(global.tracktitle[GETTRACK(o)]) : ""
2038 (void *) global.trackcreator[GETTRACK(o)] != NULL
2039 ? quote(global.trackcreator[GETTRACK(o)]) : ""
2042 fputs("\n", stderr);
2047 if ( global.verbose & SHOW_STARTPOSITIONS )
2048 if (GETTRACK(o) == CDROM_LEADOUT) {
2049 fprintf(stderr, "Leadout: %7u\n", GETSTART(o));
2054 static void DisplayToc_no_gui(unsigned long dw);
2056 static void DisplayToc_no_gui(unsigned long dw)
2061 int count_audio_trks;
2063 static struct iterator i;
2064 if (i.reset == NULL) InitIterator(&i, 1);
2067 mins = dw / ( 60*75 );
2068 secnds = ( dw % ( 60*75 ) ) / 75;
2069 frames = ( dw % 75 );
2072 count_audio_trks = 0;
2074 if (i.hasNextTrack(&i)) {
2075 TOC *o = i.getNextTrack(&i);
2076 while (i.hasNextTrack(&i)) {
2077 TOC *p = i.getNextTrack(&i);
2082 while ( p != NULL && GETTRACK(p) != CDROM_LEADOUT
2083 && GETFLAGS(o) == GETFLAGS(p) ) {
2085 p = i.getNextTrack(&i);
2087 if ((global.verbose & SHOW_SUMMARY) == 0) continue;
2090 fputs( " DATAtrack recorded copy-permitted tracktype\n" , stderr);
2092 " %2d-%2d %13.13s %14.14s data\n",
2097 ? "incremental" : "uninterrupted",
2100 IS__COPYRIGHTED(o) ? "no" : "yes"
2103 fputs( "AUDIOtrack pre-emphasis copy-permitted tracktype channels\n" , stderr);
2105 " %2d-%2d %12.12s %14.14s audio %1c\n",
2108 IS__PREEMPHASIZED(o)
2110 IS__COPYRIGHTED(o) ? "no" : "yes",
2111 IS__QUADRO(o) ? '4' : '2'
2118 if ((global.verbose & SHOW_STARTPOSITIONS) != 0) {
2119 if (global.illleadout_cd != 0 && have_multisession == 0) {
2122 "Table of Contents: total tracks:%u, (total time more than %u:%02u.%02u)\n",
2123 cdtracks, mins, secnds, frames );
2126 "Table of Contents: total tracks:%u, (total time %u:%02u.%02u)\n",
2127 cdtracks, mins, secnds, frames );
2132 if ((global.verbose & SHOW_TOC) != 0 &&
2133 i.hasNextTrack(&i)) {
2134 TOC *o = i.getNextTrack(&i);
2136 for (; i.hasNextTrack(&i);) {
2137 TOC *p = i.getNextTrack(&i);
2139 if ( GETTRACK(o) <= MAXTRK ) {
2140 unsigned char brace1, brace2;
2142 trackbeg = have_multisession && IS__DATA(o) ? have_multisession : GETSTART(o);
2144 dw = (unsigned long) (GETSTART(p) - trackbeg);
2145 mins = dw / ( 60*75 );
2146 secnds = ( dw % ( 60*75 )) / 75;
2147 frames = ( dw % 75 );
2149 if ( IS__DATA(o) ) {
2150 /* data track display */
2153 } else if (have_multisession
2154 && GETTRACK(o) == LastAudioTrack()) {
2155 /* corrected length of
2156 * last audio track in cd extra
2161 /* audio track display */
2166 " %2u.%c%2u:%02u.%02u%c",
2169 mins, secnds, frames,
2175 fputs( ",\n", stderr );
2176 else if (ii != cdtracks)
2177 fputc ( ',', stderr );
2181 if ( (ii % 5) != 0 )
2182 fputs( "\n", stderr );
2186 if ((global.verbose & SHOW_STARTPOSITIONS) != 0) {
2187 fputs ("\nTable of Contents: starting sectors\n", stderr);
2191 if (i.hasNextTrack(&i)) {
2192 TOC *o = i.getNextTrack(&i);
2193 for ( ; i.hasNextTrack(&i);) {
2194 TOC *p = i.getNextTrack(&i);
2199 && GETTRACK(o) == FirstDataTrack()
2208 if ( (ii) % 5 == 0 )
2209 fputs( ",\n", stderr );
2211 fputc ( ',', stderr );
2214 fprintf ( stderr, " lead-out(%8u)", GETSTART(o));
2215 fputs ("\n", stderr);
2218 if (global.quiet == 0) {
2219 fprintf(stderr, "CDINDEX discid: %s\n", global.cdindex_id);
2220 fprintf( stderr, "CDDB discid: 0x%08lx", (unsigned long) global.cddb_id);
2222 if (have_CDDB != 0) {
2223 fprintf(stderr, " CDDBP titles: resolved\n");
2225 fprintf(stderr, "\n");
2227 if (have_CD_text != 0) {
2228 fprintf(stderr, "CD-Text: detected\n");
2230 fprintf(stderr, "CD-Text: not detected\n");
2232 if (have_CD_extra != 0) {
2233 fprintf(stderr, "CD-Extra: detected\n");
2235 fprintf(stderr, "CD-Extra: not detected\n");
2238 if ((global.verbose & SHOW_TITLES) != 0) {
2241 if ( global.disctitle != NULL ) {
2242 fprintf( stderr, "Album title: '%s'", global.disctitle);
2243 if ( global.creator != NULL ) {
2244 fprintf( stderr, "\t[from %s]", global.creator);
2246 fputs("\n", stderr);
2250 for ( ; i.hasNextTrack(&i);) {
2251 TOC *p = i.getNextTrack(&i);
2252 int jj = GETTRACK(p);
2254 if ( global.tracktitle[jj] != NULL ) {
2255 int len = strlen((char *)global.tracktitle[jj]);
2256 maxlen = max(maxlen, len);
2259 maxlen = (maxlen + 12 + 8 + 7)/8;
2262 for ( ; i.hasNextTrack(&i); ) {
2263 TOC *p = i.getNextTrack(&i);
2271 if (jj == CDROM_LEADOUT)
2274 if ( maxlen != 3 ) {
2275 if ( global.tracktitle[jj] != NULL ) {
2276 fprintf( stderr, "Track %2u: '%s'", jj, global.tracktitle[jj]);
2278 fprintf( stderr, "Track %2u: '%s'", jj, "");
2280 if ( global.trackcreator[jj] != NULL
2281 && global.trackcreator[jj][0] != '\0'
2283 && (global.creator == NULL
2284 || 0 != strcmp((char *)global.creator,(char *)global.trackcreator[jj]))
2288 char *o = global.tracktitle[jj] != NULL
2289 ? (char *)global.tracktitle[jj]
2292 j < (maxlen - ((int)strlen(o) + 12)/8);
2294 fprintf(stderr, "\t");
2295 fprintf( stderr, "[from %s]", global.trackcreator[jj]);
2297 fputs("\n", stderr);
2303 void DisplayToc(void)
2307 /* special handling of pseudo-red-book-audio cds */
2309 && Get_StartSector(CDROM_LEADOUT) < Get_StartSector(cdtracks)) {
2310 global.illleadout_cd = 1;
2311 can_read_illleadout();
2315 /* get total time */
2316 if (global.illleadout_cd == 0)
2317 dw = (unsigned long) Get_StartSector(CDROM_LEADOUT) - Get_StartSector(1);
2319 dw = (unsigned long) Get_StartSector(cdtracks ) - Get_StartSector(1);
2321 if ( global.gui == 0 ) {
2322 /* table formatting when in cmdline mode */
2323 DisplayToc_no_gui( dw );
2324 } else if (global.gui == 1) {
2325 /* line formatting when in gui mode */
2326 DisplayToc_with_gui( dw );
2329 if (global.illleadout_cd != 0) {
2330 if (global.quiet == 0) {
2331 fprintf(stderr, "CD with illegal leadout position detected!\n");
2334 if (global.reads_illleadout == 0) {
2335 /* limit accessible tracks
2336 * to lowered leadout position
2338 restrict_tracks_illleadout();
2340 if (global.quiet == 0) {
2342 "The cdrom drive firmware does not permit access beyond the leadout position!\n");
2344 if (global.verbose & (SHOW_ISRC | SHOW_INDICES)) {
2345 global.verbose &= ~(SHOW_ISRC | SHOW_INDICES);
2346 fprintf(stderr, "Switching index scan and ISRC scan off!\n");
2349 if (global.quiet == 0) {
2351 "Audio extraction will be limited to track %ld with maximal %ld sectors...\n",
2353 Get_EndSector(LastTrack())+1
2357 /* The cdrom drive can read beyond the
2358 * indicated leadout. We patch a new leadout
2359 * position to the maximum:
2360 * 99 minutes, 59 seconds, 74 frames
2362 patch_real_end(150 + (99*60+59)*75 + 74);
2363 if (global.quiet == 0) {
2365 "Restrictions apply, since the size of the last track is unknown!\n");
2371 static void Read_MCN_toshiba(subq_chnl **sub_ch);
2373 static void Read_MCN_toshiba(subq_chnl **sub_ch)
2375 if (Toshiba3401() != 0 && global.quiet == 0
2377 || (((subq_catalog *)(*sub_ch)->data)->mc_valid & 0x80))) {
2378 /* no valid MCN yet. do more searching */
2379 long h = Get_AudioStartSector(1);
2381 while (h <= Get_AudioStartSector(1) + 100) {
2383 ReadCdRom(get_scsi_p(), RB_BASE->data, h, global.nsectors);
2384 (*sub_ch) = ReadSubQ(get_scsi_p(), GET_CATALOGNUMBER,0);
2385 if ((*sub_ch) != NULL) {
2386 subq_catalog *subq_cat;
2388 subq_cat = (subq_catalog *) (*sub_ch)->data;
2389 if ((subq_cat->mc_valid & 0x80) != 0) {
2393 h += global.nsectors;
2398 static void Get_Set_MCN(void);
2400 static void Get_Set_MCN(void)
2403 subq_catalog *subq_cat = NULL;
2404 fprintf(stderr, "scanning for MCN...");
2406 sub_ch = ReadSubQ(get_scsi_p(), GET_CATALOGNUMBER,0);
2408 #define EXPLICIT_READ_MCN_ISRC 1
2409 #if EXPLICIT_READ_MCN_ISRC == 1 /* TOSHIBA HACK */
2410 Read_MCN_toshiba( &sub_ch );
2414 subq_cat = (subq_catalog *)sub_ch->data;
2417 && (subq_cat->mc_valid & 0x80) != 0
2418 && global.quiet == 0) {
2420 /* unified format guesser:
2421 * format MCN all digits in bcd
2423 * A: ab cd ef gh ij kl m0 0 0 0 0 0 0 Plextor 6x Rel. 1.02
2424 * B: 0a 0b 0c 0d 0e 0f 0g 0h 0i 0j 0k 0l 0m Toshiba 3401
2425 * 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
2427 unsigned char *cp = subq_cat->media_catalog_number;
2428 if (!(cp[8] | cp[9] | cp[10] | cp[11] | cp[12])
2429 && ((cp[0] & 0xf0) | (cp[1] & 0xf0)
2430 | (cp[2] & 0xf0) | (cp[3] & 0xf0)
2431 | (cp[4] & 0xf0) | (cp[5] & 0xf0)
2432 | (cp[6] & 0xf0))) {
2433 /* reformat A: to B: */
2434 cp[12] = cp[6] >> 4; cp[11] = cp[5] & 0xf;
2435 cp[10] = cp[5] >> 4; cp[ 9] = cp[4] & 0xf;
2436 cp[ 8] = cp[4] >> 4; cp[ 7] = cp[3] & 0xf;
2437 cp[ 6] = cp[3] >> 4; cp[ 5] = cp[2] & 0xf;
2438 cp[ 4] = cp[2] >> 4; cp[ 3] = cp[1] & 0xf;
2439 cp[ 2] = cp[1] >> 4; cp[ 1] = cp[0] & 0xf;
2440 cp[ 0] = cp[0] >> 4;
2444 && (memcmp(subq_cat->media_catalog_number,
2445 "\0\0\0\0\0\0\0\0\0\0\0\0\0", 13) != 0)) {
2447 subq_cat->media_catalog_number,
2448 "%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",
2449 subq_cat->media_catalog_number [0],
2450 subq_cat->media_catalog_number [1],
2451 subq_cat->media_catalog_number [2],
2452 subq_cat->media_catalog_number [3],
2453 subq_cat->media_catalog_number [4],
2454 subq_cat->media_catalog_number [5],
2455 subq_cat->media_catalog_number [6],
2456 subq_cat->media_catalog_number [7],
2457 subq_cat->media_catalog_number [8],
2458 subq_cat->media_catalog_number [9],
2459 subq_cat->media_catalog_number [10],
2460 subq_cat->media_catalog_number [11],
2461 subq_cat->media_catalog_number [12]
2465 if (memcmp(subq_cat->media_catalog_number,"0000000000000",13)
2467 Set_MCN(subq_cat->media_catalog_number);
2473 static void Read_ISRC_toshiba(subq_chnl **sub_ch, unsigned tr);
2475 static void Read_ISRC_toshiba(subq_chnl **sub_ch, unsigned tr)
2477 if (Toshiba3401() != 0) {
2479 j = (Get_AudioStartSector(tr)/100 + 1) * 100;
2481 ReadCdRom(get_scsi_p(), RB_BASE->data, j, global.nsectors);
2482 *sub_ch = ReadSubQ(get_scsi_p(), GET_TRACK_ISRC, Get_Tracknumber(tr));
2483 if (*sub_ch != NULL) {
2484 subq_track_isrc * subq_tr;
2486 subq_tr = (subq_track_isrc *) (*sub_ch)->data;
2487 if (subq_tr != NULL && (subq_tr->tc_valid & 0x80) != 0)
2490 j += global.nsectors;
2491 } while (j < (Get_AudioStartSector(tr)/100 + 1) * 100 + 100);
2496 static void Get_Set_ISRC(unsigned tr);
2498 static void Get_Set_ISRC(unsigned tr)
2501 subq_track_isrc * subq_tr;
2503 fprintf(stderr, "\rscanning for ISRCs: %d ...", tr);
2506 sub_ch = ReadSubQ(get_scsi_p(), GET_TRACK_ISRC, tr);
2508 #if EXPLICIT_READ_MCN_ISRC == 1 /* TOSHIBA HACK */
2509 Read_ISRC_toshiba( &sub_ch, tr );
2513 subq_tr = (subq_track_isrc *)sub_ch->data;
2515 if (sub_ch != NULL && (subq_tr->tc_valid & 0x80)
2516 && global.quiet == 0) {
2517 unsigned char p_start[16];
2518 unsigned char *p = p_start;
2519 unsigned char *cp = subq_tr->track_isrc;
2521 /* unified format guesser:
2522 * there are 60 bits and 15 bytes available.
2523 * 5 * 6bit-items + two zero fill bits + 7 * 4bit-items
2525 * A: ab cd ef gh ij kl mn o0 0 0 0 0 0 0 0 Plextor 6x Rel. 1.02
2526 * B: 0a 0b 0c 0d 0e 0f 0g 0h 0i 0j 0k 0l 0m 0n 0o Toshiba 3401
2527 * C: AS AS AS AS AS AS AS AS AS AS AS AS AS AS AS ASCII SCSI-2
2528 * eg 'G''B''-''A''0''7''-''6''8''-''0''0''2''7''0' makes most sense
2529 * 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
2532 /* Check for format A: */
2533 if (!(cp[8] | cp[9] | cp[10] | cp[11] | cp[12] | cp[13] | cp[14]) &&
2534 ((cp[0] & 0xf0) | (cp[1] & 0xf0) | (cp[2] & 0xf0) |
2535 (cp[3] & 0xf0) | (cp[4] & 0xf0) | (cp[5] & 0xf0) |
2536 (cp[6] & 0xf0) | (cp[7] & 0xf0))) {
2538 fprintf(stderr, "a!\t");
2540 /* reformat A: to B: */
2541 cp[14] = cp[7] >> 4; cp[13] = cp[6] & 0xf;
2542 cp[12] = cp[6] >> 4; cp[11] = cp[5] & 0xf;
2543 cp[10] = cp[5] >> 4; cp[ 9] = cp[4] & 0xf;
2544 cp[ 8] = cp[4] >> 4; cp[ 7] = cp[3] & 0xf;
2545 cp[ 6] = cp[3] >> 4; cp[ 5] = cp[2] & 0xf;
2546 cp[ 4] = cp[2] >> 4; cp[ 3] = cp[1] & 0xf;
2547 cp[ 2] = cp[1] >> 4; cp[ 1] = cp[0] & 0xf;
2548 cp[ 0] = cp[0] >> 4;
2550 fprintf(stderr, "a->b: %15.15s\n", cp);
2554 /* Check for format B:
2555 * If not yet in ASCII format, do the conversion
2557 if (cp[0] < '0' && cp[1] < '0') {
2558 /* coding table for International Standard Recording Code */
2559 static char bin2ISRC[] = {
2560 '0','1','2','3','4','5','6','7','8','9', /* 10 */
2561 ':',';','<','=','>','?','@', /* 17 */
2562 'A','B','C','D','E','F','G','H','I','J','K', /* 28 */
2563 'L','M','N','O','P','Q','R','S','T','U','V', /* 39 */
2564 'W','X','Y','Z', /* 43 */
2566 '[','\\',']','^','_','`', /* 49 */
2567 'a','b','c','d','e','f','g','h','i','j','k', /* 60 */
2568 'l','m','n','o' /* 64 */
2572 /* build 6-bit vector of coded values */
2577 fprintf(stderr, "b!\n");
2579 ind = (cp[0] << 26) +
2588 if ((cp[7] & 3) == 3) {
2589 if (global.verbose) {
2591 "Recorder-ID encountered: ");
2592 for (bits = 0; bits < 30; bits +=6) {
2593 unsigned binval = (ind & (ULONG_C(0x3f) << (24-bits)))
2595 if ((binval < sizeof(bin2ISRC)) &&
2596 (binval <= 9 || binval >= 16)) {
2597 fprintf(stderr, "%X", bin2ISRC[binval]);
2601 fprintf(stderr, "%.1X%.1X%.1X%.1X%.1X%.1X%.1X",
2602 subq_tr->track_isrc [8] & 0x0f,
2603 subq_tr->track_isrc [9] & 0x0f,
2604 subq_tr->track_isrc [10] & 0x0f,
2605 subq_tr->track_isrc [11] & 0x0f,
2606 subq_tr->track_isrc [12] & 0x0f,
2607 subq_tr->track_isrc [13] & 0x0f,
2608 subq_tr->track_isrc [14] & 0x0f
2610 fprintf(stderr, "\n");
2614 if ((cp[7] & 3) != 0) {
2615 fprintf(stderr, "unknown mode 3 entry C1=0x%02x, C2=0x%02x\n",
2616 (cp[7] >> 1) & 1, cp[7] & 1);
2620 /* decode ISRC due to IEC 908 */
2621 for (bits = 0; bits < 30; bits +=6) {
2622 unsigned binval = (ind & ((unsigned long) 0x3fL << (24L-bits))) >> (24L-bits);
2623 if ((binval >= sizeof(bin2ISRC)) ||
2624 (binval > 9 && binval < 16)) {
2625 /* Illegal ISRC, dump and skip */
2628 Get_ISRC(tr)[0] = '\0';
2629 fprintf(stderr, "\nIllegal ISRC for track %d, skipped: ", tr);
2630 for (y = 0; y < 15; y++) {
2631 fprintf(stderr, "%02x ", cp[y]);
2633 fputs("\n", stderr);
2636 *p++ = bin2ISRC[binval];
2638 /* insert a dash after two country characters for legibility */
2643 /* format year and serial number */
2644 sprintf ((char *)p, "-%.1X%.1X-%.1X%.1X%.1X%.1X%.1X",
2645 subq_tr->track_isrc [8] & 0x0f,
2646 subq_tr->track_isrc [9] & 0x0f,
2647 subq_tr->track_isrc [10] & 0x0f,
2648 subq_tr->track_isrc [11] & 0x0f,
2649 subq_tr->track_isrc [12] & 0x0f,
2650 subq_tr->track_isrc [13] & 0x0f,
2651 subq_tr->track_isrc [14] & 0x0f
2654 fprintf(stderr, "b: %15.15s!\n", p_start);
2657 /* It might be in ASCII, surprise */
2659 for (ii = 0; ii < 12; ii++) {
2660 if (cp[ii] < '0' || cp[ii] > 'Z') {
2667 Get_ISRC(ii)[0] = '\0';
2668 fprintf(stderr, "\nIllegal ISRC for track %d, skipped: ", ii+1);
2669 for (y = 0; y < 15; y++) {
2670 fprintf(stderr, "%02x ", cp[y]);
2672 fputs("\n", stderr);
2677 fprintf(stderr, "ascii: %15.15s!\n", cp);
2679 for (ii = 0; ii < 12; ii++) {
2681 if ((ii == 2 || ii == 5 || ii == 7) && cp[ii] != ' ')
2686 if (p - p_start >= 16)
2687 *(p_start + 15) = '\0';
2692 if (memcmp(p_start,"00-000-00-00000",15) != 0) {
2693 Set_ISRC(tr, p_start);
2698 /* get and display Media Catalog Number ( one per disc )
2699 * and Track International Standard Recording Codes (for each track)
2701 void Read_MCN_ISRC(void)
2703 if ((global.verbose & SHOW_MCN) != 0) {
2705 if (Get_MCN()[0] == '\0') {
2709 if (Get_MCN()[0] != '\0')
2710 fprintf(stderr, "\rMedia catalog number: %13.13s\n", Get_MCN());
2712 fprintf(stderr, "\rNo media catalog number present.\n");
2717 if ((global.verbose & SHOW_ISRC) != 0) {
2718 static struct iterator i;
2720 InitIterator(&i, 1);
2722 while (i.hasNextTrack(&i)) {
2723 struct TOC *p = i.getNextTrack(&i);
2724 unsigned ii = GETTRACK(p);
2726 if (ii == CDROM_LEADOUT) break;
2731 if (GETISRC(p)[0] == '\0') {
2735 if (GETISRC(p)[0] != '\0') {
2736 fprintf (stderr, "\rT: %2d ISRC: %15.15s\n", ii, GETISRC(p));
2739 } /* for all tracks */
2741 fputs("\n", stderr);
2742 } /* if SHOW_ISRC */
2745 static int playing = 0;
2747 static subq_chnl *ReadSubChannel(unsigned sec);
2749 static subq_chnl *ReadSubChannel(unsigned sec)
2754 * For modern drives implement a direct method. If the drive supports
2755 * reading of subchannel data, do direct reads.
2757 if (ReadSubChannels != NULL) {
2758 get_scsi_p()->silent++;
2759 sub_ch = ReadSubChannels(get_scsi_p(), sec);
2760 get_scsi_p()->silent--;
2761 if (sub_ch == NULL /*&& (usal_sense_key(get_scsi_p()) == 5)*/) {
2762 /* command is not implemented */
2763 ReadSubChannels = NULL;
2764 #if defined DEBUG_SUB
2765 fprintf(stderr, "\nCommand not implemented: switching ReadSubChannels off !\n");
2770 /* check the adress mode field */
2771 if ((sub_ch->control_adr & 0x0f) == 0) {
2772 /* no Q mode information present at all, weird */
2773 sub_ch->control_adr = 0xAA;
2776 if ((int)(sub_ch->control_adr & 0x0f) > 0x01) {
2777 /* this sector just has no position information.
2778 * we try the one before and then the one after.
2782 sub_ch = ReadSubChannels(get_scsi_p(), sec);
2783 if (sub_ch == NULL) return NULL;
2786 if ((sub_ch->control_adr & 0x0f) != 0x01) {
2788 sub_ch = ReadSubChannels(get_scsi_p(), sec);
2789 if (sub_ch == NULL) return NULL;
2794 /* check adress mode field for position information */
2795 if ((sub_ch->control_adr & 0x0f) == 0x01) {
2798 ReadSubChannels = NULL;
2799 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);
2803 * We rely on audio sectors here!!!
2804 * The only method that worked even with my antique Toshiba 3401,
2805 * is playing the sector and then request the subchannel afterwards.
2808 /* We need a conformed audio track here! */
2810 /* Fallback to ancient method */
2811 if (-1 == Play_at(get_scsi_p(), sec, 1)) {
2815 sub_ch = ReadSubQ(get_scsi_p(), GET_POSITIONDATA,0);
2819 static int ReadSubControl(unsigned sec);
2820 static int ReadSubControl(unsigned sec)
2822 subq_chnl *sub_ch = ReadSubChannel(sec);
2823 if (sub_ch == NULL) return -1;
2825 return sub_ch->control_adr & 0xf0;
2828 static int HaveSCMS(unsigned StartSector);
2829 static int HaveSCMS(unsigned StartSector)
2833 int copy_bits_set = 0;
2835 for (i = 0; i < 8; i++) {
2836 cr = ReadSubControl(StartSector + i);
2837 if (cr == -1) continue;
2838 (cr & 0x20) ? copy_bits_set++ : 0;
2840 return (copy_bits_set >= 1 && copy_bits_set < 8);
2843 void Check_Toc(void)
2850 static int GetIndexOfSector(unsigned sec, unsigned track)
2852 subq_chnl *sub_ch = ReadSubChannel(sec);
2853 if (sub_ch == NULL) {
2854 if ((long)sec == Get_EndSector(track)) {
2855 fprintf(stderr, "Driver and/or firmware bug detected! Drive cannot play the very last sector (%u)!\n", sec);
2860 /* can we trust that these values are hex and NOT bcd? */
2861 if ((sub_ch->track >= 0x10) && (sub_ch->track - track > 5)) {
2862 /* change all values from bcd to hex */
2863 sub_ch->track = (sub_ch->track >> 4)*10 + (sub_ch->track & 0x0f);
2864 sub_ch->index = (sub_ch->index >> 4)*10 + (sub_ch->index & 0x0f);
2868 /* compare tracks */
2869 if (sub_ch->index != 0 && track != sub_ch->track) {
2870 if (global.verbose) fprintf(stderr, "\ntrack mismatch: %1d, in-track subchannel: %1d (index %1d, sector %1d)\n",
2871 track, sub_ch->track, sub_ch->index, sec);
2875 /* compare control field with the one from the TOC */
2876 if ((Get_Flags(track) & 0xf0) != (sub_ch->control_adr & 0xf0)) {
2877 int diffbits = (Get_Flags(track) & 0xf0) ^ (sub_ch->control_adr & 0xf0);
2878 if ((diffbits & 0x80) == 0x80) {
2879 /* broadcast difference */
2880 if (global.verbose) fprintf(stderr, "broadcast type conflict detected -> TOC:%s, subchannel:%s\n",
2881 (sub_ch->control_adr & 0x80) == 0 ? "broadcast" : "nonbroadcast"
2882 ,(sub_ch->control_adr & 0x80) != 0 ? "broadcast" : "nonbroadcast"
2885 if ((diffbits & 0x40) == 0x40) {
2886 /* track type difference */
2887 if (global.verbose) fprintf(stderr, "track type conflict detected -> TOC:%s, subchannel:%s\n",
2888 (sub_ch->control_adr & 0x40) == 0 ? "data" : "audio"
2889 ,(sub_ch->control_adr & 0x40) != 0 ? "data" : "audio"
2892 if ((diffbits & 0x20) == 0x20 && !Get_SCMS(track)) {
2893 /* copy permission difference is a sign for SCMS
2894 * and is treated elsewhere. */
2895 if (global.verbose) fprintf(stderr, "difference: TOC:%s, subchannel:%s\ncorrecting TOC...\n",
2896 (sub_ch->control_adr & 0x20) == 0 ? "unprotected" : "copyright protected",
2897 (sub_ch->control_adr & 0x20) != 0 ? "unprotected" : "copyright protected"
2900 (Get_Flags(track) & 0xDF) | (sub_ch->control_adr & 0x20),
2901 Get_Tracknumber(track),
2903 Get_AudioStartSector(track),
2909 if ((diffbits & 0x10) == 0x10) {
2910 /* preemphasis difference */
2911 if (global.verbose) fprintf(stderr, "difference: TOC:%s, subchannel:%s preemphasis\ncorrecting TOC...\n",
2912 (sub_ch->control_adr & 0x10) == 0 ? "with" : "without",
2913 (sub_ch->control_adr & 0x10) != 0 ? "with" : "without"
2916 (Get_Flags(track) & 0xEF) | (sub_ch->control_adr & 0x10),
2917 Get_Tracknumber(track),
2919 Get_AudioStartSector(track),
2928 return sub_ch ? sub_ch->index == 244 ? 1 : sub_ch->index : -1;
2931 static int ScanBackwardFrom(unsigned sec, unsigned limit, int *where,
2934 static int ScanBackwardFrom(unsigned sec, unsigned limit, int *where,
2937 unsigned lastindex = 0;
2938 unsigned mysec = sec;
2940 /* try to find the transition of index n to index 0,
2941 * if the track ends with an index 0.
2943 while ((lastindex = GetIndexOfSector(mysec, track)) == 0) {
2944 if (mysec < limit+75) {
2950 /* there is no pre-gap in this track */
2951 if (where != NULL) *where = -1;
2953 /* we have a pre-gap in this track */
2955 if (lastindex == 0) {
2956 /* we did not cross the transition yet -> search backward */
2958 if (mysec < limit+1) {
2962 } while ((lastindex = GetIndexOfSector(mysec,track)) == 0);
2963 if (lastindex != 0) {
2966 /* register mysec as transition */
2967 if (where != NULL) *where = (int) mysec;
2969 /* could not find transition */
2972 "Could not find index transition for pre-gap.\n");
2973 if (where != NULL) *where = -1;
2977 /* we have crossed the transition -> search forward */
2983 } while ((myindex = GetIndexOfSector(mysec,track)) != 0);
2986 /* register mysec as transition */
2987 if (where != NULL) *where = (int) mysec;
2989 /* could not find transition */
2992 "Could not find index transition for pre-gap.\n");
2993 if (where != NULL) *where = -1;
3000 #ifdef USE_LINEAR_SEARCH
3001 static int linear_search(int searchInd, unsigned int Start, unsigned int End,
3004 static int linear_search(int searchInd, unsigned int Start, unsigned int End,
3010 for (; l <= r; l++ ) {
3013 ind = GetIndexOfSector(l, track);
3014 if ( searchInd == ind ) {
3027 #ifndef USE_LINEAR_SEARCH
3028 #undef DEBUG_BINSEARCH
3029 static int binary_search(int searchInd, unsigned int Start, unsigned int End,
3032 static int binary_search(int searchInd, unsigned Start, unsigned End,
3042 /* try to avoid seeking */
3043 ind = GetIndexOfSector(x, track);
3044 if ( searchInd == ind ) {
3047 if ( searchInd < ind ) r = x - 1;
3051 #ifdef DEBUG_BINSEARCH
3052 fprintf(stderr, "(%d,%d,%d > ",l,x,r);
3055 /* Index found. Now find the first position of this index */
3056 /* l=LastPos x=found r=NextPos */
3060 /* try to avoid seeking */
3061 ind = GetIndexOfSector(x, track);
3062 if ( searchInd == ind ) {
3067 #ifdef DEBUG_BINSEARCH
3068 fprintf(stderr, "%d -> ",x);
3071 #ifdef DEBUG_BINSEARCH
3072 fprintf(stderr, "%d,%d)\n",l,r);
3074 if (searchInd == GetIndexOfSector(l, track))
3085 static void register_index_position(int IndexOffset,
3086 index_list **last_index_entry);
3088 static void register_index_position(int IndexOffset,
3089 index_list **last_index_entry)
3091 index_list *indexentry;
3093 /* register higher index entries */
3094 if (*last_index_entry != NULL) {
3095 indexentry = (index_list *) malloc( sizeof(index_list) );
3099 if (indexentry != NULL) {
3100 indexentry->next = NULL;
3101 (*last_index_entry)->next = indexentry;
3102 *last_index_entry = indexentry;
3103 indexentry->frameoffset = IndexOffset;
3104 #if defined INFOFILES
3106 fprintf( stderr, "No memory for index lists. Index positions\nwill not be written in info file!\n");
3111 static void Set_SCMS(unsigned long p_track);
3113 #undef DEBUG_INDLIST
3114 /* experimental code */
3115 /* search for indices (audio mode required) */
3116 unsigned ScanIndices(unsigned track, unsigned cd_index, int bulk)
3118 /* scan for indices. */
3119 /* look at last sector of track. */
3120 /* when the index is not equal 1 scan by bipartition
3121 * for offsets of all indices */
3123 unsigned starttrack, endtrack;
3124 unsigned startindex, endindex;
3129 unsigned StartSector;
3130 unsigned retval = 0;
3132 index_list *baseindex_pool;
3133 index_list *last_index_entry;
3135 SCSI *usalp = get_scsi_p();
3137 static struct iterator i;
3138 InitIterator(&i, 1);
3140 EnableCdda(usalp, 0, 0);
3141 EnableCdda(usalp, 1, CD_FRAMESIZE_RAW + 16);
3143 if (!global.quiet && !(global.verbose & SHOW_INDICES))
3144 fprintf(stderr, "seeking index start ...");
3147 starttrack = track; endtrack = track;
3149 starttrack = 1; endtrack = cdtracks;
3151 baseindex_pool = (index_list *) malloc( sizeof(index_list) * (endtrack - starttrack + 1));
3152 #ifdef DEBUG_INDLIST
3153 fprintf(stderr, "index0-mem-pool %p\n", baseindex_pool);
3157 while (i.hasNextTrack(&i)) {
3158 struct TOC *p = i.getNextTrack(&i);
3159 unsigned ii = GETTRACK(p);
3161 if ( ii < starttrack || IS__DATA(p) )
3162 continue; /* skip nonaudio tracks */
3164 if ( ii > endtrack )
3167 if ( global.verbose & SHOW_INDICES ) {
3168 if (global.illleadout_cd && global.reads_illleadout && ii == endtrack) {
3169 fprintf(stderr, "Analysis of track %d skipped due to unknown length\n", ii);
3172 if (global.illleadout_cd && global.reads_illleadout
3173 && ii == endtrack) continue;
3175 StartSector = Get_AudioStartSector(ii);
3176 if (HaveSCMS(StartSector)) {
3179 if ( global.verbose & SHOW_INDICES ) {
3180 fprintf( stderr, "\rindex scan: %d...", ii );
3183 LastIndex = ScanBackwardFrom(Get_EndSector(ii), StartSector, &n_0_transition, ii);
3184 if (LastIndex > 99) continue;
3186 if (baseindex_pool != NULL) {
3187 #ifdef DEBUG_INDLIST
3189 /* register first index entry for this track */
3190 baseindex_pool[ii - starttrack].next = NULL;
3191 baseindex_pool[ii - starttrack].frameoffset = StartSector;
3192 global.trackindexlist[ii] = &baseindex_pool[ii - starttrack];
3193 #ifdef DEBUG_INDLIST
3196 global.trackindexlist[ii] = NULL;
3198 last_index_entry = global.trackindexlist[ii];
3200 if (LastIndex < 2) {
3201 register_index_position(n_0_transition, &last_index_entry);
3205 if ((global.verbose & SHOW_INDICES) && LastIndex > 1)
3206 fprintf(stderr, "\rtrack %2d has %d indices, index table (pairs of 'index: frame offset')\n", ii, LastIndex);
3209 endindex = LastIndex;
3211 for (j = startindex; j <= endindex; j++) {
3214 /* this track has indices */
3216 #ifdef USE_LINEAR_SEARCH
3217 /* do a linear search */
3218 IndexOffset = linear_search(j, StartSector, Get_EndSector(ii), ii);
3220 /* do a binary search */
3221 IndexOffset = binary_search(j, StartSector, Get_EndSector(ii), ii);
3224 if (IndexOffset != -1) {
3225 StartSector = IndexOffset;
3229 last_index_entry->frameoffset = IndexOffset;
3231 register_index_position(IndexOffset, &last_index_entry);
3233 if ( IndexOffset == -1 ) {
3234 if (global.verbose & SHOW_INDICES) {
3235 if (global.gui == 0) {
3236 fprintf(stderr, "%2u: N/A ",j);
3237 if (((j + 1) % 8) == 0) fputs("\n", stderr);
3239 fprintf(stderr, "\rT%02d I%02u N/A\n",ii,j);
3243 if (global.verbose & SHOW_INDICES) {
3244 if (global.gui == 0) {
3248 IndexOffset-Get_AudioStartSector(ii)
3250 if (((j + 1) % 8) == 0) fputs("\n", stderr);
3253 "\rT%02d I%02u %06lu\n",
3256 IndexOffset-Get_AudioStartSector(ii)
3261 if (track == ii && cd_index == j) {
3262 retval = IndexOffset-Get_AudioStartSector(ii);
3264 } /* if IndexOffset */
3266 register_index_position(n_0_transition, &last_index_entry);
3268 /* sanity check. clear all consecutive nonindex entries (frameoffset -1) from the end. */
3270 index_list *ip = global.trackindexlist[ii];
3271 index_list *iq = NULL;
3272 index_list *lastgood = iq;
3276 if (ip->frameoffset == -1)
3278 /* no index available */
3279 if (lastgood == NULL)
3281 /* if this is the first one in a sequence, store predecessor */
3285 /* this is a valid index, reset marker */
3292 /* terminate chain at the last well defined entry. */
3293 if (lastgood != NULL)
3294 lastgood->next = NULL;
3297 if (global.gui == 0 && (global.verbose & SHOW_INDICES)
3299 fputs("\n", stderr);
3301 if (global.gui == 0 && (global.verbose & SHOW_INDICES))
3302 fputs("\n", stderr);
3303 if (playing != 0) StopPlay(get_scsi_p());
3305 EnableCdda(usalp, 0, 0);
3306 EnableCdda(usalp, 1, CD_FRAMESIZE_RAW);
3311 static unsigned char MCN[14];
3313 static void Set_MCN(unsigned char *MCN_arg)
3315 memcpy(MCN, MCN_arg, 14);
3319 unsigned char *Get_MCN(void)
3325 static TOC g_toc [MAXTRK+1]; /* hidden track + 100 regular tracks */
3327 /*#define IS_AUDIO(i) (!(g_toc[i].bFlags & 0x40))*/
3330 TOC_entries(unsigned tracks, unsigned char *a, unsigned char *b, int binvalid)
3333 for (i = 1; i <= (int)tracks; i++) {
3335 unsigned long dwStartSector;
3340 g_toc[i].bFlags = p[1];
3341 g_toc[i].bTrack = p[2];
3342 g_toc[i].ISRC[0] = 0;
3343 dwStartSector = a_to_u_4_byte(p+4);
3344 g_toc[i].dwStartSector = dwStartSector;
3345 lba_2_msf((long)dwStartSector,
3351 g_toc[i].bFlags = p[1];
3352 g_toc[i].bTrack = p[2];
3353 g_toc[i].ISRC[0] = 0;
3354 if ((int)((p[5]*60 + p[6])*75 + p[7]) >= 150) {
3355 g_toc[i].dwStartSector = (p[5]*60 + p[6])*75 + p[7] -150;
3357 g_toc[i].dwStartSector = 0;
3359 g_toc[i].mins = p[5];
3360 g_toc[i].secs = p[6];
3361 g_toc[i].frms = p[7];
3367 void toc_entry(unsigned nr, unsigned flag, unsigned tr, unsigned char *ISRC,
3368 unsigned long lba, int m, int s, int f)
3370 if (nr > MAXTRK) return;
3372 g_toc[nr].bFlags = flag;
3373 g_toc[nr].bTrack = tr;
3375 strncpy((char *)g_toc[nr].ISRC, (char *)ISRC,
3376 sizeof(g_toc[nr].ISRC) -1);
3377 g_toc[nr].ISRC[sizeof(g_toc[nr].ISRC) -1] = '\0';
3379 g_toc[nr].dwStartSector = lba;
3385 int patch_real_end(unsigned long sector)
3387 g_toc[cdtracks+1].dwStartSector = sector;
3391 static int patch_cd_extra(unsigned track, unsigned long sector)
3393 if (track <= cdtracks)
3394 g_toc[track].dwStartSector = sector;
3398 static int restrict_tracks_illleadout(void)
3400 struct TOC *o = &g_toc[cdtracks+1];
3402 for (i = cdtracks; i >= 0; i--) {
3403 struct TOC *p = &g_toc[i];
3404 if (GETSTART(o) > GETSTART(p)) break;
3406 patch_cd_extra(i+1, GETSTART(o));
3412 static void Set_ISRC(int track, const unsigned char *ISRC_arg)
3414 if (track <= (int)cdtracks) {
3415 memcpy(Get_ISRC(track), ISRC_arg, 16);
3420 unsigned char *Get_ISRC(unsigned long p_track)
3422 if (p_track <= cdtracks)
3423 return g_toc[p_track].ISRC;
3427 static void patch_to_audio(unsigned long p_track)
3429 if (p_track <= cdtracks)
3430 g_toc[p_track].bFlags &= ~0x40;
3433 int Get_Flags(unsigned long p_track)
3435 if (p_track <= cdtracks)
3436 return g_toc[p_track].bFlags;
3440 int Get_Mins(unsigned long p_track)
3442 if (p_track <= cdtracks)
3443 return g_toc[p_track].mins;
3447 int Get_Secs(unsigned long p_track)
3449 if (p_track <= cdtracks)
3450 return g_toc[p_track].secs;
3454 int Get_Frames(unsigned long p_track)
3456 if (p_track <= cdtracks)
3457 return g_toc[p_track].frms;
3461 int Get_Preemphasis(unsigned long p_track)
3463 if (p_track <= cdtracks)
3464 return g_toc[p_track].bFlags & 0x10;
3468 static void Set_SCMS(unsigned long p_track)
3470 g_toc[p_track].SCMS = 1;
3473 int Get_SCMS(unsigned long p_track)
3475 if (p_track <= cdtracks)
3476 return g_toc[p_track].SCMS;
3480 int Get_Copyright(unsigned long p_track)
3482 if (p_track <= cdtracks) {
3483 if (g_toc[p_track].SCMS) return 1;
3484 return ((int)g_toc[p_track].bFlags & 0x20) >> 4;
3489 int Get_Datatrack(unsigned long p_track)
3491 if (p_track <= cdtracks)
3492 return g_toc[p_track].bFlags & 0x40;
3496 int Get_Channels(unsigned long p_track)
3498 if (p_track <= cdtracks)
3499 return g_toc[p_track].bFlags & 0x80;
3503 int Get_Tracknumber(unsigned long p_track)
3505 if (p_track <= cdtracks)
3506 return g_toc[p_track].bTrack;
3510 static int useHiddenTrack(void)
3517 static void it_reset(struct iterator *this);
3519 static void it_reset(struct iterator *this)
3521 this->index = this->startindex;
3525 static int it_hasNextTrack(struct iterator *this);
3526 static struct TOC *it_getNextTrack(struct iterator *this);
3528 static int it_hasNextTrack(struct iterator *this)
3530 return this->index <= (int)cdtracks+1;
3535 static struct TOC *it_getNextTrack(struct iterator *this)
3537 /* if ( (*this->hasNextTrack)(this) == 0 ) return NULL; */
3538 if ( this->index > (int)cdtracks+1 ) return NULL;
3540 return &g_toc[ this->index++ ];
3544 static void InitIterator(struct iterator *iter, unsigned long p_track)
3546 if (iter == NULL) return;
3548 iter->index = iter->startindex = useHiddenTrack() ? 0 : p_track;
3549 iter->reset = it_reset;
3550 iter->getNextTrack = it_getNextTrack;
3551 iter->hasNextTrack = it_hasNextTrack;
3555 static struct iterator *NewIterator(void);
3557 static struct iterator *NewIterator ()
3559 struct iterator *retval;
3561 retval = malloc (sizeof(struct iterator));
3562 if (retval != NULL) {
3563 InitIterator(retval, 1);
3569 long Get_AudioStartSector(unsigned long p_track)
3572 if (p_track == CDROM_LEADOUT)
3573 p_track = cdtracks + 1;
3575 if (p_track <= cdtracks +1
3576 && IS__AUDIO(&g_toc[p_track]))
3577 return GETSTART(&g_toc[p_track]);
3579 static struct iterator i;
3580 if (i.reset == NULL) InitIterator(&i, p_track);
3583 if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT;
3585 while (i.hasNextTrack(&i)) {
3586 TOC *p = i.getNextTrack(&i);
3588 if (GETTRACK(p) == p_track) {
3600 long Get_StartSector(unsigned long p_track)
3603 if (p_track == CDROM_LEADOUT)
3604 p_track = cdtracks + 1;
3606 if (p_track <= cdtracks +1)
3607 return GETSTART(&g_toc[p_track]);
3609 static struct iterator i;
3610 if (i.reset == NULL) InitIterator(&i, p_track);
3613 if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT;
3615 while (i.hasNextTrack(&i)) {
3616 TOC *p = i.getNextTrack(&i);
3618 if (GETTRACK(p) == p_track) {
3627 long Get_EndSector(unsigned long p_track)
3630 if (p_track <= cdtracks)
3631 return GETSTART(&g_toc[p_track+1])-1;
3633 static struct iterator i;
3634 if (i.reset == NULL) InitIterator(&i, p_track);
3637 if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT;
3639 while (i.hasNextTrack(&i)) {
3640 TOC *p = i.getNextTrack(&i);
3641 if (GETTRACK(p) == p_track) {
3642 p = i.getNextTrack(&i);
3646 return GETSTART(p)-1;
3653 long FirstTrack(void)
3655 static struct iterator i;
3656 if (i.reset == NULL) InitIterator(&i, 1);
3659 if (i.hasNextTrack(&i)) {
3660 return GETTRACK(i.getNextTrack(&i));
3665 long FirstAudioTrack(void)
3667 static struct iterator i;
3668 if (i.reset == NULL) InitIterator(&i, 1);
3671 while (i.hasNextTrack(&i)) {
3672 TOC *p = i.getNextTrack(&i);
3673 unsigned ii = GETTRACK(p);
3675 if (ii == CDROM_LEADOUT) break;
3683 long FirstDataTrack(void)
3685 static struct iterator i;
3686 if (i.reset == NULL) InitIterator(&i, 1);
3689 while (i.hasNextTrack(&i)) {
3690 TOC *p = i.getNextTrack(&i);
3698 long LastTrack(void)
3700 return g_toc[cdtracks].bTrack;
3703 long LastAudioTrack(void)
3706 static struct iterator i;
3707 if (i.reset == NULL) InitIterator(&i, 1);
3710 while (i.hasNextTrack(&i)) {
3711 TOC *p = i.getNextTrack(&i);
3712 if (IS__AUDIO(p) && (GETTRACK(p) != CDROM_LEADOUT)) {
3719 long Get_LastSectorOnCd(unsigned long p_track)
3722 static struct iterator i;
3724 if (global.illleadout_cd && global.reads_illleadout)
3725 return 150+(99*60+59)*75+74;
3727 if (i.reset == NULL) InitIterator(&i, p_track);
3730 if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT;
3732 while (i.hasNextTrack(&i)) {
3733 TOC *p = i.getNextTrack(&i);
3735 if (GETTRACK(p) < p_track)
3738 LastSec = GETSTART(p);
3740 if (IS__DATA(p)) break;
3745 int Get_Track(unsigned long sector)
3747 static struct iterator i;
3748 if (i.reset == NULL) InitIterator(&i, 1);
3751 if (i.hasNextTrack(&i)) {
3752 TOC *o = i.getNextTrack(&i);
3753 while (i.hasNextTrack(&i)) {
3754 TOC *p = i.getNextTrack(&i);
3755 if ((GETSTART(o) <= sector) && (GETSTART(p) > sector)) {
3768 int CheckTrackrange(unsigned long from, unsigned long upto)
3770 static struct iterator i;
3771 if (i.reset == NULL) InitIterator(&i, from);
3774 while (i.hasNextTrack(&i)) {
3775 TOC *p = i.getNextTrack(&i);
3777 if (GETTRACK(p) < from)
3780 if (GETTRACK(p) == upto)
3783 /* data tracks terminate the search */
3787 /* track not found */
3792 long cdda_disc_firstsector(void *d);
3794 long cdda_disc_firstsector(void *d)
3796 return Get_StartSector(FirstAudioTrack());
3799 int cdda_tracks(void *d);
3801 int cdda_tracks(void *d)
3803 return LastAudioTrack() - FirstAudioTrack() +1;
3806 int cdda_track_audiop(void *d, int track);
3808 int cdda_track_audiop(void *d, int track)
3810 return Get_Datatrack(track) == 0;
3813 long cdda_track_firstsector(void *d, int track);
3815 long cdda_track_firstsector(void *d, int track)
3817 return Get_AudioStartSector(track);
3820 long cdda_track_lastsector(void *d, int track);
3822 long cdda_track_lastsector(void *d, int track)
3824 return Get_EndSector(track);
3827 long cdda_disc_lastsector(void *d);
3829 long cdda_disc_lastsector(void *d)
3831 return Get_LastSectorOnCd(cdtracks) - 1;
3834 int cdda_sector_gettrack(void *d,long sector);
3836 int cdda_sector_gettrack(void *d, long sector)
3838 return Get_Track(sector);