summaryrefslogtreecommitdiff
path: root/icedax/toc.c
diff options
context:
space:
mode:
Diffstat (limited to 'icedax/toc.c')
-rw-r--r--icedax/toc.c3841
1 files changed, 3841 insertions, 0 deletions
diff --git a/icedax/toc.c b/icedax/toc.c
new file mode 100644
index 0000000..e03c23d
--- /dev/null
+++ b/icedax/toc.c
@@ -0,0 +1,3841 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)toc.c 1.57 06/02/19 Copyright 1998-2003 Heiko Eissfeldt */
+/*
+ * Copyright: GNU Public License 2 applies
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * CDDA2WAV (C) Heiko Eissfeldt heiko@hexco.de
+ * CDDB routines (C) Ti Kan and Steve Scherf
+ */
+#include "config.h"
+#include <stdio.h>
+#include <standard.h>
+#include <stdxlib.h>
+#include <strdefs.h>
+#include <utypes.h>
+#include <intcvt.h>
+#include <unixstd.h> /* sleep */
+#include <ctype.h>
+#include <errno.h>
+#include <fctldefs.h>
+#include <vadefs.h>
+#include <schily.h>
+#include <libport.h>
+#include <sys/ioctl.h>
+
+#define CD_TEXT
+#define CD_EXTRA
+#undef DEBUG_XTRA
+#undef DEBUG_CDTEXT
+#undef DEBUG_CDDBP
+
+
+#include <usal/scsitransp.h>
+
+#include "mytype.h"
+#include "byteorder.h"
+#include "interface.h"
+#include "icedax.h"
+#include "global.h"
+#include "sha.h"
+#include "base64.h"
+#include "toc.h"
+#include "exitcodes.h"
+#include "ringbuff.h"
+
+int Get_Mins(unsigned long p_track);
+int Get_Secs(unsigned long p_track);
+int Get_Frames(unsigned long p_track);
+int Get_Flags(unsigned long p_track);
+int Get_SCMS(unsigned long p_track);
+
+
+#if defined USE_REMOTE
+/* tcp stuff */
+/* fix OS/2 compilation */
+#ifdef __EMX__
+#define gethostid nogethostid
+#endif
+#include <sys/socket.h>
+#undef gethostid
+#include <netinet/in.h>
+#if defined(HAVE_NETDB_H) && !defined(HOST_NOT_FOUND) && \
+ !defined(_INCL_NETDB_H)
+#include <netdb.h>
+#define _INCL_NETDB_H
+#endif
+#endif
+
+int have_CD_text;
+int have_multisession;
+int have_CD_extra;
+int have_CDDB;
+
+struct iterator;
+
+static void UpdateTrackData(int p_num);
+static void UpdateIndexData(int p_num);
+static void UpdateTimeData(int p_min, int p_sec, int p_frm);
+static unsigned int is_multisession(void);
+static unsigned int get_end_of_last_audio_track(unsigned mult_off);
+static int cddb_sum(int n);
+static void dump_extra_info(unsigned from);
+static int GetIndexOfSector(unsigned sec, unsigned track);
+static int patch_cd_extra(unsigned track, unsigned long sector);
+static void patch_to_audio(unsigned long p_track);
+static int restrict_tracks_illleadout(void);
+static void Set_MCN(unsigned char *MCN_arg);
+static void Set_ISRC(int track, const unsigned char *ISRC_arg);
+static void InitIterator(struct iterator *iter, unsigned long p_track);
+
+static unsigned char g_track=0xff, g_index=0xff; /* current track, index */
+
+/* Conversion function: from logical block adresses to minute,second,frame
+ */
+int lba_2_msf(long lba, int *m, int *s, int *f)
+{
+#ifdef __follow_redbook__
+ if (lba >= -150 && lba < 405000) { /* lba <= 404849 */
+#else
+ if (lba >= -150) {
+#endif
+ lba += 150;
+ } else if (lba >= -45150 && lba <= -151) {
+ lba += 450150;
+ } else
+ return 1;
+
+ *m = lba / 60 / 75;
+ lba -= (*m)*60*75;
+ *s = lba / 75;
+ lba -= (*s)*75;
+ *f = lba;
+
+ return 0;
+}
+
+/* print the track currently read */
+static void UpdateTrackData(int p_num)
+{
+ if (global.quiet == 0) {
+ fprintf (stderr, "\ntrack: %.2d, ", p_num); fflush(stderr);
+ }
+ g_track = (unsigned char) p_num;
+}
+
+
+/* print the index currently read */
+static void UpdateIndexData(int p_num)
+{
+ if (global.quiet == 0) {
+ fprintf (stderr, "index: %.2d\n", p_num); fflush(stderr);
+ }
+ g_index = (unsigned char) p_num;
+}
+
+
+/* print the time of track currently read */
+static void UpdateTimeData(int p_min, int p_sec, int p_frm)
+{
+ if (global.quiet == 0) {
+ fprintf (stderr, "time: %.2d:%.2d.%.2d\r", p_min, p_sec, p_frm);
+ fflush(stderr);
+ }
+}
+
+void AnalyzeQchannel(unsigned frame)
+{
+ subq_chnl *sub_ch;
+
+ if (trackindex_disp != 0) {
+ sub_ch = ReadSubQ(get_scsi_p(), GET_POSITIONDATA,0);
+ /* analyze sub Q-channel data */
+ if (sub_ch->track != g_track ||
+ sub_ch->index != g_index) {
+ UpdateTrackData (sub_ch->track);
+ UpdateIndexData (sub_ch->index);
+ }
+ }
+ frame += 150;
+ UpdateTimeData ((unsigned char) (frame / (60*75)),
+ (unsigned char) ((frame % (60*75)) / 75),
+ (unsigned char) (frame % 75));
+}
+
+unsigned cdtracks = 0;
+
+int no_disguised_audiotracks(void)
+{
+ /* we can assume no audio tracks according to toc here. */
+ /* read a data sector from the first data track */
+ unsigned char p[3000];
+ int retval;
+ get_scsi_p()->silent++;
+ retval = 1 == ReadCdRomData(get_scsi_p(), p, Get_StartSector(1), 1);
+ get_scsi_p()->silent--;
+ if (retval == 0) {
+ int i;
+fprintf(stderr, "Warning: wrong track types found: patching to audio...\n");
+ for (i = 0; i < cdtracks; i++)
+ patch_to_audio(i);
+ }
+ return retval;
+}
+
+
+#undef SIM_ILLLEADOUT
+int ReadToc(void)
+{
+ int retval = (*doReadToc)( get_scsi_p() );
+#if defined SIM_ILLLEADOUT
+ g_toc[cdtracks+1] = 20*75;
+#endif
+ return retval;
+}
+
+static int can_read_illleadout(void);
+
+static int can_read_illleadout(void)
+{
+ SCSI *usalp = get_scsi_p();
+
+ UINT4 buffer [CD_FRAMESIZE_RAW/4];
+ if (global.illleadout_cd == 0) return 0;
+
+ usalp->silent++;
+ global.reads_illleadout =
+ ReadCdRom(usalp, buffer, Get_AudioStartSector(CDROM_LEADOUT), 1);
+ usalp->silent--;
+ return global.reads_illleadout;
+}
+
+
+unsigned find_an_off_sector(unsigned lSector, unsigned SectorBurstVal);
+
+unsigned find_an_off_sector(unsigned lSector, unsigned SectorBurstVal)
+{
+ long track_of_start = Get_Track(lSector);
+ long track_of_end = Get_Track(lSector + SectorBurstVal -1);
+ long start = Get_AudioStartSector(track_of_start);
+ long end = Get_EndSector(track_of_end);
+
+ if (lSector - start > end - lSector + SectorBurstVal -1)
+ return start;
+ else
+ return end;
+}
+
+#ifdef CD_TEXT
+#include "scsi_cmds.h"
+#endif
+
+
+int handle_cdtext(void)
+{
+#ifdef CD_TEXT
+ if (bufferTOC[0] == 0 && bufferTOC[1] == 0) {
+ have_CD_text = 0;
+ return have_CD_text;
+ }
+
+ /* do a quick scan over all pack type indicators */
+ {
+ int i;
+ int count_fails = 0;
+ int len = (bufferTOC[0] << 8) | bufferTOC[1];
+
+ len = min(len, 2048);
+ for (i = 0; i < len-4; i += 18) {
+ if (bufferTOC[4+i] < 0x80 || bufferTOC[4+i] > 0x8f) {
+ count_fails++;
+ }
+ }
+ have_CD_text = len > 4 && count_fails < 3;
+ }
+
+#else
+ have_CD_text = 0;
+#endif
+ return have_CD_text;
+}
+
+
+#ifdef CD_TEXT
+#include "cd_text.c"
+#endif
+
+
+#if defined CDROMMULTISESSION
+static int tmp_fd;
+#endif
+
+#ifdef CD_EXTRA
+#include "cd_extra.c"
+#endif
+
+static unsigned session_start;
+/*
+ A Cd-Extra is detected, if it is a multisession CD with
+ only audio tracks in the first session and a data track
+ in the last session.
+ */
+static unsigned is_multisession(void)
+{
+ unsigned mult_off;
+#if defined CDROMMULTISESSION
+ /*
+ * FIXME: we would have to do a ioctl (CDROMMULTISESSION)
+ * for the cdrom device associated with the generic device
+ * not just AUX_DEVICE
+ */
+ struct cdrom_multisession ms_str;
+
+ if (interface == GENERIC_SCSI)
+ tmp_fd = open (global.aux_name, O_RDONLY);
+ else
+ tmp_fd = global.cooked_fd;
+
+ if (tmp_fd != -1) {
+ int result;
+
+ ms_str.addr_format = CDROM_LBA;
+ result = ioctl(tmp_fd, CDROMMULTISESSION, &ms_str);
+ if (result == -1) {
+ if (global.verbose != 0)
+ perror("multi session ioctl not supported: ");
+ } else {
+#ifdef DEBUG_XTRA
+ fprintf(stderr, "current ioctl multisession_offset = %u\n", ms_str.addr.lba);
+#endif
+ if (interface == GENERIC_SCSI)
+ close (tmp_fd);
+ if (ms_str.addr.lba > 0)
+ return ms_str.addr.lba;
+ }
+ }
+#endif
+ mult_off = 0;
+ if (LastAudioTrack() + 1 == FirstDataTrack()) {
+ mult_off = Get_StartSector(FirstDataTrack());
+ }
+
+#ifdef DEBUG_XTRA
+ fprintf(stderr, "current guessed multisession_offset = %u\n", mult_off);
+#endif
+ return mult_off;
+}
+
+#define SESSIONSECTORS (152*75)
+/*
+ The solution is to read the Table of Contents of the first
+ session only (if the drive permits that) and directly use
+ the start of the leadout. If this is not supported, we subtract
+ a constant of SESSIONSECTORS sectors (found heuristically).
+ */
+static unsigned get_end_of_last_audio_track(unsigned mult_off)
+{
+ unsigned retval;
+
+ /* Try to read the first session table of contents.
+ This works for Sony and mmc type drives. */
+ if (ReadLastAudio && (retval = ReadLastAudio(get_scsi_p())) != 0) {
+ return retval;
+ } else {
+ return mult_off - SESSIONSECTORS;
+ }
+}
+
+static void dump_cdtext_info(void);
+
+#if defined CDDB_SUPPORT
+static void emit_cddb_form(char *fname_baseval);
+#endif
+
+#if defined CDINDEX_SUPPORT
+static void emit_cdindex_form(char *fname_baseval);
+#endif
+
+
+typedef struct TOC { /* structure of table of contents (cdrom) */
+ unsigned char reserved1;
+ unsigned char bFlags;
+ unsigned char bTrack;
+ unsigned char reserved2;
+ unsigned int dwStartSector;
+ int mins;
+ int secs;
+ int frms;
+ unsigned char ISRC[16];
+ int SCMS;
+} TOC;
+
+
+/* Flags contains two fields:
+ bits 7-4 (ADR)
+ : 0 no sub-q-channel information
+ : 1 sub-q-channel contains current position
+ : 2 sub-q-channel contains media catalog number
+ : 3 sub-q-channel contains International Standard
+ Recording Code ISRC
+ : other values reserved
+ bits 3-0 (Control) :
+ bit 3 : when set indicates there are 4 audio channels else 2 channels
+ bit 2 : when set indicates this is a data track else an audio track
+ bit 1 : when set indicates digital copy is permitted else prohibited
+ bit 0 : when set indicates pre-emphasis is present else not present
+ */
+
+#define GETFLAGS(x) ((x)->bFlags)
+#define GETTRACK(x) ((x)->bTrack)
+#define GETSTART(x) ((x)->dwStartSector)
+#define GETMINS(x) ((x)->mins)
+#define GETSECS(x) ((x)->secs)
+#define GETFRAMES(x) ((x)->frms)
+#define GETISRC(x) ((x)->ISRC)
+
+#define IS__PREEMPHASIZED(p) ( (GETFLAGS(p) & 0x10) != 0)
+#define IS__INCREMENTAL(p) ( (GETFLAGS(p) & 0x10) != 0)
+#define IS__COPYRESTRICTED(p) (!(GETFLAGS(p) & 0x20) != 0)
+#define IS__COPYRIGHTED(p) (!(GETFLAGS(p) & 0x20) != 0)
+#define IS__DATA(p) ( (GETFLAGS(p) & 0x40) != 0)
+#define IS__AUDIO(p) (!(GETFLAGS(p) & 0x40) != 0)
+#define IS__QUADRO(p) ( (GETFLAGS(p) & 0x80) != 0)
+
+/*
+ * Iterator interface inspired from Java
+ */
+struct iterator {
+ int index;
+ int startindex;
+ void (*reset)(struct iterator *this);
+ struct TOC *(*getNextTrack)(struct iterator *this);
+ int (*hasNextTrack)(struct iterator *this);
+};
+
+
+
+
+/* The Table of Contents needs to be corrected if we
+ have a CD-Extra. In this case all audio tracks are
+ followed by a data track (in the second session).
+ Unlike for single session CDs the end of the last audio
+ track cannot be set to the start of the following
+ track, since the lead-out and lead-in would then
+ errenously be part of the audio track. This would
+ lead to read errors when trying to read into the
+ lead-out area.
+ So the length of the last track in case of Cd-Extra
+ has to be fixed.
+ */
+unsigned FixupTOC(unsigned no_tracks)
+{
+ unsigned mult_off;
+ unsigned offset = 0;
+ int j = -1;
+ unsigned real_end = 2000000;
+
+ /* get the multisession offset in sectors */
+ mult_off = is_multisession();
+
+ /* if the first track address had been the victim of an underflow,
+ * set it to zero.
+ */
+ if (Get_StartSector(1) > Get_StartSector(LastTrack())) {
+ fprintf(stderr, "Warning: first track has negative start sector! Setting to zero.\n");
+ toc_entry( 1, Get_Flags(1), Get_Tracknumber(1), Get_ISRC(1), 0, 0, 2, 0 );
+ }
+
+#ifdef DEBUG_XTRA
+ fprintf(stderr, "current multisession_offset = %u\n", mult_off);
+#endif
+ dump_cdtext_info();
+
+ if (mult_off > 100) { /* the offset has to have a minimum size */
+
+ /* believe the multisession offset :-) */
+ /* adjust end of last audio track to be in the first session */
+ real_end = get_end_of_last_audio_track(mult_off);
+#ifdef DEBUG_XTRA
+ fprintf(stderr, "current end = %u\n", real_end);
+#endif
+
+ j = FirstDataTrack();
+ if (LastAudioTrack() + 1 == j) {
+ long sj = Get_StartSector(j);
+ if (sj > (long)real_end) {
+ session_start = mult_off;
+ have_multisession = sj;
+
+#ifdef CD_EXTRA
+ offset = Read_CD_Extra_Info(sj);
+
+ if (offset != 0) {
+ have_CD_extra = sj;
+ dump_extra_info(offset);
+ }
+#endif
+ }
+ }
+ }
+ if (global.cddbp) {
+#if defined USE_REMOTE
+ if (global.disctitle == NULL) {
+ have_CDDB = !request_titles();
+ }
+#else
+ fprintf(stderr, "Cannot lookup titles: no cddbp support included!\n");
+#endif
+ }
+#if defined CDINDEX_SUPPORT || defined CDDB_SUPPORT
+ if (have_CD_text || have_CD_extra || have_CDDB) {
+ unsigned long count_audio_tracks = 0;
+ static struct iterator i;
+ if (i.reset == NULL)
+ InitIterator(&i, 1);
+
+ while (i.hasNextTrack(&i)) {
+ struct TOC *p = i.getNextTrack(&i);
+ if (IS__AUDIO(p)) count_audio_tracks++;
+ }
+
+ if (count_audio_tracks > 0 && global.no_cddbfile == 0) {
+#if defined CDINDEX_SUPPORT
+ emit_cdindex_form(global.fname_base);
+#endif
+#if defined CDDB_SUPPORT
+ emit_cddb_form(global.fname_base);
+#endif
+ }
+ }
+#endif
+ if (have_multisession) {
+ /* set start of track to beginning of lead-out */
+ patch_cd_extra(j, real_end);
+#if defined CD_EXTRA && defined DEBUG_XTRA
+ fprintf(stderr, "setting end of session (track %d) to %u\n", j, real_end);
+#endif
+ }
+ return offset;
+}
+
+static int cddb_sum(int n)
+{
+ int ret;
+
+ for (ret = 0; n > 0; n /= 10) {
+ ret += (n % 10);
+ }
+
+ return ret;
+}
+
+void calc_cddb_id(void)
+{
+ UINT4 i;
+ UINT4 t = 0;
+ UINT4 n = 0;
+
+ for (i = 1; i <= cdtracks; i++) {
+ n += cddb_sum(Get_StartSector(i)/75 + 2);
+ }
+
+ t = Get_StartSector(i)/75 - Get_StartSector(1)/75;
+
+ global.cddb_id = (n % 0xff) << 24 | (t << 8) | cdtracks;
+}
+
+
+#undef TESTCDINDEX
+#ifdef TESTCDINDEX
+void TestGenerateId(void)
+{
+ SHA_INFO sha;
+ unsigned char digest[20], *base64;
+ unsigned long size;
+
+ sha_init(&sha);
+ sha_update(&sha, (unsigned char *)"0123456789", 10);
+ sha_final(digest, &sha);
+
+ base64 = rfc822_binary((char *)digest, 20, &size);
+ if (strncmp((char*) base64, "h6zsF82dzSCnFsws9nQXtxyKcBY-", size))
+ {
+ free(base64);
+
+ fprintf(stderr, "The SHA-1 hash function failed to properly generate the\n");
+ fprintf(stderr, "test key.\n");
+ exit(INTERNAL_ERROR);
+ }
+ free(base64);
+}
+#endif
+
+void calc_cdindex_id()
+{
+ SHA_INFO sha;
+ unsigned char digest[20], *base64;
+ unsigned long size;
+ unsigned i;
+ char temp[9];
+
+#ifdef TESTCDINDEX
+ TestGenerateId();
+ g_toc[1].bTrack = 1;
+ cdtracks = 15;
+ g_toc[cdtracks].bTrack = 15;
+ i = 1;
+ g_toc[i++].dwStartSector = 0U;
+ g_toc[i++].dwStartSector = 18641U;
+ g_toc[i++].dwStartSector = 34667U;
+ g_toc[i++].dwStartSector = 56350U;
+ g_toc[i++].dwStartSector = 77006U;
+ g_toc[i++].dwStartSector = 106094U;
+ g_toc[i++].dwStartSector = 125729U;
+ g_toc[i++].dwStartSector = 149785U;
+ g_toc[i++].dwStartSector = 168885U;
+ g_toc[i++].dwStartSector = 185910U;
+ g_toc[i++].dwStartSector = 205829U;
+ g_toc[i++].dwStartSector = 230142U;
+ g_toc[i++].dwStartSector = 246659U;
+ g_toc[i++].dwStartSector = 265614U;
+ g_toc[i++].dwStartSector = 289479U;
+ g_toc[i++].dwStartSector = 325732U;
+#endif
+ sha_init(&sha);
+ sprintf(temp, "%02X", Get_Tracknumber(1));
+ sha_update(&sha, (unsigned char *)temp, 2);
+ sprintf(temp, "%02X", Get_Tracknumber(cdtracks));
+ sha_update(&sha, (unsigned char *)temp, 2);
+
+ /* the position of the leadout comes first. */
+ sprintf(temp, "%08lX", 150 + Get_StartSector(CDROM_LEADOUT));
+ sha_update(&sha, (unsigned char *)temp, 8);
+
+ /* now 99 tracks follow with their positions. */
+ for (i = 1; i <= cdtracks; i++) {
+ sprintf(temp, "%08lX", 150+Get_StartSector(i));
+ sha_update(&sha, (unsigned char *)temp, 8);
+ }
+ for (i++ ; i <= 100; i++) {
+ sha_update(&sha, (unsigned char *)"00000000", 8);
+ }
+ sha_final(digest, &sha);
+
+ base64 = rfc822_binary((char *)digest, 20, &size);
+ global.cdindex_id = base64;
+}
+
+
+#if defined CDDB_SUPPORT
+
+#ifdef PROTOTYPES
+static void escape_and_split(FILE *channel, const char *args, ...)
+#else
+/*VARARGS3*/
+static void escape_and_split(FILE *channel, const char *args, va_dcl va_alist)
+#endif
+{
+ va_list marker;
+
+ int prefixlen;
+ int len;
+ char *q;
+
+#ifdef PROTOTYPES
+ va_start(marker, args);
+#else
+ va_start(marker);
+#endif
+
+ prefixlen = strlen(args);
+ len = prefixlen;
+ fputs(args, channel);
+
+ q = va_arg(marker, char *);
+ while (*q != '\0') {
+ while (*q != '\0') {
+ len += 2;
+ if (*q == '\\')
+ fputs("\\\\", channel);
+ else if (*q == '\t')
+ fputs("\\t", channel);
+ else if (*q == '\n')
+ fputs("\\n", channel);
+ else {
+ fputc(*q, channel);
+ len--;
+ }
+ if (len > 78) {
+ fputc('\n', channel);
+ fputs(args, channel);
+ len = prefixlen;
+ }
+ q++;
+ }
+ q = va_arg(marker, char *);
+ }
+ fputc('\n', channel);
+
+ va_end(marker);
+}
+
+static void emit_cddb_form(char *fname_baseval)
+{
+ static struct iterator i;
+ unsigned first_audio;
+ FILE *cddb_form;
+ char fname[200];
+ char *pp;
+
+ if (fname_baseval == NULL || fname_baseval[0] == 0)
+ return;
+
+ if (!strcmp(fname_baseval,"standard_output")) return;
+ InitIterator(&i, 1);
+
+ strncpy(fname, fname_baseval, sizeof(fname) -1);
+ fname[sizeof(fname) -1] = 0;
+ pp = strrchr(fname, '.');
+ if (pp == NULL) {
+ pp = fname + strlen(fname);
+ }
+ strncpy(pp, ".cddb", sizeof(fname) - 1 - (pp - fname));
+
+ cddb_form = fopen(fname, "w");
+ if (cddb_form == NULL) return;
+
+ first_audio = FirstAudioTrack();
+ fprintf( cddb_form, "# xmcd\n#\n");
+ fprintf( cddb_form, "# Track frame offsets:\n#\n");
+
+ while (i.hasNextTrack(&i)) {
+ struct TOC *p = i.getNextTrack(&i);
+ if (GETTRACK(p) == CDROM_LEADOUT) break;
+ fprintf( cddb_form,
+ "# %lu\n", 150 + Get_AudioStartSector(GETTRACK(p)));
+ }
+
+ fprintf( cddb_form, "#\n# Disc length: %lu seconds\n#\n",
+ (150 + Get_StartSector(CDROM_LEADOUT)) / 75);
+ fprintf( cddb_form, "# Revision: %u\n", global.cddb_revision );
+ fprintf( cddb_form, "# Submitted via: icedax " VERSION "\n" );
+
+ fprintf( cddb_form, "DISCID=%08lx\n", (unsigned long)global.cddb_id);
+
+ if (global.disctitle == NULL && global.creator == NULL) {
+ fprintf( cddb_form, "DTITLE=\n");
+ } else {
+ if (global.creator == NULL) {
+ escape_and_split( cddb_form, "DTITLE=", global.disctitle, "");
+ } else if (global.disctitle == NULL) {
+ escape_and_split( cddb_form, "DTITLE=", global.creator, "");
+ } else {
+ escape_and_split( cddb_form, "DTITLE=", global.creator, " / ", global.disctitle, "");
+ }
+ }
+ if (global.cddb_year != 0)
+ fprintf( cddb_form, "DYEAR=%4u\n", global.cddb_year);
+ else
+ fprintf( cddb_form, "DYEAR=\n");
+ fprintf( cddb_form, "DGENRE=%s\n", global.cddb_genre);
+
+ i.reset(&i);
+ while (i.hasNextTrack(&i)) {
+ struct TOC *p = i.getNextTrack(&i);
+ int ii;
+
+ ii = GETTRACK(p);
+ if (ii == CDROM_LEADOUT) break;
+
+ if (global.tracktitle[ii] != NULL) {
+ char prefix[10];
+ sprintf(prefix, "TTITLE%d=", ii-1);
+ escape_and_split( cddb_form, prefix, global.tracktitle[ii], "");
+ } else {
+ fprintf( cddb_form, "TTITLE%d=\n", ii-1);
+ }
+ }
+
+ if (global.copyright_message == NULL) {
+ fprintf( cddb_form, "EXTD=\n");
+ } else {
+ escape_and_split( cddb_form, "EXTD=", "Copyright ", global.copyright_message, "");
+ }
+
+ i.reset(&i);
+ while (i.hasNextTrack(&i)) {
+ struct TOC *p = i.getNextTrack(&i);
+ int ii;
+
+ ii = GETTRACK(p);
+
+ if (ii == CDROM_LEADOUT) break;
+
+ fprintf( cddb_form, "EXTT%d=\n", ii-1);
+ }
+ fprintf( cddb_form, "PLAYORDER=\n");
+ fclose( cddb_form );
+}
+
+#if defined USE_REMOTE
+#include <pwd.h>
+
+static int readn(register int fd, register char *ptr, register int nbytes)
+{
+ int nread;
+
+ nread = read(fd, ptr, nbytes);
+#ifdef DEBUG_CDDBP
+ if (nread > 0) {
+ fprintf(stderr, "READ :(%d)", nread);
+ write(2, ptr, nread);
+ }
+#endif
+ if (nread < 0) {
+ perror("socket read error: ");
+ fprintf(stderr, "fd=%d, ptr=%p, nbytes=%d\n", fd, ptr, nbytes);
+ }
+
+ return nread;
+}
+
+static ssize_t writez(int fd, const char *ptr)
+{
+ size_t nleft, nbytes;
+
+ nleft = nbytes = strlen(ptr);
+
+ while (nleft > 0) {
+ ssize_t nwritten = write(fd, ptr, nleft);
+ if (nwritten <= 0) {
+ return nwritten; /* return error */
+ }
+#ifdef DEBUG_CDDBP
+ fprintf(stderr, "WRITE:%s\n", ptr);
+#endif
+
+ nleft -= nwritten;
+ ptr += nwritten;
+ }
+
+ return nbytes - nleft;
+}
+
+#define SOCKBUFF 2048
+
+static void filter_nonprintable(char *c, size_t l)
+{
+ size_t i;
+ for(i = 0; i < l; ++i) {
+ if(!isprint(c[i]) && !isspace(c[i])) {
+ c[i] = '_';
+ }
+ }
+}
+
+
+int process_cddb_titles(int sock_fd, char *inbuff, int readbytes);
+int process_cddb_titles(int sock_fd, char *inbuff, int readbytes)
+{
+ int finished = 0;
+ char *p = inbuff;
+ int ind = 0;
+ unsigned char ** target = &global.creator;
+
+ do {
+ while (readbytes > 0) {
+ /* do we have a complete line in the buffer? */
+ p = (char *)memchr(inbuff+ind, '\n', readbytes);
+ if (p == NULL) break;
+
+ /* look for the terminator first */
+ if (!strncmp(".\r\n", inbuff+ind, 3)) {
+ finished = 1;
+ break;
+ }
+ /* kill carriage return */
+ if (p > inbuff+ind && *(p-1) == '\r') {
+ *(p-1) = '\0';
+ }
+ /* kill line feed */
+ *p = '\0';
+
+ /* handle escaped characters */
+
+ {
+ char *q = inbuff+ind;
+ while (*q) {
+ if (*q++ == '\\' && *q != '\0') {
+ if (*q == '\\') {
+ readbytes--;
+ p--;
+ memmove(q, q+1, readbytes - (q-inbuff-ind));
+ } else if (*q == 'n') {
+ *(q-1) = '\n';
+ readbytes--;
+ p--;
+ memmove(q, q+1, readbytes - (q-inbuff-ind));
+ } else if (*q == 't') {
+ *(q-1) = '\t';
+ readbytes--;
+ p--;
+ memmove(q, q+1, readbytes - (q-inbuff-ind));
+ }
+ }
+ }
+
+ }
+
+ /* handle multi line entries concatenate fields */
+
+/* TODO if the delimiter is split into two lines, it is not recognized. */
+ if (!strncmp(inbuff+ind, "DTITLE=", 7)) {
+ char *res = strstr(inbuff+ind+7, " / ");
+ int clen;
+ char *q;
+
+ if (res == NULL) {
+ /* no limiter found yet */
+ /* copy until the end */
+ q = p;
+ } else {
+ /* limiter found */
+ /* copy until the limiter */
+ q = res;
+ *q = '\0';
+ }
+
+ clen = q - (inbuff+ind+7);
+ if (*target == NULL) {
+ *target = malloc(clen+1);
+ if (*target != NULL)
+ **target = '\0';
+ } else {
+ *target = realloc(*target, strlen((char *)*target) + clen - 1);
+ }
+ if (*target != NULL) {
+ strcat((char *)*target, inbuff+ind+7);
+ }
+
+ /* handle part after the delimiter, if present */
+ if (res != NULL) {
+ target = (unsigned char **)&global.disctitle;
+ /* skip the delimiter */
+ q += 3;
+ clen = p - q;
+ if (*target == NULL) {
+ *target = malloc(clen+1);
+ if (*target != NULL)
+ **target = '\0';
+ }
+ if (*target != NULL) {
+ strcat((char *)*target, q);
+ }
+ }
+ } else if (!strncmp(inbuff+ind, "TTITLE", 6)) {
+ char *q = (char *)memchr(inbuff+ind, '=', readbytes);
+ unsigned tno;
+
+ if (q != NULL) {
+ *q = '\0';
+ tno = (unsigned)atoi(inbuff+ind+6);
+ tno++;
+ if (tno < 100) {
+ if (global.tracktitle[tno] == NULL) {
+ global.tracktitle[tno] = malloc( p - q + 1 );
+ if (global.tracktitle[tno] != NULL)
+ *(global.tracktitle[tno]) = '\0';
+ } else {
+ global.tracktitle[tno] = realloc(global.tracktitle[tno], strlen((char *)global.tracktitle[tno]) + p - q + 1 );
+ }
+ if (global.tracktitle[tno] != NULL) {
+ strcat((char *)global.tracktitle[tno], q+1);
+ }
+ }
+ }
+ } else if (!strncmp(inbuff+ind, "DYEAR", 5)) {
+ char *q = (char *)memchr(inbuff+ind, '=', readbytes);
+ if (q++ != NULL) {
+ sscanf(q, "%d", &global.cddb_year);
+ }
+ } else if (!strncmp(inbuff+ind, "DGENRE", 6)) {
+ char *q = (char *)memchr(inbuff+ind, '=', readbytes);
+ if (q++ != NULL) {
+ /* patch from Joe Nuzman, thanks */
+ /* might have significant whitespace */
+ strncpy(global.cddb_genre, q, sizeof(global.cddb_genre)-1);
+ /* always have a terminator */
+ global.cddb_genre[sizeof(global.cddb_genre)-1] = '\0';
+ }
+ } else if (!strncmp(inbuff+ind, "# Revision: ", 12)) {
+ char *q = inbuff+ind+11;
+ sscanf(q, "%d", &global.cddb_revision);
+ global.cddb_revision++;
+ }
+ readbytes -= (p - inbuff -ind) + 1;
+ ind = (p - inbuff) + 1;
+ }
+ if (!finished) {
+ int newbytes;
+ memmove(inbuff, inbuff+ind, readbytes);
+ newbytes = readn(sock_fd, inbuff+readbytes, SOCKBUFF-readbytes);
+ if (newbytes < 0) {
+ fprintf(stderr, "Could not read from socket.\n");
+ return 0; /* Caller checks for != 1 */
+ }
+ filter_nonprintable(inbuff+readbytes, newbytes);
+ if (newbytes <= 0)
+ break;
+ readbytes += newbytes;
+ ind = 0;
+ }
+ } while (!(finished || readbytes == 0));
+ return finished;
+}
+
+static int handle_userchoice(char *p, unsigned size);
+
+static int handle_userchoice(char *p, unsigned size)
+{
+ unsigned nr = 0;
+ unsigned user_choice;
+ int i;
+ char *q;
+ char *o;
+
+ /* count lines. */
+ q = p;
+ while ((q = (char *)memchr(q, '\n', size - (q-p))) != NULL) {
+ nr++;
+ q++;
+ }
+ if (nr > 1) nr--;
+
+ /* handle escaped characters */
+
+ {
+ char *r = p;
+ while (*r) {
+ if (*r++ == '\\' && *r != '\0') {
+ if (*r == '\\') {
+ size--;
+ memmove(r, r+1, size - (r-p));
+ } else if (*r == 'n') {
+ *(r-1) = '\n';
+ size--;
+ memmove(r, r+1, size - (r-p));
+ } else if (*r == 't') {
+ *(r-1) = '\t';
+ size--;
+ memmove(r, r+1, size - (r-p));
+ }
+ }
+ }
+ }
+
+ /* list entries. */
+ q = p;
+ fprintf(stderr, "%u entries found:\n", nr);
+ for (q = (char *)memchr(q, '\n', size - (q-p)), o = p, i = 0; i < nr; i++) {
+ *q = '\0';
+ fprintf(stderr, "%02u: %s\n", i, o);
+ o = q+1;
+ q = (char *)memchr(q, '\n', size - (q-p));
+ }
+ fprintf(stderr, "%02u: ignore\n", i);
+
+ /* get user response. */
+ do {
+ fprintf(stderr, "please choose one (0-%u): ", nr);
+ scanf("%u", &user_choice); /* FIXME: check return value */
+ } while (user_choice > nr);
+
+ if (user_choice == nr)
+ return -1;
+
+ /* skip to choice. */
+ q = p;
+ for (i = 0; i <= (int)user_choice - 1; i++) {
+ q = (char *)memchr(q, '\0', size - (q-p)) + 1;
+ }
+ return q-p;
+}
+
+/* request disc and track titles from a cddbp server.
+ *
+ * return values:
+ * 0 titles have been found exactly (success)
+ * -1 some communication error happened.
+ * 1 titles have not been found.
+ * 2 multiple fuzzy matches have been found.
+ */
+int
+request_titles(void)
+{
+ int retval = 0;
+ int sock_fd;
+ struct sockaddr_in sa;
+ struct hostent *he;
+ struct servent *se;
+ struct passwd *pw = getpwuid(getuid());
+ char hostname[HOST_NAME_MAX];
+ char inbuff[SOCKBUFF];
+ char outbuff[SOCKBUFF];
+ int i;
+ char category[64];
+ unsigned cat_offset;
+ unsigned disc_id;
+ ssize_t readbytes;
+
+ sock_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock_fd < 0) {
+ perror("cddb socket failed: ");
+ retval = -1;
+ goto errout;
+ }
+
+ /* TODO fallbacks
+ * freedb.freedb.org
+ * de.freedb.org
+ * at.freedb.org
+ */
+ if (global.cddbp_server != NULL)
+ he = gethostbyname(global.cddbp_server);
+ else
+ he = gethostbyname(CDDBHOST /*"freedb.freedb.org"*/);
+
+ if (he == NULL) {
+ perror("cddb cannot resolve freedb host: ");
+ he = malloc(sizeof(struct hostent));
+ memset(he, 0 , sizeof(struct hostent));
+ he->h_length = 4;
+ he->h_addrtype = AF_INET;
+ he->h_addr_list = malloc(4);
+ he->h_addr_list[0] = malloc(4);
+ ((struct in_addr *)(he->h_addr_list[0]))->s_addr =
+ /* kingfisher.berlios.de freedb.freedb.de */
+ htonl(UINT_C(0xc3254d85)); /*0xc2610412*/
+ he->h_name = "freedb.freedb.org";
+#if 0
+ retval = -1;
+ goto errout;
+#endif
+ }
+
+ /* save result data IMMEDIATELY!! */
+ memset(&sa, 0 , sizeof(struct sockaddr_in));
+ sa.sin_family = he->h_addrtype; /* AF_INET; */
+ sa.sin_addr.s_addr = ((struct in_addr *)((he->h_addr_list)[0]))->s_addr;
+
+ se = NULL;
+ if (global.cddbp_port == NULL)
+ se = getservbyname("cddbp-alt", "tcp");
+
+ if (se == NULL) {
+ if (global.cddbp_port == NULL) {
+ se = getservbyname("cddbp", "tcp");
+ }
+ if (se == NULL) {
+ se = malloc(sizeof(struct servent));
+ memset(se, 0 , sizeof(struct servent));
+ se->s_port = htons(CDDBPORT /*8880*/);
+#if 0
+ perror("cddb cannot resolve cddbp or cddbp-alt port:\n ");
+ retval = -1;
+ goto errout;
+#endif
+ }
+ }
+ if (global.cddbp_port != NULL) {
+ se->s_port = htons(atoi(global.cddbp_port));
+ }
+
+ sa.sin_port = se->s_port;
+
+/* TODO timeout */
+ if (0 > connect(sock_fd, (struct sockaddr *)&sa,
+ sizeof(struct sockaddr_in))) {
+ perror("cddb connect failed: ");
+ retval = -1;
+ goto errout;
+ }
+
+ /* read banner */
+ readbytes = readn(sock_fd, inbuff, sizeof(inbuff));
+ if (readbytes < 0) {
+ fprintf(stderr, "Could not read from socket\n");
+ retval = -1;
+ goto errout;
+ }
+
+ if (strncmp(inbuff, "200 ", 4) && strncmp(inbuff, "201 ", 4)) {
+ if(readbytes == sizeof(inbuff))
+ --readbytes;
+ inbuff[readbytes] = '\0';
+ filter_nonprintable(inbuff, readbytes);
+ fprintf(stderr, "bad status from freedb server during sign-on banner: %s\n", inbuff);
+ retval = -1;
+ goto errout;
+ }
+
+ /* say hello */
+ hostname[0] = '\0';
+ if (0 > gethostname(hostname, sizeof(hostname)))
+ strcpy(hostname, "unknown_host");
+ hostname[sizeof(hostname)-1] = '\0';
+ writez(sock_fd, "cddb hello ");
+ if (pw != NULL) {
+ BOOL space_err = FALSE;
+ BOOL ascii_err = FALSE;
+ /* change spaces to underscores */
+ char *q = pw->pw_name;
+ while (*q != '\0') {
+ if (*q == ' ') {
+ if (!space_err) {
+ space_err = TRUE;
+ errmsgno(EX_BAD,
+ "Warning: Space in user name '%s'.\n",
+ pw->pw_name);
+ }
+ *q = '_';
+ }
+ if (*q < ' ' || *q > '~') {
+ if (!ascii_err) {
+ ascii_err = TRUE;
+ errmsgno(EX_BAD,
+ "Warning: Nonascii character in user name '%s'.\n",
+ pw->pw_name);
+ }
+ *q = '_';
+ }
+ q++;
+ }
+ writez(sock_fd, pw->pw_name);
+ writez(sock_fd, " ");
+ } else {
+ writez(sock_fd, "unknown ");
+ }
+
+ /* change spaces to underscores */
+ {
+ char *q = hostname;
+ BOOL space_err = FALSE;
+ BOOL ascii_err = FALSE;
+
+ while (*q != '\0') {
+ if (*q == ' ') {
+ if (!space_err) {
+ space_err = TRUE;
+ errmsgno(EX_BAD,
+ "Warning: Space in hostname '%s'.\n",
+ hostname);
+ }
+ *q = '_';
+ }
+ if (*q < ' ' || *q > '~') {
+ if (!ascii_err) {
+ ascii_err = TRUE;
+ errmsgno(EX_BAD,
+ "Warning: Nonascii character in hostname '%s'.\n",
+ hostname);
+ }
+ *q = '_';
+ }
+ q++;
+ }
+ }
+
+ writez(sock_fd, hostname);
+ writez(sock_fd, " icedax " VERSION "\n");
+
+ readbytes = readn(sock_fd, inbuff, sizeof(inbuff));
+ if (readbytes < 0) {
+ fprintf(stderr, "Could not read from socket\n");
+ retval = -1;
+ goto errout;
+ }
+ if (strncmp(inbuff, "200 ", 4)) {
+ if(readbytes == sizeof(inbuff))
+ --readbytes;
+ inbuff[readbytes] = '\0';
+ filter_nonprintable(inbuff, readbytes);
+ fprintf(stderr, "bad status from freedb server during hello: %s\n", inbuff);
+ retval = -1;
+ goto signoff;
+ }
+
+ /* enable new protocol variant. Weird command here, no cddb prefix ?!?! */
+ writez(sock_fd, "proto\n");
+ readbytes = readn(sock_fd, inbuff, sizeof(inbuff));
+ if (readbytes < 0) {
+ fprintf(stderr, "Could not read from socket\n");
+ retval = -1;
+ goto errout;
+ }
+ /* check for errors and maximum supported protocol level */
+ if (strncmp(inbuff, "201 ", 4) > 0) {
+ if(readbytes == sizeof(inbuff))
+ --readbytes;
+ inbuff[readbytes] = '\0';
+ filter_nonprintable(inbuff, readbytes);
+ fprintf(stderr, "bad status from freedb server during proto command: %s\n", inbuff);
+ retval = -1;
+ goto signoff;
+ }
+
+ /* check the supported protocol level */
+ if (!memcmp(inbuff, "200 CDDB protocol level: current 1, supported ", 46)) {
+ char *q = strstr(inbuff, " supported ");
+ unsigned pr_level;
+
+ if (q != NULL) {
+ q += 11;
+ sscanf(q, "%u\n", &pr_level);
+ if (pr_level > 1) {
+ if (pr_level > 5)
+ pr_level = 5;
+ sprintf(inbuff, "proto %1u\n", pr_level);
+ writez(sock_fd, inbuff);
+ readbytes = readn(sock_fd, inbuff, sizeof(inbuff));
+ if (readbytes < 0) {
+ fprintf(stderr, "Could not read from socket\n");
+ retval = -1;
+ goto errout;
+ }
+ /* check for errors and maximum supported protocol level */
+ if (strncmp(inbuff, "201 ", 4) > 0) {
+ if(readbytes == sizeof(inbuff))
+ --readbytes;
+ inbuff[readbytes] = '\0';
+ filter_nonprintable(inbuff,
+ readbytes);
+ fprintf(stderr, "bad status from freedb server during proto x: %s\n", inbuff);
+ retval = -1;
+ goto signoff;
+ }
+ }
+ }
+ }
+
+ /* format query string */
+ /* query */
+#define CDDPB_INCLUDING_DATATRACKS
+#ifdef CDDPB_INCLUDING_DATATRACKS
+ sprintf(outbuff, "cddb query %08lx %ld ", (unsigned long)global.cddb_id, LastTrack() - FirstTrack() + 1);
+ /* first all leading datatracks */
+ {
+ int j = FirstAudioTrack();
+ if (j < 0)
+ j = LastTrack() +1;
+ for (i = FirstTrack(); i < j; i++) {
+ sprintf(outbuff + strlen(outbuff), "%ld ", 150 + Get_StartSector(i));
+ }
+ }
+#else
+ sprintf(outbuff, "cddb query %08lx %ld ", global.cddb_id, LastAudioTrack() - FirstAudioTrack() + 1);
+#endif
+ /* all audio tracks */
+ for (i = FirstAudioTrack(); i != -1 && i <= LastAudioTrack(); i++) {
+ sprintf(outbuff + strlen(outbuff), "%ld ", 150 + Get_AudioStartSector(i));
+ }
+#ifdef CDDPB_INCLUDING_DATATRACKS
+ /* now all trailing datatracks */
+ for (; i != -1 && i <= LastTrack(); i++) {
+ sprintf(outbuff + strlen(outbuff), "%ld ", 150 + Get_StartSector(i));
+ }
+ sprintf(outbuff + strlen(outbuff), "%lu\n",
+ (150 + Get_StartSector(CDROM_LEADOUT)) / 75);
+#else
+ sprintf(outbuff + strlen(outbuff), "%lu\n",
+ (150 + Get_LastSectorOnCd(FirstAudioTrack())) / 75);
+#endif
+/* strcpy(outbuff, "cddb query 9709210c 12 150 12010 33557 50765 65380 81467 93235 109115 124135 137732 152575 166742 2339\n"); */
+/* strcpy(outbuff, "cddb query 03015501 1 296 344\n"); */
+ writez(sock_fd, outbuff);
+
+ readbytes = readn(sock_fd, inbuff, sizeof(inbuff) - 1);
+ if (readbytes < 0) {
+ fprintf(stderr, "Could not read from socket\n");
+ retval = -1;
+ goto errout;
+ }
+ inbuff[readbytes] = '\0';
+ filter_nonprintable(inbuff, readbytes);
+ cat_offset = 4;
+ if (!strncmp(inbuff, "210 ", 4)
+ || !strncmp(inbuff, "211 ", 4)) {
+ /* Check if there are really multiple entries. */
+ char *p = (char *)memchr(inbuff, '\n', readbytes-1);
+
+ if (p != NULL) cat_offset = p+1 - inbuff;
+ /* first entry */
+ if (p) p = (char *)memchr(p+1, '\n', inbuff+readbytes - p);
+ /* second entry */
+ if (p) p = (char *)memchr(p+1, '\n', inbuff+readbytes - p);
+ /* . */
+ if (p) p = (char *)memchr(p+1, '\n', inbuff+readbytes - p);
+ if (p) {
+ /* multiple entries */
+ switch (global.cddbp) {
+ case 2: /* take the first entry */
+ break;
+ case 1: /* ask user */
+ if (!global.gui) {
+ int userret = handle_userchoice(inbuff+cat_offset, readbytes - cat_offset);
+ if (userret == -1) {
+ /* ignore any selection */
+ retval = -1;
+ goto signoff;
+ }
+ cat_offset += userret;
+ }
+ break;
+ default:
+ fprintf(stderr, "multiple entries found: %s\n", inbuff);
+ retval = 2;
+ goto signoff;
+ }
+ }
+
+ } else if (strncmp(inbuff, "200 ", 4)) {
+ if (!strncmp(inbuff, "202 ", 4)) {
+ fprintf(stderr, "no cddb entry found: %s\n", inbuff);
+ retval = 1;
+ } else {
+ fprintf(stderr, "bad status from freedb server during query: %s\n%s", inbuff, outbuff);
+ retval = -1;
+ }
+ goto signoff;
+ }
+ sscanf(inbuff + cat_offset, "%s %x", category, &disc_id );
+
+
+ /* read */
+ sprintf(inbuff, "cddb read %s %08x\n", category, disc_id);
+ writez(sock_fd, inbuff);
+
+ /* read status and first buffer size. */
+ readbytes = readn(sock_fd, inbuff, sizeof(inbuff));
+ if (readbytes < 0) {
+ fprintf(stderr, "Could not read from socket\n");
+ retval = -1;
+ goto errout;
+ }
+ filter_nonprintable(inbuff, readbytes);
+ if (strncmp(inbuff, "210 ", 4)) {
+ if(readbytes == sizeof(inbuff))
+ --readbytes;
+ inbuff[readbytes] = '\0';
+ fprintf(stderr, "bad status from freedb server during read: %s\n", inbuff);
+ retval = -1;
+ goto signoff;
+ }
+
+ if (1 != process_cddb_titles(sock_fd, inbuff, readbytes)) {
+ fprintf(stderr, "cddb read finished not correctly!\n");
+ }
+
+signoff:
+ /* sign-off */
+ writez(sock_fd, "quit\n");
+ readbytes = readn(sock_fd, inbuff, sizeof(inbuff));
+ if (readbytes < 0) {
+ fprintf(stderr, "Could not read from socket\n");
+ retval = -1;
+ goto errout;
+ }
+ if (strncmp(inbuff, "230 ", 4)) {
+ if(readbytes == sizeof(inbuff))
+ --readbytes;
+ inbuff[readbytes] = '\0';
+ filter_nonprintable(inbuff, readbytes);
+ fprintf(stderr, "bad status from freedb server during quit: %s\n", inbuff);
+ goto errout;
+ }
+
+errout:
+ close(sock_fd);
+ return retval;
+}
+#endif
+#endif
+
+#if defined CDINDEX_SUPPORT
+
+static int IsSingleArtist(void);
+
+/* check, if there are more than one track creators */
+static int IsSingleArtist(void)
+{
+ static struct iterator i;
+ InitIterator(&i, 1);
+
+ while (i.hasNextTrack(&i)) {
+ struct TOC *p = i.getNextTrack(&i);
+ int ii;
+
+ if (IS__DATA(p) || GETTRACK(p) == CDROM_LEADOUT) continue;
+
+ ii = GETTRACK(p);
+ if (global.creator && global.trackcreator[ii]
+ && strcmp((char *) global.creator,
+ (char *) global.trackcreator[ii]) != 0)
+ return 0;
+ }
+ return 1;
+}
+
+static const char *a2h[255-191] = {
+"&Agrave;",
+"&Aacute;",
+"&Acirc;",
+"&Atilde;",
+"&Auml;",
+"&Aring;",
+"&AElig;",
+"&Ccedil;",
+"&Egrave;",
+"&Eacute;",
+"&Ecirc;",
+"&Euml;",
+"&Igrave;",
+"&Iacute;",
+"&Icirc;",
+"&Iuml;",
+"&ETH;",
+"&Ntilde;",
+"&Ograve;",
+"&Oacute;",
+"&Ocirc;",
+"&Otilde;",
+"&Ouml;",
+"&times;",
+"&Oslash;",
+"&Ugrave;",
+"&Uacute;",
+"&Ucirc;",
+"&Uuml;",
+"&Yacute;",
+"&THORN;",
+"&szlig;",
+"&agrave;",
+"&aacute;",
+"&acirc;",
+"&atilde;",
+"&auml;",
+"&aring;",
+"&aelig;",
+"&ccedil;",
+"&egrave;",
+"&eacute;",
+"&ecirc;",
+"&euml;",
+"&igrave;",
+"&iacute;",
+"&icirc;",
+"&iuml;",
+"&eth;",
+"&ntilde;",
+"&ograve;",
+"&oacute;",
+"&ocirc;",
+"&otilde;",
+"&ouml;",
+"&divide;",
+"&oslash;",
+"&ugrave;",
+"&uacute;",
+"&ucirc;",
+"&uuml;",
+"&yacute;",
+"&thorn;",
+"&yuml;",
+};
+
+static char *ascii2html(unsigned char *inp)
+{
+ static size_t buflen = 256;
+ static char *outline = 0;
+ size_t pos = 0;
+ size_t l=0;
+
+ /* init */
+ if(!outline) {
+ outline = malloc(buflen);
+ if(outline == 0) {
+ fprintf(stderr, "error: memory exhausted\n");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ outline[pos] = '\0';
+
+ while (*inp != '\0') {
+
+ /* Pick the sequence to insert */
+ const char *insert;
+ char b[2];
+ const int c = (unsigned char)*inp;
+ switch(c) {
+ case '"': insert = "&quot;"; break;
+ case '&': insert = "&amp;"; break;
+ case '<': insert = "&lt;"; break;
+ case '>': insert = "&gt;"; break;
+ case 160: insert = "&nbsp;"; break;
+ default: {
+ if(c < 192) {
+ b[0] = c;
+ b[1] = '\0';
+ insert = b;
+ } else {
+ insert = a2h[c - 192];
+ }
+ }
+ };
+
+ /* Resize buffer */
+ l = strlen(insert);
+ while(pos + l + 1 >= buflen) {
+ outline = realloc(outline, buflen *= 2);
+ if(outline == 0) {
+ fprintf(stderr, "error: memory exhausted\n");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ /* Copy in */
+ strcpy(&outline[pos], insert);
+ pos += l;
+
+ ++inp;
+ }
+
+ return outline;
+}
+
+static void emit_cdindex_form(char *fname_baseval)
+{
+ FILE *cdindex_form;
+ char fname[200];
+ char *pp;
+
+ if (fname_baseval == NULL || fname_baseval[0] == 0)
+ return;
+
+ strncpy(fname, fname_baseval, sizeof(fname) -1);
+ fname[sizeof(fname) -1] = 0;
+ pp = strrchr(fname, '.');
+ if (pp == NULL) {
+ pp = fname + strlen(fname);
+ }
+ strncpy(pp, ".cdindex", sizeof(fname) - 1 - (pp - fname));
+
+ cdindex_form = fopen(fname, "w");
+ if (cdindex_form == NULL) return;
+
+#define CDINDEX_URL "http://www.musicbrainz.org/dtd/CDInfo.dtd"
+
+ /* format XML page according to cdindex DTD (see www.musicbrainz.org) */
+ fprintf( cdindex_form, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<!DOCTYPE CDInfo SYSTEM \"%s\">\n\n<CDInfo>\n",
+ CDINDEX_URL);
+
+ fprintf( cdindex_form, " <Title>%s</Title>\n",
+ global.disctitle ? ascii2html(global.disctitle) : "");
+ /*
+ * In case of mixed mode and Extra-CD, nonaudio tracks are included!
+ */
+ fprintf( cdindex_form, " <NumTracks>%d</NumTracks>\n\n", cdtracks);
+ fprintf( cdindex_form, " <IdInfo>\n <DiskId>\n <Id>%s</Id>\n </DiskId>\n", global.cdindex_id);
+
+ fprintf( cdindex_form, " </IdInfo>\n\n");
+
+ if (IsSingleArtist()) {
+ static struct iterator i;
+ InitIterator(&i, 1);
+
+ fprintf( cdindex_form, " <SingleArtistCD>\n <Artist>%s</Artist>\n",
+ global.creator ? ascii2html(global.creator) : "");
+
+ while (i.hasNextTrack(&i)) {
+ struct TOC *p = i.getNextTrack(&i);
+ int ii = GETTRACK(p);
+
+ if (ii == CDROM_LEADOUT) break;
+ if (IS__AUDIO(p)) {
+ fprintf( cdindex_form,
+ " <Track Num=\"%d\">\n <Name>%s</Name>\n </Track>\n",
+ ii, global.tracktitle[ii] ? ascii2html(global.tracktitle[ii]) : "");
+ } else {
+ fprintf( cdindex_form,
+ " <Track Num=\"%d\">\n <Name>data track</Name>\n </Track>\n",
+ ii );
+ }
+ }
+ fprintf( cdindex_form, " </SingleArtistCD>\n");
+ } else {
+ static struct iterator i;
+ InitIterator(&i, 1);
+
+ fprintf( cdindex_form, " <MultipleArtistCD>\n");
+
+ while (i.hasNextTrack(&i)) {
+ struct TOC *p = i.getNextTrack(&i);
+ int ii = GETTRACK(p);
+
+ if (ii == CDROM_LEADOUT) break;
+ if (IS__AUDIO(p)) {
+ fprintf( cdindex_form, " <Artist>%s</Artist>\n",
+ global.trackcreator[ii] ? ascii2html(global.trackcreator[ii]) : "");
+ fprintf( cdindex_form, " <Name>%s</Name>\n </Track>\n",
+ global.tracktitle[ii] ? ascii2html(global.tracktitle[ii]) : "");
+ } else {
+ fprintf( cdindex_form,
+ " <Artist>data track</Artist>\n <Name>data track</Name>\n </Track>\n");
+ }
+ }
+ fprintf( cdindex_form, " </MultipleArtistCD>\n");
+ }
+ fprintf( cdindex_form, "</CDInfo>\n");
+
+ fclose( cdindex_form );
+}
+#endif
+
+static void dump_cdtext_info(void)
+{
+#ifdef CD_TEXT
+ /* interpret the contents of CD Text information based on an early draft
+ of SCSI-3 mmc version 2 from jan 2, 1998
+ CD Text information consists of a text group containing up to
+ 8 language blocks containing up to
+ 255 Pack data chunks of
+ 18 bytes each.
+ So we have at most 36720 bytes to cope with.
+ */
+ {
+ short int datalength;
+ unsigned char *p = bufferTOC;
+ unsigned char lastline[255*12];
+ int lastitem = -1;
+ int itemcount = 0;
+ int inlinecount = 0;
+ int outlinecount = 0;
+
+ lastline[0] = '\0';
+ datalength = ((p[0] << 8) + p[1]) - 2;
+ datalength = min(datalength, 2048-4);
+ p += 4;
+ for (;datalength > 0;
+ datalength -= sizeof (cdtextpackdata),
+ p += sizeof (cdtextpackdata)) {
+ unsigned char *zeroposition;
+
+ /* handle one packet of CD Text Information Descriptor Pack Data */
+ /* this is raw R-W subchannel data converted to 8 bit values. */
+ cdtextpackdata *c = (cdtextpackdata *)p;
+ int dbcc;
+ int crc_error;
+ unsigned tracknr;
+
+#ifdef DEBUG_CDTEXT
+ fprintf(stderr, "datalength =%d\n", datalength);
+#endif
+ crc_error = !cdtext_crc_ok(c);
+
+ if (lastitem != c->headerfield[0]) {
+ itemcount = 0;
+ lastitem = c->headerfield[0];
+ }
+
+ tracknr = c->headerfield[1] & 0x7f;
+ dbcc = ((unsigned)(c->headerfield[3] & 0x80)) >> 7; /* double byte character code */
+
+#if defined DEBUG_CDTEXT
+ {
+ int extension_flag;
+ int sequence_number;
+ int block_number;
+ int character_position;
+
+ extension_flag = ((unsigned)(c->headerfield[1] & 0x80)) >> 7;
+ sequence_number = c->headerfield[2];
+ block_number = ((unsigned)(c->headerfield[3] & 0x30)) >> 4; /* language */
+ character_position = c->headerfield[3] & 0x0f;
+
+ fprintf(stderr, "CDText: ext_fl=%d, trnr=%u, seq_nr=%d, dbcc=%d, block_nr=%d, char_pos=%d\n",
+ extension_flag, tracknr, sequence_number, dbcc, block_number, character_position);
+ }
+#endif
+
+ /* print ASCII information */
+ memcpy(lastline+inlinecount, c->textdatafield, 12);
+ inlinecount += 12;
+ zeroposition = (unsigned char *)memchr(lastline+outlinecount, '\0', inlinecount-outlinecount);
+ while (zeroposition != NULL) {
+ process_header(c, tracknr, dbcc, lastline+outlinecount);
+ outlinecount += zeroposition - (lastline+outlinecount) + 1;
+
+#if defined DEBUG_CDTEXT
+ fprintf(stderr, "\tin=%d, out=%d, items=%d, trcknum=%u\n", inlinecount, outlinecount, itemcount, tracknr);
+{ int q; for (q= outlinecount; q < inlinecount; q++) fprintf (stderr, "%c", lastline[q] ? lastline[q] : 'ß'); fputs("\n", stderr); }
+#else
+ if (DETAILED) {
+ if (crc_error) fputs(" ! uncorr. CRC-Error", stderr);
+ fputs("\n", stderr);
+ }
+#endif
+
+ itemcount++;
+ if (itemcount > (int)cdtracks
+ || (c->headerfield[0] == 0x8f
+ || (c->headerfield[0] <= 0x8d && c->headerfield[0] >= 0x86))) {
+ outlinecount = inlinecount;
+ break;
+ }
+ tracknr++;
+ zeroposition = (unsigned char *)memchr(lastline+outlinecount, '\0', inlinecount-outlinecount);
+ }
+ }
+ }
+#endif
+}
+
+
+
+static void dump_extra_info(unsigned int from)
+{
+#ifdef CD_EXTRA
+ unsigned char *p;
+ unsigned pos, length;
+
+ if (from == 0) return;
+
+ p = Extra_buffer + 48;
+ while (*p != '\0') {
+ pos = GET_BE_UINT_FROM_CHARP(p+2);
+ length = GET_BE_UINT_FROM_CHARP(p+6);
+ if (pos == (unsigned)-1) {
+ pos = from+1;
+ } else {
+ pos += session_start;
+ }
+
+#ifdef DEBUG_XTRA
+ if (global.gui == 0 && global.verbose != 0) {
+ fprintf(stderr, "Language: %c%c (as defined by ISO 639)", *p, *(p+1));
+ fprintf(stderr, " at sector %u, len=%u (sessionstart=%u)", pos, length, session_start);
+ fputs("\n", stderr);
+ }
+#endif
+ /* dump this entry */
+ Read_Subinfo(pos, length);
+ p += 10;
+
+ if (p + 9 > (Extra_buffer + CD_FRAMESIZE))
+ break;
+ }
+#endif
+}
+
+static char *quote(unsigned char *string);
+
+static char *quote(unsigned char *string)
+{
+ static char result[200];
+ unsigned char *p = (unsigned char *)result;
+
+ while (*string) {
+ if (*string == '\'' || *string == '\\') {
+ *p++ = '\\';
+ }
+ *p++ = *string++;
+ }
+ *p = '\0';
+
+ return result;
+}
+
+
+
+static void DisplayToc_with_gui(unsigned long dw);
+
+static void DisplayToc_with_gui(unsigned long dw)
+{
+ unsigned mins;
+ unsigned secnds;
+ unsigned frames;
+ int count_audio_trks;
+ static struct iterator i;
+ if (i.reset == NULL) InitIterator(&i, 1);
+ else i.reset(&i);
+
+ mins = dw / ( 60*75 );
+ secnds = ( dw % ( 60*75 ) ) / 75;
+ frames = ( dw % 75 );
+
+ /* summary */
+ count_audio_trks = 0;
+
+ if ((global.verbose & SHOW_STARTPOSITIONS) != 0) {
+ if (global.illleadout_cd != 0 && have_CD_extra == 0) {
+ fprintf( stderr, "Tracks:%u > %u:%02u.%02u\n", cdtracks, mins, secnds, frames );
+ } else {
+ fprintf( stderr, "Tracks:%u %u:%02u.%02u\n", cdtracks, mins, secnds, frames );
+ }
+ }
+
+ if (global.quiet == 0) {
+ fprintf( stderr, "CDINDEX discid: %s\n", global.cdindex_id);
+ fprintf( stderr, "CDDB discid: 0x%08lx", (unsigned long) global.cddb_id);
+
+ if (have_CDDB != 0) {
+ fprintf(stderr, " CDDBP titles: resolved\n");
+ } else {
+ fprintf(stderr, "\n");
+ }
+ if (have_CD_text != 0) {
+ fprintf(stderr, "CD-Text: detected\n");
+ dump_cdtext_info();
+ } else {
+ fprintf(stderr, "CD-Text: not detected\n");
+ }
+ if (have_CD_extra != 0) {
+ fprintf(stderr, "CD-Extra: detected\n");
+ dump_extra_info(have_CD_extra);
+ } else {
+ fprintf(stderr, "CD-Extra: not detected\n");
+ }
+
+ fprintf( stderr,
+ "Album title: '%s'", (void *)global.disctitle != NULL
+ ? quote(global.disctitle) : "");
+
+ fprintf( stderr, " from '%s'\n", (void *)global.creator != NULL
+ ? quote(global.creator) : "");
+ }
+ count_audio_trks = 0;
+
+
+ if ((global.verbose & (SHOW_TOC | SHOW_STARTPOSITIONS | SHOW_SUMMARY | SHOW_TITLES)) != 0
+ && i.hasNextTrack(&i)) {
+ TOC *o = i.getNextTrack(&i);
+ while (i.hasNextTrack(&i)) {
+ TOC *p = i.getNextTrack(&i);
+ int from;
+ from = GETTRACK(o);
+
+ fprintf(stderr, "T%02d:", from);
+
+ if (IS__DATA(o)) {
+ /*
+ * Special case of cd extra
+ */
+ unsigned int real_start = have_CD_extra
+ ? have_CD_extra : GETSTART(o);
+
+
+ dw = (unsigned long) (GETSTART(p) - real_start);
+
+ mins = dw / ( 60*75 );
+ secnds = ( dw % ( 60*75 )) / 75;
+ frames = ( dw % 75 );
+
+ if ( global.verbose & SHOW_STARTPOSITIONS )
+ fprintf(stderr,
+ " %7u",
+ real_start
+ );
+
+ if ( global.verbose & SHOW_TOC )
+ fprintf(stderr,
+ " %2u:%02u.%02u",
+ mins, secnds, frames
+ );
+
+ if ( global.verbose & SHOW_SUMMARY )
+ fprintf(stderr,
+ " data %s %s N/A",
+
+ /* how recorded */
+ IS__INCREMENTAL(o)
+ ? "incremental" : "uninterrupted",
+
+ /* copy-permission */
+ IS__COPYRIGHTED(o)
+ ? "copydenied" : "copyallowed"
+ );
+ fputs("\n", stderr);
+ } else {
+ dw = (unsigned long) (GETSTART(p) - GETSTART(o));
+ mins = dw / ( 60*75 );
+ secnds = ( dw % ( 60*75 )) / 75;
+ frames = ( dw % 75 );
+
+ if ( global.verbose & SHOW_STARTPOSITIONS )
+ fprintf(stderr,
+ " %7u",
+ GETSTART(o)
+ );
+
+ if ( global.verbose & SHOW_TOC )
+ fprintf(stderr,
+ " %2u:%02u.%02u",
+ mins, secnds, frames
+ );
+
+ if ( global.verbose & SHOW_SUMMARY )
+ fprintf(stderr,
+ " audio %s %s %s",
+
+ /* how recorded */
+ IS__PREEMPHASIZED(o)
+ ? "pre-emphasized" : "linear",
+
+ /* copy-permission */
+ IS__COPYRIGHTED(o)
+ ? "copydenied" : "copyallowed",
+
+ /* channels */
+ IS__QUADRO(o)
+ ? "quadro" : "stereo");
+
+ /* Title */
+ if ( global.verbose & SHOW_TITLES ) {
+ fprintf(stderr,
+ " title '%s' from ",
+
+ (void *) global.tracktitle[GETTRACK(o)] != NULL
+ ? quote(global.tracktitle[GETTRACK(o)]) : ""
+ );
+
+ fprintf(stderr,
+ "'%s'",
+
+ (void *) global.trackcreator[GETTRACK(o)] != NULL
+ ? quote(global.trackcreator[GETTRACK(o)]) : ""
+ );
+ }
+ fputs("\n", stderr);
+ count_audio_trks++;
+ }
+ o = p;
+ } /* while */
+ if ( global.verbose & SHOW_STARTPOSITIONS )
+ if (GETTRACK(o) == CDROM_LEADOUT) {
+ fprintf(stderr, "Leadout: %7u\n", GETSTART(o));
+ }
+ } /* if */
+}
+
+static void DisplayToc_no_gui(unsigned long dw);
+
+static void DisplayToc_no_gui(unsigned long dw)
+{
+ unsigned mins;
+ unsigned secnds;
+ unsigned frames;
+ int count_audio_trks;
+ unsigned ii = 0;
+ static struct iterator i;
+ if (i.reset == NULL) InitIterator(&i, 1);
+ else i.reset(&i);
+
+ mins = dw / ( 60*75 );
+ secnds = ( dw % ( 60*75 ) ) / 75;
+ frames = ( dw % 75 );
+
+ /* summary */
+ count_audio_trks = 0;
+
+ if (i.hasNextTrack(&i)) {
+ TOC *o = i.getNextTrack(&i);
+ while (i.hasNextTrack(&i)) {
+ TOC *p = i.getNextTrack(&i);
+ int from;
+ from = GETTRACK(o);
+
+
+ while ( p != NULL && GETTRACK(p) != CDROM_LEADOUT
+ && GETFLAGS(o) == GETFLAGS(p) ) {
+ o = p;
+ p = i.getNextTrack(&i);
+ }
+ if ((global.verbose & SHOW_SUMMARY) == 0) continue;
+
+ if (IS__DATA(o)) {
+ fputs( " DATAtrack recorded copy-permitted tracktype\n" , stderr);
+ fprintf(stderr,
+ " %2d-%2d %13.13s %14.14s data\n",
+ from,
+ GETTRACK(o),
+ /* how recorded */
+ IS__INCREMENTAL(o)
+ ? "incremental" : "uninterrupted",
+
+ /* copy-perm */
+ IS__COPYRIGHTED(o) ? "no" : "yes"
+ );
+ } else {
+ fputs( "AUDIOtrack pre-emphasis copy-permitted tracktype channels\n" , stderr);
+ fprintf(stderr,
+ " %2d-%2d %12.12s %14.14s audio %1c\n",
+ from,
+ GETTRACK(o),
+ IS__PREEMPHASIZED(o)
+ ? "yes" : "no",
+ IS__COPYRIGHTED(o) ? "no" : "yes",
+ IS__QUADRO(o) ? '4' : '2'
+ );
+ count_audio_trks++;
+ }
+ o = p;
+ }
+ }
+ if ((global.verbose & SHOW_STARTPOSITIONS) != 0) {
+ if (global.illleadout_cd != 0 && have_multisession == 0) {
+
+ fprintf ( stderr,
+ "Table of Contents: total tracks:%u, (total time more than %u:%02u.%02u)\n",
+ cdtracks, mins, secnds, frames );
+ } else {
+ fprintf ( stderr,
+ "Table of Contents: total tracks:%u, (total time %u:%02u.%02u)\n",
+ cdtracks, mins, secnds, frames );
+ }
+ }
+
+ i.reset(&i);
+ if ((global.verbose & SHOW_TOC) != 0 &&
+ i.hasNextTrack(&i)) {
+ TOC *o = i.getNextTrack(&i);
+
+ for (; i.hasNextTrack(&i);) {
+ TOC *p = i.getNextTrack(&i);
+
+ if ( GETTRACK(o) <= MAXTRK ) {
+ unsigned char brace1, brace2;
+ unsigned trackbeg;
+ trackbeg = have_multisession && IS__DATA(o) ? have_multisession : GETSTART(o);
+
+ dw = (unsigned long) (GETSTART(p) - trackbeg);
+ mins = dw / ( 60*75 );
+ secnds = ( dw % ( 60*75 )) / 75;
+ frames = ( dw % 75 );
+
+ if ( IS__DATA(o) ) {
+ /* data track display */
+ brace1 = '[';
+ brace2 = ']';
+ } else if (have_multisession
+ && GETTRACK(o) == LastAudioTrack()) {
+ /* corrected length of
+ * last audio track in cd extra
+ */
+ brace1 = '|';
+ brace2 = '|';
+ } else {
+ /* audio track display */
+ brace1 = '(';
+ brace2 = ')';
+ }
+ fprintf ( stderr,
+ " %2u.%c%2u:%02u.%02u%c",
+ GETTRACK(o),
+ brace1,
+ mins, secnds, frames,
+ brace2
+ );
+ ii++;
+
+ if ( ii % 5 == 0 )
+ fputs( ",\n", stderr );
+ else if (ii != cdtracks)
+ fputc ( ',', stderr );
+ }
+ o = p;
+ } /* for */
+ if ( (ii % 5) != 0 )
+ fputs( "\n", stderr );
+
+ } /* if */
+
+ if ((global.verbose & SHOW_STARTPOSITIONS) != 0) {
+ fputs ("\nTable of Contents: starting sectors\n", stderr);
+
+ ii = 0;
+ i.reset(&i);
+ if (i.hasNextTrack(&i)) {
+ TOC *o = i.getNextTrack(&i);
+ for ( ; i.hasNextTrack(&i);) {
+ TOC *p = i.getNextTrack(&i);
+ fprintf ( stderr,
+ " %2u.(%8u)",
+ GETTRACK(o),
+ have_multisession
+ && GETTRACK(o) == FirstDataTrack()
+ ? have_multisession
+ : GETSTART(o)
+#ifdef DEBUG_CDDB
+ +150
+#endif
+ );
+
+ ii++;
+ if ( (ii) % 5 == 0 )
+ fputs( ",\n", stderr );
+ else
+ fputc ( ',', stderr );
+ o = p;
+ }
+ fprintf ( stderr, " lead-out(%8u)", GETSTART(o));
+ fputs ("\n", stderr);
+ }
+ }
+ if (global.quiet == 0) {
+ fprintf(stderr, "CDINDEX discid: %s\n", global.cdindex_id);
+ fprintf( stderr, "CDDB discid: 0x%08lx", (unsigned long) global.cddb_id);
+
+ if (have_CDDB != 0) {
+ fprintf(stderr, " CDDBP titles: resolved\n");
+ } else {
+ fprintf(stderr, "\n");
+ }
+ if (have_CD_text != 0) {
+ fprintf(stderr, "CD-Text: detected\n");
+ } else {
+ fprintf(stderr, "CD-Text: not detected\n");
+ }
+ if (have_CD_extra != 0) {
+ fprintf(stderr, "CD-Extra: detected\n");
+ } else {
+ fprintf(stderr, "CD-Extra: not detected\n");
+ }
+ }
+ if ((global.verbose & SHOW_TITLES) != 0) {
+ int maxlen = 0;
+
+ if ( global.disctitle != NULL ) {
+ fprintf( stderr, "Album title: '%s'", global.disctitle);
+ if ( global.creator != NULL ) {
+ fprintf( stderr, "\t[from %s]", global.creator);
+ }
+ fputs("\n", stderr);
+ }
+
+ i.reset(&i);
+ for ( ; i.hasNextTrack(&i);) {
+ TOC *p = i.getNextTrack(&i);
+ int jj = GETTRACK(p);
+
+ if ( global.tracktitle[jj] != NULL ) {
+ int len = strlen((char *)global.tracktitle[jj]);
+ maxlen = max(maxlen, len);
+ }
+ }
+ maxlen = (maxlen + 12 + 8 + 7)/8;
+
+ i.reset(&i);
+ for ( ; i.hasNextTrack(&i); ) {
+ TOC *p = i.getNextTrack(&i);
+ int jj;
+
+ if (IS__DATA(p))
+ continue;
+
+ jj = GETTRACK(p);
+
+ if (jj == CDROM_LEADOUT)
+ break;
+
+ if ( maxlen != 3 ) {
+ if ( global.tracktitle[jj] != NULL ) {
+ fprintf( stderr, "Track %2u: '%s'", jj, global.tracktitle[jj]);
+ } else {
+ fprintf( stderr, "Track %2u: '%s'", jj, "");
+ }
+ if ( global.trackcreator[jj] != NULL
+ && global.trackcreator[jj][0] != '\0'
+#if 1
+ && (global.creator == NULL
+ || 0 != strcmp((char *)global.creator,(char *)global.trackcreator[jj]))
+#endif
+ ) {
+ int j;
+ char *o = global.tracktitle[jj] != NULL
+ ? (char *)global.tracktitle[jj]
+ : "";
+ for ( j = 0;
+ j < (maxlen - ((int)strlen(o) + 12)/8);
+ j++)
+ fprintf(stderr, "\t");
+ fprintf( stderr, "[from %s]", global.trackcreator[jj]);
+ }
+ fputs("\n", stderr);
+ }
+ }
+ }
+}
+
+void DisplayToc(void)
+{
+ unsigned long dw;
+
+ /* special handling of pseudo-red-book-audio cds */
+ if (cdtracks > 1
+ && Get_StartSector(CDROM_LEADOUT) < Get_StartSector(cdtracks)) {
+ global.illleadout_cd = 1;
+ can_read_illleadout();
+ }
+
+
+ /* get total time */
+ if (global.illleadout_cd == 0)
+ dw = (unsigned long) Get_StartSector(CDROM_LEADOUT) - Get_StartSector(1);
+ else
+ dw = (unsigned long) Get_StartSector(cdtracks ) - Get_StartSector(1);
+
+ if ( global.gui == 0 ) {
+ /* table formatting when in cmdline mode */
+ DisplayToc_no_gui( dw );
+ } else if (global.gui == 1) {
+ /* line formatting when in gui mode */
+ DisplayToc_with_gui( dw );
+ }
+
+ if (global.illleadout_cd != 0) {
+ if (global.quiet == 0) {
+ fprintf(stderr, "CD with illegal leadout position detected!\n");
+ }
+
+ if (global.reads_illleadout == 0) {
+ /* limit accessible tracks
+ * to lowered leadout position
+ */
+ restrict_tracks_illleadout();
+
+ if (global.quiet == 0) {
+ fprintf(stderr,
+ "The cdrom drive firmware does not permit access beyond the leadout position!\n");
+ }
+ if (global.verbose & (SHOW_ISRC | SHOW_INDICES)) {
+ global.verbose &= ~(SHOW_ISRC | SHOW_INDICES);
+ fprintf(stderr, "Switching index scan and ISRC scan off!\n");
+ }
+
+ if (global.quiet == 0) {
+ fprintf(stderr,
+ "Audio extraction will be limited to track %ld with maximal %ld sectors...\n",
+ LastTrack(),
+ Get_EndSector(LastTrack())+1
+ );
+ }
+ } else {
+ /* The cdrom drive can read beyond the
+ * indicated leadout. We patch a new leadout
+ * position to the maximum:
+ * 99 minutes, 59 seconds, 74 frames
+ */
+ patch_real_end(150 + (99*60+59)*75 + 74);
+ if (global.quiet == 0) {
+ fprintf(stderr,
+ "Restrictions apply, since the size of the last track is unknown!\n");
+ }
+ }
+ }
+}
+
+static void Read_MCN_toshiba(subq_chnl **sub_ch);
+
+static void Read_MCN_toshiba(subq_chnl **sub_ch)
+{
+ if (Toshiba3401() != 0 && global.quiet == 0
+ && ((*sub_ch) != 0
+ || (((subq_catalog *)(*sub_ch)->data)->mc_valid & 0x80))) {
+ /* no valid MCN yet. do more searching */
+ long h = Get_AudioStartSector(1);
+
+ while (h <= Get_AudioStartSector(1) + 100) {
+ if (Toshiba3401())
+ ReadCdRom(get_scsi_p(), RB_BASE->data, h, global.nsectors);
+ (*sub_ch) = ReadSubQ(get_scsi_p(), GET_CATALOGNUMBER,0);
+ if ((*sub_ch) != NULL) {
+ subq_catalog *subq_cat;
+
+ subq_cat = (subq_catalog *) (*sub_ch)->data;
+ if ((subq_cat->mc_valid & 0x80) != 0) {
+ break;
+ }
+ }
+ h += global.nsectors;
+ }
+ }
+}
+
+static void Get_Set_MCN(void);
+
+static void Get_Set_MCN(void)
+{
+ subq_chnl *sub_ch;
+ subq_catalog *subq_cat = NULL;
+ fprintf(stderr, "scanning for MCN...");
+
+ sub_ch = ReadSubQ(get_scsi_p(), GET_CATALOGNUMBER,0);
+
+#define EXPLICIT_READ_MCN_ISRC 1
+#if EXPLICIT_READ_MCN_ISRC == 1 /* TOSHIBA HACK */
+ Read_MCN_toshiba( &sub_ch );
+#endif
+
+ if (sub_ch != NULL)
+ subq_cat = (subq_catalog *)sub_ch->data;
+
+ if (sub_ch != NULL
+ && (subq_cat->mc_valid & 0x80) != 0
+ && global.quiet == 0) {
+
+ /* unified format guesser:
+ * format MCN all digits in bcd
+ * 1 13
+ * A: ab cd ef gh ij kl m0 0 0 0 0 0 0 Plextor 6x Rel. 1.02
+ * B: 0a 0b 0c 0d 0e 0f 0g 0h 0i 0j 0k 0l 0m Toshiba 3401
+ * C: AS AS AS AS AS AS AS AS AS AS AS AS AS ASCII SCSI-2 Plextor 4.5x and 6x Rel. 1.06
+ */
+ unsigned char *cp = subq_cat->media_catalog_number;
+ if (!(cp[8] | cp[9] | cp[10] | cp[11] | cp[12])
+ && ((cp[0] & 0xf0) | (cp[1] & 0xf0)
+ | (cp[2] & 0xf0) | (cp[3] & 0xf0)
+ | (cp[4] & 0xf0) | (cp[5] & 0xf0)
+ | (cp[6] & 0xf0))) {
+ /* reformat A: to B: */
+ cp[12] = cp[6] >> 4; cp[11] = cp[5] & 0xf;
+ cp[10] = cp[5] >> 4; cp[ 9] = cp[4] & 0xf;
+ cp[ 8] = cp[4] >> 4; cp[ 7] = cp[3] & 0xf;
+ cp[ 6] = cp[3] >> 4; cp[ 5] = cp[2] & 0xf;
+ cp[ 4] = cp[2] >> 4; cp[ 3] = cp[1] & 0xf;
+ cp[ 2] = cp[1] >> 4; cp[ 1] = cp[0] & 0xf;
+ cp[ 0] = cp[0] >> 4;
+ }
+
+ if (!isdigit(cp[0])
+ && (memcmp(subq_cat->media_catalog_number,
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0", 13) != 0)) {
+ sprintf((char *)
+ subq_cat->media_catalog_number,
+ "%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X%1.1X",
+ subq_cat->media_catalog_number [0],
+ subq_cat->media_catalog_number [1],
+ subq_cat->media_catalog_number [2],
+ subq_cat->media_catalog_number [3],
+ subq_cat->media_catalog_number [4],
+ subq_cat->media_catalog_number [5],
+ subq_cat->media_catalog_number [6],
+ subq_cat->media_catalog_number [7],
+ subq_cat->media_catalog_number [8],
+ subq_cat->media_catalog_number [9],
+ subq_cat->media_catalog_number [10],
+ subq_cat->media_catalog_number [11],
+ subq_cat->media_catalog_number [12]
+ );
+ }
+
+ if (memcmp(subq_cat->media_catalog_number,"0000000000000",13)
+ != 0) {
+ Set_MCN(subq_cat->media_catalog_number);
+ }
+ }
+}
+
+
+static void Read_ISRC_toshiba(subq_chnl **sub_ch, unsigned tr);
+
+static void Read_ISRC_toshiba(subq_chnl **sub_ch, unsigned tr)
+{
+ if (Toshiba3401() != 0) {
+ int j;
+ j = (Get_AudioStartSector(tr)/100 + 1) * 100;
+ do {
+ ReadCdRom(get_scsi_p(), RB_BASE->data, j, global.nsectors);
+ *sub_ch = ReadSubQ(get_scsi_p(), GET_TRACK_ISRC, Get_Tracknumber(tr));
+ if (*sub_ch != NULL) {
+ subq_track_isrc * subq_tr;
+
+ subq_tr = (subq_track_isrc *) (*sub_ch)->data;
+ if (subq_tr != NULL && (subq_tr->tc_valid & 0x80) != 0)
+ break;
+ }
+ j += global.nsectors;
+ } while (j < (Get_AudioStartSector(tr)/100 + 1) * 100 + 100);
+ }
+}
+
+
+static void Get_Set_ISRC(unsigned tr);
+
+static void Get_Set_ISRC(unsigned tr)
+{
+ subq_chnl *sub_ch;
+ subq_track_isrc * subq_tr;
+
+ fprintf(stderr, "\rscanning for ISRCs: %d ...", tr);
+
+ subq_tr = NULL;
+ sub_ch = ReadSubQ(get_scsi_p(), GET_TRACK_ISRC, tr);
+
+#if EXPLICIT_READ_MCN_ISRC == 1 /* TOSHIBA HACK */
+ Read_ISRC_toshiba( &sub_ch, tr );
+#endif
+
+ if (sub_ch != NULL)
+ subq_tr = (subq_track_isrc *)sub_ch->data;
+
+ if (sub_ch != NULL && (subq_tr->tc_valid & 0x80)
+ && global.quiet == 0) {
+ unsigned char p_start[16];
+ unsigned char *p = p_start;
+ unsigned char *cp = subq_tr->track_isrc;
+
+ /* unified format guesser:
+ * there are 60 bits and 15 bytes available.
+ * 5 * 6bit-items + two zero fill bits + 7 * 4bit-items
+ *
+ * A: ab cd ef gh ij kl mn o0 0 0 0 0 0 0 0 Plextor 6x Rel. 1.02
+ * B: 0a 0b 0c 0d 0e 0f 0g 0h 0i 0j 0k 0l 0m 0n 0o Toshiba 3401
+ * C: AS AS AS AS AS AS AS AS AS AS AS AS AS AS AS ASCII SCSI-2
+ * eg 'G''B''-''A''0''7''-''6''8''-''0''0''2''7''0' makes most sense
+ * D: 'G''B''A''0''7''6''8''0''0''2''7''0'0 0 0 Plextor 6x Rel. 1.06 and 4.5x R. 1.01 and 1.04
+ */
+
+ /* Check for format A: */
+ if (!(cp[8] | cp[9] | cp[10] | cp[11] | cp[12] | cp[13] | cp[14]) &&
+ ((cp[0] & 0xf0) | (cp[1] & 0xf0) | (cp[2] & 0xf0) |
+ (cp[3] & 0xf0) | (cp[4] & 0xf0) | (cp[5] & 0xf0) |
+ (cp[6] & 0xf0) | (cp[7] & 0xf0))) {
+#if DEBUG_ISRC
+ fprintf(stderr, "a!\t");
+#endif
+ /* reformat A: to B: */
+ cp[14] = cp[7] >> 4; cp[13] = cp[6] & 0xf;
+ cp[12] = cp[6] >> 4; cp[11] = cp[5] & 0xf;
+ cp[10] = cp[5] >> 4; cp[ 9] = cp[4] & 0xf;
+ cp[ 8] = cp[4] >> 4; cp[ 7] = cp[3] & 0xf;
+ cp[ 6] = cp[3] >> 4; cp[ 5] = cp[2] & 0xf;
+ cp[ 4] = cp[2] >> 4; cp[ 3] = cp[1] & 0xf;
+ cp[ 2] = cp[1] >> 4; cp[ 1] = cp[0] & 0xf;
+ cp[ 0] = cp[0] >> 4;
+#if DEBUG_ISRC
+ fprintf(stderr, "a->b: %15.15s\n", cp);
+#endif
+ }
+
+ /* Check for format B:
+ * If not yet in ASCII format, do the conversion
+ */
+ if (cp[0] < '0' && cp[1] < '0') {
+ /* coding table for International Standard Recording Code */
+ static char bin2ISRC[] = {
+ '0','1','2','3','4','5','6','7','8','9', /* 10 */
+ ':',';','<','=','>','?','@', /* 17 */
+ 'A','B','C','D','E','F','G','H','I','J','K', /* 28 */
+ 'L','M','N','O','P','Q','R','S','T','U','V', /* 39 */
+ 'W','X','Y','Z', /* 43 */
+#if 1
+ '[','\\',']','^','_','`', /* 49 */
+ 'a','b','c','d','e','f','g','h','i','j','k', /* 60 */
+ 'l','m','n','o' /* 64 */
+#endif
+ };
+
+ /* build 6-bit vector of coded values */
+ unsigned ind;
+ int bits;
+
+#if DEBUG_ISRC
+ fprintf(stderr, "b!\n");
+#endif
+ ind = (cp[0] << 26) +
+ (cp[1] << 22) +
+ (cp[2] << 18) +
+ (cp[3] << 14) +
+ (cp[4] << 10) +
+ (cp[5] << 6) +
+ (cp[6] << 2) +
+ (cp[7] >> 2);
+
+ if ((cp[7] & 3) == 3) {
+ if (global.verbose) {
+ fprintf(stderr,
+ "Recorder-ID encountered: ");
+ for (bits = 0; bits < 30; bits +=6) {
+ unsigned binval = (ind & (ULONG_C(0x3f) << (24-bits)))
+ >> (24-bits);
+ if ((binval < sizeof(bin2ISRC)) &&
+ (binval <= 9 || binval >= 16)) {
+ fprintf(stderr, "%X", bin2ISRC[binval]);
+ }
+ }
+
+ fprintf(stderr, "%.1X%.1X%.1X%.1X%.1X%.1X%.1X",
+ subq_tr->track_isrc [8] & 0x0f,
+ subq_tr->track_isrc [9] & 0x0f,
+ subq_tr->track_isrc [10] & 0x0f,
+ subq_tr->track_isrc [11] & 0x0f,
+ subq_tr->track_isrc [12] & 0x0f,
+ subq_tr->track_isrc [13] & 0x0f,
+ subq_tr->track_isrc [14] & 0x0f
+ );
+ fprintf(stderr, "\n");
+ }
+ return;
+ }
+ if ((cp[7] & 3) != 0) {
+ fprintf(stderr, "unknown mode 3 entry C1=0x%02x, C2=0x%02x\n",
+ (cp[7] >> 1) & 1, cp[7] & 1);
+ return;
+ }
+
+ /* decode ISRC due to IEC 908 */
+ for (bits = 0; bits < 30; bits +=6) {
+ unsigned binval = (ind & ((unsigned long) 0x3fL << (24L-bits))) >> (24L-bits);
+ if ((binval >= sizeof(bin2ISRC)) ||
+ (binval > 9 && binval < 16)) {
+ /* Illegal ISRC, dump and skip */
+ int y;
+
+ Get_ISRC(tr)[0] = '\0';
+ fprintf(stderr, "\nIllegal ISRC for track %d, skipped: ", tr);
+ for (y = 0; y < 15; y++) {
+ fprintf(stderr, "%02x ", cp[y]);
+ }
+ fputs("\n", stderr);
+ return;
+ }
+ *p++ = bin2ISRC[binval];
+
+ /* insert a dash after two country characters for legibility */
+ if (bits == 6)
+ *p++ = '-';
+ }
+
+ /* format year and serial number */
+ sprintf ((char *)p, "-%.1X%.1X-%.1X%.1X%.1X%.1X%.1X",
+ subq_tr->track_isrc [8] & 0x0f,
+ subq_tr->track_isrc [9] & 0x0f,
+ subq_tr->track_isrc [10] & 0x0f,
+ subq_tr->track_isrc [11] & 0x0f,
+ subq_tr->track_isrc [12] & 0x0f,
+ subq_tr->track_isrc [13] & 0x0f,
+ subq_tr->track_isrc [14] & 0x0f
+ );
+#if DEBUG_ISRC
+ fprintf(stderr, "b: %15.15s!\n", p_start);
+#endif
+ } else {
+ /* It might be in ASCII, surprise */
+ int ii;
+ for (ii = 0; ii < 12; ii++) {
+ if (cp[ii] < '0' || cp[ii] > 'Z') {
+ break;
+ }
+ }
+ if (ii != 12) {
+ int y;
+
+ Get_ISRC(ii)[0] = '\0';
+ fprintf(stderr, "\nIllegal ISRC for track %d, skipped: ", ii+1);
+ for (y = 0; y < 15; y++) {
+ fprintf(stderr, "%02x ", cp[y]);
+ }
+ fputs("\n", stderr);
+ return;
+ }
+
+#if DEBUG_ISRC
+ fprintf(stderr, "ascii: %15.15s!\n", cp);
+#endif
+ for (ii = 0; ii < 12; ii++) {
+#if 1
+ if ((ii == 2 || ii == 5 || ii == 7) && cp[ii] != ' ')
+ *p++ = '-';
+#endif
+ *p++ = cp[ii];
+ }
+ if (p - p_start >= 16)
+ *(p_start + 15) = '\0';
+ else
+ *p = '\0';
+ }
+
+ if (memcmp(p_start,"00-000-00-00000",15) != 0) {
+ Set_ISRC(tr, p_start);
+ }
+ }
+}
+
+/* get and display Media Catalog Number ( one per disc )
+ * and Track International Standard Recording Codes (for each track)
+ */
+void Read_MCN_ISRC(void)
+{
+ if ((global.verbose & SHOW_MCN) != 0) {
+
+ if (Get_MCN()[0] == '\0') {
+ Get_Set_MCN();
+ }
+
+ if (Get_MCN()[0] != '\0')
+ fprintf(stderr, "\rMedia catalog number: %13.13s\n", Get_MCN());
+ else
+ fprintf(stderr, "\rNo media catalog number present.\n");
+ }
+
+
+
+ if ((global.verbose & SHOW_ISRC) != 0) {
+ static struct iterator i;
+
+ InitIterator(&i, 1);
+
+ while (i.hasNextTrack(&i)) {
+ struct TOC *p = i.getNextTrack(&i);
+ unsigned ii = GETTRACK(p);
+
+ if (ii == CDROM_LEADOUT) break;
+
+ if (!IS__AUDIO(p))
+ continue;
+
+ if (GETISRC(p)[0] == '\0') {
+ Get_Set_ISRC(ii);
+ }
+
+ if (GETISRC(p)[0] != '\0') {
+ fprintf (stderr, "\rT: %2d ISRC: %15.15s\n", ii, GETISRC(p));
+ fflush(stderr);
+ }
+ } /* for all tracks */
+
+ fputs("\n", stderr);
+ } /* if SHOW_ISRC */
+}
+
+static int playing = 0;
+
+static subq_chnl *ReadSubChannel(unsigned sec);
+
+static subq_chnl *ReadSubChannel(unsigned sec)
+{
+ subq_chnl *sub_ch;
+
+ /*
+ * For modern drives implement a direct method. If the drive supports
+ * reading of subchannel data, do direct reads.
+ */
+ if (ReadSubChannels != NULL) {
+ get_scsi_p()->silent++;
+ sub_ch = ReadSubChannels(get_scsi_p(), sec);
+ get_scsi_p()->silent--;
+ if (sub_ch == NULL /*&& (usal_sense_key(get_scsi_p()) == 5)*/) {
+ /* command is not implemented */
+ ReadSubChannels = NULL;
+#if defined DEBUG_SUB
+fprintf(stderr, "\nCommand not implemented: switching ReadSubChannels off !\n");
+#endif
+ goto fallback;
+ }
+
+ /* check the adress mode field */
+ if ((sub_ch->control_adr & 0x0f) == 0) {
+ /* no Q mode information present at all, weird */
+ sub_ch->control_adr = 0xAA;
+ }
+
+ if ((int)(sub_ch->control_adr & 0x0f) > 0x01) {
+ /* this sector just has no position information.
+ * we try the one before and then the one after.
+ */
+ if (sec > 1) {
+ sec -= 1;
+ sub_ch = ReadSubChannels(get_scsi_p(), sec);
+ if (sub_ch == NULL) return NULL;
+ sec += 1;
+ }
+ if ((sub_ch->control_adr & 0x0f) != 0x01) {
+ sec += 2;
+ sub_ch = ReadSubChannels(get_scsi_p(), sec);
+ if (sub_ch == NULL) return NULL;
+ sec -= 2;
+ }
+ }
+
+ /* check adress mode field for position information */
+ if ((sub_ch->control_adr & 0x0f) == 0x01) {
+ return sub_ch;
+ }
+ ReadSubChannels = NULL;
+fprintf(stderr, "\nCould not get position information (%02x) for sectors %d, %d, %d: switching ReadSubChannels off !\n", sub_ch->control_adr &0x0f, sec-1, sec, sec+2);
+ }
+
+ /*
+ * We rely on audio sectors here!!!
+ * The only method that worked even with my antique Toshiba 3401,
+ * is playing the sector and then request the subchannel afterwards.
+ */
+fallback:
+ /* We need a conformed audio track here! */
+
+ /* Fallback to ancient method */
+ if (-1 == Play_at(get_scsi_p(), sec, 1)) {
+ return NULL;
+ }
+ playing = 1;
+ sub_ch = ReadSubQ(get_scsi_p(), GET_POSITIONDATA,0);
+ return sub_ch;
+}
+
+static int ReadSubControl(unsigned sec);
+static int ReadSubControl(unsigned sec)
+{
+ subq_chnl *sub_ch = ReadSubChannel(sec);
+ if (sub_ch == NULL) return -1;
+
+ return sub_ch->control_adr & 0xf0;
+}
+
+static int HaveSCMS(unsigned StartSector);
+static int HaveSCMS(unsigned StartSector)
+{
+ int i;
+ int cr;
+ int copy_bits_set = 0;
+
+ for (i = 0; i < 8; i++) {
+ cr = ReadSubControl(StartSector + i);
+ if (cr == -1) continue;
+ (cr & 0x20) ? copy_bits_set++ : 0;
+ }
+ return (copy_bits_set >= 1 && copy_bits_set < 8);
+}
+
+void Check_Toc(void)
+{
+ /* detect layout */
+
+ /* detect tracks */
+}
+
+static int GetIndexOfSector(unsigned sec, unsigned track)
+{
+ subq_chnl *sub_ch = ReadSubChannel(sec);
+ if (sub_ch == NULL) {
+ if ((long)sec == Get_EndSector(track)) {
+ fprintf(stderr, "Driver and/or firmware bug detected! Drive cannot play the very last sector (%u)!\n", sec);
+ }
+ return -1;
+ }
+
+ /* can we trust that these values are hex and NOT bcd? */
+ if ((sub_ch->track >= 0x10) && (sub_ch->track - track > 5)) {
+ /* change all values from bcd to hex */
+ sub_ch->track = (sub_ch->track >> 4)*10 + (sub_ch->track & 0x0f);
+ sub_ch->index = (sub_ch->index >> 4)*10 + (sub_ch->index & 0x0f);
+ }
+
+#if 1
+ /* compare tracks */
+ if (sub_ch->index != 0 && track != sub_ch->track) {
+ if (global.verbose) fprintf(stderr, "\ntrack mismatch: %1d, in-track subchannel: %1d (index %1d, sector %1d)\n",
+ track, sub_ch->track, sub_ch->index, sec);
+ }
+#endif
+
+ /* compare control field with the one from the TOC */
+ if ((Get_Flags(track) & 0xf0) != (sub_ch->control_adr & 0xf0)) {
+ int diffbits = (Get_Flags(track) & 0xf0) ^ (sub_ch->control_adr & 0xf0);
+ if ((diffbits & 0x80) == 0x80) {
+ /* broadcast difference */
+ if (global.verbose) fprintf(stderr, "broadcast type conflict detected -> TOC:%s, subchannel:%s\n",
+ (sub_ch->control_adr & 0x80) == 0 ? "broadcast" : "nonbroadcast"
+ ,(sub_ch->control_adr & 0x80) != 0 ? "broadcast" : "nonbroadcast"
+ );
+ }
+ if ((diffbits & 0x40) == 0x40) {
+ /* track type difference */
+ if (global.verbose) fprintf(stderr, "track type conflict detected -> TOC:%s, subchannel:%s\n",
+ (sub_ch->control_adr & 0x40) == 0 ? "data" : "audio"
+ ,(sub_ch->control_adr & 0x40) != 0 ? "data" : "audio"
+ );
+ }
+ if ((diffbits & 0x20) == 0x20 && !Get_SCMS(track)) {
+ /* copy permission difference is a sign for SCMS
+ * and is treated elsewhere. */
+ if (global.verbose) fprintf(stderr, "difference: TOC:%s, subchannel:%s\ncorrecting TOC...\n",
+ (sub_ch->control_adr & 0x20) == 0 ? "unprotected" : "copyright protected",
+ (sub_ch->control_adr & 0x20) != 0 ? "unprotected" : "copyright protected"
+ );
+ toc_entry(track,
+ (Get_Flags(track) & 0xDF) | (sub_ch->control_adr & 0x20),
+ Get_Tracknumber(track),
+ Get_ISRC(track),
+ Get_AudioStartSector(track),
+ Get_Mins(track),
+ Get_Secs(track),
+ Get_Frames(track)
+ );
+ }
+ if ((diffbits & 0x10) == 0x10) {
+ /* preemphasis difference */
+ if (global.verbose) fprintf(stderr, "difference: TOC:%s, subchannel:%s preemphasis\ncorrecting TOC...\n",
+ (sub_ch->control_adr & 0x10) == 0 ? "with" : "without",
+ (sub_ch->control_adr & 0x10) != 0 ? "with" : "without"
+ );
+ toc_entry(track,
+ (Get_Flags(track) & 0xEF) | (sub_ch->control_adr & 0x10),
+ Get_Tracknumber(track),
+ Get_ISRC(track),
+ Get_AudioStartSector(track),
+ Get_Mins(track),
+ Get_Secs(track),
+ Get_Frames(track)
+ );
+ }
+
+ }
+
+ return sub_ch ? sub_ch->index == 244 ? 1 : sub_ch->index : -1;
+}
+
+static int ScanBackwardFrom(unsigned sec, unsigned limit, int *where,
+ unsigned track);
+
+static int ScanBackwardFrom(unsigned sec, unsigned limit, int *where,
+ unsigned track)
+{
+ unsigned lastindex = 0;
+ unsigned mysec = sec;
+
+ /* try to find the transition of index n to index 0,
+ * if the track ends with an index 0.
+ */
+ while ((lastindex = GetIndexOfSector(mysec, track)) == 0) {
+ if (mysec < limit+75) {
+ break;
+ }
+ mysec -= 75;
+ }
+ if (mysec == sec) {
+ /* there is no pre-gap in this track */
+ if (where != NULL) *where = -1;
+ } else {
+ /* we have a pre-gap in this track */
+
+ if (lastindex == 0) {
+ /* we did not cross the transition yet -> search backward */
+ do {
+ if (mysec < limit+1) {
+ break;
+ }
+ mysec --;
+ } while ((lastindex = GetIndexOfSector(mysec,track)) == 0);
+ if (lastindex != 0) {
+ /* successful */
+ mysec ++;
+ /* register mysec as transition */
+ if (where != NULL) *where = (int) mysec;
+ } else {
+ /* could not find transition */
+ if (!global.quiet)
+ fprintf(stderr,
+ "Could not find index transition for pre-gap.\n");
+ if (where != NULL) *where = -1;
+ }
+ } else {
+ int myindex = -1;
+ /* we have crossed the transition -> search forward */
+ do {
+ if (mysec >= sec) {
+ break;
+ }
+ mysec ++;
+ } while ((myindex = GetIndexOfSector(mysec,track)) != 0);
+ if (myindex == 0) {
+ /* successful */
+ /* register mysec as transition */
+ if (where != NULL) *where = (int) mysec;
+ } else {
+ /* could not find transition */
+ if (!global.quiet)
+ fprintf(stderr,
+ "Could not find index transition for pre-gap.\n");
+ if (where != NULL) *where = -1;
+ }
+ }
+ }
+ return lastindex;
+}
+
+#ifdef USE_LINEAR_SEARCH
+static int linear_search(int searchInd, unsigned int Start, unsigned int End,
+ unsigned track);
+
+static int linear_search(int searchInd, unsigned int Start, unsigned int End,
+ unsigned track)
+{
+ int l = Start;
+ int r = End;
+
+ for (; l <= r; l++ ) {
+ int ind;
+
+ ind = GetIndexOfSector(l, track);
+ if ( searchInd == ind ) {
+ break;
+ }
+ }
+ if ( l <= r ) {
+ /* Index found. */
+ return l;
+ }
+
+ return -1;
+}
+#endif
+
+#ifndef USE_LINEAR_SEARCH
+#undef DEBUG_BINSEARCH
+static int binary_search(int searchInd, unsigned int Start, unsigned int End,
+ unsigned track);
+
+static int binary_search(int searchInd, unsigned Start, unsigned End,
+ unsigned track)
+{
+ int l = Start;
+ int r = End;
+ int x = 0;
+ int ind;
+
+ while ( l <= r ) {
+ x = ( l + r ) / 2;
+ /* try to avoid seeking */
+ ind = GetIndexOfSector(x, track);
+ if ( searchInd == ind ) {
+ break;
+ } else {
+ if ( searchInd < ind ) r = x - 1;
+ else l = x + 1;
+ }
+ }
+#ifdef DEBUG_BINSEARCH
+fprintf(stderr, "(%d,%d,%d > ",l,x,r);
+#endif
+ if ( l <= r ) {
+ /* Index found. Now find the first position of this index */
+ /* l=LastPos x=found r=NextPos */
+ r = x;
+ while ( l < r-1 ) {
+ x = ( l + r ) / 2;
+ /* try to avoid seeking */
+ ind = GetIndexOfSector(x, track);
+ if ( searchInd == ind ) {
+ r = x;
+ } else {
+ l = x;
+ }
+#ifdef DEBUG_BINSEARCH
+fprintf(stderr, "%d -> ",x);
+#endif
+ }
+#ifdef DEBUG_BINSEARCH
+fprintf(stderr, "%d,%d)\n",l,r);
+#endif
+ if (searchInd == GetIndexOfSector(l, track))
+ return l;
+ else
+ return r;
+ }
+
+ return -1;
+}
+#endif
+
+
+static void register_index_position(int IndexOffset,
+ index_list **last_index_entry);
+
+static void register_index_position(int IndexOffset,
+ index_list **last_index_entry)
+{
+ index_list *indexentry;
+
+ /* register higher index entries */
+ if (*last_index_entry != NULL) {
+ indexentry = (index_list *) malloc( sizeof(index_list) );
+ } else {
+ indexentry = NULL;
+ }
+ if (indexentry != NULL) {
+ indexentry->next = NULL;
+ (*last_index_entry)->next = indexentry;
+ *last_index_entry = indexentry;
+ indexentry->frameoffset = IndexOffset;
+#if defined INFOFILES
+ } else {
+ fprintf( stderr, "No memory for index lists. Index positions\nwill not be written in info file!\n");
+#endif
+ }
+}
+
+static void Set_SCMS(unsigned long p_track);
+
+#undef DEBUG_INDLIST
+/* experimental code */
+/* search for indices (audio mode required) */
+unsigned ScanIndices(unsigned track, unsigned cd_index, int bulk)
+{
+ /* scan for indices. */
+ /* look at last sector of track. */
+ /* when the index is not equal 1 scan by bipartition
+ * for offsets of all indices */
+
+ unsigned starttrack, endtrack;
+ unsigned startindex, endindex;
+
+ unsigned j;
+ int LastIndex=0;
+ int n_0_transition;
+ unsigned StartSector;
+ unsigned retval = 0;
+
+ index_list *baseindex_pool;
+ index_list *last_index_entry;
+
+ SCSI *usalp = get_scsi_p();
+
+ static struct iterator i;
+ InitIterator(&i, 1);
+
+ EnableCdda(usalp, 0, 0);
+ EnableCdda(usalp, 1, CD_FRAMESIZE_RAW + 16);
+
+ if (!global.quiet && !(global.verbose & SHOW_INDICES))
+ fprintf(stderr, "seeking index start ...");
+
+ if (bulk != 1) {
+ starttrack = track; endtrack = track;
+ } else {
+ starttrack = 1; endtrack = cdtracks;
+ }
+ baseindex_pool = (index_list *) malloc( sizeof(index_list) * (endtrack - starttrack + 1));
+#ifdef DEBUG_INDLIST
+ fprintf(stderr, "index0-mem-pool %p\n", baseindex_pool);
+#endif
+
+
+ while (i.hasNextTrack(&i)) {
+ struct TOC *p = i.getNextTrack(&i);
+ unsigned ii = GETTRACK(p);
+
+ if ( ii < starttrack || IS__DATA(p) )
+ continue; /* skip nonaudio tracks */
+
+ if ( ii > endtrack )
+ break;
+
+ if ( global.verbose & SHOW_INDICES ) {
+ if (global.illleadout_cd && global.reads_illleadout && ii == endtrack) {
+ fprintf(stderr, "Analysis of track %d skipped due to unknown length\n", ii);
+ }
+ }
+ if (global.illleadout_cd && global.reads_illleadout
+ && ii == endtrack) continue;
+
+ StartSector = Get_AudioStartSector(ii);
+ if (HaveSCMS(StartSector)) {
+ Set_SCMS(ii);
+ }
+ if ( global.verbose & SHOW_INDICES ) {
+ fprintf( stderr, "\rindex scan: %d...", ii );
+ fflush (stderr);
+ }
+ LastIndex = ScanBackwardFrom(Get_EndSector(ii), StartSector, &n_0_transition, ii);
+ if (LastIndex > 99) continue;
+
+ if (baseindex_pool != NULL) {
+#ifdef DEBUG_INDLIST
+#endif
+ /* register first index entry for this track */
+ baseindex_pool[ii - starttrack].next = NULL;
+ baseindex_pool[ii - starttrack].frameoffset = StartSector;
+ global.trackindexlist[ii] = &baseindex_pool[ii - starttrack];
+#ifdef DEBUG_INDLIST
+#endif
+ } else {
+ global.trackindexlist[ii] = NULL;
+ }
+ last_index_entry = global.trackindexlist[ii];
+
+ if (LastIndex < 2) {
+ register_index_position(n_0_transition, &last_index_entry);
+ continue;
+ }
+
+ if ((global.verbose & SHOW_INDICES) && LastIndex > 1)
+ fprintf(stderr, "\rtrack %2d has %d indices, index table (pairs of 'index: frame offset')\n", ii, LastIndex);
+
+ startindex = 0;
+ endindex = LastIndex;
+
+ for (j = startindex; j <= endindex; j++) {
+ int IndexOffset;
+
+ /* this track has indices */
+
+#ifdef USE_LINEAR_SEARCH
+ /* do a linear search */
+ IndexOffset = linear_search(j, StartSector, Get_EndSector(ii), ii);
+#else
+ /* do a binary search */
+ IndexOffset = binary_search(j, StartSector, Get_EndSector(ii), ii);
+#endif
+
+ if (IndexOffset != -1) {
+ StartSector = IndexOffset;
+ }
+
+ if (j == 1)
+ last_index_entry->frameoffset = IndexOffset;
+ else if (j > 1)
+ register_index_position(IndexOffset, &last_index_entry);
+
+ if ( IndexOffset == -1 ) {
+ if (global.verbose & SHOW_INDICES) {
+ if (global.gui == 0) {
+ fprintf(stderr, "%2u: N/A ",j);
+ if (((j + 1) % 8) == 0) fputs("\n", stderr);
+ } else {
+ fprintf(stderr, "\rT%02d I%02u N/A\n",ii,j);
+ }
+ }
+ } else {
+ if (global.verbose & SHOW_INDICES) {
+ if (global.gui == 0) {
+ fprintf(stderr,
+ "%2u:%6lu ",
+ j,
+ IndexOffset-Get_AudioStartSector(ii)
+ );
+ if (((j + 1) % 8) == 0) fputs("\n", stderr);
+ } else {
+ fprintf(stderr,
+ "\rT%02d I%02u %06lu\n",
+ ii,
+ j,
+ IndexOffset-Get_AudioStartSector(ii)
+ );
+ }
+ }
+
+ if (track == ii && cd_index == j) {
+ retval = IndexOffset-Get_AudioStartSector(ii);
+ }
+ } /* if IndexOffset */
+ } /* for index */
+ register_index_position(n_0_transition, &last_index_entry);
+
+ /* sanity check. clear all consecutive nonindex entries (frameoffset -1) from the end. */
+ {
+ index_list *ip = global.trackindexlist[ii];
+ index_list *iq = NULL;
+ index_list *lastgood = iq;
+
+ while (ip != NULL)
+ {
+ if (ip->frameoffset == -1)
+ {
+ /* no index available */
+ if (lastgood == NULL)
+ {
+ /* if this is the first one in a sequence, store predecessor */
+ lastgood = iq;
+ }
+ } else {
+ /* this is a valid index, reset marker */
+ lastgood = NULL;
+ }
+
+ iq = ip;
+ ip = ip->next;
+ }
+ /* terminate chain at the last well defined entry. */
+ if (lastgood != NULL)
+ lastgood->next = NULL;
+ }
+
+ if (global.gui == 0 && (global.verbose & SHOW_INDICES)
+ && ii != endtrack)
+ fputs("\n", stderr);
+ } /* for tracks */
+ if (global.gui == 0 && (global.verbose & SHOW_INDICES))
+ fputs("\n", stderr);
+ if (playing != 0) StopPlay(get_scsi_p());
+
+ EnableCdda(usalp, 0, 0);
+ EnableCdda(usalp, 1, CD_FRAMESIZE_RAW);
+
+ return retval;
+}
+
+static unsigned char MCN[14];
+
+static void Set_MCN(unsigned char *MCN_arg)
+{
+ memcpy(MCN, MCN_arg, 14);
+ MCN[13] = '\0';
+}
+
+unsigned char *Get_MCN(void)
+{
+ return MCN;
+}
+
+
+static TOC g_toc [MAXTRK+1]; /* hidden track + 100 regular tracks */
+
+/*#define IS_AUDIO(i) (!(g_toc[i].bFlags & 0x40))*/
+
+int
+TOC_entries(unsigned tracks, unsigned char *a, unsigned char *b, int binvalid)
+{
+ int i;
+ for (i = 1; i <= (int)tracks; i++) {
+ unsigned char *p;
+ unsigned long dwStartSector;
+
+ if (binvalid) {
+ p = a + 8*(i-1);
+
+ g_toc[i].bFlags = p[1];
+ g_toc[i].bTrack = p[2];
+ g_toc[i].ISRC[0] = 0;
+ dwStartSector = a_to_u_4_byte(p+4);
+ g_toc[i].dwStartSector = dwStartSector;
+ lba_2_msf((long)dwStartSector,
+ &g_toc[i].mins,
+ &g_toc[i].secs,
+ &g_toc[i].frms);
+ } else {
+ p = b + 8*(i-1);
+ g_toc[i].bFlags = p[1];
+ g_toc[i].bTrack = p[2];
+ g_toc[i].ISRC[0] = 0;
+ if ((int)((p[5]*60 + p[6])*75 + p[7]) >= 150) {
+ g_toc[i].dwStartSector = (p[5]*60 + p[6])*75 + p[7] -150;
+ } else {
+ g_toc[i].dwStartSector = 0;
+ }
+ g_toc[i].mins = p[5];
+ g_toc[i].secs = p[6];
+ g_toc[i].frms = p[7];
+ }
+ }
+ return 0;
+}
+
+void toc_entry(unsigned nr, unsigned flag, unsigned tr, unsigned char *ISRC,
+ unsigned long lba, int m, int s, int f)
+{
+ if (nr > MAXTRK) return;
+
+ g_toc[nr].bFlags = flag;
+ g_toc[nr].bTrack = tr;
+ if (ISRC) {
+ strncpy((char *)g_toc[nr].ISRC, (char *)ISRC,
+ sizeof(g_toc[nr].ISRC) -1);
+ g_toc[nr].ISRC[sizeof(g_toc[nr].ISRC) -1] = '\0';
+ }
+ g_toc[nr].dwStartSector = lba;
+ g_toc[nr].mins = m;
+ g_toc[nr].secs = s;
+ g_toc[nr].frms = f;
+}
+
+int patch_real_end(unsigned long sector)
+{
+ g_toc[cdtracks+1].dwStartSector = sector;
+ return 0;
+}
+
+static int patch_cd_extra(unsigned track, unsigned long sector)
+{
+ if (track <= cdtracks)
+ g_toc[track].dwStartSector = sector;
+ return 0;
+}
+
+static int restrict_tracks_illleadout(void)
+{
+ struct TOC *o = &g_toc[cdtracks+1];
+ int i;
+ for (i = cdtracks; i >= 0; i--) {
+ struct TOC *p = &g_toc[i];
+ if (GETSTART(o) > GETSTART(p)) break;
+ }
+ patch_cd_extra(i+1, GETSTART(o));
+ cdtracks = i;
+
+ return 0;
+}
+
+static void Set_ISRC(int track, const unsigned char *ISRC_arg)
+{
+ if (track <= (int)cdtracks) {
+ memcpy(Get_ISRC(track), ISRC_arg, 16);
+ }
+}
+
+
+unsigned char *Get_ISRC(unsigned long p_track)
+{
+ if (p_track <= cdtracks)
+ return g_toc[p_track].ISRC;
+ return NULL;
+}
+
+static void patch_to_audio(unsigned long p_track)
+{
+ if (p_track <= cdtracks)
+ g_toc[p_track].bFlags &= ~0x40;
+}
+
+int Get_Flags(unsigned long p_track)
+{
+ if (p_track <= cdtracks)
+ return g_toc[p_track].bFlags;
+ return -1;
+}
+
+int Get_Mins(unsigned long p_track)
+{
+ if (p_track <= cdtracks)
+ return g_toc[p_track].mins;
+ return -1;
+}
+
+int Get_Secs(unsigned long p_track)
+{
+ if (p_track <= cdtracks)
+ return g_toc[p_track].secs;
+ return -1;
+}
+
+int Get_Frames(unsigned long p_track)
+{
+ if (p_track <= cdtracks)
+ return g_toc[p_track].frms;
+ return -1;
+}
+
+int Get_Preemphasis(unsigned long p_track)
+{
+ if (p_track <= cdtracks)
+ return g_toc[p_track].bFlags & 0x10;
+ return -1;
+}
+
+static void Set_SCMS(unsigned long p_track)
+{
+ g_toc[p_track].SCMS = 1;
+}
+
+int Get_SCMS(unsigned long p_track)
+{
+ if (p_track <= cdtracks)
+ return g_toc[p_track].SCMS;
+ return -1;
+}
+
+int Get_Copyright(unsigned long p_track)
+{
+ if (p_track <= cdtracks) {
+ if (g_toc[p_track].SCMS) return 1;
+ return ((int)g_toc[p_track].bFlags & 0x20) >> 4;
+ }
+ return -1;
+}
+
+int Get_Datatrack(unsigned long p_track)
+{
+ if (p_track <= cdtracks)
+ return g_toc[p_track].bFlags & 0x40;
+ return -1;
+}
+
+int Get_Channels(unsigned long p_track)
+{
+ if (p_track <= cdtracks)
+ return g_toc[p_track].bFlags & 0x80;
+ return -1;
+}
+
+int Get_Tracknumber(unsigned long p_track)
+{
+ if (p_track <= cdtracks)
+ return g_toc[p_track].bTrack;
+ return -1;
+}
+
+static int useHiddenTrack(void)
+{
+ return 0;
+}
+
+
+
+static void it_reset(struct iterator *this);
+
+static void it_reset(struct iterator *this)
+{
+ this->index = this->startindex;
+}
+
+
+static int it_hasNextTrack(struct iterator *this);
+static struct TOC *it_getNextTrack(struct iterator *this);
+
+static int it_hasNextTrack(struct iterator *this)
+{
+ return this->index <= (int)cdtracks+1;
+}
+
+
+
+static struct TOC *it_getNextTrack(struct iterator *this)
+{
+ /* if ( (*this->hasNextTrack)(this) == 0 ) return NULL; */
+ if ( this->index > (int)cdtracks+1 ) return NULL;
+
+ return &g_toc[ this->index++ ];
+}
+
+
+static void InitIterator(struct iterator *iter, unsigned long p_track)
+{
+ if (iter == NULL) return;
+
+ iter->index = iter->startindex = useHiddenTrack() ? 0 : p_track;
+ iter->reset = it_reset;
+ iter->getNextTrack = it_getNextTrack;
+ iter->hasNextTrack = it_hasNextTrack;
+}
+
+#if 0
+static struct iterator *NewIterator(void);
+
+static struct iterator *NewIterator ()
+{
+ struct iterator *retval;
+
+ retval = malloc (sizeof(struct iterator));
+ if (retval != NULL) {
+ InitIterator(retval, 1);
+ }
+ return retval;
+}
+#endif
+
+long Get_AudioStartSector(unsigned long p_track)
+{
+#if 1
+ if (p_track == CDROM_LEADOUT)
+ p_track = cdtracks + 1;
+
+ if (p_track <= cdtracks +1
+ && IS__AUDIO(&g_toc[p_track]))
+ return GETSTART(&g_toc[p_track]);
+#else
+ static struct iterator i;
+ if (i.reset == NULL) InitIterator(&i, p_track);
+ else i.reset(&i);
+
+ if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT;
+
+ while (i.hasNextTrack(&i)) {
+ TOC *p = i.getNextTrack(&i);
+
+ if (GETTRACK(p) == p_track) {
+ if (IS__DATA(p)) {
+ return -1;
+ }
+ return GETSTART(p);
+ }
+ }
+#endif
+ return -1;
+}
+
+
+long Get_StartSector(unsigned long p_track)
+{
+#if 1
+ if (p_track == CDROM_LEADOUT)
+ p_track = cdtracks + 1;
+
+ if (p_track <= cdtracks +1)
+ return GETSTART(&g_toc[p_track]);
+#else
+ static struct iterator i;
+ if (i.reset == NULL) InitIterator(&i, p_track);
+ else i.reset(&i);
+
+ if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT;
+
+ while (i.hasNextTrack(&i)) {
+ TOC *p = i.getNextTrack(&i);
+
+ if (GETTRACK(p) == p_track) {
+ return GETSTART(p);
+ }
+ }
+#endif
+ return -1;
+}
+
+
+long Get_EndSector(unsigned long p_track)
+{
+#if 1
+ if (p_track <= cdtracks)
+ return GETSTART(&g_toc[p_track+1])-1;
+#else
+ static struct iterator i;
+ if (i.reset == NULL) InitIterator(&i, p_track);
+ else i.reset(&i);
+
+ if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT;
+
+ while (i.hasNextTrack(&i)) {
+ TOC *p = i.getNextTrack(&i);
+ if (GETTRACK(p) == p_track) {
+ p = i.getNextTrack(&i);
+ if (p == NULL) {
+ return -1;
+ }
+ return GETSTART(p)-1;
+ }
+ }
+#endif
+ return -1;
+}
+
+long FirstTrack(void)
+{
+ static struct iterator i;
+ if (i.reset == NULL) InitIterator(&i, 1);
+ else i.reset(&i);
+
+ if (i.hasNextTrack(&i)) {
+ return GETTRACK(i.getNextTrack(&i));
+ }
+ return -1;
+}
+
+long FirstAudioTrack(void)
+{
+ static struct iterator i;
+ if (i.reset == NULL) InitIterator(&i, 1);
+ else i.reset(&i);
+
+ while (i.hasNextTrack(&i)) {
+ TOC *p = i.getNextTrack(&i);
+ unsigned ii = GETTRACK(p);
+
+ if (ii == CDROM_LEADOUT) break;
+ if (IS__AUDIO(p)) {
+ return ii;
+ }
+ }
+ return -1;
+}
+
+long FirstDataTrack(void)
+{
+ static struct iterator i;
+ if (i.reset == NULL) InitIterator(&i, 1);
+ else i.reset(&i);
+
+ while (i.hasNextTrack(&i)) {
+ TOC *p = i.getNextTrack(&i);
+ if (IS__DATA(p)) {
+ return GETTRACK(p);
+ }
+ }
+ return -1;
+}
+
+long LastTrack(void)
+{
+ return g_toc[cdtracks].bTrack;
+}
+
+long LastAudioTrack(void)
+{
+ long j = -1;
+ static struct iterator i;
+ if (i.reset == NULL) InitIterator(&i, 1);
+ else i.reset(&i);
+
+ while (i.hasNextTrack(&i)) {
+ TOC *p = i.getNextTrack(&i);
+ if (IS__AUDIO(p) && (GETTRACK(p) != CDROM_LEADOUT)) {
+ j = GETTRACK(p);
+ }
+ }
+ return j;
+}
+
+long Get_LastSectorOnCd(unsigned long p_track)
+{
+ long LastSec = 0;
+ static struct iterator i;
+
+ if (global.illleadout_cd && global.reads_illleadout)
+ return 150+(99*60+59)*75+74;
+
+ if (i.reset == NULL) InitIterator(&i, p_track);
+ else i.reset(&i);
+
+ if (p_track == cdtracks + 1) p_track = CDROM_LEADOUT;
+
+ while (i.hasNextTrack(&i)) {
+ TOC *p = i.getNextTrack(&i);
+
+ if (GETTRACK(p) < p_track)
+ continue;
+
+ LastSec = GETSTART(p);
+
+ if (IS__DATA(p)) break;
+ }
+ return LastSec;
+}
+
+int Get_Track(unsigned long sector)
+{
+ static struct iterator i;
+ if (i.reset == NULL) InitIterator(&i, 1);
+ else i.reset(&i);
+
+ if (i.hasNextTrack(&i)) {
+ TOC *o = i.getNextTrack(&i);
+ while (i.hasNextTrack(&i)) {
+ TOC *p = i.getNextTrack(&i);
+ if ((GETSTART(o) <= sector) && (GETSTART(p) > sector)) {
+ if (IS__DATA(o)) {
+ return -1;
+ } else {
+ return GETTRACK(o);
+ }
+ }
+ o = p;
+ }
+ }
+ return -1;
+}
+
+int CheckTrackrange(unsigned long from, unsigned long upto)
+{
+ static struct iterator i;
+ if (i.reset == NULL) InitIterator(&i, from);
+ else i.reset(&i);
+
+ while (i.hasNextTrack(&i)) {
+ TOC *p = i.getNextTrack(&i);
+
+ if (GETTRACK(p) < from)
+ continue;
+
+ if (GETTRACK(p) == upto)
+ return 1;
+
+ /* data tracks terminate the search */
+ if (IS__DATA(p))
+ return 0;
+ }
+ /* track not found */
+ return 0;
+}
+
+#ifdef USE_PARANOIA
+long cdda_disc_firstsector(void *d);
+
+long cdda_disc_firstsector(void *d)
+{
+ return Get_StartSector(FirstAudioTrack());
+}
+
+int cdda_tracks(void *d);
+
+int cdda_tracks(void *d)
+{
+ return LastAudioTrack() - FirstAudioTrack() +1;
+}
+
+int cdda_track_audiop(void *d, int track);
+
+int cdda_track_audiop(void *d, int track)
+{
+ return Get_Datatrack(track) == 0;
+}
+
+long cdda_track_firstsector(void *d, int track);
+
+long cdda_track_firstsector(void *d, int track)
+{
+ return Get_AudioStartSector(track);
+}
+
+long cdda_track_lastsector(void *d, int track);
+
+long cdda_track_lastsector(void *d, int track)
+{
+ return Get_EndSector(track);
+}
+
+long cdda_disc_lastsector(void *d);
+
+long cdda_disc_lastsector(void *d)
+{
+ return Get_LastSectorOnCd(cdtracks) - 1;
+}
+
+int cdda_sector_gettrack(void *d,long sector);
+
+int cdda_sector_gettrack(void *d, long sector)
+{
+ return Get_Track(sector);
+}
+
+#endif