1 /*
2 Copyright (C) 2002-2006, 2008, 2011-2012, 2014, 2017
3 Rocky Bernstein <rocky@gnu.org>
4 Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org>
5 cue parsing routine adapted from cuetools
6 Copyright (C) 2003 Svend Sanjay Sorensen <ssorensen@fastmail.fm>
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /* This code implements low-level access functions for a CD images
23 residing inside a disk file (*.bin) and its associated cue sheet.
24 (*.cue).
25 */
26 #include "portable.h"
27 #include "image.h"
28 #include "cdio_assert.h"
29 #include "cdio_private.h"
30 #include "cdtext_private.h"
31 #include "_cdio_stdio.h"
32
33 #include <cdio/logging.h>
34 #include <cdio/util.h>
35 #include <cdio/version.h>
36
37 #ifdef HAVE_STDIO_H
38 #include <stdio.h>
39 #endif
40 #ifdef HAVE_STDLIB_H
41 #include <stdlib.h>
42 #endif
43 #ifdef HAVE_STRING_H
44 #include <string.h>
45 #endif
46 #ifdef HAVE_STRINGS_H
47 #include <strings.h>
48 #endif
49 #ifdef HAVE_ERRNO_H
50 #include <errno.h>
51 #endif
52 #ifdef HAVE_GLOB_H
53 #include <glob.h>
54 #endif
55 #ifdef HAVE_INTTYPES_H
56 #include <inttypes.h>
57 #else
58 #define PRId64 "lld"
59 #endif
60 #ifdef HAVE_WINDOWS_H
61 #include <windows.h>
62 #endif
63
64 #include <ctype.h>
65
66 #include <cdio/logging.h>
67 #include <cdio/util.h>
68 #include <cdio/utf8.h>
69 #include <cdio/version.h>
70
71 #include "image.h"
72 #include "cdio_assert.h"
73 #include "cdio_private.h"
74 #include "_cdio_stdio.h"
75
76 /* reader */
77
78 #define DEFAULT_CDIO_DEVICE "videocd.bin"
79 #define DEFAULT_CDIO_CUE "videocd.cue"
80
81 #ifdef _WIN32
82 #define CDIO_FOPEN fopen_utf8
83 #else
84 #define CDIO_FOPEN fopen
85 #endif
86
87 static lsn_t get_disc_last_lsn_bincue(void *p_user_data);
88 #include "image_common.h"
89 static bool parse_cuefile(_img_private_t *cd, const char *toc_name);
90
91 /*!
92 Initialize image structures.
93 */
94 static bool
_init_bincue(_img_private_t * p_env)95 _init_bincue(_img_private_t *p_env)
96 {
97 lsn_t lead_lsn;
98
99 if (p_env->gen.init)
100 return false;
101
102 if (!(p_env->gen.data_source = cdio_stdio_new (p_env->gen.source_name))) {
103 cdio_warn ("init failed");
104 return false;
105 }
106
107 /* Have to set init before calling get_disc_last_lsn_bincue() or we will
108 get into infinite recursion calling passing right here.
109 */
110 p_env->gen.init = true;
111 p_env->gen.i_first_track = 1;
112 p_env->psz_mcn = NULL;
113 p_env->disc_mode = CDIO_DISC_MODE_NO_INFO;
114
115 lead_lsn = get_disc_last_lsn_bincue( (_img_private_t *) p_env);
116
117 if (-1 == lead_lsn) return false;
118
119 if (NULL == p_env->psz_cue_name) return false;
120
121 /* Read in CUE sheet. */
122 if ( !parse_cuefile(p_env, p_env->psz_cue_name) ) return false;
123
124 /* Fake out leadout track and sector count for last track*/
125 cdio_lsn_to_msf (lead_lsn, &p_env->tocent[p_env->gen.i_tracks].start_msf);
126 p_env->tocent[p_env->gen.i_tracks].start_lba = cdio_lsn_to_lba(lead_lsn);
127 p_env->tocent[p_env->gen.i_tracks - p_env->gen.i_first_track].sec_count =
128 cdio_lsn_to_lba(lead_lsn -
129 p_env->tocent[p_env->gen.i_tracks - p_env->gen.i_first_track].start_lba);
130
131 return true;
132 }
133
134 /*!
135 Reads into buf the next size bytes.
136 Returns -1 on error.
137 Would be libc's seek() but we have to adjust for the extra track header
138 information in each sector.
139 */
140 static off_t
_lseek_bincue(void * p_user_data,off_t offset,int whence)141 _lseek_bincue (void *p_user_data, off_t offset, int whence)
142 {
143 _img_private_t *p_env = p_user_data;
144
145 /* real_offset is the real byte offset inside the disk image
146 The number below was determined empirically. I'm guessing
147 the 1st 24 bytes of a bin file are used for something.
148 */
149 off_t real_offset=0;
150
151 unsigned int i;
152
153 p_env->pos.lba = 0;
154 for (i=0; i<p_env->gen.i_tracks; i++) {
155 track_info_t *this_track=&(p_env->tocent[i]);
156 p_env->pos.index = i;
157 if ( (this_track->sec_count*this_track->datasize) >= offset) {
158 int blocks = (int) (offset / this_track->datasize);
159 int rem = (int) (offset % this_track->datasize);
160 off_t block_offset = blocks * this_track->blocksize;
161 real_offset += block_offset + rem;
162 p_env->pos.buff_offset = rem;
163 p_env->pos.lba += (lba_t) blocks;
164 break;
165 }
166 real_offset += this_track->sec_count*this_track->blocksize;
167 offset -= this_track->sec_count*this_track->datasize;
168 p_env->pos.lba += this_track->sec_count;
169 }
170
171 if (i==p_env->gen.i_tracks) {
172 cdio_warn ("seeking outside range of disk image");
173 return DRIVER_OP_ERROR;
174 } else {
175 real_offset += p_env->tocent[i].datastart;
176 return cdio_stream_seek(p_env->gen.data_source, real_offset, whence);
177 }
178 }
179
180 /*!
181 Reads into buf the next size bytes.
182 Returns -1 on error.
183 FIXME:
184 At present we assume a read doesn't cross sector or track
185 boundaries.
186 */
187 static ssize_t
_read_bincue(void * p_user_data,void * data,size_t size)188 _read_bincue (void *p_user_data, void *data, size_t size)
189 {
190 _img_private_t *p_env = p_user_data;
191 char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
192 char *p = data;
193 ssize_t final_size=0;
194 ssize_t this_size;
195 track_info_t *this_track=&(p_env->tocent[p_env->pos.index]);
196 ssize_t skip_size = this_track->datastart + this_track->endsize;
197
198 while (size > 0) {
199 long int rem = (long int) (this_track->datasize - p_env->pos.buff_offset);
200 if ((long int) size <= rem) {
201 this_size = cdio_stream_read(p_env->gen.data_source, buf, size, 1);
202 final_size += this_size;
203 memcpy (p, buf, this_size);
204 break;
205 }
206
207 /* Finish off reading this sector. */
208 cdio_warn ("Reading across block boundaries not finished");
209
210 size -= rem;
211 this_size = cdio_stream_read(p_env->gen.data_source, buf, rem, 1);
212 final_size += this_size;
213 memcpy (p, buf, this_size);
214 p += this_size;
215 cdio_stream_read(p_env->gen.data_source, buf, rem, 1);
216
217 /* Skip over stuff at end of this sector and the beginning of the next.
218 */
219 cdio_stream_read(p_env->gen.data_source, buf, skip_size, 1);
220
221 /* Get ready to read another sector. */
222 p_env->pos.buff_offset=0;
223 p_env->pos.lba++;
224
225 /* Have gone into next track. */
226 if (p_env->pos.lba >= p_env->tocent[p_env->pos.index+1].start_lba) {
227 p_env->pos.index++;
228 this_track=&(p_env->tocent[p_env->pos.index]);
229 skip_size = this_track->datastart + this_track->endsize;
230 }
231 }
232 return final_size;
233 }
234
235 /*!
236 Return the size of the CD in logical block address (LBA) units.
237 */
238 static lsn_t
get_disc_last_lsn_bincue(void * p_user_data)239 get_disc_last_lsn_bincue (void *p_user_data)
240 {
241 _img_private_t *p_env = p_user_data;
242 off_t size;
243
244 size = cdio_stream_stat (p_env->gen.data_source);
245
246 if (size % CDIO_CD_FRAMESIZE_RAW)
247 {
248 cdio_warn ("image %s size (%" PRId64 ") not multiple of blocksize (%d)",
249 p_env->gen.source_name, (int64_t)size, CDIO_CD_FRAMESIZE_RAW);
250 if (size % M2RAW_SECTOR_SIZE == 0)
251 cdio_warn ("this may be a 2336-type disc image");
252 else if (size % CDIO_CD_FRAMESIZE_RAW == 0)
253 cdio_warn ("this may be a 2352-type disc image");
254 /* exit (EXIT_FAILURE); */
255 }
256
257 size /= CDIO_CD_FRAMESIZE_RAW;
258
259 return (lsn_t)size;
260 }
261
262 #define MAXLINE 4096 /* maximum line length + 1 */
263
264 static bool
parse_cuefile(_img_private_t * cd,const char * psz_cue_name)265 parse_cuefile (_img_private_t *cd, const char *psz_cue_name)
266 {
267 /* The below declarations may be common in other image-parse routines. */
268 FILE *fp;
269 char psz_line[MAXLINE]; /* text of current line read in file fp. */
270 unsigned int i_line=0; /* line number in file of psz_line. */
271 int i = -1; /* Position in tocent. Same as
272 cd->gen.i_tracks - 1 */
273 char *psz_keyword, *psz_field, *psz_cue_name_dup;
274 cdio_log_level_t log_level = (NULL == cd) ? CDIO_LOG_INFO : CDIO_LOG_WARN;
275 cdtext_field_t cdtext_key;
276
277 /* The below declarations may be unique to this image-parse routine. */
278 int start_index;
279 bool b_first_index_for_track=false;
280
281 if (NULL == psz_cue_name)
282 return false;
283
284 psz_cue_name_dup = _cdio_strdup_fixpath(psz_cue_name);
285 if (NULL == psz_cue_name_dup)
286 return false;
287
288 fp = CDIO_FOPEN (psz_cue_name_dup, "r");
289 cdio_free(psz_cue_name_dup);
290 if (fp == NULL) {
291 cdio_log(log_level, "error opening %s for reading: %s",
292 psz_cue_name, strerror(errno));
293 return false;
294 }
295
296 if (cd) {
297 cd->gen.i_tracks=0;
298 cd->gen.i_first_track=1;
299 cd->psz_mcn=NULL;
300 }
301
302 while ((fgets(psz_line, MAXLINE, fp)) != NULL) {
303
304 i_line++;
305
306 if (NULL != (psz_keyword = strtok (psz_line, " \t\n\r"))) {
307 /* REM remarks ... */
308 if (0 == strcmp ("REM", psz_keyword)) {
309 ;
310
311 /* global section */
312 /* CATALOG ddddddddddddd */
313 } else if (0 == strcmp ("CATALOG", psz_keyword)) {
314 if (-1 == i) {
315 if (NULL == (psz_field = strtok (NULL, " \t\n\r"))) {
316 cdio_log(log_level,
317 "%s line %d after word CATALOG: ",
318 psz_cue_name, i_line);
319 cdio_log(log_level,
320 "expecting 13-digit media catalog number, got nothing.");
321 goto err_exit;
322 }
323 if (strlen(psz_field) != 13) {
324 cdio_log(log_level,
325 "%s line %d after word CATALOG: ",
326 psz_cue_name, i_line);
327 cdio_log(log_level,
328 "Token %s has length %ld. Should be 13 digits.",
329 psz_field, (long int) strlen(psz_field));
330 goto err_exit;
331 } else {
332 /* Check that we have all digits*/
333 unsigned int j;
334 for (j=0; j<13; j++) {
335 if (!isdigit((unsigned char) psz_field[j])) {
336 cdio_log(log_level,
337 "%s line %d after word CATALOG:",
338 psz_cue_name, i_line);
339 cdio_log(log_level,
340 "Character \"%c\" at postition %i of token \"%s\" "
341 "is not all digits.",
342 psz_field[j], j+1, psz_field);
343 goto err_exit;
344 }
345 }
346 }
347
348 if (cd) cd->psz_mcn = strdup (psz_field);
349 if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
350 goto format_error;
351 }
352 } else {
353 goto not_in_global_section;
354 }
355
356 /* CDTEXTFILE "<filename>" */
357 } else if (0 == strcmp ("CDTEXTFILE", psz_keyword)) {
358 if(NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) {
359 if (cd) {
360 uint8_t cdt_data[CDTEXT_LEN_BINARY_MAX+4];
361 int size;
362 CdioDataSource_t *source;
363 char *dirname = cdio_dirname(psz_cue_name);
364 char *psz_filename = cdio_abspath(dirname, psz_field);
365
366 if(NULL == (source = cdio_stdio_new(psz_filename))) {
367 cdio_log(log_level, "%s line %d: can't open file `%s' for reading",
368 psz_cue_name, i_line, psz_field);
369 free(psz_filename);
370 free(dirname);
371 goto err_exit;
372 }
373 size = cdio_stream_read(source, cdt_data, CDTEXT_LEN_BINARY_MAX, 1);
374
375 if (size < 5) {
376 cdio_log(log_level,
377 "%s line %d: file `%s' is too small to contain CD-TEXT",
378 psz_cue_name, i_line, psz_filename);
379 free(psz_filename);
380 free(dirname);
381 free(source);
382 goto err_exit;
383 }
384
385 /* Truncate header when it is too large. */
386 if (cdt_data[0] > 0x80) {
387 size -= 4;
388 }
389
390 /* ignore trailing 0 */
391 if (1 == size % 18)
392 size -= 1;
393
394 /* init cdtext */
395 if (NULL == cd->gen.cdtext) {
396 cd->gen.cdtext = cdtext_init ();
397 }
398
399 if(0 != cdtext_data_init(cd->gen.cdtext, cdt_data, size))
400 cdio_log (log_level, "%s line %d: failed to parse CD-TEXT file `%s'", psz_cue_name, i_line, psz_filename);
401
402 cdio_stdio_destroy (source);
403 free(psz_filename);
404 free(dirname);
405 }
406 } else {
407 goto format_error;
408 }
409
410 /* FILE "<filename>" <BINARY|WAVE|other?> */
411 } else if (0 == strcmp ("FILE", psz_keyword)) {
412 if (NULL != (psz_field = strtok (NULL, "\"\t\n\r"))) {
413 char *dirname = cdio_dirname(psz_cue_name);
414 char *filename = cdio_abspath(dirname, psz_field);
415 if (cd) cd->tocent[i + 1].filename = strdup(filename);
416 free(filename);
417 free(dirname);
418 } else {
419 goto format_error;
420 }
421
422 /* TRACK N <mode> */
423 } else if (0 == strcmp("TRACK", psz_keyword)) {
424 int i_track;
425
426 if (NULL != (psz_field = strtok(NULL, " \t\n\r"))) {
427 if (1!=sscanf(psz_field, "%d", &i_track)) {
428 cdio_log(log_level,
429 "%s line %d after word TRACK:",
430 psz_cue_name, i_line);
431 cdio_log(log_level,
432 "Expecting a track number, got %s", psz_field);
433 goto err_exit;
434 }
435 if (i_track < 1 || i_track > 99) {
436 cdio_log(log_level,
437 "Track number out of range 1 to 99, got %s", psz_field);
438 goto err_exit;
439 }
440 if(cd) {
441 if (-1 == i) {
442 cd->gen.i_first_track = i_track;
443 } else if(cd->gen.i_first_track + i + 1 != i_track) {
444 cdio_log(log_level,
445 "Track number out of sequence. Expected %d, got %d",
446 cd->gen.i_first_track + i + 1, i_track);
447 }
448 }
449 }
450 if (NULL != (psz_field = strtok(NULL, " \t\n\r"))) {
451 track_info_t *this_track=NULL;
452
453 if (cd) {
454 this_track = &(cd->tocent[cd->gen.i_tracks]);
455 this_track->track_num = cd->gen.i_tracks;
456 this_track->num_indices = 0;
457 b_first_index_for_track = false;
458 cd->gen.i_tracks++;
459 }
460 i++;
461
462 if (0 == strcmp("AUDIO", psz_field)) {
463 if (cd) {
464 this_track->mode = AUDIO;
465 this_track->blocksize = CDIO_CD_FRAMESIZE_RAW;
466 this_track->datasize = CDIO_CD_FRAMESIZE_RAW;
467 this_track->datastart = 0;
468 this_track->endsize = 0;
469 this_track->track_format = TRACK_FORMAT_AUDIO;
470 this_track->track_green = false;
471 switch(cd->disc_mode) {
472 case CDIO_DISC_MODE_NO_INFO:
473 cd->disc_mode = CDIO_DISC_MODE_CD_DA;
474 break;
475 case CDIO_DISC_MODE_CD_DA:
476 case CDIO_DISC_MODE_CD_MIXED:
477 case CDIO_DISC_MODE_ERROR:
478 /* Disc type stays the same. */
479 break;
480 case CDIO_DISC_MODE_CD_DATA:
481 case CDIO_DISC_MODE_CD_XA:
482 cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
483 break;
484 default:
485 cd->disc_mode = CDIO_DISC_MODE_ERROR;
486 }
487 }
488 } else if (0 == strcmp("MODE1/2048", psz_field)) {
489 if (cd) {
490 this_track->mode = MODE1;
491 this_track->blocksize = 2048;
492 this_track->track_format= TRACK_FORMAT_DATA;
493 this_track->track_green = false;
494 /* Is the below correct? */
495 this_track->datastart = 0;
496 this_track->datasize = CDIO_CD_FRAMESIZE;
497 this_track->endsize = 0;
498 switch(cd->disc_mode) {
499 case CDIO_DISC_MODE_NO_INFO:
500 cd->disc_mode = CDIO_DISC_MODE_CD_DATA;
501 break;
502 case CDIO_DISC_MODE_CD_DATA:
503 case CDIO_DISC_MODE_CD_MIXED:
504 case CDIO_DISC_MODE_ERROR:
505 /* Disc type stays the same. */
506 break;
507 case CDIO_DISC_MODE_CD_DA:
508 case CDIO_DISC_MODE_CD_XA:
509 cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
510 break;
511 default:
512 cd->disc_mode = CDIO_DISC_MODE_ERROR;
513 }
514 }
515 } else if (0 == strcmp("MODE1/2352", psz_field)) {
516 if (cd) {
517 this_track->blocksize = 2352;
518 this_track->track_format= TRACK_FORMAT_DATA;
519 this_track->track_green = false;
520 this_track->datastart = CDIO_CD_SYNC_SIZE
521 + CDIO_CD_HEADER_SIZE;
522 this_track->datasize = CDIO_CD_FRAMESIZE;
523 this_track->endsize = CDIO_CD_EDC_SIZE
524 + CDIO_CD_M1F1_ZERO_SIZE + CDIO_CD_ECC_SIZE;
525 this_track->mode = MODE1_RAW;
526 switch(cd->disc_mode) {
527 case CDIO_DISC_MODE_NO_INFO:
528 cd->disc_mode = CDIO_DISC_MODE_CD_DATA;
529 break;
530 case CDIO_DISC_MODE_CD_DATA:
531 case CDIO_DISC_MODE_CD_MIXED:
532 case CDIO_DISC_MODE_ERROR:
533 /* Disc type stays the same. */
534 break;
535 case CDIO_DISC_MODE_CD_DA:
536 case CDIO_DISC_MODE_CD_XA:
537 cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
538 break;
539 default:
540 cd->disc_mode = CDIO_DISC_MODE_ERROR;
541 }
542 }
543 } else if (0 == strcmp("MODE2/2336", psz_field)) {
544 if (cd) {
545 this_track->blocksize = 2336;
546 this_track->track_format= TRACK_FORMAT_XA;
547 this_track->track_green = true;
548 this_track->mode = MODE2;
549 this_track->datastart = CDIO_CD_SYNC_SIZE
550 + CDIO_CD_HEADER_SIZE;
551 this_track->datasize = M2RAW_SECTOR_SIZE;
552 this_track->endsize = 0;
553 switch(cd->disc_mode) {
554 case CDIO_DISC_MODE_NO_INFO:
555 cd->disc_mode = CDIO_DISC_MODE_CD_DATA;
556 break;
557 case CDIO_DISC_MODE_CD_DATA:
558 case CDIO_DISC_MODE_CD_MIXED:
559 case CDIO_DISC_MODE_ERROR:
560 /* Disc type stays the same. */
561 break;
562 case CDIO_DISC_MODE_CD_DA:
563 case CDIO_DISC_MODE_CD_XA:
564 cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
565 break;
566 default:
567 cd->disc_mode = CDIO_DISC_MODE_ERROR;
568 }
569 }
570 } else if (0 == strcmp("MODE2/2048", psz_field)) {
571 if (cd) {
572 this_track->blocksize = 2048;
573 this_track->track_format= TRACK_FORMAT_XA;
574 this_track->track_green = true;
575 this_track->mode = MODE2_FORM1;
576 switch(cd->disc_mode) {
577 case CDIO_DISC_MODE_NO_INFO:
578 cd->disc_mode = CDIO_DISC_MODE_CD_XA;
579 break;
580 case CDIO_DISC_MODE_CD_XA:
581 case CDIO_DISC_MODE_CD_MIXED:
582 case CDIO_DISC_MODE_ERROR:
583 /* Disc type stays the same. */
584 break;
585 case CDIO_DISC_MODE_CD_DA:
586 case CDIO_DISC_MODE_CD_DATA:
587 cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
588 break;
589 default:
590 cd->disc_mode = CDIO_DISC_MODE_ERROR;
591 }
592 }
593 } else if (0 == strcmp("MODE2/2324", psz_field)) {
594 if (cd) {
595 this_track->blocksize = 2324;
596 this_track->track_format= TRACK_FORMAT_XA;
597 this_track->track_green = true;
598 this_track->mode = MODE2_FORM2;
599 switch(cd->disc_mode) {
600 case CDIO_DISC_MODE_NO_INFO:
601 cd->disc_mode = CDIO_DISC_MODE_CD_XA;
602 break;
603 case CDIO_DISC_MODE_CD_XA:
604 case CDIO_DISC_MODE_CD_MIXED:
605 case CDIO_DISC_MODE_ERROR:
606 /* Disc type stays the same. */
607 break;
608 case CDIO_DISC_MODE_CD_DA:
609 case CDIO_DISC_MODE_CD_DATA:
610 cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
611 break;
612 default:
613 cd->disc_mode = CDIO_DISC_MODE_ERROR;
614 }
615 }
616 } else if (0 == strcmp("MODE2/2336", psz_field)) {
617 if (cd) {
618 this_track->blocksize = 2336;
619 this_track->track_format= TRACK_FORMAT_XA;
620 this_track->track_green = true;
621 this_track->mode = MODE2_FORM_MIX;
622 this_track->datastart = CDIO_CD_SYNC_SIZE
623 + CDIO_CD_HEADER_SIZE;
624 this_track->datasize = M2RAW_SECTOR_SIZE;
625 this_track->endsize = 0;
626 switch(cd->disc_mode) {
627 case CDIO_DISC_MODE_NO_INFO:
628 cd->disc_mode = CDIO_DISC_MODE_CD_XA;
629 break;
630 case CDIO_DISC_MODE_CD_XA:
631 case CDIO_DISC_MODE_CD_MIXED:
632 case CDIO_DISC_MODE_ERROR:
633 /* Disc type stays the same. */
634 break;
635 case CDIO_DISC_MODE_CD_DA:
636 case CDIO_DISC_MODE_CD_DATA:
637 cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
638 break;
639 default:
640 cd->disc_mode = CDIO_DISC_MODE_ERROR;
641 }
642 }
643 } else if (0 == strcmp("MODE2/2352", psz_field)) {
644 if (cd) {
645 this_track->blocksize = 2352;
646 this_track->track_format= TRACK_FORMAT_XA;
647 this_track->track_green = true;
648 this_track->mode = MODE2_RAW;
649 this_track->datastart = CDIO_CD_SYNC_SIZE
650 + CDIO_CD_HEADER_SIZE + CDIO_CD_SUBHEADER_SIZE;
651 this_track->datasize = CDIO_CD_FRAMESIZE;
652 this_track->endsize = CDIO_CD_SYNC_SIZE + CDIO_CD_ECC_SIZE;
653 switch(cd->disc_mode) {
654 case CDIO_DISC_MODE_NO_INFO:
655 cd->disc_mode = CDIO_DISC_MODE_CD_XA;
656 break;
657 case CDIO_DISC_MODE_CD_XA:
658 case CDIO_DISC_MODE_CD_MIXED:
659 case CDIO_DISC_MODE_ERROR:
660 /* Disc type stays the same. */
661 break;
662 case CDIO_DISC_MODE_CD_DA:
663 case CDIO_DISC_MODE_CD_DATA:
664 cd->disc_mode = CDIO_DISC_MODE_CD_MIXED;
665 break;
666 default:
667 cd->disc_mode = CDIO_DISC_MODE_ERROR;
668 }
669 }
670 } else {
671 cdio_log(log_level,
672 "%s line %d after word TRACK:",
673 psz_cue_name, i_line);
674 cdio_log(log_level,
675 "Unknown track mode %s", psz_field);
676 goto err_exit;
677 }
678 } else {
679 goto format_error;
680 }
681
682 /* FLAGS flag1 flag2 ... */
683 } else if (0 == strcmp("FLAGS", psz_keyword)) {
684 if (0 <= i) {
685 while (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
686 if (0 == strcmp ("PRE", psz_field)) {
687 if (cd) cd->tocent[i].flags |= PRE_EMPHASIS;
688 } else if (0 == strcmp ("DCP", psz_field)) {
689 if (cd) cd->tocent[i].flags |= COPY_PERMITTED;
690 } else if (0 == strcmp ("4CH", psz_field)) {
691 if (cd) cd->tocent[i].flags |= FOUR_CHANNEL_AUDIO;
692 } else if (0 == strcmp ("SCMS", psz_field)) {
693 if (cd) cd->tocent[i].flags |= SCMS;
694 } else {
695 goto format_error;
696 }
697 }
698 } else {
699 goto format_error;
700 }
701
702 /* ISRC CCOOOYYSSSSS */
703 } else if (0 == strcmp("ISRC", psz_keyword)) {
704 if (0 <= i) {
705 if (NULL != (psz_field = strtok (NULL, " \t\n\r"))) {
706 if (cd) cd->tocent[i].isrc = strdup (psz_field);
707 } else {
708 goto format_error;
709 }
710 } else {
711 goto in_global_section;
712 }
713
714 /* PREGAP MM:SS:FF */
715 } else if (0 == strcmp("PREGAP", psz_keyword)) {
716 if (0 <= i) {
717 if (NULL != (psz_field = strtok(NULL, " \t\n\r"))) {
718 lba_t lba = cdio_lsn_to_lba(cdio_mmssff_to_lba (psz_field));
719 if (CDIO_INVALID_LBA == lba) {
720 cdio_log(log_level, "%s line %d: after word PREGAP:",
721 psz_cue_name, i_line);
722 cdio_log(log_level, "Invalid MSF string %s",
723 psz_field);
724 goto err_exit;
725 }
726 if (cd) {
727 cd->tocent[i].silence = lba;
728 }
729 } else {
730 goto format_error;
731 } if (NULL != strtok(NULL, " \t\n\r")) {
732 goto format_error;
733 }
734 } else {
735 goto in_global_section;
736 }
737
738 /* INDEX [##] MM:SS:FF */
739 } else if (0 == strcmp ("INDEX", psz_keyword)) {
740 if (0 <= i) {
741 if (NULL != (psz_field = strtok(NULL, " \t\n\r")))
742 if (1!=sscanf(psz_field, "%d", &start_index)) {
743 cdio_log(log_level,
744 "%s line %d after word INDEX:",
745 psz_cue_name, i_line);
746 cdio_log(log_level,
747 "expecting an index number, got %s",
748 psz_field);
749 goto err_exit;
750 }
751 if (NULL != (psz_field = strtok(NULL, " \t\n\r"))) {
752 lba_t lba = cdio_mmssff_to_lba (psz_field);
753 if (CDIO_INVALID_LBA == lba) {
754 cdio_log(log_level, "%s line %d: after word INDEX:",
755 psz_cue_name, i_line);
756 cdio_log(log_level, "Invalid MSF string %s",
757 psz_field);
758 goto err_exit;
759 }
760 if (cd) {
761 #ifdef FIXME
762 cd->tocent[i].indexes[cd->tocent[i].nindex++] = lba;
763 #else
764 track_info_t *this_track=
765 &(cd->tocent[cd->gen.i_tracks - 1]);
766
767 switch (start_index) {
768
769 case 0:
770 lba += CDIO_PREGAP_SECTORS;
771 this_track->pregap = lba;
772 break;
773
774 case 1:
775 if (!b_first_index_for_track) {
776 lba += CDIO_PREGAP_SECTORS;
777 cdio_lba_to_msf(lba, &(this_track->start_msf));
778 b_first_index_for_track = true;
779 this_track->start_lba = lba;
780 }
781
782 if (cd->gen.i_tracks > 1) {
783 /* Figure out number of sectors for previous track */
784 track_info_t *prev_track=&(cd->tocent[cd->gen.i_tracks-2]);
785 if ( this_track->start_lba < prev_track->start_lba ) {
786 cdio_log (log_level,
787 "track %d at LBA %lu starts before track %d at LBA %lu",
788 cd->gen.i_tracks,
789 (unsigned long int) this_track->start_lba,
790 cd->gen.i_tracks,
791 (unsigned long int) prev_track->start_lba);
792 prev_track->sec_count = 0;
793 } else if ( this_track->start_lba >= prev_track->start_lba
794 + CDIO_PREGAP_SECTORS ) {
795 prev_track->sec_count = this_track->start_lba -
796 prev_track->start_lba - CDIO_PREGAP_SECTORS ;
797 } else {
798 cdio_log (log_level,
799 "%lu fewer than pregap (%d) sectors in track %d",
800 (long unsigned int)
801 this_track->start_lba - prev_track->start_lba,
802 CDIO_PREGAP_SECTORS,
803 cd->gen.i_tracks);
804 /* Include pregap portion in sec_count. Maybe the pregap
805 was omitted. */
806 prev_track->sec_count = this_track->start_lba -
807 prev_track->start_lba;
808 }
809 }
810 this_track->num_indices++;
811 break;
812
813 default:
814 break;
815 }
816 }
817 #endif
818 } else {
819 goto format_error;
820 }
821 } else {
822 goto in_global_section;
823 }
824
825 /* CD-Text */
826 } else if ( CDTEXT_FIELD_INVALID !=
827 (cdtext_key = cdtext_is_field (psz_keyword)) ) {
828 if (cd) {
829 if (NULL == cd->gen.cdtext) {
830 cd->gen.cdtext = cdtext_init ();
831 cd->gen.cdtext->block[cd->gen.cdtext->block_i].language_code = CDTEXT_LANGUAGE_ENGLISH;
832 }
833 cdtext_set (cd->gen.cdtext, cdtext_key, (uint8_t*) strtok(NULL, "\"\t\n\r"),
834 (-1 == i ? 0 : cd->gen.i_first_track + i),
835 "ISO-8859-1");
836 }
837
838 /* unrecognized line */
839 } else {
840 cdio_log(log_level, "%s line %d: warning: unrecognized keyword: %s",
841 psz_cue_name, i_line, psz_keyword);
842 goto err_exit;
843 }
844 }
845 }
846
847 if (NULL != cd) {
848 cd->gen.toc_init = true;
849 }
850
851 fclose (fp);
852 return true;
853
854 format_error:
855 cdio_log(log_level, "%s line %d after word %s",
856 psz_cue_name, i_line, psz_keyword);
857 goto err_exit;
858
859 in_global_section:
860 cdio_log(log_level, "%s line %d: word %s not allowed in global section",
861 psz_cue_name, i_line, psz_keyword);
862 goto err_exit;
863
864 not_in_global_section:
865 cdio_log(log_level, "%s line %d: word %s only allowed in global section",
866 psz_cue_name, i_line, psz_keyword);
867
868 err_exit:
869 fclose (fp);
870 return false;
871
872 }
873
874 /*!
875 Reads a single audio sector from CD device into data starting
876 from lsn. Returns 0 if no error.
877 */
878 static driver_return_code_t
879 _read_audio_sectors_bincue (void *p_user_data, void *data, lsn_t lsn,
880 unsigned int nblocks)
881 {
882 _img_private_t *p_env = p_user_data;
883 int ret;
884
885 ret = cdio_stream_seek (p_env->gen.data_source,
886 lsn * CDIO_CD_FRAMESIZE_RAW, SEEK_SET);
887 if (ret!=0) return ret;
888
889 ret = cdio_stream_read (p_env->gen.data_source, data,
890 CDIO_CD_FRAMESIZE_RAW, nblocks);
891
892 /* ret is number of bytes if okay, but we need to return 0 okay. */
893 return ret == 0;
894 }
895
896 /*!
897 Reads a single mode2 sector from cd device into data starting
898 from lsn. Returns 0 if no error.
899 */
900 static driver_return_code_t
901 _read_mode1_sector_bincue (void *p_user_data, void *data, lsn_t lsn,
902 bool b_form2)
903 {
904 _img_private_t *p_env = p_user_data;
905 int ret;
906 char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
907 int blocksize = CDIO_CD_FRAMESIZE_RAW;
908
909 ret = cdio_stream_seek (p_env->gen.data_source, lsn * blocksize, SEEK_SET);
910 if (ret!=0) return ret;
911
912 /* FIXME: Not completely sure the below is correct. */
913 ret = cdio_stream_read (p_env->gen.data_source, buf, CDIO_CD_FRAMESIZE_RAW, 1);
914 if (ret==0) return ret;
915
916 memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE,
917 b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE);
918
919 return DRIVER_OP_SUCCESS;
920 }
921
922 /*!
923 Reads nblocks of mode1 sectors from cd device into data starting
924 from lsn.
925 Returns 0 if no error.
926 */
927 static driver_return_code_t
928 _read_mode1_sectors_bincue (void *p_user_data, void *data, lsn_t lsn,
929 bool b_form2, unsigned int nblocks)
930 {
931 _img_private_t *p_env = p_user_data;
932 int i;
933 int retval;
934 unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE;
935
936 for (i = 0; i < nblocks; i++) {
937 if ( (retval = _read_mode1_sector_bincue (p_env,
938 ((char *)data) + (blocksize * i),
939 lsn + i, b_form2)) )
940 return retval;
941 }
942 return DRIVER_OP_SUCCESS;
943 }
944
945 /*!
946 Reads a single mode1 sector from cd device into data starting
947 from lsn. Returns 0 if no error.
948 */
949 static driver_return_code_t
950 _read_mode2_sector_bincue (void *p_user_data, void *data, lsn_t lsn,
951 bool b_form2)
952 {
953 _img_private_t *p_env = p_user_data;
954 int ret;
955 char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
956
957 /* NOTE: The logic below seems a bit wrong and convoluted
958 to me, but passes the regression tests. (Perhaps it is why we get
959 valgrind errors in vcdxrip). Leave it the way it was for now.
960 Review this sector 2336 stuff later.
961 */
962
963 int blocksize = CDIO_CD_FRAMESIZE_RAW;
964
965 ret = cdio_stream_seek (p_env->gen.data_source, lsn * blocksize, SEEK_SET);
966 if (ret!=0) return ret;
967
968 ret = cdio_stream_read (p_env->gen.data_source, buf, CDIO_CD_FRAMESIZE_RAW, 1);
969 if (ret==0) return ret;
970
971
972 /* See NOTE above. */
973 if (b_form2)
974 memcpy (data, buf + CDIO_CD_SYNC_SIZE + CDIO_CD_HEADER_SIZE,
975 M2RAW_SECTOR_SIZE);
976 else
977 memcpy (data, buf + CDIO_CD_XA_SYNC_HEADER, CDIO_CD_FRAMESIZE);
978
979 return DRIVER_OP_SUCCESS;
980 }
981
982 /*!
983 Reads nblocks of mode2 sectors from cd device into data starting
984 from lsn.
985 Returns 0 if no error.
986 */
987 static driver_return_code_t
988 _read_mode2_sectors_bincue (void *p_user_data, void *data, lsn_t lsn,
989 bool b_form2, unsigned int nblocks)
990 {
991 _img_private_t *p_env = p_user_data;
992 int i;
993 int retval;
994 unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE;
995
996 for (i = 0; i < nblocks; i++) {
997 if ( (retval = _read_mode2_sector_bincue (p_env,
998 ((char *)data) + (blocksize * i),
999 lsn + i, b_form2)) )
1000 return retval;
1001 }
1002 return 0;
1003 }
1004
1005 #if !defined(HAVE_GLOB_H) && defined(_WIN32)
1006 static void Win32Glob(const char* pattern, const char* szCurPath, char ***drives, unsigned int *num_files)
1007 {
1008 char szPath[MAX_PATH];
1009 WIN32_FIND_DATAA ffd;
1010 HANDLE hFind;
1011 BOOL bFound;
1012
1013 SetCurrentDirectoryA(szCurPath);
1014
1015 hFind = FindFirstFileA(pattern, &ffd);
1016 bFound = (hFind != INVALID_HANDLE_VALUE);
1017 while (bFound) {
1018 cdio_add_device_list(drives, ffd.cFileName, num_files);
1019 bFound = FindNextFileA(hFind, &ffd);
1020 }
1021 if (hFind != INVALID_HANDLE_VALUE)
1022 FindClose(hFind);
1023
1024 hFind = FindFirstFileA("*", &ffd);
1025 bFound = (hFind != INVALID_HANDLE_VALUE);
1026 while (bFound) {
1027 if ( (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
1028 (strcmp(ffd.cFileName, ".") != 0) && (strcmp(ffd.cFileName, "..") != 0) ) {
1029 GetFullPathNameA(ffd.cFileName, sizeof(szPath), szPath, NULL);
1030 Win32Glob(pattern, szPath, drives, num_files);
1031 SetCurrentDirectoryA(szCurPath);
1032 }
1033 bFound = FindNextFileA(hFind, &ffd);
1034 }
1035 if (hFind != INVALID_HANDLE_VALUE)
1036 FindClose(hFind);
1037 }
1038 #endif
1039
1040 /*!
1041 Return an array of strings giving possible BIN/CUE disk images.
1042 */
1043 char **
1044 cdio_get_devices_bincue (void)
1045 {
1046 char **drives = NULL;
1047 unsigned int num_files=0;
1048 #ifdef HAVE_GLOB_H
1049 unsigned int i;
1050 glob_t globbuf;
1051 globbuf.gl_offs = 0;
1052 glob("*.cue", GLOB_DOOFFS, NULL, &globbuf);
1053 for (i=0; i<globbuf.gl_pathc; i++) {
1054 cdio_add_device_list(&drives, globbuf.gl_pathv[i], &num_files);
1055 }
1056 globfree(&globbuf);
1057 #elif defined(_WIN32)
1058 char szStartDir[MAX_PATH];
1059 GetCurrentDirectoryA(sizeof(szStartDir), szStartDir);
1060 Win32Glob("*.cue", szStartDir, &drives, &num_files);
1061 #else
1062 cdio_add_device_list(&drives, DEFAULT_CDIO_DEVICE, &num_files);
1063 #endif /*HAVE_GLOB_H*/
1064 cdio_add_device_list(&drives, NULL, &num_files);
1065 return drives;
1066 }
1067
1068 /*!
1069 Return a string containing the default CD device.
1070 */
1071 char *
1072 cdio_get_default_device_bincue(void)
1073 {
1074 char **drives = cdio_get_devices_bincue();
1075 char *drive = (drives[0] == NULL) ? NULL : strdup(drives[0]);
1076 cdio_free_device_list(drives);
1077 return drive;
1078 }
1079
1080 static bool
1081 get_hwinfo_bincue ( const CdIo_t *p_cdio, /*out*/ cdio_hwinfo_t *hw_info)
1082 {
1083 strncpy(hw_info->psz_vendor, "libcdio",
1084 sizeof(hw_info->psz_vendor)-1);
1085 hw_info->psz_vendor[sizeof(hw_info->psz_vendor)-1] = '\0';
1086 strncpy(hw_info->psz_model, "CDRWIN",
1087 sizeof(hw_info->psz_model)-1);
1088 hw_info->psz_model[sizeof(hw_info->psz_model)-1] = '\0';
1089 strncpy(hw_info->psz_revision, CDIO_VERSION,
1090 sizeof(hw_info->psz_revision)-1);
1091 hw_info->psz_revision[sizeof(hw_info->psz_revision)-1] = '\0';
1092 return true;
1093
1094 }
1095
1096 /*!
1097 Return the number of tracks in the current medium.
1098 CDIO_INVALID_TRACK is returned on error.
1099 */
1100 static track_format_t
1101 _get_track_format_bincue(void *p_user_data, track_t i_track)
1102 {
1103 const _img_private_t *p_env = p_user_data;
1104
1105 if (!p_env->gen.init) return TRACK_FORMAT_ERROR;
1106
1107 if (i_track > p_env->gen.i_first_track + p_env->gen.i_tracks - 1
1108 || i_track < p_env->gen.i_first_track)
1109 return TRACK_FORMAT_ERROR;
1110
1111 return p_env->tocent[i_track-p_env->gen.i_first_track].track_format;
1112 }
1113
1114 /*!
1115 Return true if we have XA data (green, mode2 form1) or
1116 XA data (green, mode2 form2). That is track begins:
1117 sync - header - subheader
1118 12 4 - 8
1119
1120 FIXME: there's gotta be a better design for this and get_track_format?
1121 */
1122 static bool
1123 _get_track_green_bincue(void *p_user_data, track_t i_track)
1124 {
1125 _img_private_t *p_env = p_user_data;
1126
1127 if ( NULL == p_env ||
1128 ( i_track < p_env->gen.i_first_track
1129 || i_track >= p_env->gen.i_tracks + p_env->gen.i_first_track ) )
1130 return false;
1131
1132 return p_env->tocent[i_track-p_env->gen.i_first_track].track_green;
1133 }
1134
1135 /*!
1136 Return the starting LSN track number
1137 i_track in obj. Track numbers start at 1.
1138 The "leadout" track is specified either by
1139 using i_track LEADOUT_TRACK or the total tracks+1.
1140 False is returned if there is no track entry.
1141 */
1142 static lba_t
1143 _get_lba_track_bincue(void *p_user_data, track_t i_track)
1144 {
1145 _img_private_t *p_env = p_user_data;
1146
1147 if (i_track == CDIO_CDROM_LEADOUT_TRACK)
1148 i_track = p_env->gen.i_tracks + p_env->gen.i_first_track;
1149
1150 if (i_track <= p_env->gen.i_tracks + p_env->gen.i_first_track
1151 && i_track >= p_env->gen.i_first_track) {
1152 return p_env->tocent[i_track-p_env->gen.i_first_track].start_lba;
1153 } else
1154 return CDIO_INVALID_LBA;
1155 }
1156
1157 /*!
1158 Return corresponding BIN file if psz_cue_name is a cue file or NULL
1159 if not a CUE file.
1160 */
1161 char *
1162 cdio_is_cuefile(const char *psz_cue_name)
1163 {
1164 int i;
1165 char *psz_bin_name;
1166
1167 if (psz_cue_name == NULL) return NULL;
1168
1169 /* FIXME? Now that we have cue parsing, should we really force
1170 the filename extension requirement or is it enough just to
1171 parse the cuefile?
1172 */
1173
1174 psz_bin_name=strdup(psz_cue_name);
1175 i=strlen(psz_bin_name)-strlen("cue");
1176
1177 if (i>0) {
1178 if (psz_cue_name[i]=='c' && psz_cue_name[i+1]=='u' && psz_cue_name[i+2]=='e') {
1179 psz_bin_name[i++]='b'; psz_bin_name[i++]='i'; psz_bin_name[i++]='n';
1180 if (parse_cuefile(NULL, psz_cue_name))
1181 return psz_bin_name;
1182 else
1183 goto error;
1184 }
1185 else if (psz_cue_name[i]=='C' && psz_cue_name[i+1]=='U' && psz_cue_name[i+2]=='E') {
1186 psz_bin_name[i++]='B'; psz_bin_name[i++]='I'; psz_bin_name[i++]='N';
1187 if (parse_cuefile(NULL, psz_cue_name))
1188 return psz_bin_name;
1189 else
1190 goto error;
1191 }
1192 }
1193 error:
1194 free(psz_bin_name);
1195 return NULL;
1196 }
1197
1198 /*!
1199 Return corresponding CUE file if psz_bin_name is a bin file or NULL
1200 if not a BIN file.
1201 */
1202 char *
1203 cdio_is_binfile(const char *psz_bin_name)
1204 {
1205 int i;
1206 char *psz_cue_name;
1207
1208 if (psz_bin_name == NULL) return NULL;
1209
1210 psz_cue_name=strdup(psz_bin_name);
1211 i=strlen(psz_bin_name)-strlen("bin");
1212
1213 if (i>0) {
1214 if (psz_bin_name[i]=='b' && psz_bin_name[i+1]=='i' && psz_bin_name[i+2]=='n') {
1215 psz_cue_name[i++]='c'; psz_cue_name[i++]='u'; psz_cue_name[i++]='e';
1216 return psz_cue_name;
1217 }
1218 else if (psz_bin_name[i]=='B' && psz_bin_name[i+1]=='I' && psz_bin_name[i+2]=='N') {
1219 psz_cue_name[i++]='C'; psz_cue_name[i++]='U'; psz_cue_name[i++]='E';
1220 return psz_cue_name;
1221 }
1222 }
1223 free(psz_cue_name);
1224 return NULL;
1225 }
1226
1227 /*!
1228 Initialization routine. This is the only thing that doesn't
1229 get called via a function pointer. In fact *we* are the
1230 ones to set that up.
1231 */
1232 CdIo_t *
1233 cdio_open_am_bincue (const char *psz_source_name, const char *psz_access_mode)
1234 {
1235 if (psz_access_mode != NULL)
1236 cdio_warn ("there is only one access mode for bincue. Arg %s ignored",
1237 psz_access_mode);
1238 return cdio_open_bincue(psz_source_name);
1239 }
1240
1241 /*!
1242 Initialization routine. This is the only thing that doesn't
1243 get called via a function pointer. In fact *we* are the
1244 ones to set that up.
1245 */
1246 CdIo_t *
1247 cdio_open_bincue (const char *psz_source)
1248 {
1249 char *psz_bin_name = cdio_is_cuefile(psz_source);
1250
1251 if (NULL != psz_bin_name) {
1252 free(psz_bin_name);
1253 return cdio_open_cue(psz_source);
1254 } else {
1255 char *psz_cue_name = cdio_is_binfile(psz_source);
1256 CdIo_t *cdio = cdio_open_cue(psz_cue_name);
1257 free(psz_cue_name);
1258 return cdio;
1259 }
1260 }
1261
1262 CdIo_t *
1263 cdio_open_cue (const char *psz_cue_name)
1264 {
1265 CdIo_t *ret;
1266 _img_private_t *p_data;
1267 char *psz_bin_name;
1268
1269 cdio_funcs_t _funcs;
1270
1271 memset( &_funcs, 0, sizeof(_funcs) );
1272
1273 _funcs.eject_media = _eject_media_image;
1274 _funcs.free = _free_image;
1275 _funcs.get_arg = _get_arg_image;
1276 _funcs.get_cdtext = _get_cdtext_image;
1277 _funcs.get_cdtext_raw = NULL;
1278 _funcs.get_devices = cdio_get_devices_bincue;
1279 _funcs.get_default_device = cdio_get_default_device_bincue;
1280 _funcs.get_disc_last_lsn = get_disc_last_lsn_bincue;
1281 _funcs.get_discmode = _get_discmode_image;
1282 _funcs.get_drive_cap = _get_drive_cap_image;
1283 _funcs.get_first_track_num = _get_first_track_num_image;
1284 _funcs.get_hwinfo = get_hwinfo_bincue;
1285 _funcs.get_media_changed = get_media_changed_image;
1286 _funcs.get_mcn = _get_mcn_image;
1287 _funcs.get_num_tracks = _get_num_tracks_image;
1288 _funcs.get_track_channels = get_track_channels_image;
1289 _funcs.get_track_copy_permit = get_track_copy_permit_image;
1290 _funcs.get_track_format = _get_track_format_bincue;
1291 _funcs.get_track_green = _get_track_green_bincue;
1292 _funcs.get_track_lba = _get_lba_track_bincue;
1293 _funcs.get_track_msf = _get_track_msf_image;
1294 _funcs.get_track_preemphasis = get_track_preemphasis_image;
1295 _funcs.get_track_pregap_lba = get_track_pregap_lba_image;
1296 _funcs.get_track_isrc = get_track_isrc_image;
1297 _funcs.lseek = _lseek_bincue;
1298 _funcs.read = _read_bincue;
1299 _funcs.read_audio_sectors = _read_audio_sectors_bincue;
1300 _funcs.read_data_sectors = read_data_sectors_image;
1301 _funcs.read_mode1_sector = _read_mode1_sector_bincue;
1302 _funcs.read_mode1_sectors = _read_mode1_sectors_bincue;
1303 _funcs.read_mode2_sector = _read_mode2_sector_bincue;
1304 _funcs.read_mode2_sectors = _read_mode2_sectors_bincue;
1305 _funcs.run_mmc_cmd = NULL;
1306 _funcs.set_arg = _set_arg_image;
1307 _funcs.set_speed = cdio_generic_unimplemented_set_speed;
1308 _funcs.set_blocksize = cdio_generic_unimplemented_set_blocksize;
1309
1310 if (NULL == psz_cue_name) return NULL;
1311
1312 p_data = calloc(1, sizeof (_img_private_t));
1313 p_data->gen.init = false;
1314 p_data->psz_cue_name = NULL;
1315
1316 ret = cdio_new ((void *)p_data, &_funcs);
1317
1318 if (ret == NULL) {
1319 free(p_data);
1320 return NULL;
1321 }
1322
1323 ret->driver_id = DRIVER_BINCUE;
1324 psz_bin_name = cdio_is_cuefile(psz_cue_name);
1325
1326 if (NULL == psz_bin_name) {
1327 cdio_error ("source name %s is not recognized as a CUE file",
1328 psz_cue_name);
1329 }
1330
1331 _set_arg_image (p_data, "cue", psz_cue_name);
1332 _set_arg_image (p_data, "source", psz_bin_name);
1333 _set_arg_image (p_data, "access-mode", "bincue");
1334 free(psz_bin_name);
1335
1336 if (_init_bincue(p_data)) {
1337 return ret;
1338 } else {
1339 _free_image(p_data);
1340 free(ret);
1341 return NULL;
1342 }
1343 }
1344
1345 bool
1346 cdio_have_bincue (void)
1347 {
1348 return true;
1349 }
1350