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