Annotation of src/usr.bin/cdio/rip.c, Revision 1.17
1.17 ! cheloha 1: /* $OpenBSD: rip.c,v 1.16 2015/08/20 22:32:41 deraadt Exp $ */
1.6 deraadt 2:
1.1 mjc 3: /*
4: * Copyright (c) 2007 Alexey Vatchenko <av@bsdua.org>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18: #include <sys/types.h>
1.2 deraadt 19: #include <sys/signal.h>
1.3 deraadt 20: #include <sys/device.h>
1.1 mjc 21:
22: #include <sys/cdio.h>
23: #include <sys/ioctl.h>
24: #include <sys/scsiio.h>
25: #include <sys/stat.h>
1.17 ! cheloha 26: #include <sys/time.h>
1.1 mjc 27:
28: #include <scsi/scsi_all.h>
29: #include <scsi/scsi_disk.h>
30: #include <scsi/scsiconf.h>
31: #include <scsi/cd.h>
32:
33: #include <ctype.h>
34: #include <err.h>
35: #include <errno.h>
36: #include <fcntl.h>
1.9 jakemsr 37: #include <sndio.h>
1.1 mjc 38: #include <stdio.h>
39: #include <stdlib.h>
40: #include <string.h>
1.17 ! cheloha 41: #include <time.h>
1.1 mjc 42: #include <unistd.h>
43:
1.13 deraadt 44: #include "extern.h"
45:
1.1 mjc 46: extern int fd;
47: extern int msf;
48: extern struct cd_toc_entry *toc_buffer;
49:
50: extern u_int msf2lba(u_char m, u_char s, u_char f);
51: extern int read_toc_entrys(int size);
52:
53: /*
54: * Arguments parser
55: */
56: TAILQ_HEAD(track_pair_head, track_pair);
57:
58: static int _parse_val(char *start, char *nxt, int *val);
59: static int _parse_pair(char *start, char *nxt, int *val1, int *val2);
60: static int _add_pair(struct track_pair_head *head, int val1, int val2,
61: int issorted);
62:
63: struct track_pair {
64: u_char start;
65: u_char end;
66: TAILQ_ENTRY(track_pair) list;
67: };
68:
69: void parse_tracks_init(struct track_pair_head *head);
70: void parse_tracks_final(struct track_pair_head *head);
71: int parse_tracks(struct track_pair_head *head, u_char first, u_char last,
72: const char *arg, int issorted);
73: int parse_tracks_add(struct track_pair_head *head, u_char first,
74: u_char last, int issorted);
75:
76: /*
77: * Tracks ripping
78: */
79: /* Header of the canonical WAVE file */
80: static u_char wavehdr[44] = {
81: 'R', 'I', 'F', 'F', 0x0, 0x0, 0x0, 0x0, 'W', 'A', 'V', 'E',
82: 'f', 'm', 't', ' ', 0x10, 0x0, 0x0, 0x0, 0x1, 0x0, 0x2, 0x0,
83: 0x44, 0xac, 0x0, 0x0, 0x10, 0xb1, 0x2, 0x0, 0x4, 0x0, 0x10, 0x0,
84: 'd', 'a', 't', 'a', 0x0, 0x0, 0x0, 0x0
85: };
86:
1.8 av 87: static int write_sector(int, u_char *, u_int32_t);
1.1 mjc 88:
1.8 av 89: int read_data_sector(u_int32_t, u_char *, u_int32_t);
1.1 mjc 90:
1.13 deraadt 91: struct track {
1.1 mjc 92: int fd; /* descriptor of output file */
1.9 jakemsr 93: struct sio_hdl *hdl; /* sndio handle */
94: struct sio_par par; /* sndio parameters */
1.1 mjc 95: u_int track; /* track number */
96: char name[12]; /* output file name, i.e. trackXX.wav/trackXX.dat */
97: u_char isaudio; /* true if audio track, otherwise it's data track */
98: u_int32_t start_lba; /* starting address of this track */
99: u_int32_t end_lba; /* starting address of the next track */
100: };
101:
1.13 deraadt 102: int read_track(struct track *);
1.1 mjc 103:
1.13 deraadt 104: int rip_next_track(struct track *);
105: int play_next_track(struct track *);
1.1 mjc 106:
107: static int rip_tracks_loop(struct track_pair *tp, u_int n_tracks,
1.13 deraadt 108: int (*next_track)(struct track *));
1.1 mjc 109:
1.13 deraadt 110: int rip_tracks(char *arg, int (*next_track)(struct track *),
1.1 mjc 111: int issorted);
112:
113: /* Next-Track function exit codes */
114: #define NXTRACK_OK 0
115: #define NXTRACK_FAIL 1
116: #define NXTRACK_SKIP 2
117:
118: static int
119: _parse_val(char *start, char *nxt, int *val)
120: {
121: char *p;
122: int i, base, n;
123:
124: n = nxt - start;
125:
126: if (n > 3 || n < 1)
127: return (-1);
128: for (p = start; p < nxt; p++) {
1.14 deraadt 129: if (!isdigit((unsigned char)*p))
1.1 mjc 130: return (-1);
131: }
132:
133: *val = 0;
134: base = 1;
135: for (i = 0; i < n; i++) {
136: *val += base * (start[n - i - 1] - '0');
137: base *= 10;
138: }
139: return (0);
140: }
141:
142: static int
143: _parse_pair(char *start, char *nxt, int *val1, int *val2)
144: {
145: char *p, *delim;
146: int error;
147:
148: delim = NULL;
149: p = start;
150: while (p < nxt) {
151: if (*p == '-')
152: delim = p;
153: p++;
154: }
155:
156: if (delim != NULL) {
157: error = 0;
158: if (delim - start < 1)
159: *val1 = -1;
160: else
161: error = _parse_val(start, delim, val1);
162:
163: if (error == 0) {
164: if ((nxt - delim - 1) < 1)
165: *val2 = -1;
166: else
167: error = _parse_val(delim + 1, nxt, val2);
168: }
169: } else {
170: error = _parse_val(start, nxt, val1);
171: *val2 = *val1;
172: }
173:
174: if (error == 0) {
175: if (*val1 > 99 || *val2 > 99)
176: error = -1;
177: }
178:
179: return (error);
180: }
181:
182: static int
183: _add_pair(struct track_pair_head *head, int val1, int val2, int issorted)
184: {
185: u_char v1, v2, v3;
186: struct track_pair *tp, *entry;
187: int fix;
188:
189: v1 = (u_char)val1;
190: v2 = (u_char)val2;
191:
192: if (issorted) {
193: /* 1. Fix order */
194: if (v1 > v2) {
195: v3 = v1;
196: v1 = v2;
197: v2 = v3;
198: }
199:
200: /* 2. Find closest range and fix it */
201: fix = 0;
202: TAILQ_FOREACH(entry, head, list) {
203: if (v1 + 1 == entry->start || v1 == entry->start)
204: fix = 1;
205: else if (v1 > entry->start && v1 <= entry->end + 1)
206: fix = 1;
207: else if (v2 + 1 == entry->start || v2 == entry->start)
208: fix = 1;
209: else if (v2 > entry->start && v2 <= entry->end + 1)
210: fix = 1;
211: if (fix)
212: break;
213: }
214:
215: if (fix) {
216: if (v1 < entry->start)
217: entry->start = v1;
218: if (v2 > entry->end)
219: entry->end = v2;
220:
221: return (0);
222: }
223: }
224:
1.16 deraadt 225: tp = malloc(sizeof(*tp));
1.1 mjc 226: if (tp == NULL)
227: return (-1);
228:
229: tp->start = v1;
230: tp->end = v2;
231: TAILQ_INSERT_TAIL(head, tp, list);
232:
233: return (0);
234: }
235:
236: void
237: parse_tracks_init(struct track_pair_head *head)
238: {
239:
240: memset(head, 0, sizeof(*head));
241: TAILQ_INIT(head);
242: }
243:
244: void
245: parse_tracks_final(struct track_pair_head *head)
246: {
247: struct track_pair *tp;
248:
1.15 doug 249: while ((tp = TAILQ_FIRST(head)) != NULL) {
1.1 mjc 250: TAILQ_REMOVE(head, tp, list);
251: free(tp);
252: }
253: }
254:
255: int
256: parse_tracks(struct track_pair_head *head, u_char first, u_char last,
257: const char *arg, int issorted)
258: {
259: char *p, *nxt;
260: int error, val1, val2;
261:
262: p = (char *)arg;
263: for (;;) {
264: /* Skip trailing spaces */
1.14 deraadt 265: while (*p != '\0' && isspace((unsigned char)*p))
1.1 mjc 266: ++p;
267: if (*p == '\0')
268: break;
269:
270: /* Search for the next space symbol */
271: nxt = p;
1.14 deraadt 272: while (*nxt != '\0' && !isspace((unsigned char)*nxt))
1.1 mjc 273: ++nxt;
274: /* ``nxt'' can't be equal to ``p'' here */
275: error = _parse_pair(p, nxt, &val1, &val2);
276: if (error != 0)
277: break; /* parse error */
278:
279: if (val1 == -1)
280: val1 = first;
281: if (val2 == -1)
282: val2 = last;
283:
284: error = _add_pair(head, val1, val2, issorted);
285: if (error != 0)
286: break; /* allocation error */
287:
288: p = nxt;
289: }
290:
291: return (0);
292: }
293:
294: int
295: parse_tracks_add(struct track_pair_head *head, u_char first, u_char last,
296: int issorted)
297: {
298:
299: return _add_pair(head, first, last, issorted);
300: }
301:
302: static int
303: write_sector(int fd, u_char *sec, u_int32_t secsize)
304: {
305: ssize_t res;
306:
307: while (secsize > 0) {
308: res = write(fd, sec, secsize);
309: if (res < 0)
310: return (-1);
311:
312: sec += res;
313: secsize -= res;
314: }
315:
316: return (0);
317: }
318:
319: /*
320: * ERRORS
321: * The function can return
322: * [EBUSY] Device is busy.
323: * [ETIMEDOUT] Operation timeout.
324: * [EIO] Any other errors.
325: * [EAGAIN] The operation must be made again. XXX - not implemented
326: */
327: int
1.4 mjc 328: read_data_sector(u_int32_t lba, u_char *sec, u_int32_t secsize)
1.1 mjc 329: {
330: scsireq_t scr;
331: u_char *cmd;
332: int error;
333:
334: memset(&scr, 0, sizeof(scr));
335:
336: cmd = (u_char *)scr.cmd;
337: cmd[0] = 0xbe; /* READ CD */
338: _lto4b(lba, cmd + 2); /* Starting Logical Block Address */
339: _lto3b(1, cmd + 6); /* Transfer Length in Blocks */
340: cmd[9] = 0x10; /* User Data field */
341:
342: scr.flags = SCCMD_ESCAPE | SCCMD_READ;
343: scr.databuf = sec;
344: scr.datalen = secsize;
345: scr.cmdlen = 12;
346: scr.timeout = 120000;
347: scr.senselen = SENSEBUFLEN;
348:
349: /* XXX - what's wrong with DVD? */
350:
351: error = ioctl(fd, SCIOCCOMMAND, &scr);
352: if (error == -1)
353: return (EIO);
354: else if (scr.retsts == SCCMD_BUSY)
355: return (EBUSY);
356: else if (scr.retsts == SCCMD_TIMEOUT)
357: return (ETIMEDOUT);
358: else if (scr.retsts != SCCMD_OK)
359: return (EIO);
360:
361: return (0);
362: }
363:
364: int
1.13 deraadt 365: read_track(struct track *ti)
1.1 mjc 366: {
1.17 ! cheloha 367: struct timespec ts, ots, ats;
1.1 mjc 368: u_int32_t i, blksize, n_sec;
369: u_char *sec;
370: int error;
371:
372: n_sec = ti->end_lba - ti->start_lba;
373: blksize = (ti->isaudio) ? 2352 : 2048;
1.16 deraadt 374: sec = malloc(blksize);
1.1 mjc 375: if (sec == NULL)
376: return (-1);
377:
1.17 ! cheloha 378: timespecclear(&ots);
! 379: ats.tv_sec = 1;
! 380: ats.tv_nsec = 0;
1.6 deraadt 381:
1.1 mjc 382: for (i = 0; i < n_sec; ) {
1.17 ! cheloha 383: clock_gettime(CLOCK_MONOTONIC, &ts);
! 384: if (timespeccmp(&ts, &ots, >)) {
1.6 deraadt 385: fprintf(stderr, "\rtrack %u '%c' %08u/%08u %3u%%",
386: ti->track,
387: (ti->isaudio) ? 'a' : 'd', i, n_sec,
388: 100 * i / n_sec);
1.17 ! cheloha 389: timespecadd(&ts, &ats, &ots);
1.6 deraadt 390: }
1.1 mjc 391:
1.4 mjc 392: error = read_data_sector(i + ti->start_lba, sec, blksize);
1.1 mjc 393: if (error == 0) {
1.9 jakemsr 394: if (ti->fd >= 0 &&
395: (write_sector(ti->fd, sec, blksize) != 0)) {
1.1 mjc 396: free(sec);
397: warnx("\nerror while writing to the %s file",
398: ti->name);
399: return (-1);
400: }
1.9 jakemsr 401: if (ti->hdl != NULL &&
402: (sio_write(ti->hdl, sec, blksize) == 0)) {
403: sio_close(ti->hdl);
404: ti->hdl = NULL;
405: warnx("\nerror while writing to audio output");
406: return (-1);
407: }
1.1 mjc 408:
409: i++;
410: } else if (error != EAGAIN) {
411: free(sec);
412: warnx("\nerror while reading from device");
413: return (-1);
414: }
415: }
416:
417: free(sec);
1.6 deraadt 418: fprintf(stderr, "\rtrack %u '%c' %08u/%08u 100%%\n",
419: ti->track,
1.1 mjc 420: (ti->isaudio) ? 'a' : 'd', i, n_sec);
421: return (0);
422: }
423:
424: int
1.13 deraadt 425: rip_next_track(struct track *info)
1.1 mjc 426: {
427: int error;
428: u_int32_t size;
429:
430: info->fd = open(info->name, O_CREAT | O_TRUNC | O_RDWR,
431: S_IRUSR | S_IWUSR);
432: if (info->fd == -1) {
433: warnx("can't open %s file", info->name);
434: return (NXTRACK_FAIL);
435: }
436:
437: if (info->isaudio) {
438: /*
439: * Prepend audio track with Wave header
440: */
441: size = 2352 * (info->end_lba - info->start_lba);
442: *(u_int32_t *)(wavehdr + 4) = htole32(size + 36);
443: *(u_int32_t *)(wavehdr + 40) = htole32(size);
444: error = write_sector(info->fd, wavehdr, sizeof(wavehdr));
445: if (error == -1) {
446: warnx("can't write WAVE header for %s file",
447: info->name);
448: return (NXTRACK_FAIL);
449: }
450: }
451:
452: return (NXTRACK_OK);
453: }
454:
455: int
1.13 deraadt 456: play_next_track(struct track *info)
1.1 mjc 457: {
458: if (!info->isaudio)
459: return (NXTRACK_SKIP);
460:
1.9 jakemsr 461: if (info->hdl != NULL)
462: return (NXTRACK_OK);
463:
1.12 ratchov 464: info->hdl = sio_open(NULL, SIO_PLAY, 0);
1.9 jakemsr 465: if (info->hdl == NULL) {
466: warnx("could not open audio backend");
467: goto bad;
468: }
469:
470: sio_initpar(&info->par);
471:
472: info->par.rate = 44100;
473: info->par.pchan = 2;
474: info->par.bits = 16;
475: info->par.sig = 1;
476: info->par.le = 1;
1.11 ratchov 477: info->par.appbufsz = info->par.rate * 3 / 4;
1.9 jakemsr 478:
479: if (sio_setpar(info->hdl, &info->par) == 0) {
480: warnx("could not set audio parameters");
481: goto bad;
482: }
483:
484: if (sio_getpar(info->hdl, &info->par) == 0) {
485: warnx("could not get audio parameters");
486: goto bad;
487: }
488:
489: if (info->par.le != 1 ||
490: info->par.sig != 1 ||
491: info->par.bits != 16 ||
492: info->par.pchan != 2 ||
493: (info->par.rate > 44100 * 1.05 || info->par.rate < 44100 * 0.95)) {
494: warnx("could not configure audio parameters as desired");
495: goto bad;
1.1 mjc 496: }
497:
1.9 jakemsr 498: if (sio_start(info->hdl) == 0) {
499: warnx("could not start audio output");
500: goto bad;
1.1 mjc 501: }
502:
503: return (NXTRACK_OK);
1.9 jakemsr 504:
505: bad:
1.10 jakemsr 506: if (info->hdl != NULL) {
507: sio_close(info->hdl);
508: info->hdl = NULL;
509: }
1.9 jakemsr 510: return (NXTRACK_FAIL);
1.1 mjc 511: }
512:
513: static int
514: rip_tracks_loop(struct track_pair *tp, u_int n_tracks,
1.13 deraadt 515: int (*next_track)(struct track *))
1.1 mjc 516: {
1.13 deraadt 517: struct track info;
1.1 mjc 518: u_char trk;
519: u_int i;
520: char order;
521: int error;
522:
1.9 jakemsr 523: info.fd = -1;
524: info.hdl = NULL;
525:
1.1 mjc 526: order = (tp->start > tp->end) ? -1 : 1;
527: trk = tp->start;
528: for (;;) {
529: error = 0;
530: for (i = 0; i < n_tracks; i++) {
531: if (trk == toc_buffer[i].track)
532: break;
533: }
534:
535: if (i != n_tracks) {
536: /* Track is found */
537: info.track = toc_buffer[i].track;
538: info.isaudio = (toc_buffer[i].control & 4) == 0;
539: snprintf(info.name, sizeof(info.name), "track%02u.%s",
540: toc_buffer[i].track,
541: (info.isaudio) ? "wav" : "dat");
542:
543: if (msf) {
544: info.start_lba = msf2lba(
545: toc_buffer[i].addr.msf.minute,
546: toc_buffer[i].addr.msf.second,
547: toc_buffer[i].addr.msf.frame);
548: info.end_lba = msf2lba(
549: toc_buffer[i + 1].addr.msf.minute,
550: toc_buffer[i + 1].addr.msf.second,
551: toc_buffer[i + 1].addr.msf.frame);
552: } else {
553: info.start_lba = toc_buffer[i].addr.lba;
554: info.end_lba = toc_buffer[i + 1].addr.lba;
1.5 mjc 555: }
556:
557: error = next_track(&info);
1.7 jakemsr 558: if (error == NXTRACK_FAIL) {
1.5 mjc 559: error = -1;
560: break;
1.7 jakemsr 561: } else if (error != NXTRACK_SKIP) {
1.9 jakemsr 562: error = read_track(&info);
563: if (info.fd >= 0) {
564: close(info.fd);
565: info.fd = -1;
566: }
1.7 jakemsr 567: if (error != 0) {
568: warnx("can't rip %u track",
569: toc_buffer[i].track);
570: break;
571: }
1.1 mjc 572: }
573: }
574:
575: if (trk == tp->end)
576: break;
577: trk += order;
1.9 jakemsr 578: }
579:
580: if (info.hdl != NULL) {
581: sio_close(info.hdl);
582: info.hdl = NULL;
1.1 mjc 583: }
584:
585: return (error);
586: }
587:
588: int
1.13 deraadt 589: rip_tracks(char *arg, int (*next_track)(struct track *), int issorted)
1.1 mjc 590: {
591: struct track_pair_head list;
592: struct track_pair *tp;
593: struct ioc_toc_header h;
594: u_int n;
595: int rc;
596:
597: rc = ioctl(fd, CDIOREADTOCHEADER, &h);
598: if (rc < 0)
599: return (rc);
600:
601: if (h.starting_track > h.ending_track) {
602: warnx("TOC starting_track > TOC ending_track");
603: return (0);
604: }
605:
606: n = h.ending_track - h.starting_track + 1;
607: rc = read_toc_entrys((n + 1) * sizeof(struct cd_toc_entry));
608: if (rc < 0)
609: return (rc);
610:
611: parse_tracks_init(&list);
612: /* We assume that all spaces are skipped in ``arg''. */
613: if (arg == NULL || *arg == '\0') {
614: rc = parse_tracks_add(&list, h.starting_track, h.ending_track,
615: 0);
616: } else {
617: rc = parse_tracks(&list, h.starting_track, h.ending_track, arg,
618: issorted);
619: }
620: if (rc < 0) {
621: warnx("can't create track list");
622: parse_tracks_final(&list);
623: return (rc);
624: }
625:
626: TAILQ_FOREACH(tp, &list, list) {
627: rc = rip_tracks_loop(tp, n, next_track);
628: if (rc < 0)
629: break;
630: }
631:
632: parse_tracks_final(&list);
633: return (0);
634: }
635:
636: int
637: cdrip(char *arg)
638: {
639: return rip_tracks(arg, rip_next_track, 1);
640: }
641:
642: int
643: cdplay(char *arg)
644: {
645: return rip_tracks(arg, play_next_track, 0);
646: }