1 /****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 1998-2001 VLC authors and VideoLAN
7 * Authors: Johan Bilien <jobi@via.ecp.fr>
8 * Gildas Bazin <gbazin@netcourrier.com>
19 * This program is free software; you can redistribute it and/or modify it
20 * under the terms of the GNU Lesser General Public License as published by
21 * the Free Software Foundation; either version 2.1 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU Lesser General Public License for more details.
29 * You should have received a copy of the GNU Lesser General Public License
30 * along with this program; if not, write to the Free Software Foundation,
31 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
32 *****************************************************************************/
34 /*****************************************************************************
36 *****************************************************************************/
42 # define INCL_DOSDEVIOCTL
45 #include <sys/types.h>
50 #ifdef HAVE_ARPA_INET_H
51 #include <arpa/inet.h>
54 #include <vlc_common.h>
55 #include <vlc_access.h>
56 #include <vlc_charset.h>
60 #if defined( SYS_BSDI )
62 #elif defined ( __APPLE__ )
63 # include <CoreFoundation/CFBase.h>
64 # include <IOKit/IOKitLib.h>
65 # include <IOKit/storage/IOCDTypes.h>
66 # include <IOKit/storage/IOCDMedia.h>
67 # include <IOKit/storage/IOCDMediaBSDClient.h>
68 #elif defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
69 # include <inttypes.h>
70 # include <sys/cdio.h>
71 # include <sys/scsiio.h>
72 #elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H )
73 # include <sys/cdio.h>
74 # include <sys/cdrio.h>
75 #elif defined( _WIN32 )
77 # include <winioctl.h>
78 #elif defined (__linux__)
79 # include <sys/ioctl.h>
80 # include <linux/cdrom.h>
81 #elif defined( __OS2__ )
85 /*****************************************************************************
86 * vlc_DosDevIOCtl: high memory safe wrapper for DosDevIOCtl
87 *****************************************************************************
88 * Unfortunately, DosDevIOCtl() is not high memory safe API, and is not
89 * covered by os2safe.h. So define a wrapper function for it here.
90 *****************************************************************************/
92 static APIRET
vlc_DosDevIOCtl( HFILE hdevice
, ULONG category
, ULONG function
,
93 PVOID pParams
, ULONG cbParamLenMax
,
94 PULONG pcbParamLen
, PVOID pData
,
95 ULONG cbDataLenMax
, PULONG pcbDataLen
)
97 PVOID pParamsLow
= NULL
;
98 PVOID pDataLow
= NULL
;
104 rc
= DosAllocMem( &pParamsLow
, cbParamLenMax
, fALLOC
);
108 rc
= DosAllocMem( &pDataLow
, cbDataLenMax
, fALLOC
);
112 memcpy( pParamsLow
, pParams
, cbParamLenMax
);
113 memcpy( pDataLow
, pData
, cbDataLenMax
);
115 cbParamLenLow
= *pcbParamLen
;
116 cbDataLenLow
= *pcbDataLen
;
118 rc
= DosDevIOCtl( hdevice
, category
, function
, pParamsLow
,
119 cbParamLenMax
, &cbParamLenLow
, pDataLow
, cbDataLenMax
,
124 memcpy( pParams
, pParamsLow
, cbParamLenMax
);
125 memcpy( pData
, pDataLow
, cbDataLenMax
);
127 *pcbParamLen
= cbParamLenLow
;
128 *pcbDataLen
= cbDataLenLow
;
132 DosFreeMem( pParamsLow
);
133 DosFreeMem( pDataLow
);
138 # define DosDevIOCtl vlc_DosDevIOCtl
143 #include "cdrom_internals.h"
146 /*****************************************************************************
147 * ioctl_Open: Opens a VCD device or file and returns an opaque handle
148 *****************************************************************************/
149 vcddev_t
*ioctl_Open( vlc_object_t
*p_this
, const char *psz_dev
)
154 #if !defined( _WIN32 ) && !defined( __OS2__ )
155 struct stat fileinfo
;
158 if( !psz_dev
) return NULL
;
161 * Initialize structure with default values
163 p_vcddev
= malloc( sizeof(*p_vcddev
) );
164 if( p_vcddev
== NULL
)
166 p_vcddev
->i_vcdimage_handle
= -1;
167 p_vcddev
->psz_dev
= NULL
;
171 * Check if we are dealing with a device or a file (vcd image)
173 #if defined( _WIN32 ) || defined( __OS2__ )
174 if( (strlen( psz_dev
) == 2 && psz_dev
[1] == ':') )
180 if( vlc_stat( psz_dev
, &fileinfo
) < 0 )
186 /* Check if this is a block/char device */
187 if( S_ISBLK( fileinfo
.st_mode
) || S_ISCHR( fileinfo
.st_mode
) )
193 i_ret
= OpenVCDImage( p_this
, psz_dev
, p_vcddev
);
198 * open the vcd device
202 i_ret
= win32_vcd_open( p_this
, psz_dev
, p_vcddev
);
203 #elif defined( __OS2__ )
204 i_ret
= os2_vcd_open( p_this
, psz_dev
, p_vcddev
);
206 p_vcddev
->i_device_handle
= -1;
207 p_vcddev
->i_device_handle
= vlc_open( psz_dev
, O_RDONLY
| O_NONBLOCK
);
208 i_ret
= (p_vcddev
->i_device_handle
== -1) ? -1 : 0;
214 p_vcddev
->psz_dev
= (char *)strdup( psz_dev
);
225 /*****************************************************************************
226 * ioctl_Close: Closes an already opened VCD device or file.
227 *****************************************************************************/
228 void ioctl_Close( vlc_object_t
* p_this
, vcddev_t
*p_vcddev
)
230 free( p_vcddev
->psz_dev
);
232 if( p_vcddev
->i_vcdimage_handle
!= -1 )
238 CloseVCDImage( p_this
, p_vcddev
);
247 if( p_vcddev
->h_device_handle
)
248 CloseHandle( p_vcddev
->h_device_handle
);
249 #elif defined( __OS2__ )
251 DosClose( p_vcddev
->hcd
);
253 if( p_vcddev
->i_device_handle
!= -1 )
254 vlc_close( p_vcddev
->i_device_handle
);
259 /*****************************************************************************
260 * ioctl_GetTracksMap: Read the Table of Content, fill in the pp_sectors map
261 * if pp_sectors is not null and return the number of
263 *****************************************************************************/
264 int ioctl_GetTracksMap( vlc_object_t
*p_this
, const vcddev_t
*p_vcddev
,
269 if( p_vcddev
->i_vcdimage_handle
!= -1 )
275 i_tracks
= p_vcddev
->i_tracks
;
279 *pp_sectors
= calloc( i_tracks
+ 1, sizeof(**pp_sectors
) );
280 if( *pp_sectors
== NULL
)
282 memcpy( *pp_sectors
, p_vcddev
->p_sectors
,
283 (i_tracks
+ 1) * sizeof(**pp_sectors
) );
295 #if defined( __APPLE__ )
300 if( ( pTOC
= darwin_getTOC( p_this
, p_vcddev
) ) == NULL
)
302 msg_Err( p_this
, "failed to get the TOC" );
306 i_descriptors
= CDTOCGetDescriptorCount( pTOC
);
307 i_tracks
= darwin_getNumberOfTracks( pTOC
, i_descriptors
);
311 int i
, i_leadout
= -1;
312 CDTOCDescriptor
*pTrackDescriptors
;
315 *pp_sectors
= calloc( i_tracks
+ 1, sizeof(**pp_sectors
) );
316 if( *pp_sectors
== NULL
)
318 darwin_freeTOC( pTOC
);
322 pTrackDescriptors
= pTOC
->descriptors
;
324 for( i_tracks
= 0, i
= 0; i
< i_descriptors
; i
++ )
326 track
= pTrackDescriptors
[i
].point
;
331 if( track
> CD_MAX_TRACK_NO
|| track
< CD_MIN_TRACK_NO
)
334 (*pp_sectors
)[i_tracks
++] =
335 CDConvertMSFToLBA( pTrackDescriptors
[i
].p
);
338 if( i_leadout
== -1 )
340 msg_Err( p_this
, "leadout not found" );
342 darwin_freeTOC( pTOC
);
346 /* set leadout sector */
347 (*pp_sectors
)[i_tracks
] =
348 CDConvertMSFToLBA( pTrackDescriptors
[i_leadout
].p
);
351 darwin_freeTOC( pTOC
);
353 #elif defined( _WIN32 )
354 DWORD dwBytesReturned
;
357 if( DeviceIoControl( p_vcddev
->h_device_handle
, IOCTL_CDROM_READ_TOC
,
358 NULL
, 0, &cdrom_toc
, sizeof(CDROM_TOC
),
359 &dwBytesReturned
, NULL
) == 0 )
361 msg_Err( p_this
, "could not read TOCHDR" );
365 i_tracks
= cdrom_toc
.LastTrack
- cdrom_toc
.FirstTrack
+ 1;
369 *pp_sectors
= calloc( i_tracks
+ 1, sizeof(**pp_sectors
) );
370 if( *pp_sectors
== NULL
)
373 for( int i
= 0 ; i
<= i_tracks
; i
++ )
375 (*pp_sectors
)[ i
] = MSF_TO_LBA2(
376 cdrom_toc
.TrackData
[i
].Address
[1],
377 cdrom_toc
.TrackData
[i
].Address
[2],
378 cdrom_toc
.TrackData
[i
].Address
[3] );
379 msg_Dbg( p_this
, "p_sectors: %i, %i", i
, (*pp_sectors
)[i
]);
383 #elif defined( __OS2__ )
384 cdrom_get_tochdr_t get_tochdr
= {{'C', 'D', '0', '1'}};
385 cdrom_tochdr_t tochdr
;
391 rc
= DosDevIOCtl( p_vcddev
->hcd
, IOCTL_CDROMAUDIO
,
392 CDROMAUDIO_GETAUDIODISK
,
393 &get_tochdr
, sizeof( get_tochdr
), ¶m_len
,
394 &tochdr
, sizeof( tochdr
), &data_len
);
397 msg_Err( p_this
, "could not read TOCHDR" );
401 i_tracks
= tochdr
.last_track
- tochdr
.first_track
+ 1;
405 cdrom_get_track_t get_track
= {{'C', 'D', '0', '1'}, };
409 *pp_sectors
= calloc( i_tracks
+ 1, sizeof(**pp_sectors
) );
410 if( *pp_sectors
== NULL
)
413 for( i
= 0 ; i
< i_tracks
; i
++ )
415 get_track
.track
= tochdr
.first_track
+ i
;
416 rc
= DosDevIOCtl( p_vcddev
->hcd
, IOCTL_CDROMAUDIO
,
417 CDROMAUDIO_GETAUDIOTRACK
,
418 &get_track
, sizeof(get_track
), ¶m_len
,
419 &track
, sizeof(track
), &data_len
);
422 msg_Err( p_this
, "could not read %d track",
427 (*pp_sectors
)[ i
] = MSF_TO_LBA2(
431 msg_Dbg( p_this
, "p_sectors: %i, %i", i
, (*pp_sectors
)[i
]);
434 /* for lead-out track */
435 (*pp_sectors
)[ i
] = MSF_TO_LBA2(
436 tochdr
.lead_out
.minute
,
437 tochdr
.lead_out
.second
,
438 tochdr
.lead_out
.frame
);
439 msg_Dbg( p_this
, "p_sectors: %i, %i", i
, (*pp_sectors
)[i
]);
442 #elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H ) \
443 || defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
444 struct ioc_toc_header tochdr
;
445 struct ioc_read_toc_entry toc_entries
;
447 if( ioctl( p_vcddev
->i_device_handle
, CDIOREADTOCHEADER
, &tochdr
)
450 msg_Err( p_this
, "could not read TOCHDR" );
454 i_tracks
= tochdr
.ending_track
- tochdr
.starting_track
+ 1;
458 *pp_sectors
= calloc( i_tracks
+ 1, sizeof(**pp_sectors
) );
459 if( *pp_sectors
== NULL
)
462 toc_entries
.address_format
= CD_LBA_FORMAT
;
463 toc_entries
.starting_track
= 0;
464 toc_entries
.data_len
= ( i_tracks
+ 1 ) *
465 sizeof( struct cd_toc_entry
);
466 toc_entries
.data
= (struct cd_toc_entry
*)
467 malloc( toc_entries
.data_len
);
468 if( toc_entries
.data
== NULL
)
475 if( ioctl( p_vcddev
->i_device_handle
, CDIOREADTOCENTRYS
,
476 &toc_entries
) == -1 )
478 msg_Err( p_this
, "could not read the TOC" );
480 free( toc_entries
.data
);
484 /* Fill the p_sectors structure with the track/sector matches */
485 for( int i
= 0 ; i
<= i_tracks
; i
++ )
487 #if defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
488 /* FIXME: is this ok? */
489 (*pp_sectors
)[ i
] = toc_entries
.data
[i
].addr
.lba
;
491 (*pp_sectors
)[ i
] = ntohl( toc_entries
.data
[i
].addr
.lba
);
496 struct cdrom_tochdr tochdr
;
497 struct cdrom_tocentry tocent
;
499 /* First we read the TOC header */
500 if( ioctl( p_vcddev
->i_device_handle
, CDROMREADTOCHDR
, &tochdr
)
503 msg_Err( p_this
, "could not read TOCHDR" );
507 i_tracks
= tochdr
.cdth_trk1
- tochdr
.cdth_trk0
+ 1;
511 *pp_sectors
= calloc( i_tracks
+ 1, sizeof(**pp_sectors
) );
512 if( *pp_sectors
== NULL
)
515 /* Fill the p_sectors structure with the track/sector matches */
516 for( int i
= 0 ; i
<= i_tracks
; i
++ )
518 tocent
.cdte_format
= CDROM_LBA
;
520 ( i
== i_tracks
) ? CDROM_LEADOUT
: tochdr
.cdth_trk0
+ i
;
522 if( ioctl( p_vcddev
->i_device_handle
, CDROMREADTOCENTRY
,
525 msg_Err( p_this
, "could not read TOCENTRY" );
530 (*pp_sectors
)[ i
] = tocent
.cdte_addr
.lba
;
539 /****************************************************************************
540 * ioctl_ReadSector: Read VCD or CDDA sectors
541 ****************************************************************************/
542 int ioctl_ReadSectors( vlc_object_t
*p_this
, const vcddev_t
*p_vcddev
,
543 int i_sector
, uint8_t *p_buffer
, int i_nb
, int i_type
)
547 if( i_type
== VCD_TYPE
)
548 p_block
= vlc_alloc( i_nb
, VCD_SECTOR_SIZE
);
552 if( p_vcddev
->i_vcdimage_handle
!= -1 )
557 if( lseek( p_vcddev
->i_vcdimage_handle
, i_sector
* VCD_SECTOR_SIZE
,
560 msg_Err( p_this
, "Could not lseek to sector %d", i_sector
);
564 if( read( p_vcddev
->i_vcdimage_handle
, p_block
, VCD_SECTOR_SIZE
* i_nb
)
567 msg_Err( p_this
, "Could not read sector %d", i_sector
);
579 #if defined( __APPLE__ )
580 dk_cd_read_t cd_read
;
582 memset( &cd_read
, 0, sizeof(cd_read
) );
584 cd_read
.offset
= i_sector
* VCD_SECTOR_SIZE
;
585 cd_read
.sectorArea
= kCDSectorAreaSync
| kCDSectorAreaHeader
|
586 kCDSectorAreaSubHeader
| kCDSectorAreaUser
|
587 kCDSectorAreaAuxiliary
;
588 cd_read
.sectorType
= kCDSectorTypeUnknown
;
590 cd_read
.buffer
= p_block
;
591 cd_read
.bufferLength
= VCD_SECTOR_SIZE
* i_nb
;
593 if( ioctl( p_vcddev
->i_device_handle
, DKIOCCDREAD
, &cd_read
) == -1 )
595 msg_Err( p_this
, "could not read block %d", i_sector
);
599 #elif defined( _WIN32 )
600 DWORD dwBytesReturned
;
601 RAW_READ_INFO cdrom_raw
;
603 /* Initialize CDROM_RAW_READ structure */
604 cdrom_raw
.DiskOffset
.QuadPart
= CD_SECTOR_SIZE
* i_sector
;
605 cdrom_raw
.SectorCount
= i_nb
;
606 cdrom_raw
.TrackMode
= i_type
== VCD_TYPE
? XAForm2
: CDDA
;
608 if( DeviceIoControl( p_vcddev
->h_device_handle
, IOCTL_CDROM_RAW_READ
,
609 &cdrom_raw
, sizeof(RAW_READ_INFO
), p_block
,
610 VCD_SECTOR_SIZE
* i_nb
, &dwBytesReturned
,
613 if( i_type
== VCD_TYPE
)
615 /* Retry in YellowMode2 */
616 cdrom_raw
.TrackMode
= YellowMode2
;
617 if( DeviceIoControl( p_vcddev
->h_device_handle
,
618 IOCTL_CDROM_RAW_READ
, &cdrom_raw
,
619 sizeof(RAW_READ_INFO
), p_block
,
620 VCD_SECTOR_SIZE
* i_nb
, &dwBytesReturned
,
627 #elif defined( __OS2__ )
628 cdrom_readlong_t readlong
= {{'C', 'D', '0', '1'}, };
634 readlong
.addr_mode
= 0; /* LBA mode */
635 readlong
.sectors
= i_nb
;
636 readlong
.start
= i_sector
;
638 rc
= DosDevIOCtl( p_vcddev
->hcd
, IOCTL_CDROMDISK
, CDROMDISK_READLONG
,
639 &readlong
, sizeof( readlong
), ¶m_len
,
640 p_block
, VCD_SECTOR_SIZE
* i_nb
, &data_len
);
643 msg_Err( p_this
, "could not read block %d", i_sector
);
647 #elif defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
651 memset( &sc
, 0, sizeof(sc
) );
653 sc
.cmd
[1] = i_type
== VCD_TYPE
? SECTOR_TYPE_MODE2_FORM2
:
655 sc
.cmd
[2] = (i_sector
>> 24) & 0xff;
656 sc
.cmd
[3] = (i_sector
>> 16) & 0xff;
657 sc
.cmd
[4] = (i_sector
>> 8) & 0xff;
658 sc
.cmd
[5] = (i_sector
>> 0) & 0xff;
659 sc
.cmd
[6] = (i_nb
>> 16) & 0xff;
660 sc
.cmd
[7] = (i_nb
>> 8) & 0xff;
661 sc
.cmd
[8] = (i_nb
) & 0xff;
662 sc
.cmd
[9] = i_type
== VCD_TYPE
? READ_CD_RAW_MODE2
: READ_CD_USERDATA
;
663 sc
.cmd
[10] = 0; /* sub channel */
665 sc
.databuf
= (caddr_t
)p_block
;
666 sc
.datalen
= VCD_SECTOR_SIZE
* i_nb
;
667 sc
.senselen
= sizeof( sc
.sense
);
668 sc
.flags
= SCCMD_READ
;
671 i_ret
= ioctl( p_vcddev
->i_device_handle
, SCIOCCOMMAND
, &sc
);
674 msg_Err( p_this
, "SCIOCCOMMAND failed" );
677 if( sc
.retsts
|| sc
.error
)
679 msg_Err( p_this
, "SCSI command failed: status %d error %d",
680 sc
.retsts
, sc
.error
);
684 #elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H )
685 int i_size
= VCD_SECTOR_SIZE
;
687 if( ioctl( p_vcddev
->i_device_handle
, CDRIOCSETBLOCKSIZE
, &i_size
)
690 msg_Err( p_this
, "Could not set block size" );
694 if( lseek( p_vcddev
->i_device_handle
,
695 i_sector
* VCD_SECTOR_SIZE
, SEEK_SET
) == -1 )
697 msg_Err( p_this
, "Could not lseek to sector %d", i_sector
);
701 if( read( p_vcddev
->i_device_handle
,
702 p_block
, VCD_SECTOR_SIZE
* i_nb
) == -1 )
704 msg_Err( p_this
, "Could not read sector %d", i_sector
);
709 for( int i
= 0; i
< i_nb
; i
++ )
711 int i_dummy
= i_sector
+ i
+ 2 * CD_FRAMES
;
713 #define p_msf ((struct cdrom_msf0 *)(p_block + i * VCD_SECTOR_SIZE))
714 p_msf
->minute
= i_dummy
/ (CD_FRAMES
* CD_SECS
);
715 p_msf
->second
= ( i_dummy
% (CD_FRAMES
* CD_SECS
) ) / CD_FRAMES
;
716 p_msf
->frame
= ( i_dummy
% (CD_FRAMES
* CD_SECS
) ) % CD_FRAMES
;
719 if( ioctl( p_vcddev
->i_device_handle
, CDROMREADRAW
,
720 p_block
+ i
* VCD_SECTOR_SIZE
) == -1 )
722 msg_Err( p_this
, "could not read block %i from disc",
734 /* For VCDs, we don't want to keep the header and footer of the
736 if( i_type
== VCD_TYPE
)
738 for( int i
= 0; i
< i_nb
; i
++ )
740 memcpy( p_buffer
+ i
* VCD_DATA_SIZE
,
741 p_block
+ i
* VCD_SECTOR_SIZE
+ VCD_DATA_START
,
750 if( i_type
== VCD_TYPE
)
755 /****************************************************************************
757 ****************************************************************************/
759 /****************************************************************************
760 * OpenVCDImage: try to open a vcd image from a .cue file
761 ****************************************************************************/
762 static int OpenVCDImage( vlc_object_t
* p_this
, const char *psz_dev
,
767 char *psz_vcdfile
= NULL
;
768 char *psz_cuefile
= NULL
;
769 FILE *cuefile
= NULL
;
770 int *p_sectors
= NULL
;
772 bool b_found
= false;
774 /* Check if we are dealing with a .cue file */
775 p_pos
= strrchr( psz_dev
, '.' );
776 if( p_pos
&& !strcmp( p_pos
, ".cue" ) )
778 /* psz_dev must be the cue file. Let's assume there's a .bin
779 * file with the same filename */
780 if( asprintf( &psz_vcdfile
, "%.*s.bin", (int)(p_pos
- psz_dev
),
783 psz_cuefile
= strdup( psz_dev
);
788 /* psz_dev must be the actual vcd file. Let's assume there's a .cue
789 * file with the same filename */
790 if( asprintf( &psz_cuefile
, "%.*s.cue", (int)(p_pos
- psz_dev
),
793 psz_vcdfile
= strdup( psz_dev
);
797 if( asprintf( &psz_cuefile
, "%s.cue", psz_dev
) == -1 )
799 /* If we need to look up the .cue file, then we don't have to look
801 psz_vcdfile
= strdup( psz_dev
);
804 if( psz_cuefile
== NULL
|| psz_vcdfile
== NULL
)
807 /* Open the cue file and try to parse it */
808 msg_Dbg( p_this
,"trying .cue file: %s", psz_cuefile
);
809 cuefile
= vlc_fopen( psz_cuefile
, "rt" );
810 if( cuefile
== NULL
)
812 msg_Dbg( p_this
, "could not find .cue file" );
816 msg_Dbg( p_this
,"guessing vcd image file: %s", psz_vcdfile
);
817 p_vcddev
->i_vcdimage_handle
= vlc_open( psz_vcdfile
,
818 O_RDONLY
| O_NONBLOCK
| O_BINARY
);
820 while( fgets( line
, 1024, cuefile
) && !b_found
)
822 /* We have a cue file, but no valid vcd file yet */
825 int i_temp
= sscanf( line
, "FILE \"%1023[^\"]\" %15s", filename
, type
);
829 msg_Dbg( p_this
, "the cue file says the data file is %s", type
);
830 if( strcasecmp( type
, "BINARY" ) )
831 goto error
; /* Error if not binary, otherwise treat as case 1 */
833 if( p_vcddev
->i_vcdimage_handle
== -1 )
835 msg_Dbg( p_this
, "we could not find the data file, but we found a new path" );
837 if( *filename
!= '/' && ((p_pos
= strrchr( psz_cuefile
, '/' ))
838 || (p_pos
= strrchr( psz_cuefile
, '\\' ) )) )
840 psz_vcdfile
= malloc( strlen(filename
) +
841 (p_pos
- psz_cuefile
+ 1) + 1 );
842 strncpy( psz_vcdfile
, psz_cuefile
, (p_pos
- psz_cuefile
+ 1) );
843 strcpy( psz_vcdfile
+ (p_pos
- psz_cuefile
+ 1), filename
);
844 } else psz_vcdfile
= strdup( filename
);
845 msg_Dbg( p_this
,"using vcd image file: %s", psz_vcdfile
);
846 p_vcddev
->i_vcdimage_handle
= vlc_open( psz_vcdfile
,
847 O_RDONLY
| O_NONBLOCK
| O_BINARY
);
855 if( p_vcddev
->i_vcdimage_handle
== -1)
858 /* Try to parse the i_tracks and p_sectors info so we can just forget
859 * about the cuefile */
862 while( fgets( line
, 1024, cuefile
) && i_tracks
< INT_MAX
-1 )
864 /* look for a TRACK line */
866 if( !sscanf( line
, "%9s", psz_dummy
) || strcmp(psz_dummy
, "TRACK") )
869 /* look for an INDEX line */
870 while( fgets( line
, 1024, cuefile
) )
872 int i_num
, i_min
, i_sec
, i_frame
;
874 if( (sscanf( line
, "%*9s %2u %2u:%2u:%2u", &i_num
,
875 &i_min
, &i_sec
, &i_frame
) != 4) || (i_num
!= 1) )
878 int *buf
= realloc (p_sectors
, (i_tracks
+ 1) * sizeof (*buf
));
882 p_sectors
[i_tracks
] = MSF_TO_LBA(i_min
, i_sec
, i_frame
);
883 msg_Dbg( p_this
, "vcd track %i begins at sector:%i",
884 (int)i_tracks
, (int)p_sectors
[i_tracks
] );
890 /* fill in the last entry */
891 int *buf
= realloc (p_sectors
, (i_tracks
+ 1) * sizeof (*buf
));
895 p_sectors
[i_tracks
] = lseek(p_vcddev
->i_vcdimage_handle
, 0, SEEK_END
)
897 msg_Dbg( p_this
, "vcd track %i, begins at sector:%i",
898 (int)i_tracks
, (int)p_sectors
[i_tracks
] );
899 p_vcddev
->i_tracks
= ++i_tracks
;
900 p_vcddev
->p_sectors
= p_sectors
;
905 if( cuefile
) fclose( cuefile
);
913 /****************************************************************************
914 * CloseVCDImage: closes a vcd image opened by OpenVCDImage
915 ****************************************************************************/
916 static void CloseVCDImage( vlc_object_t
* p_this
, vcddev_t
*p_vcddev
)
918 VLC_UNUSED( p_this
);
919 if( p_vcddev
->i_vcdimage_handle
!= -1 )
920 vlc_close( p_vcddev
->i_vcdimage_handle
);
924 free( p_vcddev
->p_sectors
);
927 #if defined( __APPLE__ )
928 /****************************************************************************
929 * darwin_getTOC: get the TOC
930 ****************************************************************************/
931 static CDTOC
*darwin_getTOC( vlc_object_t
* p_this
, const vcddev_t
*p_vcddev
)
937 io_iterator_t iterator
;
938 io_registry_entry_t service
;
939 CFMutableDictionaryRef properties
;
942 /* get the device name */
943 if( ( psz_devname
= strrchr( p_vcddev
->psz_dev
, '/') ) != NULL
)
946 psz_devname
= p_vcddev
->psz_dev
;
948 /* unraw the device name */
949 if( *psz_devname
== 'r' )
952 /* get port for IOKit communication */
953 if( ( ret
= IOMasterPort( MACH_PORT_NULL
, &port
) ) != KERN_SUCCESS
)
955 msg_Err( p_this
, "IOMasterPort: 0x%08x", ret
);
959 /* get service iterator for the device */
960 if( ( ret
= IOServiceGetMatchingServices(
961 port
, IOBSDNameMatching( port
, 0, psz_devname
),
962 &iterator
) ) != KERN_SUCCESS
)
964 msg_Err( p_this
, "IOServiceGetMatchingServices: 0x%08x", ret
);
969 service
= IOIteratorNext( iterator
);
970 IOObjectRelease( iterator
);
972 /* search for kIOCDMediaClass */
973 while( service
&& !IOObjectConformsTo( service
, kIOCDMediaClass
) )
975 if( ( ret
= IORegistryEntryGetParentIterator( service
,
976 kIOServicePlane
, &iterator
) ) != KERN_SUCCESS
)
978 msg_Err( p_this
, "IORegistryEntryGetParentIterator: 0x%08x", ret
);
979 IOObjectRelease( service
);
983 IOObjectRelease( service
);
984 service
= IOIteratorNext( iterator
);
985 IOObjectRelease( iterator
);
990 msg_Err( p_this
, "search for kIOCDMediaClass came up empty" );
994 /* create a CF dictionary containing the TOC */
995 if( ( ret
= IORegistryEntryCreateCFProperties( service
, &properties
,
996 kCFAllocatorDefault
, kNilOptions
) ) != KERN_SUCCESS
)
998 msg_Err( p_this
, "IORegistryEntryCreateCFProperties: 0x%08x", ret
);
999 IOObjectRelease( service
);
1003 /* get the TOC from the dictionary */
1004 if( ( data
= (CFDataRef
) CFDictionaryGetValue( properties
,
1005 CFSTR(kIOCDMediaTOCKey
) ) ) != NULL
)
1010 buf_len
= CFDataGetLength( data
) + 1;
1011 range
= CFRangeMake( 0, buf_len
);
1013 if( ( pTOC
= malloc( buf_len
) ) != NULL
)
1015 CFDataGetBytes( data
, range
, (u_char
*)pTOC
);
1020 msg_Err( p_this
, "CFDictionaryGetValue failed" );
1023 CFRelease( properties
);
1024 IOObjectRelease( service
);
1029 /****************************************************************************
1030 * darwin_getNumberOfTracks: get number of tracks in TOC
1031 ****************************************************************************/
1032 static int darwin_getNumberOfTracks( CDTOC
*pTOC
, int i_descriptors
)
1035 int i
, i_tracks
= 0;
1036 CDTOCDescriptor
*pTrackDescriptors
= NULL
;
1038 pTrackDescriptors
= (CDTOCDescriptor
*)pTOC
->descriptors
;
1040 for( i
= i_descriptors
; i
> 0; i
-- )
1042 track
= pTrackDescriptors
[i
].point
;
1044 if( track
> CD_MAX_TRACK_NO
|| track
< CD_MIN_TRACK_NO
)
1052 #endif /* __APPLE__ */
1054 #if defined( _WIN32 )
1055 /*****************************************************************************
1056 * win32_vcd_open: open vcd drive
1057 *****************************************************************************
1058 * Use IOCTLs on WinNT/2K/XP.
1059 *****************************************************************************/
1060 static int win32_vcd_open( vlc_object_t
* p_this
, const char *psz_dev
,
1061 vcddev_t
*p_vcddev
)
1063 /* Initializations */
1064 p_vcddev
->h_device_handle
= NULL
;
1066 char psz_win32_drive
[7];
1068 msg_Dbg( p_this
, "using winNT/2K/XP ioctl layer" );
1070 sprintf( psz_win32_drive
, "\\\\.\\%c:", psz_dev
[0] );
1072 p_vcddev
->h_device_handle
= CreateFileA( psz_win32_drive
, GENERIC_READ
,
1073 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1074 NULL
, OPEN_EXISTING
,
1075 FILE_FLAG_NO_BUFFERING
|
1076 FILE_FLAG_RANDOM_ACCESS
, NULL
);
1077 return (p_vcddev
->h_device_handle
== NULL
) ? -1 : 0;
1083 /*****************************************************************************
1084 * os2_vcd_open: open vcd drive
1085 *****************************************************************************/
1086 static int os2_vcd_open( vlc_object_t
* p_this
, const char *psz_dev
,
1087 vcddev_t
*p_vcddev
)
1089 char device
[] = "X:";
1096 device
[0] = psz_dev
[0];
1097 rc
= DosOpen( device
, &hcd
, &i_action
, 0, FILE_NORMAL
,
1098 OPEN_ACTION_OPEN_IF_EXISTS
| OPEN_ACTION_FAIL_IF_NEW
,
1099 OPEN_ACCESS_READONLY
| OPEN_SHARE_DENYNONE
| OPEN_FLAGS_DASD
,
1103 msg_Err( p_this
, "could not open the device %s", psz_dev
);
1108 p_vcddev
->hcd
= hcd
;
1115 static void astrcat( char **ppsz_dst
, char *psz_src
)
1117 char *psz_old
= *ppsz_dst
;
1121 *ppsz_dst
= strdup( psz_src
);
1125 if( asprintf( ppsz_dst
, "%s%s", psz_old
, psz_src
) < 0 )
1126 *ppsz_dst
= psz_old
;
1133 static int CdTextParse( vlc_meta_t
***ppp_tracks
, int *pi_tracks
,
1134 const uint8_t *p_buffer
, int i_buffer
)
1136 char *pppsz_info
[128][0x10];
1137 int i_track_last
= -1;
1141 memset( pppsz_info
, 0, sizeof(pppsz_info
) );
1143 for( int i
= 0; i
< (i_buffer
-4)/18; i
++ )
1145 const uint8_t *p_block
= &p_buffer
[4 + 18*i
];
1146 char psz_text
[12+1];
1148 const int i_pack_type
= p_block
[0];
1149 if( i_pack_type
< 0x80 || i_pack_type
> 0x8f )
1152 const int i_track_number
= (p_block
[1] >> 0)&0x7f;
1153 const int i_extension_flag
= ( p_block
[1] >> 7)& 0x01;
1154 if( i_extension_flag
)
1157 //const int i_sequence_number = p_block[2];
1158 //const int i_charater_position = (p_block[3] >> 0) &0x0f;
1159 //const int i_block_number = (p_block[3] >> 4) &0x07;
1160 /* TODO unicode support
1161 * I need a sample */
1162 //const int i_unicode = ( p_block[3] >> 7)&0x01;
1163 //const int i_crc = (p_block[4+12] << 8) | (p_block[4+13] << 0);
1166 memcpy( psz_text
, &p_block
[4], 12 );
1167 psz_text
[12] = '\0';
1170 int i_track
= i_track_number
;
1171 char *psz_track
= &psz_text
[0];
1172 while( i_track
<= 127 && psz_track
< &psz_text
[12] )
1174 //fprintf( stderr, "t=%d psz_track=%p end=%p", i_track, (void *)psz_track, (void *)&psz_text[12] );
1177 astrcat( &pppsz_info
[i_track
][i_pack_type
-0x80], psz_track
);
1178 i_track_last
= __MAX( i_track_last
, i_track
);
1182 psz_track
+= 1 + strlen(psz_track
);
1186 if( i_track_last
< 0 )
1189 vlc_meta_t
**pp_tracks
= calloc( i_track_last
+1, sizeof(*pp_tracks
) );
1193 for( int j
= 0; j
< 0x10; j
++ )
1195 for( int i
= 0; i
<= i_track_last
; i
++ )
1198 if( pppsz_info
[i
][j
] )
1199 EnsureUTF8( pppsz_info
[i
][j
] );
1202 const char *psz_default
= pppsz_info
[0][j
];
1203 const char *psz_value
= pppsz_info
[i
][j
];
1205 if( !psz_value
&& !psz_default
)
1207 vlc_meta_t
*p_track
= pp_tracks
[i
];
1210 p_track
= pp_tracks
[i
] = vlc_meta_New();
1216 case 0x00: /* Album/Title */
1219 vlc_meta_SetAlbum( p_track
, psz_value
);
1224 vlc_meta_SetTitle( p_track
, psz_value
);
1226 vlc_meta_SetAlbum( p_track
, psz_default
);
1229 case 0x01: /* Performer */
1230 vlc_meta_SetArtist( p_track
,
1231 psz_value
? psz_value
: psz_default
);
1233 case 0x05: /* Messages */
1234 vlc_meta_SetDescription( p_track
,
1235 psz_value
? psz_value
: psz_default
);
1237 case 0x07: /* Genre */
1238 vlc_meta_SetGenre( p_track
,
1239 psz_value
? psz_value
: psz_default
);
1241 /* FIXME unsupported:
1251 for( int j
= 0; j
< 0x10; j
++ )
1252 for( int i
= 0; i
<= i_track_last
; i
++ )
1253 free( pppsz_info
[i
][j
] );
1255 *ppp_tracks
= pp_tracks
;
1256 *pi_tracks
= i_track_last
+1;
1257 return pp_tracks
? 0 : -1;
1260 #if defined( __APPLE__ ) || \
1261 defined( __OS2__ ) || \
1262 defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H ) || \
1263 defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
1264 static int CdTextRead( vlc_object_t
*p_object
, const vcddev_t
*p_vcddev
,
1265 uint8_t **pp_buffer
, int *pi_buffer
)
1267 VLC_UNUSED( p_object
);
1268 VLC_UNUSED( p_vcddev
);
1269 VLC_UNUSED( pp_buffer
);
1270 VLC_UNUSED( pi_buffer
);
1273 #elif defined( _WIN32 )
1274 static int CdTextRead( vlc_object_t
*p_object
, const vcddev_t
*p_vcddev
,
1275 uint8_t **pp_buffer
, int *pi_buffer
)
1277 VLC_UNUSED( p_object
);
1279 CDROM_READ_TOC_EX TOCEx
;
1280 memset(&TOCEx
, 0, sizeof(TOCEx
));
1281 TOCEx
.Format
= CDROM_READ_TOC_EX_FORMAT_CDTEXT
;
1283 const int i_header_size
= __MAX( 4, MINIMUM_CDROM_READ_TOC_EX_SIZE
);
1284 uint8_t header
[i_header_size
];
1286 if( !DeviceIoControl( p_vcddev
->h_device_handle
, IOCTL_CDROM_READ_TOC_EX
,
1287 &TOCEx
, sizeof(TOCEx
), header
, i_header_size
, &i_read
, 0 ) )
1290 const int i_text
= 2 + (header
[0] << 8) + header
[1];
1294 /* Read complete CD-TEXT */
1295 uint8_t *p_text
= calloc( 1, i_text
);
1297 return VLC_EGENERIC
;
1299 if( !DeviceIoControl( p_vcddev
->h_device_handle
, IOCTL_CDROM_READ_TOC_EX
,
1300 &TOCEx
, sizeof(TOCEx
), p_text
, i_text
, &i_read
, 0 ) )
1303 return VLC_EGENERIC
;
1307 *pp_buffer
= p_text
;
1308 *pi_buffer
= i_text
;
1312 static int CdTextRead( vlc_object_t
*p_object
, const vcddev_t
*p_vcddev
,
1313 uint8_t **pp_buffer
, int *pi_buffer
)
1315 VLC_UNUSED( p_object
);
1317 if( p_vcddev
->i_device_handle
== -1 )
1320 struct cdrom_generic_command gc
;
1323 /* Read CD-TEXT size */
1324 memset( header
, 0, sizeof(header
) );
1325 memset( &gc
, 0, sizeof(gc
) );
1326 gc
.cmd
[0] = 0x43; /* Read TOC */
1327 gc
.cmd
[1] = 0x02; /* MSF */
1328 gc
.cmd
[2] = 5; /* CD-Text */
1329 gc
.cmd
[7] = ( sizeof(header
) >> 8 ) & 0xff;
1330 gc
.cmd
[8] = ( sizeof(header
) >> 0 ) & 0xff;
1332 gc
.buflen
= sizeof(header
);
1334 gc
.data_direction
= CGC_DATA_READ
;
1337 if( ioctl( p_vcddev
->i_device_handle
, CDROM_SEND_PACKET
, &gc
) == -1 )
1338 return VLC_EGENERIC
;
1340 /* If the size is less than 4 it is an error, if it 4 then
1341 * it means no text data */
1342 const int i_text
= 2 + (header
[0] << 8) + header
[1];
1344 return VLC_EGENERIC
;
1346 /* Read complete CD-TEXT */
1347 uint8_t *p_text
= calloc( 1, i_text
);
1349 return VLC_EGENERIC
;
1351 memset( &gc
, 0, sizeof(gc
) );
1352 gc
.cmd
[0] = 0x43; /* Read TOC */
1353 gc
.cmd
[1] = 0x02; /* MSF */
1354 gc
.cmd
[2] = 5; /* CD-Text */
1355 gc
.cmd
[7] = ( i_text
>> 8 ) & 0xff;
1356 gc
.cmd
[8] = ( i_text
>> 0 ) & 0xff;
1360 gc
.data_direction
= CGC_DATA_READ
;
1363 if( ioctl( p_vcddev
->i_device_handle
, CDROM_SEND_PACKET
, &gc
) == -1 )
1366 return VLC_EGENERIC
;
1370 *pp_buffer
= p_text
;
1371 *pi_buffer
= i_text
;
1376 int ioctl_GetCdText( vlc_object_t
*p_object
, const vcddev_t
*p_vcddev
,
1377 vlc_meta_t
***ppp_tracks
, int *pi_tracks
)
1382 if( p_vcddev
->i_vcdimage_handle
!= -1 )
1385 if( CdTextRead( p_object
, p_vcddev
, &p_text
, &i_text
) )
1388 CdTextParse( ppp_tracks
, pi_tracks
, p_text
, i_text
);