xref: /openbsd-src/usr.bin/cdio/rip.c (revision 0a3fb11d8f988dff9bc7c6d306eb14f8a6d4e6d0)
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