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