You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libburn/libburn/write.c

3236 lines
85 KiB

17 years ago
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens
Copyright (c) 2006 - 2012 Thomas Schmitt <scdbackup@gmx.net>
Provided under GPL version 2 or later.
*/
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
17 years ago
#include <unistd.h>
#include <signal.h>
/* ts A61009 */
/* #include <a ssert.h> */
/* ts A61106 : Deliberate defect provocation macros
DO NOT DEFINE THESE IF YOU WANT SUCCESSFUL TAO !
#define Libburn_experimental_no_close_tracK 1
#define Libburn_experimental_no_close_sessioN 1
*/
/* ts A61114 : Highly experimental : try to achieve SAO on appendables
THIS DOES NOT WORK YET !
#define Libburn_sao_can_appenD 1
*/
#include <sys/types.h>
17 years ago
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/time.h>
17 years ago
#include "error.h"
#include "sector.h"
#include "libburn.h"
#include "drive.h"
#include "transport.h"
#include "debug.h"
#include "init.h"
#include "toc.h"
#include "util.h"
#include "sg.h"
#include "write.h"
#include "options.h"
#include "structure.h"
#include "source.h"
#include "mmc.h"
#include "spc.h"
17 years ago
#include "libdax_msgs.h"
extern struct libdax_msgs *libdax_messenger;
/* ts A91120 : <<< experimental */
#ifdef Libburn_mmap_write_buffeR
#include <sys/mman.h>
#endif
/* The maximum output size to be used with CD media. This is also curbed
by BURN_OS_TRANSPORT_BUFFER_SIZE. The smaller number gets into effect.
*/
#define Libburn_cd_obS (32 * 1024)
/* The size to be used with DVD media.
*/
#define Libburn_dvd_obS (32 * 1024)
/* The size to be used with BD-RE media in normal, not streamed mode.
*/
#define Libburn_bd_re_obS (32 * 1024)
/* The size to be used with BD-RE media in streamed mode.
*/
#define Libburn_bd_re_streamed_obS (64 * 1024)
17 years ago
static int type_to_ctrl(int mode)
{
int ctrl = 0;
int data = BURN_MODE2 | BURN_MODE1 | BURN_MODE0;
if (mode & data) {
ctrl |= 4;
} else if (mode & BURN_AUDIO) {
if (mode & BURN_4CH)
ctrl |= 8;
if (mode & BURN_PREEMPHASIS)
ctrl |= 1;
} else
/* ts A61008 */
/* a ssert(0); */
return -1;
17 years ago
if (mode & BURN_COPY)
ctrl |= 2;
return ctrl;
}
/* only the ctrl nibble is set here (not adr) */
/* ts A61009 : removed "static" , reacted on type_to_ctrl() == -1
preserved ignorance towards unknown modes (for now) */
void type_to_form(int mode, unsigned char *ctladr, int *form)
17 years ago
{
int ret;
ret = type_to_ctrl(mode) << 4;
if (ret == -1) {
*ctladr = 0xff;
*form = -1;
return;
}
*ctladr = ret;
17 years ago
if (mode & BURN_AUDIO)
*form = 0;
if (mode & BURN_MODE0) {
/* ts A61009 */
/* a ssert(0); */
*form = -1;
return;
}
17 years ago
if (mode & BURN_MODE1)
*form = 0x10;
if (mode & BURN_MODE2) {
/* ts A61009 */
/* a ssert(0); */ /* XXX someone's gonna want this sometime */
*form = -1;
return;
}
17 years ago
if (mode & BURN_MODE_RAW)
*form = 0;
if (mode & BURN_SUBCODE_P16) /* must be expanded to R96 */
*form |= 0x40;
if (mode & BURN_SUBCODE_P96)
*form |= 0xC0;
if (mode & BURN_SUBCODE_R96)
*form |= 0x40;
}
/* ts A71002 : outsourced from burn_write_flush() : no sync cache here */
int burn_write_flush_buffer(struct burn_write_opts *o,struct burn_track *track)
17 years ago
{
struct burn_drive *d = o->drive;
if (d->buffer->bytes && !d->cancel) {
int err;
err = d->write(d, d->nwa, d->buffer);
if (err == BE_CANCELLED)
return 0;
/* A61101 */
if(track != NULL) {
track->writecount += d->buffer->bytes;
track->written_sectors += d->buffer->sectors;
}
/* ts A61119 */
d->progress.buffered_bytes += d->buffer->bytes;
17 years ago
d->nwa += d->buffer->sectors;
d->buffer->bytes = 0;
d->buffer->sectors = 0;
17 years ago
}
return 1;
}
int burn_write_flush(struct burn_write_opts *o, struct burn_track *track)
{
int ret;
struct burn_drive *d = o->drive;
ret = burn_write_flush_buffer(o, track);
if (ret <= 0)
return ret;
17 years ago
d->sync_cache(d);
return 1;
}
/* ts A71002 : outsourced from burn_write_close_track() */
int burn_write_track_minsize(struct burn_write_opts *o, struct burn_session *s,
int tnum)
{
char msg[81];
struct burn_drive *d;
struct burn_track *t;
int todo, step, cancelled, seclen;
d = o->drive;
t = s->track[tnum];
/* ts A61103 : pad up track to minimum size of 600 sectors */
if (t->written_sectors < 300) {
todo = 300 - t->written_sectors;
sprintf(msg,"Padding up track to minimum size (+ %d sectors)",
todo);
libdax_msgs_submit(libdax_messenger, o->drive->global_index,
0x0002011a,
LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg,0,0);
step = BUFFER_SIZE / 4096; /* shall fit any sector size */
if (step <= 0)
step = 1;
seclen = burn_sector_length(t->mode);
if (seclen <= 0)
seclen = 2048;
memset(d->buffer, 0, sizeof(struct buffer));
cancelled = d->cancel;
for (; todo > 0; todo -= step) {
if (step > todo)
step = todo;
d->buffer->bytes = step*seclen;
d->buffer->sectors = step;
d->cancel = 0;
d->write(d, d->nwa, d->buffer);
d->nwa += d->buffer->sectors;
t->writecount += d->buffer->bytes;
t->written_sectors += d->buffer->sectors;
d->progress.buffered_bytes += d->buffer->bytes;
}
d->cancel = cancelled;
}
return 1;
}
/* ts A61030 */
int burn_write_close_track(struct burn_write_opts *o, struct burn_session *s,
int tnum)
{
char msg[81];
struct burn_drive *d;
/* ts A61106 */
#ifdef Libburn_experimental_no_close_tracK
return 1;
#endif
d = o->drive;
d->busy = BURN_DRIVE_CLOSING_TRACK;
sprintf(msg, "Closing track %2.2d", tnum+1);
libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0);
/* MMC-1 mentions track number 0xFF for "the incomplete track",
MMC-3 does not. I tried both. 0xFF was in effect when other
bugs finally gave up and made way for readable tracks. */
/* ts A70129
Probably the right value for appendables is d->last_track_no
*/
d->close_track_session(o->drive, 0, 0xff);
d->busy = BURN_DRIVE_WRITING;
return 1;
}
/* ts A61030 */
int burn_write_close_session(struct burn_write_opts *o)
{
/* ts A61106 */
#ifdef Libburn_experimental_no_close_sessioN
return 1;
#endif
libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH,
"Closing session", 0, 0);
/* ts A61102 */
o->drive->busy = BURN_DRIVE_CLOSING_SESSION;
o->drive->close_track_session(o->drive, 1, 0);
/* ts A61102 */
o->drive->busy = BURN_DRIVE_WRITING;
return 1;
}
/* ts A60819, B20101:
This is useful only when changes about CD SAO get tested.
# define Libburn_write_with_function_print_cuE yes
*/
#ifdef Libburn_write_with_function_print_cuE
static char cue_printify(char c)
{
if (c >= 32 && c < 127)
return c;
return '#';
}
17 years ago
static void print_cue(struct cue_sheet *sheet)
{
int i;
unsigned char *unit;
printf("\n");
printf("ctladr|trno|indx|form|scms| msf | text\n");
printf("------+----+----+----+----+----------+--------\n");
17 years ago
for (i = 0; i < sheet->count; i++) {
unit = sheet->data + 8 * i;
if ((unit[0] & 0xf) == 2) {
printf(
" %1X %1X | | | | | | %c%c%c%c%c%c%c\n",
(unit[0] & 0xf0) >> 4, unit[0] & 0xf,
cue_printify(unit[1]), cue_printify(unit[2]),
cue_printify(unit[3]), cue_printify(unit[4]),
cue_printify(unit[5]), cue_printify(unit[6]),
unit[7] == 0 ? ' ' : cue_printify(unit[7]));
} else if ((unit[0] & 0xf) == 3) {
printf(
" %1X %1X | %02X | | | | | %c%c%c%c%c%c\n",
(unit[0] & 0xf0) >> 4, unit[0] & 0xf,
unit[1], cue_printify(unit[2]),
cue_printify(unit[3]), cue_printify(unit[4]),
cue_printify(unit[5]), cue_printify(unit[6]),
cue_printify(unit[7]));
} else {
printf(" %1X %1X | %02X | %02X | %02X | %02X |",
(unit[0] & 0xf0) >> 4, unit[0] & 0xf,
unit[1], unit[2], unit[3], unit[4]);
printf(" %02d:%02d:%02d |\n",
unit[5], unit[6], unit[7]);
}
17 years ago
}
fflush(stdout);
17 years ago
}
#endif /* Libburn_write_with_print_cuE */
/* ts B11226 */
static int new_cue(struct cue_sheet *sheet, int number, int flag)
17 years ago
{
unsigned char *ptr;
ptr = realloc(sheet->data, (sheet->count + number) * 8);
if (ptr == NULL) {
libdax_msgs_submit(libdax_messenger, -1, 0x00020111,
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
"Could not allocate new auxiliary object (cue_sheet->data)",
0, 0);
return -1;
}
17 years ago
sheet->data = ptr;
sheet->count += number;
return 1;
}
/* ts B11226 : outsourced new_cue() */
/** @return 1 = success , <=0 failure */
static int add_cue(struct cue_sheet *sheet, unsigned char ctladr,
unsigned char tno, unsigned char indx,
unsigned char form, unsigned char scms, int lba)
{
unsigned char *unit;
int m, s, f, ret;
burn_lba_to_msf(lba, &m, &s, &f);
ret = new_cue(sheet, 1, 0);
if (ret <= 0)
return -1;
17 years ago
unit = sheet->data + (sheet->count - 1) * 8;
unit[0] = ctladr;
unit[1] = tno;
unit[2] = indx;
unit[3] = form;
unit[4] = scms;
unit[5] = m;
unit[6] = s;
unit[7] = f;
return 1;
17 years ago
}
/* ts B11226 */
static int add_catalog_cue(struct cue_sheet *sheet, unsigned char catalog[13])
{
unsigned char *unit;
int i, ret;
ret = new_cue(sheet, 2, 0);
if (ret <= 0)
return -1;
unit = sheet->data + (sheet->count - 2) * 8;
unit[0] = unit[8] = 0x02;
for (i = 0; i < 13; i++)
unit[1 + (i >= 7) * 8 + (i % 7)] = catalog[i];
unit[15] = 0x00;
return 1;
}
/* ts B11226 */
static int add_isrc_cue(struct cue_sheet *sheet, unsigned char ctladr, int tno,
struct isrc *isrc)
{
unsigned char *unit;
int i, ret;
char text[8];
ret = new_cue(sheet, 2, 0);
if (ret <= 0)
return -1;
unit = sheet->data + (sheet->count - 2) * 8;
unit[0] = unit[8] = (ctladr & 0xf0) | 0x03;
unit[1] = unit[9] = tno;
unit[2] = isrc->country[0];
unit[3] = isrc->country[1];
unit[4] = isrc->owner[0];
unit[5] = isrc->owner[1];
unit[6] = isrc->owner[2];
sprintf(text, "%-2.2u%-5.5u", (unsigned int) isrc->year, isrc->serial);
unit[7] = text[0];
for (i = 1; i < 7; i++)
unit[9 + i] = text[i];
return 1;
}
/* ts A61114: added parameter nwa */
17 years ago
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
struct burn_session *session,
int nwa)
17 years ago
{
int i, m, s, f, form, pform, runtime = -150, ret, track_length;
int leadin_form, leadin_start, pregap;
unsigned char ctladr, scms;
17 years ago
struct burn_drive *d;
struct burn_toc_entry *e;
struct cue_sheet *sheet;
struct burn_track **tar = session->track;
int ntr = session->tracks;
int rem = 0;
#define Libburn_track_multi_indeX yes
#ifdef Libburn_track_multi_indeX
int j;
#endif
17 years ago
d = o->drive;
#ifdef Libburn_sao_can_appenD
if (d->status == BURN_DISC_APPENDABLE)
runtime = nwa-150;
#endif
sheet = calloc(1, sizeof(struct cue_sheet));
/* ts A61009 : react on failures of calloc(), add_cue_sheet()
type_to_form() */
if (sheet == NULL) {
libdax_msgs_submit(libdax_messenger, -1, 0x00020111,
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
"Could not allocate new auxiliary object (cue_sheet)",
0, 0);
return NULL;
}
17 years ago
sheet->data = NULL;
sheet->count = 0;
type_to_form(tar[0]->mode, &ctladr, &form);
if (form == -1) {
libdax_msgs_submit(libdax_messenger, -1, 0x00020116,
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
"Track mode has unusable value", 0, 0);
goto failed;
}
if (o->num_text_packs > 0) {
leadin_form = 0x41;
} else {
leadin_form = 0x01;
/* Check for CD-TEXT in session. Not the final creation,
because the cue sheet content might be needed for CD-TEXT
pack type 0x88 "TOC".
*/
if (o->text_packs == NULL) {
ret = burn_cdtext_from_session(session, NULL, NULL, 1);
if (ret < 0)
goto failed;
else if (ret > 0)
leadin_form = 0x41;
}
}
if (o->has_mediacatalog)
ret = add_catalog_cue(sheet, o->mediacatalog);
else if (session->mediacatalog[0])
ret = add_catalog_cue(sheet, session->mediacatalog);
else
ret = 1;
if (ret <= 0)
goto failed;
/* ts B11225
MMC-5 6.33.3.15 Data Form of Sub-channel
seems to indicate that for leadin_form 0x41 one should announce
d->start_lba as start of the leadin (e.g. -12490) and that data
block type should 2 or 3 with mode page 05h. But my drives refuse
on that.
It works with LBA -150 and data block type 0. Shrug.
*/
leadin_start = runtime;
ret = add_cue(sheet, (ctladr & 64) | 1, 0, 0, leadin_form, 0,
leadin_start);
if (ret <= 0)
goto failed;
17 years ago
d->toc_entries = ntr + 3;
/* ts A61009 */
/* a ssert(d->toc_entry == NULL); */
if (d->toc_entry != NULL) {
/* ts A61109 : this happens with appendable CDs
>>> Open question: is the existing TOC needed ? */
/* ts A61109 : for non-SAO, this sheet is thrown away later */
free((char *) d->toc_entry);
/*
libdax_msgs_submit(libdax_messenger,
d->global_index, 0x00020117,
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
"toc_entry of drive is already in use", 0, 0);
goto failed;
*/
}
if (session->firsttrack + ntr - 1 > 99) {
libdax_msgs_submit(libdax_messenger, -1, 0x0002019b,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
"CD track number exceeds 99", 0, 0);
goto failed;
}
session->lasttrack = session->firsttrack + ntr - 1;
d->toc_entry = calloc(d->toc_entries, sizeof(struct burn_toc_entry));
17 years ago
e = d->toc_entry;
e[0].point = 0xA0;
if (tar[0]->mode & BURN_AUDIO)
e[0].control = TOC_CONTROL_AUDIO;
else
e[0].control = TOC_CONTROL_DATA;
e[0].pmin = session->firsttrack;
17 years ago
e[0].psec = o->format;
e[0].adr = 1;
e[1].point = 0xA1;
e[1].pmin = session->lasttrack;
17 years ago
e[1].adr = 1;
if (tar[ntr - 1]->mode & BURN_AUDIO)
e[1].control = TOC_CONTROL_AUDIO;
else
e[1].control = TOC_CONTROL_DATA;
e[2].point = 0xA2;
e[2].control = e[1].control;
e[2].adr = 1;
/* ts A70121 : The pause before the first track is not really Pre-gap.
To count it as part 2 of a Pre-gap is a dirty hack. It also seems
to have caused confusion in dealing with part 1 of an eventual
real Pre-gap. mmc5r03c.pdf 6.33.3.2, 6.33.3.18 .
ts B20103 : It is not really Pre-gap with audio tracks.
*/
17 years ago
tar[0]->pregap2 = 1;
pregap = 150;
17 years ago
pform = form;
for (i = 0; i < ntr; i++) {
type_to_form(tar[i]->mode, &ctladr, &form);
if (tar[i]->mode & BURN_SCMS)
scms = 0x80;
else
scms = 0;
if (tar[i]->isrc.has_isrc) {
ret = add_isrc_cue(sheet, ctladr,
i + session->firsttrack, &(tar[i]->isrc));
if (ret <= 0)
goto failed;
}
#ifdef Libburn_track_multi_indeX
for(j = 0; j < tar[i]->indices || j < 2; j++) {
if(tar[i]->index[j] == 0x7fffffff) {
if (j > 1)
break;
if (j == 0 && i > 0)
continue;
/* force existence of mandatory index */
tar[i]->index[j] = 0;
} else if (j == 0) {
tar[i]->index[j] = 0;
} else if (j == 1 && tar[i]->index[0] == 0x7fffffff) {
tar[i]->index[j] = 0;
}
if (j == 1) {
tar[i]->entry = &e[3 + i];
e[3 + i].point = i + session->firsttrack;
burn_lba_to_msf(runtime, &m, &s, &f);
e[3 + i].pmin = m;
e[3 + i].psec = s;
e[3 + i].pframe = f;
e[3 + i].adr = 1;
e[3 + i].control = type_to_ctrl(tar[i]->mode);
}
/* >>> ??? else if j == 0 && mode change to -data :
Extended pregap */;
/* >>> ??? user defined pregap ? */;
/* >>> check index with track size */;
tar[i]->index[j] += runtime;
ret = add_cue(sheet, ctladr | 1,
i + session->firsttrack, j, form, scms,
tar[i]->index[j]);
if (ret <= 0)
goto failed;
runtime += pregap;
pregap = 0;
}
#else /* Libburn_track_multi_indeX */
if (i == 0) {
ret = add_cue(sheet, ctladr | 1, session->firsttrack,
0, form, 0, runtime);
if (ret <= 0)
goto failed;
runtime += 150;
} else if (pform != form) {
/* ts A70121 : This seems to be thw wrong test. Correct would
be to compare tar[]->mode or bit2 of ctladr.
*/
ret = add_cue(sheet, ctladr | 1,
i + session->firsttrack, 0, form, scms,
runtime);
if (ret <= 0)
goto failed;
17 years ago
runtime += 150;
/* XXX fix pregap interval 1 for data tracks */
/* ts A60813 silence righteous compiler warning about C++ style comments
This is possibly not a comment but rather a trace of Derek Foreman
experiments. Thus not to be beautified - but to be preserved rectified.
/ / if (!(form & BURN_AUDIO))
/ / tar[i]->pregap1 = 1;
*/
/* ts A70121 : it is unclear why (form & BURN_AUDIO) should prevent pregap1.
I believe, correct would be:
runtime += 75;
tar[i]->pregap1 = 1;
The test for pform != form is wrong anyway.
Next one has to care for Post-gap: table 555 in mmc5r03c.pdf does not
show any although 6.33.3.19 would prescribe some.
Nobody seems to have ever tested this situation, up to now.
It is banned for now in burn_disc_write().
Warning have been placed in libburn.h .
*/
17 years ago
tar[i]->pregap2 = 1;
}
/* XXX HERE IS WHERE WE DO INDICES IN THE CUE SHEET */
/* XXX and we should make sure the gaps conform to ecma-130... */
tar[i]->entry = &e[3 + i];
e[3 + i].point = i + session->firsttrack;
17 years ago
burn_lba_to_msf(runtime, &m, &s, &f);
e[3 + i].pmin = m;
e[3 + i].psec = s;
e[3 + i].pframe = f;
e[3 + i].adr = 1;
e[3 + i].control = type_to_ctrl(tar[i]->mode);
ret = add_cue(sheet, ctladr | 1, i + session->firsttrack,
1, form, scms, runtime);
if (ret <= 0)
goto failed;
#endif /* ! Libburn_track_multi_indeX */
/* ts A70125 :
Still not understanding the sense behind linking tracks,
i decided to at least enforce the MMC specs' minimum
track length.
*/
track_length = burn_track_get_sectors(tar[i]);
if (track_length < 300 && !burn_track_is_open_ended(tar[i])) {
track_length = 300;
if (!tar[i]->pad)
tar[i]->pad = 1;
burn_track_set_sectors(tar[i], track_length);
}
runtime += track_length;
17 years ago
/* if we're padding, we'll clear any current shortage.
if we're not, we'll slip toc entries by a sector every time our
shortage is more than a sector
XXX this is untested :)
*/
if (!tar[i]->pad) {
rem += burn_track_get_shortage(tar[i]);
/* ts A61101 : I doubt that linking would yield a
desireable effect. With TAO it is
counterproductive in any way.
*/
if (o->write_type == BURN_WRITE_TAO)
tar[i]->source->next = NULL;
else
if (i +1 != ntr)
tar[i]->source->next = tar[i+1]->source;
17 years ago
} else if (rem) {
rem = 0;
runtime++;
}
if (rem > burn_sector_length(tar[i]->mode)) {
rem -= burn_sector_length(tar[i]->mode);
runtime--;
}
pform = form;
}
burn_lba_to_msf(runtime, &m, &s, &f);
e[2].pmin = m;
e[2].psec = s;
e[2].pframe = f;
ret = add_cue(sheet, ctladr | 1, 0xAA, 1, 1, 0, runtime);
if (ret <= 0)
goto failed;
17 years ago
return sheet;
failed:;
if (sheet != NULL)
free((char *) sheet);
return NULL;
17 years ago
}
int burn_sector_length(int tracktype)
{
if (tracktype & BURN_AUDIO)
return 2352;
if (tracktype & BURN_MODE_RAW)
return 2352;
if (tracktype & BURN_MODE1)
return 2048;
/* ts A61009 */
/* a ssert(0); */
return -1;
17 years ago
}
int burn_subcode_length(int tracktype)
{
if (tracktype & BURN_SUBCODE_P16)
return 16;
if ((tracktype & BURN_SUBCODE_P96) || (tracktype & BURN_SUBCODE_R96))
return 96;
return 0;
}
int burn_write_leadin(struct burn_write_opts *o,
struct burn_session *s, int first)
{
struct burn_drive *d = o->drive;
int count;
d->busy = BURN_DRIVE_WRITING_LEADIN;
if (first)
count = 0 - d->alba - 150;
else
count = 4500;
d->progress.start_sector = d->alba;
d->progress.sectors = count;
d->progress.sector = 0;
while (count != 0) {
if (!sector_toc(o, s->track[0]->mode))
return 0;
count--;
d->progress.sector++;
}
d->busy = BURN_DRIVE_WRITING;
return 1;
}
int burn_write_leadout(struct burn_write_opts *o,
int first, unsigned char control, int mode)
{
struct burn_drive *d = o->drive;
int count;
d->busy = BURN_DRIVE_WRITING_LEADOUT;
d->rlba = -150;
if (first)
count = 6750;
else
count = 2250;
d->progress.start_sector = d->alba;
d->progress.sectors = count;
d->progress.sector = 0;
while (count != 0) {
if (!sector_lout(o, control, mode))
return 0;
count--;
d->progress.sector++;
}
d->busy = BURN_DRIVE_WRITING;
return 1;
}
static int burn_create_text_packs(struct burn_write_opts *o,
struct burn_session *s,
int flag)
{
int ret, num_packs = 0;
unsigned char *text_packs = NULL;
ret = burn_cdtext_from_session(s, &text_packs, &num_packs, 0);
if (ret > 0) {
if (o->text_packs != NULL)
free(o->text_packs);
o->text_packs = text_packs;
o->num_text_packs = num_packs;
}
return(ret);
}
static int burn_write_leadin_cdtext(struct burn_write_opts *o,
struct burn_session *s, int flag)
{
int ret, i, j, si, lba, sub_cursor = 0, err, write_lba, sectors = 0;
int self_made_text_packs = 0;
unsigned char *subdata = NULL;
struct burn_drive *d = o->drive;
struct buffer *buf = NULL;
enum burn_drive_status was_busy = o->drive->busy;
#ifdef Libburn_debug_cd_texT
unsigned char *packs;
#endif
if (o->num_text_packs <= 0) {
if (o->text_packs != NULL)
{ret = 1; goto ex;}
/* Try to create CD-TEXT from .cdtext_* of session and track */