Commit 235dfe29 authored by Gildas Bazin's avatar Gildas Bazin

* modules/access/vcd/*, configure.ac.in:
   - Major changes to allow reading vcd images directly from the hard drive
      (you need a .cue and .bin file).
   - Removed duplicated code by merging ioctl_GetTrackCount and ioctl_GetSectors.
   - Implemented necessary ioctls for Win9x/NT/2K/XP.
parent 954bdeb8
......@@ -305,7 +305,7 @@ AC_EGREP_HEADER(strncasecmp,strings.h,[
dnl Check for headers
AC_CHECK_HEADERS(stdint.h getopt.h strings.h inttypes.h sys/int_types.h)
AC_CHECK_HEADERS(sys/sockio.h fcntl.h sys/types.h sys/time.h sys/times.h)
AC_CHECK_HEADERS(sys/sockio.h fcntl.h sys/types.h sys/time.h sys/times.h sys/ioctl.h)
AC_CHECK_HEADERS(dlfcn.h image.h)
AC_CHECK_HEADERS(arpa/inet.h net/if.h netinet/in.h sys/socket.h)
AC_CHECK_HEADERS(machine/param.h sys/shm.h)
......@@ -965,7 +965,7 @@ dnl
dnl VCD module
dnl
AC_ARG_ENABLE(vcd,
[ --enable-vcd VCD support for Linux, FreeBSD and MacOS X (default enabled)])
[ --enable-vcd VCD support for Linux, FreeBSD, MacOS X and Win32 (default enabled)])
if test "x${enable_vcd}" != "xno"
then
......@@ -978,7 +978,7 @@ then
AC_DEFINE(HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H, 1, For FreeBSD VCD support)
])
if test "x${SYS}" = "xbsdi"
if test "x${SYS}" = "xbsdi" -o "x${SYS}" = "xmingw32"
then
PLUGINS="${PLUGINS} vcd"
fi
......
......@@ -2,10 +2,11 @@
* cdrom.c: cdrom tools
*****************************************************************************
* Copyright (C) 1998-2001 VideoLAN
* $Id: cdrom.c,v 1.3 2002/08/09 23:47:22 massiot Exp $
* $Id: cdrom.c,v 1.4 2002/10/15 19:56:59 gbazin Exp $
*
* Author: Johan Bilien <jobi@via.ecp.fr>
* Jon Lech Johansen <jon-vl@nanocrew.net>
* Authors: Johan Bilien <jobi@via.ecp.fr>
* Gildas Bazin <gbazin@netcourrier.com>
* Jon Lech Johansen <jon-vl@nanocrew.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -39,7 +40,9 @@
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#if defined( SYS_BSDI )
# include <dvd.h>
......@@ -52,289 +55,820 @@
#elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H )
# include <sys/cdio.h>
# include <sys/cdrio.h>
#elif defined( WIN32 )
# include <windows.h>
# include <winioctl.h>
#else
# include <linux/cdrom.h>
#endif
#include "cdrom.h"
#include "vcd.h"
/*****************************************************************************
* Platform specific
* ioctl_Open: Opens a VCD device or file and returns an opaque handle
*****************************************************************************/
#if defined( SYS_DARWIN )
CDTOC *getTOC( vlc_object_t *, const char * );
#define freeTOC( p ) free( (void*)p )
int getNumberOfDescriptors( CDTOC * );
int getNumberOfTracks( CDTOC *, int );
#define CD_MIN_TRACK_NO 01
#define CD_MAX_TRACK_NO 99
vcddev_t *ioctl_Open( vlc_object_t *p_this, const char *psz_dev )
{
int i_ret;
int b_is_file;
vcddev_t *p_vcddev;
#ifndef WIN32
struct stat fileinfo;
#endif
/*****************************************************************************
* ioctl_ReadTocHeader: Read the TOC header and return the track number.
*****************************************************************************/
int ioctl_GetTrackCount( vlc_object_t * p_this, int i_fd, const char *psz_dev )
{
int i_count = -1;
if( !psz_dev ) return NULL;
#if defined( SYS_DARWIN )
CDTOC *pTOC;
int i_descriptors;
if( ( pTOC = getTOC( p_this, psz_dev ) ) == NULL )
/*
* Initialize structure with default values
*/
p_vcddev = (vcddev_t *)malloc( sizeof(vcddev_t) );
if( p_vcddev == NULL )
{
msg_Err( p_this, "failed to get the TOC" );
return( -1 );
msg_Err( p_this, "out of memory" );
return NULL;
}
p_vcddev->i_vcdimage_handle = -1;
p_vcddev->psz_dev = NULL;
b_is_file = 1;
/*
* Check if we are dealing with a device or a file (vcd image)
*/
#ifdef WIN32
if( strlen( psz_dev ) == 1 ||
(strlen( psz_dev ) == 2 && psz_dev[1] == ':') )
{
b_is_file = 0;
}
i_descriptors = getNumberOfDescriptors( pTOC );
i_count = getNumberOfTracks( pTOC, i_descriptors );
#else
if( stat( psz_dev, &fileinfo ) < 0 )
{
free( p_vcddev );
return NULL;
}
freeTOC( pTOC );
/* Check if this is a block/char device */
if( S_ISBLK( fileinfo.st_mode ) || S_ISCHR( fileinfo.st_mode ) )
b_is_file = 0;
#endif
#elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H )
struct ioc_toc_header tochdr;
if( ioctl( i_fd, CDIOREADTOCHEADER, &tochdr ) == -1 )
if( b_is_file )
{
msg_Err( p_this, "could not read TOCHDR" );
return -1;
i_ret = OpenVCDImage( p_this, psz_dev, p_vcddev );
}
else
{
/*
* open the vcd device
*/
i_count = tochdr.ending_track - tochdr.starting_track + 1;
#ifdef WIN32
i_ret = win32_vcd_open( p_this, psz_dev, p_vcddev );
#else
struct cdrom_tochdr tochdr;
p_vcddev->i_device_handle = -1;
p_vcddev->i_device_handle = open( psz_dev, O_RDONLY | O_NONBLOCK );
i_ret = (p_vcddev->i_device_handle == -1) ? -1 : 0;
#endif
}
/* First we read the TOC header */
if( ioctl( i_fd, CDROMREADTOCHDR, &tochdr ) == -1 )
if( i_ret == 0 )
{
msg_Err( p_this, "could not read TOCHDR" );
return -1;
p_vcddev->psz_dev = (char *)strdup( psz_dev );
}
else
{
free( p_vcddev );
p_vcddev = NULL;
}
i_count = tochdr.cdth_trk1 - tochdr.cdth_trk0 + 1;
#endif
return( i_count );
return p_vcddev;
}
/*****************************************************************************
* ioctl_GetSectors: Read the Table of Contents and fill p_vcd.
* ioctl_Close: Closes an already opened VCD device or file.
*****************************************************************************/
int * ioctl_GetSectors( vlc_object_t *p_this, int i_fd, const char *psz_dev )
void ioctl_Close( vlc_object_t * p_this, vcddev_t *p_vcddev )
{
int i, i_tracks;
int *p_sectors = NULL;
#if defined( SYS_DARWIN )
CDTOC *pTOC;
u_char track;
int i_descriptors;
int i_leadout = -1;
CDTOCDescriptor *pTrackDescriptors;
if( p_vcddev->psz_dev ) free( p_vcddev->psz_dev );
if( ( pTOC = getTOC( p_this, psz_dev ) ) == NULL )
if( p_vcddev->i_vcdimage_handle != -1 )
{
msg_Err( p_this, "failed to get the TOC" );
return( NULL );
/*
* vcd image mode
*/
CloseVCDImage( p_this, p_vcddev );
return;
}
i_descriptors = getNumberOfDescriptors( pTOC );
i_tracks = getNumberOfTracks( pTOC, i_descriptors );
/*
* vcd device mode
*/
p_sectors = malloc( (i_tracks + 1) * sizeof(int) );
if( p_sectors == NULL )
{
msg_Err( p_this, "out of memory" );
freeTOC( pTOC );
return NULL;
}
pTrackDescriptors = pTOC->descriptors;
#ifdef WIN32
if( p_vcddev->h_device_handle )
CloseHandle( p_vcddev->h_device_handle );
if( p_vcddev->hASPI )
FreeLibrary( (HMODULE)p_vcddev->hASPI );
#else
if( p_vcddev->i_device_handle != -1 )
close( p_vcddev->i_device_handle );
#endif
}
for( i_tracks = 0, i = 0; i <= i_descriptors; i++ )
/*****************************************************************************
* ioctl_GetTracksMap: Read the Table of Content, fill in the pp_sectors map
* if pp_sectors is not null and return the number of
* tracks available.
*****************************************************************************/
int ioctl_GetTracksMap( vlc_object_t *p_this, const vcddev_t *p_vcddev,
int **pp_sectors )
{
int i_tracks = 0;
if( p_vcddev->i_vcdimage_handle != -1 )
{
track = pTrackDescriptors[i].point;
/*
* vcd image mode
*/
if( track == 0xA2 )
i_leadout = i;
i_tracks = p_vcddev->i_tracks;
if( track > CD_MAX_TRACK_NO || track < CD_MIN_TRACK_NO )
continue;
if( pp_sectors )
{
*pp_sectors = malloc( (i_tracks + 1) * sizeof(int) );
if( *pp_sectors == NULL )
{
msg_Err( p_this, "out of memory" );
return 0;
}
memcpy( *pp_sectors, p_vcddev->p_sectors,
(i_tracks + 1) * sizeof(int) );
}
p_sectors[i_tracks++] =
CDConvertMSFToLBA( pTrackDescriptors[i].p );
return i_tracks;
}
if( i_leadout == -1 )
else
{
msg_Err( p_this, "leadout not found" );
free( p_sectors );
freeTOC( pTOC );
return( NULL );
}
/* set leadout sector */
p_sectors[i_tracks] =
CDConvertMSFToLBA( pTrackDescriptors[i_leadout].p );
/*
* vcd device mode
*/
freeTOC( pTOC );
#if defined( SYS_DARWIN )
CDTOC *pTOC;
int i_descriptors;
if( ( pTOC = darwin_getTOC( p_this, p_vcddev->psz_dev ) ) == NULL )
{
msg_Err( p_this, "failed to get the TOC" );
return 0;
}
i_descriptors = darwin_getNumberOfDescriptors( pTOC );
i_tracks = darwin_getNumberOfTracks( pTOC, i_descriptors );
if( pp_sectors )
{
int i, i_leadout = -1;
CDTOCDescriptor *pTrackDescriptors;
u_char track;
*pp_sectors = malloc( (i_tracks + 1) * sizeof(int) );
if( *pp_sectors == NULL )
{
msg_Err( p_this, "out of memory" );
darwin_freeTOC( pTOC );
return 0;
}
pTrackDescriptors = pTOC->descriptors;
for( i_tracks = 0, i = 0; i <= i_descriptors; i++ )
{
track = pTrackDescriptors[i].point;
if( track == 0xA2 )
i_leadout = i;
if( track > CD_MAX_TRACK_NO || track < CD_MIN_TRACK_NO )
continue;
(*pp_sectors)[i_tracks++] =
CDConvertMSFToLBA( pTrackDescriptors[i].p );
}
if( i_leadout == -1 )
{
msg_Err( p_this, "leadout not found" );
free( *pp_sectors );
darwin_freeTOC( pTOC );
return 0;
}
/* set leadout sector */
(*pp_sectors)[i_tracks] =
CDConvertMSFToLBA( pTrackDescriptors[i_leadout].p );
}
darwin_freeTOC( pTOC );
#elif defined( WIN32 )
if( p_vcddev->hASPI )
{
HANDLE hEvent;
struct SRB_ExecSCSICmd ssc;
byte_t p_tocheader[ 4 ];
/* Create the transfer completion event */
hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
if( hEvent == NULL )
{
return -1;
}
memset( &ssc, 0, sizeof( ssc ) );
ssc.SRB_Cmd = SC_EXEC_SCSI_CMD;
ssc.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
ssc.SRB_HaId = LOBYTE( p_vcddev->i_sid );
ssc.SRB_Target = HIBYTE( p_vcddev->i_sid );
ssc.SRB_SenseLen = SENSE_LEN;
ssc.SRB_PostProc = (LPVOID) hEvent;
ssc.SRB_CDBLen = 10;
/* Operation code */
ssc.CDBByte[ 0 ] = READ_TOC;
/* Format */
ssc.CDBByte[ 2 ] = READ_TOC_FORMAT_TOC;
/* Starting track */
ssc.CDBByte[ 6 ] = 0;
/* Allocation length and buffer */
ssc.SRB_BufLen = sizeof( p_tocheader );
ssc.SRB_BufPointer = p_tocheader;
ssc.CDBByte[ 7 ] = ( ssc.SRB_BufLen >> 8 ) & 0xff;
ssc.CDBByte[ 8 ] = ( ssc.SRB_BufLen ) & 0xff;
/* Initiate transfer */
ResetEvent( hEvent );
p_vcddev->lpSendCommand( (void*) &ssc );
/* If the command has still not been processed, wait until it's
* finished */
if( ssc.SRB_Status == SS_PENDING )
WaitForSingleObject( hEvent, INFINITE );
/* check that the transfer went as planned */
if( ssc.SRB_Status != SS_COMP )
{
CloseHandle( hEvent );
return 0;
}
i_tracks = p_tocheader[3] - p_tocheader[2] + 1;
if( pp_sectors )
{
int i, i_toclength;
byte_t *p_fulltoc;
i_toclength = 4 /* header */ + p_tocheader[0] +
((unsigned int)p_tocheader[1] << 8);
p_fulltoc = malloc( i_toclength );
*pp_sectors = malloc( (i_tracks + 1) * sizeof(int) );
if( *pp_sectors == NULL || p_fulltoc == NULL )
{
if( *pp_sectors ) free( *pp_sectors );
if( p_fulltoc ) free( p_fulltoc );
msg_Err( p_this, "out of memory" );
CloseHandle( hEvent );
return 0;
}
/* Allocation length and buffer */
ssc.SRB_BufLen = i_toclength;
ssc.SRB_BufPointer = p_fulltoc;
ssc.CDBByte[ 7 ] = ( ssc.SRB_BufLen >> 8 ) & 0xff;
ssc.CDBByte[ 8 ] = ( ssc.SRB_BufLen ) & 0xff;
/* Initiate transfer */
ResetEvent( hEvent );
p_vcddev->lpSendCommand( (void*) &ssc );
/* If the command has still not been processed, wait until it's
* finished */
if( ssc.SRB_Status == SS_PENDING )
WaitForSingleObject( hEvent, INFINITE );
/* check that the transfer went as planned */
if( ssc.SRB_Status != SS_COMP )
i_tracks = 0;
for( i = 0 ; i <= i_tracks ; i++ )
{
int i_index = 8 + 8 * i;
(*pp_sectors)[ i ] = ((int)p_fulltoc[ i_index ] << 24) +
((int)p_fulltoc[ i_index+1 ] << 16) +
((int)p_fulltoc[ i_index+2 ] << 8) +
(int)p_fulltoc[ i_index+3 ];
msg_Dbg( p_this, "p_sectors: %i, %i", i, (*pp_sectors)[i]);
}
free( p_fulltoc );
}
CloseHandle( hEvent );
}
else
{
DWORD dwBytesReturned;
CDROM_TOC cdrom_toc;
if( DeviceIoControl( p_vcddev->h_device_handle,
IOCTL_CDROM_READ_TOC,
NULL, 0, &cdrom_toc, sizeof(CDROM_TOC),
&dwBytesReturned, NULL ) == 0 )
{
msg_Err( p_this, "could not read TOCHDR" );
return 0;
}
i_tracks = cdrom_toc.LastTrack - cdrom_toc.FirstTrack + 1;
if( pp_sectors )
{
int i;
*pp_sectors = malloc( (i_tracks + 1) * sizeof(int) );
if( *pp_sectors == NULL )
{
msg_Err( p_this, "out of memory" );
return 0;
}
for( i = 0 ; i <= i_tracks ; i++ )
{
(*pp_sectors)[ i ] = MSF_TO_LBA2(
cdrom_toc.TrackData[i].Address[1],
cdrom_toc.TrackData[i].Address[2],
cdrom_toc.TrackData[i].Address[3] );
msg_Dbg( p_this, "p_sectors: %i, %i", i, (*pp_sectors)[i]);
}
}
}
#elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H )
struct ioc_read_toc_entry toc_entries;
struct ioc_toc_header tochdr;
struct ioc_read_toc_entry toc_entries;
i_tracks = ioctl_GetTrackCount( p_this, i_fd, psz_dev );
p_sectors = malloc( (i_tracks + 1) * sizeof(int) );
if( p_sectors == NULL )
{
msg_Err( p_this, "out of memory" );
return NULL;
}
if( ioctl( p_vcddev->i_devicd_handle, CDIOREADTOCHEADER, &tochdr )
== -1 )
{
msg_Err( p_this, "could not read TOCHDR" );
return 0;
}
toc_entries.address_format = CD_LBA_FORMAT;
toc_entries.starting_track = 0;
toc_entries.data_len = ( i_tracks + 1 ) * sizeof( struct cd_toc_entry );
toc_entries.data = (struct cd_toc_entry *) malloc( toc_entries.data_len );
if( toc_entries.data == NULL )
{
msg_Err( p_this, "out of memory" );
free( p_sectors );
return NULL;
}
i_tracks = tochdr.ending_track - tochdr.starting_track + 1;
if( pp_sectors )
{
int i;
*pp_sectors = malloc( (i_tracks + 1) * sizeof(int) );
if( *pp_sectors == NULL )
{
msg_Err( p_this, "out of memory" );
return NULL;
}
toc_entries.address_format = CD_LBA_FORMAT;
toc_entries.starting_track = 0;
toc_entries.data_len = ( i_tracks + 1 ) *
sizeof( struct cd_toc_entry );
toc_entries.data = (struct cd_toc_entry *)
malloc( toc_entries.data_len );
if( toc_entries.data == NULL )
{
msg_Err( p_this, "out of memory" );
free( *pp_sectors );
return 0;
}
/* Read the TOC */
if( ioctl( i_fd, CDIOREADTOCENTRYS, &toc_entries ) == -1 )
{
msg_Err( p_this, "could not read the TOC" );
free( p_sectors );
free( toc_entries.data );
return NULL;
}
/* Read the TOC */
if( ioctl( p_vcddev->i_device_handle, CDIOREADTOCENTRYS,
&toc_entries ) == -1 )
{
msg_Err( p_this, "could not read the TOC" );
free( *pp_sectors );
free( toc_entries.data );
return 0;
}
/* Fill the p_sectors structure with the track/sector matches */
for( i = 0 ; i <= i_tracks ; i++ )
{
p_sectors[ i ] = ntohl( toc_entries.data[i].addr.lba );
}
/* Fill the p_sectors structure with the track/sector matches */
for( i = 0 ; i <= i_tracks ; i++ )
{
(*pp_sectors)[ i ] = ntohl( toc_entries.data[i].addr.lba );
}
}
#else
struct cdrom_tochdr tochdr;
struct cdrom_tocentry tocent;
/* First we read the TOC header */
if( ioctl( i_fd, CDROMREADTOCHDR, &tochdr ) == -1 )
{
msg_Err( p_this, "could not read TOCHDR" );
return NULL;
}
struct cdrom_tochdr tochdr;
struct cdrom_tocentry tocent;
i_tracks = tochdr.cdth_trk1 - tochdr.cdth_trk0 + 1;
p_sectors = malloc( (i_tracks + 1) * sizeof(int) );
if( p_sectors == NULL )
{
msg_Err( p_this, "out of memory" );
return NULL;
}
/* First we read the TOC header */
if( ioctl( p_vcddev->i_device_handle, CDROMREADTOCHDR, &tochdr )
== -1 )
{
msg_Err( p_this, "could not read TOCHDR" );
return 0;
}
/* Fill the p_sectors structure with the track/sector matches */
for( i = 0 ; i <= i_tracks ; i++ )
{
tocent.cdte_format = CDROM_LBA;
tocent.cdte_track =
( i == i_tracks ) ? CDROM_LEADOUT : tochdr.cdth_trk0 + i;
i_tracks = tochdr.cdth_trk1 - tochdr.cdth_trk0 + 1;
if( ioctl( i_fd, CDROMREADTOCENTRY, &tocent ) == -1 )
if( pp_sectors )
{
msg_Err( p_this, "could not read TOCENTRY" );
free( p_sectors );