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, §orsperbuf,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