/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */

/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens
   Copyright (c) 2006 - 2021 Thomas Schmitt <scdbackup@gmx.net>
   Provided under GPL version 2 or later.
*/

#include "../config.h"

#include <stdio.h>

/* ts A61010 */
/* #include <a ssert.h> */

#include <unistd.h>
#include <string.h>
#include "error.h"
#include "options.h"
#include "transport.h"
#include "libburn.h"
#include "drive.h"
#include "sector.h"
#include "crc.h"
#include "debug.h"
#include "toc.h"
#include "write.h"

#include "libdax_msgs.h"
extern struct libdax_msgs *libdax_messenger;

#include "ecma130ab.h"


#ifdef Libburn_log_in_and_out_streaM
/* ts A61031 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif /* Libburn_log_in_and_out_streaM */

/* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */
#ifndef O_BINARY
#define O_BINARY 0
#endif

/*static unsigned char isrc[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";*/

#define sector_common(X) d->alba++; d->rlba X;

static void uncook_subs(unsigned char *dest, unsigned char *source)
{
	int i, j, code;

	memset(dest, 0, 96);

	for (i = 0; i < 12; i++) {
		for (j = 0; j < 8; j++) {
			for (code = 0; code < 8; code++) {
				if (source[code * 12 + i] & 0x80)
					dest[j + i * 8] |= (1 << (7 - code));
				source[code * 12 + i] <<= 1;
			}
		}
	}
}

/* @return >=0 : valid , <0 invalid */
int sector_get_outmode(enum burn_write_types write_type,
		    enum burn_block_types block_type)
{
	/* ts A61103 : extended SAO condition to TAO */
	if (write_type == BURN_WRITE_SAO || write_type == BURN_WRITE_TAO)
		return 0;
	else
		switch (block_type) {
		case BURN_BLOCK_RAW0:
			return BURN_MODE_RAW;
		case BURN_BLOCK_RAW16:
			return BURN_MODE_RAW | BURN_SUBCODE_P16;
		case BURN_BLOCK_RAW96P:
			return BURN_MODE_RAW | BURN_SUBCODE_P96;
		case BURN_BLOCK_RAW96R:
			return BURN_MODE_RAW | BURN_SUBCODE_R96;
		case BURN_BLOCK_MODE1:
			return BURN_MODE1;
		default:
			return -1;
		}

	/* ts A61007 : now handled in burn_write_opts_set_write_type() */
	/* a ssert(0); */	/* return BURN_MODE_UNIMPLEMENTED :) */
}

/* 0 means "same as inmode" */
static int get_outmode(struct burn_write_opts *o)
{
	/* ts A61007 */
	return sector_get_outmode(o->write_type, o->block_type);

	/* -1 is prevented by check in burn_write_opts_set_write_type() */
	/* a ssert(0); */		/* return BURN_MODE_UNIMPLEMENTED :) */
}


static void get_bytes(struct burn_track *track, int count, unsigned char *data)
{
	int valid, shortage, curr, i, tr;

#ifdef Libburn_log_in_and_out_streaM 
        /* ts A61031 */
        static int tee_fd= -1;
        if(tee_fd==-1)
                tee_fd= open("/tmp/libburn_sg_readin",
				O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
				S_IRUSR | S_IWUSR);
#endif /* Libburn_log_in_and_out_streaM */


/* no track pointer means we're just generating 0s */
	if (!track) {
		memset(data, 0, count);
		return;
	}

/* first we use up any offset */
	valid = track->offset - track->offsetcount;
	if (valid > count)
		valid = count;

	if (valid) {
		track->offsetcount += valid;
		memset(data, 0, valid);
	}
	shortage = count - valid;

	if (!shortage)
		goto ex;

/* Next we use source data */
	curr = valid;
	if (!track->eos) {
		if (track->source->read != NULL)
			valid = track->source->read(track->source,
						data + curr, count - curr);
		else
			valid = track->source->read_xt(track->source,
						data + curr, count - curr);
	} else valid = 0;

	if (valid <= 0) { /* ts A61031 : extended from (valid == -1) */
		track->eos = 1;
		valid = 0;
	}
	track->sourcecount += valid;

#ifdef Libburn_log_in_and_out_streaM
	/* ts A61031 */
        if(tee_fd!=-1 && valid>0) {
                write(tee_fd, data + curr, valid);
        }
#endif /* Libburn_log_in_and_out_streaM */

	curr += valid;
	shortage = count - curr;

	if (!shortage)
		goto ex;

/* Before going to the next track, we run through any tail */

	valid = track->tail - track->tailcount;
	if (valid > count - curr)
		valid = count - curr;

	if (valid) {
		track->tailcount += valid;
		memset(data + curr, 0, valid);
	}
	curr += valid;
	shortage -= valid;

	if (!shortage)
		goto ex;

	/* ts A61031 - B10103 */
	if (shortage >= count)
		track->track_data_done = 1;
	if (track->end_on_premature_eoi && shortage >= count &&
	    !track->open_ended) {
		char msg[80];
		off_t missing, inp_block_size, track_blocks;

		inp_block_size = burn_sector_length(track->mode);
		track_blocks = burn_track_get_sectors_2(track, 1);
		if (track_blocks < 0)
			track_blocks = 0;
		missing = track_blocks * inp_block_size - track->sourcecount;
		sprintf(msg,
		      "Premature end of input encountered. Missing: %.f bytes",
		      (double) missing);
		libdax_msgs_submit(libdax_messenger, -1, 0x00020180,
				LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH,
				msg, 0,0);
		/* Memorize that premature end of input happened */
		track->end_on_premature_eoi = 2;
	}
	if (track->open_ended || track->end_on_premature_eoi)
		goto ex;

/* If we're still short, and there's a "next" pointer, we pull from that.
   if that depletes, we'll just fill with 0s.
*/
	if (track->source->next) {
		struct burn_source *src;
		printf("pulling from next track\n");
		src = track->source->next;
		valid = src->read(src, data + curr, shortage);
		if (valid > 0) {
			shortage -= valid;
			curr += valid;
		}
	}
ex:;
	/* ts A61024 : general finalizing processing */ 
	if(shortage)
		memset(data + curr, 0, shortage); /* this is old icculus.org */
	if (track->swap_source_bytes == 1) {
		for (i = 1; i < count; i += 2) {
			tr = data[i];
			data[i] = data[i-1];
			data[i-1] = tr;
		}
	}
}


/* ts B20113 : outsourced from get_sector() */
int sector_write_buffer(struct burn_drive *d, 
			struct burn_track *track, int flag)
{
	int err, i;
	struct buffer *out;

	out = d->buffer;
	if (out->sectors <= 0)
		return 2;
	err = d->write(d, d->nwa, out);
	if (err == BE_CANCELLED)
		return 0;

	/* ts A61101 */
	if(track != NULL) {
		track->writecount += out->bytes;
		track->written_sectors += out->sectors;

		/* Determine current index */
		for (i = d->progress.index; i + 1 < track->indices; i++) {
			if (track->index[i + 1] > d->nwa + out->sectors)
		break;
			d->progress.index = i + 1;
		}
	}
	/* ts A61119 */
	d->progress.buffered_bytes += out->bytes;

	d->nwa += out->sectors;
	out->bytes = 0;
	out->sectors = 0;
	return 1;
}


/* ts A61009 : seems to hand out sector start pointer in opts->drive->buffer
		and to count hand outs as well as reserved bytes */
/* ts A61101 : added parameter track for counting written bytes */
static unsigned char *get_sector(struct burn_write_opts *opts, 
				struct burn_track *track, int inmode)
{
	struct burn_drive *d = opts->drive;
	struct buffer *out = d->buffer;
	int outmode, seclen, write_ret;
	unsigned char *ret;

	outmode = get_outmode(opts);
	if (outmode == 0)
		outmode = inmode;

	/* ts A61009 : react on eventual failure of burn_sector_length()
			(should not happen if API tested properly).
			Ensures out->bytes >= out->sectors  */
	seclen = burn_sector_length(outmode);
	if (seclen <= 0)
		return NULL;
	seclen += burn_subcode_length(outmode);

	/* ts A61219 : opts->obs is eventually a 32k trigger for DVD */
	/* (there is enough buffer size reserve for track->cdxa_conversion) */
	if (out->bytes + seclen > BUFFER_SIZE ||
	    (opts->obs > 0 && out->bytes + seclen > opts->obs)) {
		write_ret = sector_write_buffer(d, track, 0);
		if (write_ret <= 0)
			return NULL;
	}
	ret = out->data + out->bytes;
	out->bytes += seclen;
	out->sectors++;

	return ret;
}

/* ts A61031 */
/* Revoke the counting of the most recent sector handed out by get_sector() */
static void unget_sector(struct burn_write_opts *opts, int inmode)
{
	struct burn_drive *d = opts->drive;
	struct buffer *out = d->buffer;
	int outmode;
	int seclen;

	outmode = get_outmode(opts);
	if (outmode == 0)
		outmode = inmode;

	/* ts A61009 : react on eventual failure of burn_sector_length()
			(should not happen if API tested properly).
			Ensures out->bytes >= out->sectors  */
	seclen = burn_sector_length(outmode);
	if (seclen <= 0)
		return;
	seclen += burn_subcode_length(outmode);

	out->bytes -= seclen;
	out->sectors--;
}


/* either inmode == outmode, or outmode == raw.  anything else is bad news */
/* ts A61010 : changed type to int in order to propagate said bad news */
/** @return 1 is ok, <= 0 is failure */
static int convert_data(struct burn_write_opts *o, struct burn_track *track,
			 int inmode, unsigned char *data)
{
	int outlen, inlen;
	int offset = -1;
	int outmode;

	outmode = get_outmode(o);
	if (outmode == 0)
		outmode = inmode;

	outlen = burn_sector_length(outmode);
	inlen = burn_sector_length(inmode);

	/* ts A61010 */
	/* a ssert(outlen >= inlen); */
	if (outlen < inlen || outlen < 0 || inlen < 0)
		return 0;

	if ((outmode & BURN_MODE_BITS) == (inmode & BURN_MODE_BITS)) {
		/* see MMC-5 4.2.3.8.5.3 Block Format for Mode 2 form 1 Data
		            Table 24  Mode 2 Formed Sector Sub-header Format */
		if (track != NULL)
			if (track->cdxa_conversion == 1)
				inlen += 8;

		get_bytes(track, inlen, data);

		if (track != NULL)
			if (track->cdxa_conversion == 1)
				memmove(data, data + 8, inlen - 8);
		return 1;
	}

	/* ts A61010 */
	/* a ssert(outmode & BURN_MODE_RAW); */
	if (!(outmode & BURN_MODE_RAW))
		return 0;

	if (inmode & BURN_MODE1)
		offset = 16;
	if (inmode & BURN_MODE_RAW)
		offset = 0;
	if (inmode & BURN_AUDIO)
		offset = 0;

	/* ts A61010 */
	/* a ssert(offset != -1); */
	if (offset == -1)
		return 0;

	get_bytes(track, inlen, data + offset);
	return 1;
}

static void convert_subs(struct burn_write_opts *o, int inmode,
			 unsigned char *subs, unsigned char *sector)
{
	unsigned char *out;
	int outmode;

	outmode = get_outmode(o);
	if (outmode == 0)
		outmode = inmode;
	sector += burn_sector_length(outmode);
/* XXX for sao with subs, we'd need something else... */

	switch (o->block_type) {
	case BURN_BLOCK_RAW96R:
		uncook_subs(sector, subs);
		break;

	case BURN_BLOCK_RAW16:
		memcpy(sector, subs + 12, 12);
		out = sector + 12;
		out[0] = 0;
		out[1] = 0;
		out[2] = 0;
/*XXX find a better way to deal with partially damaged P channels*/
		if (subs[2] != 0)
			out[3] = 0x80;
		else
			out[3] = 0;
		out = sector + 10;

		out[0] = ~out[0];
		out[1] = ~out[1];
		break;
	/* ts A61119 : to silence compiler warnings */
	default:;
	}
}

static void subcode_toc(struct burn_drive *d, int mode, unsigned char *data)
{
	unsigned char *q;
	int track;
	int crc;
	int min, sec, frame;

	track = d->toc_temp / 3;
	memset(data, 0, 96);
	q = data + 12;

	burn_lba_to_msf(d->rlba, &min, &sec, &frame);
/*XXX track numbers are BCD
a0 - 1st track ctrl
a1 - last track ctrl
a2 - lout ctrl
*/
	q[0] = (d->toc_entry[track].control << 4) + 1;
	q[1] = 0;
	if (d->toc_entry[track].point < 100)
		q[2] = dec_to_bcd(d->toc_entry[track].point);
	else
		q[2] = d->toc_entry[track].point;
	q[3] = dec_to_bcd(min);
	q[4] = dec_to_bcd(sec);
	q[5] = dec_to_bcd(frame);
	q[6] = 0;
	q[7] = dec_to_bcd(d->toc_entry[track].pmin);
	q[8] = dec_to_bcd(d->toc_entry[track].psec);
	q[9] = dec_to_bcd(d->toc_entry[track].pframe);

#ifdef Libburn_no_crc_C
	crc = 0; /* dummy */
#else
	crc = crc_ccitt(q, 10);
#endif

	q[10] = crc >> 8;
	q[11] = crc & 0xFF;
	d->toc_temp++;
	d->toc_temp %= (d->toc_entries * 3);
}

int sector_toc(struct burn_write_opts *o, int mode)
{
	struct burn_drive *d = o->drive;
	unsigned char *data;
	unsigned char subs[96];

	data = get_sector(o, NULL, mode);
	if (data == NULL)
		return 0;
	/* ts A61010 */
	if (convert_data(o, NULL, mode, data) <= 0)
		return 0;
	subcode_toc(d, mode, subs);
	convert_subs(o, mode, subs, data);
	if (sector_headers(o, data, mode, 1) <= 0)
		return 0;
	sector_common(++)
	return 1;
}

int sector_pregap(struct burn_write_opts *o,
		   unsigned char tno, unsigned char control, int mode)
{
	struct burn_drive *d = o->drive;
	unsigned char *data;
	unsigned char subs[96];

	data = get_sector(o, NULL, mode);
	if (data == NULL)
		return 0;
	/* ts A61010 */
	if (convert_data(o, NULL, mode, data) <= 0)
		return 0;
	subcode_user(o, subs, tno, control, 0, NULL, 1);
	convert_subs(o, mode, subs, data);
	if (sector_headers(o, data, mode, 0) <= 0)
		return 0;
	sector_common(--)
	return 1;
}

int sector_postgap(struct burn_write_opts *o,
		    unsigned char tno, unsigned char control, int mode)
{
	struct burn_drive *d = o->drive;
	unsigned char subs[96];
	unsigned char *data;

	data = get_sector(o, NULL, mode);
	if (data == NULL)
		return 0;
	/* ts A61010 */
	if (convert_data(o, NULL, mode, data) <= 0)
		return 0;
/* use last index in track */
	subcode_user(o, subs, tno, control, 1, NULL, 1);
	convert_subs(o, mode, subs, data);
	if (sector_headers(o, data, mode, 0) <= 0)
		return 0;
	sector_common(++)
	return 1;
}

static void subcode_lout(struct burn_write_opts *o, unsigned char control,
			 unsigned char *data)
{
	struct burn_drive *d = o->drive;
	unsigned char *q;
	int crc;
	int rmin, min, rsec, sec, rframe, frame;

	memset(data, 0, 96);
	q = data + 12;

	burn_lba_to_msf(d->alba, &min, &sec, &frame);
	burn_lba_to_msf(d->rlba, &rmin, &rsec, &rframe);

	if (((rmin == 0) && (rsec == 0) && (rframe == 0)) ||
	    ((rsec >= 2) && !((rframe / 19) % 2)))
		memset(data, 0xFF, 12);
	q[0] = (control << 4) + 1;
	q[1] = 0xAA;
	q[2] = 0x01;
	q[3] = dec_to_bcd(rmin);
	q[4] = dec_to_bcd(rsec);
	q[5] = dec_to_bcd(rframe);
	q[6] = 0;
	q[7] = dec_to_bcd(min);
	q[8] = dec_to_bcd(sec);
	q[9] = dec_to_bcd(frame);

#ifdef Libburn_no_crc_C
	crc = 0; /* dummy */
#else
	crc = crc_ccitt(q, 10);
#endif

	q[10] = crc >> 8;
	q[11] = crc & 0xFF;
}

static char char_to_isrc(char c)
{
	if (c >= '0' && c <= '9')
		return c - '0';
	if (c >= 'A' && c <= 'Z')
		return 0x11 + (c - 'A');
	if (c >= 'a' && c <= 'z')
		return 0x11 + (c - 'a');

	/* ts A61008 : obsoleted by test in burn_track_set_isrc() */
	/* a ssert(0); */
	return 0;
}

void subcode_user(struct burn_write_opts *o, unsigned char *subcodes,
		  unsigned char tno, unsigned char control,
		  unsigned char indx, struct isrc *isrc, int psub)
{
	struct burn_drive *d = o->drive;
	unsigned char *p, *q;
	int crc;
	int m, s, f, c, qmode;	/* 1, 2 or 3 */

	memset(subcodes, 0, 96);

	p = subcodes;
	if ((tno == 1) && (d->rlba == -150))
		memset(p, 0xFF, 12);

	if (psub)
		memset(p, 0xFF, 12);
	q = subcodes + 12;

	qmode = 1;
	/* every 1 in 10 we can do something different */
	if (d->rlba % 10 == 0) {
		/* each of these can occur 1 in 100 */
		if ((d->rlba / 10) % 10 == 0) {
			if (o->has_mediacatalog)
				qmode = 2;
		} else if ((d->rlba / 10) % 10 == 1) {
			if (isrc && isrc->has_isrc)
				qmode = 3;
		}
	}

	/* ts A61010 : this cannot happen. Assert for fun ? */
	/* a ssert(qmode == 1 || qmode == 2 || qmode == 3); */

	switch (qmode) {
	case 1:
		q[1] = dec_to_bcd(tno);	/* track number */
		q[2] = dec_to_bcd(indx);	/* index XXX read this shit
						   from the track array */
		burn_lba_to_msf(d->rlba, &m, &s, &f);
		q[3] = dec_to_bcd(m);	/* rel min */
		q[4] = dec_to_bcd(s);	/* rel sec */
		q[5] = dec_to_bcd(f);	/* rel frame */
		q[6] = 0;	/* zero */
		burn_lba_to_msf(d->alba, &m, &s, &f);
		q[7] = dec_to_bcd(m);	/* abs min */
		q[8] = dec_to_bcd(s);	/* abs sec */
		q[9] = dec_to_bcd(f);	/* abs frame */
		break;
	case 2:
		/* media catalog number */
		q[1] = (o->mediacatalog[0] << 4) + o->mediacatalog[1];
		q[2] = (o->mediacatalog[2] << 4) + o->mediacatalog[3];
		q[3] = (o->mediacatalog[4] << 4) + o->mediacatalog[5];
		q[4] = (o->mediacatalog[6] << 4) + o->mediacatalog[7];
		q[5] = (o->mediacatalog[8] << 4) + o->mediacatalog[9];
		q[6] = (o->mediacatalog[10] << 4) + o->mediacatalog[11];
		q[7] = o->mediacatalog[12] << 4;

		q[8] = 0;
		burn_lba_to_msf(d->alba, &m, &s, &f);
		q[9] = dec_to_bcd(f);	/* abs frame */
		break;
	case 3:
		c = char_to_isrc(isrc->country[0]);
		/* top 6 bits of [1] is the first country code */
		q[1] = c << 2;
		c = char_to_isrc(isrc->country[1]);
		/* bottom 2 bits of [1] is part of the second country code */
		q[1] += (c >> 4);
		/* top 4 bits if [2] is the rest of the second country code */
		q[2] = c << 4;

		c = char_to_isrc(isrc->owner[0]);
		/* bottom 4 bits of [2] is part of the first owner code */
		q[2] += (c >> 2);
		/* top 2 bits of [3] is the rest of the first owner code */
		q[3] = c << 6;
		c = char_to_isrc(isrc->owner[1]);
		/* bottom 6 bits of [3] is the entire second owner code */
		q[3] += c;
		c = char_to_isrc(isrc->owner[2]);
		/* top 6 bits of [4] are the third owner code */
		q[4] = c << 2;

		/* [5] is the year in 2 BCD numbers */
		q[5] = dec_to_bcd(isrc->year % 100);
		/* [6] is the first 2 digits in the serial */
		q[6] = dec_to_bcd(isrc->serial % 100);
		/* [7] is the next 2 digits in the serial */
		q[7] = dec_to_bcd((isrc->serial / 100) % 100);
		/* the top 4 bits of [8] is the last serial digit, the rest is 
		   zeros */
		q[8] = dec_to_bcd((isrc->serial / 10000) % 10) << 4;
		burn_lba_to_msf(d->alba, &m, &s, &f);
		q[9] = dec_to_bcd(f);	/* abs frame */
		break;
	}
	q[0] = (control << 4) + qmode;


#ifdef Libburn_no_crc_C
	crc = 0; /* dummy */
#else
	crc = crc_ccitt(q, 10);
#endif

	q[10] = crc >> 8;
	q[11] = crc & 0xff;
}

int sector_lout(struct burn_write_opts *o, unsigned char control, int mode)
{
	struct burn_drive *d = o->drive;
	unsigned char subs[96];
	unsigned char *data;

	data = get_sector(o, NULL, mode);
	if (!data)
		return 0;
	/* ts A61010 */
	if (convert_data(o, NULL, mode, data) <= 0)
		return 0;
	subcode_lout(o, control, subs);
	convert_subs(o, mode, subs, data);
	if (sector_headers(o, data, mode, 0) <= 0)
		return 0;
	sector_common(++)
	return 1;
}

int sector_data(struct burn_write_opts *o, struct burn_track *t, int psub)
{
	struct burn_drive *d = o->drive;
	unsigned char subs[96];
	unsigned char *data;

	data = get_sector(o, t, t->mode);
	if (data == NULL)
		return 0;
	/* ts A61010 */
	if (convert_data(o, t, t->mode, data) <= 0)
		return 0;

	/* ts A61031 */
	if ((t->open_ended || t->end_on_premature_eoi) && t->track_data_done) {
		unget_sector(o, t->mode);
		return 2;
	}

	/* ts A61219 : allow track without .entry */
	if (t->entry == NULL)
		;
	else if (!t->source->read_sub)
		subcode_user(o, subs, t->entry->point,
			     t->entry->control, 1, &t->isrc, psub);
	else if (!t->source->read_sub(t->source, subs, 96))
		subcode_user(o, subs, t->entry->point,
			     t->entry->control, 1, &t->isrc, psub);
	convert_subs(o, t->mode, subs, data);

	if (sector_headers(o, data, t->mode, 0) <= 0)
		return 0;
	sector_common(++)
	return 1;
}

int burn_msf_to_lba(int m, int s, int f)
{
	if (m < 90)
		return (m * 60 + s) * 75 + f - 150;
	else
		return (m * 60 + s) * 75 + f - 450150;
}

void burn_lba_to_msf(int lba, int *m, int *s, int *f)
{
	if (lba >= -150) {
		*m = (lba + 150) / (60 * 75);
		*s = (lba + 150 - *m * 60 * 75) / 75;
		*f = lba + 150 - *m * 60 * 75 - *s * 75;
	} else {
		*m = (lba + 450150) / (60 * 75);
		*s = (lba + 450150 - *m * 60 * 75) / 75;
		*f = lba + 450150 - *m * 60 * 75 - *s * 75;
	}
}

int dec_to_bcd(int d)
{
	int top, bottom;

	top = d / 10;
	bottom = d - (top * 10);
	return (top << 4) + bottom;
}

int sector_headers_is_ok(struct burn_write_opts *o, int mode)
{
	if (mode & BURN_AUDIO)	/* no headers for "audio" */
		return 1;
	if (o->write_type == BURN_WRITE_SAO)
		return 1;

	/* ts A61031 */
	if (o->write_type == BURN_WRITE_TAO)
		return 1;

	if (mode & BURN_MODE1)
		return 2;
	return 0;
}

/* ts A90830 : changed return type to int
   @return 0= failure
           1= success
*/
int sector_headers(struct burn_write_opts *o, unsigned char *out,
		    int mode, int leadin)
{

#ifdef Libburn_ecma130ab_includeD

	struct burn_drive *d = o->drive;
	unsigned int crc;
	int min, sec, frame;
	int modebyte = -1;
	int ret;

	ret = sector_headers_is_ok(o, mode);
	if (ret != 2)
		return !!ret;
	modebyte = 1;

	out[0] = 0;
	memset(out + 1, 0xFF, 10);	/* sync */
	out[11] = 0;

	if (leadin) {
		burn_lba_to_msf(d->rlba, &min, &sec, &frame);
		out[12] = dec_to_bcd(min) + 0xA0;
		out[13] = dec_to_bcd(sec);
		out[14] = dec_to_bcd(frame);
		out[15] = modebyte;
	} else {
		burn_lba_to_msf(d->alba, &min, &sec, &frame);
		out[12] = dec_to_bcd(min);
		out[13] = dec_to_bcd(sec);
		out[14] = dec_to_bcd(frame);
		out[15] = modebyte;
	}
	if (mode & BURN_MODE1) {

#ifdef Libburn_no_crc_C
		crc = 0; /* dummy */
#else
		crc = crc_32(out, 2064);
#endif

		out[2064] = crc & 0xFF;
		crc >>= 8;
		out[2065] = crc & 0xFF;
		crc >>= 8;
		out[2066] = crc & 0xFF;
		crc >>= 8;
		out[2067] = crc & 0xFF;
	}
	if (mode & BURN_MODE1) {
		memset(out + 2068, 0, 8);
		burn_rspc_parity_p(out);
		burn_rspc_parity_q(out);
	}
	burn_ecma130_scramble(out);
	return 1;

#else /* Libburn_ecma130ab_includeD */

	int ret;

	ret = sector_headers_is_ok(o, mode);
	if (ret != 2)
		return (!! ret);

	/* ts A90830 : lec.c is copied from cdrdao.
	   I have no idea yet how lec.c implements the Reed-Solomon encoding
	   which is described in ECMA-130 for CD-ROM.
	   So this got removed for now.
	*/
	libdax_msgs_submit(libdax_messenger, o->drive->global_index,
				0x0002010a,
				LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
				"Raw CD write modes are not supported", 0, 0);
	return 0;

#endif /* ! Libburn_ecma130ab_includeD */

}

#if 0
void process_q(struct burn_drive *d, unsigned char *q)
{
	unsigned char i[5];
	int mode;

	mode = q[0] & 0xF;
/*      burn_print(12, "mode: %d : ", mode);*/
	switch (mode) {
	case 1:
/*              burn_print(12, "tno = %d : ", q[1]);
                burn_print(12, "index = %d\n", q[2]);
*/
		/* q[1] is the track number (starting at 1) q[2] is the index
		   number (starting at 0) */
#warning this is totally bogus
		if (q[1] - 1 > 99)
			break;
		if (q[2] > d->toc->track[q[1] - 1].indices) {
			burn_print(12, "new index at %d\n", d->alba);
			d->toc->track[q[1] - 1].index[q[2]] = d->alba;
			d->toc->track[q[1] - 1].indices++;
		}
		break;
	case 2:
		/* XXX do not ignore these */
		break;
	case 3:
/*              burn_print(12, "ISRC data in mode 3 q\n");*/
		i[0] = isrc[(q[1] << 2) >> 2];
/*              burn_print(12, "0x%x 0x%x 0x%x 0x%x 0x%x\n", q[1], q[2], q[3], q[4], q[5]);
                burn_print(12, "ISRC - %c%c%c%c%c\n", i[0], i[1], i[2], i[3], i[4]);
*/
		break;
	default:

		/* ts A61009 : if reactivated then without Assert */
		a ssert(0);
	}
}
#endif

/* this needs more info.  subs in the data? control/adr? */

/* ts A61119 : One should not use unofficial compiler extensions.
   >>> Some day this function needs to be implemented. At least for now
       the result does not match the "mode" of cdrecord -toc.
 */
/*
#warning sector_identify needs to be written
*/
int sector_identify(unsigned char *data)
{

/*
	burn_ecma130_scramble(data);
check mode byte for 1 or 2
test parity to see if it's a valid sector
if invalid, return BURN_MODE_AUDIO;
else return mode byte  (what about mode 2 formless?  heh)
*/

	return BURN_MODE1;
}
