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