1 /****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 1998-2001 the VideoLAN team
7 * Authors: Johan Bilien <jobi@via.ecp.fr>
8 * Gildas Bazin <gbazin@netcourrier.com>
9 * Jon Lech Johansen <jon-vl@nanocrew.net>
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.
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.
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 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 # define INCL_DOSDEVIOCTL
40 #include <sys/types.h>
41 #ifdef HAVE_SYS_STAT_H
42 # include <sys/stat.h>
45 #ifdef HAVE_ARPA_INET_H
46 # include <arpa/inet.h>
49 #include <vlc_common.h>
50 #include <vlc_access.h>
51 #include <vlc_charset.h>
55 #if defined( SYS_BSDI )
57 #elif defined ( __APPLE__ )
58 # include <CoreFoundation/CFBase.h>
59 # include <IOKit/IOKitLib.h>
60 # include <IOKit/storage/IOCDTypes.h>
61 # include <IOKit/storage/IOCDMedia.h>
62 # include <IOKit/storage/IOCDMediaBSDClient.h>
63 #elif defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
64 # include <inttypes.h>
65 # include <sys/cdio.h>
66 # include <sys/scsiio.h>
67 #elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H )
68 # include <sys/cdio.h>
69 # include <sys/cdrio.h>
70 #elif defined( WIN32 )
72 # include <winioctl.h>
73 #elif defined (__linux__)
74 # include <sys/ioctl.h>
75 # include <linux/cdrom.h>
76 #elif defined( __OS2__ )
82 #include "cdrom_internals.h"
86 /*****************************************************************************
87 * ioctl_Open: Opens a VCD device or file and returns an opaque handle
88 *****************************************************************************/
89 vcddev_t *ioctl_Open( vlc_object_t *p_this, const char *psz_dev )
94 #if !defined( WIN32 ) && !defined( __OS2__ )
98 if( !psz_dev ) return NULL;
101 * Initialize structure with default values
103 p_vcddev = malloc( sizeof(*p_vcddev) );
104 if( p_vcddev == NULL )
106 p_vcddev->i_vcdimage_handle = -1;
107 p_vcddev->psz_dev = NULL;
111 * Check if we are dealing with a device or a file (vcd image)
113 #if defined( WIN32 ) || defined( __OS2__ )
114 if( (strlen( psz_dev ) == 2 && psz_dev[1] == ':') )
120 if( vlc_stat( psz_dev, &fileinfo ) < 0 )
126 /* Check if this is a block/char device */
127 if( S_ISBLK( fileinfo.st_mode ) || S_ISCHR( fileinfo.st_mode ) )
133 i_ret = OpenVCDImage( p_this, psz_dev, p_vcddev );
138 * open the vcd device
142 i_ret = win32_vcd_open( p_this, psz_dev, p_vcddev );
143 #elif defined( __OS2__ )
144 i_ret = os2_vcd_open( p_this, psz_dev, p_vcddev );
146 p_vcddev->i_device_handle = -1;
147 p_vcddev->i_device_handle = vlc_open( psz_dev, O_RDONLY | O_NONBLOCK );
148 i_ret = (p_vcddev->i_device_handle == -1) ? -1 : 0;
154 p_vcddev->psz_dev = (char *)strdup( psz_dev );
165 /*****************************************************************************
166 * ioctl_Close: Closes an already opened VCD device or file.
167 *****************************************************************************/
168 void ioctl_Close( vlc_object_t * p_this, vcddev_t *p_vcddev )
170 free( p_vcddev->psz_dev );
172 if( p_vcddev->i_vcdimage_handle != -1 )
178 CloseVCDImage( p_this, p_vcddev );
187 if( p_vcddev->h_device_handle )
188 CloseHandle( p_vcddev->h_device_handle );
189 #elif defined( __OS2__ )
191 DosClose( p_vcddev->hcd );
193 if( p_vcddev->i_device_handle != -1 )
194 close( p_vcddev->i_device_handle );
199 /*****************************************************************************
200 * ioctl_GetTracksMap: Read the Table of Content, fill in the pp_sectors map
201 * if pp_sectors is not null and return the number of
203 *****************************************************************************/
204 int ioctl_GetTracksMap( vlc_object_t *p_this, const vcddev_t *p_vcddev,
209 if( p_vcddev->i_vcdimage_handle != -1 )
215 i_tracks = p_vcddev->i_tracks;
219 *pp_sectors = calloc( i_tracks + 1, sizeof(**pp_sectors) );
220 if( *pp_sectors == NULL )
222 memcpy( *pp_sectors, p_vcddev->p_sectors,
223 (i_tracks + 1) * sizeof(**pp_sectors) );
235 #if defined( __APPLE__ )
240 if( ( pTOC = darwin_getTOC( p_this, p_vcddev ) ) == NULL )
242 msg_Err( p_this, "failed to get the TOC" );
246 i_descriptors = CDTOCGetDescriptorCount( pTOC );
247 i_tracks = darwin_getNumberOfTracks( pTOC, i_descriptors );
251 int i, i_leadout = -1;
252 CDTOCDescriptor *pTrackDescriptors;
255 *pp_sectors = calloc( i_tracks + 1, sizeof(**pp_sectors) );
256 if( *pp_sectors == NULL )
258 darwin_freeTOC( pTOC );
262 pTrackDescriptors = pTOC->descriptors;
264 for( i_tracks = 0, i = 0; i < i_descriptors; i++ )
266 track = pTrackDescriptors[i].point;
271 if( track > CD_MAX_TRACK_NO || track < CD_MIN_TRACK_NO )
274 (*pp_sectors)[i_tracks++] =
275 CDConvertMSFToLBA( pTrackDescriptors[i].p );
278 if( i_leadout == -1 )
280 msg_Err( p_this, "leadout not found" );
282 darwin_freeTOC( pTOC );
286 /* set leadout sector */
287 (*pp_sectors)[i_tracks] =
288 CDConvertMSFToLBA( pTrackDescriptors[i_leadout].p );
291 darwin_freeTOC( pTOC );
293 #elif defined( WIN32 )
294 DWORD dwBytesReturned;
297 if( DeviceIoControl( p_vcddev->h_device_handle, IOCTL_CDROM_READ_TOC,
298 NULL, 0, &cdrom_toc, sizeof(CDROM_TOC),
299 &dwBytesReturned, NULL ) == 0 )
301 msg_Err( p_this, "could not read TOCHDR" );
305 i_tracks = cdrom_toc.LastTrack - cdrom_toc.FirstTrack + 1;
309 *pp_sectors = calloc( i_tracks + 1, sizeof(**pp_sectors) );
310 if( *pp_sectors == NULL )
313 for( int i = 0 ; i <= i_tracks ; i++ )
315 (*pp_sectors)[ i ] = MSF_TO_LBA2(
316 cdrom_toc.TrackData[i].Address[1],
317 cdrom_toc.TrackData[i].Address[2],
318 cdrom_toc.TrackData[i].Address[3] );
319 msg_Dbg( p_this, "p_sectors: %i, %i", i, (*pp_sectors)[i]);
323 #elif defined( __OS2__ )
324 cdrom_get_tochdr_t get_tochdr = {{'C', 'D', '0', '1'}};
325 cdrom_tochdr_t tochdr;
331 rc = DosDevIOCtl( p_vcddev->hcd, IOCTL_CDROMAUDIO,
332 CDROMAUDIO_GETAUDIODISK,
333 &get_tochdr, sizeof( get_tochdr ), ¶m_len,
334 &tochdr, sizeof( tochdr ), &data_len );
337 msg_Err( p_this, "could not read TOCHDR" );
341 i_tracks = tochdr.last_track - tochdr.first_track + 1;
345 cdrom_get_track_t get_track = {{'C', 'D', '0', '1'}, };
349 *pp_sectors = calloc( i_tracks + 1, sizeof(**pp_sectors) );
350 if( *pp_sectors == NULL )
353 for( i = 0 ; i < i_tracks ; i++ )
355 get_track.track = tochdr.first_track + i;
356 rc = DosDevIOCtl( p_vcddev->hcd, IOCTL_CDROMAUDIO,
357 CDROMAUDIO_GETAUDIOTRACK,
358 &get_track, sizeof(get_track), ¶m_len,
359 &track, sizeof(track), &data_len );
362 msg_Err( p_this, "could not read %d track",
367 (*pp_sectors)[ i ] = MSF_TO_LBA2(
371 msg_Dbg( p_this, "p_sectors: %i, %i", i, (*pp_sectors)[i]);
374 /* for lead-out track */
375 (*pp_sectors)[ i ] = MSF_TO_LBA2(
376 tochdr.lead_out.minute,
377 tochdr.lead_out.second,
378 tochdr.lead_out.frame );
379 msg_Dbg( p_this, "p_sectors: %i, %i", i, (*pp_sectors)[i]);
382 #elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H ) \
383 || defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
384 struct ioc_toc_header tochdr;
385 struct ioc_read_toc_entry toc_entries;
387 if( ioctl( p_vcddev->i_device_handle, CDIOREADTOCHEADER, &tochdr )
390 msg_Err( p_this, "could not read TOCHDR" );
394 i_tracks = tochdr.ending_track - tochdr.starting_track + 1;
400 *pp_sectors = calloc( i_tracks + 1, sizeof(**pp_sectors) );
401 if( *pp_sectors == NULL )
404 toc_entries.address_format = CD_LBA_FORMAT;
405 toc_entries.starting_track = 0;
406 toc_entries.data_len = ( i_tracks + 1 ) *
407 sizeof( struct cd_toc_entry );
408 toc_entries.data = (struct cd_toc_entry *)
409 malloc( toc_entries.data_len );
410 if( toc_entries.data == NULL )
417 if( ioctl( p_vcddev->i_device_handle, CDIOREADTOCENTRYS,
418 &toc_entries ) == -1 )
420 msg_Err( p_this, "could not read the TOC" );
422 free( toc_entries.data );
426 /* Fill the p_sectors structure with the track/sector matches */
427 for( i = 0 ; i <= i_tracks ; i++ )
429 #if defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
430 /* FIXME: is this ok? */
431 (*pp_sectors)[ i ] = toc_entries.data[i].addr.lba;
433 (*pp_sectors)[ i ] = ntohl( toc_entries.data[i].addr.lba );
438 struct cdrom_tochdr tochdr;
439 struct cdrom_tocentry tocent;
441 /* First we read the TOC header */
442 if( ioctl( p_vcddev->i_device_handle, CDROMREADTOCHDR, &tochdr )
445 msg_Err( p_this, "could not read TOCHDR" );
449 i_tracks = tochdr.cdth_trk1 - tochdr.cdth_trk0 + 1;
455 *pp_sectors = calloc( i_tracks + 1, sizeof(**pp_sectors) );
456 if( *pp_sectors == NULL )
459 /* Fill the p_sectors structure with the track/sector matches */
460 for( i = 0 ; i <= i_tracks ; i++ )
462 tocent.cdte_format = CDROM_LBA;
464 ( i == i_tracks ) ? CDROM_LEADOUT : tochdr.cdth_trk0 + i;
466 if( ioctl( p_vcddev->i_device_handle, CDROMREADTOCENTRY,
469 msg_Err( p_this, "could not read TOCENTRY" );
474 (*pp_sectors)[ i ] = tocent.cdte_addr.lba;
483 /****************************************************************************
484 * ioctl_ReadSector: Read VCD or CDDA sectors
485 ****************************************************************************/
486 int ioctl_ReadSectors( vlc_object_t *p_this, const vcddev_t *p_vcddev,
487 int i_sector, uint8_t *p_buffer, int i_nb, int i_type )
492 if( i_type == VCD_TYPE )
493 p_block = malloc( VCD_SECTOR_SIZE * i_nb );
497 if( p_vcddev->i_vcdimage_handle != -1 )
502 if( lseek( p_vcddev->i_vcdimage_handle, i_sector * VCD_SECTOR_SIZE,
505 msg_Err( p_this, "Could not lseek to sector %d", i_sector );
509 if( read( p_vcddev->i_vcdimage_handle, p_block, VCD_SECTOR_SIZE * i_nb)
512 msg_Err( p_this, "Could not read sector %d", i_sector );
524 #if defined( __APPLE__ )
525 dk_cd_read_t cd_read;
527 memset( &cd_read, 0, sizeof(cd_read) );
529 cd_read.offset = i_sector * VCD_SECTOR_SIZE;
530 cd_read.sectorArea = kCDSectorAreaSync | kCDSectorAreaHeader |
531 kCDSectorAreaSubHeader | kCDSectorAreaUser |
532 kCDSectorAreaAuxiliary;
533 cd_read.sectorType = kCDSectorTypeUnknown;
535 cd_read.buffer = p_block;
536 cd_read.bufferLength = VCD_SECTOR_SIZE * i_nb;
538 if( ioctl( p_vcddev->i_device_handle, DKIOCCDREAD, &cd_read ) == -1 )
540 msg_Err( p_this, "could not read block %d", i_sector );
544 #elif defined( WIN32 )
545 DWORD dwBytesReturned;
546 RAW_READ_INFO cdrom_raw;
548 /* Initialize CDROM_RAW_READ structure */
549 cdrom_raw.DiskOffset.QuadPart = CD_SECTOR_SIZE * i_sector;
550 cdrom_raw.SectorCount = i_nb;
551 cdrom_raw.TrackMode = i_type == VCD_TYPE ? XAForm2 : CDDA;
553 if( DeviceIoControl( p_vcddev->h_device_handle, IOCTL_CDROM_RAW_READ,
554 &cdrom_raw, sizeof(RAW_READ_INFO), p_block,
555 VCD_SECTOR_SIZE * i_nb, &dwBytesReturned,
558 if( i_type == VCD_TYPE )
560 /* Retry in YellowMode2 */
561 cdrom_raw.TrackMode = YellowMode2;
562 if( DeviceIoControl( p_vcddev->h_device_handle,
563 IOCTL_CDROM_RAW_READ, &cdrom_raw,
564 sizeof(RAW_READ_INFO), p_block,
565 VCD_SECTOR_SIZE * i_nb, &dwBytesReturned,
572 #elif defined( __OS2__ )
573 cdrom_readlong_t readlong = {{'C', 'D', '0', '1'}, };
579 readlong.addr_mode = 0; /* LBA mode */
580 readlong.sectors = i_nb;
581 readlong.start = i_sector;
583 rc = DosDevIOCtl( p_vcddev->hcd, IOCTL_CDROMDISK, CDROMDISK_READLONG,
584 &readlong, sizeof( readlong ), ¶m_len,
585 p_block, VCD_SECTOR_SIZE * i_nb, &data_len );
588 msg_Err( p_this, "could not read block %d", i_sector );
592 #elif defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
596 memset( &sc, 0, sizeof(sc) );
598 sc.cmd[1] = i_type == VCD_TYPE ? SECTOR_TYPE_MODE2_FORM2:
600 sc.cmd[2] = (i_sector >> 24) & 0xff;
601 sc.cmd[3] = (i_sector >> 16) & 0xff;
602 sc.cmd[4] = (i_sector >> 8) & 0xff;
603 sc.cmd[5] = (i_sector >> 0) & 0xff;
604 sc.cmd[6] = (i_nb >> 16) & 0xff;
605 sc.cmd[7] = (i_nb >> 8) & 0xff;
606 sc.cmd[8] = (i_nb ) & 0xff;
607 sc.cmd[9] = i_type == VCD_TYPE ? READ_CD_RAW_MODE2 : READ_CD_USERDATA;
608 sc.cmd[10] = 0; /* sub channel */
610 sc.databuf = (caddr_t)p_block;
611 sc.datalen = VCD_SECTOR_SIZE * i_nb;
612 sc.senselen = sizeof( sc.sense );
613 sc.flags = SCCMD_READ;
616 i_ret = ioctl( p_vcddev->i_device_handle, SCIOCCOMMAND, &sc );
619 msg_Err( p_this, "SCIOCCOMMAND failed" );
622 if( sc.retsts || sc.error )
624 msg_Err( p_this, "SCSI command failed: status %d error %d",
625 sc.retsts, sc.error );
629 #elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H )
630 int i_size = VCD_SECTOR_SIZE;
632 if( ioctl( p_vcddev->i_device_handle, CDRIOCSETBLOCKSIZE, &i_size )
635 msg_Err( p_this, "Could not set block size" );
639 if( lseek( p_vcddev->i_device_handle,
640 i_sector * VCD_SECTOR_SIZE, SEEK_SET ) == -1 )
642 msg_Err( p_this, "Could not lseek to sector %d", i_sector );
646 if( read( p_vcddev->i_device_handle,
647 p_block, VCD_SECTOR_SIZE * i_nb ) == -1 )
649 msg_Err( p_this, "Could not read sector %d", i_sector );
654 for( i = 0; i < i_nb; i++ )
656 int i_dummy = i_sector + i + 2 * CD_FRAMES;
658 #define p_msf ((struct cdrom_msf0 *)(p_block + i * VCD_SECTOR_SIZE))
659 p_msf->minute = i_dummy / (CD_FRAMES * CD_SECS);
660 p_msf->second = ( i_dummy % (CD_FRAMES * CD_SECS) ) / CD_FRAMES;
661 p_msf->frame = ( i_dummy % (CD_FRAMES * CD_SECS) ) % CD_FRAMES;
664 if( ioctl( p_vcddev->i_device_handle, CDROMREADRAW,
665 p_block + i * VCD_SECTOR_SIZE ) == -1 )
667 msg_Err( p_this, "could not read block %i from disc",
679 /* For VCDs, we don't want to keep the header and footer of the
681 if( i_type == VCD_TYPE )
683 for( i = 0; i < i_nb; i++ )
685 memcpy( p_buffer + i * VCD_DATA_SIZE,
686 p_block + i * VCD_SECTOR_SIZE + VCD_DATA_START,
695 if( i_type == VCD_TYPE )
700 /****************************************************************************
702 ****************************************************************************/
704 /****************************************************************************
705 * OpenVCDImage: try to open a vcd image from a .cue file
706 ****************************************************************************/
707 static int OpenVCDImage( vlc_object_t * p_this, const char *psz_dev,
712 char *psz_vcdfile = NULL;
713 char *psz_cuefile = NULL;
714 FILE *cuefile = NULL;
715 int *p_sectors = NULL;
717 bool b_found = false;
719 /* Check if we are dealing with a .cue file */
720 p_pos = strrchr( psz_dev, '.' );
721 if( p_pos && !strcmp( p_pos, ".cue" ) )
723 /* psz_dev must be the cue file. Let's assume there's a .bin
724 * file with the same filename */
725 psz_vcdfile = malloc( p_pos - psz_dev + 5 /* ".bin" */ );
726 strncpy( psz_vcdfile, psz_dev, p_pos - psz_dev );
727 strcpy( psz_vcdfile + (p_pos - psz_dev), ".bin");
728 psz_cuefile = strdup( psz_dev );
732 /* psz_dev must be the actual vcd file. Let's assume there's a .cue
733 * file with the same filename */
736 psz_cuefile = malloc( p_pos - psz_dev + 5 /* ".cue" */ );
737 strncpy( psz_cuefile, psz_dev, p_pos - psz_dev );
738 strcpy( psz_cuefile + (p_pos - psz_dev), ".cue");
742 if( asprintf( &psz_cuefile, "%s.cue", psz_dev ) == -1 )
745 /* If we need to look up the .cue file, then we don't have to look for the vcd */
746 psz_vcdfile = strdup( psz_dev );
749 /* Open the cue file and try to parse it */
750 msg_Dbg( p_this,"trying .cue file: %s", psz_cuefile );
751 cuefile = vlc_fopen( psz_cuefile, "rt" );
752 if( cuefile == NULL )
754 msg_Dbg( p_this, "could not find .cue file" );
758 msg_Dbg( p_this,"guessing vcd image file: %s", psz_vcdfile );
759 p_vcddev->i_vcdimage_handle = vlc_open( psz_vcdfile,
760 O_RDONLY | O_NONBLOCK | O_BINARY );
762 while( fgets( line, 1024, cuefile ) && !b_found )
764 /* We have a cue file, but no valid vcd file yet */
767 int i_temp = sscanf( line, "FILE \"%1023[^\"]\" %15s", filename, type );
771 msg_Dbg( p_this, "the cue file says the data file is %s", type );
772 if( strcasecmp( type, "BINARY" ) )
773 goto error; /* Error if not binary, otherwise treat as case 1 */
775 if( p_vcddev->i_vcdimage_handle == -1 )
777 msg_Dbg( p_this, "we could not find the data file, but we found a new path" );
779 if( *filename != '/' && ((p_pos = strrchr( psz_cuefile, '/' ))
780 || (p_pos = strrchr( psz_cuefile, '\\' ) )) )
782 psz_vcdfile = malloc( strlen(filename) +
783 (p_pos - psz_cuefile + 1) + 1 );
784 strncpy( psz_vcdfile, psz_cuefile, (p_pos - psz_cuefile + 1) );
785 strcpy( psz_vcdfile + (p_pos - psz_cuefile + 1), filename );
786 } else psz_vcdfile = strdup( filename );
787 msg_Dbg( p_this,"using vcd image file: %s", psz_vcdfile );
788 p_vcddev->i_vcdimage_handle = vlc_open( psz_vcdfile,
789 O_RDONLY | O_NONBLOCK | O_BINARY );
797 if( p_vcddev->i_vcdimage_handle == -1)
800 /* Try to parse the i_tracks and p_sectors info so we can just forget
801 * about the cuefile */
804 while( fgets( line, 1024, cuefile ) && i_tracks < INT_MAX-1 )
806 /* look for a TRACK line */
808 if( !sscanf( line, "%9s", psz_dummy ) || strcmp(psz_dummy, "TRACK") )
811 /* look for an INDEX line */
812 while( fgets( line, 1024, cuefile ) )
814 int i_num, i_min, i_sec, i_frame;
816 if( (sscanf( line, "%*9s %2u %2u:%2u:%2u", &i_num,
817 &i_min, &i_sec, &i_frame ) != 4) || (i_num != 1) )
820 int *buf = realloc (p_sectors, (i_tracks + 1) * sizeof (*buf));
824 p_sectors[i_tracks] = MSF_TO_LBA(i_min, i_sec, i_frame);
825 msg_Dbg( p_this, "vcd track %i begins at sector:%i",
826 (int)i_tracks, (int)p_sectors[i_tracks] );
832 /* fill in the last entry */
833 int *buf = realloc (p_sectors, (i_tracks + 1) * sizeof (*buf));
837 p_sectors[i_tracks] = lseek(p_vcddev->i_vcdimage_handle, 0, SEEK_END)
839 msg_Dbg( p_this, "vcd track %i, begins at sector:%i",
840 (int)i_tracks, (int)p_sectors[i_tracks] );
841 p_vcddev->i_tracks = ++i_tracks;
842 p_vcddev->p_sectors = p_sectors;
846 if( cuefile ) fclose( cuefile );
854 /****************************************************************************
855 * CloseVCDImage: closes a vcd image opened by OpenVCDImage
856 ****************************************************************************/
857 static void CloseVCDImage( vlc_object_t * p_this, vcddev_t *p_vcddev )
859 VLC_UNUSED( p_this );
860 if( p_vcddev->i_vcdimage_handle != -1 )
861 close( p_vcddev->i_vcdimage_handle );
865 free( p_vcddev->p_sectors );
868 #if defined( __APPLE__ )
869 /****************************************************************************
870 * darwin_getTOC: get the TOC
871 ****************************************************************************/
872 static CDTOC *darwin_getTOC( vlc_object_t * p_this, const vcddev_t *p_vcddev )
878 io_iterator_t iterator;
879 io_registry_entry_t service;
880 CFMutableDictionaryRef properties;
883 /* get the device name */
884 if( ( psz_devname = strrchr( p_vcddev->psz_dev, '/') ) != NULL )
887 psz_devname = p_vcddev->psz_dev;
889 /* unraw the device name */
890 if( *psz_devname == 'r' )
893 /* get port for IOKit communication */
894 if( ( ret = IOMasterPort( MACH_PORT_NULL, &port ) ) != KERN_SUCCESS )
896 msg_Err( p_this, "IOMasterPort: 0x%08x", ret );
900 /* get service iterator for the device */
901 if( ( ret = IOServiceGetMatchingServices(
902 port, IOBSDNameMatching( port, 0, psz_devname ),
903 &iterator ) ) != KERN_SUCCESS )
905 msg_Err( p_this, "IOServiceGetMatchingServices: 0x%08x", ret );
910 service = IOIteratorNext( iterator );
911 IOObjectRelease( iterator );
913 /* search for kIOCDMediaClass */
914 while( service && !IOObjectConformsTo( service, kIOCDMediaClass ) )
916 if( ( ret = IORegistryEntryGetParentIterator( service,
917 kIOServicePlane, &iterator ) ) != KERN_SUCCESS )
919 msg_Err( p_this, "IORegistryEntryGetParentIterator: 0x%08x", ret );
920 IOObjectRelease( service );
924 IOObjectRelease( service );
925 service = IOIteratorNext( iterator );
926 IOObjectRelease( iterator );
931 msg_Err( p_this, "search for kIOCDMediaClass came up empty" );
935 /* create a CF dictionary containing the TOC */
936 if( ( ret = IORegistryEntryCreateCFProperties( service, &properties,
937 kCFAllocatorDefault, kNilOptions ) ) != KERN_SUCCESS )
939 msg_Err( p_this, "IORegistryEntryCreateCFProperties: 0x%08x", ret );
940 IOObjectRelease( service );
944 /* get the TOC from the dictionary */
945 if( ( data = (CFDataRef) CFDictionaryGetValue( properties,
946 CFSTR(kIOCDMediaTOCKey) ) ) != NULL )
951 buf_len = CFDataGetLength( data ) + 1;
952 range = CFRangeMake( 0, buf_len );
954 if( ( pTOC = malloc( buf_len ) ) != NULL )
956 CFDataGetBytes( data, range, (u_char *)pTOC );
961 msg_Err( p_this, "CFDictionaryGetValue failed" );
964 CFRelease( properties );
965 IOObjectRelease( service );
970 /****************************************************************************
971 * darwin_getNumberOfTracks: get number of tracks in TOC
972 ****************************************************************************/
973 static int darwin_getNumberOfTracks( CDTOC *pTOC, int i_descriptors )
977 CDTOCDescriptor *pTrackDescriptors = NULL;
979 pTrackDescriptors = (CDTOCDescriptor *)pTOC->descriptors;
981 for( i = i_descriptors; i > 0; i-- )
983 track = pTrackDescriptors[i].point;
985 if( track > CD_MAX_TRACK_NO || track < CD_MIN_TRACK_NO )
993 #endif /* __APPLE__ */
996 /*****************************************************************************
997 * win32_vcd_open: open vcd drive
998 *****************************************************************************
999 * Use IOCTLs on WinNT/2K/XP.
1000 *****************************************************************************/
1001 static int win32_vcd_open( vlc_object_t * p_this, const char *psz_dev,
1002 vcddev_t *p_vcddev )
1004 /* Initializations */
1005 p_vcddev->h_device_handle = NULL;
1007 char psz_win32_drive[7];
1009 msg_Dbg( p_this, "using winNT/2K/XP ioctl layer" );
1011 sprintf( psz_win32_drive, "\\\\.\\%c:", psz_dev[0] );
1013 p_vcddev->h_device_handle = CreateFile( psz_win32_drive, GENERIC_READ,
1014 FILE_SHARE_READ | FILE_SHARE_WRITE,
1015 NULL, OPEN_EXISTING,
1016 FILE_FLAG_NO_BUFFERING |
1017 FILE_FLAG_RANDOM_ACCESS, NULL );
1018 return (p_vcddev->h_device_handle == NULL) ? -1 : 0;
1024 /*****************************************************************************
1025 * os2_vcd_open: open vcd drive
1026 *****************************************************************************/
1027 static int os2_vcd_open( vlc_object_t * p_this, const char *psz_dev,
1028 vcddev_t *p_vcddev )
1030 char device[] = "X:";
1037 device[0] = psz_dev[0];
1038 rc = DosOpen( device, &hcd, &i_action, 0, FILE_NORMAL,
1039 OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
1040 OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE | OPEN_FLAGS_DASD,
1044 msg_Err( p_this, "could not open the device %s", psz_dev );
1049 p_vcddev->hcd = hcd;
1056 static void astrcat( char **ppsz_dst, char *psz_src )
1058 char *psz_old = *ppsz_dst;
1062 *ppsz_dst = strdup( psz_src );
1066 if( asprintf( ppsz_dst, "%s%s", psz_old, psz_src ) < 0 )
1067 *ppsz_dst = psz_old;
1074 static int CdTextParse( vlc_meta_t ***ppp_tracks, int *pi_tracks,
1075 const uint8_t *p_buffer, int i_buffer )
1077 char *pppsz_info[128][0x10];
1078 int i_track_last = -1;
1082 memset( pppsz_info, 0, sizeof(pppsz_info) );
1084 for( int i = 0; i < (i_buffer-4)/18; i++ )
1086 const uint8_t *p_block = &p_buffer[4 + 18*i];
1087 char psz_text[12+1];
1089 const int i_pack_type = p_block[0];
1090 if( i_pack_type < 0x80 || i_pack_type > 0x8f )
1093 const int i_track_number = (p_block[1] >> 0)&0x7f;
1094 const int i_extension_flag = ( p_block[1] >> 7)& 0x01;
1095 if( i_extension_flag )
1098 //const int i_sequence_number = p_block[2];
1099 //const int i_charater_position = (p_block[3] >> 0) &0x0f;
1100 //const int i_block_number = (p_block[3] >> 4) &0x07;
1101 /* TODO unicode support
1102 * I need a sample */
1103 //const int i_unicode = ( p_block[3] >> 7)&0x01;
1104 //const int i_crc = (p_block[4+12] << 8) | (p_block[4+13] << 0);
1107 memcpy( psz_text, &p_block[4], 12 );
1108 psz_text[12] = '\0';
1111 int i_track = i_track_number;
1112 char *psz_track = &psz_text[0];
1113 while( i_track <= 127 && psz_track < &psz_text[12] )
1115 //fprintf( stderr, "t=%d psz_track=%p end=%p", i_track, psz_track, &psz_text[12] );
1118 astrcat( &pppsz_info[i_track][i_pack_type-0x80], psz_track );
1119 i_track_last = __MAX( i_track_last, i_track );
1123 psz_track += 1 + strlen(psz_track);
1127 if( i_track_last < 0 )
1130 vlc_meta_t **pp_tracks = calloc( i_track_last+1, sizeof(*pp_tracks) );
1134 for( int j = 0; j < 0x10; j++ )
1136 for( int i = 0; i <= i_track_last; i++ )
1139 if( pppsz_info[i][j] )
1140 EnsureUTF8( pppsz_info[i][j] );
1143 const char *psz_default = pppsz_info[0][j];
1144 const char *psz_value = pppsz_info[i][j];
1146 if( !psz_value && !psz_default )
1148 vlc_meta_t *p_track = pp_tracks[i];
1151 p_track = pp_tracks[i] = vlc_meta_New();
1157 case 0x00: /* Album/Title */
1160 vlc_meta_SetAlbum( p_track, psz_value );
1165 vlc_meta_SetTitle( p_track, psz_value );
1167 vlc_meta_SetAlbum( p_track, psz_default );
1170 case 0x01: /* Performer */
1171 vlc_meta_SetArtist( p_track,
1172 psz_value ? psz_value : psz_default );
1174 case 0x05: /* Messages */
1175 vlc_meta_SetDescription( p_track,
1176 psz_value ? psz_value : psz_default );
1178 case 0x07: /* Genre */
1179 vlc_meta_SetGenre( p_track,
1180 psz_value ? psz_value : psz_default );
1182 /* FIXME unsupported:
1192 for( int j = 0; j < 0x10; j++ )
1193 for( int i = 0; i <= i_track_last; i++ )
1194 free( pppsz_info[i][j] );
1196 *ppp_tracks = pp_tracks;
1197 *pi_tracks = i_track_last+1;
1198 return pp_tracks ? 0 : -1;
1201 #if defined( __APPLE__ ) || \
1202 defined( __OS2__ ) || \
1203 defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H ) || \
1204 defined( HAVE_SCSIREQ_IN_SYS_SCSIIO_H )
1205 static int CdTextRead( vlc_object_t *p_object, const vcddev_t *p_vcddev,
1206 uint8_t **pp_buffer, int *pi_buffer )
1208 VLC_UNUSED( p_object );
1209 VLC_UNUSED( p_vcddev );
1210 VLC_UNUSED( pp_buffer );
1211 VLC_UNUSED( pi_buffer );
1214 #elif defined( WIN32 )
1215 static int CdTextRead( vlc_object_t *p_object, const vcddev_t *p_vcddev,
1216 uint8_t **pp_buffer, int *pi_buffer )
1218 VLC_UNUSED( p_object );
1220 CDROM_READ_TOC_EX TOCEx;
1221 memset(&TOCEx, 0, sizeof(TOCEx));
1222 TOCEx.Format = CDROM_READ_TOC_EX_FORMAT_CDTEXT;
1224 const int i_header_size = __MAX( 4, MINIMUM_CDROM_READ_TOC_EX_SIZE );
1225 uint8_t header[i_header_size];
1227 if( !DeviceIoControl( p_vcddev->h_device_handle, IOCTL_CDROM_READ_TOC_EX,
1228 &TOCEx, sizeof(TOCEx), header, i_header_size, &i_read, 0 ) )
1231 const int i_text = 2 + (header[0] << 8) + header[1];
1235 /* Read complete CD-TEXT */
1236 uint8_t *p_text = calloc( 1, i_text );
1238 return VLC_EGENERIC;
1240 if( !DeviceIoControl( p_vcddev->h_device_handle, IOCTL_CDROM_READ_TOC_EX,
1241 &TOCEx, sizeof(TOCEx), p_text, i_text, &i_read, 0 ) )
1244 return VLC_EGENERIC;
1248 *pp_buffer = p_text;
1249 *pi_buffer = i_text;
1253 static int CdTextRead( vlc_object_t *p_object, const vcddev_t *p_vcddev,
1254 uint8_t **pp_buffer, int *pi_buffer )
1256 VLC_UNUSED( p_object );
1258 if( p_vcddev->i_device_handle == -1 )
1261 struct cdrom_generic_command gc;
1264 /* Read CD-TEXT size */
1265 memset( header, 0, sizeof(header) );
1266 memset( &gc, 0, sizeof(gc) );
1267 gc.cmd[0] = 0x43; /* Read TOC */
1268 gc.cmd[1] = 0x02; /* MSF */
1269 gc.cmd[2] = 5; /* CD-Text */
1270 gc.cmd[7] = ( sizeof(header) >> 8 ) & 0xff;
1271 gc.cmd[8] = ( sizeof(header) >> 0 ) & 0xff;
1273 gc.buflen = sizeof(header);
1275 gc.data_direction = CGC_DATA_READ;
1278 if( ioctl( p_vcddev->i_device_handle, CDROM_SEND_PACKET, &gc ) == -1 )
1279 return VLC_EGENERIC;
1281 /* If the size is less than 4 it is an error, if it 4 then
1282 * it means no text data */
1283 const int i_text = 2 + (header[0] << 8) + header[1];
1285 return VLC_EGENERIC;
1287 /* Read complete CD-TEXT */
1288 uint8_t *p_text = calloc( 1, i_text );
1290 return VLC_EGENERIC;
1292 memset( &gc, 0, sizeof(gc) );
1293 gc.cmd[0] = 0x43; /* Read TOC */
1294 gc.cmd[1] = 0x02; /* MSF */
1295 gc.cmd[2] = 5; /* CD-Text */
1296 gc.cmd[7] = ( i_text >> 8 ) & 0xff;
1297 gc.cmd[8] = ( i_text >> 0 ) & 0xff;
1301 gc.data_direction = CGC_DATA_READ;
1304 if( ioctl( p_vcddev->i_device_handle, CDROM_SEND_PACKET, &gc ) == -1 )
1307 return VLC_EGENERIC;
1311 *pp_buffer = p_text;
1312 *pi_buffer = i_text;
1317 int ioctl_GetCdText( vlc_object_t *p_object, const vcddev_t *p_vcddev,
1318 vlc_meta_t ***ppp_tracks, int *pi_tracks )
1323 if( p_vcddev->i_vcdimage_handle != -1 )
1326 if( CdTextRead( p_object, p_vcddev, &p_text, &i_text ) )
1329 CdTextParse( ppp_tracks, pi_tracks, p_text, i_text );