diff --git a/cdrskin/cdrskin_timestamp.h b/cdrskin/cdrskin_timestamp.h index 89f49c1..581c188 100644 --- a/cdrskin/cdrskin_timestamp.h +++ b/cdrskin/cdrskin_timestamp.h @@ -1 +1 @@ -#define Cdrskin_timestamP "2011.12.03.113345" +#define Cdrskin_timestamP "2011.12.05.203600" diff --git a/doc/cookbook.txt b/doc/cookbook.txt index 68cfda1..8c3dfec 100644 --- a/doc/cookbook.txt +++ b/doc/cookbook.txt @@ -20,6 +20,7 @@ Content: Guided by reading mmc-r10a.pdf , O.8 "Write a Track" from http://www.t10.org/ftp/t10/drafts/mmc/ backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/ +by reading mmc3r10g.pdf from http://www.t10.org/ftp/t10/drafts/mmc3/ by reading spc3r23.pdf from http://www.t10.org/ftp/t10/drafts/spc3/ by reading libburn/* from http://icculus.org/burn and by experiments with drives NEC ND-4570A, LG GSA-4082B, LITE-ON LTR48125S @@ -193,7 +194,9 @@ If POINT is >= 1 and <= 99 (63h) then the descriptor is about the track of which POINT tells the number. The start address of this track can be read from PMIN, PSEC, PFRAME where it is encoded in MSF format: -blocks = frames - 150, 75 frames = 1 sec , 60 sec = 1 min. +If M is smaller than 90: LBA = (M * 60 + S) * 75 + F - 150 +Else : LBA = (M * 60 + S) * 75 + F - 450150 + The length of the track is given by MIN,SEC,FRAME in the same format. If POINT = A0h then the descriptor tells in PMIN the first track number of its @@ -267,8 +270,8 @@ TNO is the track number (1 to 99). INDEX is a subaddress within tracks. This recipe uses only INDEX 01h within tracks. (mmc5r03c.pdf 4.2.3.5.2) -DATA FORM is 00h for audio payload , 10h for data. (01h for audio pause is not -used in libburn). +DATA FORM is 00h for audio payload, 01h for audio pause, 10h for data, +41h for CD-TEXT in Lead-in. (mmc5r03c.pdf 6.33.3.11 CD-DA Data Form, 6.33.3.12 CD-ROM mode 1 Form) SCMS is always 00h. MIN, SEC, FRAME give the MSF address where the described data entity starts. @@ -277,8 +280,9 @@ This address must increase from entry to entry (or at least stay equal). The first entry describes the Lead-in. Its content is -(CTL|ADR ,00h,00h,01h,00h,00h,00h,00h) +(CTL|ADR ,00h,00h, DATA FORM ,00h,00h,00h,00h) With the CTL|ADR for the first track: 41h for data, 01h for audio. +DATA FORM is 41h if CD-TEXT shall be stored in Lean-in. Else it is 01h. The LBA for the first write is negative: -150. This corresponds to MSF address 00h:00h:00h. All addresses are to be given in MSF format. @@ -312,6 +316,12 @@ next lower possible value by the drive. So it is helpful to add a few kbytes/sec just in case the drive has rounding problems. (mmc5r03c.pdf 6.37) +If CD-TEXT shall be written into Lead-in, then it is necessary to obtain the +Start Time of Lead-in by 43h READ TOC/PMA/ATIP Format 0100b. It is an MFS +address which varies from media manufacturer to media manufacturer. +Minute will be >= 90. Therefore this conversion applies: + LBA = (M * 60 + S) * 75 + F - 450150 + A Write Parameters mode page 05h has to be composed and transmitted via 55h MODE SELECT. This page describes the following parameters: BUFE Buffer Underrun protection 0=off, 1=on @@ -338,15 +348,31 @@ blocks written. I.e the Transfer Length of the previous 2Ah WRITE has to be added to the Logical Block Address for the next 2Ah WRITE. Only full blocks can be written. (mmc5r03c.pdf, 6.44) -Writing begins at LBA -150 which is to be transmitted as 4-byte, Big-endian, -two's-complement. E.g: -150 = FFh FFh FFh 6Ah. This is the natural form found -with about any 32-bit processor, so only the endianness has to be taken into -respect when converting a 32-bit integer into a LBA for command 2Ah WRITE. +Block addresses may be negative for areas before the normally readable +data. Data representation of addresses is 4-byte, big-endian, two's-complement. +E.g: -150 = FFh FFh FFh 6Ah. +This is the natural form found with about any 32-bit processor, so only +the endianness has to be taken into respect when converting a 32-bit +integer into a LBA for command 2Ah WRITE. +If CD-TEXT shall be written into Lead-in, then writing begins at the start +address of Lead-in, which was obtained above. +The 18 bytes of each text pack have to be split up to 24 bytes with only the +lowest six bits used in each byte. E.g. text pack + 8F 00 2A 00 01 01 03 00 06 05 04 05 07 06 01 02 48 65 +becomes + 23 30 00 2A 00 00 04 01 00 30 00 06 01 10 10 05 01 30 18 01 00 24 21 25 +4 of these 24 byte packs form a block of DATA FORM 41h. I.e. only 96 bytes +payload per block. The whole range from Lead-in start to LBA -150 has to be +filled with blocks of this form. Therefore it is necessary to write the +list of given packs in repeated cycles. +A typical Lead-in start address is -11635 = FFh FFh D2h 8Dh. -At first the mandatory pause preceding the first track has to be written as -150 blocks of the matching sector size: 2048 for data, 2352 for audio. -By this, the LBA increases from -150 to 0. +Writing without CD-TEXT begins at LBA -150 = FFh FFh FFh 6Ah. + +In both cases, the mandatory pause preceding the first track has to be +written as 150 blocks of the matching sector size: 2048 for data, +2352 for audio. By this, the LBA increases from -150 to 0. Next the tracks' payload is sent. For each track exactly the number of blocks has to be transmitted as is announced in the Cue Sheet by the difference @@ -370,6 +396,58 @@ to media by 35h SYNCHRONIZE CACHE. No further finalization is necessary. (I.e. no 5Bh CLOSE TRACK SESSION.) +------------------------------------------------------------------------------- +Obtaining CD-TEXT from Lead-in : + +Audio CDs may contain CD-TEXT information in their Lead-in. It is gained by +43h READ TOC/PMA/ATIP, Format 0101b. The reply consists of 4 bytes header, +of which the first two bytes give the number of following bytes as big-endian +16 bit number. The other two bytes are 0. +Following are text packs of 18 bytes each. +(mmc5r03c.pdf 6.26.3.7.1 table 495) + +The format of a text pack is explained in (mmc3r10g.pdf, appendix J). + +Each pack of a 4-bytes are header, 12 byte pieces of 0-terminated texts +or binary data, and 2 bytes of CRC. + +The first byte of each pack tells the pack type (text meaning): + 0x80 = Title + 0x81 = Names of performers + 0x82 = Songwriters + 0x83 = Composers, + 0x84 = Arrangers + 0x85 = Messages + 0x86 = text-and-binary: Disc Identification + 0x87 = text-and-binary: Genre Identification + 0x88 = binary: Table of Content information + 0x89 = binary: Second Table of Content information + 0x8e = UPC/EAN code of the album and ISRC code of each track + 0x8f = binary: Size Information of the Block + +The second byte tells the track number to which the first text piece in +a pack is associated. Number 0 means the whole album. Higher numbers are +valid for types 0x80 to 0x85, and 0x8e. With these types, there should be +one text for the disc and one for each track. + +The third byte is a sequential counter. + +The fourth byte is the Block Number and Character Position Indicator. +It consists of three bit fields: + bit7 = Double Bytes Character Code (0= single byte characters) + bit4-6 = Block Number (groups text packs in language blocks) + bit0-3 = Character position. Either the number of characters which + the current text inherited from the previous pack, or + 15 if the current text started before the previous pack. + +A text may span over several packs. Unused characters in a pack are used for +the next text of the same pack type. If no text of the same type follows, +then the remaining text bytes are set to 0. + +The CRC algorithm is known as CRC-16-CCITT with divisor 0x11021. +MMC-3 says: "All bits shall be inverted." + + ---------------------------------------------------------------------------- What is known about mixed mode sessions : diff --git a/libburn/libburn.h b/libburn/libburn.h index a3af7b7..cb9ff50 100644 --- a/libburn/libburn.h +++ b/libburn/libburn.h @@ -1310,8 +1310,8 @@ int burn_disc_get_cd_info(struct burn_drive *d, char disc_type[80], 0x8f = binary: Size Information of the Block The second byte tells the track number to which the first text piece in a pack is associated. Number 0 means the whole album. Higher numbers are - valid for types 0x80 to 0x85, and 0x8e. There should be one text for each - track with these types. + valid for types 0x80 to 0x85, and 0x8e. With these types, there should be + one text for the disc and one for each track. The third byte is a sequential counter. The fourth byte is the Block Number and Character Position Indicator. It consists of three bit fields: @@ -1321,13 +1321,14 @@ int burn_disc_get_cd_info(struct burn_drive *d, char disc_type[80], the current text inherited from the previous pack, or 15 if the current text started before the previous pack. The two CRC bytes are optional. Polynomial is x^16 + x^12 + x^5 + 1. + "All bits shall be inverted." @param d The drive to query. @param text_packs Will point to an allocated memory buffer with CD-TEXT. It will only contain text packs, and not be prepended by the TOC header of four bytes, which gets stored with file cdtext.dat by cdrecord -vv -toc. The first two of - these bytes are supposed hold the number of CD-TEXT + these bytes are supposed to hold the number of CD-TEXT bytes + 2. The other two bytes are supposed to be 0. Dispose this buffer by free(), when no longer needed. @param num_packs Will tell the number of text packs, i.e. the number of @@ -2431,6 +2432,23 @@ void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, unsigned cha */ void burn_write_opts_set_multi(struct burn_write_opts *opts, int multi); +/* ts B11204 */ +/** Submit an array of CD-TEXT packs which shall be written to the Lead-in + of a SAO write run on CD. + @param opts The option object to be manipulated + @param text_packs Array of bytes which form CD-TEXT packs of 18 bytes + each. See burn_disc_get_leadin_text() for a description + of the text pack format. + No header of 4 bytes must be prepended which would + tell the number of pack bytes + 2. + @param num_packs The number of 18 byte packs in text_packs. + @param flag Bitfield for control purposes. Unused yet. Submit 0. + @return 1 on success, <= 0 on failure + @since 1.2.0 +*/ +int burn_write_opts_set_leadin_text(struct burn_write_opts *opts, + unsigned char *text_packs, + int num_packs, int flag); /* ts A61222 */ /** Sets a start address for writing to media and write modes which allow to diff --git a/libburn/libburn.ver b/libburn/libburn.ver index 90940f4..d697723 100644 --- a/libburn/libburn.ver +++ b/libburn/libburn.ver @@ -154,6 +154,7 @@ burn_write_opts_set_fillup; burn_write_opts_set_force; burn_write_opts_set_format; burn_write_opts_set_has_mediacatalog; +burn_write_opts_set_leadin_text; burn_write_opts_set_mediacatalog; burn_write_opts_set_multi; burn_write_opts_set_perform_opc; diff --git a/libburn/libdax_msgs.h b/libburn/libdax_msgs.h index a6a6f88..9e04a43 100644 --- a/libburn/libdax_msgs.h +++ b/libburn/libdax_msgs.h @@ -577,6 +577,7 @@ Range "scdbackup" : 0x00020000 to 0x0002ffff 0x00020188 (FAILURE,HIGH) = Cannot close damaged track on given media type 0x00020189 (FATAL,HIGH) = Drive is already grabbed by libburn 0x0002018a (SORRY,HIGH) = Timeout exceeded. Retry cancled. + 0x0002018b (FAILURE,HIGH) = Too many CD-TEXT packs libdax_audioxtr: 0x00020200 (SORRY,HIGH) = Cannot open audio source file diff --git a/libburn/mmc.c b/libburn/mmc.c index 0ea3b69..8f5113e 100644 --- a/libburn/mmc.c +++ b/libburn/mmc.c @@ -945,7 +945,7 @@ int mmc_write(struct burn_drive *d, int start, struct buffer *buf) #ifdef Libburn_log_in_and_out_streaM /* <<< ts A61031 */ if(tee_fd!=-1) { - write(tee_fd,c->page->data,len*2048); + write(tee_fd, c->page->data, c->page->bytes); } #endif /* Libburn_log_in_and_out_streaM */ diff --git a/libburn/options.c b/libburn/options.c index 58c4136..bbd307b 100644 --- a/libburn/options.c +++ b/libburn/options.c @@ -12,6 +12,7 @@ #include "options.h" #include "drive.h" #include "transport.h" +#include "init.h" /* ts A61007 */ /* #include */ @@ -51,6 +52,8 @@ struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive) opts->do_stream_recording = 0; opts->dvd_obs_override = 0; opts->stdio_fsync_size = Libburn_stdio_fsync_limiT; + opts->text_packs = NULL; + opts->num_text_packs = 0; opts->has_mediacatalog = 0; opts->format = BURN_CDROM; opts->multi = 0; @@ -60,8 +63,11 @@ struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive) void burn_write_opts_free(struct burn_write_opts *opts) { - if (--opts->refcount <= 0) - free(opts); + if (--opts->refcount > 0) + return; + if (opts->text_packs != NULL) + free(opts->text_packs); + free(opts); } struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive) @@ -189,6 +195,41 @@ void burn_write_opts_set_multi(struct burn_write_opts *opts, int multi) } +/* ts B11204 */ +int burn_write_opts_set_leadin_text(struct burn_write_opts *opts, + unsigned char *text_packs, + int num_packs, int flag) +{ + int ret; + unsigned char *pack_buffer = NULL; + + if (num_packs > 3640) { + /* READ TOC/PMA/ATIP can at most return 3640.7 packs */ + libdax_msgs_submit(libdax_messenger, opts->drive->global_index, + 0x0002018b, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Too many CD-TEXT packs (> 3640)", 0, 0); + ret= 0; goto ex; + } + + if (num_packs > 0) + BURN_ALLOC_MEM(pack_buffer, unsigned char, num_packs * 18); + + if (opts->text_packs != NULL) { + free(opts->text_packs); + opts->text_packs = NULL; + } + if (num_packs > 0) { + memcpy(pack_buffer, text_packs, num_packs * 18); + opts->text_packs = pack_buffer; + } + opts->num_text_packs = num_packs; + ret = 1; +ex:; + return ret; +} + + /* ts A61222 */ void burn_write_opts_set_start_byte(struct burn_write_opts *opts, off_t value) { @@ -296,6 +337,10 @@ do_sao:; {wt = BURN_WRITE_SAO; goto ex;} no_sao:; try_tao:; + if (opts->num_text_packs > 0) { + strcat(reasons, "CD-TEXT: write type SAO required, "); + {wt = BURN_WRITE_NONE; goto ex;} + } if ((flag & 1) && opts->write_type != BURN_WRITE_TAO) goto try_raw; reason_pt = reasons + strlen(reasons); diff --git a/libburn/options.h b/libburn/options.h index 4e2c6a0..34d2fef 100644 --- a/libburn/options.h +++ b/libburn/options.h @@ -69,6 +69,9 @@ struct burn_write_opts Values 0 or >= 32 counted in 2 KB blocks. */ int stdio_fsync_size; + /* ts B11203 : CD-TEXT */ + unsigned char *text_packs; + int num_text_packs; /** A disc can have a media catalog number */ int has_mediacatalog; diff --git a/libburn/write.c b/libburn/write.c index 0257b52..dda2c0a 100644 --- a/libburn/write.c +++ b/libburn/write.c @@ -368,6 +368,7 @@ struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o, int nwa) { int i, m, s, f, form, pform, runtime = -150, ret, track_length; + int leadin_form; unsigned char ctladr; struct burn_drive *d; struct burn_toc_entry *e; @@ -404,7 +405,11 @@ struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o, "Track mode has unusable value", 0, 0); goto failed; } - ret = add_cue(sheet, ctladr | 1, 0, 0, 1, 0, runtime); + if (o->num_text_packs > 0) + leadin_form = 0x41; + else + leadin_form = 0x01; + ret = add_cue(sheet, ctladr | 1, 0, 0, leadin_form, 0, runtime); if (ret <= 0) goto failed; ret = add_cue(sheet, ctladr | 1, 1, 0, form, 0, runtime); @@ -650,11 +655,110 @@ int burn_write_leadout(struct burn_write_opts *o, return 1; } + +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; + unsigned char *subdata = NULL; + struct burn_drive *d = o->drive; + struct buffer *buf = NULL; + enum burn_drive_status was_busy; +#ifdef Libburn_debug_cd_texT + unsigned char *packs; +#endif + + if (o->num_text_packs <= 0) + {ret = 1; goto ex;} + + was_busy = d->busy; + d->busy = BURN_DRIVE_WRITING_LEADIN; + +#ifdef Libburn_debug_cd_texT + packs = o->text_packs; + fprintf(stderr, + "libburn_DEBUG: 8 bit CD-TEXT packs to be transmitted:\n"); + for (i = 0; i < 18 * o->num_text_packs; i += 18) { + fprintf(stderr, "%4d :", i / 18); + for (j = 0; j < 18; j++) { + if (j >= 4 && j <= 15 && packs[i + j] >= 32 && + packs[i + j] <= 126 && packs[i] != 0x88 && + packs[i] != 0x89 && packs[i] != 0x8f) + fprintf(stderr, " %c", packs[i + j]); + else + fprintf(stderr, " %2.2X", packs[i + j]); + } + fprintf(stderr, "\n"); + } +#endif /* Libburn_debug_cd_texT */ + + /* Chop from 8 bit text pack to 6 bit subchannel */ + BURN_ALLOC_MEM(subdata, unsigned char, o->num_text_packs * 24); + for (i = 0; i < 18 * o->num_text_packs; i += 3) { + si = i / 3 * 4; + subdata[si + 0] = (o->text_packs[i + 0] >> 2) & 0x3f; + subdata[si + 1] = (o->text_packs[i + 0] << 4) & 0x30; + subdata[si + 1] |= (o->text_packs[i + 1] >> 4) & 0x0f; + subdata[si + 2] = (o->text_packs[i + 1] << 2) & 0x3c; + subdata[si + 2] |= (o->text_packs[i + 2] >> 6) & 0x03; + subdata[si + 3] = (o->text_packs[i + 2] >> 0) & 0x3f; + } + + /* Start at Lead-in address of ATIP and write blocks up to -150 */ + BURN_ALLOC_MEM(buf, struct buffer, 1); + write_lba = d->start_lba; + for (lba = d->start_lba; lba < -150; lba++) { + /* Collect subdata in buf */ + for (j = 0; j < 4; j++) { + memcpy(buf->data + buf->bytes, + subdata + sub_cursor * 24, 24); + sub_cursor = (sub_cursor + 1) % o->num_text_packs; + buf->bytes += 24; + } + buf->sectors++; + sectors++; + + /* When full or last sector : perform WRITE */ + if (buf->bytes + 96 >= 32768 || lba == -151) { + +#ifdef Libburn_debug_cd_texT + fprintf(stderr, + "libburn_DEBUG: 6 bit data to be transmitted:\n"); + for (i = 0; i < buf->bytes; i += 24) { + fprintf(stderr, "%4d :", i / 24); + for (j = 0; j < 24; j++) + fprintf(stderr, " %2.2X", + buf->data[i + j]); + fprintf(stderr, "\n"); + } +#endif /* Libburn_debug_cd_texT */ + + err = d->write(d, write_lba, buf); + if (err == BE_CANCELLED) + { ret = 0; goto ex; } + write_lba += sectors; + sectors = buf->sectors = buf->bytes = 0; + } + } + ret = 1; +ex:; + BURN_FREE_MEM(subdata); + BURN_FREE_MEM(buf); + d->busy = was_busy; + return ret; +} + + int burn_write_session(struct burn_write_opts *o, struct burn_session *s) { struct burn_drive *d = o->drive; int i, ret; + if (o->write_type == BURN_WRITE_SAO) { + ret = burn_write_leadin_cdtext(o, s, 0); + if (ret <= 0) + goto ex; + } d->rlba = 0; for (i = 0; i < s->tracks; i++) { if (!burn_write_track(o, s, i)) @@ -1001,6 +1105,10 @@ int burn_precheck_write(struct burn_write_opts *o, struct burn_disc *disc, reason_pt= reasons + strlen(reasons); if (d->status == BURN_DISC_UNSUITABLE) goto unsuitable_profile; + if (d->current_profile != 0x09 && d->current_profile != 0x0a) + if (o->num_text_packs > 0) + strcat(reasons, + "CD-TEXT supported only with write CD media, "); if (d->drive_role == 2 || d->drive_role == 5 || d->current_profile == 0x1a || d->current_profile == 0x12 || d->current_profile == 0x43) { @@ -1014,6 +1122,14 @@ int burn_precheck_write(struct burn_write_opts *o, struct burn_disc *disc, strcat(reasons, "unsuitable track mode found, "); if (o->start_byte >= 0) strcat(reasons, "write start address not supported, "); + if (o->num_text_packs > 0) { + if (o->write_type != BURN_WRITE_SAO) + strcat(reasons, + "CD-TEXT supported only with write type SAO, "); + if (d->start_lba == -2000000000) + strcat(reasons, + "No Lead-in start address known with CD-TEXT, "); + } } else if (d->current_profile == 0x13) { /* DVD-RW Restricted Overwrite */ if (o->start_byte >= 0 && (o->start_byte % 32768))