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