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