--- a/src/libdvdcss.c
+++ b/src/libdvdcss.c
@@ -1,17 +1,18 @@
-/* libdvdcss.c: DVD reading library.
+/*****************************************************************************
+ * libdvdcss.c: DVD reading library.
+ *****************************************************************************
+ * Copyright (C) 1998-2001 VideoLAN
+ * $Id: libdvdcss.c,v 1.1 2001/12/22 00:08:13 sam Exp $
  *
- * Authors: StĂŠphane Borel <stef@via.ecp.fr>
- *          Sam Hocevar <sam@zoy.org>
- *          HĂĽkan Hjort <d95hjort@dtek.chalmers.se>
- *
- * Copyright (C) 1998-2008 VideoLAN
- * $Id$
+ * Authors: Stéphane Borel <stef@via.ecp.fr>
+ *          Samuel Hocevar <sam@zoy.org>
+ *          Hĺkan Hjort <d95hjort@dtek.chalmers.se>
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
- *
+ * 
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -20,82 +21,11 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
- */
-
-/**
- * \mainpage libdvdcss developer documentation
- *
- * \section intro Introduction
- *
- * \e libdvdcss is a simple library designed for accessing DVDs like a block
- * device without having to bother about the decryption. The important features
- * are:
- * \li portability: currently supported platforms are GNU/Linux, FreeBSD,
- *     NetBSD, OpenBSD, BSD/OS, BeOS, Windows 95/98, Windows NT/2000, MacOS X,
- *     Solaris, HP-UX and OS/2.
- * \li adaptability: unlike most similar projects, libdvdcss doesn't require
- *     the region of your drive to be set and will try its best to read from
- *     the disc even in the case of a region mismatch.
- * \li simplicity: a DVD player can be built around the \e libdvdcss API using
- *     no more than 4 or 5 library calls.
- *
- * \e libdvdcss is free software, released under the General Public License.
- * This ensures that \e libdvdcss remains free and used only with free
- * software.
- *
- * \section api The libdvdcss API
- *
- * The complete \e libdvdcss programming interface is documented in the
- * dvdcss.h file.
- *
- * \section env Environment variables
- *
- * Some environment variables can be used to change the behaviour of
- * \e libdvdcss without having to modify the program which uses it. These
- * variables are:
- *
- * \li \b DVDCSS_VERBOSE: sets the verbosity level.
- *     - \c 0 outputs no messages at all.
- *     - \c 1 outputs error messages to stderr.
- *     - \c 2 outputs error messages and debug messages to stderr.
- *
- * \li \b DVDCSS_METHOD: sets the authentication and decryption method
- *     that \e libdvdcss will use to read scrambled discs. Can be one
- *     of \c title, \c key or \c disc.
- *     - \c key is the default method. \e libdvdcss will use a set of
- *       calculated player keys to try and get the disc key. This can fail
- *       if the drive does not recognize any of the player keys.
- *     - \c disc is a fallback method when \c key has failed. Instead of
- *       using player keys, \e libdvdcss will crack the disc key using
- *       a brute force algorithm. This process is CPU intensive and requires
- *       64 MB of memory to store temporary data.
- *     - \c title is the fallback when all other methods have failed. It does
- *       not rely on a key exchange with the DVD drive, but rather uses a
- *       crypto attack to guess the title key. On rare cases this may fail
- *       because there is not enough encrypted data on the disc to perform
- *       a statistical attack, but in the other hand it is the only way to
- *       decrypt a DVD stored on a hard disc, or a DVD with the wrong region
- *       on an RPC2 drive.
- *
- * \li \b DVDCSS_RAW_DEVICE: specify the raw device to use. Exact usage will
- *     depend on your operating system, the Linux utility to set up raw devices
- *     is \c raw(8) for instance. Please note that on most operating systems,
- *     using a raw device requires highly aligned buffers: Linux requires a
- *     2048 bytes alignment (which is the size of a DVD sector).
- *
- * \li \b DVDCSS_CACHE: specify a directory in which to store title key
- *     values. This will speed up descrambling of DVDs which are in the
- *     cache. The DVDCSS_CACHE directory is created if it does not exist,
- *     and a subdirectory is created named after the DVD's title or
- *     manufacturing date. If DVDCSS_CACHE is not set or is empty, \e libdvdcss
- *     will use the default value which is "${HOME}/.dvdcss/" under Unix and
- *     "C:\Documents and Settings\$USER\Application Data\dvdcss\" under Win32.
- *     The special value "off" disables caching.
- */
-
-/*
+ *****************************************************************************/
+
+/*****************************************************************************
  * Preamble
- */
+ *****************************************************************************/
 #include "config.h"
 
 #include <stdio.h>
@@ -103,112 +33,81 @@
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#ifdef HAVE_SYS_PARAM_H
-#   include <sys/param.h>
-#endif
-#ifdef HAVE_PWD_H
-#   include <pwd.h>
-#endif
 #include <fcntl.h>
-#include <errno.h>
 
 #ifdef HAVE_UNISTD_H
 #   include <unistd.h>
 #endif
 
-#ifdef HAVE_LIMITS_H
-#   include <limits.h>
-#endif
-
-#ifdef HAVE_DIRECT_H
-#   include <direct.h>
-#endif
-
-#include "dvdcss/dvdcss.h"
+#if defined( WIN32 )
+#   include <io.h>                                                 /* read() */
+#else
+#   include <sys/uio.h>                                      /* struct iovec */
+#endif
+
+#if defined( WIN32 )
+#   include "input_iovec.h"
+#endif
+
+#include "dvdcss.h"
 
 #include "common.h"
 #include "css.h"
 #include "libdvdcss.h"
+#include "csstables.h"
 #include "ioctl.h"
-#include "device.h"
-
-/**
- * \brief Symbol for version checks.
- *
- * The name of this symbol contains the library major number, which makes it
- * easy to check which \e libdvdcss development headers are installed on the
- * system with tools such as autoconf.
- *
- * The variable itself contains the exact version number of the library,
- * which can be useful for specific feature needs.
- */
-char * dvdcss_interface_2 = VERSION;
-
-/**
- * \brief Open a DVD device or directory and return a dvdcss instance.
- *
- * \param psz_target a string containing the target name, for instance
- *        "/dev/hdc" or "E:".
- * \return a handle to a dvdcss instance or NULL on error.
- *
- * Initialize the \e libdvdcss library and open the requested DVD device or
- * directory. \e libdvdcss checks whether ioctls can be performed on the disc,
- * and when possible, the disc key is retrieved.
- *
- * dvdcss_open() returns a handle to be used for all subsequent \e libdvdcss
- * calls. If an error occurred, NULL is returned.
- */
-LIBDVDCSS_EXPORT dvdcss_t dvdcss_open ( char *psz_target )
-{
-    char psz_buffer[PATH_MAX];
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int _dvdcss_open  ( dvdcss_handle, char *psz_target );
+static int _dvdcss_close ( dvdcss_handle );
+static int _dvdcss_readv ( dvdcss_handle, struct iovec *p_iovec, int i_blocks );
+
+/*****************************************************************************
+ * Local prototypes, win32 specific
+ *****************************************************************************/
+#if defined( WIN32 )
+static int _win32_dvdcss_readv  ( int i_fd, struct iovec *p_iovec,
+                                  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 );
+static int _win32_dvdcss_aread  ( int i_fd, void *p_data, int i_blocks );
+#else
+static int _dvdcss_raw_open     ( dvdcss_handle, char *psz_target );
+#endif
+
+/*****************************************************************************
+ * dvdcss_open: initialize library, open a DVD device, crack CSS key
+ *****************************************************************************/
+extern dvdcss_handle dvdcss_open ( char *psz_target )
+{
     int i_ret;
 
     char *psz_method = getenv( "DVDCSS_METHOD" );
     char *psz_verbose = getenv( "DVDCSS_VERBOSE" );
-    char *psz_cache = getenv( "DVDCSS_CACHE" );
 #ifndef WIN32
     char *psz_raw_device = getenv( "DVDCSS_RAW_DEVICE" );
 #endif
 
-    dvdcss_t dvdcss;
-
-    /*
-     *  Allocate the library structure
-     */
+    dvdcss_handle dvdcss;
+
+    /* Allocate the library structure */
     dvdcss = malloc( sizeof( struct dvdcss_s ) );
     if( dvdcss == NULL )
     {
         return NULL;
     }
 
-    /*
-     *  Initialize structure with default values
-     */
-#ifndef WIN32
-    dvdcss->i_raw_fd = -1;
-#endif
+    /* Initialize structure with default values */
     dvdcss->p_titles = NULL;
-    dvdcss->psz_device = (char *)strdup( psz_target );
     dvdcss->psz_error = "no error";
-    dvdcss->i_method = DVDCSS_METHOD_KEY;
-    dvdcss->psz_cachefile[0] = '\0';
+    dvdcss->i_method = DVDCSS_METHOD_TITLE;
     dvdcss->b_debug = 0;
-    dvdcss->b_errors = 0;
-
-    /*
-     *  Find verbosity from DVDCSS_VERBOSE environment variable
-     */
-    if( psz_verbose != NULL )
-    {
-        int i = atoi( psz_verbose );
-
-        if( i >= 2 ) dvdcss->b_debug = i;
-        if( i >= 1 ) dvdcss->b_errors = 1;
-    }
-
-    /*
-     *  Find method from DVDCSS_METHOD environment variable
-     */
+    dvdcss->b_errors = 1;
+
+    /* Find method from DVDCSS_METHOD environment variable */
     if( psz_method != NULL )
     {
         if( !strncmp( psz_method, "key", 4 ) )
@@ -225,316 +124,69 @@
         }
         else
         {
-            print_error( dvdcss, "unknown decrypt method, please choose "
-                                 "from 'title', 'key' or 'disc'" );
-            free( dvdcss->psz_device );
+            _dvdcss_error( dvdcss, "unknown decrypt method, please choose "
+                                   "from 'title', 'key' or 'disc'" );
             free( dvdcss );
             return NULL;
         }
     }
 
-    /*
-     *  If DVDCSS_CACHE was not set, try to guess a default value
-     */
-    if( psz_cache == NULL || psz_cache[0] == '\0' )
-    {
-#ifdef HAVE_DIRECT_H
-        typedef HRESULT( WINAPI *SHGETFOLDERPATH )
-                       ( HWND, int, HANDLE, DWORD, LPTSTR );
-
-#   define CSIDL_FLAG_CREATE 0x8000
-#   define CSIDL_APPDATA 0x1A
-#   define SHGFP_TYPE_CURRENT 0
-
-        char psz_home[MAX_PATH];
-        HINSTANCE p_dll;
-        SHGETFOLDERPATH p_getpath;
-
-        *psz_home = '\0';
-
-        /* Load the shfolder dll to retrieve SHGetFolderPath */
-        p_dll = LoadLibrary( "shfolder.dll" );
-        if( p_dll )
-        {
-            p_getpath = (void*)GetProcAddress( p_dll, "SHGetFolderPathA" );
-            if( p_getpath )
-            {
-                /* Get the "Application Data" folder for the current user */
-                if( p_getpath( NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE,
-                               NULL, SHGFP_TYPE_CURRENT, psz_home ) == S_OK )
-                {
-                    FreeLibrary( p_dll );
-                }
-                else
-                {
-                    *psz_home = '\0';
-                }
-            }
-            FreeLibrary( p_dll );
-        }
-
-        /* Cache our keys in
-         * C:\Documents and Settings\$USER\Application Data\dvdcss\ */
-        if( *psz_home )
-        {
-            snprintf( psz_buffer, PATH_MAX, "%s/dvdcss", psz_home );
-            psz_buffer[PATH_MAX-1] = '\0';
-            psz_cache = psz_buffer;
-        }
-#else
-        char *psz_home = NULL;
-#   ifdef HAVE_PWD_H
-        struct passwd *p_pwd;
-
-        /* Try looking in password file for home dir. */
-        p_pwd = getpwuid(getuid());
-        if( p_pwd )
-        {
-            psz_home = p_pwd->pw_dir;
-        }
-#   endif
-
-        if( psz_home == NULL )
-        {
-            psz_home = getenv( "HOME" );
-        }
-        if( psz_home == NULL )
-        {
-            psz_home = getenv( "USERPROFILE" );
-        }
-
-        /* Cache our keys in ${HOME}/.dvdcss/ */
-        if( psz_home )
-        {
-            snprintf( psz_buffer, PATH_MAX, "%s/.dvdcss", psz_home );
-            psz_buffer[PATH_MAX-1] = '\0';
-            psz_cache = psz_buffer;
-        }
-#endif
-    }
-
-    /*
-     *  Find cache dir from the DVDCSS_CACHE environment variable
-     */
-    if( psz_cache != NULL )
-    {
-        if( psz_cache[0] == '\0' || !strcmp( psz_cache, "off" ) )
-        {
-            psz_cache = NULL;
-        }
-        /* Check that we can add the ID directory and the block filename */
-        else if( strlen( psz_cache ) + 1 + 32 + 1 + (KEY_SIZE * 2) + 10 + 1
-                  > PATH_MAX )
-        {
-            print_error( dvdcss, "cache directory name is too long" );
-            psz_cache = NULL;
-        }
-    }
-
-    /*
-     *  Open device
-     */
-    _dvdcss_check( dvdcss );
-    i_ret = _dvdcss_open( dvdcss );
+    /* Find verbosity from DVDCSS_VERBOSE environment variable */
+    if( psz_verbose != NULL )
+    {
+        switch( atoi( psz_verbose ) )
+        {
+        case 0:
+            dvdcss->b_errors = 0;
+            break;
+        case 1:
+            break;
+        case 2:
+            dvdcss->b_debug = 1;
+            break;
+        default:
+            _dvdcss_error( dvdcss, "unknown verbose level, please choose "
+                                   "from '0', '1' or '2'" );
+            free( dvdcss );
+            return NULL;
+            break;
+        }
+    }
+
+    /* Open device */
+    i_ret = _dvdcss_open( dvdcss, psz_target );
     if( i_ret < 0 )
     {
-        free( dvdcss->psz_device );
         free( dvdcss );
         return NULL;
     }
 
-    dvdcss->b_scrambled = 1; /* Assume the worst */
-    dvdcss->b_ioctls = _dvdcss_use_ioctls( dvdcss );
-
-    if( dvdcss->b_ioctls )
-    {
-        i_ret = _dvdcss_test( dvdcss );
+    i_ret = CSSTest( dvdcss );
+    if( i_ret < 0 )
+    {
+        _dvdcss_error( dvdcss, "CSS test failed" );
+        /* Disable the CSS ioctls and hope that it works? */
+        dvdcss->b_ioctls = 0;
+        dvdcss->b_encrypted = 1;
+    }
+    else
+    {
+        dvdcss->b_ioctls = 1;
+        dvdcss->b_encrypted = i_ret;
+    }
+
+    /* If disc is CSS protected and the ioctls work, authenticate the drive */
+    if( dvdcss->b_encrypted && dvdcss->b_ioctls )
+    {
+        i_ret = CSSGetDiscKey( dvdcss );
+
         if( i_ret < 0 )
         {
-            /* Disable the CSS ioctls and hope that it works? */
-            print_debug( dvdcss,
-                         "could not check whether the disc was scrambled" );
-            dvdcss->b_ioctls = 0;
-        }
-        else
-        {
-            print_debug( dvdcss, i_ret ? "disc is scrambled"
-                                       : "disc is unscrambled" );
-            dvdcss->b_scrambled = i_ret;
-        }
-    }
-
-    /* If disc is CSS protected and the ioctls work, authenticate the drive */
-    if( dvdcss->b_scrambled && dvdcss->b_ioctls )
-    {
-        i_ret = _dvdcss_disckey( dvdcss );
-
-        if( i_ret < 0 )
-        {
-            print_debug( dvdcss, "could not get disc key" );
-        }
-    }
-    else
-    {
-        memset( dvdcss->css.p_disc_key, 0, KEY_SIZE );
-    }
-
-    /* If the cache is enabled, write the cache directory tag */
-    if( psz_cache )
-    {
-        char *psz_tag = "Signature: 8a477f597d28d172789f06886806bc55\r\n"
-            "# This file is a cache directory tag created by libdvdcss.\r\n"
-            "# For information about cache directory tags, see:\r\n"
-            "#   http://www.brynosaurus.com/cachedir/\r\n";
-        char psz_tagfile[PATH_MAX + 1 + 12 + 1];
-        int i_fd;
-
-        sprintf( psz_tagfile, "%s/CACHEDIR.TAG", psz_cache );
-        i_fd = open( psz_tagfile, O_RDWR|O_CREAT, 0644 );
-        if( i_fd >= 0 )
-        {
-            write( i_fd, psz_tag, strlen(psz_tag) );
-            close( i_fd );
-        }
-    }
-
-    /* If the cache is enabled, extract a unique disc ID */
-    if( psz_cache )
-    {
-        uint8_t p_sector[DVDCSS_BLOCK_SIZE];
-        char psz_debug[PATH_MAX + 30];
-        char psz_key[1 + KEY_SIZE * 2 + 1];
-        char *psz_title;
-        uint8_t *psz_serial;
-        int i;
-
-        /* We read sector 0. If it starts with 0x000001ba (BE), we are
-         * reading a VOB file, and we should not cache anything. */
-
-        i_ret = dvdcss->pf_seek( dvdcss, 0 );
-        if( i_ret != 0 )
-        {
-            goto nocache;
-        }
-
-        i_ret = dvdcss->pf_read( dvdcss, p_sector, 1 );
-        if( i_ret != 1 )
-        {
-            goto nocache;
-        }
-
-        if( p_sector[0] == 0x00 && p_sector[1] == 0x00
-             && p_sector[2] == 0x01 && p_sector[3] == 0xba )
-        {
-            goto nocache;
-        }
-
-        /* The data we are looking for is at sector 16 (32768 bytes):
-         *  - offset 40: disc title (32 uppercase chars)
-         *  - offset 813: manufacturing date + serial no (16 digits) */
-
-        i_ret = dvdcss->pf_seek( dvdcss, 16 );
-        if( i_ret != 16 )
-        {
-            goto nocache;
-        }
-
-        i_ret = dvdcss->pf_read( dvdcss, p_sector, 1 );
-        if( i_ret != 1 )
-        {
-            goto nocache;
-        }
-
-        /* Get the disc title */
-        psz_title = (char *)p_sector + 40;
-        psz_title[32] = '\0';
-
-        for( i = 0 ; i < 32 ; i++ )
-        {
-            if( psz_title[i] <= ' ' )
-            {
-                psz_title[i] = '\0';
-                break;
-            }
-            else if( psz_title[i] == '/' || psz_title[i] == '\\' )
-            {
-                psz_title[i] = '-';
-            }
-        }
-
-        /* Get the date + serial */
-        psz_serial = p_sector + 813;
-        psz_serial[16] = '\0';
-
-        /* Check that all characters are digits, otherwise convert. */
-        for( i = 0 ; i < 16 ; i++ )
-        {
-            if( psz_serial[i] < '0' || psz_serial[i] > '9' )
-            {
-                char psz_tmp[16 + 1];
-                sprintf( psz_tmp,
-                         "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
-                         psz_serial[0], psz_serial[1], psz_serial[2],
-                         psz_serial[3], psz_serial[4], psz_serial[5],
-                         psz_serial[6], psz_serial[7] );
-                memcpy( psz_serial, psz_tmp, 16 );
-                break;
-            }
-        }
-
-        /* Get disk key, since some discs have got same title, manufacturing
-         * date and serial number, but different keys */
-        if( dvdcss->b_scrambled )
-        {
-             psz_key[0] = '-';
-             for( i = 0; i < KEY_SIZE; i++ )
-             {
-                 sprintf( &psz_key[1+i*2], "%.2x", dvdcss->css.p_disc_key[i] );
-             }
-             psz_key[1 + KEY_SIZE * 2] = '\0';
-        }
-        else
-        {
-             psz_key[0] = 0;
-        }
-
-        /* We have a disc name or ID, we can create the cache dir */
-        i = sprintf( dvdcss->psz_cachefile, "%s", psz_cache );
-#if !defined( WIN32 ) || defined( SYS_CYGWIN )
-        i_ret = mkdir( dvdcss->psz_cachefile, 0755 );
-#else
-        i_ret = mkdir( dvdcss->psz_cachefile );
-#endif
-        if( i_ret < 0 && errno != EEXIST )
-        {
-            print_error( dvdcss, "failed creating cache directory" );
-            dvdcss->psz_cachefile[0] = '\0';
-            goto nocache;
-        }
-
-        i += sprintf( dvdcss->psz_cachefile + i, "/%s-%s%s", psz_title,
-                      psz_serial, psz_key );
-#if !defined( WIN32 ) || defined( SYS_CYGWIN )
-        i_ret = mkdir( dvdcss->psz_cachefile, 0755 );
-#else
-        i_ret = mkdir( dvdcss->psz_cachefile );
-#endif
-        if( i_ret < 0 && errno != EEXIST )
-        {
-            print_error( dvdcss, "failed creating cache subdirectory" );
-            dvdcss->psz_cachefile[0] = '\0';
-            goto nocache;
-        }
-        i += sprintf( dvdcss->psz_cachefile + i, "/");
-
-        /* Pointer to the filename we will use. */
-        dvdcss->psz_block = dvdcss->psz_cachefile + i;
-
-        sprintf( psz_debug, "using CSS key cache dir: %s",
-                            dvdcss->psz_cachefile );
-        print_debug( dvdcss, psz_debug );
-    }
-    nocache:
+            _dvdcss_close( dvdcss );
+            free( dvdcss );
+            return NULL;
+        }
+    }
 
 #ifndef WIN32
     if( psz_raw_device != NULL )
@@ -543,186 +195,189 @@
     }
 #endif
 
-    /* Seek at the beginning, just for safety. */
-    dvdcss->pf_seek( dvdcss, 0 );
-
     return dvdcss;
 }
 
-/**
- * \brief Return a string containing the latest error that occurred in the
- *        given \e libdvdcss instance.
- *
- * \param dvdcss a \e libdvdcss instance.
- * \return a null-terminated string containing the latest error message.
- *
- * This function returns a constant string containing the latest error that
- * occurred in \e libdvdcss. It can be used to format error messages at your
- * convenience in your application.
- */
-LIBDVDCSS_EXPORT char * dvdcss_error ( dvdcss_t dvdcss )
+/*****************************************************************************
+ * dvdcss_error: return the last libdvdcss error message
+ *****************************************************************************/
+extern char * dvdcss_error ( dvdcss_handle dvdcss )
 {
     return dvdcss->psz_error;
 }
 
-/**
- * \brief Seek in the disc and change the current key if requested.
- *
- * \param dvdcss a \e libdvdcss instance.
- * \param i_blocks an absolute block offset to seek to.
- * \param i_flags #DVDCSS_NOFLAGS, optionally ored with one of #DVDCSS_SEEK_KEY
- *        or #DVDCSS_SEEK_MPEG.
- * \return the new position in blocks, or a negative value in case an error
- *         happened.
- *
- * This function seeks to the requested position, in logical blocks.
- *
- * You typically set \p i_flags to #DVDCSS_NOFLAGS when seeking in a .IFO.
- *
- * If #DVDCSS_SEEK_MPEG is specified in \p i_flags and if \e libdvdcss finds it
- * reasonable to do so (ie, if the dvdcss method is not "title"), the current
- * title key will be checked and a new one will be calculated if necessary.
- * This flag is typically used when reading data from a VOB.
- *
- * If #DVDCSS_SEEK_KEY is specified, the title key will be always checked,
- * even with the "title" method. This is equivalent to using the now
- * deprecated dvdcss_title() call. This flag is typically used when seeking
- * in a new title.
- */
-LIBDVDCSS_EXPORT int dvdcss_seek ( dvdcss_t dvdcss, int i_blocks, int i_flags )
+/*****************************************************************************
+ * dvdcss_seek: seek into the device
+ *****************************************************************************/
+extern int dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks, int i_flags )
 {
     /* title cracking method is too slow to be used at each seek */
     if( ( ( i_flags & DVDCSS_SEEK_MPEG )
-             && ( dvdcss->i_method != DVDCSS_METHOD_TITLE ) )
+             && ( dvdcss->i_method != DVDCSS_METHOD_TITLE ) ) 
        || ( i_flags & DVDCSS_SEEK_KEY ) )
     {
         /* check the title key */
-        if( _dvdcss_title( dvdcss, i_blocks ) )
+        if( dvdcss_title( dvdcss, i_blocks ) ) 
         {
             return -1;
         }
     }
 
-    return dvdcss->pf_seek( dvdcss, i_blocks );
-}
-
-/**
- * \brief Read from the disc and decrypt data if requested.
- *
- * \param dvdcss a \e libdvdcss instance.
- * \param p_buffer a buffer that will contain the data read from the disc.
- * \param i_blocks the amount of blocks to read.
- * \param i_flags #DVDCSS_NOFLAGS, optionally ored with #DVDCSS_READ_DECRYPT.
- * \return the amount of blocks read, or a negative value in case an
- *         error happened.
- *
- * This function reads \p i_blocks logical blocks from the DVD.
- *
- * You typically set \p i_flags to #DVDCSS_NOFLAGS when reading data from a
- * .IFO file on the DVD.
- *
- * If #DVDCSS_READ_DECRYPT is specified in \p i_flags, dvdcss_read() will
- * automatically decrypt scrambled sectors. This flag is typically used when
- * reading data from a .VOB file on the DVD. It has no effect on unscrambled
- * discs or unscrambled sectors, and can be safely used on those.
- *
- * \warning dvdcss_read() expects to be able to write \p i_blocks *
- *          #DVDCSS_BLOCK_SIZE bytes in \p p_buffer.
- */
-LIBDVDCSS_EXPORT int dvdcss_read ( dvdcss_t dvdcss, void *p_buffer,
-                                          int i_blocks,
-                                          int i_flags )
+    return _dvdcss_seek( dvdcss, i_blocks );
+}
+
+/*****************************************************************************
+ * dvdcss_title: crack or decrypt the current title key if needed
+ *****************************************************************************
+ * This function should only be called by dvdcss_seek and should eventually
+ * not be external if possible.
+ *****************************************************************************/
+extern int dvdcss_title ( dvdcss_handle dvdcss, int i_block )
+{
+    dvd_title_t *p_title;
+    dvd_title_t *p_newtitle;
+    int          i_ret;
+
+    if( ! dvdcss->b_encrypted )
+    {
+        return 0;
+    }
+
+    /* Check if we've already cracked this key */
+    p_title = dvdcss->p_titles;
+    while( p_title != NULL
+            && p_title->p_next != NULL
+            && p_title->p_next->i_startlb <= i_block )
+    {
+        p_title = p_title->p_next;
+    }
+
+    if( p_title != NULL
+         && p_title->i_startlb == i_block )
+    {
+        /* We've already cracked this key, nothing to do */
+        memcpy( dvdcss->css.p_title_key, p_title->p_key, sizeof(dvd_key_t) );
+        return 0;
+    }
+
+    /* Crack or decrypt CSS title key for current VTS */
+    i_ret = CSSGetTitleKey( dvdcss, i_block );
+
+    if( i_ret < 0 )
+    {
+        _dvdcss_error( dvdcss, "fatal error in vts css key" );
+        return i_ret;
+    }
+    else if( i_ret > 0 )
+    {
+        _dvdcss_error( dvdcss, "decryption unavailable" );
+        return -1;
+    }
+
+    /* Find our spot in the list */
+    p_newtitle = NULL;
+    p_title = dvdcss->p_titles;
+    while( ( p_title != NULL ) && ( p_title->i_startlb < i_block ) )
+    {
+        p_newtitle = p_title;
+        p_title = p_title->p_next;
+    }
+
+    /* 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, dvdcss->css.p_title_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;
+}
+
+#define Pkey dvdcss->css.p_title_key
+/*****************************************************************************
+ * dvdcss_read: read data from the device, decrypt if requested
+ *****************************************************************************/
+extern int dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer,
+                                               int i_blocks,
+                                               int i_flags )
 {
     int i_ret, i_index;
 
-    i_ret = dvdcss->pf_read( dvdcss, p_buffer, i_blocks );
+    i_ret = _dvdcss_read( dvdcss, p_buffer, i_blocks );
 
     if( i_ret <= 0
-         || !dvdcss->b_scrambled
+         || !dvdcss->b_encrypted
          || !(i_flags & DVDCSS_READ_DECRYPT) )
     {
         return i_ret;
     }
 
-    if( ! memcmp( dvdcss->css.p_title_key, "\0\0\0\0\0", 5 ) )
-    {
-        /* For what we believe is an unencrypted title,
-         * check that there are no encrypted blocks */
+    /* For what we believe is an unencrypted title, 
+       check that there are no encrypted blocks */
+    if( !( Pkey[0] | Pkey[1] | Pkey[2] | Pkey[3] | Pkey[4] ) ) 
+    {
         for( i_index = i_ret; i_index; i_index-- )
         {
-            if( ((uint8_t*)p_buffer)[0x14] & 0x30 )
+            if( ((u8*)p_buffer)[0x14] & 0x30 )
             {
-                print_error( dvdcss, "no key but found encrypted block" );
+                _dvdcss_error( dvdcss, "no key but found encrypted block" );
                 /* Only return the initial range of unscrambled blocks? */
+                i_ret = i_index;
                 /* or fail completely? return 0; */
-                break;
             }
-            p_buffer = (void *) ((uint8_t *)p_buffer + DVDCSS_BLOCK_SIZE);
-        }
-    }
-    else
-    {
-        /* Decrypt the blocks we managed to read */
-        for( i_index = i_ret; i_index; i_index-- )
-        {
-            _dvdcss_unscramble( dvdcss->css.p_title_key, p_buffer );
-            ((uint8_t*)p_buffer)[0x14] &= 0x8f;
-            p_buffer = (void *) ((uint8_t *)p_buffer + DVDCSS_BLOCK_SIZE);
-        }
+            (u8*)p_buffer += DVDCSS_BLOCK_SIZE;
+        }
+    }
+
+    /* Decrypt the blocks we managed to read */
+    for( i_index = i_ret; i_index; i_index-- )
+    {
+        CSSDescrambleSector( dvdcss->css.p_title_key, p_buffer );
+        ((u8*)p_buffer)[0x14] &= 0x8f;
+        (u8*)p_buffer += DVDCSS_BLOCK_SIZE;
     }
 
     return i_ret;
 }
 
-/**
- * \brief Read from the disc into multiple buffers and decrypt data if
- *        requested.
- *
- * \param dvdcss a \e libdvdcss instance.
- * \param p_iovec a pointer to an array of iovec structures that will contain
- *        the data read from the disc.
- * \param i_blocks the amount of blocks to read.
- * \param i_flags #DVDCSS_NOFLAGS, optionally ored with #DVDCSS_READ_DECRYPT.
- * \return the amount of blocks read, or a negative value in case an
- *         error happened.
- *
- * This function reads \p i_blocks logical blocks from the DVD and writes them
- * to an array of iovec structures.
- *
- * You typically set \p i_flags to #DVDCSS_NOFLAGS when reading data from a
- * .IFO file on the DVD.
- *
- * If #DVDCSS_READ_DECRYPT is specified in \p i_flags, dvdcss_readv() will
- * automatically decrypt scrambled sectors. This flag is typically used when
- * reading data from a .VOB file on the DVD. It has no effect on unscrambled
- * discs or unscrambled sectors, and can be safely used on those.
- *
- * \warning dvdcss_readv() expects to be able to write \p i_blocks *
- *          #DVDCSS_BLOCK_SIZE bytes in the buffers pointed by \p p_iovec.
- *          Moreover, all iov_len members of the iovec structures should be
- *          multiples of #DVDCSS_BLOCK_SIZE.
- */
-LIBDVDCSS_EXPORT int dvdcss_readv ( dvdcss_t dvdcss, void *p_iovec,
-                                           int i_blocks,
-                                           int i_flags )
-{
-    struct iovec *_p_iovec = (struct iovec *)p_iovec;
+/*****************************************************************************
+ * dvdcss_readv: read data to an iovec structure, decrypt if requested
+ *****************************************************************************/
+extern int dvdcss_readv ( dvdcss_handle dvdcss, void *p_iovec,
+                                                int i_blocks,
+                                                int i_flags )
+{
+#define P_IOVEC ((struct iovec*)p_iovec)
     int i_ret, i_index;
     void *iov_base;
     size_t iov_len;
 
-    i_ret = dvdcss->pf_readv( dvdcss, _p_iovec, i_blocks );
+    i_ret = _dvdcss_readv( dvdcss, P_IOVEC, i_blocks );
 
     if( i_ret <= 0
-         || !dvdcss->b_scrambled
+         || !dvdcss->b_encrypted
          || !(i_flags & DVDCSS_READ_DECRYPT) )
     {
         return i_ret;
     }
 
+
     /* Initialize loop for decryption */
-    iov_base = _p_iovec->iov_base;
-    iov_len = _p_iovec->iov_len;
+    iov_base = P_IOVEC->iov_base;
+    iov_len = P_IOVEC->iov_len;
 
     /* Decrypt the blocks we managed to read */
     for( i_index = i_ret; i_index; i_index-- )
@@ -735,32 +390,27 @@
 
         while( iov_len == 0 )
         {
-            _p_iovec++;
-            iov_base = _p_iovec->iov_base;
-            iov_len = _p_iovec->iov_len;
-        }
-
-        _dvdcss_unscramble( dvdcss->css.p_title_key, iov_base );
-        ((uint8_t*)iov_base)[0x14] &= 0x8f;
-
-        iov_base = (void *) ((uint8_t*)iov_base + DVDCSS_BLOCK_SIZE);
-        iov_len -= DVDCSS_BLOCK_SIZE;
+            P_IOVEC++;
+            iov_base = P_IOVEC->iov_base;
+            iov_len = P_IOVEC->iov_len;
+        }
+
+        CSSDescrambleSector( dvdcss->css.p_title_key, iov_base );
+        ((u8*)iov_base)[0x14] &= 0x8f;
+
+        (u8*)iov_base += DVDCSS_BLOCK_SIZE;
+        (u8*)iov_len -= DVDCSS_BLOCK_SIZE;
     }
 
     return i_ret;
-}
-
-/**
- * \brief Close the DVD and clean up the library.
- *
- * \param dvdcss a \e libdvdcss instance.
- * \return zero in case of success, a negative value otherwise.
- *
- * This function closes the DVD device and frees all the memory allocated
- * by \e libdvdcss. On return, the #dvdcss_t is invalidated and may not be
- * used again.
- */
-LIBDVDCSS_EXPORT int dvdcss_close ( dvdcss_t dvdcss )
+#undef P_IOVEC
+}
+#undef Pkey
+
+/*****************************************************************************
+ * dvdcss_close: close the DVD device and clean up the library
+ *****************************************************************************/
+extern int dvdcss_close ( dvdcss_handle dvdcss )
 {
     dvd_title_t *p_title;
     int i_ret;
@@ -781,31 +431,500 @@
         return i_ret;
     }
 
-    free( dvdcss->psz_device );
     free( dvdcss );
 
     return 0;
 }
 
-/*
- *  Deprecated. See dvdcss_seek().
- */
-#undef dvdcss_title
-LIBDVDCSS_EXPORT int dvdcss_title ( dvdcss_t dvdcss, int i_block )
-{
-    return _dvdcss_title( dvdcss, i_block );
-}
-
-/**
- * \brief Return 1 if the DVD is scrambled, 0 otherwise.
+/* Following functions are local */
+
+static int _dvdcss_open ( dvdcss_handle dvdcss, char *psz_target )
+{
+#if defined( WIN32 )
+    if( WIN2K )
+    {
+        char psz_dvd[7];
+        _snprintf( psz_dvd, 7, "\\\\.\\%c:", psz_target[0] );
+        (HANDLE) dvdcss->i_fd =
+                CreateFile( psz_dvd, GENERIC_READ | GENERIC_WRITE,
+                                FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                NULL, OPEN_EXISTING, 0, NULL );
+        if( (HANDLE) dvdcss->i_fd == INVALID_HANDLE_VALUE )
+        {
+            _dvdcss_error( dvdcss, "failed opening device" );
+            return -1;
+        }
+    }
+    else
+    {
+        dvdcss->i_fd = _win32_dvdcss_aopen( psz_target[0], dvdcss );
+        if( dvdcss->i_fd == -1 )
+        {
+            _dvdcss_error( dvdcss, "failed opening device" );
+            return -1;
+        }
+    }
+
+    /* initialise readv temporary buffer */
+    dvdcss->p_readv_buffer   = NULL;
+    dvdcss->i_readv_buf_size = 0;
+
+#else
+    dvdcss->i_fd = dvdcss->i_read_fd = open( psz_target, 0 );
+
+    if( dvdcss->i_fd == -1 )
+    {
+        _dvdcss_error( dvdcss, "failed opening device" );
+        return -1;
+    }
+
+#endif
+
+    return 0;
+}
+
+#ifndef WIN32
+static int _dvdcss_raw_open ( dvdcss_handle dvdcss, char *psz_target )
+{
+    dvdcss->i_raw_fd = open( psz_target, 0 );
+
+    if( dvdcss->i_raw_fd == -1 )
+    {
+        _dvdcss_error( dvdcss, "failed opening raw device, continuing" );
+        return -1;
+    }
+    else
+    {
+        dvdcss->i_read_fd = dvdcss->i_raw_fd;
+    }
+
+    return 0;
+}
+#endif
+
+static int _dvdcss_close ( dvdcss_handle dvdcss )
+{
+#if defined( WIN32 )
+    if( WIN2K )
+    {
+        CloseHandle( (HANDLE) dvdcss->i_fd );
+    }
+    else
+    {
+        _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 );
+
+    if( dvdcss->i_raw_fd >= 0 )
+    {
+        close( dvdcss->i_raw_fd );
+        dvdcss->i_raw_fd = -1;
+    }
+
+#endif
+
+    return 0;
+}
+
+int _dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
+{
+#if defined( WIN32 )
+    dvdcss->i_seekpos = i_blocks;
+
+    if( WIN2K )
+    {
+        LARGE_INTEGER li_read;
+
+#ifndef INVALID_SET_FILE_POINTER
+#define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
+
+        li_read.QuadPart = (LONGLONG)i_blocks * DVDCSS_BLOCK_SIZE;
+
+        li_read.LowPart = SetFilePointer( (HANDLE) dvdcss->i_fd,
+                                          li_read.LowPart,
+                                          &li_read.HighPart, FILE_BEGIN );
+        if( (li_read.LowPart == INVALID_SET_FILE_POINTER)
+            && GetLastError() != NO_ERROR)
+        {
+            li_read.QuadPart = -DVDCSS_BLOCK_SIZE;
+        }
+
+        li_read.QuadPart /= DVDCSS_BLOCK_SIZE;
+        return (int)li_read.QuadPart;
+    }
+    else
+    {
+        return ( _win32_dvdcss_aseek( dvdcss->i_fd, i_blocks, SEEK_SET ) );
+    }
+#else
+    off_t   i_read;
+
+    dvdcss->i_seekpos = i_blocks;
+
+    i_read = lseek( dvdcss->i_read_fd,
+                    (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE, SEEK_SET );
+
+    return i_read / DVDCSS_BLOCK_SIZE;
+#endif
+
+}
+
+int _dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer, int i_blocks )
+{
+#if defined( WIN32 ) 
+    if( WIN2K )
+    {
+        int i_bytes;
+
+        if( !ReadFile( (HANDLE) dvdcss->i_fd, p_buffer,
+                  i_blocks * DVDCSS_BLOCK_SIZE,
+                  (LPDWORD)&i_bytes, NULL ) )
+        {
+            return -1;
+        }
+        return i_bytes / DVDCSS_BLOCK_SIZE;
+    }
+    else
+    {
+        return _win32_dvdcss_aread( dvdcss->i_fd, p_buffer, i_blocks );
+    }
+
+#else
+    int i_bytes;
+
+    i_bytes = read( dvdcss->i_read_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 )
+{
+    int i_read;
+
+#if defined( WIN32 )
+    /* 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_read_fd, p_iovec, i_blocks );
+    return i_read / DVDCSS_BLOCK_SIZE;
+
+#endif
+}
+
+
+#if defined( WIN32 )
+
+/*****************************************************************************
+ * _win32_dvdcss_readv: vectored read using ReadFile for Win2K and
+ *                      _win32_dvdcss_aread for win9x
+ *****************************************************************************/
+static int _win32_dvdcss_readv( int i_fd, struct iovec *p_iovec,
+                                int i_num_buffers, char *p_tmp_buffer )
+{
+    int i_index;
+    int i_blocks, i_blocks_total = 0;
+
+    for( i_index = i_num_buffers; i_index; i_index-- )
+    {
+        i_blocks_total += p_iovec[i_index-1].iov_len; 
+    }
+
+    if( i_blocks_total <= 0 ) return 0;
+
+    i_blocks_total /= DVDCSS_BLOCK_SIZE;
+
+    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 */
+        }
+    }
+
+    /* 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;
+}
+
+/*****************************************************************************
+ * _win32_dvdcss_aopen: open dvd drive (load aspi and init w32_aspidev
+ *                      structure)
+ *****************************************************************************/
+static int _win32_dvdcss_aopen( char c_drive, dvdcss_handle dvdcss )
+{
+    HMODULE hASPI;
+    DWORD dwSupportInfo;
+    struct w32_aspidev *fd;
+    int i, j, i_hostadapters;
+    long (*lpGetSupport)( void );
+    long (*lpSendCommand)( void* );
+     
+    hASPI = LoadLibrary( "wnaspi32.dll" );
+    if( hASPI == NULL )
+    {
+        _dvdcss_error( dvdcss, "unable to load wnaspi32.dll" );
+        return -1;
+    }
+
+    (FARPROC) lpGetSupport = GetProcAddress( hASPI, "GetASPI32SupportInfo" );
+    (FARPROC) lpSendCommand = GetProcAddress( hASPI, "SendASPI32Command" );
+ 
+    if(lpGetSupport == NULL || lpSendCommand == NULL )
+    {
+        _dvdcss_debug( dvdcss, "unable to get aspi function pointers" );
+        FreeLibrary( hASPI );
+        return -1;
+    }
+
+    dwSupportInfo = lpGetSupport();
+
+    if( HIBYTE( LOWORD ( dwSupportInfo ) ) == SS_NO_ADAPTERS )
+    {
+        _dvdcss_debug( dvdcss, "no host adapters found (aspi)" );
+        FreeLibrary( hASPI );
+        return -1;
+    }
+
+    if( HIBYTE( LOWORD ( dwSupportInfo ) ) != SS_COMP )
+    {
+        _dvdcss_error( dvdcss, "unable to initalize aspi layer" );
+        FreeLibrary( hASPI );
+        return -1;
+    }
+
+    i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) );
+    if( i_hostadapters == 0 )
+    {
+        FreeLibrary( hASPI );
+        return -1;
+    }
+
+    fd = malloc( sizeof( struct w32_aspidev ) );
+    if( fd == NULL )
+    {
+        FreeLibrary( hASPI );
+        return -1;
+    }
+
+    fd->i_blocks = 0;
+    fd->hASPI = (long) hASPI;
+    fd->lpSendCommand = lpSendCommand;
+
+    c_drive = c_drive > 'Z' ? c_drive - 'a' : c_drive - 'A';
+
+    for( i = 0; i < i_hostadapters; i++ )
+    {
+        for( j = 0; j < 15; j++ )
+        {
+            struct SRB_GetDiskInfo srbDiskInfo;
+
+            srbDiskInfo.SRB_Cmd         = SC_GET_DISK_INFO;
+            srbDiskInfo.SRB_HaId        = i;
+            srbDiskInfo.SRB_Flags       = 0;
+            srbDiskInfo.SRB_Hdr_Rsvd    = 0;
+            srbDiskInfo.SRB_Target      = j;
+            srbDiskInfo.SRB_Lun         = 0;
+
+            lpSendCommand( (void*) &srbDiskInfo );
+
+            if( (srbDiskInfo.SRB_Status == SS_COMP) &&
+                (srbDiskInfo.SRB_Int13HDriveInfo == c_drive) )
+            {
+                fd->i_sid = MAKEWORD( i, j );
+                return (int) fd;
+            }
+        }
+    }
+
+    free( (void*) fd );
+    FreeLibrary( hASPI );
+    _dvdcss_debug( dvdcss, "unable to get haid and target (aspi)" );
+    return( -1 );        
+}
+
+/*****************************************************************************
+ * _win32_dvdcss_aclose: close dvd drive (unload aspi and free w32_aspidev
+ *                       structure)
+ *****************************************************************************/
+static int _win32_dvdcss_aclose( int i_fd )
+{
+    struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
+
+    FreeLibrary( (HMODULE) fd->hASPI );
+    free( (void*) i_fd );
+
+    return 0;
+}
+
+/*****************************************************************************
+ * _win32_dvdcss_aseek: aspi version of _dvdcss_seek
+ * 
+ * returns the number of blocks read.
+ *****************************************************************************/
+static int _win32_dvdcss_aseek( int i_fd, int i_blocks, int i_method )
+{
+    int i_old_blocks;
+    char sz_buf[ DVDCSS_BLOCK_SIZE ];
+    struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
+    
+    i_old_blocks = fd->i_blocks;
+    fd->i_blocks = i_blocks;
+
+    if( _win32_dvdcss_aread( i_fd, sz_buf, 1 ) == -1 )
+    {
+        fd->i_blocks = i_old_blocks;
+        return -1;
+    }
+
+    (fd->i_blocks)--;
+
+    return fd->i_blocks;
+}
+
+/*****************************************************************************
+ * _win32_dvdcss_aread: aspi version of _dvdcss_read
  *
- * \param dvdcss a \e libdvdcss instance.
- * \return 1 if the DVD is scrambled, 0 otherwise.
- *
- * This function returns whether the DVD is scrambled.
- */
-LIBDVDCSS_EXPORT int dvdcss_is_scrambled ( dvdcss_t dvdcss )
-{
-    return dvdcss->b_scrambled;
-}
-
+ * returns the number of blocks read.
+ *****************************************************************************/
+static int _win32_dvdcss_aread( int i_fd, void *p_data, int i_blocks )
+{
+    HANDLE hEvent;
+    struct SRB_ExecSCSICmd ssc;
+    struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
+
+    /* 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_BufPointer  = p_data;
+    ssc.SRB_CDBLen      = 12;
+    
+    ssc.CDBByte[0]      = 0xA8; /* RAW */
+    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 )
+    {
+        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 i_blocks;
+}
+
+#endif
+