Logo Search packages:      
Sourcecode: cdrkit version File versions  Download package

cdtext.c

/*
 * 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;
}

Generated by  Doxygen 1.6.0   Back to index