/* @(#)toc.c 1.104 17/07/19 Copyright 1998-2003,2017 Heiko Eissfeldt, Copyright 2004-2017 J. Schilling */ #include "config.h" #ifndef lint static UConst char sccsid[] = "@(#)toc.c 1.104 17/07/19 Copyright 1998-2003,2017 Heiko Eissfeldt, Copyright 2004-2017 J. Schilling"; #endif /* * CDDA2WAV (C) Heiko Eissfeldt heiko@hexco.de * Copyright (c) 2004-2017 J. Schilling, Heiko Eissfeldt * * The CDDB routines are compatible to cddbd (C) Ti Kan and Steve Scherf */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * A copy of the CDDL is also available via the Internet at * http://www.opensource.org/licenses/cddl1.txt * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ #include "config.h" #include #include #include #include #include #include #include /* sleep */ #include #include #include #include #include #include #include #include #include /* tcp stuff */ /* fix OS/2 compilation */ #ifdef __EMX__ #define gethostid nogethostid #endif #include #undef gethostid #include #include #include "mytype.h" #include "byteorder.h" #include "interface.h" #include "cdda2wav.h" #include "global.h" #include "base64.h" #include "toc.h" #include "exitcodes.h" #include "ringbuff.h" #include "version.h" #include #define CD_TEXT #define CD_EXTRA #undef DEBUG_XTRA #undef DEBUG_CDTEXT #undef DEBUG_CDDBP int have_CD_text; int have_multisession; int have_CD_extra; int have_CDDB; struct iterator; int Get_Mins __PR((unsigned long p_track)); int Get_Secs __PR((unsigned long p_track)); int Get_Frames __PR((unsigned long p_track)); int Get_Flags __PR((unsigned long p_track)); int Get_SCMS __PR((unsigned long p_track)); LOCAL void UpdateTrackData __PR((int p_num)); LOCAL void UpdateIndexData __PR((int p_num)); LOCAL void UpdateTimeData __PR((int p_min, int p_sec, int p_frm)); LOCAL unsigned int is_multisession __PR((void)); LOCAL unsigned int get_end_of_last_audio_track __PR((unsigned mult_off)); LOCAL void check_hidden __PR((void)); LOCAL int cddb_sum __PR((int n)); LOCAL void dump_extra_info __PR((unsigned from)); LOCAL int GetIndexOfSector __PR((unsigned sec, unsigned track)); LOCAL int patch_cd_extra __PR((unsigned track, unsigned long sector)); LOCAL void patch_to_audio __PR((unsigned long p_track)); LOCAL int restrict_tracks_illleadout __PR((void)); LOCAL void Set_MCN __PR((unsigned char *MCN_arg)); LOCAL void Set_ISRC __PR((unsigned track, const unsigned char *ISRC_arg)); LOCAL unsigned char g_track = 0xff; /* current track */ LOCAL unsigned char g_index = 0xff; /* current index */ LOCAL void InitIterator __PR((struct iterator *iter, unsigned long p_track)); /* * Conversion function: from logical block addresses to minute,second,frame */ int lba_2_msf(lba, m, s, f) 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 */ LOCAL void UpdateTrackData(p_num) int p_num; { if (global.quiet == 0) { fprintf(outfp, _("\ntrack: %.2d, "), p_num); fflush(outfp); } g_track = (unsigned char) p_num; } /* * print the index currently read */ LOCAL void UpdateIndexData(p_num) int p_num; { if (global.quiet == 0) { fprintf(outfp, _("index: %.2d\n"), p_num); fflush(outfp); } g_index = (unsigned char) p_num; } /* * print the time of track currently read */ LOCAL void UpdateTimeData(p_min, p_sec, p_frm) int p_min; int p_sec; int p_frm; { if (global.quiet == 0) { fprintf(outfp, _("time: %.2d:%.2d.%.2d\r"), p_min, p_sec, p_frm); fflush(outfp); } } void AnalyzeQchannel(frame) unsigned frame; { if (trackindex_disp != 0) { subq_chnl *sub_ch; 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; LOCAL int have_hiddenAudioTrack = 0; int no_disguised_audiotracks() { /* * 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; errmsgno(EX_BAD, _("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() { int retval = (*doReadToc)(get_scsi_p()); #if defined SIM_ILLLEADOUT extern TOC g_toc[MAXTRK+1]; /* hidden track + 100 regular tracks */ g_toc[cdtracks+1] = 20*75; #endif return (retval); } LOCAL int can_read_illleadout __PR((void)); LOCAL int can_read_illleadout() { SCSI *scgp = get_scsi_p(); UINT4 buffer [CD_FRAMESIZE_RAW/4]; if (global.illleadout_cd == 0) return (0); scgp->silent++; global.reads_illleadout = ReadCdRom(scgp, buffer, Get_AudioStartSector(CDROM_LEADOUT), 1); scgp->silent--; return (global.reads_illleadout); } unsigned find_an_off_sector __PR((unsigned lSector, unsigned SectorBurstVal)); unsigned find_an_off_sector(lSector, SectorBurstVal) 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 __PR((void)); int handle_cdtext() { #ifdef CD_TEXT if (global.buf[0] == 0 && global.buf[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 = ((global.buf[0] & 0xFF) << 8) | (global.buf[1] & 0xFF); len += 2; len = min(len, global.bufsize); for (i = 0; i < len-4; i += 18) { if ((global.buf[4+i] & 0xFF) < 0x80 || (global.buf[4+i] & 0xFF) > 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 LOCAL int tmp_fd; #endif #ifdef CD_EXTRA #include "cd_extra.c" #endif LOCAL 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. */ LOCAL unsigned is_multisession() { 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) errmsg(_("Multi session ioctl not supported.\n")); } else { #ifdef DEBUG_XTRA fprintf(outfp, _("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(outfp, _("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). */ LOCAL unsigned get_end_of_last_audio_track(mult_off) 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); } } LOCAL void dump_cdtext_info __PR((void)); #if defined CDDB_SUPPORT LOCAL void emit_cddb_form __PR((char *fname_baseval)); #endif #if defined CDINDEX_SUPPORT LOCAL void emit_cdindex_form __PR((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) __PR((struct iterator *this)); struct TOC *(*getNextTrack) __PR((struct iterator *this)); int (*hasNextTrack) __PR((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(no_tracks) 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())) { errmsgno(EX_BAD, _("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(outfp, "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(outfp, "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 errmsgno(EX_BAD, _("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; struct iterator i; 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(outfp, "setting end of session (track %d) to %u\n", j, real_end); #endif } check_hidden(); return (offset); } LOCAL void check_hidden() { long sect; if (!global.no_hidden_track && (sect = Get_AudioStartSector(FirstAudioTrack())) > 0) { myringbuff *p = RB_BASE; /* Not yet initialized */ int i; int n; BOOL isdata = TRUE; get_scsi_p()->silent++; /* * switch cdrom to data mode */ EnableCdda(get_scsi_p(), 0, 0); i = ReadCdRomData(get_scsi_p(), (Uchar *)p->data, 0, 1); if (i != 1) { have_hiddenAudioTrack = 1; isdata = FALSE; } if (global.quiet == 0) { fprintf(outfp, _("%ld sectors of %sdata before track #%ld"), sect, !isdata ? _("audio "):"", FirstAudioTrack()); } /* * switch cdrom to audio mode */ EnableCdda(get_scsi_p(), 1, CD_FRAMESIZE_RAW); i = ReadCdRom(get_scsi_p(), p->data, 0, 1); if (isdata && i != 1) { if (global.quiet == 0) { fprintf(outfp, _(", ignoring.\n")); } } else if (i != 1) { if (global.quiet == 0) { fprintf(outfp, _(", unreadable by this drive.\n")); } have_hiddenAudioTrack = 0; } else { for (n = 0; n < sect; n += global.nsectors) { int o; fillbytes(p->data, CD_FRAMESIZE_RAW, '\0'); i = ReadCdRom(get_scsi_p(), p->data, n, global.nsectors); if (i != global.nsectors) break; if ((o = cmpnullbytes(p->data, global.nsectors * CD_FRAMESIZE_RAW)) < global.nsectors * CD_FRAMESIZE_RAW) { if (global.quiet == 0) { fprintf(outfp, _(", audible data at sector %d.\n"), n + o / CD_FRAMESIZE_RAW); } break; } } if (n >= sect) { have_hiddenAudioTrack = 0; if (global.quiet == 0) fprintf(outfp, "\n"); } else { if (global.quiet == 0) fprintf(outfp, _("Hidden audio track with %ld sectors found.\n"), sect); } } get_scsi_p()->silent--; } } LOCAL int cddb_sum(n) int n; { int ret; for (ret = 0; n > 0; n /= 10) { ret += (n % 10); } return (ret); } void calc_cddb_id() { 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() { SHA1_CTX sha; unsigned char digest[20], *base64; unsigned long size; SHA1Init(&sha); SHA1Update(&sha, (unsigned char *)"0123456789", 10); SHA1Final(digest, &sha); base64 = rfc822_binary((char *)digest, 20, &size); if (strncmp((char *) base64, "h6zsF82dzSCnFsws9nQXtxyKcBY-", size)) { free(base64); fprintf(outfp, "The SHA-1 hash function failed to properly generate the\n"); fprintf(outfp, "test key.\n"); exit(INTERNAL_ERROR); } free(base64); } #endif void calc_cdindex_id() { SHA1_CTX sha; unsigned char digest[20], *base64; unsigned long size; unsigned i; char temp[9]; #ifdef TESTCDINDEX extern TOC g_toc[MAXTRK+1]; /* hidden track + 100 regular tracks */ 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 SHA1Init(&sha); sprintf(temp, "%02X", Get_Tracknumber(1)); SHA1Update(&sha, (unsigned char *)temp, 2); sprintf(temp, "%02X", Get_Tracknumber(cdtracks)); SHA1Update(&sha, (unsigned char *)temp, 2); /* the position of the leadout comes first. */ sprintf(temp, "%08lX", 150 + Get_StartSector(CDROM_LEADOUT)); SHA1Update(&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)); SHA1Update(&sha, (unsigned char *)temp, 8); } for (i++; i <= 100; i++) { SHA1Update(&sha, (unsigned char *)"00000000", 8); } SHA1Final(digest, &sha); base64 = rfc822_binary((char *)digest, 20, &size); global.cdindex_id = base64; } #if defined CDDB_SUPPORT #ifdef PROTOTYPES LOCAL void escape_and_split(FILE *channel, const char *args, ...) #else /*VARARGS3*/ LOCAL void escape_and_split(channel, args, va_alist) FILE *channel; const char *args; va_dcl #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); } LOCAL void emit_cddb_form(fname_baseval) char *fname_baseval; { 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") == 0) 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: cdda2wav "); fprintf(cddb_form, VERSION); fprintf(cddb_form, VERSION_OS); fprintf(cddb_form, "\n"); fprintf(cddb_form, "DISCID=%08lx\n", (unsigned long)global.cddb_id); if (global.disctitle == NULL && global.performer == NULL) { fprintf(cddb_form, "DTITLE=\n"); } else { if (global.performer == NULL) { escape_and_split(cddb_form, "DTITLE=", global.disctitle, ""); } else if (global.disctitle == NULL) { escape_and_split(cddb_form, "DTITLE=", global.performer, ""); } else { escape_and_split(cddb_form, "DTITLE=", global.performer, " / ", 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); InitIterator(&i, 1); 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, ""); } InitIterator(&i, 1); 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 LOCAL int readn __PR((register int fd, register char *ptr, register int nbytes)); LOCAL int writen __PR((register int fd, register char *ptr, register int nbytes)); LOCAL int readn(fd, ptr, nbytes) register int fd; register char *ptr; register int nbytes; { int nread; nread = _niread(fd, ptr, nbytes); #ifdef DEBUG_CDDBP if (nread > 0) { fprintf(outfp, "READ :(%d)", nread); write(2, ptr, nread); } #endif if (nread < 0) { errmsg(_("Socket read error: fd=%d, ptr=%p, nbytes=%d.\n"), fd, ptr, nbytes); ptr[0] = '\0'; } return (nread); } LOCAL int writen(fd, ptr, nbytes) register int fd; register char *ptr; register int nbytes; { int nwritten; nwritten = _nixwrite(fd, ptr, nbytes); #ifdef DEBUG_CDDBP if (nwritten <= 0) fprintf(outfp, "WRITE:%s\n", ptr); #endif if (nwritten < 0) { errmsg(_("Socket write error: fd=%d, ptr=%p, nbytes=%d.\n"), fd, ptr, nbytes); } return (nwritten); } #define SOCKBUFF 2048 LOCAL int process_cddb_titles __PR((int sock_fd, char *inbuff, int readbytes)); LOCAL int process_cddb_titles(sock_fd, inbuff, readbytes) int sock_fd; char *inbuff; int readbytes; { int finished = 0; char *p = inbuff; int ind = 0; char **target = (char **)&global.performer; 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) == 0) { 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) == 0) { 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(*target) + clen + 1); } if (*target != NULL) { strcat((char *)*target, inbuff+ind+7); } /* * handle part after the delimiter, if present */ if (res != NULL) { target = (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) == 0) { 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) == 0) { char *q = (char *)memchr(inbuff+ind, '=', readbytes); if (q++ != NULL) { sscanf(q, "%d", &global.cddb_year); } } else if (strncmp(inbuff+ind, "DGENRE", 6) == 0) { 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) == 0) { 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) break; readbytes += newbytes; ind = 0; } } while (!(finished || readbytes == 0)); return (finished); } LOCAL int handle_userchoice __PR((char *p, unsigned size)); LOCAL int handle_userchoice(p, size) 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(outfp, _("%u entries found:\n"), nr); for (q = (char *)memchr(q, '\n', size - (q-p)), o = p, i = 0; i < nr; i++) { *q = '\0'; fprintf(outfp, "%02u: %s\n", i, o); o = q+1; q = (char *)memchr(q, '\n', size - (q-p)); } fprintf(outfp, _("%02u: ignore\n"), i); /* * get user response. * Some OS seem to misshandle stderr and make it buffered, so we * call fflush(outfp) here. */ do { fprintf(outfp, _("please choose one (0-%u): "), nr); fflush(outfp); if (scanf("%u", &user_choice) != 1) user_choice = nr; } 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. */ EXPORT int request_titles() { int retval = 0; int sock_fd; struct sockaddr_in sa; struct hostent *he; struct servent *se; struct passwd *pw = getpwuid(getuid()); char hostname[MAXHOSTNAMELEN+1]; 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) { errmsg(_("Cddb socket failed.\n")); 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"*/); /* * save result data IMMEDIATELY!! */ memset(&sa, 0, sizeof (struct sockaddr_in)); if (he != NULL) { sa.sin_family = he->h_addrtype; /* AF_INET; */ sa.sin_addr.s_addr = ((struct in_addr *)((he->h_addr_list)[0]))->s_addr; } else { errmsg(_("Cddb cannot resolve freedb host.\n")); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(UINT_C(0x526256aa)); /* freedb.freedb.de */ } se = NULL; if (global.cddbp_port != NULL) { sa.sin_port = htons(atoi(global.cddbp_port)); } else { se = getservbyname("cddbp-alt", "tcp"); if (se == NULL) { if (global.cddbp_port == NULL) { se = getservbyname("cddbp", "tcp"); } } if (se != NULL) { sa.sin_port = se->s_port; } else { #if 0 errmsg("Cddb cannot resolve cddbp or cddbp-alt port.\n"); #endif sa.sin_port = htons(CDDBPORT /*8880*/); } } /* TODO timeout */ if (0 > connect(sock_fd, (struct sockaddr *)&sa, sizeof (struct sockaddr_in))) { errmsg(_("Cddb connect failed.\n")); retval = -1; goto errout; } /* * read banner */ readbytes = readn(sock_fd, inbuff, sizeof (inbuff)); if (readbytes < 0) { retval = -1; goto errout; } if (strncmp(inbuff, "200 ", 4) && strncmp(inbuff, "201 ", 4)) { errmsgno(EX_BAD, _("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'; writen(sock_fd, "cddb hello ", 11); 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++; } writen(sock_fd, pw->pw_name, strlen(pw->pw_name)); } else { writen(sock_fd, "unknown", 7); } writen(sock_fd, " ", 1); /* 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++; } } writen(sock_fd, hostname, strlen(hostname)); writen(sock_fd, " cdda2wav ", 10); writen(sock_fd, VERSION, strlen(VERSION)); writen(sock_fd, VERSION_OS, strlen(VERSION_OS)); writen(sock_fd, "\n", 1); readbytes = readn(sock_fd, inbuff, sizeof (inbuff)); if (readbytes < 0) { retval = -1; goto signoff; } if (strncmp(inbuff, "200 ", 4)) { inbuff[readbytes] = '\0'; errmsgno(EX_BAD, _("Bad status from freedb server during hello: %s.\n"), inbuff); retval = -1; goto signoff; } /* * enable new protocol variant. Weird command here, no cddb prefix ?!?! */ writen(sock_fd, "proto\n", 6); readbytes = readn(sock_fd, inbuff, sizeof (inbuff)); if (readbytes < 0) { retval = -1; goto signoff; } /* * check for errors and maximum supported protocol level */ if (strncmp(inbuff, "201 ", 4) > 0) { inbuff[readbytes] = '\0'; errmsgno(EX_BAD, _("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); writen(sock_fd, inbuff, 8); readbytes = readn(sock_fd, inbuff, sizeof (inbuff)); if (readbytes < 0) { retval = -1; goto signoff; } /* * check for errors and maximum supported * protocol level */ if (strncmp(inbuff, "201 ", 4) > 0) { inbuff[readbytes] = '\0'; errmsgno(EX_BAD, _("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"); */ /* * The next line is a CD with Tracktitle= 'Au fond d'un rêve doré' */ /* sprintf(outbuff, "cddb query %s\n", "35069e05 5 150 12471 25528 35331 56166 1696");*/ writen(sock_fd, outbuff, strlen(outbuff)); readbytes = readn(sock_fd, inbuff, sizeof (inbuff)); if (readbytes < 0) { retval = -1; goto signoff; } inbuff[readbytes] = '\0'; cat_offset = 4; if (strncmp(inbuff, "210 ", 4) == 0 || strncmp(inbuff, "211 ", 4) == 0) { /* * 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: errmsgno(EX_BAD, _("Multiple entries found: %s.\n"), inbuff); retval = 2; goto signoff; } } } else if (strncmp(inbuff, "200 ", 4)) { if (strncmp(inbuff, "202 ", 4) == 0) { errmsgno(EX_BAD, _("No cddb entry found: %s.\n"), inbuff); retval = 1; } else { errmsgno(EX_BAD, _("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); /* * The next line is a CD with Tracktitle= 'Au fond d'un rêve doré' */ /* sprintf(inbuff, "cddb read %s %08x\n", category, 0x35069e05);*/ writen(sock_fd, inbuff, strlen(inbuff)); /* * read status and first buffer size. */ readbytes = readn(sock_fd, inbuff, sizeof (inbuff)); if (readbytes < 0) { retval = -1; goto signoff; } if (strncmp(inbuff, "210 ", 4)) { inbuff[readbytes] = '\0'; errmsgno(EX_BAD, _("Bad status from freedb server during read: %s.\n"), inbuff); retval = -1; goto signoff; } if (1 != process_cddb_titles(sock_fd, inbuff, readbytes)) { errmsgno(EX_BAD, _("Cddb read finished not correctly!\n")); } signoff: /* * sign-off */ writen(sock_fd, "quit\n", 5); readbytes = readn(sock_fd, inbuff, sizeof (inbuff)); if (readbytes < 0) { retval = -1; goto errout; } if (strncmp(inbuff, "230 ", 4)) { inbuff[readbytes] = '\0'; errmsgno(EX_BAD, _("Bad status from freedb server during quit: %s.\n"), inbuff); goto errout; } errout: close(sock_fd); return (retval); } #endif #endif #if defined CDINDEX_SUPPORT LOCAL int IsSingleArtist __PR((void)); /* * check, if there are more than one track performers */ LOCAL int IsSingleArtist() { 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.performer && global.trackperformer[ii] && strcmp((char *) global.performer, (char *) global.trackperformer[ii]) != 0) return (0); } return (1); } LOCAL const char *a2h[255-191] = { "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷", "ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ", }; LOCAL char *ascii2html __PR((unsigned char *inp)); LOCAL char * ascii2html(inp) unsigned char *inp; { static unsigned char outline[300]; unsigned char *outp = outline; #define copy_translation(a, b) else if (*inp == (a)) \ { strcpy((char *)outp, (b)); \ outp += sizeof ((b))-1; } while (*inp != '\0') { if (0); copy_translation('"', """) copy_translation('&', "&") copy_translation('<', "<") copy_translation('>', ">") copy_translation(160, " ") else if (*inp < 192) { *outp++ = *inp; } else { strcpy((char *)outp, a2h[*inp-192]); outp += strlen(a2h[*inp-192]); } inp++; } *outp = '\0'; return ((char *) outline); } #undef copy_translation LOCAL void emit_cdindex_form(fname_baseval) 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()) { struct iterator i; InitIterator(&i, 1); fprintf(cdindex_form, " \n %s\n", global.performer ? ascii2html(global.performer) : ""); while (i.hasNextTrack(&i)) { struct TOC *p = i.getNextTrack(&i); unsigned 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 { struct iterator i; InitIterator(&i, 1); fprintf(cdindex_form, " \n"); while (i.hasNextTrack(&i)) { struct TOC *p = i.getNextTrack(&i); unsigned int ii = GETTRACK(p); if (ii == CDROM_LEADOUT) break; if (IS__AUDIO(p)) { fprintf(cdindex_form, " %s\n", global.trackperformer[ii] ? ascii2html(global.trackperformer[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 LOCAL void dump_cdtext_info() { #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; int fulllength; BOOL text_ok = TRUE; unsigned char *p = (unsigned char *)global.buf; 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, global.bufsize); fulllength = datalength; p += 4; datalength -= 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(outfp, "datalength =%d\n", datalength); #endif crc_error = !cdtext_crc_ok(c); if (crc_error) text_ok = FALSE; 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(outfp, _("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(outfp, "\tin=%d, out=%d, items=%d, trcknum=%u\n", inlinecount, outlinecount, itemcount, tracknr); { int q; for (q = outlinecount; q < inlinecount; q++) fprintf(outfp, "%c", lastline[q] ? lastline[q] : 'ß'); fputs("\n", outfp); } #else if (DETAILED) { if (crc_error) fputs(_(" ! uncorr. CRC-Error"), outfp); fputs("\n", outfp); } #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); } } if (!global.no_textfile && text_ok && fulllength > 4) { char fname[200]; char *pp; FILE *f; strncpy(fname, global.fname_base, sizeof (fname) -1); fname[sizeof (fname) -1] = 0; pp = strrchr(fname, '.'); if (pp == NULL) pp = fname + strlen(fname); strncpy(pp, ".cdtext", sizeof (fname) - 1 - (pp - fname)); f = fileopen(fname, "wctb"); if (f) { filewrite(f, global.buf, fulllength); fclose(f); global.did_textfile = 1; } } #endif } LOCAL void dump_extra_info(from) unsigned int from; { #ifdef CD_EXTRA unsigned char *p; if (from == 0) return; p = Extra_buffer + 48; while (*p != '\0') { unsigned pos; unsigned length; 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(outfp, "Language: %c%c (as defined by ISO 639)", *p, *(p+1)); fprintf(outfp, " at sector %u, len=%u (sessionstart=%u)", pos, length, session_start); fputs("\n", outfp); } #endif /* * dump this entry */ Read_Subinfo(pos, length); p += 10; if (p + 9 > (Extra_buffer + CD_FRAMESIZE)) break; } #endif } LOCAL char *quote __PR((unsigned char *string)); LOCAL char * quote(string) 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); } LOCAL void DisplayToc_with_gui __PR((unsigned long dw)); LOCAL void DisplayToc_with_gui(dw) unsigned long dw; { unsigned mins; unsigned secnds; unsigned frames; int count_audio_trks; struct iterator i; InitIterator(&i, 1); 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(outfp, _("Tracks:%u > %u:%02u.%02u\n"), cdtracks, mins, secnds, frames); } else { fprintf(outfp, _("Tracks:%u %u:%02u.%02u\n"), cdtracks, mins, secnds, frames); } } if (global.quiet == 0) { fprintf(outfp, _("CDINDEX discid: %s\n"), global.cdindex_id); fprintf(outfp, _("CDDB discid: 0x%08lx"), (unsigned long) global.cddb_id); if (have_CDDB != 0) { fprintf(outfp, _(" CDDBP titles: resolved\n")); } else { fprintf(outfp, "\n"); } if (have_CD_text != 0) { fprintf(outfp, _("CD-Text: detected\n")); dump_cdtext_info(); } else { fprintf(outfp, _("CD-Text: not detected\n")); } if (have_CD_extra != 0) { fprintf(outfp, _("CD-Extra: detected\n")); dump_extra_info(have_CD_extra); } else { fprintf(outfp, _("CD-Extra: not detected\n")); } fprintf(outfp, _("Album title: '%s'"), (void *)global.disctitle != NULL ? quote(global.disctitle) : ""); fprintf(outfp, _(" from '%s'\n"), (void *)global.performer != NULL ? quote(global.performer) : ""); } 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(outfp, "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(outfp, " %7u", real_start); if (global.verbose & SHOW_TOC) fprintf(outfp, " %2u:%02u.%02u", mins, secnds, frames); if (global.verbose & SHOW_SUMMARY) fprintf(outfp, _(" data %s %s N/A"), /* how recorded */ IS__INCREMENTAL(o) ? _("incremental") : _("uninterrupted"), /* copy-permission */ IS__COPYRIGHTED(o) ? _("copydenied") : _("copyallowed")); fputs("\n", outfp); } 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(outfp, " %7u", GETSTART(o)); if (global.verbose & SHOW_TOC) fprintf(outfp, " %2u:%02u.%02u", mins, secnds, frames); if (global.verbose & SHOW_SUMMARY) fprintf(outfp, _(" 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(outfp, _(" title '%s' from "), (void *) global.tracktitle[GETTRACK(o)] != NULL ? quote(global.tracktitle[GETTRACK(o)]) : ""); fprintf(outfp, "'%s'", (void *) global.trackperformer[GETTRACK(o)] != NULL ? quote(global.trackperformer[GETTRACK(o)]) : ""); } fputs("\n", outfp); count_audio_trks++; } o = p; } /* while */ if (global.verbose & SHOW_STARTPOSITIONS) if (GETTRACK(o) == CDROM_LEADOUT) { fprintf(outfp, _("Leadout: %7u\n"), GETSTART(o)); } } /* if */ } LOCAL void DisplayToc_no_gui __PR((unsigned long dw)); LOCAL void DisplayToc_no_gui(dw) unsigned long dw; { unsigned mins; unsigned secnds; unsigned frames; int count_audio_trks; unsigned ii = 0; struct iterator i; InitIterator(&i, 1); 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"), outfp); fprintf(outfp, _(" %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"), outfp); fprintf(outfp, _(" %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(outfp, _("Table of Contents: total tracks:%u, (total time more than %u:%02u.%02u)\n"), cdtracks, mins, secnds, frames); } else { fprintf(outfp, _("Table of Contents: total tracks:%u, (total time %u:%02u.%02u)\n"), cdtracks, mins, secnds, frames); } } InitIterator(&i, 1); 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(outfp, " %2u.%c%2u:%02u.%02u%c", GETTRACK(o), brace1, mins, secnds, frames, brace2); ii++; if (ii % 5 == 0) fputs(",\n", outfp); else if (ii != cdtracks) fputc(',', outfp); } o = p; } /* for */ if ((ii % 5) != 0) fputs("\n", outfp); } /* if */ if ((global.verbose & SHOW_STARTPOSITIONS) != 0) { fputs(_("\nTable of Contents: starting sectors\n"), outfp); ii = 0; InitIterator(&i, 1); if (i.hasNextTrack(&i)) { TOC *o = i.getNextTrack(&i); for (; i.hasNextTrack(&i); ) { TOC *p = i.getNextTrack(&i); fprintf(outfp, " %2u.(%8u)", GETTRACK(o), have_multisession && GETTRACK(o) == FirstDataTrack() ? have_multisession : GETSTART(o) #ifdef DEBUG_CDDB +150) #else +0); #endif ii++; if ((ii) % 5 == 0) fputs(",\n", outfp); else fputc(',', outfp); o = p; } fprintf(outfp, _(" lead-out(%8u)"), GETSTART(o)); fputs("\n", outfp); } } if (global.quiet == 0) { fprintf(outfp, _("CDINDEX discid: %s\n"), global.cdindex_id); fprintf(outfp, _("CDDB discid: 0x%08lx"), (unsigned long) global.cddb_id); if (have_CDDB != 0) { fprintf(outfp, _(" CDDBP titles: resolved\n")); } else { fprintf(outfp, "\n"); } if (have_CD_text != 0) { fprintf(outfp, _("CD-Text: detected\n")); } else { fprintf(outfp, _("CD-Text: not detected\n")); } if (have_CD_extra != 0) { fprintf(outfp, _("CD-Extra: detected\n")); } else { fprintf(outfp, _("CD-Extra: not detected\n")); } } if ((global.verbose & SHOW_TITLES) != 0) { unsigned int maxlen = 0; if (global.disctitle != NULL) { fprintf(outfp, _("Album title: '%s'"), global.disctitle); if (global.performer != NULL) { fprintf(outfp, _("\t[from %s]"), global.performer); } fputs("\n", outfp); } InitIterator(&i, 1); for (; i.hasNextTrack(&i); ) { TOC *p = i.getNextTrack(&i); unsigned int jj = GETTRACK(p); if (global.tracktitle[jj] != NULL) { unsigned int len; len = strlen((char *)global.tracktitle[jj]); maxlen = max(maxlen, len); } } maxlen = (maxlen + 12 + 8 + 7)/8; InitIterator(&i, 1); for (; i.hasNextTrack(&i); ) { TOC *p = i.getNextTrack(&i); unsigned int jj; if (IS__DATA(p)) continue; jj = GETTRACK(p); if (jj == CDROM_LEADOUT) break; if (maxlen != 3) { if (global.tracktitle[jj] != NULL) { fprintf(outfp, _("Track %2u: '%s'"), jj, global.tracktitle[jj]); } else { fprintf(outfp, _("Track %2u: '%s'"), jj, ""); } if (global.trackperformer[jj] != NULL && #if 1 global.trackperformer[jj][0] != '\0' && (global.performer == NULL || 0 != strcmp((char *)global.performer, (char *)global.trackperformer[jj]))) { #else global.trackperformer[jj][0] != '\0') { #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(outfp, "\t"); } fprintf(outfp, _("[from %s]"), global.trackperformer[jj]); } fputs("\n", outfp); } } } } void DisplayToc() { 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) { errmsgno(EX_BAD, _("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) { errmsgno(EX_BAD, _("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(outfp, _("Switching index scan and ISRC scan off!\n")); } if (global.quiet == 0) { fprintf(outfp, _("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(outfp, _("Restrictions apply, since the size of the last track is unknown!\n")); } } } } LOCAL void Read_MCN_toshiba __PR((subq_chnl **sub_ch)); LOCAL void Read_MCN_toshiba(sub_ch) 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; } } } LOCAL void Get_Set_MCN __PR((void)); LOCAL void Get_Set_MCN() { subq_chnl *sub_ch; subq_catalog *subq_cat = NULL; fprintf(outfp, _("scanning for MCN...")); fflush(outfp); 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 * 01 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); } } } LOCAL void Read_ISRC_toshiba __PR((subq_chnl **sub_ch, unsigned tr)); LOCAL void Read_ISRC_toshiba(sub_ch, tr) 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); } } LOCAL void Get_Set_ISRC __PR((unsigned tr)); LOCAL void Get_Set_ISRC(tr) unsigned tr; { subq_chnl *sub_ch; subq_track_isrc *subq_tr; fprintf(outfp, _("\rscanning for ISRCs: %u ..."), tr); fflush(outfp); 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(outfp, "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(outfp, "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 */ /* BEGIN CSTYLED */ 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 }; /* END CSTYLED */ /* * build 6-bit vector of coded values */ unsigned ind; int bits; #if DEBUG_ISRC fprintf(outfp, "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(outfp, _("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(outfp, "%X", bin2ISRC[binval]); } } fprintf(outfp, "%.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(outfp, "\n"); } return; } if ((cp[7] & 3) != 0) { fprintf(outfp, _("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(outfp, _("\nIllegal ISRC for track %u, skipped: "), tr); for (y = 0; y < 15; y++) { fprintf(outfp, "%02x ", cp[y]); } fputs("\n", outfp); 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(outfp, "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(outfp, _("\nIllegal ISRC for track %d, skipped: "), ii+1); for (y = 0; y < 15; y++) { fprintf(outfp, "%02x ", cp[y]); } fputs("\n", outfp); return; } #if DEBUG_ISRC fprintf(outfp, "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(startTrack, endTrack) unsigned startTrack; unsigned endTrack; { int old_hidden = have_hiddenAudioTrack; have_hiddenAudioTrack = 0; /* Don'tcheck track #0 here */ if ((global.verbose & SHOW_MCN) != 0) { if (Get_MCN()[0] == '\0') { Get_Set_MCN(); } if (Get_MCN()[0] != '\0') { fprintf(outfp, _("\rMedia catalog number: %13.13s\n"), Get_MCN()); } else { fprintf(outfp, _("\rNo media catalog number present.\n")); } } if ((global.verbose & SHOW_ISRC) != 0) { 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 (ii < startTrack || ii > endTrack) continue; if (!IS__AUDIO(p)) continue; if (GETISRC(p)[0] == '\0') { Get_Set_ISRC(ii); } if (GETISRC(p)[0] != '\0') { fprintf(outfp, "\rT: %2u ISRC: %15.15s\n", ii, GETISRC(p)); fflush(outfp); } } /* for all tracks */ fputs("\n", outfp); } /* if SHOW_ISRC */ have_hiddenAudioTrack = old_hidden; /* Restore old value */ } LOCAL int playing = 0; LOCAL subq_chnl *ReadSubChannel __PR((unsigned sec)); LOCAL subq_chnl * ReadSubChannel(sec) 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 /*&& (scg_sense_key(get_scsi_p()) == 5)*/) { /* * command is not implemented */ ReadSubChannels = NULL; #if defined DEBUG_SUB fprintf(outfp, "\nCommand not implemented: switching ReadSubChannels off !\n"); #endif goto fallback; } /* * check the address 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 address mode field for position information */ if ((sub_ch->control_adr & 0x0f) == 0x01) { return (sub_ch); } ReadSubChannels = NULL; fprintf(outfp, _("\nCould not get position information (%02x) for sectors %u, %u, %u: 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); } LOCAL int ReadSubControl __PR((unsigned sec)); LOCAL int ReadSubControl(sec) unsigned sec; { subq_chnl *sub_ch = ReadSubChannel(sec); if (sub_ch == NULL) return (-1); return (sub_ch->control_adr & 0xf0); } LOCAL int HaveSCMS __PR((unsigned StartSector)); LOCAL int HaveSCMS(StartSector) unsigned StartSector; { int i; int copy_bits_set = 0; for (i = 0; i < 8; i++) { int cr; 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() { /* * detect layout * detect tracks */ } LOCAL int GetIndexOfSector(sec, track) unsigned sec; unsigned track; { subq_chnl *sub_ch = ReadSubChannel(sec); if (sub_ch == NULL) { if ((long)sec == Get_EndSector(track)) { errmsgno(EX_BAD, _("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(outfp, _("\ntrack mismatch: %1u, in-track subchannel: %1u (index %1u, sector %1u)\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(outfp, _("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(outfp, _("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(outfp, _("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(outfp, _("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); } LOCAL int ScanBackwardFrom __PR((unsigned sec, unsigned limit, int *where, unsigned track)); LOCAL int ScanBackwardFrom(sec, limit, where, track) 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) errmsgno(EX_BAD, _("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) errmsgno(EX_BAD, _("Could not find index transition for pre-gap.\n")); if (where != NULL) *where = -1; } } } return (lastindex); } #ifdef USE_LINEAR_SEARCH LOCAL int linear_search __PR((int searchInd, unsigned int Start, unsigned int End, unsigned track)); LOCAL int linear_search(searchInd, Start, End, track) int searchInd; unsigned Start; unsigned 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 LOCAL int binary_search __PR((int searchInd, unsigned int Start, unsigned int End, unsigned track)); LOCAL int binary_search(searchInd, Start, End, track) 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(outfp, "(%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(outfp, "%d -> ", x); #endif } #ifdef DEBUG_BINSEARCH fprintf(outfp, "%d,%d)\n", l, r); #endif if (searchInd == GetIndexOfSector(l, track)) return (l); else return (r); } return (-1); } #endif LOCAL void register_index_position __PR((int IndexOffset, index_list **last_index_entry)); LOCAL void register_index_position(IndexOffset, last_index_entry) 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(outfp, _("No memory for index lists. Index positions\nwill not be written in info file!\n")); #endif } } LOCAL void Set_SCMS __PR((unsigned long p_track)); #undef DEBUG_INDLIST /* * experimental code * search for indices (audio mode required) */ unsigned ScanIndices(track, cd_index, bulk) 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; unsigned endtrack; unsigned startindex; unsigned 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 *scgp = get_scsi_p(); int old_hidden = have_hiddenAudioTrack; struct iterator i; have_hiddenAudioTrack = 0; /* Don'tcheck track #0 here */ InitIterator(&i, 1); EnableCdda(scgp, 0, 0); EnableCdda(scgp, 1, CD_FRAMESIZE_RAW + 16); if (!global.quiet && !(global.verbose & SHOW_INDICES)) fprintf(outfp, _("seeking index start ...")); starttrack = track; endtrack = global.endtrack; baseindex_pool = (index_list *) malloc(sizeof (index_list) * (endtrack - starttrack + 1)); #ifdef DEBUG_INDLIST fprintf(outfp, "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(outfp, _("Analysis of track %u 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(outfp, _("\rindex scan: %u..."), ii); fflush(outfp); } 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(outfp, _("\rtrack %2u 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(outfp, "%2u: N/A ", j); if (((j + 1) % 8) == 0) fputs("\n", outfp); } else { fprintf(outfp, "\rT%02u I%02u N/A\n", ii, j); } } } else { if (global.verbose & SHOW_INDICES) { if (global.gui == 0) { fprintf(outfp, "%2u:%6lu ", j, IndexOffset- Get_AudioStartSector(ii)); if (((j + 1) % 8) == 0) fputs("\n", outfp); } else { fprintf(outfp, "\rT%02u 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 * unless it is the last entry and * there is no index 0 transition */ if (!(ip->next == NULL && ip->frameoffset == n_0_transition)) 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", outfp); } /* for tracks */ if (global.gui == 0 && (global.verbose & SHOW_INDICES)) fputs("\n", outfp); if (playing != 0) StopPlay(get_scsi_p()); EnableCdda(scgp, 0, 0); EnableCdda(scgp, 1, CD_FRAMESIZE_RAW); have_hiddenAudioTrack = old_hidden; /* Restore old value */ return (retval); } LOCAL unsigned char MCN[14]; LOCAL void Set_MCN(MCN_arg) unsigned char *MCN_arg; { memcpy(MCN, MCN_arg, 14); MCN[13] = '\0'; } unsigned char * Get_MCN() { return (MCN); } LOCAL TOC g_toc[MAXTRK+1]; /* hidden track + 100 regular tracks */ /*#define IS_AUDIO(i) (!(g_toc[i].bFlags & 0x40))*/ int TOC_entries(tracks, a, b, binvalid) unsigned tracks; unsigned char *a; unsigned char *b; int binvalid; { int i; for (i = 1; i <= (int)tracks; i++) { unsigned char *p; if (binvalid) { unsigned long dwStartSector; 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(nr, flag, tr, ISRC, lba, m, s, f) 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(sector) unsigned long sector; { g_toc[cdtracks+1].dwStartSector = sector; return (0); } LOCAL int patch_cd_extra(track, sector) unsigned track; unsigned long sector; { if (track <= cdtracks) g_toc[track].dwStartSector = sector; return (0); } LOCAL int restrict_tracks_illleadout() { 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); } LOCAL void Set_ISRC(track, ISRC_arg) unsigned track; const unsigned char *ISRC_arg; { if (track <= (int)cdtracks) { memcpy(Get_ISRC(track), ISRC_arg, 16); } } unsigned char * Get_ISRC(p_track) unsigned long p_track; { if (p_track <= cdtracks) return (g_toc[p_track].ISRC); return (NULL); } LOCAL void patch_to_audio(p_track) unsigned long p_track; { if (p_track <= cdtracks) g_toc[p_track].bFlags &= ~0x40; } int Get_Flags(p_track) unsigned long p_track; { if (p_track <= cdtracks) return (g_toc[p_track].bFlags); return (-1); } int Get_Mins(p_track) unsigned long p_track; { if (p_track <= cdtracks) return (g_toc[p_track].mins); return (-1); } int Get_Secs(p_track) unsigned long p_track; { if (p_track <= cdtracks) return (g_toc[p_track].secs); return (-1); } int Get_Frames(p_track) unsigned long p_track; { if (p_track <= cdtracks) return (g_toc[p_track].frms); return (-1); } int Get_Preemphasis(p_track) unsigned long p_track; { if (p_track <= cdtracks) return (g_toc[p_track].bFlags & 0x10); return (-1); } LOCAL void Set_SCMS(p_track) unsigned long p_track; { g_toc[p_track].SCMS = 1; } int Get_SCMS(p_track) unsigned long p_track; { if (p_track <= cdtracks) return (g_toc[p_track].SCMS); return (-1); } int Get_Copyright(p_track) 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(p_track) unsigned long p_track; { if (p_track <= cdtracks) return (g_toc[p_track].bFlags & 0x40); return (-1); } int Get_Channels(p_track) unsigned long p_track; { if (p_track <= cdtracks) return (g_toc[p_track].bFlags & 0x80); return (-1); } int Get_Tracknumber(p_track) unsigned long p_track; { if (p_track <= cdtracks) return (g_toc[p_track].bTrack); return (-1); } int useHiddenTrack __PR((void)); int useHiddenTrack() { if (global.no_hidden_track) return (0); return (have_hiddenAudioTrack); } LOCAL void it_reset __PR((struct iterator *this)); LOCAL void it_reset(this) struct iterator *this; { this->index = this->startindex; } LOCAL int it_hasNextTrack __PR((struct iterator *this)); LOCAL struct TOC *it_getNextTrack __PR((struct iterator *this)); LOCAL int it_hasNextTrack(this) struct iterator *this; { return (this->index <= (int)cdtracks+1); } LOCAL struct TOC * it_getNextTrack(this) struct iterator *this; { /* if ((*this->hasNextTrack)(this) == 0) return (NULL); */ if (this->index > (int)cdtracks+1) return (NULL); return (&g_toc[this->index++]); } LOCAL void InitIterator(iter, p_track) struct iterator *iter; unsigned long p_track; { if (iter == NULL) return; iter->startindex = useHiddenTrack() ? 0 : p_track; iter->reset = it_reset; iter->getNextTrack = it_getNextTrack; iter->hasNextTrack = it_hasNextTrack; iter->reset(iter); } #if 0 LOCAL struct iterator *NewIterator __PR((void)); LOCAL struct iterator * NewIterator() { struct iterator *retval; retval = malloc(sizeof (struct iterator)); if (retval != NULL) { InitIterator(retval, 1); } return (retval); } #endif long Get_AudioStartSector(p_track) 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 struct iterator i; InitIterator(&i, p_track); 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(p_track) 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 struct iterator i; InitIterator(&i, p_track); 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(p_track) unsigned long p_track; { #if 1 if (p_track <= cdtracks) return (GETSTART(&g_toc[p_track+1])-1); #else struct iterator i; InitIterator(&i, p_track); 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() { struct iterator i; InitIterator(&i, 1); if (i.hasNextTrack(&i)) { return (GETTRACK(i.getNextTrack(&i))); } return (-1); } long FirstAudioTrack() { struct iterator i; InitIterator(&i, 1); 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() { struct iterator i; InitIterator(&i, 1); while (i.hasNextTrack(&i)) { TOC *p = i.getNextTrack(&i); if (IS__DATA(p)) { return (GETTRACK(p)); } } return (-1); } long LastTrack() { return (g_toc[cdtracks].bTrack); } long LastAudioTrack() { long j = -1; struct iterator i; InitIterator(&i, 1); 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(p_track) unsigned long p_track; { long LastSec = 0; struct iterator i; if (global.illleadout_cd && global.reads_illleadout) return (150+(99*60+59)*75+74); InitIterator(&i, p_track); 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(sector) unsigned long sector; { struct iterator i; InitIterator(&i, 1); 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(from, upto) unsigned long from; unsigned long upto; { struct iterator i; InitIterator(&i, from); 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 __PR((void *d)); long cdda_disc_firstsector(d) void *d; { return (Get_StartSector(FirstAudioTrack())); } int cdda_tracks __PR((void *d)); int cdda_tracks(d) void *d; { return (LastAudioTrack() - FirstAudioTrack() +1); } int cdda_track_audiop __PR((void *d, int track)); int cdda_track_audiop(d, track) void *d; int track; { return (Get_Datatrack(track) == 0); } long cdda_track_firstsector __PR((void *d, int track)); long cdda_track_firstsector(d, track) void *d; int track; { return (Get_AudioStartSector(track)); } long cdda_track_lastsector __PR((void *d, int track)); long cdda_track_lastsector(d, track) void *d; int track; { return (Get_EndSector(track)); } long cdda_disc_lastsector __PR((void *d)); long cdda_disc_lastsector(d) void *d; { return (Get_LastSectorOnCd(cdtracks) - 1); } int cdda_sector_gettrack __PR((void *d, long sector)); int cdda_sector_gettrack(d, sector) void *d; long sector; { return (Get_Track(sector)); } #endif