Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions lib/driver/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@
#include "cdio_private.h"
#include <cdio/sector.h>

/**
Sub-channel data stored after each sector in a disc image.
*/
typedef enum {
IMAGE_SUBCHANNEL_NONE = 0,
IMAGE_SUBCHANNEL_RW, /**< 96 bytes of packed R-W (cdrdao RW) */
IMAGE_SUBCHANNEL_RW_RAW /**< 96 bytes of raw interleaved sub-channel
data (cdrdao RW_RAW). cdrdao read-cd
stores the drive's raw P-W data. */
} image_subchannel_t;

/**
The universal format for information about a track for CD image readers
It may be that some fields can be derived from other fields.
Expand Down Expand Up @@ -78,6 +89,9 @@ typedef struct {
This stuff may have error correction
(EDC, or ECC).*/
uint16_t blocksize; /**< total block size = start + size + end */
image_subchannel_t subchannel; /**< sub-channel data in endsize */
lba_t data_lba; /**< LBA of the sector stored at offset in
data_source, 0 if unknown */
} track_info_t;


Expand Down
149 changes: 110 additions & 39 deletions lib/driver/image/cdrdao.c
Original file line number Diff line number Diff line change
Expand Up @@ -636,13 +636,24 @@ parse_tocfile (_img_private_t *cd, const char *psz_cue_name)
goto err_exit;
}
if (NULL != (psz_field = strtok_r(NULL, " \t\n\r", &saveptr))) {
/* \todo: set sub-channel-mode */
#ifdef TODO
/* <sub-channel-mode>: 96 bytes follow each sector in the file */
image_subchannel_t subchannel;
if (0 == strcmp ("RW", psz_field))
;
subchannel = IMAGE_SUBCHANNEL_RW;
else if (0 == strcmp ("RW_RAW", psz_field))
;
#endif
subchannel = IMAGE_SUBCHANNEL_RW_RAW;
else {
cdio_log(log_level, "%s line %d after TRACK:",
psz_cue_name, i_line);
cdio_log(log_level, "'%s' not a valid sub-channel mode.",
psz_field);
goto err_exit;
}
if (NULL != cd) {
cd->tocent[i_track].subchannel = subchannel;
cd->tocent[i_track].endsize += CDIO_CD_FRAMESIZE_SUB;
cd->tocent[i_track].blocksize += CDIO_CD_FRAMESIZE_SUB;
}
}
if (NULL != (psz_field = strtok_r(NULL, " \t\n\r", &saveptr))) {
goto format_error;
Expand Down Expand Up @@ -717,30 +728,31 @@ parse_tocfile (_img_private_t *cd, const char *psz_cue_name)
if (0 <= i_track) {
if (NULL != (psz_field = strtok_r(NULL, "\"\t\n\r", &saveptr))) {
/* Handle "<filename>" */
char *psz_dirname = cdio_dirname(psz_cue_name);
char *psz_filename = cdio_abspath(psz_dirname, psz_field);
free(psz_dirname);
if (cd) {
char *psz_dirname = cdio_dirname(psz_cue_name);
char *psz_filename = cdio_abspath(psz_dirname, psz_field);
cd->tocent[i_track].filename = strdup (psz_filename);
free(psz_filename);
free(psz_dirname);
/* To do: do something about reusing existing files. */
if (!(cd->tocent[i_track].data_source = cdio_stdio_new (psz_field))) {
if (!(cd->tocent[i_track].data_source = cdio_stdio_new (psz_filename))) {
cdio_log (log_level,
"%s line %d: can't open file `%s' for reading",
psz_cue_name, i_line, psz_field);
free(psz_filename);
goto err_exit;
}
} else {
CdioDataSource_t *s = cdio_stdio_new (psz_field);
CdioDataSource_t *s = cdio_stdio_new (psz_filename);
if (!s) {
cdio_log (log_level,
"%s line %d: can't open file `%s' for reading",
psz_cue_name, i_line, psz_field);
cdio_stdio_destroy (s);
free(psz_filename);
goto err_exit;
}
cdio_stdio_destroy (s);
}
free(psz_filename);
}

if (NULL != (psz_field = strtok_r(NULL, " \t\n\r", &saveptr))) {
Expand Down Expand Up @@ -788,7 +800,7 @@ parse_tocfile (_img_private_t *cd, const char *psz_cue_name)
goto not_in_global_section;
}

/* DATAFILE "<filename>" #byte-offset <start-msf> */
/* DATAFILE "<filename>" [#byte-offset] [<length>] */
} else if (0 == strcmp ("DATAFILE", psz_keyword)) {
if (0 <= i_track) {
if (NULL != (psz_field = strtok_r(NULL, "\"\t\n\r", &saveptr))) {
Expand All @@ -798,7 +810,7 @@ parse_tocfile (_img_private_t *cd, const char *psz_cue_name)
if (cd) {
cd->tocent[i_track].filename = strdup(psz_filename);
/* To do: do something about reusing existing files. */
if (!(cd->tocent[i_track].data_source = cdio_stdio_new (psz_field))) {
if (!(cd->tocent[i_track].data_source = cdio_stdio_new (psz_filename))) {
cdio_log (log_level,
"%s line %d: can't open file `%s' for reading",
psz_cue_name, i_line, psz_field);
Expand Down Expand Up @@ -845,37 +857,38 @@ parse_tocfile (_img_private_t *cd, const char *psz_cue_name)
}
}
if (psz_field) {
/* Handle start-msf */
/* Handle <length> */
lba_t lba = cdio_mmssff_to_lba (psz_field);
if (CDIO_INVALID_LBA == lba) {
cdio_log(log_level, "%s line %d: invalid MSF string %s",
psz_cue_name, i_line, psz_field);
goto err_exit;
}
if (cd) {
cd->tocent[i_track].start_lba = lba;
cdio_lba_to_msf(cd->tocent[i_track].start_lba,
&(cd->tocent[i_track].start_msf));
}
} else {
/* No start-msf. */
if (cd) {
if (i_track) {
uint16_t i_blocksize = cd->tocent[i_track-1].blocksize;
off_t i_size =
cdio_stream_stat(cd->tocent[i_track-1].data_source);

check_track_is_blocksize_multiple(cd->tocent[i_track-1].filename,
i_track-1, i_size, i_blocksize);
/* Append size of previous datafile. */
cd->tocent[i_track].start_lba = (lba_t) (cd->tocent[i_track-1].start_lba +
(i_size / i_blocksize));
}
cd->tocent[i_track].offset = 0;
cd->tocent[i_track].start_lba += CDIO_PREGAP_SECTORS;
cdio_lba_to_msf(cd->tocent[i_track].start_lba,
&(cd->tocent[i_track].start_msf));
if (cd)
cd->tocent[i_track].sec_count = lba;
}
if (cd) {
/* The track starts where the previous one ends */
if (0 == i_track) {
cd->tocent[i_track].start_lba = CDIO_PREGAP_SECTORS;
} else if (cd->tocent[i_track-1].sec_count) {
cd->tocent[i_track].start_lba = cd->tocent[i_track-1].start_lba +
cd->tocent[i_track-1].sec_count;
} else {
/* No length: the previous track takes up the rest of its file */
const track_info_t *prev = &cd->tocent[i_track-1];
uint16_t i_blocksize = prev->blocksize;
off_t i_size = cdio_stream_stat(prev->data_source) - prev->offset;

check_track_is_blocksize_multiple(prev->filename,
i_track-1, i_size, i_blocksize);
cd->tocent[i_track].start_lba = (lba_t)
((prev->data_lba ? prev->data_lba : prev->start_lba) +
(i_size / i_blocksize));
}
cd->tocent[i_track].data_lba = cd->tocent[i_track].start_lba;
cdio_lba_to_msf(cd->tocent[i_track].start_lba,
&(cd->tocent[i_track].start_msf));
}

} else {
Expand All @@ -896,7 +909,9 @@ parse_tocfile (_img_private_t *cd, const char *psz_cue_name)
cd->tocent[i_track].start_lba += cdio_mmssff_to_lba (psz_field);
cdio_lba_to_msf(cd->tocent[i_track].start_lba,
&(cd->tocent[i_track].start_msf));
cd->tocent[i_track].sec_count -= cdio_mmssff_to_lba (psz_field);
/* Unknown lengths stay unknown */
if (cd->tocent[i_track].sec_count)
cd->tocent[i_track].sec_count -= cdio_mmssff_to_lba (psz_field);
}
}

Expand Down Expand Up @@ -1032,6 +1047,42 @@ parse_tocfile (_img_private_t *cd, const char *psz_cue_name)
return false;
}

/**
Returns the DATAFILE track that holds lsn, or NULL if there is none.
Pregap sectors set with START belong to the track that follows them.
*/
static const track_info_t *
_get_data_track_cdrdao (const _img_private_t *env, lsn_t lsn)
{
const lba_t lba = cdio_lsn_to_lba(lsn);
int i;

for (i = env->gen.i_tracks - 1; i >= 0; i--) {
const track_info_t *this_track = &env->tocent[i];
if (this_track->data_lba && this_track->data_lba <= lba)
return this_track;
}
return NULL;
}

/**
Reads the whole block of the sector at lsn, blocksize bytes, from
this_track's data file into buf.
*/
static driver_return_code_t
_read_block_cdrdao (const track_info_t *this_track, uint8_t *buf, lsn_t lsn)
{
const off_t pos = this_track->offset + (off_t)
(cdio_lsn_to_lba(lsn) - this_track->data_lba) * this_track->blocksize;

if (0 != cdio_stream_seek (this_track->data_source, pos, SEEK_SET))
return DRIVER_OP_ERROR;
if (this_track->blocksize !=
cdio_stream_read (this_track->data_source, buf, this_track->blocksize, 1))
return DRIVER_OP_ERROR;
return DRIVER_OP_SUCCESS;
}

/**
Reads a single audio sector from CD device into data starting
from lsn. Returns 0 if no error.
Expand All @@ -1043,6 +1094,26 @@ _read_audio_sectors_cdrdao (void *user_data, void *data, lsn_t lsn,
_img_private_t *env = user_data;
int ret;

if (_get_data_track_cdrdao(env, lsn)) {
uint8_t buf[CDIO_CD_FRAMESIZE_RAW + CDIO_CD_FRAMESIZE_SUB];
uint8_t *p = data;
unsigned int i;

for (i = 0; i < nblocks; i++) {
const track_info_t *this_track = _get_data_track_cdrdao(env, lsn + i);
if (!this_track || this_track->blocksize < CDIO_CD_FRAMESIZE_RAW ||
this_track->blocksize > sizeof(buf))
return DRIVER_OP_ERROR;
ret = _read_block_cdrdao(this_track, buf, lsn + i);
if (ret)
return ret;
memcpy(p, buf, CDIO_CD_FRAMESIZE_RAW);
p += CDIO_CD_FRAMESIZE_RAW;
}
return DRIVER_OP_SUCCESS;
}

/* FILE and AUDIOFILE tracks */
ret = cdio_stream_seek (env->tocent[0].data_source,
lsn * CDIO_CD_FRAMESIZE_RAW, SEEK_SET);
if (ret!=0) return ret;
Expand Down
2 changes: 1 addition & 1 deletion test/check_fuzzyiso.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fi
cd $srcdir; src_dir=`pwd`
for file in $src_dir/data/*.bin $src_dir/data/*.iso $src_dir/data/*.nrg ; do
case "$file" in
$src_dir/data/p1.nrg | $src_dir/data/p1.bin | $src_dir/data/cdda.bin | $src_dir/data/cdda_4_5.bin | $src_dir/data/cdtextfile.bin | $src_dir/data/cdda-mcn.nrg | $src_dir/data/svcdgs.nrg | $src_dir/data/cdtext.bin | $src_dir/data/udf102.iso | $src_dir/data/too-many-tracks.nrg )
$src_dir/data/p1.nrg | $src_dir/data/p1.bin | $src_dir/data/cdda.bin | $src_dir/data/cdda-rw-raw.bin | $src_dir/data/cdda_4_5.bin | $src_dir/data/cdtextfile.bin | $src_dir/data/cdda-mcn.nrg | $src_dir/data/svcdgs.nrg | $src_dir/data/cdtext.bin | $src_dir/data/udf102.iso | $src_dir/data/too-many-tracks.nrg )
good_rc=1
;;
*)
Expand Down
3 changes: 3 additions & 0 deletions test/data/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ check_DATA = \
bad-tracknum-exceeded.cue \
bad-tracknum-exceeded.toc \
bad-tracknum-overflow.toc \
cdda-rw-raw.bin \
cdda-rw-raw.toc \
cdda.bin \
cdda.cue \
cdda.toc \
cdda_4_5.bin \
cdda_4_5.cue \
cdimage-u2.iso \
Expand Down
Binary file added test/data/cdda-rw-raw.bin
Binary file not shown.
20 changes: 20 additions & 0 deletions test/data/cdda-rw-raw.toc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Two audio tracks with raw P-W sub-channel data after each sector, as
// written by cdrdao read-cd --read-subchan rw_raw. Track 2 has a two
// sector pregap. Sectors hold (lsn * 31 + byte) & 0xff, the Q sub-channel
// gives each sector's track, index and position.
CD_DA

// Track 1
TRACK AUDIO RW_RAW
NO COPY
NO PRE_EMPHASIS
TWO_CHANNEL_AUDIO
DATAFILE "cdda-rw-raw.bin" 00:00:06 // length in bytes: 14688

// Track 2
TRACK AUDIO RW_RAW
NO COPY
NO PRE_EMPHASIS
TWO_CHANNEL_AUDIO
DATAFILE "cdda-rw-raw.bin" #14688 00:00:08 // length in bytes: 19584
START 00:00:02
62 changes: 61 additions & 1 deletion test/driver/cdrdao.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,66 @@
#define NUM_GOOD_TOCS 17
#define NUM_BAD_TOCS 10

#define RW_RAW_SECTORS 14
#define RW_RAW_BLOCKSIZE (CDIO_CD_FRAMESIZE_RAW + CDIO_CD_FRAMESIZE_SUB)

/* Read the image of cdda-rw-raw.toc, opened from outside its directory */
static int
check_rw_raw(void)
{
const char *toc_name = DATA_DIR "/cdda-rw-raw.toc";
static uint8_t image[RW_RAW_SECTORS * RW_RAW_BLOCKSIZE];
static uint8_t buf[RW_RAW_SECTORS * RW_RAW_BLOCKSIZE];
FILE *bin = fopen(DATA_DIR "/cdda-rw-raw.bin", "rb");
CdIo_t *p_cdio;
lsn_t lsn;
int ret = 0;

if (!bin || sizeof(image) != fread(image, 1, sizeof(image), bin)) {
fprintf(stderr, "Unable to read cdda-rw-raw.bin\n");
if (bin)
fclose(bin);
return 1;
}
fclose(bin);

p_cdio = cdio_open_cdrdao(toc_name);
if (!p_cdio) {
fprintf(stderr, "Incorrect: %s doesn't open.\n", toc_name);
return 1;
}

if (2 != cdio_get_num_tracks(p_cdio) ||
0 != cdio_get_track_lsn(p_cdio, 1) ||
6 != cdio_get_track_pregap_lsn(p_cdio, 2) ||
8 != cdio_get_track_lsn(p_cdio, 2) ||
RW_RAW_SECTORS != cdio_get_track_lsn(p_cdio, CDIO_CDROM_LEADOUT_TRACK)) {
fprintf(stderr, "Incorrect: %s track positions.\n", toc_name);
ret = 1;
goto out;
}

/* Audio sectors leave out the sub-channel data */
if (DRIVER_OP_SUCCESS !=
cdio_read_audio_sectors(p_cdio, buf, 0, RW_RAW_SECTORS)) {
fprintf(stderr, "Incorrect: %s audio read failed.\n", toc_name);
ret = 1;
goto out;
}
for (lsn = 0; lsn < RW_RAW_SECTORS; lsn++) {
if (memcmp(buf + lsn * CDIO_CD_FRAMESIZE_RAW,
image + lsn * RW_RAW_BLOCKSIZE, CDIO_CD_FRAMESIZE_RAW)) {
fprintf(stderr, "Incorrect: %s audio of LSN %d.\n", toc_name, lsn);
ret = 1;
goto out;
}
}

out:
cdio_destroy(p_cdio);
return ret;
}

static int
check_no_tracks(void)
{
Expand Down Expand Up @@ -139,7 +199,7 @@ main(int argc, const char *argv[])
char psz_tocfile[500];
unsigned int verbose = (argc > 1);

if (check_no_tracks() || check_track_without_mode())
if (check_no_tracks() || check_track_without_mode() || check_rw_raw())
ret = 1;

#ifdef HAVE_CHDIR
Expand Down
Loading