1*0a3fb11dSkrw /* $OpenBSD: rip.c,v 1.21 2022/10/11 16:32:40 krw Exp $ */
24aec353bSderaadt
3a076f0c4Smjc /*
4a076f0c4Smjc * Copyright (c) 2007 Alexey Vatchenko <av@bsdua.org>
5a076f0c4Smjc *
6a076f0c4Smjc * Permission to use, copy, modify, and distribute this software for any
7a076f0c4Smjc * purpose with or without fee is hereby granted, provided that the above
8a076f0c4Smjc * copyright notice and this permission notice appear in all copies.
9a076f0c4Smjc *
10a076f0c4Smjc * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a076f0c4Smjc * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a076f0c4Smjc * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a076f0c4Smjc * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a076f0c4Smjc * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a076f0c4Smjc * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a076f0c4Smjc * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a076f0c4Smjc */
18a076f0c4Smjc #include <sys/types.h>
1961598123Sderaadt #include <sys/signal.h>
206052e43fSderaadt #include <sys/device.h>
21a076f0c4Smjc
22a076f0c4Smjc #include <sys/cdio.h>
23a076f0c4Smjc #include <sys/ioctl.h>
24a076f0c4Smjc #include <sys/scsiio.h>
25a076f0c4Smjc #include <sys/stat.h>
265eb87a29Scheloha #include <sys/time.h>
27a076f0c4Smjc
28a076f0c4Smjc #include <scsi/scsi_all.h>
29a076f0c4Smjc #include <scsi/scsi_disk.h>
30a076f0c4Smjc #include <scsi/scsiconf.h>
31a076f0c4Smjc #include <scsi/cd.h>
32a076f0c4Smjc
33a076f0c4Smjc #include <ctype.h>
34a076f0c4Smjc #include <err.h>
35a076f0c4Smjc #include <errno.h>
36a076f0c4Smjc #include <fcntl.h>
374a68a64cSjakemsr #include <sndio.h>
38a076f0c4Smjc #include <stdio.h>
39a076f0c4Smjc #include <stdlib.h>
40a076f0c4Smjc #include <string.h>
415eb87a29Scheloha #include <time.h>
42a076f0c4Smjc #include <unistd.h>
43a076f0c4Smjc
443af8cbcbSderaadt #include "extern.h"
453af8cbcbSderaadt
46a076f0c4Smjc extern int fd;
47a076f0c4Smjc extern int msf;
48a076f0c4Smjc extern struct cd_toc_entry *toc_buffer;
49a076f0c4Smjc
50a076f0c4Smjc extern u_int msf2lba(u_char m, u_char s, u_char f);
51a076f0c4Smjc extern int read_toc_entrys(int size);
52a076f0c4Smjc
53a076f0c4Smjc /*
54a076f0c4Smjc * Arguments parser
55a076f0c4Smjc */
56a076f0c4Smjc TAILQ_HEAD(track_pair_head, track_pair);
57a076f0c4Smjc
58a076f0c4Smjc static int _parse_val(char *start, char *nxt, int *val);
59a076f0c4Smjc static int _parse_pair(char *start, char *nxt, int *val1, int *val2);
60a076f0c4Smjc static int _add_pair(struct track_pair_head *head, int val1, int val2,
61a076f0c4Smjc int issorted);
62a076f0c4Smjc
63a076f0c4Smjc struct track_pair {
64a076f0c4Smjc u_char start;
65a076f0c4Smjc u_char end;
66a076f0c4Smjc TAILQ_ENTRY(track_pair) list;
67a076f0c4Smjc };
68a076f0c4Smjc
69a076f0c4Smjc void parse_tracks_init(struct track_pair_head *head);
70a076f0c4Smjc void parse_tracks_final(struct track_pair_head *head);
71a076f0c4Smjc int parse_tracks(struct track_pair_head *head, u_char first, u_char last,
72a076f0c4Smjc const char *arg, int issorted);
73a076f0c4Smjc int parse_tracks_add(struct track_pair_head *head, u_char first,
74a076f0c4Smjc u_char last, int issorted);
75a076f0c4Smjc
76a076f0c4Smjc /*
77a076f0c4Smjc * Tracks ripping
78a076f0c4Smjc */
79a076f0c4Smjc /* Header of the canonical WAVE file */
80a076f0c4Smjc static u_char wavehdr[44] = {
81a076f0c4Smjc 'R', 'I', 'F', 'F', 0x0, 0x0, 0x0, 0x0, 'W', 'A', 'V', 'E',
82a076f0c4Smjc 'f', 'm', 't', ' ', 0x10, 0x0, 0x0, 0x0, 0x1, 0x0, 0x2, 0x0,
83a076f0c4Smjc 0x44, 0xac, 0x0, 0x0, 0x10, 0xb1, 0x2, 0x0, 0x4, 0x0, 0x10, 0x0,
84a076f0c4Smjc 'd', 'a', 't', 'a', 0x0, 0x0, 0x0, 0x0
85a076f0c4Smjc };
86a076f0c4Smjc
877b34dc5fSav static int write_sector(int, u_char *, u_int32_t);
88a076f0c4Smjc
897b34dc5fSav int read_data_sector(u_int32_t, u_char *, u_int32_t);
90a076f0c4Smjc
913af8cbcbSderaadt struct track {
92a076f0c4Smjc int fd; /* descriptor of output file */
934a68a64cSjakemsr struct sio_hdl *hdl; /* sndio handle */
944a68a64cSjakemsr struct sio_par par; /* sndio parameters */
95a076f0c4Smjc u_int track; /* track number */
96a076f0c4Smjc char name[12]; /* output file name, i.e. trackXX.wav/trackXX.dat */
97a076f0c4Smjc u_char isaudio; /* true if audio track, otherwise it's data track */
98a076f0c4Smjc u_int32_t start_lba; /* starting address of this track */
99a076f0c4Smjc u_int32_t end_lba; /* starting address of the next track */
100a076f0c4Smjc };
101a076f0c4Smjc
1023af8cbcbSderaadt int read_track(struct track *);
103a076f0c4Smjc
1043af8cbcbSderaadt int rip_next_track(struct track *);
1053af8cbcbSderaadt int play_next_track(struct track *);
106a076f0c4Smjc
107a076f0c4Smjc static int rip_tracks_loop(struct track_pair *tp, u_int n_tracks,
1083af8cbcbSderaadt int (*next_track)(struct track *));
109a076f0c4Smjc
1103af8cbcbSderaadt int rip_tracks(char *arg, int (*next_track)(struct track *),
111a076f0c4Smjc int issorted);
112a076f0c4Smjc
113a076f0c4Smjc /* Next-Track function exit codes */
114a076f0c4Smjc #define NXTRACK_OK 0
115a076f0c4Smjc #define NXTRACK_FAIL 1
116a076f0c4Smjc #define NXTRACK_SKIP 2
117a076f0c4Smjc
118a076f0c4Smjc static int
_parse_val(char * start,char * nxt,int * val)119a076f0c4Smjc _parse_val(char *start, char *nxt, int *val)
120a076f0c4Smjc {
121a076f0c4Smjc char *p;
122a076f0c4Smjc int i, base, n;
123a076f0c4Smjc
124a076f0c4Smjc n = nxt - start;
125a076f0c4Smjc
126a076f0c4Smjc if (n > 3 || n < 1)
127a076f0c4Smjc return (-1);
128a076f0c4Smjc for (p = start; p < nxt; p++) {
12920ae3208Sderaadt if (!isdigit((unsigned char)*p))
130a076f0c4Smjc return (-1);
131a076f0c4Smjc }
132a076f0c4Smjc
133a076f0c4Smjc *val = 0;
134a076f0c4Smjc base = 1;
135a076f0c4Smjc for (i = 0; i < n; i++) {
136a076f0c4Smjc *val += base * (start[n - i - 1] - '0');
137a076f0c4Smjc base *= 10;
138a076f0c4Smjc }
139a076f0c4Smjc return (0);
140a076f0c4Smjc }
141a076f0c4Smjc
142a076f0c4Smjc static int
_parse_pair(char * start,char * nxt,int * val1,int * val2)143a076f0c4Smjc _parse_pair(char *start, char *nxt, int *val1, int *val2)
144a076f0c4Smjc {
145a076f0c4Smjc char *p, *delim;
146a076f0c4Smjc int error;
147a076f0c4Smjc
148a076f0c4Smjc delim = NULL;
149a076f0c4Smjc p = start;
150a076f0c4Smjc while (p < nxt) {
151a076f0c4Smjc if (*p == '-')
152a076f0c4Smjc delim = p;
153a076f0c4Smjc p++;
154a076f0c4Smjc }
155a076f0c4Smjc
156a076f0c4Smjc if (delim != NULL) {
157a076f0c4Smjc error = 0;
158a076f0c4Smjc if (delim - start < 1)
159a076f0c4Smjc *val1 = -1;
160a076f0c4Smjc else
161a076f0c4Smjc error = _parse_val(start, delim, val1);
162a076f0c4Smjc
163a076f0c4Smjc if (error == 0) {
164a076f0c4Smjc if ((nxt - delim - 1) < 1)
165a076f0c4Smjc *val2 = -1;
166a076f0c4Smjc else
167a076f0c4Smjc error = _parse_val(delim + 1, nxt, val2);
168a076f0c4Smjc }
169a076f0c4Smjc } else {
170a076f0c4Smjc error = _parse_val(start, nxt, val1);
171a076f0c4Smjc *val2 = *val1;
172a076f0c4Smjc }
173a076f0c4Smjc
174a076f0c4Smjc if (error == 0) {
175a076f0c4Smjc if (*val1 > 99 || *val2 > 99)
176a076f0c4Smjc error = -1;
177a076f0c4Smjc }
178a076f0c4Smjc
179a076f0c4Smjc return (error);
180a076f0c4Smjc }
181a076f0c4Smjc
182a076f0c4Smjc static int
_add_pair(struct track_pair_head * head,int val1,int val2,int issorted)183a076f0c4Smjc _add_pair(struct track_pair_head *head, int val1, int val2, int issorted)
184a076f0c4Smjc {
185a076f0c4Smjc u_char v1, v2, v3;
186a076f0c4Smjc struct track_pair *tp, *entry;
187a076f0c4Smjc int fix;
188a076f0c4Smjc
189a076f0c4Smjc v1 = (u_char)val1;
190a076f0c4Smjc v2 = (u_char)val2;
191a076f0c4Smjc
192a076f0c4Smjc if (issorted) {
193a076f0c4Smjc /* 1. Fix order */
194a076f0c4Smjc if (v1 > v2) {
195a076f0c4Smjc v3 = v1;
196a076f0c4Smjc v1 = v2;
197a076f0c4Smjc v2 = v3;
198a076f0c4Smjc }
199a076f0c4Smjc
200a076f0c4Smjc /* 2. Find closest range and fix it */
201a076f0c4Smjc fix = 0;
202a076f0c4Smjc TAILQ_FOREACH(entry, head, list) {
203a076f0c4Smjc if (v1 + 1 == entry->start || v1 == entry->start)
204a076f0c4Smjc fix = 1;
205a076f0c4Smjc else if (v1 > entry->start && v1 <= entry->end + 1)
206a076f0c4Smjc fix = 1;
207a076f0c4Smjc else if (v2 + 1 == entry->start || v2 == entry->start)
208a076f0c4Smjc fix = 1;
209a076f0c4Smjc else if (v2 > entry->start && v2 <= entry->end + 1)
210a076f0c4Smjc fix = 1;
211a076f0c4Smjc if (fix)
212a076f0c4Smjc break;
213a076f0c4Smjc }
214a076f0c4Smjc
215a076f0c4Smjc if (fix) {
216a076f0c4Smjc if (v1 < entry->start)
217a076f0c4Smjc entry->start = v1;
218a076f0c4Smjc if (v2 > entry->end)
219a076f0c4Smjc entry->end = v2;
220a076f0c4Smjc
221a076f0c4Smjc return (0);
222a076f0c4Smjc }
223a076f0c4Smjc }
224a076f0c4Smjc
225cfff592fSderaadt tp = malloc(sizeof(*tp));
226a076f0c4Smjc if (tp == NULL)
227a076f0c4Smjc return (-1);
228a076f0c4Smjc
229a076f0c4Smjc tp->start = v1;
230a076f0c4Smjc tp->end = v2;
231a076f0c4Smjc TAILQ_INSERT_TAIL(head, tp, list);
232a076f0c4Smjc
233a076f0c4Smjc return (0);
234a076f0c4Smjc }
235a076f0c4Smjc
236a076f0c4Smjc void
parse_tracks_init(struct track_pair_head * head)237a076f0c4Smjc parse_tracks_init(struct track_pair_head *head)
238a076f0c4Smjc {
239a076f0c4Smjc
240a076f0c4Smjc memset(head, 0, sizeof(*head));
241a076f0c4Smjc TAILQ_INIT(head);
242a076f0c4Smjc }
243a076f0c4Smjc
244a076f0c4Smjc void
parse_tracks_final(struct track_pair_head * head)245a076f0c4Smjc parse_tracks_final(struct track_pair_head *head)
246a076f0c4Smjc {
247a076f0c4Smjc struct track_pair *tp;
248a076f0c4Smjc
249abcbcc4dSdoug while ((tp = TAILQ_FIRST(head)) != NULL) {
250a076f0c4Smjc TAILQ_REMOVE(head, tp, list);
251a076f0c4Smjc free(tp);
252a076f0c4Smjc }
253a076f0c4Smjc }
254a076f0c4Smjc
255a076f0c4Smjc int
parse_tracks(struct track_pair_head * head,u_char first,u_char last,const char * arg,int issorted)256a076f0c4Smjc parse_tracks(struct track_pair_head *head, u_char first, u_char last,
257a076f0c4Smjc const char *arg, int issorted)
258a076f0c4Smjc {
259a076f0c4Smjc char *p, *nxt;
260a076f0c4Smjc int error, val1, val2;
261a076f0c4Smjc
262a076f0c4Smjc p = (char *)arg;
263a076f0c4Smjc for (;;) {
264a076f0c4Smjc /* Skip trailing spaces */
26520ae3208Sderaadt while (*p != '\0' && isspace((unsigned char)*p))
266a076f0c4Smjc ++p;
267a076f0c4Smjc if (*p == '\0')
268a076f0c4Smjc break;
269a076f0c4Smjc
270a076f0c4Smjc /* Search for the next space symbol */
271a076f0c4Smjc nxt = p;
27220ae3208Sderaadt while (*nxt != '\0' && !isspace((unsigned char)*nxt))
273a076f0c4Smjc ++nxt;
274a076f0c4Smjc /* ``nxt'' can't be equal to ``p'' here */
275a076f0c4Smjc error = _parse_pair(p, nxt, &val1, &val2);
276a076f0c4Smjc if (error != 0)
277a076f0c4Smjc break; /* parse error */
278a076f0c4Smjc
279a076f0c4Smjc if (val1 == -1)
280a076f0c4Smjc val1 = first;
281a076f0c4Smjc if (val2 == -1)
282a076f0c4Smjc val2 = last;
283a076f0c4Smjc
284a076f0c4Smjc error = _add_pair(head, val1, val2, issorted);
285a076f0c4Smjc if (error != 0)
286a076f0c4Smjc break; /* allocation error */
287a076f0c4Smjc
288a076f0c4Smjc p = nxt;
289a076f0c4Smjc }
290a076f0c4Smjc
291a076f0c4Smjc return (0);
292a076f0c4Smjc }
293a076f0c4Smjc
294a076f0c4Smjc int
parse_tracks_add(struct track_pair_head * head,u_char first,u_char last,int issorted)295a076f0c4Smjc parse_tracks_add(struct track_pair_head *head, u_char first, u_char last,
296a076f0c4Smjc int issorted)
297a076f0c4Smjc {
298a076f0c4Smjc
299a076f0c4Smjc return _add_pair(head, first, last, issorted);
300a076f0c4Smjc }
301a076f0c4Smjc
302a076f0c4Smjc static int
write_sector(int fd,u_char * sec,u_int32_t secsize)303a076f0c4Smjc write_sector(int fd, u_char *sec, u_int32_t secsize)
304a076f0c4Smjc {
305a076f0c4Smjc ssize_t res;
306a076f0c4Smjc
307a076f0c4Smjc while (secsize > 0) {
308a076f0c4Smjc res = write(fd, sec, secsize);
3093aaa63ebSderaadt if (res == -1)
310a076f0c4Smjc return (-1);
311a076f0c4Smjc
312a076f0c4Smjc sec += res;
313a076f0c4Smjc secsize -= res;
314a076f0c4Smjc }
315a076f0c4Smjc
316a076f0c4Smjc return (0);
317a076f0c4Smjc }
318a076f0c4Smjc
319a076f0c4Smjc /*
320a076f0c4Smjc * ERRORS
321a076f0c4Smjc * The function can return
322a076f0c4Smjc * [EBUSY] Device is busy.
323a076f0c4Smjc * [ETIMEDOUT] Operation timeout.
324a076f0c4Smjc * [EIO] Any other errors.
325a076f0c4Smjc * [EAGAIN] The operation must be made again. XXX - not implemented
326a076f0c4Smjc */
327a076f0c4Smjc int
read_data_sector(u_int32_t lba,u_char * sec,u_int32_t secsize)3288e55b7d8Smjc read_data_sector(u_int32_t lba, u_char *sec, u_int32_t secsize)
329a076f0c4Smjc {
330a076f0c4Smjc scsireq_t scr;
331a076f0c4Smjc u_char *cmd;
332a076f0c4Smjc int error;
333a076f0c4Smjc
334a076f0c4Smjc memset(&scr, 0, sizeof(scr));
335a076f0c4Smjc
336a076f0c4Smjc cmd = (u_char *)scr.cmd;
337a076f0c4Smjc cmd[0] = 0xbe; /* READ CD */
338a076f0c4Smjc _lto4b(lba, cmd + 2); /* Starting Logical Block Address */
339a076f0c4Smjc _lto3b(1, cmd + 6); /* Transfer Length in Blocks */
340a076f0c4Smjc cmd[9] = 0x10; /* User Data field */
341a076f0c4Smjc
342a076f0c4Smjc scr.flags = SCCMD_ESCAPE | SCCMD_READ;
343a076f0c4Smjc scr.databuf = sec;
344a076f0c4Smjc scr.datalen = secsize;
345a076f0c4Smjc scr.cmdlen = 12;
346a076f0c4Smjc scr.timeout = 120000;
347a076f0c4Smjc scr.senselen = SENSEBUFLEN;
348a076f0c4Smjc
349a076f0c4Smjc /* XXX - what's wrong with DVD? */
350a076f0c4Smjc
351a076f0c4Smjc error = ioctl(fd, SCIOCCOMMAND, &scr);
352a076f0c4Smjc if (error == -1)
353a076f0c4Smjc return (EIO);
354a076f0c4Smjc else if (scr.retsts == SCCMD_BUSY)
355a076f0c4Smjc return (EBUSY);
356a076f0c4Smjc else if (scr.retsts == SCCMD_TIMEOUT)
357a076f0c4Smjc return (ETIMEDOUT);
358a076f0c4Smjc else if (scr.retsts != SCCMD_OK)
359a076f0c4Smjc return (EIO);
360a076f0c4Smjc
361a076f0c4Smjc return (0);
362a076f0c4Smjc }
363a076f0c4Smjc
364a076f0c4Smjc int
read_track(struct track * ti)3653af8cbcbSderaadt read_track(struct track *ti)
366a076f0c4Smjc {
3675eb87a29Scheloha struct timespec ts, ots, ats;
368a076f0c4Smjc u_int32_t i, blksize, n_sec;
369a076f0c4Smjc u_char *sec;
370a076f0c4Smjc int error;
371a076f0c4Smjc
372a076f0c4Smjc n_sec = ti->end_lba - ti->start_lba;
373a076f0c4Smjc blksize = (ti->isaudio) ? 2352 : 2048;
374cfff592fSderaadt sec = malloc(blksize);
375a076f0c4Smjc if (sec == NULL)
376a076f0c4Smjc return (-1);
377a076f0c4Smjc
3785eb87a29Scheloha timespecclear(&ots);
3795eb87a29Scheloha ats.tv_sec = 1;
3805eb87a29Scheloha ats.tv_nsec = 0;
3814aec353bSderaadt
382a076f0c4Smjc for (i = 0; i < n_sec; ) {
3835eb87a29Scheloha clock_gettime(CLOCK_MONOTONIC, &ts);
3845eb87a29Scheloha if (timespeccmp(&ts, &ots, >)) {
3854aec353bSderaadt fprintf(stderr, "\rtrack %u '%c' %08u/%08u %3u%%",
3864aec353bSderaadt ti->track,
3874aec353bSderaadt (ti->isaudio) ? 'a' : 'd', i, n_sec,
3884aec353bSderaadt 100 * i / n_sec);
3895eb87a29Scheloha timespecadd(&ts, &ats, &ots);
3904aec353bSderaadt }
391a076f0c4Smjc
3928e55b7d8Smjc error = read_data_sector(i + ti->start_lba, sec, blksize);
393a076f0c4Smjc if (error == 0) {
3944a68a64cSjakemsr if (ti->fd >= 0 &&
3954a68a64cSjakemsr (write_sector(ti->fd, sec, blksize) != 0)) {
396a076f0c4Smjc free(sec);
397*0a3fb11dSkrw fprintf(stderr, "\n");
398*0a3fb11dSkrw warnx("error while writing to the %s file",
399*0a3fb11dSkrw ti->name);
400a076f0c4Smjc return (-1);
401a076f0c4Smjc }
4024a68a64cSjakemsr if (ti->hdl != NULL &&
4034a68a64cSjakemsr (sio_write(ti->hdl, sec, blksize) == 0)) {
4044a68a64cSjakemsr sio_close(ti->hdl);
4054a68a64cSjakemsr ti->hdl = NULL;
40680203c1bSkrw free(sec);
407*0a3fb11dSkrw fprintf(stderr, "\n");
408*0a3fb11dSkrw warnx("error while writing to audio output");
4094a68a64cSjakemsr return (-1);
4104a68a64cSjakemsr }
411a076f0c4Smjc
412a076f0c4Smjc i++;
413a076f0c4Smjc } else if (error != EAGAIN) {
414a076f0c4Smjc free(sec);
415*0a3fb11dSkrw fprintf(stderr, "\n");
416*0a3fb11dSkrw warnx("error while reading from device");
417a076f0c4Smjc return (-1);
418a076f0c4Smjc }
419a076f0c4Smjc }
420a076f0c4Smjc
421a076f0c4Smjc free(sec);
4224aec353bSderaadt fprintf(stderr, "\rtrack %u '%c' %08u/%08u 100%%\n",
4234aec353bSderaadt ti->track,
424a076f0c4Smjc (ti->isaudio) ? 'a' : 'd', i, n_sec);
425a076f0c4Smjc return (0);
426a076f0c4Smjc }
427a076f0c4Smjc
428a076f0c4Smjc int
rip_next_track(struct track * info)4293af8cbcbSderaadt rip_next_track(struct track *info)
430a076f0c4Smjc {
431a076f0c4Smjc int error;
432a076f0c4Smjc u_int32_t size;
433a076f0c4Smjc
434a076f0c4Smjc info->fd = open(info->name, O_CREAT | O_TRUNC | O_RDWR,
435a076f0c4Smjc S_IRUSR | S_IWUSR);
436a076f0c4Smjc if (info->fd == -1) {
437a076f0c4Smjc warnx("can't open %s file", info->name);
438a076f0c4Smjc return (NXTRACK_FAIL);
439a076f0c4Smjc }
440a076f0c4Smjc
441a076f0c4Smjc if (info->isaudio) {
442a076f0c4Smjc /*
443a076f0c4Smjc * Prepend audio track with Wave header
444a076f0c4Smjc */
445a076f0c4Smjc size = 2352 * (info->end_lba - info->start_lba);
446a076f0c4Smjc *(u_int32_t *)(wavehdr + 4) = htole32(size + 36);
447a076f0c4Smjc *(u_int32_t *)(wavehdr + 40) = htole32(size);
448a076f0c4Smjc error = write_sector(info->fd, wavehdr, sizeof(wavehdr));
449a076f0c4Smjc if (error == -1) {
450a076f0c4Smjc warnx("can't write WAVE header for %s file",
451a076f0c4Smjc info->name);
452a076f0c4Smjc return (NXTRACK_FAIL);
453a076f0c4Smjc }
454a076f0c4Smjc }
455a076f0c4Smjc
456a076f0c4Smjc return (NXTRACK_OK);
457a076f0c4Smjc }
458a076f0c4Smjc
459a076f0c4Smjc int
play_next_track(struct track * info)4603af8cbcbSderaadt play_next_track(struct track *info)
461a076f0c4Smjc {
462a076f0c4Smjc if (!info->isaudio)
463a076f0c4Smjc return (NXTRACK_SKIP);
464a076f0c4Smjc
4654a68a64cSjakemsr if (info->hdl != NULL)
4664a68a64cSjakemsr return (NXTRACK_OK);
4674a68a64cSjakemsr
46851e3cd06Sratchov info->hdl = sio_open(NULL, SIO_PLAY, 0);
4694a68a64cSjakemsr if (info->hdl == NULL) {
4704a68a64cSjakemsr warnx("could not open audio backend");
4714a68a64cSjakemsr goto bad;
472a076f0c4Smjc }
473a076f0c4Smjc
4744a68a64cSjakemsr sio_initpar(&info->par);
475a076f0c4Smjc
4764a68a64cSjakemsr info->par.rate = 44100;
4774a68a64cSjakemsr info->par.pchan = 2;
4784a68a64cSjakemsr info->par.bits = 16;
4794a68a64cSjakemsr info->par.sig = 1;
4804a68a64cSjakemsr info->par.le = 1;
48109aec223Sratchov info->par.appbufsz = info->par.rate * 3 / 4;
4824a68a64cSjakemsr
4834a68a64cSjakemsr if (sio_setpar(info->hdl, &info->par) == 0) {
4844a68a64cSjakemsr warnx("could not set audio parameters");
4854a68a64cSjakemsr goto bad;
4864a68a64cSjakemsr }
4874a68a64cSjakemsr
4884a68a64cSjakemsr if (sio_getpar(info->hdl, &info->par) == 0) {
4894a68a64cSjakemsr warnx("could not get audio parameters");
4904a68a64cSjakemsr goto bad;
4914a68a64cSjakemsr }
4924a68a64cSjakemsr
4934a68a64cSjakemsr if (info->par.le != 1 ||
4944a68a64cSjakemsr info->par.sig != 1 ||
4954a68a64cSjakemsr info->par.bits != 16 ||
4964a68a64cSjakemsr info->par.pchan != 2 ||
4974a68a64cSjakemsr (info->par.rate > 44100 * 1.05 || info->par.rate < 44100 * 0.95)) {
4984a68a64cSjakemsr warnx("could not configure audio parameters as desired");
4994a68a64cSjakemsr goto bad;
5004a68a64cSjakemsr }
5014a68a64cSjakemsr
5024a68a64cSjakemsr if (sio_start(info->hdl) == 0) {
5034a68a64cSjakemsr warnx("could not start audio output");
5044a68a64cSjakemsr goto bad;
505a076f0c4Smjc }
506a076f0c4Smjc
507a076f0c4Smjc return (NXTRACK_OK);
5084a68a64cSjakemsr
5094a68a64cSjakemsr bad:
510c6db944cSjakemsr if (info->hdl != NULL) {
5114a68a64cSjakemsr sio_close(info->hdl);
5124a68a64cSjakemsr info->hdl = NULL;
513c6db944cSjakemsr }
5144a68a64cSjakemsr return (NXTRACK_FAIL);
515a076f0c4Smjc }
516a076f0c4Smjc
517a076f0c4Smjc static int
rip_tracks_loop(struct track_pair * tp,u_int n_tracks,int (* next_track)(struct track *))518a076f0c4Smjc rip_tracks_loop(struct track_pair *tp, u_int n_tracks,
5193af8cbcbSderaadt int (*next_track)(struct track *))
520a076f0c4Smjc {
5213af8cbcbSderaadt struct track info;
522a076f0c4Smjc u_char trk;
523a076f0c4Smjc u_int i;
524a076f0c4Smjc char order;
525a076f0c4Smjc int error;
526a076f0c4Smjc
5274a68a64cSjakemsr info.fd = -1;
5284a68a64cSjakemsr info.hdl = NULL;
5294a68a64cSjakemsr
530a076f0c4Smjc order = (tp->start > tp->end) ? -1 : 1;
531a076f0c4Smjc trk = tp->start;
532a076f0c4Smjc for (;;) {
533a076f0c4Smjc error = 0;
534a076f0c4Smjc for (i = 0; i < n_tracks; i++) {
535a076f0c4Smjc if (trk == toc_buffer[i].track)
536a076f0c4Smjc break;
537a076f0c4Smjc }
538a076f0c4Smjc
539a076f0c4Smjc if (i != n_tracks) {
540a076f0c4Smjc /* Track is found */
541a076f0c4Smjc info.track = toc_buffer[i].track;
542a076f0c4Smjc info.isaudio = (toc_buffer[i].control & 4) == 0;
543a076f0c4Smjc snprintf(info.name, sizeof(info.name), "track%02u.%s",
544a076f0c4Smjc toc_buffer[i].track,
545a076f0c4Smjc (info.isaudio) ? "wav" : "dat");
546a076f0c4Smjc
547a076f0c4Smjc if (msf) {
548a076f0c4Smjc info.start_lba = msf2lba(
549a076f0c4Smjc toc_buffer[i].addr.msf.minute,
550a076f0c4Smjc toc_buffer[i].addr.msf.second,
551a076f0c4Smjc toc_buffer[i].addr.msf.frame);
552a076f0c4Smjc info.end_lba = msf2lba(
553a076f0c4Smjc toc_buffer[i + 1].addr.msf.minute,
554a076f0c4Smjc toc_buffer[i + 1].addr.msf.second,
555a076f0c4Smjc toc_buffer[i + 1].addr.msf.frame);
556a076f0c4Smjc } else {
557a076f0c4Smjc info.start_lba = toc_buffer[i].addr.lba;
558a076f0c4Smjc info.end_lba = toc_buffer[i + 1].addr.lba;
559a076f0c4Smjc }
560a076f0c4Smjc
561abd70c8fSmjc error = next_track(&info);
562068ec9bbSjakemsr if (error == NXTRACK_FAIL) {
563abd70c8fSmjc error = -1;
564abd70c8fSmjc break;
565068ec9bbSjakemsr } else if (error != NXTRACK_SKIP) {
5664a68a64cSjakemsr error = read_track(&info);
5674a68a64cSjakemsr if (info.fd >= 0) {
568a076f0c4Smjc close(info.fd);
5694a68a64cSjakemsr info.fd = -1;
5704a68a64cSjakemsr }
571a076f0c4Smjc if (error != 0) {
572*0a3fb11dSkrw warnx("can't rip track %u",
573a076f0c4Smjc toc_buffer[i].track);
574a076f0c4Smjc break;
575a076f0c4Smjc }
576a076f0c4Smjc }
577068ec9bbSjakemsr }
578a076f0c4Smjc
579a076f0c4Smjc if (trk == tp->end)
580a076f0c4Smjc break;
581a076f0c4Smjc trk += order;
582a076f0c4Smjc }
583a076f0c4Smjc
5844a68a64cSjakemsr if (info.hdl != NULL) {
5854a68a64cSjakemsr sio_close(info.hdl);
5864a68a64cSjakemsr info.hdl = NULL;
5874a68a64cSjakemsr }
5884a68a64cSjakemsr
589a076f0c4Smjc return (error);
590a076f0c4Smjc }
591a076f0c4Smjc
592a076f0c4Smjc int
rip_tracks(char * arg,int (* next_track)(struct track *),int issorted)5933af8cbcbSderaadt rip_tracks(char *arg, int (*next_track)(struct track *), int issorted)
594a076f0c4Smjc {
595a076f0c4Smjc struct track_pair_head list;
596a076f0c4Smjc struct track_pair *tp;
597a076f0c4Smjc struct ioc_toc_header h;
598a076f0c4Smjc u_int n;
599a076f0c4Smjc int rc;
600a076f0c4Smjc
601a076f0c4Smjc rc = ioctl(fd, CDIOREADTOCHEADER, &h);
6023aaa63ebSderaadt if (rc == -1)
603a076f0c4Smjc return (rc);
604a076f0c4Smjc
605a076f0c4Smjc if (h.starting_track > h.ending_track) {
606a076f0c4Smjc warnx("TOC starting_track > TOC ending_track");
607a076f0c4Smjc return (0);
608a076f0c4Smjc }
609a076f0c4Smjc
610a076f0c4Smjc n = h.ending_track - h.starting_track + 1;
611a076f0c4Smjc rc = read_toc_entrys((n + 1) * sizeof(struct cd_toc_entry));
612a076f0c4Smjc if (rc < 0)
613a076f0c4Smjc return (rc);
614a076f0c4Smjc
615a076f0c4Smjc parse_tracks_init(&list);
616a076f0c4Smjc /* We assume that all spaces are skipped in ``arg''. */
617a076f0c4Smjc if (arg == NULL || *arg == '\0') {
618a076f0c4Smjc rc = parse_tracks_add(&list, h.starting_track, h.ending_track,
619a076f0c4Smjc 0);
620a076f0c4Smjc } else {
621a076f0c4Smjc rc = parse_tracks(&list, h.starting_track, h.ending_track, arg,
622a076f0c4Smjc issorted);
623a076f0c4Smjc }
624a076f0c4Smjc if (rc < 0) {
625a076f0c4Smjc warnx("can't create track list");
626a076f0c4Smjc parse_tracks_final(&list);
627a076f0c4Smjc return (rc);
628a076f0c4Smjc }
629a076f0c4Smjc
630a076f0c4Smjc TAILQ_FOREACH(tp, &list, list) {
631a076f0c4Smjc rc = rip_tracks_loop(tp, n, next_track);
632a076f0c4Smjc if (rc < 0)
633a076f0c4Smjc break;
634a076f0c4Smjc }
635a076f0c4Smjc
636a076f0c4Smjc parse_tracks_final(&list);
637a076f0c4Smjc return (0);
638a076f0c4Smjc }
639a076f0c4Smjc
640a076f0c4Smjc int
cdrip(char * arg)641a076f0c4Smjc cdrip(char *arg)
642a076f0c4Smjc {
643a076f0c4Smjc return rip_tracks(arg, rip_next_track, 1);
644a076f0c4Smjc }
645a076f0c4Smjc
646a076f0c4Smjc int
cdplay(char * arg)647a076f0c4Smjc cdplay(char *arg)
648a076f0c4Smjc {
649a076f0c4Smjc return rip_tracks(arg, play_next_track, 0);
650a076f0c4Smjc }
651