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