Imported Upstream version 1.1.11
[platform/upstream/cdrkit.git] / wodim / cdtext.c
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  *
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 }