summaryrefslogtreecommitdiff
path: root/wodim/cdtext.c
diff options
context:
space:
mode:
Diffstat (limited to 'wodim/cdtext.c')
-rw-r--r--wodim/cdtext.c546
1 files changed, 546 insertions, 0 deletions
diff --git a/wodim/cdtext.c b/wodim/cdtext.c
new file mode 100644
index 0000000..40df2a5
--- /dev/null
+++ b/wodim/cdtext.c
@@ -0,0 +1,546 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)cdtext.c 1.10 04/03/01 Copyright 1999-2004 J. Schilling */
+/*
+ * Generic CD-Text support functions
+ *
+ * Copyright (c) 1999-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <stdxlib.h>
+#include <unixstd.h> /* Include sys/types.h to make off_t available */
+#include <standard.h>
+#include <utypes.h>
+#include <strdefs.h>
+#include <schily.h>
+
+#include <usal/scsitransp.h> /* For write_leadin() */
+
+#include "cdtext.h"
+#include "wodim.h"
+#include "crc16.h"
+
+#define PTI_TITLE 0x80 /* Album name and Track titles */
+#define PTI_PERFORMER 0x81 /* Singer/player/conductor/orchestra */
+#define PTI_SONGWRITER 0x82 /* Name of the songwriter */
+#define PTI_COMPOSER 0x83 /* Name of the composer */
+#define PTI_ARRANGER 0x84 /* Name of the arranger */
+#define PTI_MESSAGE 0x85 /* Message from content provider or artist */
+#define PTI_DISK_ID 0x86 /* Disk identification information */
+#define PTI_GENRE 0x87 /* Genre identification / information */
+#define PTI_TOC 0x88 /* TOC information */
+#define PTI_TOC2 0x89 /* Second TOC */
+#define PTI_RES_8A 0x8A /* Reserved 8A */
+#define PTI_RES_8B 0x8B /* Reserved 8B */
+#define PTI_RES_8C 0x8C /* Reserved 8C */
+#define PTI_CLOSED_INFO 0x8D /* For internal use by content provider */
+#define PTI_ISRC 0x8E /* UPC/EAN code of album and ISRC for tracks */
+#define PTI_SIZE 0x8F /* Size information of the block */
+
+extern int xdebug;
+
+typedef struct textpack {
+ Uchar pack_type; /* Pack Type indicator */
+ char track_no; /* Track Number (0..99) */
+ char seq_number; /* Sequence Number */
+ char block_number; /* Block # / Char pos */
+ char text[12]; /* CD-Text Data field */
+ char crc[2]; /* CRC 16 */
+} txtpack_t;
+
+#define EXT_DATA 0x80 /* Extended data indicator in track_no */
+#define DBCC 0x80 /* Double byte char indicator in block */
+
+/*
+ * CD-Text size example:
+ *
+ * 0 1 2 3 00 01 02 03 04 05 06 07 08 09 10 11 CRC16
+ *
+ * 8F 00 2B 00 01 01 0D 03 0C 0C 00 00 00 00 01 00 7B 3D
+ * 8F 01 2C 00 00 00 00 00 00 00 12 03 2D 00 00 00 DA B7
+ * 8F 02 2D 00 00 00 00 00 09 00 00 00 00 00 00 00 6A 24
+ *
+ * charcode 1
+ * first tr 1
+ * last tr 13
+ * Copyr 3
+ * Pack Count 80= 12, 81 = 12, 86 = 1, 8e = 18, 8f = 3
+ * last seq 0 = 2d
+ * languages 0 = 9
+ */
+
+typedef struct textsizes {
+ char charcode;
+ char first_track;
+ char last_track;
+ char copyr_flags;
+ char pack_count[16];
+ char last_seqnum[8];
+ char language_codes[8];
+} txtsize_t;
+
+typedef struct textargs {
+ txtpack_t *tp;
+ char *p;
+ txtsize_t *tsize;
+ int seqno;
+} txtarg_t;
+
+
+Uchar *textsub;
+int textlen;
+
+BOOL checktextfile(char *fname);
+static void setuptextdata(Uchar *bp, int len);
+static BOOL cdtext_crc_ok(struct textpack *p);
+void packtext(int tracks, track_t *trackp);
+static BOOL anytext(int pack_type, int tracks, track_t *trackp);
+static void fillup_pack(txtarg_t *ap);
+static void fillpacks(txtarg_t *ap, char *from, int len, int track_no, int pack_type);
+int write_cdtext(SCSI *usalp, cdr_t *dp, long startsec);
+static void eight2six(Uchar *in, Uchar *out);
+static void six2eight(Uchar *in, Uchar *out);
+
+
+BOOL checktextfile(char *fname)
+{
+ FILE *f;
+ Uchar hbuf[4];
+ Uchar *bp;
+ struct textpack *tp;
+ int len;
+ int crc;
+ int n;
+ int j;
+ off_t fs;
+
+ if ((f = fileopen(fname, "rb")) == NULL) {
+ errmsg("Cannot open '%s'.\n", fname);
+ return (FALSE);
+ }
+ fs = filesize(f);
+ j = fs % sizeof (struct textpack);
+ if (j == 4) {
+ n = fileread(f, hbuf, 4);
+ if (n != 4) {
+ if (n < 0)
+ errmsg("Cannot read '%s'.\n", fname);
+ else
+ errmsgno(EX_BAD, "File '%s' is too small for CD-Text.\n", fname);
+ return (FALSE);
+ }
+ len = hbuf[0] * 256 + hbuf[1];
+ len -= 2;
+ n = fs - 4;
+ if (n != len) {
+ errmsgno(EX_BAD, "Inconsistent CD-Text file '%s' length should be %d but is %lld\n",
+ fname, len+4, (Llong)fs);
+ return (FALSE);
+ }
+ } else if (j != 0) {
+ errmsgno(EX_BAD, "Inconsistent CD-Text file '%s' not a multiple of pack length\n",
+ fname);
+ return (FALSE);
+ } else {
+ len = fs;
+ }
+ printf("Text len: %d\n", len);
+ bp = malloc(len);
+ if (bp == NULL) {
+ errmsg("Cannot malloc CD-Text read buffer.\n");
+ return (FALSE);
+ }
+ n = fileread(f, bp, len);
+
+ tp = (struct textpack *)bp;
+ for (n = 0; n < len; n += sizeof (struct textpack), tp++) {
+ if (tp->pack_type < 0x80 || tp->pack_type > 0x8F) {
+ errmsgno(EX_BAD, "Illegal pack type 0x%02X pack #%ld in CD-Text file '%s'.\n",
+ tp->pack_type, (long)(n/sizeof (struct textpack)), fname);
+ return (FALSE);
+ }
+ crc = (tp->crc[0] & 0xFF) << 8 | (tp->crc[1] & 0xFF);
+ crc ^= 0xFFFF;
+ if (crc != calcCRC((Uchar *)tp, sizeof (*tp)-2)) {
+ if (cdtext_crc_ok(tp)) {
+ errmsgno(EX_BAD,
+ "Corrected CRC ERROR in pack #%ld (offset %d-%ld) in CD-Text file '%s'.\n",
+ (long)(n/sizeof (struct textpack)),
+ n+j, (long)(n+j+sizeof (struct textpack)),
+ fname);
+ } else {
+ errmsgno(EX_BAD, "CRC ERROR in pack #%ld (offset %d-%ld) in CD-Text file '%s'.\n",
+ (long)(n/sizeof (struct textpack)),
+ n+j, (long)(n+j+sizeof (struct textpack)),
+ fname);
+ return (FALSE);
+ }
+ }
+ }
+ setuptextdata(bp, len);
+ free(bp);
+
+ return (TRUE);
+}
+
+static void setuptextdata(Uchar *bp, int len)
+{
+ int n;
+ int i;
+ int j;
+ Uchar *p;
+
+ if (xdebug) {
+ printf("%ld packs %% 4 = %ld\n",
+ (long)(len/sizeof (struct textpack)),
+ (long)(len/sizeof (struct textpack)) % 4);
+ }
+ i = (len/sizeof (struct textpack)) % 4;
+ if (i == 0) {
+ n = len;
+ } else if (i == 2) {
+ n = 2 * len;
+ } else {
+ n = 4 * len;
+ }
+ n = (n * 4) / 3;
+ p = malloc(n);
+ if (p == NULL) {
+ errmsg("Cannot malloc CD-Text write buffer.\n");
+ }
+ for (i = 0, j = 0; j < n; ) {
+ eight2six(&bp[i%len], &p[j]);
+ i += 3;
+ j += 4;
+ }
+ textsub = p;
+ textlen = n;
+
+#ifdef DEBUG
+ {
+ Uchar sbuf[10000];
+ struct textpack *tp;
+ FILE *f;
+ int crc;
+
+ tp = (struct textpack *)bp;
+ p = sbuf;
+ for (n = 0; n < len; n += sizeof (struct textpack), tp++) {
+ crc = (tp->crc[0] & 0xFF) << 8 | (tp->crc[1] & 0xFF);
+ crc ^= 0xFFFF;
+
+ printf("Pack:%3d ", n/ sizeof (struct textpack));
+ printf("Pack type: %02X ", tp->pack_type & 0xFF);
+ printf("Track #: %2d ", tp->track_no & 0xFF);
+ printf("Sequence #:%3d ", tp->seq_number & 0xFF);
+ printf("Block #:%3d ", tp->block_number & 0xFF);
+ printf("CRC: %04X (%04X) ", crc, calcCRC((Uchar *)tp, sizeof (*tp)-2));
+ printf("Text: '%.12s'\n", tp->text);
+ movebytes(tp->text, p, 12);
+ p += 12;
+ }
+ printf("len total: %d\n", n);
+ f = fileopen("cdtext.out", "wctb");
+ if (f) {
+ filewrite(f, sbuf, p - sbuf);
+ fflush(f);
+ fclose(f);
+ }
+ }
+#endif
+}
+
+static BOOL cdtext_crc_ok(struct textpack *p)
+{
+ int crc;
+ struct textpack new;
+
+ movebytes(p, &new, sizeof (struct textpack));
+ new.crc[0] ^= 0xFF;
+ new.crc[1] ^= 0xFF;
+ crc = calcCRC((Uchar *)&new, sizeof (struct textpack));
+ crc = flip_crc_error_corr((Uchar *)&new, sizeof (struct textpack), crc);
+ new.crc[0] ^= 0xFF;
+ new.crc[1] ^= 0xFF;
+ if (crc == 0)
+ movebytes(&new, p, 18);
+
+ return (crc == 0);
+}
+
+
+void packtext(int tracks, track_t *trackp)
+{
+ int type;
+ int i;
+ struct textpack *tp;
+ struct textsizes tsize;
+ txtarg_t targ;
+ char sbuf[256*18];
+
+ fillbytes(sbuf, sizeof (sbuf), 0);
+ fillbytes(&tsize, sizeof (tsize), 0);
+
+ tsize.charcode = CC_8859_1; /* ISO-8859-1 */
+ tsize.first_track = trackp[1].trackno;
+ tsize.last_track = trackp[1].trackno + tracks - 1;
+#ifdef __FOUND_ON_COMMERCIAL_CD__
+ tsize.copyr_flags = 3; /* for titles/names */
+#else
+ tsize.copyr_flags = 0; /* no Copyr. limitat. */
+#endif
+ tsize.pack_count[0x0F] = 3; /* 3 size packs */
+ tsize.last_seqnum[0] = 0; /* Start value only */
+ tsize.language_codes[0] = LANG_ENGLISH; /* English */
+
+ tp = (struct textpack *)sbuf;
+
+ targ.tp = tp;
+ targ.p = NULL;
+ targ.tsize = &tsize;
+ targ.seqno = 0;
+
+ for (type = 0; type <= 0x0E; type++) {
+ register int maxtrk;
+ register char *s;
+
+ if (!anytext(type, tracks, trackp))
+ continue;
+ maxtrk = tsize.last_track;
+ if (type == 6) {
+ maxtrk = 0;
+ }
+ for (i = 0; i <= maxtrk; i++) {
+ s = trackp[i].text;
+ if (s)
+ s = ((textptr_t *)s)->textcodes[type];
+ if (s)
+ fillpacks(&targ, s, strlen(s)+1, i, 0x80| type);
+ else
+ fillpacks(&targ, "", 1, i, 0x80| type);
+
+ }
+ fillup_pack(&targ);
+ }
+
+ /*
+ * targ.seqno overshoots by one and we add 3 size packs...
+ */
+ tsize.last_seqnum[0] = targ.seqno + 2;
+
+ for (i = 0; i < 3; i++) {
+ fillpacks(&targ, &((char *)(&tsize))[i*12], 12, i, 0x8f);
+ }
+
+ setuptextdata((Uchar *)sbuf, targ.seqno*18);
+
+#ifdef DEBUG
+ { FILE *f;
+
+ f = fileopen("cdtext.new", "wctb");
+ if (f) {
+ filewrite(f, sbuf, targ.seqno*18);
+ fflush(f);
+ fclose(f);
+ }
+ }
+#endif
+}
+
+static BOOL anytext(int pack_type, int tracks, track_t *trackp)
+{
+ register int i;
+ register char *p;
+
+ for (i = 0; i <= tracks; i++) {
+ if (trackp[i].text == NULL)
+ continue;
+ p = ((textptr_t *)(trackp[i].text))->textcodes[pack_type];
+ if (p != NULL && *p != '\0')
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+static void fillup_pack(register txtarg_t *ap)
+{
+ if (ap->p) {
+ fillbytes(ap->p, &ap->tp->text[12] - ap->p, '\0');
+ fillcrc((Uchar *)ap->tp, sizeof (*ap->tp));
+ ap->p = 0;
+ ap->tp++;
+ }
+}
+
+static void fillpacks(register txtarg_t *ap, register char *from, int len,
+ int track_no, int pack_type)
+{
+ register int charpos;
+ register char *p;
+ register txtpack_t *tp;
+
+ tp = ap->tp;
+ p = ap->p;
+ charpos = 0;
+ do {
+ if (p == 0) {
+ p = tp->text;
+ tp->pack_type = pack_type;
+ if (pack_type != 0x8f)
+ ap->tsize->pack_count[pack_type & 0x0F]++;
+ tp->track_no = track_no;
+ tp->seq_number = ap->seqno++;
+ if (charpos < 15)
+ tp->block_number = charpos;
+ else
+ tp->block_number = 15;
+ }
+ for (; --len >= 0 && p < &tp->text[12]; charpos++) {
+ *p++ = *from++;
+ }
+ len++; /* Overshoot compensation */
+
+ if (p >= &tp->text[12]) {
+ fillcrc((Uchar *)tp, sizeof (*tp));
+ p = 0;
+ tp++;
+ }
+ } while (len > 0);
+
+ ap->tp = tp;
+ ap->p = p;
+}
+
+int write_cdtext(SCSI *usalp, cdr_t *dp, long startsec)
+{
+ char *bp = (char *)textsub;
+ int buflen = textlen;
+ long amount;
+ long bytes = 0;
+ long end = -150;
+ int secspt = textlen / 96;
+ int bytespt = textlen;
+ long maxdma = usalp->maxbuf;
+ int idx;
+ int secs;
+ int nbytes;
+
+/*maxdma = 4320;*/
+ if (maxdma >= (2*textlen)) {
+ /*
+ * Try to make each CD-Text transfer use as much data
+ * as possible.
+ */
+ bp = usalp->bufptr;
+ for (idx = 0; (idx + textlen) <= maxdma; idx += textlen)
+ movebytes(textsub, &bp[idx], textlen);
+ buflen = idx;
+ secspt = buflen / 96;
+ bytespt = buflen;
+/*printf("textlen: %d buflen: %d secspt: %d\n", textlen, buflen, secspt);*/
+ } else if (maxdma < buflen) {
+ /*
+ * We have more CD-Text data than we may transfer at once.
+ */
+ secspt = maxdma / 96;
+ bytespt = secspt * 96;
+ }
+ while (startsec < end) {
+ if ((end - startsec) < secspt) {
+ secspt = end - startsec;
+ bytespt = secspt * 96;
+ }
+ idx = 0;
+ secs = secspt;
+ nbytes = bytespt;
+ do { /* loop over CD-Text data buffer */
+
+ if ((idx + nbytes) > buflen) {
+ nbytes = buflen - idx;
+ secs = nbytes / 96;
+ }
+/*printf("idx: %d nbytes: %d secs: %d startsec: %ld\n",*/
+/*idx, nbytes, secs, startsec);*/
+ amount = write_secs(usalp, dp,
+ (char *)&bp[idx], startsec, nbytes, secs, FALSE);
+ if (amount < 0) {
+ printf("write CD-Text data: error after %ld bytes\n",
+ bytes);
+ return (-1);
+ }
+ bytes += amount;
+ idx += amount;
+ startsec += secs;
+ } while (idx < buflen && startsec < end);
+ }
+ return (0);
+}
+
+
+/*
+ * 3 input bytes (8 bit based) are converted into 4 output bytes (6 bit based).
+ */
+static void eight2six(register Uchar *in, register Uchar *out)
+{
+ register int c;
+
+ c = in[0];
+ out[0] = (c >> 2) & 0x3F;
+ out[1] = (c & 0x03) << 4;
+
+ c = in[1];
+ out[1] |= (c & 0xF0) >> 4;
+ out[2] = (c & 0x0F) << 2;
+
+ c = in[2];
+ out[2] |= (c & 0xC0) >> 6;
+ out[3] = c & 0x3F;
+}
+
+/*
+ * 4 input bytes (6 bit based) are converted into 3 output bytes (8 bit based).
+ */
+static void six2eight(register Uchar *in, register Uchar *out)
+{
+ register int c;
+
+ c = in[0] & 0x3F;
+ out[0] = c << 2;
+
+ c = in[1] & 0x3F;
+ out[0] |= c >> 4;
+ out[1] = c << 4;
+
+ c = in[2] & 0x3F;
+ out[1] |= c >> 2;
+ out[2] = c << 6;
+
+ c = in[3] & 0x3F;
+ out[2] |= c;
+}