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