1 /* @(#)cdtext.c	1.20 11/04/03 Copyright 1999-2011 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)cdtext.c	1.20 11/04/03 Copyright 1999-2011 J. Schilling";
6 #endif
7 /*
8  *	Generic CD-Text support functions
9  *
10  *	Copyright (c) 1999-2011 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include <schily/mconfig.h>
27 #include <schily/stdio.h>
28 #include <schily/stdlib.h>
29 #include <schily/unistd.h>	/* Include sys/types.h to make off_t available */
30 #include <schily/standard.h>
31 #include <schily/utypes.h>
32 #include <schily/string.h>
33 #include <schily/schily.h>
34 #include <schily/nlsdefs.h>
35 
36 #include <scg/scsitransp.h>	/* For write_leadin() */
37 
38 #include "cdtext.h"
39 #include "cdrecord.h"
40 #include "crc16.h"
41 
42 #define	PTI_TITLE	0x80	/* Album name and Track titles */
43 #define	PTI_PERFORMER	0x81	/* Singer/player/conductor/orchestra */
44 #define	PTI_SONGWRITER	0x82	/* Name of the songwriter */
45 #define	PTI_COMPOSER	0x83	/* Name of the composer */
46 #define	PTI_ARRANGER	0x84	/* Name of the arranger */
47 #define	PTI_MESSAGE	0x85	/* Message from content provider or artist */
48 #define	PTI_DISK_ID	0x86	/* Disk identification information */
49 #define	PTI_GENRE	0x87	/* Genre identification / information */
50 #define	PTI_TOC		0x88	/* TOC information */
51 #define	PTI_TOC2	0x89	/* Second TOC */
52 #define	PTI_RES_8A	0x8A	/* Reserved 8A */
53 #define	PTI_RES_8B	0x8B	/* Reserved 8B */
54 #define	PTI_RES_8C	0x8C	/* Reserved 8C */
55 #define	PTI_CLOSED_INFO	0x8D	/* For internal use by content provider */
56 #define	PTI_ISRC	0x8E	/* UPC/EAN code of album and ISRC for tracks */
57 #define	PTI_SIZE	0x8F	/* Size information of the block */
58 
59 extern	int	xdebug;
60 
61 typedef struct textpack {
62 	Uchar	pack_type;	/* Pack Type indicator	*/
63 	char	track_no;	/* Track Number (0..99)	*/
64 	char	seq_number;	/* Sequence Number	*/
65 	char	block_number;	/* Block # / Char pos	*/
66 	char	text[12];	/* CD-Text Data field	*/
67 	char	crc[2];		/* CRC 16		*/
68 } txtpack_t;
69 
70 #define	EXT_DATA 0x80		/* Extended data indicator in track_no */
71 #define	DBCC	 0x80		/* Double byte char indicator in block */
72 
73 /*
74  *	CD-Text size example:
75  *
76  *	0  1  2  3  00 01 02 03 04 05 06 07 08 09 10 11 CRC16
77  *
78  *	8F 00 2B 00 01 01 0D 03 0C 0C 00 00 00 00 01 00 7B 3D
79  *	8F 01 2C 00 00 00 00 00 00 00 12 03 2D 00 00 00 DA B7
80  *	8F 02 2D 00 00 00 00 00 09 00 00 00 00 00 00 00 6A 24
81  *
82  *	charcode 1
83  *	first tr 1
84  *	last tr  13
85  *	Copyr	 3
86  *	Pack Count 80= 12, 81 = 12, 86 = 1, 8e = 18, 8f = 3
87  *	last seq   0 = 2d
88  *	languages  0 = 9
89  */
90 
91 typedef struct textsizes {
92 	char	charcode;
93 	char	first_track;
94 	char	last_track;
95 	char	copyr_flags;
96 	char	pack_count[16];
97 	char	last_seqnum[8];
98 	char	language_codes[8];
99 } txtsize_t;
100 
101 typedef struct textargs {
102 	txtpack_t	*tp;
103 	char		*p;
104 	txtsize_t	*tsize;
105 	txtpack_t	*endp;
106 	int		seqno;
107 } txtarg_t;
108 
109 
110 EXPORT	Uchar	*textsub;
111 EXPORT	int	textlen;
112 
113 EXPORT	BOOL	checktextfile	__PR((char *fname));
114 LOCAL	void	setuptextdata	__PR((Uchar *bp, int len));
115 LOCAL	BOOL	cdtext_crc_ok	__PR((struct textpack *p));
116 EXPORT	void	packtext	__PR((int tracks, track_t *trackp));
117 LOCAL	BOOL	anytext		__PR((int pack_type, int tracks, track_t *trackp));
118 LOCAL	void	fillup_pack	__PR((txtarg_t *ap));
119 LOCAL	void	fillpacks	__PR((txtarg_t *ap, char *from, int len, int track_no, int pack_type));
120 EXPORT	int	write_cdtext	__PR((SCSI *scgp, cdr_t *dp, long startsec));
121 LOCAL	void	eight2six	__PR((Uchar *in, Uchar *out));
122 #ifdef	__needed__
123 LOCAL	void	six2eight	__PR((Uchar *in, Uchar *out));
124 #endif
125 
126 
127 EXPORT BOOL
checktextfile(fname)128 checktextfile(fname)
129 	char	*fname;
130 {
131 	FILE	*f;
132 	Uchar	hbuf[4];
133 	Uchar	*bp;
134 	struct textpack *tp;
135 	int	len;
136 	int	crc;
137 	int	n;
138 	int	j;
139 	off_t	fs;
140 
141 	if ((f = fileopen(fname, "rb")) == NULL) {
142 		errmsg(_("Cannot open '%s'.\n"), fname);
143 		return (FALSE);
144 	}
145 	fs = filesize(f);
146 	if (fs == (off_t)0) {
147 		errmsgno(EX_BAD, _("Empty CD-Text file.\n"));
148 		fclose(f);
149 		return (FALSE);
150 	}
151 	j = fs % sizeof (struct textpack);
152 	if (j == 4) {
153 		n = fileread(f, hbuf, 4);
154 		if (n != 4) {
155 			if (n < 0)
156 				errmsg(_("Cannot read '%s'.\n"), fname);
157 			else
158 				errmsgno(EX_BAD, _("File '%s' is too small for CD-Text.\n"), fname);
159 			fclose(f);
160 			return (FALSE);
161 		}
162 		len = hbuf[0] * 256 + hbuf[1];
163 		len -= 2;
164 		n = fs - 4;
165 		if (n != len) {
166 			errmsgno(EX_BAD, _("Inconsistent CD-Text file '%s' length should be %d but is %lld\n"),
167 				fname, len+4, (Llong)fs);
168 			fclose(f);
169 			return (FALSE);
170 		}
171 	} else if (j != 0) {
172 		errmsgno(EX_BAD, _("Inconsistent CD-Text file '%s' not a multiple of pack length\n"),
173 			fname);
174 		fclose(f);
175 		return (FALSE);
176 	} else {
177 		len = fs;
178 	}
179 	printf(_("Text len: %d\n"), len);
180 	bp = malloc(len);
181 	if (bp == NULL) {
182 		errmsg(_("Cannot malloc CD-Text read buffer.\n"));
183 		fclose(f);
184 		return (FALSE);
185 	}
186 	n = fileread(f, bp, len);
187 
188 	tp = (struct textpack *)bp;
189 	for (n = 0; n < len; n += sizeof (struct textpack), tp++) {
190 		if (tp->pack_type < 0x80 || tp->pack_type > 0x8F) {
191 			errmsgno(EX_BAD, _("Illegal pack type 0x%02X pack #%ld in CD-Text file '%s'.\n"),
192 				tp->pack_type, (long)(n/sizeof (struct textpack)), fname);
193 			fclose(f);
194 			return (FALSE);
195 		}
196 		crc = (tp->crc[0] & 0xFF) << 8 | (tp->crc[1] & 0xFF);
197 		crc ^= 0xFFFF;
198 		if (crc != calcCRC((Uchar *)tp, sizeof (*tp)-2)) {
199 			if (cdtext_crc_ok(tp)) {
200 				errmsgno(EX_BAD,
201 				_("Corrected CRC ERROR in pack #%ld (offset %d-%ld) in CD-Text file '%s'.\n"),
202 				(long)(n/sizeof (struct textpack)),
203 				n+j, (long)(n+j+sizeof (struct textpack)),
204 				fname);
205 			} else {
206 			errmsgno(EX_BAD, _("CRC ERROR in pack #%ld (offset %d-%ld) in CD-Text file '%s'.\n"),
207 				(long)(n/sizeof (struct textpack)),
208 				n+j, (long)(n+j+sizeof (struct textpack)),
209 				fname);
210 			fclose(f);
211 			return (FALSE);
212 			}
213 		}
214 	}
215 	setuptextdata(bp, len);
216 	free(bp);
217 	fclose(f);
218 
219 	if (textlen == 0 || textsub == NULL)
220 		return (FALSE);
221 	return (TRUE);
222 }
223 
224 LOCAL void
setuptextdata(bp,len)225 setuptextdata(bp, len)
226 	Uchar	*bp;
227 	int	len;
228 {
229 	int	n;
230 	int	i;
231 	int	j;
232 	Uchar	*p;
233 
234 	if (xdebug) {
235 		printf(_("%ld packs %% 4 = %ld\n"),
236 			(long)(len/sizeof (struct textpack)),
237 			(long)(len/sizeof (struct textpack)) % 4);
238 	}
239 	if (len == 0) {
240 		errmsgno(EX_BAD, _("No CD-Text data found.\n"));
241 		return;
242 	}
243 	i = (len/sizeof (struct textpack)) % 4;
244 	if (i == 0) {
245 		n = len;
246 	} else if (i == 2) {
247 		n = 2 * len;
248 	} else {
249 		n = 4 * len;
250 	}
251 	n = (n * 4) / 3;
252 	p = malloc(n);
253 	if (p == NULL) {
254 		errmsg(_("Cannot malloc CD-Text write buffer.\n"));
255 		return;
256 	}
257 	for (i = 0, j = 0; j < n; ) {
258 		eight2six(&bp[i%len], &p[j]);
259 		i += 3;
260 		j += 4;
261 	}
262 	textsub = p;
263 	textlen = n;
264 
265 #ifdef	DEBUG
266 	{
267 	Uchar	sbuf[10000];
268 	struct textpack *tp;
269 	FILE		*f;
270 	int		crc;
271 
272 	tp = (struct textpack *)bp;
273 	p = sbuf;
274 	for (n = 0; n < len; n += sizeof (struct textpack), tp++) {
275 		crc = (tp->crc[0] & 0xFF) << 8 | (tp->crc[1] & 0xFF);
276 		crc ^= 0xFFFF;
277 
278 		printf("Pack:%3d ", n/ sizeof (struct textpack));
279 		printf("Pack type: %02X ", tp->pack_type & 0xFF);
280 		printf("Track #: %2d ", tp->track_no & 0xFF);
281 		printf("Sequence #:%3d ", tp->seq_number & 0xFF);
282 		printf("Block #:%3d ", tp->block_number & 0xFF);
283 		printf("CRC: %04X (%04X) ", crc, calcCRC((Uchar *)tp, sizeof (*tp)-2));
284 		printf("Text: '%.12s'\n", tp->text);
285 		movebytes(tp->text, p, 12);
286 		p += 12;
287 	}
288 	printf("len total: %d\n", n);
289 	f = fileopen("cdtext.out", "wctb");
290 	if (f) {
291 		filewrite(f, sbuf, p - sbuf);
292 		fflush(f);
293 		fclose(f);
294 	}
295 	}
296 #endif
297 }
298 
299 LOCAL BOOL
cdtext_crc_ok(p)300 cdtext_crc_ok(p)
301 	struct textpack *p;
302 {
303 	int		crc;
304 	struct textpack	new;
305 
306 	movebytes(p, &new, sizeof (struct textpack));
307 	new.crc[0] ^= 0xFF;
308 	new.crc[1] ^= 0xFF;
309 	crc = calcCRC((Uchar *)&new, sizeof (struct textpack));
310 	crc = flip_crc_error_corr((Uchar *)&new, sizeof (struct textpack), crc);
311 	new.crc[0] ^= 0xFF;
312 	new.crc[1] ^= 0xFF;
313 	if (crc == 0)
314 		movebytes(&new, p, 18);
315 
316 	return (crc == 0);
317 }
318 
319 
320 EXPORT void
packtext(tracks,trackp)321 packtext(tracks, trackp)
322 	int	tracks;
323 	track_t	*trackp;
324 {
325 	int	type;
326 	int	i;
327 	struct textpack *tp;
328 	struct textsizes tsize;
329 	txtarg_t targ;
330 	char	sbuf[256*18];	/* Sufficient for a single language block */
331 				/* Max 8 languages in total... */
332 
333 	fillbytes(sbuf, sizeof (sbuf), 0);
334 	fillbytes(&tsize, sizeof (tsize), 0);
335 
336 	tsize.charcode		= CC_8859_1;		/* ISO-8859-1	    */
337 	tsize.first_track	= trackp[1].trackno;
338 	tsize.last_track	= trackp[1].trackno + tracks - 1;
339 #ifdef	__FOUND_ON_COMMERCIAL_CD__
340 	tsize.copyr_flags	= 3;			/* for titles/names */
341 #else
342 	tsize.copyr_flags	= 0;			/* no Copyr. limitat. */
343 #endif
344 	tsize.pack_count[0x0F]	= 3;			/* 3 size packs	    */
345 	tsize.last_seqnum[0]	= 0;			/* Start value only */
346 	tsize.language_codes[0]	= LANG_ENGLISH;		/* English	    */
347 
348 	tp = (struct textpack *)sbuf;
349 
350 	targ.tp = tp;
351 	targ.p = NULL;
352 	targ.tsize = &tsize;
353 	targ.endp = (struct textpack *)&sbuf[256 * sizeof (struct textpack)];
354 	targ.seqno = 0;
355 
356 	for (type = 0; type <= 0x0E; type++) {
357 		register int	maxtrk;
358 		register char	*s;
359 
360 		if (!anytext(type, tracks, trackp))
361 			continue;
362 		maxtrk = tsize.last_track;
363 		if (type == 6) {
364 			maxtrk = 0;
365 		}
366 		for (i = 0; i <= maxtrk; i++) {
367 			s = trackp[i].text;
368 			if (s)
369 				s = ((textptr_t *)s)->textcodes[type];
370 			if (s)
371 				fillpacks(&targ, s, strlen(s)+1, i, 0x80| type);
372 			else
373 				fillpacks(&targ, "", 1, i, 0x80| type);
374 
375 		}
376 		fillup_pack(&targ);
377 	}
378 
379 	/*
380 	 * targ.seqno overshoots by one and we add 3 size packs...
381 	 */
382 	tsize.last_seqnum[0] = targ.seqno + 2;
383 
384 	for (i = 0; i < 3; i++) {
385 		fillpacks(&targ, &((char *)(&tsize))[i*12], 12, i, 0x8f);
386 	}
387 
388 	setuptextdata((Uchar *)sbuf, targ.seqno*18);
389 
390 #ifdef	DEBUG
391 	{	FILE	*f;
392 
393 	f = fileopen("cdtext.new", "wctb");
394 	if (f) {
395 		filewrite(f, sbuf, targ.seqno*18);
396 		fflush(f);
397 		fclose(f);
398 	}
399 	}
400 #endif
401 }
402 
403 LOCAL BOOL
anytext(pack_type,tracks,trackp)404 anytext(pack_type, tracks, trackp)
405 	int		pack_type;
406 	int	tracks;
407 	track_t	*trackp;
408 {
409 	register int	i;
410 	register char	*p;
411 
412 	for (i = 0; i <= tracks; i++) {
413 		if (trackp[i].text == NULL)
414 			continue;
415 		p = ((textptr_t *)(trackp[i].text))->textcodes[pack_type];
416 		if (p != NULL && *p != '\0')
417 			return (TRUE);
418 	}
419 	return (FALSE);
420 }
421 
422 LOCAL void
fillup_pack(ap)423 fillup_pack(ap)
424 	register txtarg_t *ap;
425 {
426 	if (ap->p) {
427 		fillbytes(ap->p, &ap->tp->text[12] - ap->p, '\0');
428 		fillcrc((Uchar *)ap->tp, sizeof (*ap->tp));
429 		ap->p  = 0;
430 		ap->tp++;
431 	}
432 }
433 
434 LOCAL void
fillpacks(ap,from,len,track_no,pack_type)435 fillpacks(ap, from, len, track_no, pack_type)
436 	register txtarg_t	*ap;
437 	register char		*from;
438 	register int		len;
439 	register int		track_no;
440 	register int		pack_type;
441 {
442 	register int		charpos;
443 	register char		*p;
444 	register txtpack_t	*tp;
445 
446 	tp = ap->tp;
447 	p  = ap->p;
448 	charpos = 0;
449 	do {
450 		if (tp >= ap->endp) {
451 			comerrno(EX_BAD,
452 				_("CD-Text size overflow in track %d.\n"),
453 				track_no);
454 		}
455 		if (p == 0) {
456 			p = tp->text;
457 			tp->pack_type = pack_type;
458 			if (pack_type != 0x8f)
459 				ap->tsize->pack_count[pack_type & 0x0F]++;
460 			tp->track_no = track_no;
461 			tp->seq_number = ap->seqno++;
462 			if (charpos < 15)
463 				tp->block_number = charpos;
464 			else
465 				tp->block_number = 15;
466 		}
467 		for (; --len >= 0 && p < &tp->text[12]; charpos++) {
468 			*p++ = *from++;
469 		}
470 		len++;	/* Overshoot compensation */
471 
472 		if (p >= &tp->text[12]) {
473 			fillcrc((Uchar *)tp, sizeof (*tp));
474 			p = 0;
475 			tp++;
476 		}
477 	} while (len > 0);
478 
479 	ap->tp = tp;
480 	ap->p = p;
481 }
482 
483 EXPORT int
write_cdtext(scgp,dp,startsec)484 write_cdtext(scgp, dp, startsec)
485 	SCSI	*scgp;
486 	cdr_t	*dp;
487 	long	startsec;
488 {
489 	char	*bp = (char *)textsub;
490 	int	buflen = textlen;
491 	long	amount;
492 	long	bytes = 0;
493 	long	end = -150;
494 	int	secspt = textlen / 96;
495 	int	bytespt = textlen;
496 	long	maxdma = scgp->maxbuf;
497 	int	idx;
498 	int	secs;
499 	int	nbytes;
500 
501 	if (textlen <= 0)
502 		return (-1);
503 
504 /*maxdma = 4320;*/
505 	if (maxdma >= (2*textlen)) {
506 		/*
507 		 * Try to make each CD-Text transfer use as much data
508 		 * as possible.
509 		 */
510 		bp = scgp->bufptr;
511 		for (idx = 0; (idx + textlen) <= maxdma; idx += textlen)
512 			movebytes(textsub, &bp[idx], textlen);
513 		buflen = idx;
514 		secspt = buflen / 96;
515 		bytespt = buflen;
516 /*printf("textlen: %d buflen: %d secspt: %d\n", textlen, buflen, secspt);*/
517 	} else if (maxdma < buflen) {
518 		/*
519 		 * We have more CD-Text data than we may transfer at once.
520 		 */
521 		secspt = maxdma / 96;
522 		bytespt = secspt * 96;
523 	}
524 	while (startsec < end) {
525 		if ((end - startsec) < secspt) {
526 			secspt = end - startsec;
527 			bytespt = secspt * 96;
528 		}
529 		idx = 0;
530 		secs = secspt;
531 		nbytes = bytespt;
532 		do {			/* loop over CD-Text data buffer */
533 
534 			if ((idx + nbytes) > buflen) {
535 				nbytes = buflen - idx;
536 				secs = nbytes / 96;
537 			}
538 /*printf("idx: %d nbytes: %d secs: %d startsec: %ld\n",*/
539 /*idx, nbytes, secs, startsec);*/
540 			amount = write_secs(scgp, dp,
541 				(char *)&bp[idx], startsec, nbytes, secs, FALSE);
542 			if (amount < 0) {
543 				printf(_("write CD-Text data: error after %ld bytes\n"),
544 						bytes);
545 				return (-1);
546 			}
547 			bytes += amount;
548 			idx += amount;
549 			startsec += secs;
550 		} while (idx < buflen && startsec < end);
551 	}
552 	return (0);
553 }
554 
555 
556 /*
557  * 3 input bytes (8 bit based) are converted into 4 output bytes (6 bit based).
558  */
559 LOCAL void
eight2six(in,out)560 eight2six(in, out)
561 	register Uchar	*in;
562 	register Uchar	*out;
563 {
564 	register int	c;
565 
566 	c = in[0];
567 	out[0]  = (c >> 2) & 0x3F;
568 	out[1]  = (c & 0x03) << 4;
569 
570 	c = in[1];
571 	out[1] |= (c & 0xF0) >> 4;
572 	out[2]  = (c & 0x0F) << 2;
573 
574 	c = in[2];
575 	out[2] |= (c & 0xC0) >> 6;
576 	out[3]  = c & 0x3F;
577 }
578 
579 #ifdef	__needed__
580 /*
581  * 4 input bytes (6 bit based) are converted into 3 output bytes (8 bit based).
582  */
583 LOCAL void
six2eight(in,out)584 six2eight(in, out)
585 	register Uchar	*in;
586 	register Uchar	*out;
587 {
588 	register int	c;
589 
590 	c = in[0] & 0x3F;
591 	out[0]  = c << 2;
592 
593 	c = in[1] & 0x3F;
594 	out[0] |= c >> 4;
595 	out[1]  = c << 4;
596 
597 	c = in[2] & 0x3F;
598 	out[1] |= c >> 2;
599 	out[2]  = c << 6;
600 
601 	c = in[3] & 0x3F;
602 	out[2] |= c;
603 }
604 #endif
605