Annotation of src/usr.bin/cdio/rip.c, Revision 1.20
1.20 ! krw 1: /* $OpenBSD: rip.c,v 1.19 2022/10/07 19:59:19 krw 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);
1.18 deraadt 309: if (res == -1)
1.1 mjc 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);
1.19 krw 397: fprintf(stderr, "\nerror while writing to the "
398: "%s file\n", ti->name);
1.1 mjc 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;
1.20 ! krw 405: free(sec);
1.19 krw 406: fprintf(stderr, "\nerror while writing to audio "
407: "output\n");
1.9 jakemsr 408: return (-1);
409: }
1.1 mjc 410:
411: i++;
412: } else if (error != EAGAIN) {
413: free(sec);
1.19 krw 414: fprintf(stderr, "\nerror while reading from device\n");
1.1 mjc 415: return (-1);
416: }
417: }
418:
419: free(sec);
1.6 deraadt 420: fprintf(stderr, "\rtrack %u '%c' %08u/%08u 100%%\n",
421: ti->track,
1.1 mjc 422: (ti->isaudio) ? 'a' : 'd', i, n_sec);
423: return (0);
424: }
425:
426: int
1.13 deraadt 427: rip_next_track(struct track *info)
1.1 mjc 428: {
429: int error;
430: u_int32_t size;
431:
432: info->fd = open(info->name, O_CREAT | O_TRUNC | O_RDWR,
433: S_IRUSR | S_IWUSR);
434: if (info->fd == -1) {
435: warnx("can't open %s file", info->name);
436: return (NXTRACK_FAIL);
437: }
438:
439: if (info->isaudio) {
440: /*
441: * Prepend audio track with Wave header
442: */
443: size = 2352 * (info->end_lba - info->start_lba);
444: *(u_int32_t *)(wavehdr + 4) = htole32(size + 36);
445: *(u_int32_t *)(wavehdr + 40) = htole32(size);
446: error = write_sector(info->fd, wavehdr, sizeof(wavehdr));
447: if (error == -1) {
448: warnx("can't write WAVE header for %s file",
449: info->name);
450: return (NXTRACK_FAIL);
451: }
452: }
453:
454: return (NXTRACK_OK);
455: }
456:
457: int
1.13 deraadt 458: play_next_track(struct track *info)
1.1 mjc 459: {
460: if (!info->isaudio)
461: return (NXTRACK_SKIP);
462:
1.9 jakemsr 463: if (info->hdl != NULL)
464: return (NXTRACK_OK);
465:
1.12 ratchov 466: info->hdl = sio_open(NULL, SIO_PLAY, 0);
1.9 jakemsr 467: if (info->hdl == NULL) {
468: warnx("could not open audio backend");
469: goto bad;
470: }
471:
472: sio_initpar(&info->par);
473:
474: info->par.rate = 44100;
475: info->par.pchan = 2;
476: info->par.bits = 16;
477: info->par.sig = 1;
478: info->par.le = 1;
1.11 ratchov 479: info->par.appbufsz = info->par.rate * 3 / 4;
1.9 jakemsr 480:
481: if (sio_setpar(info->hdl, &info->par) == 0) {
482: warnx("could not set audio parameters");
483: goto bad;
484: }
485:
486: if (sio_getpar(info->hdl, &info->par) == 0) {
487: warnx("could not get audio parameters");
488: goto bad;
489: }
490:
491: if (info->par.le != 1 ||
492: info->par.sig != 1 ||
493: info->par.bits != 16 ||
494: info->par.pchan != 2 ||
495: (info->par.rate > 44100 * 1.05 || info->par.rate < 44100 * 0.95)) {
496: warnx("could not configure audio parameters as desired");
497: goto bad;
1.1 mjc 498: }
499:
1.9 jakemsr 500: if (sio_start(info->hdl) == 0) {
501: warnx("could not start audio output");
502: goto bad;
1.1 mjc 503: }
504:
505: return (NXTRACK_OK);
1.9 jakemsr 506:
507: bad:
1.10 jakemsr 508: if (info->hdl != NULL) {
509: sio_close(info->hdl);
510: info->hdl = NULL;
511: }
1.9 jakemsr 512: return (NXTRACK_FAIL);
1.1 mjc 513: }
514:
515: static int
516: rip_tracks_loop(struct track_pair *tp, u_int n_tracks,
1.13 deraadt 517: int (*next_track)(struct track *))
1.1 mjc 518: {
1.13 deraadt 519: struct track info;
1.1 mjc 520: u_char trk;
521: u_int i;
522: char order;
523: int error;
524:
1.9 jakemsr 525: info.fd = -1;
526: info.hdl = NULL;
527:
1.1 mjc 528: order = (tp->start > tp->end) ? -1 : 1;
529: trk = tp->start;
530: for (;;) {
531: error = 0;
532: for (i = 0; i < n_tracks; i++) {
533: if (trk == toc_buffer[i].track)
534: break;
535: }
536:
537: if (i != n_tracks) {
538: /* Track is found */
539: info.track = toc_buffer[i].track;
540: info.isaudio = (toc_buffer[i].control & 4) == 0;
541: snprintf(info.name, sizeof(info.name), "track%02u.%s",
542: toc_buffer[i].track,
543: (info.isaudio) ? "wav" : "dat");
544:
545: if (msf) {
546: info.start_lba = msf2lba(
547: toc_buffer[i].addr.msf.minute,
548: toc_buffer[i].addr.msf.second,
549: toc_buffer[i].addr.msf.frame);
550: info.end_lba = msf2lba(
551: toc_buffer[i + 1].addr.msf.minute,
552: toc_buffer[i + 1].addr.msf.second,
553: toc_buffer[i + 1].addr.msf.frame);
554: } else {
555: info.start_lba = toc_buffer[i].addr.lba;
556: info.end_lba = toc_buffer[i + 1].addr.lba;
1.5 mjc 557: }
558:
559: error = next_track(&info);
1.7 jakemsr 560: if (error == NXTRACK_FAIL) {
1.5 mjc 561: error = -1;
562: break;
1.7 jakemsr 563: } else if (error != NXTRACK_SKIP) {
1.9 jakemsr 564: error = read_track(&info);
565: if (info.fd >= 0) {
566: close(info.fd);
567: info.fd = -1;
568: }
1.7 jakemsr 569: if (error != 0) {
1.19 krw 570: fprintf(stderr, "can't rip track %u\n",
1.7 jakemsr 571: toc_buffer[i].track);
572: break;
573: }
1.1 mjc 574: }
575: }
576:
577: if (trk == tp->end)
578: break;
579: trk += order;
1.9 jakemsr 580: }
581:
582: if (info.hdl != NULL) {
583: sio_close(info.hdl);
584: info.hdl = NULL;
1.1 mjc 585: }
586:
587: return (error);
588: }
589:
590: int
1.13 deraadt 591: rip_tracks(char *arg, int (*next_track)(struct track *), int issorted)
1.1 mjc 592: {
593: struct track_pair_head list;
594: struct track_pair *tp;
595: struct ioc_toc_header h;
596: u_int n;
597: int rc;
598:
599: rc = ioctl(fd, CDIOREADTOCHEADER, &h);
1.18 deraadt 600: if (rc == -1)
1.1 mjc 601: return (rc);
602:
603: if (h.starting_track > h.ending_track) {
604: warnx("TOC starting_track > TOC ending_track");
605: return (0);
606: }
607:
608: n = h.ending_track - h.starting_track + 1;
609: rc = read_toc_entrys((n + 1) * sizeof(struct cd_toc_entry));
610: if (rc < 0)
611: return (rc);
612:
613: parse_tracks_init(&list);
614: /* We assume that all spaces are skipped in ``arg''. */
615: if (arg == NULL || *arg == '\0') {
616: rc = parse_tracks_add(&list, h.starting_track, h.ending_track,
617: 0);
618: } else {
619: rc = parse_tracks(&list, h.starting_track, h.ending_track, arg,
620: issorted);
621: }
622: if (rc < 0) {
623: warnx("can't create track list");
624: parse_tracks_final(&list);
625: return (rc);
626: }
627:
628: TAILQ_FOREACH(tp, &list, list) {
629: rc = rip_tracks_loop(tp, n, next_track);
630: if (rc < 0)
631: break;
632: }
633:
634: parse_tracks_final(&list);
635: return (0);
636: }
637:
638: int
639: cdrip(char *arg)
640: {
641: return rip_tracks(arg, rip_next_track, 1);
642: }
643:
644: int
645: cdplay(char *arg)
646: {
647: return rip_tracks(arg, play_next_track, 0);
648: }