]> git.sesse.net Git - vlc/blob - modules/access/vcd/cdrom.c
814990e814ff8ed299fb482fe28d524788ab5881
[vlc] / modules / access / vcd / cdrom.c
1 /****************************************************************************
2  * cdrom.c: cdrom tools
3  *****************************************************************************
4  * Copyright (C) 1998-2001 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Johan Bilien <jobi@via.ecp.fr>
8  *          Gildas Bazin <gbazin@netcourrier.com>
9  *          Jon Lech Johansen <jon-vl@nanocrew.net>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <vlc/vlc.h>
30 #include <vlc_access.h>
31
32 #ifdef HAVE_UNISTD_H
33 #   include <unistd.h>
34 #endif
35
36 #include <errno.h>
37 #ifdef HAVE_SYS_TYPES_H
38 #   include <sys/types.h>
39 #endif
40 #ifdef HAVE_SYS_STAT_H
41 #   include <sys/stat.h>
42 #endif
43 #ifdef HAVE_FCNTL_H 
44 #   include <fcntl.h>
45 #endif
46
47 #if defined( SYS_BSDI )
48 #   include <dvd.h>
49 #elif defined ( __APPLE__ )
50 #   include <CoreFoundation/CFBase.h>
51 #   include <IOKit/IOKitLib.h>
52 #   include <IOKit/storage/IOCDTypes.h>
53 #   include <IOKit/storage/IOCDMedia.h>
54 #   include <IOKit/storage/IOCDMediaBSDClient.h>
55 #elif defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
56 #   include <sys/inttypes.h>
57 #   include <sys/cdio.h>
58 #   include <sys/scsiio.h>
59 #elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H )
60 #   include <sys/cdio.h>
61 #   include <sys/cdrio.h>
62 #elif defined( WIN32 )
63 #   include <windows.h>
64 #   include <winioctl.h>
65 #elif defined (__linux__)
66 #   include <sys/ioctl.h>
67 #   include <linux/cdrom.h>
68 #else
69 #   error FIXME
70 #endif
71
72 #include "cdrom_internals.h"
73 #include "cdrom.h"
74 #include <vlc_charset.h>
75
76 /*****************************************************************************
77  * ioctl_Open: Opens a VCD device or file and returns an opaque handle
78  *****************************************************************************/
79 vcddev_t *ioctl_Open( vlc_object_t *p_this, const char *psz_dev )
80 {
81     int i_ret;
82     int b_is_file;
83     vcddev_t *p_vcddev;
84 #ifndef WIN32
85     struct stat fileinfo;
86 #endif
87
88     if( !psz_dev ) return NULL;
89
90     /*
91      *  Initialize structure with default values
92      */
93     p_vcddev = (vcddev_t *)malloc( sizeof(vcddev_t) );
94     if( p_vcddev == NULL )
95     {
96         msg_Err( p_this, "out of memory" );
97         return NULL;
98     }
99     p_vcddev->i_vcdimage_handle = -1;
100     p_vcddev->psz_dev = NULL;
101     b_is_file = 1;
102
103     /*
104      *  Check if we are dealing with a device or a file (vcd image)
105      */
106 #ifdef WIN32
107     if( (strlen( psz_dev ) == 2 && psz_dev[1] == ':') )
108     {
109         b_is_file = 0;
110     }
111
112 #else
113     if( stat( psz_dev, &fileinfo ) < 0 )
114     {
115         free( p_vcddev );
116         return NULL;
117     }
118
119     /* Check if this is a block/char device */
120     if( S_ISBLK( fileinfo.st_mode ) || S_ISCHR( fileinfo.st_mode ) )
121         b_is_file = 0;
122 #endif
123
124     if( b_is_file )
125     {
126         i_ret = OpenVCDImage( p_this, psz_dev, p_vcddev );
127     }
128     else
129     {
130         /*
131          *  open the vcd device
132          */
133
134 #ifdef WIN32
135         i_ret = win32_vcd_open( p_this, psz_dev, p_vcddev );
136 #else
137         p_vcddev->i_device_handle = -1;
138         p_vcddev->i_device_handle = open( psz_dev, O_RDONLY | O_NONBLOCK );
139         i_ret = (p_vcddev->i_device_handle == -1) ? -1 : 0;
140 #endif
141     }
142
143     if( i_ret == 0 )
144     {
145         p_vcddev->psz_dev = (char *)strdup( psz_dev );
146     }
147     else
148     {
149         free( p_vcddev );
150         p_vcddev = NULL;
151     }
152
153     return p_vcddev;
154 }
155
156 /*****************************************************************************
157  * ioctl_Close: Closes an already opened VCD device or file.
158  *****************************************************************************/
159 void ioctl_Close( vlc_object_t * p_this, vcddev_t *p_vcddev )
160 {
161     if( p_vcddev->psz_dev ) free( p_vcddev->psz_dev );
162
163     if( p_vcddev->i_vcdimage_handle != -1 )
164     {
165         /*
166          *  vcd image mode
167          */
168
169         CloseVCDImage( p_this, p_vcddev );
170         return;
171     }
172
173     /*
174      *  vcd device mode
175      */
176
177 #ifdef WIN32
178     if( p_vcddev->h_device_handle )
179         CloseHandle( p_vcddev->h_device_handle );
180     if( p_vcddev->hASPI )
181         FreeLibrary( (HMODULE)p_vcddev->hASPI );
182 #else
183     if( p_vcddev->i_device_handle != -1 )
184         close( p_vcddev->i_device_handle );
185 #endif
186 }
187
188 /*****************************************************************************
189  * ioctl_GetTracksMap: Read the Table of Content, fill in the pp_sectors map
190  *                     if pp_sectors is not null and return the number of
191  *                     tracks available.
192  *****************************************************************************/
193 int ioctl_GetTracksMap( vlc_object_t *p_this, const vcddev_t *p_vcddev,
194                         int **pp_sectors )
195 {
196     int i_tracks = 0;
197
198     if( p_vcddev->i_vcdimage_handle != -1 )
199     {
200         /*
201          *  vcd image mode
202          */
203
204         i_tracks = p_vcddev->i_tracks;
205
206         if( pp_sectors )
207         {
208             *pp_sectors = malloc( (i_tracks + 1) * sizeof(int) );
209             if( *pp_sectors == NULL )
210             {
211                 msg_Err( p_this, "out of memory" );
212                 return 0;
213             }
214             memcpy( *pp_sectors, p_vcddev->p_sectors,
215                     (i_tracks + 1) * sizeof(int) );
216         }
217
218         return i_tracks;
219     }
220     else
221     {
222
223         /*
224          *  vcd device mode
225          */
226
227 #if defined( __APPLE__ )
228
229         CDTOC *pTOC;
230         int i_descriptors;
231
232         if( ( pTOC = darwin_getTOC( p_this, p_vcddev ) ) == NULL )
233         {
234             msg_Err( p_this, "failed to get the TOC" );
235             return 0;
236         }
237
238         i_descriptors = CDTOCGetDescriptorCount( pTOC );
239         i_tracks = darwin_getNumberOfTracks( pTOC, i_descriptors );
240
241         if( pp_sectors )
242         {
243             int i, i_leadout = -1;
244             CDTOCDescriptor *pTrackDescriptors;
245             u_char track;
246
247             *pp_sectors = malloc( (i_tracks + 1) * sizeof(int) );
248             if( *pp_sectors == NULL )
249             {
250                 msg_Err( p_this, "out of memory" );
251                 darwin_freeTOC( pTOC );
252                 return 0;
253             }
254
255             pTrackDescriptors = pTOC->descriptors;
256
257             for( i_tracks = 0, i = 0; i < i_descriptors; i++ )
258             {
259                 track = pTrackDescriptors[i].point;
260
261                 if( track == 0xA2 )
262                     i_leadout = i;
263
264                 if( track > CD_MAX_TRACK_NO || track < CD_MIN_TRACK_NO )
265                     continue;
266
267                 (*pp_sectors)[i_tracks++] =
268                     CDConvertMSFToLBA( pTrackDescriptors[i].p );
269             }
270
271             if( i_leadout == -1 )
272             {
273                 msg_Err( p_this, "leadout not found" );
274                 free( *pp_sectors );
275                 darwin_freeTOC( pTOC );
276                 return 0;
277             }
278
279             /* set leadout sector */
280             (*pp_sectors)[i_tracks] =
281                 CDConvertMSFToLBA( pTrackDescriptors[i_leadout].p );
282         }
283
284         darwin_freeTOC( pTOC );
285
286 #elif defined( WIN32 )
287         if( p_vcddev->hASPI )
288         {
289             HANDLE hEvent;
290             struct SRB_ExecSCSICmd ssc;
291             byte_t p_tocheader[ 4 ];
292
293             /* Create the transfer completion event */
294             hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
295             if( hEvent == NULL )
296             {
297                 return -1;
298             }
299
300             memset( &ssc, 0, sizeof( ssc ) );
301
302             ssc.SRB_Cmd         = SC_EXEC_SCSI_CMD;
303             ssc.SRB_Flags       = SRB_DIR_IN | SRB_EVENT_NOTIFY;
304             ssc.SRB_HaId        = LOBYTE( p_vcddev->i_sid );
305             ssc.SRB_Target      = HIBYTE( p_vcddev->i_sid );
306             ssc.SRB_SenseLen    = SENSE_LEN;
307
308             ssc.SRB_PostProc = (LPVOID) hEvent;
309             ssc.SRB_CDBLen      = 10;
310
311             /* Operation code */
312             ssc.CDBByte[ 0 ] = READ_TOC;
313
314             /* Format */
315             ssc.CDBByte[ 2 ] = READ_TOC_FORMAT_TOC;
316
317             /* Starting track */
318             ssc.CDBByte[ 6 ] = 0;
319
320             /* Allocation length and buffer */
321             ssc.SRB_BufLen = sizeof( p_tocheader );
322             ssc.SRB_BufPointer  = p_tocheader;
323             ssc.CDBByte[ 7 ] = ( ssc.SRB_BufLen >>  8 ) & 0xff;
324             ssc.CDBByte[ 8 ] = ( ssc.SRB_BufLen       ) & 0xff;
325
326             /* Initiate transfer */
327             ResetEvent( hEvent );
328             p_vcddev->lpSendCommand( (void*) &ssc );
329
330             /* If the command has still not been processed, wait until it's
331              * finished */
332             if( ssc.SRB_Status == SS_PENDING )
333                 WaitForSingleObject( hEvent, INFINITE );
334
335             /* check that the transfer went as planned */
336             if( ssc.SRB_Status != SS_COMP )
337             {
338                 CloseHandle( hEvent );
339                 return 0;
340             }
341
342             i_tracks = p_tocheader[3] - p_tocheader[2] + 1;
343
344             if( pp_sectors )
345             {
346                 int i, i_toclength;
347                 byte_t *p_fulltoc;
348
349                 i_toclength = 4 /* header */ + p_tocheader[0] +
350                               ((unsigned int)p_tocheader[1] << 8);
351
352                 p_fulltoc = malloc( i_toclength );
353                 *pp_sectors = malloc( (i_tracks + 1) * sizeof(int) );
354
355                 if( *pp_sectors == NULL || p_fulltoc == NULL )
356                 {
357                     if( *pp_sectors ) free( *pp_sectors );
358                     if( p_fulltoc ) free( p_fulltoc );
359                     msg_Err( p_this, "out of memory" );
360                     CloseHandle( hEvent );
361                     return 0;
362                 }
363
364                 /* Allocation length and buffer */
365                 ssc.SRB_BufLen = i_toclength;
366                 ssc.SRB_BufPointer  = p_fulltoc;
367                 ssc.CDBByte[ 7 ] = ( ssc.SRB_BufLen >>  8 ) & 0xff;
368                 ssc.CDBByte[ 8 ] = ( ssc.SRB_BufLen       ) & 0xff;
369
370                 /* Initiate transfer */
371                 ResetEvent( hEvent );
372                 p_vcddev->lpSendCommand( (void*) &ssc );
373
374                 /* If the command has still not been processed, wait until it's
375                  * finished */
376                 if( ssc.SRB_Status == SS_PENDING )
377                     WaitForSingleObject( hEvent, INFINITE );
378
379                 /* check that the transfer went as planned */
380                 if( ssc.SRB_Status != SS_COMP )
381                     i_tracks = 0;
382
383                 for( i = 0 ; i <= i_tracks ; i++ )
384                 {
385                     int i_index = 8 + 8 * i;
386                     (*pp_sectors)[ i ] = ((int)p_fulltoc[ i_index ] << 24) +
387                                          ((int)p_fulltoc[ i_index+1 ] << 16) +
388                                          ((int)p_fulltoc[ i_index+2 ] << 8) +
389                                          (int)p_fulltoc[ i_index+3 ];
390
391                     msg_Dbg( p_this, "p_sectors: %i, %i", i, (*pp_sectors)[i]);
392                 }
393
394                 free( p_fulltoc );
395             }
396
397             CloseHandle( hEvent );
398
399         }
400         else
401         {
402             DWORD dwBytesReturned;
403             CDROM_TOC cdrom_toc;
404
405             if( DeviceIoControl( p_vcddev->h_device_handle,
406                                  IOCTL_CDROM_READ_TOC,
407                                  NULL, 0, &cdrom_toc, sizeof(CDROM_TOC),
408                                  &dwBytesReturned, NULL ) == 0 )
409             {
410                 msg_Err( p_this, "could not read TOCHDR" );
411                 return 0;
412             }
413
414             i_tracks = cdrom_toc.LastTrack - cdrom_toc.FirstTrack + 1;
415
416             if( pp_sectors )
417             {
418                 int i;
419
420                 *pp_sectors = malloc( (i_tracks + 1) * sizeof(int) );
421                 if( *pp_sectors == NULL )
422                 {
423                     msg_Err( p_this, "out of memory" );
424                     return 0;
425                 }
426
427                 for( i = 0 ; i <= i_tracks ; i++ )
428                 {
429                     (*pp_sectors)[ i ] = MSF_TO_LBA2(
430                                            cdrom_toc.TrackData[i].Address[1],
431                                            cdrom_toc.TrackData[i].Address[2],
432                                            cdrom_toc.TrackData[i].Address[3] );
433                     msg_Dbg( p_this, "p_sectors: %i, %i", i, (*pp_sectors)[i]);
434                 }
435             }
436         }
437
438 #elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H ) \
439        || defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
440         struct ioc_toc_header tochdr;
441         struct ioc_read_toc_entry toc_entries;
442
443         if( ioctl( p_vcddev->i_device_handle, CDIOREADTOCHEADER, &tochdr )
444             == -1 )
445         {
446             msg_Err( p_this, "could not read TOCHDR" );
447             return 0;
448         }
449
450         i_tracks = tochdr.ending_track - tochdr.starting_track + 1;
451
452         if( pp_sectors )
453         {
454              int i;
455
456              *pp_sectors = malloc( (i_tracks + 1) * sizeof(int) );
457              if( *pp_sectors == NULL )
458              {
459                  msg_Err( p_this, "out of memory" );
460                  return NULL;
461              }
462
463              toc_entries.address_format = CD_LBA_FORMAT;
464              toc_entries.starting_track = 0;
465              toc_entries.data_len = ( i_tracks + 1 ) *
466                                         sizeof( struct cd_toc_entry );
467              toc_entries.data = (struct cd_toc_entry *)
468                                     malloc( toc_entries.data_len );
469              if( toc_entries.data == NULL )
470              {
471                  msg_Err( p_this, "out of memory" );
472                  free( *pp_sectors );
473                  return 0;
474              }
475
476              /* Read the TOC */
477              if( ioctl( p_vcddev->i_device_handle, CDIOREADTOCENTRYS,
478                         &toc_entries ) == -1 )
479              {
480                  msg_Err( p_this, "could not read the TOC" );
481                  free( *pp_sectors );
482                  free( toc_entries.data );
483                  return 0;
484              }
485
486              /* Fill the p_sectors structure with the track/sector matches */
487              for( i = 0 ; i <= i_tracks ; i++ )
488              {
489 #if defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
490                  /* FIXME: is this ok? */
491                  (*pp_sectors)[ i ] = toc_entries.data[i].addr.lba;
492 #else
493                  (*pp_sectors)[ i ] = ntohl( toc_entries.data[i].addr.lba );
494 #endif
495              }
496         }
497 #else
498         struct cdrom_tochdr   tochdr;
499         struct cdrom_tocentry tocent;
500
501         /* First we read the TOC header */
502         if( ioctl( p_vcddev->i_device_handle, CDROMREADTOCHDR, &tochdr )
503             == -1 )
504         {
505             msg_Err( p_this, "could not read TOCHDR" );
506             return 0;
507         }
508
509         i_tracks = tochdr.cdth_trk1 - tochdr.cdth_trk0 + 1;
510
511         if( pp_sectors )
512         {
513             int i;
514
515             *pp_sectors = malloc( (i_tracks + 1) * sizeof(int) );
516             if( *pp_sectors == NULL )
517             {
518                 msg_Err( p_this, "out of memory" );
519                 return 0;
520             }
521
522             /* Fill the p_sectors structure with the track/sector matches */
523             for( i = 0 ; i <= i_tracks ; i++ )
524             {
525                 tocent.cdte_format = CDROM_LBA;
526                 tocent.cdte_track =
527                     ( i == i_tracks ) ? CDROM_LEADOUT : tochdr.cdth_trk0 + i;
528
529                 if( ioctl( p_vcddev->i_device_handle, CDROMREADTOCENTRY,
530                            &tocent ) == -1 )
531                 {
532                     msg_Err( p_this, "could not read TOCENTRY" );
533                     free( *pp_sectors );
534                     return 0;
535                 }
536
537                 (*pp_sectors)[ i ] = tocent.cdte_addr.lba;
538             }
539         }
540 #endif
541
542         return i_tracks;
543     }
544 }
545
546 /****************************************************************************
547  * ioctl_ReadSector: Read VCD or CDDA sectors
548  ****************************************************************************/
549 int ioctl_ReadSectors( vlc_object_t *p_this, const vcddev_t *p_vcddev,
550                        int i_sector, byte_t * p_buffer, int i_nb, int i_type )
551 {
552     byte_t *p_block;
553     int i;
554
555     if( i_type == VCD_TYPE ) p_block = malloc( VCD_SECTOR_SIZE * i_nb );
556     else p_block = p_buffer;
557
558     if( p_vcddev->i_vcdimage_handle != -1 )
559     {
560         /*
561          *  vcd image mode
562          */
563         if( lseek( p_vcddev->i_vcdimage_handle, i_sector * VCD_SECTOR_SIZE,
564                    SEEK_SET ) == -1 )
565         {
566             msg_Err( p_this, "Could not lseek to sector %d", i_sector );
567             if( i_type == VCD_TYPE ) free( p_block );
568             return -1;
569         }
570
571         if( read( p_vcddev->i_vcdimage_handle, p_block, VCD_SECTOR_SIZE * i_nb)
572             == -1 )
573         {
574             msg_Err( p_this, "Could not read sector %d", i_sector );
575             if( i_type == VCD_TYPE ) free( p_block );
576             return -1;
577         }
578
579     }
580     else
581     {
582
583         /*
584          *  vcd device mode
585          */
586
587 #if defined( __APPLE__ )
588         dk_cd_read_t cd_read;
589
590         memset( &cd_read, 0, sizeof(cd_read) );
591
592         cd_read.offset = i_sector * VCD_SECTOR_SIZE;
593         cd_read.sectorArea = kCDSectorAreaSync | kCDSectorAreaHeader |
594                              kCDSectorAreaSubHeader | kCDSectorAreaUser |
595                              kCDSectorAreaAuxiliary;
596         cd_read.sectorType = kCDSectorTypeUnknown;
597
598         cd_read.buffer = p_block;
599         cd_read.bufferLength = VCD_SECTOR_SIZE * i_nb;
600
601         if( ioctl( p_vcddev->i_device_handle, DKIOCCDREAD, &cd_read ) == -1 )
602         {
603             msg_Err( p_this, "could not read block %d", i_sector );
604             if( i_type == VCD_TYPE ) free( p_block );
605             return -1;
606         }
607
608 #elif defined( WIN32 )
609         if( p_vcddev->hASPI )
610         {
611             HANDLE hEvent;
612             struct SRB_ExecSCSICmd ssc;
613
614             /* Create the transfer completion event */
615             hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
616             if( hEvent == NULL )
617             {
618                 if( i_type == VCD_TYPE ) free( p_block );
619                 return -1;
620             }
621
622             memset( &ssc, 0, sizeof( ssc ) );
623
624             ssc.SRB_Cmd         = SC_EXEC_SCSI_CMD;
625             ssc.SRB_Flags       = SRB_DIR_IN | SRB_EVENT_NOTIFY;
626             ssc.SRB_HaId        = LOBYTE( p_vcddev->i_sid );
627             ssc.SRB_Target      = HIBYTE( p_vcddev->i_sid );
628             ssc.SRB_SenseLen    = SENSE_LEN;
629
630             ssc.SRB_PostProc = (LPVOID) hEvent;
631             ssc.SRB_CDBLen      = 12;
632
633             /* Operation code */
634             ssc.CDBByte[ 0 ] = READ_CD;
635
636             /* Sector type */
637             ssc.CDBByte[ 1 ] = i_type == VCD_TYPE ? SECTOR_TYPE_MODE2_FORM2 :
638                                                     SECTOR_TYPE_CDDA;
639
640             /* Start of LBA */
641             ssc.CDBByte[ 2 ] = ( i_sector >> 24 ) & 0xff;
642             ssc.CDBByte[ 3 ] = ( i_sector >> 16 ) & 0xff;
643             ssc.CDBByte[ 4 ] = ( i_sector >>  8 ) & 0xff;
644             ssc.CDBByte[ 5 ] = ( i_sector       ) & 0xff;
645
646             /* Transfer length */
647             ssc.CDBByte[ 6 ] = ( i_nb >> 16 ) & 0xff;
648             ssc.CDBByte[ 7 ] = ( i_nb >> 8  ) & 0xff;
649             ssc.CDBByte[ 8 ] = ( i_nb       ) & 0xff;
650
651             /* Data selection */
652             ssc.CDBByte[ 9 ] = i_type == VCD_TYPE ? READ_CD_RAW_MODE2 :
653                                                     READ_CD_USERDATA;
654
655             /* Result buffer */
656             ssc.SRB_BufPointer  = p_block;
657             ssc.SRB_BufLen = VCD_SECTOR_SIZE * i_nb;
658
659             /* Initiate transfer */
660             ResetEvent( hEvent );
661             p_vcddev->lpSendCommand( (void*) &ssc );
662
663             /* If the command has still not been processed, wait until it's
664              * finished */
665             if( ssc.SRB_Status == SS_PENDING )
666             {
667                 WaitForSingleObject( hEvent, INFINITE );
668             }
669             CloseHandle( hEvent );
670
671             /* check that the transfer went as planned */
672             if( ssc.SRB_Status != SS_COMP )
673             {
674                 if( i_type == VCD_TYPE ) free( p_block );
675                 return -1;
676             }
677         }
678         else
679         {
680             DWORD dwBytesReturned;
681             RAW_READ_INFO cdrom_raw;
682
683             /* Initialize CDROM_RAW_READ structure */
684             cdrom_raw.DiskOffset.QuadPart = CD_SECTOR_SIZE * i_sector;
685             cdrom_raw.SectorCount = i_nb;
686             cdrom_raw.TrackMode =  i_type == VCD_TYPE ? XAForm2 : CDDA;
687
688             if( DeviceIoControl( p_vcddev->h_device_handle,
689                                  IOCTL_CDROM_RAW_READ, &cdrom_raw,
690                                  sizeof(RAW_READ_INFO), p_block,
691                                  VCD_SECTOR_SIZE * i_nb, &dwBytesReturned,
692                                  NULL ) == 0 )
693             {
694                 if( i_type == VCD_TYPE )
695                 {
696                     /* Retry in YellowMode2 */
697                     cdrom_raw.TrackMode = YellowMode2;
698                     if( DeviceIoControl( p_vcddev->h_device_handle,
699                                  IOCTL_CDROM_RAW_READ, &cdrom_raw,
700                                  sizeof(RAW_READ_INFO), p_block,
701                                  VCD_SECTOR_SIZE * i_nb, &dwBytesReturned,
702                                  NULL ) == 0 )
703                     {
704                         free( p_block );
705                         return -1;
706                     }
707                 }
708                 else return -1;
709             }
710         }
711
712 #elif defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
713         struct scsireq  sc;
714         int i_ret;
715
716         memset( &sc, 0, sizeof(sc) );
717         sc.cmd[0] = 0xBE;
718         sc.cmd[1] = i_type == VCD_TYPE ? SECTOR_TYPE_MODE2_FORM2:
719                                          SECTOR_TYPE_CDDA;
720         sc.cmd[2] = (i_sector >> 24) & 0xff;
721         sc.cmd[3] = (i_sector >> 16) & 0xff;
722         sc.cmd[4] = (i_sector >>  8) & 0xff;
723         sc.cmd[5] = (i_sector >>  0) & 0xff;
724         sc.cmd[6] = (i_nb >> 16) & 0xff;
725         sc.cmd[7] = (i_nb >>  8) & 0xff;
726         sc.cmd[8] = (i_nb      ) & 0xff;
727         sc.cmd[9] = i_type == VCD_TYPE ? READ_CD_RAW_MODE2 : READ_CD_USERDATA;
728         sc.cmd[10] = 0; /* sub channel */
729         sc.cmdlen = 12;
730         sc.databuf = (caddr_t)p_block;
731         sc.datalen = VCD_SECTOR_SIZE * i_nb;
732         sc.senselen = sizeof( sc.sense );
733         sc.flags = SCCMD_READ;
734         sc.timeout = 10000;
735
736         i_ret = ioctl( i_fd, SCIOCCOMMAND, &sc );
737         if( i_ret == -1 )
738         {
739             msg_Err( p_this, "SCIOCCOMMAND failed" );
740             if( i_type == VCD_TYPE ) free( p_block );
741             return -1;
742         }
743         if( sc.retsts || sc.error )
744         {
745             msg_Err( p_this, "SCSI command failed: status %d error %d\n",
746                              sc.retsts, sc.error );
747             if( i_type == VCD_TYPE ) free( p_block );
748            return -1;
749         }
750
751 #elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H )
752         int i_size = VCD_SECTOR_SIZE;
753
754         if( ioctl( p_vcddev->i_device_handle, CDRIOCSETBLOCKSIZE, &i_size )
755             == -1 )
756         {
757             msg_Err( p_this, "Could not set block size" );
758             if( i_type == VCD_TYPE ) free( p_block );
759             return( -1 );
760         }
761
762         if( lseek( p_vcddev->i_device_handle,
763                    i_sector * VCD_SECTOR_SIZE, SEEK_SET ) == -1 )
764         {
765             msg_Err( p_this, "Could not lseek to sector %d", i_sector );
766             if( i_type == VCD_TYPE ) free( p_block );
767             return( -1 );
768         }
769
770         if( read( p_vcddev->i_device_handle,
771                   p_block, VCD_SECTOR_SIZE * i_nb ) == -1 )
772         {
773             msg_Err( p_this, "Could not read sector %d", i_sector );
774             if( i_type == VCD_TYPE ) free( p_block );
775             return( -1 );
776         }
777
778 #else
779         for( i = 0; i < i_nb; i++ )
780         {
781             int i_dummy = i_sector + i + 2 * CD_FRAMES;
782
783 #define p_msf ((struct cdrom_msf0 *)(p_block + i * VCD_SECTOR_SIZE))
784             p_msf->minute =   i_dummy / (CD_FRAMES * CD_SECS);
785             p_msf->second = ( i_dummy % (CD_FRAMES * CD_SECS) ) / CD_FRAMES;
786             p_msf->frame =  ( i_dummy % (CD_FRAMES * CD_SECS) ) % CD_FRAMES;
787 #undef p_msf
788
789             if( ioctl( p_vcddev->i_device_handle, CDROMREADRAW,
790                        p_block + i * VCD_SECTOR_SIZE ) == -1 )
791             {
792                 msg_Err( p_this, "could not read block %i from disc",
793                          i_sector );
794
795                 if( i == 0 )
796                 {
797                     if( i_type == VCD_TYPE ) free( p_block );
798                     return( -1 );
799                 }
800                 else break;
801             }
802         }
803 #endif
804     }
805
806     /* For VCDs, we don't want to keep the header and footer of the
807      * sectors read */
808     if( i_type == VCD_TYPE )
809     {
810         for( i = 0; i < i_nb; i++ )
811         {
812             memcpy( p_buffer + i * VCD_DATA_SIZE,
813                     p_block + i * VCD_SECTOR_SIZE + VCD_DATA_START,
814                     VCD_DATA_SIZE );
815         }
816         free( p_block );
817     }
818
819     return( 0 );
820 }
821
822 /****************************************************************************
823  * Private functions
824  ****************************************************************************/
825
826 /****************************************************************************
827  * OpenVCDImage: try to open a vcd image from a .cue file
828  ****************************************************************************/
829 static int OpenVCDImage( vlc_object_t * p_this, const char *psz_dev,
830                          vcddev_t *p_vcddev )
831 {
832     int i_ret = -1;
833     char *p_pos;
834     char *psz_vcdfile = NULL;
835     char *psz_cuefile = NULL;
836     FILE *cuefile     = NULL;
837     char line[1024];
838
839     /* Check if we are dealing with a .cue file */
840     p_pos = strrchr( psz_dev, '.' );
841     if( p_pos && !strcmp( p_pos, ".cue" ) )
842     {
843         /* psz_dev must be the cue file. Let's assume there's a .bin
844          * file with the same filename */
845         if( p_pos )
846         {
847             psz_vcdfile = malloc( p_pos - psz_dev + 5 /* ".bin" */ );
848             strncpy( psz_vcdfile, psz_dev, p_pos - psz_dev );
849             strcpy( psz_vcdfile + (p_pos - psz_dev), ".bin");
850         }
851         else
852         {
853             psz_vcdfile = malloc( strlen(psz_dev) + 5 /* ".bin" */ );
854             sprintf( psz_vcdfile, "%s.bin", psz_dev );
855         }
856         psz_cuefile = strdup( psz_dev );
857     }
858     else
859     {
860         /* psz_dev must be the actual vcd file. Let's assume there's a .cue
861          * file with the same filename */
862         if( p_pos )
863         {
864             psz_cuefile = malloc( p_pos - psz_dev + 5 /* ".cue" */ );
865             strncpy( psz_cuefile, psz_dev, p_pos - psz_dev );
866             strcpy( psz_cuefile + (p_pos - psz_dev), ".cue");
867         }
868         else
869         {
870             psz_cuefile = malloc( strlen(psz_dev) + 5 /* ".cue" */ );
871             sprintf( psz_cuefile, "%s.cue", psz_dev );
872         }
873         /* If we need to look up the .cue file, then we don't have to look for the vcd */
874         psz_vcdfile = strdup( psz_dev );
875     }
876
877     /* Open the cue file and try to parse it */
878     msg_Dbg( p_this,"trying .cue file: %s", psz_cuefile );
879     cuefile = utf8_fopen( psz_cuefile, "rt" );
880     if( cuefile == NULL )
881     {
882         i_ret = -1;
883         msg_Dbg( p_this, "could not find .cue file" );
884         goto error;
885     }
886
887     msg_Dbg( p_this,"using vcd image file: %s", psz_vcdfile );
888     p_vcddev->i_vcdimage_handle = utf8_open( psz_vcdfile,
889                                     O_RDONLY | O_NONBLOCK | O_BINARY, 0666 );
890         
891     if( p_vcddev->i_vcdimage_handle == -1 && 
892         fscanf( cuefile, "FILE %c", line ) &&
893         fgets( line, 1024, cuefile ) )
894     {
895         /* We have a cue file, but no valid vcd file yet */
896         free( psz_vcdfile );
897         p_pos = strchr( line, '"' );
898         if( p_pos )
899         {
900             *p_pos = 0;
901
902             /* Take care of path standardization */
903             if( *line != '/' && ((p_pos = strrchr( psz_cuefile, '/' ))
904                 || (p_pos = strrchr( psz_cuefile, '\\' ) )) )
905             {
906                 psz_vcdfile = malloc( strlen(line) +
907                                       (p_pos - psz_cuefile + 1) + 1 );
908                 strncpy( psz_vcdfile, psz_cuefile, (p_pos - psz_cuefile + 1) );
909                 strcpy( psz_vcdfile + (p_pos - psz_cuefile + 1), line );
910             }
911             else psz_vcdfile = strdup( line );
912         }
913         msg_Dbg( p_this,"using vcd image file: %s", psz_vcdfile );
914         p_vcddev->i_vcdimage_handle = utf8_open( psz_vcdfile,
915                                         O_RDONLY | O_NONBLOCK | O_BINARY, 0666 );
916     }
917
918     if( p_vcddev->i_vcdimage_handle == -1)
919     {
920         i_ret = -1;
921         goto error;
922     }
923     else i_ret = 0;
924
925     /* Try to parse the i_tracks and p_sectors info so we can just forget
926      * about the cuefile */
927     if( i_ret == 0 )
928     {
929         int p_sectors[100];
930         int i_tracks = 0;
931         int i_num;
932         char psz_dummy[10];
933
934         while( fgets( line, 1024, cuefile ) )
935         {
936             /* look for a TRACK line */
937             if( !sscanf( line, "%9s", psz_dummy ) ||
938                 strcmp(psz_dummy, "TRACK") )
939                 continue;
940
941             /* look for an INDEX line */
942             while( fgets( line, 1024, cuefile ) )
943             {
944                 int i_min, i_sec, i_frame;
945
946                 if( (sscanf( line, "%9s %2u %2u:%2u:%2u", psz_dummy, &i_num,
947                             &i_min, &i_sec, &i_frame ) != 5) || (i_num != 1) )
948                     continue;
949
950                 i_tracks++;
951                 p_sectors[i_tracks - 1] = MSF_TO_LBA(i_min, i_sec, i_frame);
952                 msg_Dbg( p_this, "vcd track %i begins at sector:%i",
953                          i_tracks - 1, p_sectors[i_tracks - 1] );
954                 break;
955             }
956         }
957
958         /* fill in the last entry */
959         p_sectors[i_tracks] = lseek(p_vcddev->i_vcdimage_handle, 0, SEEK_END)
960                                 / VCD_SECTOR_SIZE;
961         msg_Dbg( p_this, "vcd track %i, begins at sector:%i",
962                  i_tracks, p_sectors[i_tracks] );
963         p_vcddev->i_tracks = i_tracks;
964         p_vcddev->p_sectors = malloc( (i_tracks + 1) * sizeof(int) );
965         memcpy( p_vcddev->p_sectors, p_sectors, (i_tracks + 1) * sizeof(int) );
966
967     }
968
969 error:
970     if( cuefile ) fclose( cuefile );
971     if( psz_cuefile ) free( psz_cuefile );
972     if( psz_vcdfile ) free( psz_vcdfile );
973
974     return i_ret;
975 }
976
977 /****************************************************************************
978  * CloseVCDImage: closes a vcd image opened by OpenVCDImage
979  ****************************************************************************/
980 static void CloseVCDImage( vlc_object_t * p_this, vcddev_t *p_vcddev )
981 {
982     if( p_vcddev->i_vcdimage_handle != -1 )
983         close( p_vcddev->i_vcdimage_handle );
984     else
985         return;
986
987     if( p_vcddev->p_sectors )
988         free( p_vcddev->p_sectors );
989 }
990
991 #if defined( __APPLE__ )
992 /****************************************************************************
993  * darwin_getTOC: get the TOC
994  ****************************************************************************/
995 static CDTOC *darwin_getTOC( vlc_object_t * p_this, const vcddev_t *p_vcddev )
996 {
997     mach_port_t port;
998     char *psz_devname;
999     kern_return_t ret;
1000     CDTOC *pTOC = NULL;
1001     io_iterator_t iterator;
1002     io_registry_entry_t service;
1003     CFMutableDictionaryRef properties;
1004     CFDataRef data;
1005
1006     /* get the device name */
1007     if( ( psz_devname = strrchr( p_vcddev->psz_dev, '/') ) != NULL )
1008         ++psz_devname;
1009     else
1010         psz_devname = p_vcddev->psz_dev;
1011
1012     /* unraw the device name */
1013     if( *psz_devname == 'r' )
1014         ++psz_devname;
1015
1016     /* get port for IOKit communication */
1017     if( ( ret = IOMasterPort( MACH_PORT_NULL, &port ) ) != KERN_SUCCESS )
1018     {
1019         msg_Err( p_this, "IOMasterPort: 0x%08x", ret );
1020         return( NULL );
1021     }
1022
1023     /* get service iterator for the device */
1024     if( ( ret = IOServiceGetMatchingServices( 
1025                     port, IOBSDNameMatching( port, 0, psz_devname ),
1026                     &iterator ) ) != KERN_SUCCESS )
1027     {
1028         msg_Err( p_this, "IOServiceGetMatchingServices: 0x%08x", ret );
1029         return( NULL );
1030     }
1031
1032     /* first service */
1033     service = IOIteratorNext( iterator );
1034     IOObjectRelease( iterator );
1035
1036     /* search for kIOCDMediaClass */ 
1037     while( service && !IOObjectConformsTo( service, kIOCDMediaClass ) )
1038     {
1039         if( ( ret = IORegistryEntryGetParentIterator( service, 
1040                         kIOServicePlane, &iterator ) ) != KERN_SUCCESS )
1041         {
1042             msg_Err( p_this, "IORegistryEntryGetParentIterator: 0x%08x", ret );
1043             IOObjectRelease( service );
1044             return( NULL );
1045         }
1046
1047         IOObjectRelease( service );
1048         service = IOIteratorNext( iterator );
1049         IOObjectRelease( iterator );
1050     }
1051
1052     if( service == NULL )
1053     {
1054         msg_Err( p_this, "search for kIOCDMediaClass came up empty" );
1055         return( NULL );
1056     }
1057
1058     /* create a CF dictionary containing the TOC */
1059     if( ( ret = IORegistryEntryCreateCFProperties( service, &properties,
1060                     kCFAllocatorDefault, kNilOptions ) ) != KERN_SUCCESS )
1061     {
1062         msg_Err( p_this, "IORegistryEntryCreateCFProperties: 0x%08x", ret );
1063         IOObjectRelease( service );
1064         return( NULL );
1065     }
1066
1067     /* get the TOC from the dictionary */
1068     if( ( data = (CFDataRef) CFDictionaryGetValue( properties,
1069                                     CFSTR(kIOCDMediaTOCKey) ) ) != NULL )
1070     {
1071         CFRange range;
1072         CFIndex buf_len;
1073
1074         buf_len = CFDataGetLength( data ) + 1;
1075         range = CFRangeMake( 0, buf_len );
1076
1077         if( ( pTOC = (CDTOC *)malloc( buf_len ) ) != NULL )
1078         {
1079             CFDataGetBytes( data, range, (u_char *)pTOC );
1080         }
1081     }
1082     else
1083     {
1084         msg_Err( p_this, "CFDictionaryGetValue failed" );
1085     }
1086
1087     CFRelease( properties );
1088     IOObjectRelease( service ); 
1089
1090     return( pTOC ); 
1091 }
1092
1093 /****************************************************************************
1094  * darwin_getNumberOfTracks: get number of tracks in TOC 
1095  ****************************************************************************/
1096 static int darwin_getNumberOfTracks( CDTOC *pTOC, int i_descriptors )
1097 {
1098     u_char track;
1099     int i, i_tracks = 0; 
1100     CDTOCDescriptor *pTrackDescriptors = NULL;
1101
1102     pTrackDescriptors = (CDTOCDescriptor *)pTOC->descriptors;
1103
1104     for( i = i_descriptors; i > 0; i-- )
1105     {
1106         track = pTrackDescriptors[i].point;
1107
1108         if( track > CD_MAX_TRACK_NO || track < CD_MIN_TRACK_NO )
1109             continue;
1110
1111         i_tracks++; 
1112     }
1113
1114     return( i_tracks );
1115 }
1116 #endif /* __APPLE__ */
1117
1118 #if defined( WIN32 )
1119 /*****************************************************************************
1120  * win32_vcd_open: open vcd drive
1121  *****************************************************************************
1122  * Load and use aspi if it is available, otherwise use IOCTLs on WinNT/2K/XP.
1123  *****************************************************************************/
1124 static int win32_vcd_open( vlc_object_t * p_this, const char *psz_dev,
1125                            vcddev_t *p_vcddev )
1126 {
1127     /* Initializations */
1128     p_vcddev->h_device_handle = NULL;
1129     p_vcddev->i_sid = 0;
1130     p_vcddev->hASPI = 0;
1131     p_vcddev->lpSendCommand = 0;
1132
1133     if( WIN_NT )
1134     {
1135         char psz_win32_drive[7];
1136
1137         msg_Dbg( p_this, "using winNT/2K/XP ioctl layer" );
1138
1139         sprintf( psz_win32_drive, "\\\\.\\%c:", psz_dev[0] );
1140
1141         p_vcddev->h_device_handle = CreateFile( psz_win32_drive, GENERIC_READ,
1142                                             FILE_SHARE_READ | FILE_SHARE_WRITE,
1143                                             NULL, OPEN_EXISTING,
1144                                             FILE_FLAG_NO_BUFFERING |
1145                                             FILE_FLAG_RANDOM_ACCESS, NULL );
1146         return (p_vcddev->h_device_handle == NULL) ? -1 : 0;
1147     }
1148     else
1149     {
1150         HMODULE hASPI = NULL;
1151         long (*lpGetSupport)( void ) = NULL;
1152         long (*lpSendCommand)( void* ) = NULL;
1153         DWORD dwSupportInfo;
1154         int i, j, i_hostadapters;
1155         char c_drive = psz_dev[0];
1156
1157         hASPI = LoadLibrary( "wnaspi32.dll" );
1158         if( hASPI != NULL )
1159         {
1160             (FARPROC) lpGetSupport = GetProcAddress( hASPI,
1161                                                      "GetASPI32SupportInfo" );
1162             (FARPROC) lpSendCommand = GetProcAddress( hASPI,
1163                                                       "SendASPI32Command" );
1164         }
1165
1166         if( hASPI == NULL || lpGetSupport == NULL || lpSendCommand == NULL )
1167         {
1168             msg_Dbg( p_this,
1169                      "unable to load aspi or get aspi function pointers" );
1170             if( hASPI ) FreeLibrary( hASPI );
1171             return -1;
1172         }
1173
1174         /* ASPI support seems to be there */
1175
1176         dwSupportInfo = lpGetSupport();
1177
1178         if( HIBYTE( LOWORD ( dwSupportInfo ) ) == SS_NO_ADAPTERS )
1179         {
1180             msg_Dbg( p_this, "no host adapters found (aspi)" );
1181             FreeLibrary( hASPI );
1182             return -1;
1183         }
1184
1185         if( HIBYTE( LOWORD ( dwSupportInfo ) ) != SS_COMP )
1186         {
1187             msg_Dbg( p_this, "unable to initalize aspi layer" );
1188             FreeLibrary( hASPI );
1189             return -1;
1190         }
1191
1192         i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) );
1193         if( i_hostadapters == 0 )
1194         {
1195             FreeLibrary( hASPI );
1196             return -1;
1197         }
1198
1199         c_drive = c_drive > 'Z' ? c_drive - 'a' : c_drive - 'A';
1200
1201         for( i = 0; i < i_hostadapters; i++ )
1202         {
1203           for( j = 0; j < 15; j++ )
1204           {
1205               struct SRB_GetDiskInfo srbDiskInfo;
1206
1207               srbDiskInfo.SRB_Cmd         = SC_GET_DISK_INFO;
1208               srbDiskInfo.SRB_HaId        = i;
1209               srbDiskInfo.SRB_Flags       = 0;
1210               srbDiskInfo.SRB_Hdr_Rsvd    = 0;
1211               srbDiskInfo.SRB_Target      = j;
1212               srbDiskInfo.SRB_Lun         = 0;
1213
1214               lpSendCommand( (void*) &srbDiskInfo );
1215
1216               if( (srbDiskInfo.SRB_Status == SS_COMP) &&
1217                   (srbDiskInfo.SRB_Int13HDriveInfo == c_drive) )
1218               {
1219                   /* Make sure this is a cdrom device */
1220                   struct SRB_GDEVBlock   srbGDEVBlock;
1221
1222                   memset( &srbGDEVBlock, 0, sizeof(struct SRB_GDEVBlock) );
1223                   srbGDEVBlock.SRB_Cmd    = SC_GET_DEV_TYPE;
1224                   srbGDEVBlock.SRB_HaId   = i;
1225                   srbGDEVBlock.SRB_Target = j;
1226
1227                   lpSendCommand( (void*) &srbGDEVBlock );
1228
1229                   if( ( srbGDEVBlock.SRB_Status == SS_COMP ) &&
1230                       ( srbGDEVBlock.SRB_DeviceType == DTYPE_CDROM ) )
1231                   {
1232                       p_vcddev->i_sid = MAKEWORD( i, j );
1233                       p_vcddev->hASPI = (long)hASPI;
1234                       p_vcddev->lpSendCommand = lpSendCommand;
1235                       msg_Dbg( p_this, "using aspi layer" );
1236
1237                       return 0;
1238                   }
1239                   else
1240                   {
1241                       FreeLibrary( hASPI );
1242                       msg_Dbg( p_this, "%c: is not a cdrom drive",
1243                                psz_dev[0] );
1244                       return -1;
1245                   }
1246               }
1247           }
1248         }
1249
1250         FreeLibrary( hASPI );
1251         msg_Dbg( p_this, "unable to get haid and target (aspi)" );
1252
1253     }
1254
1255     return -1;
1256 }
1257
1258 #endif /* WIN32 */