From 8cdba24ff9cee41078c18c55fce8ae75eac074d0 Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Sun, 25 Dec 2011 10:39:05 +0000 Subject: [PATCH] New API calls burn_cdtext_from_packfile() and burn_session_by_cue_file() --- cdrskin/cdrskin_timestamp.h | 2 +- libburn/cdtext.c | 102 ++++- libburn/file.c | 32 +- libburn/libburn.h | 93 ++++- libburn/libburn.ver | 2 + libburn/libdax_msgs.h | 13 +- libburn/source.c | 7 +- libburn/structure.c | 794 ++++++++++++++++++++++++++++++++++++ 8 files changed, 1010 insertions(+), 35 deletions(-) diff --git a/cdrskin/cdrskin_timestamp.h b/cdrskin/cdrskin_timestamp.h index ba9f4db..ef32d8f 100644 --- a/cdrskin/cdrskin_timestamp.h +++ b/cdrskin/cdrskin_timestamp.h @@ -1 +1 @@ -#define Cdrskin_timestamP "2011.12.22.112138" +#define Cdrskin_timestamP "2011.12.25.103852" diff --git a/libburn/cdtext.c b/libburn/cdtext.c index 3cc2e1e..f3c3657 100755 --- a/libburn/cdtext.c +++ b/libburn/cdtext.c @@ -438,8 +438,11 @@ ex:; else if (flag & 1) ret = -1; BURN_FREE_MEM(crs.packs); - } else if (crs.num_packs > 0) { - *text_packs = crs.packs; + } else { + if (crs.num_packs > 0) + *text_packs = crs.packs; + else + BURN_FREE_MEM(crs.packs); *num_packs = crs.num_packs; } return(ret); @@ -616,6 +619,7 @@ static int v07t_cdtext_to_track(struct burn_track *track, int block, } +/* ts B11215 API */ int burn_session_input_sheet_v07t(struct burn_session *session, char *path, int block, int flag) { @@ -1006,3 +1010,97 @@ ex:; return ret; } + +/* ts B11221 API */ +int burn_cdtext_from_packfile(char *path, unsigned char **text_packs, + int *num_packs, int flag) +{ + int ret = 0, residue = 0; + struct stat stbuf; + FILE *fp = NULL; + unsigned char head[4], tail[1]; + char *msg = NULL; + + BURN_ALLOC_MEM(msg, char, 4096); + + if (stat(path, &stbuf) == -1) { +cannot_open:; + sprintf(msg, "Cannot open CD-TEXT pack file '%.4000s'", path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020198, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), errno, 0); + ret = 0; goto ex; + } + if (!S_ISREG(stbuf.st_mode)) + goto not_a_textfile; + residue = (stbuf.st_size % 18); + if(residue != 4 && residue != 0 && residue != 1) { +not_a_textfile:; + sprintf(msg, + "File is not of usable type or content for CD-TEXT packs: '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020198, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + if (stbuf.st_size < 18) + goto not_a_textfile; + + fp = fopen(path, "rb"); + if (fp == NULL) + goto cannot_open; + if (residue == 4) { /* This is for files from cdrecord -vv -toc */ + ret = fread(head, 4, 1, fp); + if (ret != 1) { +cannot_read:; + sprintf(msg, + "Cannot read all bytes from CD-TEXT pack file '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020198, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), errno, 0); + ret = 0; goto ex; + } + if (head[0] * 256 + head[1] != stbuf.st_size - 2) + goto not_a_textfile; + } + *num_packs = (stbuf.st_size - residue) / 18; + if (*num_packs > 2048) { + /* Each block can have 256 text packs. + There are 8 blocks at most. */ + sprintf(msg, + "CD-Text pack file too large (max. 36864 bytes): '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x0002018b, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), errno, 0); + ret = 0; goto ex; + } + + BURN_ALLOC_MEM(*text_packs, unsigned char, *num_packs * 18); + ret = fread(*text_packs, *num_packs * 18, 1, fp); + if (ret != 1) + goto cannot_read; + if (residue == 1) { /* This is for Sony CDTEXT files */ + ret = fread(tail, 1, 1, fp); + if (ret != 1) + goto cannot_read; + if (tail[0] != 0) + goto not_a_textfile; + } + + ret= 1; +ex:; + if (ret <= 0) { + BURN_FREE_MEM(*text_packs); + *text_packs = NULL; + *num_packs = 0; + } + if (fp != NULL) + fclose(fp); + BURN_FREE_MEM(msg); + return ret; +} + + diff --git a/libburn/file.c b/libburn/file.c index 65f195d..f878dae 100644 --- a/libburn/file.c +++ b/libburn/file.c @@ -26,6 +26,7 @@ #include "libburn.h" #include "file.h" #include "async.h" +#include "init.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; @@ -175,7 +176,7 @@ struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size) if (datafd == -1) return NULL; - fs = calloc(1, sizeof(struct burn_source_file)); + fs = burn_alloc_mem(sizeof(struct burn_source_file), 1, 0); if (fs == NULL) /* ts A70825 */ return NULL; fs->datafd = datafd; @@ -510,7 +511,7 @@ struct burn_source *burn_fifo_source_new(struct burn_source *inp, "Desired fifo buffer too small", 0, 0); return NULL; } - fs = calloc(1, sizeof(struct burn_source_fifo)); + fs = burn_alloc_mem(sizeof(struct burn_source_fifo), 1, 0); if (fs == NULL) return NULL; fs->is_started = 0; @@ -757,9 +758,12 @@ int burn_fifo_fill(struct burn_source *source, int bufsize, int flag) static void offst_free(struct burn_source *source); -static struct burn_source_offst *offst_auth(struct burn_source *source) +/* @param flag bit0 = do not check for burn_source_offst, do not return NULL +*/ +static struct burn_source_offst *offst_auth(struct burn_source *source, + int flag) { - if (source->free_data != offst_free) { + if (source->free_data != offst_free && !(flag & 1)) { libdax_msgs_submit(libdax_messenger, -1, 0x0002017a, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Expected offset source object as parameter", @@ -773,7 +777,7 @@ static off_t offst_get_size(struct burn_source *source) { struct burn_source_offst *fs; - if ((fs = offst_auth(source)) == NULL) + if ((fs = offst_auth(source, 0)) == NULL) return (off_t) 0; return fs->size; } @@ -782,7 +786,7 @@ static int offst_set_size(struct burn_source *source, off_t size) { struct burn_source_offst *fs; - if ((fs = offst_auth(source)) == NULL) + if ((fs = offst_auth(source, 0)) == NULL) return 0; fs->size = size; return 1; @@ -792,12 +796,12 @@ static void offst_free(struct burn_source *source) { struct burn_source_offst *fs; - if ((fs = offst_auth(source)) == NULL) + if ((fs = offst_auth(source, 0)) == NULL) return; if (fs->prev != NULL) - offst_auth(fs->prev)->next = fs->next; + offst_auth(fs->prev, 1)->next = fs->next; if (fs->next != NULL) - offst_auth(fs->next)->prev = fs->prev; + offst_auth(fs->next, 1)->prev = fs->prev; if (fs->inp != NULL) burn_source_free(fs->inp); /* i.e. decrement refcount */ free(source->data); @@ -809,13 +813,13 @@ static int offst_read(struct burn_source *source, unsigned char *buffer, int ret, to_read, todo; struct burn_source_offst *fs; - if ((fs = offst_auth(source)) == NULL) + if ((fs = offst_auth(source, 0)) == NULL) return -1; /* Eventually skip bytes up to start position */; if (!fs->running) { if (fs->prev != NULL) - fs->pos = offst_auth(fs->prev)->pos; + fs->pos = offst_auth(fs->prev, 1)->pos; fs->running= 1; } if(fs->pos < fs->start) { @@ -850,7 +854,7 @@ static int offst_cancel(struct burn_source *source) int ret; struct burn_source_offst *fs; - if ((fs = offst_auth(source)) == NULL) + if ((fs = offst_auth(source, 0)) == NULL) return -1; ret = burn_source_cancel(fs->inp); return ret; @@ -864,7 +868,7 @@ struct burn_source *burn_offst_source_new( struct burn_source_offst *fs, *prev_fs = NULL; if (prev != NULL) - if ((prev_fs = offst_auth(prev)) == NULL) + if ((prev_fs = offst_auth(prev, 0)) == NULL) return NULL; /* Not type burn_source_offst */ fs = calloc(1, sizeof(struct burn_source_offst)); @@ -889,7 +893,7 @@ struct burn_source *burn_offst_source_new( fs->next = NULL; if (prev != NULL) { if (prev_fs->next != NULL) { - offst_auth(prev_fs->next)->prev = src; + offst_auth(prev_fs->next, 1)->prev = src; fs->next = prev_fs->next; } prev_fs->next = src; diff --git a/libburn/libburn.h b/libburn/libburn.h index be2538f..1678f50 100644 --- a/libburn/libburn.h +++ b/libburn/libburn.h @@ -1850,7 +1850,56 @@ int burn_disc_add_session(struct burn_disc *d, struct burn_session *s, int burn_disc_remove_session(struct burn_disc *d, struct burn_session *s); -/** Create a track (for TAO recording, or to put in a session) */ +/* ts B11219 */ +/** Read a CDRWIN cue sheet file and equip the session object by tracks and + CD-TEXT according to the content of the file. + For a description of CDRWIN file format see + http://digitalx.org/cue-sheet/syntax/ +>>> supported commands: CDTEXTFILE PERFORMER REM SONGWRITER TITLE +>>> partly supported commands: CATALOG FILE ISRC INDEX TRACK +>>> supported FILE types: BINARY MOTOROLA +>>> supported FLAGS: +>>> supported TRACK datatypes: AUDIO MODE1/2048 +>>> ignored commands: POSTGAP PREGAP FLAGS +>>> ignored INDEX numbers: 00, 02 to 99 +>>> ignored CUE SHEET features: CATALOG and ISRC (but supported as CD-TEXT) +>>> ignored FLAGS: DCP 4CH PRE SCMS +>>> not allowed: mixing of ADUIO and MODE1/2048 +>>> not allowed: unsupported FILE types +>>> not allowed: unsupported TRACK datatypes +>>> +>>> + @param session Session where to attach tracks. It must not yet have + tracks or else this call will fail. + @param path Filesystem address of the CDRWIN cue sheet file. + Normally with suffix .cue + @param fifo_size Number of bytes in fifo. This will be rounded up by + the block size of the track mode. <= 0 means no fifo. + @param fifo Returns a reference to the burn_source object that + was installed as fifo between FILE and the track + burn sources. One may use this to inquire the fifo + state. Dispose it by burn_source_free() when no longer + needed. It is permissible to pass this parameter to + libburn as NULL, in order to immediately drop ownership + on the fifo. + @param text_packs Returns pre-formatted CD-TEXT packs resulting from + cue sheet command CDTEXTFILE. To be used with call + burn_write_opts_set_leadin_text(). + It is permissible to pass this parameter to libburn + as NULL, in order to disable CDTEXTFILE. + @param num_packs Returns the number of 18 byte records in text_packs. + @param flag Bitfield for control purposes. + bit0= Do not attach CD-TEXT information to session and + tracks. Do not load text_packs. + @return > 0 indicates success, <= 0 indicates failure + @since 1.2.0 +*/ +int burn_session_by_cue_file(struct burn_session *session, + char *path, int fifo_size, struct burn_source **fifo, + unsigned char **text_packs, int *num_packs, int flag); + + +/** Create a track */ struct burn_track *burn_track_create(void); /** Free a track @@ -2074,6 +2123,9 @@ int burn_session_set_cdtext(struct burn_session *s, int block, See above burn_session_set_cdtext(). @param payload Will return a pointer to text or binary bytes. Not a copy of data. Do not free() this address. + If no text attribute is attached for pack type and + block, then payload is returned as NULL. The return + value will not indicate error in this case. @pram length Will return the number of bytes pointed to by payload. Including terminating 0-bytes. @param flag Bitfield for control purposes. Unused yet. Submit 0. @@ -2145,6 +2197,26 @@ int burn_cdtext_from_session(struct burn_session *s, int burn_session_dispose_cdtext(struct burn_session *s, int block); +/* ts B11221*/ +/** Read an array of CD-TEXT packs from a file. This array should be suitable + for burn_write_opts_set_leadin_text(). + The function tolerates and removes 4-byte headers as produced by + cdrecord -vv -toc, if this header tells the correct number of bytes which + matches the file size. If no 4-byte header is present, then the function + tolerates and removes a trailing 0-byte as of Sony specs. + @param path Filesystem address of the CD-TEXT pack file. + Normally with suffix .cdt or .dat + @param text_packs Will return the buffer with the CD-TEXT packs. + Dispose by free() when no longer needed. + @param num_packs Will return the number of 18 byte text packs. + @param flag Bitfield for control purposes. Unused yet.Submit 0. + @return 0 is success, <= 0 failure + @since 1.2.0 +*/ +int burn_cdtext_from_packfile(char *path, unsigned char **text_packs, + int *num_packs, int flag); + + /** Define the data in a track @param t the track to define @param offset The lib will write this many 0s before start of data @@ -2214,6 +2286,9 @@ int burn_track_set_cdtext(struct burn_track *t, int block, See above burn_track_set_cdtext(). @param payload Will return a pointer to text bytes. Not a copy of data. Do not free() this address. + If no text attribute is attached for pack type and + block, then payload is returned as NULL. The return + value will not indicate error in this case. @pram length Will return the number of bytes pointed to by payload. Including terminating 0-bytes. @param flag Bitfield for control purposes. Unused yet. Submit 0. @@ -2424,14 +2499,15 @@ struct burn_source *burn_offst_source_new( /** Creates a fifo which acts as proxy for an already existing data source. The fifo provides a ring buffer which shall smoothen the data stream between burn_source and writer thread. Each fifo serves only for one - data source and gets attached to one track as its only data source - by burn_track_set_source(). + data source. It may be attached to one track as its only data source + by burn_track_set_source(), or it may be used as input for other burn + sources. A fifo starts its life in "standby" mode with no buffer space allocated. - As soon as its track requires bytes, the fifo establishes a worker thread - and allocates its buffer. After input has ended and all buffer content is - consumed, the buffer space gets freed and the worker thread ends. - This happens asynchronously. So expect two buffers and worker threads to - exist for a short time between tracks. Be modest in your size demands if + As soon as its consumer requires bytes, the fifo establishes a worker + thread and allocates its buffer. After input has ended and all buffer + content is consumed, the buffer space gets freed and the worker thread + ends. This happens asynchronously. So expect two buffers and worker threads + to exist for a short time between tracks. Be modest in your size demands if multiple tracks are to be expected. @param inp The burn_source for which the fifo shall act as proxy. It can be disposed by burn_source_free() immediately @@ -3699,7 +3775,6 @@ int libdax_audioxtr_detach_fd(struct libdax_audioxtr *o, int *fd, int flag); int libdax_audioxtr_destroy(struct libdax_audioxtr **xtr, int flag); - #ifndef DOXYGEN BURN_END_DECLS diff --git a/libburn/libburn.ver b/libburn/libburn.ver index 7904be6..9f8abb6 100644 --- a/libburn/libburn.ver +++ b/libburn/libburn.ver @@ -5,6 +5,7 @@ burn_abort_pacifier; burn_allow_drive_role_4; burn_allow_untested_profiles; burn_cdtext_from_session; +burn_cdtext_from_packfile; burn_disc_add_session; burn_disc_available_space; burn_disc_close_damaged; @@ -113,6 +114,7 @@ burn_read_opts_transfer_damaged_blocks; burn_scsi_transport_id; burn_sectors_to_msf; burn_session_add_track; +burn_session_by_cue_file; burn_session_create; burn_session_dispose_cdtext; burn_session_free; diff --git a/libburn/libdax_msgs.h b/libburn/libdax_msgs.h index b6e44ad..c093291 100644 --- a/libburn/libdax_msgs.h +++ b/libburn/libdax_msgs.h @@ -583,11 +583,14 @@ Range "scdbackup" : 0x00020000 to 0x0002ffff 0x0002018e (FAILURE,HIGH) = Too many CD-TEXT packs in block 0x0002018f (FAILURE,HIGH) = CD-TEXT pack CRC mismatch 0x00020190 (WARNING,HIGH) = CD-TEXT pack CRC mismatch had to be corrected - 0x00020191 (FAILURE,HIGH) = Unknown v07t parameter - 0x00020192 (FAILURE,HIGH) = Input sheet v07t sequence error - 0x00020193 (FAILURE,HIGH) = Input sheet v07t readability problem - 0x00020194 (FAILURE,HIGH) = Input sheet v07t syntax error - 0x00020195 (WARNING,HIGH) = Input sheet v07t warning + 0x00020191 (FAILURE,HIGH) = Unknown parameter in text input file + 0x00020192 (FAILURE,HIGH) = Text input file sequence error + 0x00020193 (FAILURE,HIGH) = Text input file readability problem + 0x00020194 (FAILURE,HIGH) = Text input file syntax error or specs violation + 0x00020195 (WARNING,HIGH) = Text input file warning + 0x00020196 (FAILURE,HIGH) = Session has already defined tracks + 0x00020197 (FAILURE,HIGH) = Unsupported text input file feature + 0x00020198 (FAILURE,HIGH) = CD-TEXT pack file readability problem libdax_audioxtr: diff --git a/libburn/source.c b/libburn/source.c index a9e04a3..a2d8aae 100644 --- a/libburn/source.c +++ b/libburn/source.c @@ -15,6 +15,7 @@ #include "libburn.h" #include "source.h" #include "structure.h" +#include "init.h" void burn_source_free(struct burn_source *src) { @@ -41,12 +42,10 @@ struct burn_source *burn_source_new(void) { struct burn_source *out; - out = calloc(1, sizeof(struct burn_source)); - - /* ts A70825 */ + /* ts A70825 , B11219 */ + out = burn_alloc_mem(sizeof(struct burn_source), 1, 0); if (out == NULL) return NULL; - memset((char *) out, 0, sizeof(struct burn_source)); out->refcount = 1; return out; diff --git a/libburn/structure.c b/libburn/structure.c index 07dc427..cca0d3a 100644 --- a/libburn/structure.c +++ b/libburn/structure.c @@ -15,11 +15,18 @@ #include #include #include +#include +#include +#include +#include +#include + #include "libburn.h" #include "structure.h" #include "write.h" #include "debug.h" #include "init.h" +#include "util.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; @@ -849,6 +856,11 @@ int burn_track_get_cdtext(struct burn_track *t, int block, if (burn_cdtext_check_blockno(block) <= 0) return 0; + if (t->cdtext[block] == NULL) { + *payload = NULL; + *length = 0; + return 1; + } ret = burn_cdtext_get(t->cdtext[block], pack_type, pack_type_name, payload, length, 0); return ret; @@ -899,6 +911,12 @@ int burn_session_get_cdtext(struct burn_session *s, int block, if (burn_cdtext_check_blockno(block) <= 0) return 0; + + if (s->cdtext[block] == NULL) { + *payload = NULL; + *length = 0; + return 1; + } ret = burn_cdtext_get(s->cdtext[block], pack_type, pack_type_name, payload, length, 0); return ret; @@ -962,3 +980,779 @@ int burn_session_dispose_cdtext(struct burn_session *s, int block) } +/* --------------------- Reading CDRWIN cue sheet files ----------------- */ + + +struct burn_cue_file_cursor { + char *cdtextfile; + char *source_file; + off_t source_size; + struct burn_source *file_source; + int fifo_size; + struct burn_source *fifo; + int swap_audio_bytes; + int no_cdtext; + struct burn_source *offst_source; + int current_file_ba; + struct burn_track *prev_track; + int prev_file_ba; + int prev_block_size; + struct burn_track *track; + int track_no; + int track_has_index; + int track_has_source; + int block_size; + int block_size_locked; + int flags; +}; + + +static int cue_crs_new(struct burn_cue_file_cursor **reply, int flag) +{ + int ret; + struct burn_cue_file_cursor *crs; + + BURN_ALLOC_MEM(crs, struct burn_cue_file_cursor, 1); + crs->cdtextfile = NULL; + crs->source_file = NULL; + crs->source_size = -1; + crs->file_source = NULL; + crs->fifo_size = 0; + crs->fifo = NULL; + crs->swap_audio_bytes = 0; + crs->no_cdtext = 0; + crs->offst_source = NULL; + crs->current_file_ba = -1000000000; + crs->prev_track = NULL; + crs->prev_file_ba = -1000000000; + crs->prev_block_size = 0; + crs->track = NULL; + crs->track_no = 0; + crs->track_has_index = 0; + crs->track_has_source = 0; + crs->block_size = 0; + crs->block_size_locked = 0; + crs->flags = 0; + + *reply = crs; + ret = 1; +ex:; + return ret; +} + + +static int cue_crs_destroy(struct burn_cue_file_cursor **victim, int flag) +{ + struct burn_cue_file_cursor *crs; + + if (*victim == NULL) + return 2; + crs = *victim; + if (crs->cdtextfile != NULL) + free(crs->cdtextfile); + if (crs->source_file != NULL) + free(crs->source_file); + if (crs->file_source != NULL) + burn_source_free(crs->file_source); + if (crs->fifo != NULL) + burn_source_free(crs->fifo); + if (crs->offst_source != NULL) + burn_source_free(crs->offst_source); + if (crs->prev_track != NULL) + burn_track_free(crs->prev_track); + if (crs->track != NULL) + burn_track_free(crs->track); + BURN_FREE_MEM(crs); + *victim = NULL; + return 1; +} + + +static char *cue_unquote_text(char *text, int flag) +{ + char *ept, *spt; + + spt = text; + for (ept = text + strlen(text); ept > text; ept--) + if (*(ept - 1) != 32 && *(ept - 1) != 9) + break; + if (text[0] == '"') { + spt = text + 1; + if (ept > spt) + if (*(ept - 1) == '"') + ept--; + } + *ept = 0; + return spt; +} + + +/* @param flag bit0= insist in having a track object + bit1= remove quotation marks if present +*/ +static int cue_set_cdtext(struct burn_session *session, + struct burn_track *track, int pack_type, char *text, + struct burn_cue_file_cursor *crs, int flag) +{ + int ret; + char *payload; + + if (crs->no_cdtext == 1) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020195, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: Being set to ignore all CD-TEXT aspects", + 0, 0); + crs->no_cdtext = 2; + } + if (crs->no_cdtext) + return 2; + if ((flag & 1) && track == NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Track attribute set before first track in cue sheet file", + 0, 0); + ret = 0; goto ex; + } + if (flag & 2) + payload = cue_unquote_text(text, 0); + else + payload = text; + if (track != NULL) { + ret = burn_track_set_cdtext(track, 0, pack_type, "", + (unsigned char *) payload, + strlen(payload) + 1, 0); + } else { + ret = burn_session_set_cdtext(session, 0, pack_type, "", + (unsigned char *) payload, + strlen(payload) + 1, 0); + } +ex:; + return ret; +} + + +static int cue_attach_track(struct burn_session *session, + struct burn_cue_file_cursor *crs, int flag) +{ + int ret; + + if (crs->track == NULL) + return 2; + + if (!crs->track_has_source) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: TRACK without INDEX 01", 0, 0); + return 0; + } + if (crs->track_no > 1 && session->tracks == 0) { + + /* >>> ??? implement ? */; + + libdax_msgs_submit(libdax_messenger, -1, 0x00020195, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: TRACK numbering does not start with 01", + 0, 0); + } + ret = burn_session_add_track(session, crs->track, BURN_POS_END); + if (ret <= 0) + return ret; + if (crs->prev_track != NULL) + burn_track_free(crs->prev_track); /* release reference */ + crs->prev_track = crs->track; + crs->prev_file_ba = crs->current_file_ba; + crs->prev_block_size = crs->block_size; + crs->track = NULL; + crs->track_has_index = crs->track_has_source = 0; + crs->current_file_ba = -1; + if (!crs->block_size_locked) + crs->block_size = 0; + return 1; +} + + +/* @param flag bit0= do not alter the content of *payload + do not change *payload +*/ +static int cue_read_number(char **payload, int *number, int flag) +{ + int ret, at_end = 0; + char *apt, *msg = NULL; + + for(apt = *payload; *apt != 0 && *apt != 32 && *apt != 9; apt++); + if (*apt == 0) + at_end = 1; + else if (!(flag & 1)) + *apt = 0; + ret = sscanf(*payload, "%d", number); + if (ret != 1) { + BURN_ALLOC_MEM(msg, char, 4096); + sprintf(msg, + "Unsuitable number in cue sheet file: '%.4000s'", + *payload); + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + /* Find start of next argument */ + if (!at_end) + for (apt++; *apt == 32 || *apt == 9; apt++); + if (!(flag & 1)) + *payload = apt; + + ret = 1; +ex: + BURN_FREE_MEM(msg); + return ret; +} + + +static int cue_create_file_source(char *path, struct burn_cue_file_cursor *crs, + int flag) +{ + int fd, ret; + char *msg = NULL; + + BURN_ALLOC_MEM(msg, char, 4096); + + fd = open(path, O_RDONLY); + if (fd == -1) { + sprintf(msg, "In cue sheet: Cannot open FILE '%.4000s'", path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), errno, 0); + ret = 0; goto ex; + } + crs->file_source = burn_fd_source_new(fd, -1, crs->source_size); + if (crs->file_source == NULL) { + ret = -1; goto ex; + } + + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +static int cue_interpret_line(struct burn_session *session, char *line, + struct burn_cue_file_cursor *crs, int flag) +{ + int ret, mode, index_no, minute, second, frame, file_ba, chunks; + int block_size; + off_t size; + char *cmd, *apt, *msg = NULL, msf[3], *msf_pt, *cpt, *filetype; + struct burn_source *src, *inp_src; + enum burn_source_status source_status; + struct stat stbuf; + + BURN_ALLOC_MEM(msg, char, 4096); + + if (line[0] == 0 || line[0] == '#') { + ret = 1; goto ex; + } + + for (cmd = line; *cmd == 32 || *cmd == 9; cmd++); + for(apt = cmd; *apt != 0 && *apt != 32 && *apt != 9; apt++); + if (*apt != 0) { + *apt = 0; + for (apt++; *apt == 32 || *apt == 9; apt++); + } + + if (strcmp(cmd, "CATALOG") == 0) { + ret = cue_set_cdtext(session, NULL, 0x8e, apt, crs, 0); + if (ret <= 0) + goto ex; + + /* >>> ??? data for burn_write_opts_set_mediacatalog ? + (not implemented yet in SAO) */ + + + } else if (strcmp(cmd, "CDTEXTFILE") == 0) { + if (crs->no_cdtext) { + ret = 1; goto ex; + } + apt = cue_unquote_text(apt, 0); + if (crs->cdtextfile != NULL) + free(crs->cdtextfile); + crs->cdtextfile = strdup(apt); + if (crs->cdtextfile == NULL) { +out_of_mem:; + libdax_msgs_submit(libdax_messenger, -1, 0x00000003, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Out of virtual memory", 0, 0); + ret = -1; goto ex; + } + + } else if (strcmp(cmd, "FILE") == 0) { + if (crs->file_source != NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: Multiple occurences of FILE", + 0, 0); + ret = 0; goto ex; + } + /* Obtain type */ + for (cpt = apt + (strlen(apt) - 1); + cpt > apt && (*cpt == 32 || *cpt == 9); cpt--); + cpt[1] = 0; + for (; cpt > apt && *cpt != 32 && *cpt != 9; cpt--); + if (cpt <= apt) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: FILE without type word", + 0, 0); + ret = 0; goto ex; + } + *cpt = 0; + filetype = cpt + 1; + if (strcmp(filetype, "BINARY") == 0) { + crs->swap_audio_bytes = 0; + } else if (strcmp(filetype, "MOTOROLA") == 0) { + crs->swap_audio_bytes = 1; + } else if (strcmp(filetype, "WAVE") == 0 && 0 ) { + + /* >>> Use libdax_audioxtr_* functions to extract */; + + } else { + sprintf(msg, + "In cue sheet file: Unsupported FILE type '%.4000s'", + filetype); + libdax_msgs_submit(libdax_messenger, -1, 0x00020197, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + + apt = cue_unquote_text(apt, 0); + if (*apt == 0) + ret = -1; + else + ret = stat(apt, &stbuf); + if (ret == -1) { +not_usable_file:; + sprintf(msg, + "In cue sheet file: Unusable FILE '%.4000s'", + apt); + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + if (!S_ISREG(stbuf.st_mode)) + goto not_usable_file; + crs->source_size = stbuf.st_size; + if (crs->source_file != NULL) + free(crs->source_file); + crs->source_file = strdup(apt); + if (crs->source_file == NULL) + goto out_of_mem; + ret = cue_create_file_source(apt, crs, 0); + if (ret <= 0) + goto ex; + + } else if (strcmp(cmd, "FLAGS") == 0) { + + /* >>> Interpret DCP 4CH PRE SCMS into crs->flags */; + + } else if (strcmp(cmd, "INDEX") == 0) { + if (crs->track == NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: INDEX found before TRACK", + 0, 0); + ret = 0; goto ex; + } + + ret = cue_read_number(&apt, &index_no, 0); + if (ret <= 0) + goto ex; + + /* Obtain time point */ + if (strlen(apt) < 8) { +no_time_point:; + sprintf(msg, + "Inappropriate cue sheet file index time point '%.4000s'", + apt); + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + if (apt[2] != ':' || apt[5] != ':' || + (apt[8] != 0 && apt[8] != 32 && apt[8] != 9)) + goto no_time_point; + msf[2] = 0; + msf_pt = msf; + strncpy(msf, apt, 2); + ret = cue_read_number(&msf_pt, &minute, 1); + if (ret <= 0) + goto ex; + strncpy(msf, apt + 3, 2); + ret = cue_read_number(&msf_pt, &second, 1); + if (ret <= 0) + goto ex; + strncpy(msf, apt + 6, 2); + ret = cue_read_number(&msf_pt, &frame, 1); + if (ret <= 0) + goto ex; + + file_ba = ((minute * 60) + second ) * 75 + frame; + if (file_ba <= crs->prev_file_ba) { +overlapping_ba:; + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Overlapping INDEX addresses in cue sheet file", + 0, 0); + ret = 0; goto ex; + } + + if (crs->prev_track != NULL && !crs->track_has_index) { + size = (file_ba - crs->prev_file_ba) * + crs->prev_block_size; + if (size <= 0) + goto overlapping_ba; + burn_track_set_size(crs->prev_track, size); + } + crs->track_has_index = 1; + + if (index_no != 1) { + + /* >>> what to do with index != 1 ? */; + /* >>> INDEX 00 defines start of a track pregap + Pregap and postgap still has to be properly + mapped onto track + */; + + + ret = 1; goto ex; + } + + if (crs->block_size_locked && crs->fifo == NULL && + crs->fifo_size > 0) { + /* Now that the block size is known from TRACK: + Create fifo and use it for creating the offset + sources. This will fixate the block size to one + common value. + */ + chunks = crs->fifo_size / crs->block_size + + !!(crs->fifo_size % crs->block_size); + if (chunks < 4) + chunks = 4; + crs->fifo = burn_fifo_source_new(crs->file_source, + crs->block_size, chunks, 0); + if (crs->fifo == NULL) { + ret = -1; goto ex; + } + } + if (crs->fifo != NULL) + inp_src = crs->fifo; + else + inp_src = crs->file_source; + src = burn_offst_source_new(inp_src, crs->offst_source, + (off_t) (file_ba * crs->block_size), (off_t) 0, 0); + if (src == NULL) + goto out_of_mem; + + /* >>> Alternative to above fifo creation: + Create a fifo for each track track. + This will be necessary if mixed-mode sessions get supporded. + */; + + source_status = burn_track_set_source(crs->track, src); + if (source_status != BURN_SOURCE_OK) { + ret = -1; goto ex; + } + + /* Switch current source in crs */ + if (crs->offst_source != NULL) + burn_source_free(crs->offst_source); + crs->offst_source = src; + crs->current_file_ba = file_ba; + crs->track_has_source = 1; + + } else if (strcmp(cmd, "ISRC") == 0) { + ret = cue_set_cdtext(session, crs->track, 0x8e, apt, crs, + 1 | 2); + if (ret <= 0) + goto ex; + + /* >>> ??? burn_track_set_isrc ? + (not implemented yet in SAO) */ + + } else if (strcmp(cmd, "PERFORMER") == 0) { + ret = cue_set_cdtext(session, crs->track, 0x81, apt, crs, 2); + if (ret <= 0) + goto ex; + + } else if (strcmp(cmd, "POSTGAP") == 0) { + + /* >>> ??? implement ? */; + + libdax_msgs_submit(libdax_messenger, -1, 0x00020195, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: POSTGAP command not supported", + 0, 0); + + } else if (strcmp(cmd, "PREGAP") == 0) { + + /* >>> ??? implement ? */; + + libdax_msgs_submit(libdax_messenger, -1, 0x00020195, + LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: PREGAP command not supported", + 0, 0); + + } else if (strcmp(cmd, "REM") == 0) { + ; + + } else if (strcmp(cmd, "SONGWRITER") == 0) { + ret = cue_set_cdtext(session, crs->track, 0x82, apt, crs, 2); + if (ret <= 0) + goto ex; + + } else if (strcmp(cmd, "TITLE") == 0) { + ret = cue_set_cdtext(session, crs->track, 0x80, apt, crs, 2); + if (ret <= 0) + goto ex; + + } else if (strcmp(cmd, "TRACK") == 0) { + if (crs->file_source == NULL) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "No FILE defined before TRACK in cue sheet file", + 0, 0); + ret = 0; goto ex; + } + /* Attach previous track to session */ + ret = cue_attach_track(session, crs, 0); + if (ret <= 0) + goto ex; + /* Create new track */; + ret = cue_read_number(&apt, &(crs->track_no), 0); + if (ret <= 0) + goto ex; + if (crs->track_no < 1 || crs->track_no > 99) { + sprintf(msg, + "Inappropriate cue sheet file track number %d", + crs->track_no); + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + if (strcmp(apt, "AUDIO") == 0) { + mode = BURN_AUDIO; + block_size = 2352; + } else if (strcmp(apt, "MODE1/2048") == 0) { + mode = BURN_MODE1; + block_size = 2048; + } else { + sprintf(msg, + "Unsupported cue sheet file track datatype '%.4000s'", + apt); + libdax_msgs_submit(libdax_messenger, -1, 0x00020197, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + if (block_size != crs->block_size && crs->block_size > 0 && + crs->block_size_locked) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020197, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "In cue sheet file: Unsupported mix track block sizes", + 0, 0); + ret = 0; goto ex; + } + crs->block_size = block_size; + + crs->track = burn_track_create(); + if (crs->track == NULL) + goto out_of_mem; + crs->track_has_source = 0; + burn_track_define_data(crs->track, 0, 0, 1, mode); + if (mode == BURN_AUDIO) + burn_track_set_byte_swap(crs->track, + !!crs->swap_audio_bytes); + + } else { + sprintf(msg, "Unknown cue sheet file command '%.4000s'", line); + libdax_msgs_submit(libdax_messenger, -1, 0x00020191, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + + ret = 1; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + + +/* ts B11216 API */ +/* @param flag bit0= do not attach CD-TEXT information to session and tracks +*/ +int burn_session_by_cue_file(struct burn_session *session, char *path, + int fifo_size, struct burn_source **fifo, + unsigned char **text_packs, int *num_packs, int flag) +{ + int ret, num_tracks, i, pack_type, length, double_byte = 0; + struct burn_track **tracks; + char *msg = NULL, *line = NULL; + unsigned char *payload; + struct stat stbuf; + FILE *fp = NULL; + struct burn_cue_file_cursor *crs; + + static unsigned char dummy_cdtext[2] = {0, 0}; + + if (fifo != NULL) + *fifo = NULL; + if (text_packs != NULL) + *text_packs = NULL; + *num_packs = 0; + + BURN_ALLOC_MEM(msg, char, 4096); + BURN_ALLOC_MEM(line, char, 4096); + ret = cue_crs_new(&crs, 0); + if (ret <= 0) + goto ex; + crs->no_cdtext = (flag & 1); + crs->fifo_size = fifo_size; + crs->block_size_locked = 1; /* No mixed sessions for now */ + + tracks = burn_session_get_tracks(session, &num_tracks); + if (num_tracks > 0) { + sprintf(msg, + "Cue sheet file reader called while session has already defined tracks"); + libdax_msgs_submit(libdax_messenger, -1, 0x00020196, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + if (stat(path, &stbuf) == -1) { +cannot_open:; + sprintf(msg, "Cannot open cue sheet file '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), errno, 0); + ret = 0; goto ex; + } + if (!S_ISREG(stbuf.st_mode)) { + sprintf(msg, + "File is not of usable type: Cue sheet file '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + + fp = fopen(path, "rb"); + if (fp == NULL) + goto cannot_open; + + while (1) { + if (burn_sfile_fgets(line, 4095, fp) == NULL) { + if (!ferror(fp)) + break; + sprintf(msg, + "Cannot read all bytes from cue sheet file '%.4000s'", + path); + libdax_msgs_submit(libdax_messenger, -1, 0x00020193, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + burn_printify(msg), 0, 0); + ret = 0; goto ex; + } + ret = cue_interpret_line(session, line, crs, 0); + if (ret <= 0) + goto ex; + } + + /* Attach last track to session */ + if (crs->track != NULL) { + /* Set track size up to end of file */ + if (crs->current_file_ba < 0) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020192, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "No INDEX 01 defined for last TRACK in cue sheet file", + 0, 0); + ret = 0; goto ex; + } + if (crs->current_file_ba * crs->block_size >= + crs->source_size) { + libdax_msgs_submit(libdax_messenger, -1, 0x00020194, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "INDEX 01 time point exceeds size of FILE from cue sheet file", + 0, 0); + ret = 0; goto ex; + } + burn_track_set_size(crs->track, crs->source_size - + (off_t) (crs->current_file_ba * crs->block_size)); + + ret = cue_attach_track(session, crs, 0); + if (ret <= 0) + goto ex; + } + if (crs->cdtextfile != NULL) { + if (text_packs == NULL) { + + /* >>> Warn of ignored text packs */; + + } else { + ret = burn_cdtext_from_packfile(crs->cdtextfile, + text_packs, num_packs, 0); + if (ret <= 0) + goto ex; + } + } + + /* Check which tracks have data of pack types where session has not */ + tracks = burn_session_get_tracks(session, &num_tracks); + for (pack_type = 0x80; pack_type < 0x8f; pack_type++) { + if (pack_type > 0x86 && pack_type != 0x8e) + continue; + ret = burn_session_get_cdtext(session, 0, pack_type, "", + &payload, &length, 0); + if (ret <= 0) + goto ex; + if (payload != NULL) + continue; + for (i = 0; i < num_tracks; i++) { + ret = burn_track_get_cdtext(tracks[i], 0, pack_type, + "", &payload, &length, 0); + if (ret <= 0) + goto ex; + double_byte = (ret > 1); + if (payload != NULL) + break; + } + if (i < num_tracks) { + ret = burn_session_set_cdtext(session, 0, pack_type, + "", dummy_cdtext, 1 + double_byte, + double_byte); + if (ret <= 0) + goto ex; + } + } + ret = 1; +ex: + if (ret <= 0) { + tracks = burn_session_get_tracks(session, &num_tracks); + for (i = 0; i < num_tracks; i++) + burn_track_free(tracks[i]); + } else { + if (fifo != NULL) { + *fifo = crs->fifo; + crs->fifo = NULL; + } + } + cue_crs_destroy(&crs, 0); + BURN_FREE_MEM(line); + BURN_FREE_MEM(msg); + return ret; +} +