1 /*
2  *   tosha.c
3  *
4  *   Oliver Fromme  <olli@fromme.com>
5  *
6  *   Copyright (C) 1997,1998,1999
7  *        Oliver Fromme.  All rights reserved.
8  *
9  *   Redistribution and use in source and binary forms, with or without
10  *   modification, are permitted provided that the following conditions
11  *   are met:
12  *   1. Redistributions of source code must retain the above copyright
13  *      notice, this list of conditions and the following disclaimer.
14  *   2. Redistributions in binary form must reproduce the above copyright
15  *      notice, this list of conditions and the following disclaimer in the
16  *      documentation and/or other materials provided with the distribution.
17  *   3. Neither the name of the author nor the names of any co-contributors
18  *      may be used to endorse or promote products derived from this software
19  *      without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY OLIVER FROMME AND CONTRIBUTORS ``AS IS'' AND
22  *   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  *   ARE DISCLAIMED.  IN NO EVENT SHALL OLIVER FROMME OR CONTRIBUTORS BE LIABLE
25  *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  *   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  *   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  *   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  *   SUCH DAMAGE.
32  *
33  *   @(#)$Id: tosha.c,v 1.7 1999/01/01 23:32:04 olli Exp $
34  */
35 
36 static const char cvsid[]
37     = "@(#)$Id: tosha.c,v 1.7 1999/01/01 23:32:04 olli Exp $";
38 
39 #include "utils.h"
40 #include "getlopt.h"
41 #include "global.h"
42 #include "toscsi.h"
43 #include "toform.h"
44 #include "toconf.h"
45 
46 const char *Vnumber = "0.6";
47 const char *Vdate   = "01-Jan-1999";
48 
49 /*
50  *   In case someone is actually reading this source:
51  *   Every CD DA audio sector consists of 98 frames.
52  *   Thus:
53  *
54  *      1 sample = 4 bytes    (16 bits left, 16 bits right)
55  *      1 frame  = 6 samples  (plus 1 byte subchannel info,
56  *                             but we don't need that)
57  *      1 sector = 98 frames  (= 2352 bytes = 1/75 second)
58  *      1 second = 75 sectors (= 44,100 samples = 176,400 bytes)
59  *
60  *   CD-XA mode2/form2 sectors (used for Video-CD) also consist
61  *   of 2352 bytes, 2324 of which contain the actual MPEG system
62  *   stream data.  Video-CDs use the same data rate as audio
63  *   CDs (1 sector = 1/75 second).
64  */
65 
66 int readcmd = 0x28;	/* read command to use for DA reading */
67 int byteswap = FALSE;	/* do we have to swap byte order? */
68 toscsi_handle *handle;	/* SCSI layer access handle */
69 
70 byte buf[READBUFSIZE];
71 
72 /*
73  *   Get the current density code and sector size.
74  */
75 
76 void
get_density(int * density,int * secsize)77 get_density (int *density, int *secsize)
78 {
79 	toscsi_request (handle, 12, TOSCSI_READ, "1A 0 1 0 C 0");
80 	*density = buf[4];
81 	*secsize = buf[10] << 8 | buf[11];
82 }
83 
84 /*
85  *   Set the density code and sector size.
86  */
87 
88 void
set_density(int density,int secsize)89 set_density (int density, int secsize)
90 {
91 	memset (buf, 0, 12);
92 	buf[3] = 8;
93 	buf[4] = density;
94 	buf[10] = secsize >> 8;
95 	buf[11] = secsize & 0xff;
96 	toscsi_request (handle, 12, TOSCSI_WRITE, "15 10 0 0 C 0");
97 }
98 
99 /*
100  *   Calculate minutes/seconds/sectors from a number of sectors.
101  */
102 
103 void
sectors2msf(ulong sectors,int * min,int * sec,int * frm)104 sectors2msf (ulong sectors, int *min, int *sec, int *frm)
105 {
106 	*min = sectors / (60 * 75);
107 	*sec = (sectors - *min * (60 * 75)) / 75;
108 	*frm = sectors - 75 * (*min * 60 + *sec);
109 }
110 
111 /*
112  *   Variables and defaults for command line options.
113  *   Refer to the manual page tosha(1) for more information.
114  */
115 
116 char default_tracklstr[] = "1-100";
117 char default_outname[]   = "track%02d.%s";
118 char default_device[]    = "/dev/cd0c";
119 #define DEFAULT_KBPS 128
120 const formspec *default_formatspec = format;	/* also see formatspec! */
121 
122 char *tracklstr	 = default_tracklstr;
123 char *outname 	 = default_outname;
124 char *device	 = default_device;
125 int do_reset	 = FALSE;
126 int indexonly	 = FALSE;
127 int quiet	 = FALSE;
128 int verbose	 = 0;
129 int startsec	 = -1;
130 int endsec	 = -1;
131 int kbps	 = DEFAULT_KBPS;
132 int sectorsperbuf= -1;
133 int userjitter	 = -1;
134 const formspec *formatspec = format;	/* 1st format == raw/pcm */
135 
136 void
tracklist_usage()137 tracklist_usage ()
138 {
139 	fprintf (stderr, "Track list format:  a | a,b | [a]-[b] ...\n");
140 	fprintf (stderr, "Example:  -3,5,8-10,12-\n");
141 	exit (1);
142 }
143 
144 void
tracklist_syntax()145 tracklist_syntax ()
146 {
147 	fprintf (stderr, "%s: Syntax error in track list.\n", me);
148 	tracklist_usage();
149 }
150 
151 long long starttime;		/* for estimating the remaining time */
152 ulong totalsectors = 0;		/* number of sectors we're going to read */
153 int tracklistsize = 0;		/* number of tracks to read */
154 int tracklist[100];		/* tracks numbers we're going to read */
155 ulong trackstart[101];		/* start offsets of the CD's tracks */
156 unsigned char trackctl[101];	/* type/control codes of the CD's tracks */
157 int numtracks;			/* number of tracks on the CD */
158 toconf_entry *devconfig;
159 int modechange, denschange, jitter;
160 
161 /*
162  *   Add the tracks from x to y to our tracklist.
163  */
164 
165 void
addtracks(int x,int y)166 addtracks (int x, int y)
167 {
168 	while (x <= y && tracklistsize < 100)
169 		tracklist[tracklistsize++] = x++ - 1;
170 	if (x <= y) {
171 		fprintf (stderr, "%s: Too many tracks specified (maximum is 100).\n", me);
172 		exit (1);
173 	}
174 }
175 
176 /*
177  *   Convert the track list specification (char *tracklstr)
178  *   to a sequence of track numbers (int tracklist[]).
179  */
180 
181 void
xlate_tracklist()182 xlate_tracklist ()
183 {
184 	int a = 0, b = 1;
185 	int listopen = FALSE;
186 	char *cptr = tracklstr;
187 
188 	while (*cptr)
189 		switch (*cptr) {
190 			case ',':
191 				if (listopen)
192 					addtracks (a, b);
193 				else
194 					if (b > 0)
195 						addtracks (b, b);
196 					else
197 						tracklist_syntax();
198 				listopen = FALSE;
199 				a = 0;
200 				b = 1;
201 				cptr++;
202 				break;
203 			case '-':
204 				if (listopen)
205 					tracklist_syntax();
206 				a = b;
207 				b = 100;
208 				listopen = TRUE;
209 				cptr++;
210 				break;
211 		default:
212 			if (*cptr >= '0' && *cptr <= '9') {
213 				b = 0;
214 				do {
215 					b = b * 10 + (*cptr++ - '0');
216 				} while (*cptr >= '0' && *cptr <= '9');
217 
218 			}
219 			else {
220 				fprintf (stderr, "%s: Illegal character \"%c\" in track list.\n", me, *cptr);
221 				tracklist_usage();
222 			}
223 		}
224 	if (listopen)
225 		addtracks (a, b);
226 	else
227 		if (b > 0)
228 			addtracks (b, b);
229 		else
230 			tracklist_syntax();
231 }
232 
233 char *
resolve_extension(char * template)234 resolve_extension (char *template)
235 {
236 	char *cptr, *tmpstr;
237 	int prefix;
238 
239 	if (!(cptr = strstr(template, "%s")))
240 		return template;
241 	if (!(tmpstr = malloc(strlen(template) + strlen(formatspec->ext) - 1)))
242 		out_of_memory();
243 	if ((prefix = cptr - template)) {
244 		strncpy (tmpstr, template, prefix);
245 		tmpstr[prefix] = '\0';
246 	}
247 	strcat (tmpstr + prefix, formatspec->ext);
248 	prefix += strlen(formatspec->ext);
249 	strcat (tmpstr + prefix, cptr + 2);
250 	return tmpstr;
251 }
252 
253 int
version(char * dummy)254 version (char *dummy)
255 {
256 	fprintf (stderr, "tosha v%s, %s\n", Vnumber, Vdate);
257 	fprintf (stderr, "Copyright (c) 1997-1999 by Oliver Fromme <olli@fromme.com>\n");
258 	exit (1);
259 }
260 
261 int
usage(char * dummy)262 usage (char *dummy)
263 {
264 	int i;
265 
266 	fprintf (stderr, "Usage:  %s [options]\n", me);
267 	fprintf (stderr, "Options [defaults]:\n");
268 	fprintf (stderr, "   -t track(s)   list of tracks to read   [%s]\n", default_tracklstr);
269 	fprintf (stderr, "   -o file       output file name (\"-\" = stdout)   [%s]\n", default_outname);
270 	fprintf (stderr, "   -f format     output format:");
271 	for (i = 0; format[i].name1; i++)
272 		fprintf (stderr, "%s %s", i ? "," : "", format[i].name1);
273 	fprintf (stderr, "   [%s]\n", default_formatspec->name1);
274 	fprintf (stderr, "   -d device     CD device   [%s]\n", default_device);
275 	fprintf (stderr, "   -s sector     start sector (overrides -t) [none]\n");
276 	fprintf (stderr, "   -e sector     end sector (overrides -t) [none]\n");
277 	fprintf (stderr, "   -k kbps       kbps for mp3 size estimates [%d]\n", DEFAULT_KBPS);
278 	fprintf (stderr, "   -b size       size of read buffer in sectors [device-dependant]\n");
279 	fprintf (stderr, "   -j size       size of jitter corr. overlap (0 = no JC) [device-dependant]\n");
280 	fprintf (stderr, "   -i            display track index only (-iq = compact format for scripts)\n");
281 	fprintf (stderr, "   -r            try to reset CD-ROM density and sector size, then exit\n");
282 	fprintf (stderr, "   -q            quiet (don't display anything except for errors)\n");
283 	fprintf (stderr, "   -v            verbose (display additional information, -vv = even more)\n");
284 	fprintf (stderr, "   -V            print the version number, then exit\n");
285 	exit (1);
286 }
287 
288 int
setfmt(char * fmtname)289 setfmt (char *fmtname)
290 {
291 	int i;
292 
293 	for (i = 0; format[i].name1; i++)
294 		if (strcasecmp(format[i].name1, fmtname) == 0 ||
295 		    strcasecmp(format[i].name1, fmtname) == 0) {
296 			formatspec = format + i;
297 			return 0;
298 		}
299 	usage (NULL);
300 	return -1;
301 }
302 
303 int
incverb(char * dummy)304 incverb (char *dummy)
305 {
306 	verbose++;
307 	return 0;
308 }
309 
310 /*
311  *   Definition of command line options.
312  *   See getlopt.h for more information if you're interested.
313  */
314 
315 topt opts[] = {
316 	{'t', "track",   GLO_CHAR, 0,       &tracklstr,    0},
317 	{'o', "output",  GLO_CHAR, 0,       &outname,      0},
318 	{'f', "format",  GLO_CHAR, setfmt,  0,             0},
319 	{'d', "device",  GLO_CHAR, 0,       &device,       0},
320 	{'s', "start",   GLO_NUM,  0,       &startsec,     0},
321 	{'e', "end",     GLO_NUM,  0,       &endsec,       0},
322 	{'k', "kbps",    GLO_NUM,  0,       &kbps,         0},
323 	{'b', "buffer",  GLO_NUM,  0,       &sectorsperbuf,0},
324 	{'j', "jitter",  GLO_NUM,  0,       &userjitter,   0},
325 	{'i', "index",   0,        0,       &indexonly,    TRUE},
326 	{'r', "reset",   0,        0,       &do_reset,     TRUE},
327 	{'q', "quiet",   0,        0,       &quiet,        TRUE},
328 	{'v', "verbose", 0,        incverb, 0,             0},
329 	{'V', "version", 0,        version, 0,             0},
330 	{'h', "help",    0,        usage,   0,             0},
331 	{0, 0, 0, 0, 0, 0}
332 };
333 
334 void
print_separator(void)335 print_separator (void)
336 {
337 	fprintf (stderr, "---------------------------------------------------------------------\n");
338 }
339 
340 void
print_head(void)341 print_head (void)
342 {
343 	fprintf (stderr, "\n");
344 	fprintf (stderr, " track   playing  start    end     raw size  mp3 size   # of   track\n");
345 	fprintf (stderr, " number   time    sector  sector   in bytes  %3d kbps  frames  type\n", kbps);
346 	print_separator();
347 }
348 
349 /*
350  *   Return the track mode:
351  *      -1   data
352  *       0   video
353  *       1   audio
354  *
355  *   We're declaring all mode2 tracks as "video" tracks...
356  *   This is certainly not correct, but good enough for tosha.
357  */
358 
359 int
is_audio(int track,ulong start)360 is_audio (int track, ulong start)
361 {
362 	int i, frmctl;
363 
364 	frmctl = -1;
365 	if (track > 0)
366 		frmctl = trackctl[track - 1];
367 	else {
368 		for (i = 0; i < numtracks; i++)
369 			if (start >= trackstart[i]
370 			    && start < trackstart[i + 1]) {
371 				frmctl = trackctl[i];
372 				break;
373 			}
374 		if (frmctl < 0) { /* Ugh! */
375 			if (numtracks >= 1 && start >= trackstart[1])
376 				frmctl = trackctl[numtracks - 1];
377 			else
378 				frmctl = trackctl[0];
379 		}
380 	}
381 	if ((frmctl & 4) == 0)
382 		return 1;
383 	else if ((frmctl & 3) == 2)
384 		return 0;
385 	else
386 		return -1;
387 }
388 
389 void
print_trackinfo(int track,int tracks,ulong start,ulong end1,int audio)390 print_trackinfo (int track, int tracks, ulong start, ulong end1, int audio)
391 {
392 	int min, sec, frm;
393 	int audiosectors, timesectors;
394 	static int audiototal = 0;
395 	static int timetotal = 0;
396 
397 	if (track > 0 && tracks == 1)
398 		fprintf (stderr, "  %3d  ", track);
399 	if (track == -2)
400 		timesectors = timetotal;
401 	else
402 		timesectors = (audio >= 0) ? end1 - start : 0;
403 	if (timesectors > 0) {
404 		sectors2msf (timesectors, &min, &sec, &frm);
405 		fprintf (stderr, "%3d:%02d'%02d", min, sec, frm);
406 	}
407 	else
408 		fprintf (stderr, "        -");
409 	if (track == -2) {
410 		fprintf (stderr, " %7ld sectors", end1 - start);
411 		audiosectors = audiototal;
412 	}
413 	else {
414 		fprintf (stderr, " %7ld %7ld", start, end1 - 1);
415 		audiosectors = (audio > 0) ? end1 - start : 0;
416 		audiototal += audiosectors;
417 	}
418 	fprintf (stderr, " %10ld", (long) (end1 - start) * 2352);
419 	if (audiosectors > 0)
420 		fprintf (stderr, " %9ld %7ld",
421 		    (audiosectors * (long) kbps * 5) / 3
422 			/* simple heuristic: add two mp3 frames per track */
423 			+ (kbps * (long)320 * tracks) / 49,
424 		    ((long) audiosectors * 49 + 95) / 96);
425 			/* This one doesn't work for layer-1 streams,
426 			   but who uses them anyway ...  */
427 	else
428 		fprintf (stderr, "         -       -");
429 	if (track >= -1)
430 		if (audio >= 0) {
431 			fprintf (stderr, "  %s\n",
432 			    (audio > 0) ? "audio" : "video");
433 			timetotal += end1 - start;
434 		}
435 		else
436 			fprintf (stderr, "  data\n");
437 	else
438 		fprintf (stderr, "\n");
439 }
440 
441 void
get_time(long long * tm)442 get_time (long long *tm)
443 {
444 	struct timeval tv;
445 
446 	gettimeofday (&tv, NULL);
447 	*tm = (long long)tv.tv_sec * 1000000 + tv.tv_usec;
448 }
449 
450 /*
451 int orig_density, orig_sectorsize;
452 */
453 
454 /*
455  *   Get the original density code from the drive.
456  */
457 
458 void
get_orig_density(void)459 get_orig_density (void)
460 {
461 	/*
462 	get_density (&orig_density, &orig_sectorsize);
463 #ifdef DEBUG
464 	fprintf (stderr, "Original density: code 0x%02x, sector size %d.\n",
465 	    density, sectorsize);
466 #endif
467 	*/
468 }
469 
470 /*
471  *   Reset the density code to the original value.
472  */
473 
474 void
reset_density(void)475 reset_density (void)
476 {
477 	/*
478 	set_density (orig_density, orig_sectorsize);
479 	*/
480 	set_density (0, 2048);
481 }
482 
483 /*
484  *   Set CDDA density code and sector size,
485  *   if the drive wants this.
486  */
487 
488 void
init_density(bool audio)489 init_density (bool audio)
490 {
491 	static int last_audio = 42;
492 	static bool mode_changed = FALSE;
493 
494 	if (last_audio == audio)
495 		return;
496 	last_audio = audio;
497 	if (audio) {
498 		readcmd = devconfig->readcmd;
499 		modechange = devconfig->mdchng;
500 		denschange = devconfig->density;
501 		byteswap = devconfig->swab;
502 		if (userjitter >= 0)
503 			jitter = userjitter;
504 		else
505 			jitter = devconfig->jitter;
506 	}
507 	else {
508 		readcmd = 0x28;
509 		modechange = 1;
510 		denschange = 0x00;
511 		byteswap = FALSE;	/* Don't byteswap VCD data! */
512 		if (userjitter >= 0) {
513 			jitter = userjitter;
514 			fprintf (stderr, "%s: Warning: Using non-zero "
515 			    "overlap size of %d sectors.\n", me, jitter);
516 		}
517 		else
518 			jitter = 0;	/* ignore default jitter for VCDs */
519 	}
520 	if (modechange) {
521 		set_density (denschange, 2352);
522 		mode_changed = TRUE;
523 	}
524 	else if (mode_changed) {
525 		reset_density();
526 		mode_changed = FALSE;
527 	}
528 }
529 
530 /*
531  *   Read the table of contents (TOC) from the CD.
532  */
533 
534 void
read_toc(void)535 read_toc (void)
536 {
537 	int i;
538 	int trackspp;	/* number of tracks incl. lead-out track */
539 	bool needmod2;
540 
541 	set_density (0, 2048);
542 	if (toscsi_request (handle, 2048, TOSCSI_READ,
543 	    "43 0 0 0 0 0 1 8 0 0") < 0) {
544 		fprintf (stderr, "%s: Can't read table of contents.\n", me);
545 		exit (1);
546 	}
547 	trackspp = ((buf[0] << 8 | buf[1]) - 2) / 8;
548 	if (trackspp > 101)
549 		trackspp = 101;	/* max. 100 + lead-out track */
550 	else if (trackspp < 1)
551 		trackspp = 1;
552 	numtracks = trackspp - 1;
553 	for (i = 0; i < trackspp; i++) {
554 		trackctl[i] = buf[5 + 8 * i];
555 		trackstart[i] =
556 		    buf[8 + 8 * i] << 24 | buf[9 + 8 * i] << 16 |
557 		    buf[10 + 8 * i] << 8 | buf[11 + 8 * i];
558 	}
559 	needmod2 = TRUE;
560 	for (i = 0; i < numtracks; i++)
561 		if ((trackctl[i] & 4) != 0) {
562 			if (needmod2) {
563 				set_density (0, 2352);
564 				needmod2 = FALSE;
565 			}
566 			trackctl[i] = 4
567 			    | toscsi_readmode(handle, trackstart[i]);
568 		}
569 	reset_density();
570 }
571 
572 /*
573  *   This is the ugly long main function ...
574  */
575 
576 int
main(int argc,char * argv[])577 main (int argc, char *argv[])
578 {
579 	int pcmfd = 1;	/* output file descriptor, default = 1 = stdout */
580 	long long endtime;
581 	int i;
582 
583 	char *vendor, *product, *versid;
584 	char *ofname;	/* output file name */
585 	int index;
586 	int singlefile = FALSE;
587 	int isaudio;
588 
589 	utils_init (argv[0]);
590 #ifdef DEBUG
591 	fprintf (stderr, "%s: DEBUG mode is ON.\n", me);
592 #endif
593 	if (argc <= 1)
594 		usage (NULL);
595 	toconf_readconfig();
596 	parselopts (argc, argv, opts, me);
597 	if (loptind < argc)
598 		usage (NULL);
599 	if (startsec >= 0 || endsec >= 0) {
600 		if (startsec < 0 || endsec < 0) {
601 			fprintf (stderr,
602 			    "%s: -s and -e must be used together.\n", me);
603 			exit (1);
604 		}
605 		if (startsec > endsec) {
606 			fprintf (stderr, "%s: end sector must not be "
607 			    "smaller than start sector.\n", me);
608 			exit (1);
609 		}
610 		endsec++;
611 		tracklistsize = 1;
612 	}
613 	else
614 		xlate_tracklist();
615 
616 	/*
617 	 *   Open SCSI device and initialize SCSI request structure.
618 	 */
619 
620 	handle = toscsi_open(device);
621 	if (do_reset) {
622 		if (!quiet)
623 			fprintf (stderr, "Trying to reset CD-ROM "
624 			    "density and sector size...\n");
625 		set_density (0, 2048);
626 		toscsi_close (handle);
627 		if (!quiet)
628 			fprintf (stderr, "Done.\n");
629 		exit (0);
630 	}
631 
632 	/*
633 	 *   Get vendor & product IDs.
634 	 */
635 
636 	toscsi_request (handle, 64, TOSCSI_READ, "12 0 0 0 40 0");
637 	vendor = justify(strndup((char *) buf + 8, 8));
638 	product = justify(strndup((char *) buf + 16, 16));
639 	versid = justify(strndup((char *) buf + 32, 4));
640 	if (!quiet)
641 		fprintf (stderr, "Device: %s -- \"%s\" \"%s\" \"%s\"\n",
642 			device, vendor, product, versid);
643 	devconfig = toconf_searchentry(vendor, product, versid);
644 	if (sectorsperbuf <= 0)
645 		sectorsperbuf = devconfig->blocks;
646 	else
647 		fprintf (stderr, "%s: Warning: Overriding read size "
648 		    "of %d sectors.\n", me, devconfig->blocks);
649 	if (userjitter >= 0)
650 		fprintf (stderr, "%s: Warning: Overriding overlap size "
651 		    "of %d sectors.\n", me, devconfig->jitter);
652 	if (sectorsperbuf < 1 || sectorsperbuf > MAX_SECTORSPERBUF) {
653 		fprintf (stderr, "%s: Error: Number of sectors per "
654 		    "read access must be <= %d.\n",
655 		    me, MAX_SECTORSPERBUF);
656 		exit (1);
657 	}
658 	if (userjitter >= sectorsperbuf) {
659 		fprintf (stderr, "%s: Error: Overlapping sectors "
660 		    "must be < sectors per read access (%d).\n",
661 		    me, sectorsperbuf);
662 		exit (1);
663 	}
664 	if (userjitter > 0) {
665 		fprintf (stderr, "%s: Error: Jitter correction is not "
666 		    "enabled in this version of tosha.\n", me);
667 		exit (1);
668 	}
669 
670 	/*
671 	 *   Read the table of contents (track index).
672 	 */
673 
674 	get_orig_density();
675 	read_toc();
676 
677 	/*
678 	 *   Calculate total number of sectors that we're going to read.
679 	 */
680 
681 	if (startsec >= 0)
682 		totalsectors = endsec - startsec;
683 	else {
684 		totalsectors = 0;
685 		for (index = 0; index < tracklistsize; index++) {
686 			if ((i = tracklist[index]) >= numtracks)
687 				continue;
688 			totalsectors += trackstart[i+1] - trackstart[i];
689 		}
690 	}
691 
692 	/*
693 	 *   Initialize variables for the desired output format.
694 	 */
695 
696 	if (formatspec->swapbytes)
697 		byteswap = !byteswap;
698 	outname = resolve_extension(outname);
699 
700 	/*
701 	 *   Now get us the stuff!
702 	 */
703 
704 	if (!indexonly && (singlefile = startsec >= 0 || !strchr(outname, '%'))){
705 		if (!strcmp(outname, "-"))
706 			pcmfd = 1;
707 		else {
708 			if (!quiet)
709 				fprintf (stderr,
710 					"Output file: %s\n",
711 					outname);
712 			if ((pcmfd = open(outname, O_WRONLY | O_CREAT |
713 					O_TRUNC, 0644)) < 0)
714 				die ("open(output file)");
715 		}
716 	}
717 	if (!quiet)
718 		print_head();
719 	get_time (&starttime);
720 	if (startsec >= 0) {
721 		isaudio = is_audio(-1, startsec);
722 		if (!quiet || indexonly) {
723 			fprintf (stderr, "    -  ");
724 			print_trackinfo (-1, 1, startsec, endsec, isaudio);
725 		}
726 		if (!indexonly) {
727 			if (!quiet)
728 				fprintf (stderr, "  Reading ...\r");
729 			formatspec->writeheader (
730 			    (endsec - startsec) * SECTORSIZE, pcmfd);
731 			init_density (isaudio > 0);
732 			toscsi_readsectors (handle, startsec, endsec, pcmfd);
733 			reset_density();
734 		}
735 	}
736 	else {
737 		if (singlefile && !indexonly)
738 			formatspec->writeheader (totalsectors * SECTORSIZE,
739 			    pcmfd);
740 		for (index = 0; index < tracklistsize; index++) {
741 			if ((i = tracklist[index]) >= numtracks)
742 				continue;
743 			isaudio = is_audio(i + 1, trackstart[i]);
744 			if (!quiet || indexonly)
745 				print_trackinfo (i + 1, 1, trackstart[i],
746 				    trackstart[i+1], isaudio);
747 			if (indexonly)
748 				continue;
749 			if (singlefile) {
750 				if (!quiet)
751 					fprintf (stderr, "  Reading ...\r");
752 			}
753 			else {
754 				asprintf (&ofname, outname, i+1);
755 				if (!quiet)
756 					fprintf (stderr, verbose >= 2 ?
757 						"  (output file: %s)\n" :
758 						"  Reading to %s ...\r",
759 						ofname);
760 				if ((pcmfd = open(ofname, O_WRONLY | O_CREAT |
761 						O_TRUNC, 0644)) < 0)
762 					die ("open(output file)");
763 				formatspec->writeheader ((trackstart[i+1] -
764 				    trackstart[i]) * SECTORSIZE, pcmfd);
765 			}
766 			init_density (isaudio > 0);
767 			toscsi_readsectors (handle,
768 			    trackstart[i], trackstart[i + 1], pcmfd);
769 			if (!singlefile)
770 				close (pcmfd);
771 		}
772 		reset_density();
773 		if (!quiet && tracklistsize > 1) {
774 			print_separator();
775 			fprintf (stderr, " total ");
776 			print_trackinfo (-2, tracklistsize, 0, totalsectors,
777 			    TRUE);
778 		}
779 	}
780 	if (!indexonly && singlefile && pcmfd != 1)
781 		close (pcmfd);
782 	get_time (&endtime);
783 	if (endtime <= starttime)
784 		endtime = starttime + 1;
785 	if ((!quiet || verbose) && !indexonly) {
786 		ulong tdiff = (endtime - starttime + 500) / 1000;
787 		float Kb = (float)(totalsectors * 147) / 64;
788 		float kbs = (Kb * 128) / (tdiff < 4 ? 1 : ((tdiff + 4) / 8));
789 		float speed = kbs / 176.4;
790 		if (!quiet)
791 			fprintf (stderr, "%62s\n", "");
792 		fprintf (stderr,
793 			"Transferred %.3f Mb in %.1f seconds, %.1f kb/s, speed: %.1f.\n",
794 			Kb / 1024, (float)tdiff / 1000, kbs, speed);
795 	}
796 
797 	/*
798 	 *   We're done.  Close the device.
799 	 */
800 
801 	toscsi_close (handle);
802 	exit (0);
803 }
804 
805 /* EOF */
806