]> git.sesse.net Git - vlc/blobdiff - extras/libdvdcss/libdvdcss.c
* Fixed a Win32 bug in libdvdcss. This bug was appearing on title change.
[vlc] / extras / libdvdcss / libdvdcss.c
index f00e679eb9bd762d92f1f4cac3e5af4111b98c02..30b037f43ae8b41fb2bb9428a7eedde76b0f36bf 100644 (file)
@@ -2,7 +2,7 @@
  * libdvdcss.c: DVD reading library.
  *****************************************************************************
  * Copyright (C) 1998-2001 VideoLAN
- * $Id: libdvdcss.c,v 1.7 2001/07/15 09:49:09 gbazin Exp $
+ * $Id: libdvdcss.c,v 1.13 2001/07/30 18:56:35 gbazin Exp $
  *
  * Authors: Stéphane Borel <stef@via.ecp.fr>
  *          Samuel Hocevar <sam@zoy.org>
@@ -38,8 +38,7 @@
 #endif
 
 #if defined( WIN32 )
-#   include <io.h>
-#   include "input_iovec.h"
+#   include <io.h>                                                 /* read() */
 #else
 #   include <sys/uio.h>                                      /* struct iovec */
 #endif
 #include "config.h"
 #include "common.h"
 
+#if defined( WIN32 )
+#   include "input_iovec.h"
+#endif
+
 #include "videolan/dvdcss.h"
 #include "libdvdcss.h"
 #include "ioctl.h"
@@ -65,7 +68,7 @@ static int _dvdcss_readv ( dvdcss_handle, struct iovec *p_iovec, int i_blocks );
  *****************************************************************************/
 #if defined( WIN32 )
 static int _win32_dvdcss_readv  ( int i_fd, struct iovec *p_iovec,
-                                  int i_num_buffers );
+                                  int i_num_buffers, char *p_tmp_buffer );
 static int _win32_dvdcss_aopen  ( char c_drive, dvdcss_handle dvdcss );
 static int _win32_dvdcss_aclose ( int i_fd );
 static int _win32_dvdcss_aseek  ( int i_fd, int i_blocks, int i_method );
@@ -94,7 +97,7 @@ extern dvdcss_handle dvdcss_open ( char *psz_target, int i_flags )
     }
 
     /* Initialize structure */
-    dvdcss->p_keys = NULL;
+    dvdcss->p_titles = NULL;
     dvdcss->b_debug = i_flags & DVDCSS_INIT_DEBUG;
     dvdcss->b_errors = !(i_flags & DVDCSS_INIT_QUIET);
     dvdcss->psz_error = "no error";
@@ -150,13 +153,11 @@ extern int dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
 }
 
 /*****************************************************************************
- * dvdcss_crack: crack the current title key
+ * dvdcss_title: crack the current title key if needed
  *****************************************************************************/
-extern int dvdcss_crack ( dvdcss_handle dvdcss, int i_block )
+extern int dvdcss_title ( dvdcss_handle dvdcss, int i_block )
 {
-    title_key_t **pp_writekey;
-    title_key_t **pp_currentkey;
-    title_key_t *p_titlekey;
+    dvd_title_t *p_title;
     dvd_key_t p_key;
     int i_ret;
 
@@ -165,16 +166,19 @@ extern int dvdcss_crack ( dvdcss_handle dvdcss, int i_block )
         return 0;
     }
 
+    //fprintf( stderr, "looking for a key for offset %i\n", i_block );
+
     /* Check if we've already cracked this key */
-    p_titlekey = dvdcss->p_keys;
-    while( p_titlekey != NULL
-            && p_titlekey->p_next != NULL
-            && p_titlekey->p_next->i_startlb < i_block )
+    p_title = dvdcss->p_titles;
+    while( p_title != NULL
+            && p_title->p_next != NULL
+            && p_title->p_next->i_startlb <= i_block )
     {
-        p_titlekey = p_titlekey->p_next;
+        p_title = p_title->p_next;
     }
 
-    if( p_titlekey != NULL && p_titlekey->i_startlb == i_block )
+    if( p_title != NULL
+         && p_title->i_startlb == i_block )
     {
         /* We've already cracked this key, nothing to do */
         return 0;
@@ -194,25 +198,43 @@ extern int dvdcss_crack ( dvdcss_handle dvdcss, int i_block )
         return -1;
     }
 
+    //fprintf( stderr, "cracked key is %.2x %.2x %.2x %.2x %.2x\n",
+    //         p_key[0], p_key[1], p_key[2], p_key[3], p_key[4] );
+
     /* Add key to keytable if it isn't empty */
-    if( p_key[0] || p_key[1] || p_key[2] || p_key[3] || p_key[4] )
+    if( p_key[0] | p_key[1] | p_key[2] | p_key[3] | p_key[4] )
     {
+        dvd_title_t *p_newtitle;
+
         /* Find our spot in the list */
-        pp_writekey = &(dvdcss->p_keys);
-        pp_currentkey = pp_writekey;
-        while( *pp_currentkey != NULL
-                && (*pp_currentkey)->i_startlb < i_block )
+        p_newtitle = NULL;
+        p_title = dvdcss->p_titles;
+        while( p_title != NULL
+                && p_title->i_startlb < i_block )
         {
-            pp_writekey = pp_currentkey;
-            pp_currentkey = &((*pp_currentkey)->p_next);
+            p_newtitle = p_title;
+            p_title = p_title->p_next;
         }
 
-        /* Write in the new key */
-        p_titlekey = *pp_writekey;
-        *pp_writekey = malloc( sizeof( title_key_t ) );
-        (*pp_writekey)->i_startlb = i_block;
-        memcpy( (*pp_writekey)->p_key, p_key, KEY_SIZE );
-        (*pp_writekey)->p_next = p_titlekey;
+        /* Save the found title */
+        p_title = p_newtitle;
+
+        /* Write in the new title and its key */
+        p_newtitle = malloc( sizeof( dvd_title_t ) );
+        p_newtitle->i_startlb = i_block;
+        memcpy( p_newtitle->p_key, p_key, KEY_SIZE );
+
+        /* Link the new title, either at the beginning or inside the list */
+        if( p_title == NULL )
+        {
+            dvdcss->p_titles = p_newtitle;
+            p_newtitle->p_next = NULL;
+        }
+        else
+        {
+            p_newtitle->p_next = p_title->p_next;
+            p_title->p_next = p_newtitle;
+        }
     }
 
     return 0;
@@ -225,7 +247,7 @@ extern int dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer,
                                                int i_blocks,
                                                int i_flags )
 {
-    title_key_t *p_current;
+    dvd_title_t *p_title;
     int i_ret, i_index;
 
     i_ret = _dvdcss_read( dvdcss, p_buffer, i_blocks );
@@ -238,15 +260,15 @@ extern int dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer,
     }
 
     /* find our key */
-    p_current = dvdcss->p_keys;
-    while( p_current != NULL
-            && p_current->p_next
-            && p_current->p_next->i_startlb < dvdcss->i_seekpos )
+    p_title = dvdcss->p_titles;
+    while( p_title != NULL
+            && p_title->p_next
+            && p_title->p_next->i_startlb <= dvdcss->i_seekpos )
     {
-        p_current = p_current->p_next;
+        p_title = p_title->p_next;
     }
 
-    if( p_current == NULL )
+    if( p_title == NULL )
     {
         /* no css key found to use, so no decryption to do */
         return 0;
@@ -255,7 +277,7 @@ extern int dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer,
     /* Decrypt the blocks we managed to read */
     for( i_index = i_ret; i_index; i_index-- )
     {
-        CSSDescrambleSector( p_current->p_key, p_buffer );
+        CSSDescrambleSector( p_title->p_key, p_buffer );
         ((u8*)p_buffer)[0x14] &= 0x8f;
         (u8*)p_buffer += DVDCSS_BLOCK_SIZE;
     }
@@ -271,7 +293,7 @@ extern int dvdcss_readv ( dvdcss_handle dvdcss, void *p_iovec,
                                                 int i_flags )
 {
 #define P_IOVEC ((struct iovec*)p_iovec)
-    title_key_t *p_current;
+    dvd_title_t *p_title;
     int i_ret, i_index;
     void *iov_base;
     size_t iov_len;
@@ -286,21 +308,20 @@ extern int dvdcss_readv ( dvdcss_handle dvdcss, void *p_iovec,
     }
 
     /* Find our key */
-    p_current = dvdcss->p_keys;
-    while( p_current != NULL
-            && p_current->p_next
-            && p_current->p_next->i_startlb < dvdcss->i_seekpos )
+    p_title = dvdcss->p_titles;
+    while( p_title != NULL
+            && p_title->p_next != NULL
+            && p_title->p_next->i_startlb <= dvdcss->i_seekpos )
     {
-        p_current = p_current->p_next;
+        p_title = p_title->p_next;
     }
 
-    if( p_current == NULL )
+    if( p_title == NULL )
     {
         /* no css key found to use, so no decryption to do */
         return 0;
     }
 
-
     /* Initialize loop for decryption */
     iov_base = P_IOVEC->iov_base;
     iov_len = P_IOVEC->iov_len;
@@ -321,7 +342,7 @@ extern int dvdcss_readv ( dvdcss_handle dvdcss, void *p_iovec,
             iov_len = P_IOVEC->iov_len;
         }
 
-        CSSDescrambleSector( p_current->p_key, iov_base );
+        CSSDescrambleSector( p_title->p_key, iov_base );
         ((u8*)iov_base)[0x14] &= 0x8f;
 
         (u8*)iov_base += DVDCSS_BLOCK_SIZE;
@@ -337,16 +358,16 @@ extern int dvdcss_readv ( dvdcss_handle dvdcss, void *p_iovec,
  *****************************************************************************/
 extern int dvdcss_close ( dvdcss_handle dvdcss )
 {
-    title_key_t *p_currentkey;
+    dvd_title_t *p_title;
     int i_ret;
 
     /* Free our list of keys */
-    p_currentkey = dvdcss->p_keys;
-    while( p_currentkey )
+    p_title = dvdcss->p_titles;
+    while( p_title )
     {
-        title_key_t *p_tmpkey = p_currentkey->p_next;
-        free( p_currentkey );
-        p_currentkey = p_tmpkey;
+        dvd_title_t *p_tmptitle = p_title->p_next;
+        free( p_title );
+        p_title = p_tmptitle;
     }
 
     i_ret = _dvdcss_close( dvdcss );
@@ -390,6 +411,10 @@ static int _dvdcss_open ( dvdcss_handle dvdcss, char *psz_target )
         }
     }
 
+    /* initialise readv temporary buffer */
+    dvdcss->p_readv_buffer   = NULL;
+    dvdcss->i_readv_buf_size = 0;
+
 #else
     dvdcss->i_fd = open( psz_target, 0 );
 
@@ -415,8 +440,18 @@ static int _dvdcss_close ( dvdcss_handle dvdcss )
     {
         _win32_dvdcss_aclose( dvdcss->i_fd );
     }
+
+    /* Free readv temporary buffer */
+    if( dvdcss->p_readv_buffer )
+    {
+        free( dvdcss->p_readv_buffer );
+        dvdcss->p_readv_buffer   = NULL;
+        dvdcss->i_readv_buf_size = 0;
+    }
+
 #else
     close( dvdcss->i_fd );
+
 #endif
 
     return 0;
@@ -425,6 +460,8 @@ static int _dvdcss_close ( dvdcss_handle dvdcss )
 static int _dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
 {
 #if defined( WIN32 )
+    dvdcss->i_seekpos = i_blocks;
+
     if( WIN2K )
     {
         LARGE_INTEGER li_read;
@@ -487,25 +524,49 @@ static int _dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer, int i_blocks )
 #else
     int i_bytes;
 
-    i_bytes = read( dvdcss->i_fd, p_buffer, (size_t)i_blocks * DVDCSS_BLOCK_SIZE );
+    i_bytes = read( dvdcss->i_fd, p_buffer,
+                    (size_t)i_blocks * DVDCSS_BLOCK_SIZE );
     return i_bytes / DVDCSS_BLOCK_SIZE;
 #endif
 
 }
 
-static int _dvdcss_readv ( dvdcss_handle dvdcss, struct iovec *p_iovec, int i_blocks )
+static int _dvdcss_readv ( dvdcss_handle dvdcss, struct iovec *p_iovec,
+                           int i_blocks )
 {
     int i_read;
 
 #if defined( WIN32 )
-    i_read = _win32_dvdcss_readv( dvdcss->i_fd, p_iovec, i_blocks );
+    /* Check the size of the readv temp buffer, just in case we need to
+     * realloc something bigger */
+    if( dvdcss->i_readv_buf_size < i_blocks * DVDCSS_BLOCK_SIZE )
+    {
+        dvdcss->i_readv_buf_size = i_blocks * DVDCSS_BLOCK_SIZE;
+
+        if( dvdcss->p_readv_buffer ) free( dvdcss->p_readv_buffer );
+
+        /* Allocate a buffer which will be used as a temporary storage
+         * for readv */
+        dvdcss->p_readv_buffer = malloc( dvdcss->i_readv_buf_size );
+        if( !dvdcss->p_readv_buffer )
+        {
+            _dvdcss_error( dvdcss, " failed (readv)" );
+            return -1;
+        }
+    }
+
+    i_read = _win32_dvdcss_readv( dvdcss->i_fd, p_iovec, i_blocks,
+                                  dvdcss->p_readv_buffer );
     return i_read;
+
 #else
     i_read = readv( dvdcss->i_fd, p_iovec, i_blocks );
     return i_read / DVDCSS_BLOCK_SIZE;
+
 #endif
 }
 
+
 #if defined( WIN32 )
 
 /*****************************************************************************
@@ -513,75 +574,58 @@ static int _dvdcss_readv ( dvdcss_handle dvdcss, struct iovec *p_iovec, int i_bl
  *                      _win32_dvdcss_aread for win9x
  *****************************************************************************/
 static int _win32_dvdcss_readv( int i_fd, struct iovec *p_iovec,
-                                int i_num_buffers )
+                                int i_num_buffers, char *p_tmp_buffer )
 {
-    int i_index, i_len, i_total = 0;
-    unsigned char *p_base;
-    int i_blocks;
+    int i_index;
+    int i_blocks, i_blocks_total = 0;
 
-    if( WIN2K )
+    for( i_index = i_num_buffers; i_index; i_index-- )
     {
-        for( i_index = i_num_buffers; i_index; i_index-- )
-       {
-           i_len  = p_iovec->iov_len;
-           p_base = p_iovec->iov_base;
-
-           if( i_len > 0 )
-           {
-                unsigned long int i_bytes;
-                if( !ReadFile( (HANDLE) i_fd, p_base, i_len, &i_bytes, NULL ) )
-                {
-                    return -1;
-                    /* One of the reads failed, too bad.
-                       We won't even bother returning the reads that went well,
-                       and like in the posix spec the file postition is left
-                       unspecified after a failure */
-                }
-                i_blocks = i_bytes / DVDCSS_BLOCK_SIZE;
-
-               i_total += i_blocks;
-
-               if( i_blocks != (i_len / DVDCSS_BLOCK_SIZE) )
-               {
-                   /* we reached the end of the file */
-                   return i_total;
-               }
-
-           }
-
-           p_iovec++;
-       }
+        i_blocks_total += p_iovec[i_index-1].iov_len; 
     }
-    else /* Win9x */
-    {
-        for( i_index = i_num_buffers; i_index; i_index-- )
-       {
-           i_len  = p_iovec->iov_len / DVDCSS_BLOCK_SIZE;
-           p_base = p_iovec->iov_base;
-
-           if( i_len > 0 )
-           {
-                i_blocks = _win32_dvdcss_aread( i_fd, p_base, i_len );
-                if( i_blocks < 0 )
-                {
-                    return -1;  /* idem */
-                }
 
-               i_total += i_blocks;
+    if( i_blocks_total <= 0 ) return 0;
 
-               if( i_blocks != i_len )
-               {
-                   /* we reached the end of the file or a signal interrupted
-                       the read */
-                   return i_total;
-               }
-           }
+    i_blocks_total /= DVDCSS_BLOCK_SIZE;
 
-           p_iovec++;
-       }
+    if( WIN2K )
+    {
+        unsigned long int i_bytes;
+        if( !ReadFile( (HANDLE)i_fd, p_tmp_buffer,
+                       i_blocks_total * DVDCSS_BLOCK_SIZE, &i_bytes, NULL ) )
+        {
+            return -1;
+            /* The read failed... too bad.
+               As in the posix spec the file postition is left
+               unspecified after a failure */
+        }
+        i_blocks = i_bytes / DVDCSS_BLOCK_SIZE;
+    }
+    else /* Win9x */
+    {
+        i_blocks = _win32_dvdcss_aread( i_fd, p_tmp_buffer, i_blocks_total );
+        if( i_blocks < 0 )
+        {
+            return -1;  /* idem */
+        }
     }
 
-    return i_total;
+    /* We just have to copy the content of the temp buffer into the iovecs */
+    i_index = 0;
+    i_blocks_total = i_blocks;
+    while( i_blocks_total > 0 )
+    {
+        memcpy( p_iovec[i_index].iov_base,
+                &p_tmp_buffer[(i_blocks - i_blocks_total) * DVDCSS_BLOCK_SIZE],
+                p_iovec[i_index].iov_len );
+        /* if we read less blocks than asked, we'll just end up copying
+           garbage, this isn't an issue as we return the number of
+           blocks actually read */
+        i_blocks_total -= ( p_iovec[i_index].iov_len / DVDCSS_BLOCK_SIZE );
+        i_index++;
+    } 
+
+    return i_blocks;
 }
 
 /*****************************************************************************
@@ -596,7 +640,7 @@ static int _win32_dvdcss_aopen( char c_drive, dvdcss_handle dvdcss )
     int i, j, i_hostadapters;
     long (*lpGetSupport)( void );
     long (*lpSendCommand)( void* );
-    
+     
     hASPI = LoadLibrary( "wnaspi32.dll" );
     if( hASPI == NULL )
     {
@@ -606,7 +650,7 @@ static int _win32_dvdcss_aopen( char c_drive, dvdcss_handle dvdcss )
 
     (FARPROC) lpGetSupport = GetProcAddress( hASPI, "GetASPI32SupportInfo" );
     (FARPROC) lpSendCommand = GetProcAddress( hASPI, "SendASPI32Command" );
-    
     if(lpGetSupport == NULL || lpSendCommand == NULL )
     {
         _dvdcss_debug( dvdcss, "unable to get aspi function pointers" );
@@ -665,8 +709,8 @@ static int _win32_dvdcss_aopen( char c_drive, dvdcss_handle dvdcss )
 
             lpSendCommand( (void*) &srbDiskInfo );
 
-            if( srbDiskInfo.SRB_Status == SS_COMP &&
-                srbDiskInfo.SRB_Int13HDriveInfo == c_drive )
+            if( (srbDiskInfo.SRB_Status == SS_COMP) &&
+                (srbDiskInfo.SRB_Int13HDriveInfo == c_drive) )
             {
                 fd->i_sid = MAKEWORD( i, j );
                 return (int) fd;
@@ -677,8 +721,7 @@ static int _win32_dvdcss_aopen( char c_drive, dvdcss_handle dvdcss )
     free( (void*) fd );
     FreeLibrary( hASPI );
     _dvdcss_debug( dvdcss, "unable to get haid and target (aspi)" );
-
-    return( -1 );
+    return( -1 );        
 }
 
 /*****************************************************************************
@@ -728,56 +771,78 @@ static int _win32_dvdcss_aseek( int i_fd, int i_blocks, int i_method )
 static int _win32_dvdcss_aread( int i_fd, void *p_data, int i_blocks )
 {
     HANDLE hEvent;
-    DWORD dwStart, dwLen;
     struct SRB_ExecSCSICmd ssc;
     struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
 
-    memset( &ssc, 0, sizeof( ssc ) );
-
-    dwStart = fd->i_blocks;
-    dwLen = i_blocks;
-
+    /* 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( fd->i_sid );
     ssc.SRB_Target      = HIBYTE( fd->i_sid );
     ssc.SRB_SenseLen    = SENSE_LEN;
-    ssc.SRB_PostProc    = (LPVOID) hEvent;
-
-    ssc.SRB_BufLen      = dwLen * DVDCSS_BLOCK_SIZE;
+    
+    ssc.SRB_PostProc = (LPVOID) hEvent;
     ssc.SRB_BufPointer  = p_data;
     ssc.SRB_CDBLen      = 12;
-
+    
     ssc.CDBByte[0]      = 0xA8; /* RAW */
-    ssc.CDBByte[2]      = (UCHAR) dwStart >> 24;
-    ssc.CDBByte[3]      = (UCHAR) (dwStart >> 16) & 0xff;
-    ssc.CDBByte[4]      = (UCHAR) (dwStart >> 8) & 0xff;
-    ssc.CDBByte[5]      = (UCHAR) (dwStart) & 0xff;
-    ssc.CDBByte[6]      = (UCHAR) dwLen >> 24;
-    ssc.CDBByte[7]      = (UCHAR) (dwLen >> 16) & 0xff;
-    ssc.CDBByte[8]      = (UCHAR) (dwLen >> 8) & 0xff;
-    ssc.CDBByte[9]      = (UCHAR) (dwLen) & 0xff;
-
-    ResetEvent( hEvent );
-    if( fd->lpSendCommand( (void*) &ssc ) == SS_PENDING )
+    ssc.CDBByte[2]      = (UCHAR) (fd->i_blocks >> 24);
+    ssc.CDBByte[3]      = (UCHAR) (fd->i_blocks >> 16) & 0xff;
+    ssc.CDBByte[4]      = (UCHAR) (fd->i_blocks >> 8) & 0xff;
+    ssc.CDBByte[5]      = (UCHAR) (fd->i_blocks) & 0xff;
+    
+    /* We have to break down the reads into 64kb pieces (ASPI restriction) */
+    if( i_blocks > 32 )
     {
-        WaitForSingleObject( hEvent, INFINITE );
+        ssc.SRB_BufLen = 32 * DVDCSS_BLOCK_SIZE;
+        ssc.CDBByte[9] = 32;
+        fd->i_blocks  += 32;
+
+        /* Initiate transfer */  
+        ResetEvent( hEvent );
+        fd->lpSendCommand( (void*) &ssc );
+
+        /* transfer the next 64kb (_win32_dvdcss_aread is called recursively)
+         * We need to check the status of the read on return */
+        if( _win32_dvdcss_aread( i_fd, (u8*) p_data + 32 * DVDCSS_BLOCK_SIZE,
+                                 i_blocks - 32) < 0 )
+        {
+            return -1;
+        }
+    }
+    else
+    {
+        /* This is the last transfer */
+        ssc.SRB_BufLen   = i_blocks * DVDCSS_BLOCK_SIZE;
+        ssc.CDBByte[9]   = (UCHAR) i_blocks;
+        fd->i_blocks += i_blocks;
+
+        /* Initiate transfer */  
+        ResetEvent( hEvent );
+        fd->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 );
+    }
     CloseHandle( hEvent );
 
+    /* check that the transfer went as planned */
     if( ssc.SRB_Status != SS_COMP )
     {
-        return -1;
+      return -1;
     }
-        
-    fd->i_blocks += i_blocks;
 
     return i_blocks;
 }