1 /* $OpenBSD: rip.c,v 1.18 2019/06/28 13:35:00 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 #include <sys/time.h> 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> 37 #include <sndio.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <time.h> 42 #include <unistd.h> 43 44 #include "extern.h" 45 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 87 static int write_sector(int, u_char *, u_int32_t); 88 89 int read_data_sector(u_int32_t, u_char *, u_int32_t); 90 91 struct track { 92 int fd; /* descriptor of output file */ 93 struct sio_hdl *hdl; /* sndio handle */ 94 struct sio_par par; /* sndio parameters */ 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 102 int read_track(struct track *); 103 104 int rip_next_track(struct track *); 105 int play_next_track(struct track *); 106 107 static int rip_tracks_loop(struct track_pair *tp, u_int n_tracks, 108 int (*next_track)(struct track *)); 109 110 int rip_tracks(char *arg, int (*next_track)(struct track *), 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++) { 129 if (!isdigit((unsigned char)*p)) 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 225 tp = malloc(sizeof(*tp)); 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 249 while ((tp = TAILQ_FIRST(head)) != NULL) { 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 */ 265 while (*p != '\0' && isspace((unsigned char)*p)) 266 ++p; 267 if (*p == '\0') 268 break; 269 270 /* Search for the next space symbol */ 271 nxt = p; 272 while (*nxt != '\0' && !isspace((unsigned char)*nxt)) 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); 309 if (res == -1) 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 328 read_data_sector(u_int32_t lba, u_char *sec, u_int32_t secsize) 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 365 read_track(struct track *ti) 366 { 367 struct timespec ts, ots, ats; 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; 374 sec = malloc(blksize); 375 if (sec == NULL) 376 return (-1); 377 378 timespecclear(&ots); 379 ats.tv_sec = 1; 380 ats.tv_nsec = 0; 381 382 for (i = 0; i < n_sec; ) { 383 clock_gettime(CLOCK_MONOTONIC, &ts); 384 if (timespeccmp(&ts, &ots, >)) { 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); 389 timespecadd(&ts, &ats, &ots); 390 } 391 392 error = read_data_sector(i + ti->start_lba, sec, blksize); 393 if (error == 0) { 394 if (ti->fd >= 0 && 395 (write_sector(ti->fd, sec, blksize) != 0)) { 396 free(sec); 397 warnx("\nerror while writing to the %s file", 398 ti->name); 399 return (-1); 400 } 401 if (ti->hdl != NULL && 402 (sio_write(ti->hdl, sec, blksize) == 0)) { 403 sio_close(ti->hdl); 404 ti->hdl = NULL; 405 warnx("\nerror while writing to audio output"); 406 return (-1); 407 } 408 409 i++; 410 } else if (error != EAGAIN) { 411 free(sec); 412 warnx("\nerror while reading from device"); 413 return (-1); 414 } 415 } 416 417 free(sec); 418 fprintf(stderr, "\rtrack %u '%c' %08u/%08u 100%%\n", 419 ti->track, 420 (ti->isaudio) ? 'a' : 'd', i, n_sec); 421 return (0); 422 } 423 424 int 425 rip_next_track(struct track *info) 426 { 427 int error; 428 u_int32_t size; 429 430 info->fd = open(info->name, O_CREAT | O_TRUNC | O_RDWR, 431 S_IRUSR | S_IWUSR); 432 if (info->fd == -1) { 433 warnx("can't open %s file", info->name); 434 return (NXTRACK_FAIL); 435 } 436 437 if (info->isaudio) { 438 /* 439 * Prepend audio track with Wave header 440 */ 441 size = 2352 * (info->end_lba - info->start_lba); 442 *(u_int32_t *)(wavehdr + 4) = htole32(size + 36); 443 *(u_int32_t *)(wavehdr + 40) = htole32(size); 444 error = write_sector(info->fd, wavehdr, sizeof(wavehdr)); 445 if (error == -1) { 446 warnx("can't write WAVE header for %s file", 447 info->name); 448 return (NXTRACK_FAIL); 449 } 450 } 451 452 return (NXTRACK_OK); 453 } 454 455 int 456 play_next_track(struct track *info) 457 { 458 if (!info->isaudio) 459 return (NXTRACK_SKIP); 460 461 if (info->hdl != NULL) 462 return (NXTRACK_OK); 463 464 info->hdl = sio_open(NULL, SIO_PLAY, 0); 465 if (info->hdl == NULL) { 466 warnx("could not open audio backend"); 467 goto bad; 468 } 469 470 sio_initpar(&info->par); 471 472 info->par.rate = 44100; 473 info->par.pchan = 2; 474 info->par.bits = 16; 475 info->par.sig = 1; 476 info->par.le = 1; 477 info->par.appbufsz = info->par.rate * 3 / 4; 478 479 if (sio_setpar(info->hdl, &info->par) == 0) { 480 warnx("could not set audio parameters"); 481 goto bad; 482 } 483 484 if (sio_getpar(info->hdl, &info->par) == 0) { 485 warnx("could not get audio parameters"); 486 goto bad; 487 } 488 489 if (info->par.le != 1 || 490 info->par.sig != 1 || 491 info->par.bits != 16 || 492 info->par.pchan != 2 || 493 (info->par.rate > 44100 * 1.05 || info->par.rate < 44100 * 0.95)) { 494 warnx("could not configure audio parameters as desired"); 495 goto bad; 496 } 497 498 if (sio_start(info->hdl) == 0) { 499 warnx("could not start audio output"); 500 goto bad; 501 } 502 503 return (NXTRACK_OK); 504 505 bad: 506 if (info->hdl != NULL) { 507 sio_close(info->hdl); 508 info->hdl = NULL; 509 } 510 return (NXTRACK_FAIL); 511 } 512 513 static int 514 rip_tracks_loop(struct track_pair *tp, u_int n_tracks, 515 int (*next_track)(struct track *)) 516 { 517 struct track info; 518 u_char trk; 519 u_int i; 520 char order; 521 int error; 522 523 info.fd = -1; 524 info.hdl = NULL; 525 526 order = (tp->start > tp->end) ? -1 : 1; 527 trk = tp->start; 528 for (;;) { 529 error = 0; 530 for (i = 0; i < n_tracks; i++) { 531 if (trk == toc_buffer[i].track) 532 break; 533 } 534 535 if (i != n_tracks) { 536 /* Track is found */ 537 info.track = toc_buffer[i].track; 538 info.isaudio = (toc_buffer[i].control & 4) == 0; 539 snprintf(info.name, sizeof(info.name), "track%02u.%s", 540 toc_buffer[i].track, 541 (info.isaudio) ? "wav" : "dat"); 542 543 if (msf) { 544 info.start_lba = msf2lba( 545 toc_buffer[i].addr.msf.minute, 546 toc_buffer[i].addr.msf.second, 547 toc_buffer[i].addr.msf.frame); 548 info.end_lba = msf2lba( 549 toc_buffer[i + 1].addr.msf.minute, 550 toc_buffer[i + 1].addr.msf.second, 551 toc_buffer[i + 1].addr.msf.frame); 552 } else { 553 info.start_lba = toc_buffer[i].addr.lba; 554 info.end_lba = toc_buffer[i + 1].addr.lba; 555 } 556 557 error = next_track(&info); 558 if (error == NXTRACK_FAIL) { 559 error = -1; 560 break; 561 } else if (error != NXTRACK_SKIP) { 562 error = read_track(&info); 563 if (info.fd >= 0) { 564 close(info.fd); 565 info.fd = -1; 566 } 567 if (error != 0) { 568 warnx("can't rip %u track", 569 toc_buffer[i].track); 570 break; 571 } 572 } 573 } 574 575 if (trk == tp->end) 576 break; 577 trk += order; 578 } 579 580 if (info.hdl != NULL) { 581 sio_close(info.hdl); 582 info.hdl = NULL; 583 } 584 585 return (error); 586 } 587 588 int 589 rip_tracks(char *arg, int (*next_track)(struct track *), int issorted) 590 { 591 struct track_pair_head list; 592 struct track_pair *tp; 593 struct ioc_toc_header h; 594 u_int n; 595 int rc; 596 597 rc = ioctl(fd, CDIOREADTOCHEADER, &h); 598 if (rc == -1) 599 return (rc); 600 601 if (h.starting_track > h.ending_track) { 602 warnx("TOC starting_track > TOC ending_track"); 603 return (0); 604 } 605 606 n = h.ending_track - h.starting_track + 1; 607 rc = read_toc_entrys((n + 1) * sizeof(struct cd_toc_entry)); 608 if (rc < 0) 609 return (rc); 610 611 parse_tracks_init(&list); 612 /* We assume that all spaces are skipped in ``arg''. */ 613 if (arg == NULL || *arg == '\0') { 614 rc = parse_tracks_add(&list, h.starting_track, h.ending_track, 615 0); 616 } else { 617 rc = parse_tracks(&list, h.starting_track, h.ending_track, arg, 618 issorted); 619 } 620 if (rc < 0) { 621 warnx("can't create track list"); 622 parse_tracks_final(&list); 623 return (rc); 624 } 625 626 TAILQ_FOREACH(tp, &list, list) { 627 rc = rip_tracks_loop(tp, n, next_track); 628 if (rc < 0) 629 break; 630 } 631 632 parse_tracks_final(&list); 633 return (0); 634 } 635 636 int 637 cdrip(char *arg) 638 { 639 return rip_tracks(arg, rip_next_track, 1); 640 } 641 642 int 643 cdplay(char *arg) 644 { 645 return rip_tracks(arg, play_next_track, 0); 646 } 647