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