cdrom.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:47k
源码类别:

midi

开发平台:

Unix_Linux

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