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