]> git.sesse.net Git - vlc/blob - extras/libdvdread/dvd_reader.c
-DVD access plugin is only called if specified under windows (no automatic detection)
[vlc] / extras / libdvdread / dvd_reader.c
1 /**
2  * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or (at
7  * your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/uio.h> /* readv() */
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <limits.h>
29 #include <dirent.h>
30
31 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__)
32 #define SYS_BSD 1
33 #endif
34
35 #if defined(__sun)
36 #include <sys/mnttab.h>
37 #elif defined(SYS_BSD)
38 #include <fstab.h>
39 #elif defined(__linux__)
40 #include <mntent.h>
41 #endif
42
43 #if defined(SYS_BSD)
44 typedef off_t off64_t;
45 #define lseek64 lseek
46 #define stat64 stat
47 #endif
48
49 #include "dvd_udf.h"
50 #include "dvd_reader.h"
51
52 #include "dvd_setup.h"
53
54 struct dvd_reader_s {
55     /* Basic information. */
56     int isImageFile;
57
58     /* Information required for an image file. */
59     dvd_handle dev;
60
61     /* Information required for a directory path drive. */
62     char *path_root;
63 };
64
65 struct dvd_file_s {
66     /* Basic information. */
67     dvd_reader_t *dvd;
68
69     /* Information required for an image file. */
70     uint32_t lb_start;
71     uint32_t seek_pos;
72
73     /* Information required for a directory path drive. */
74     dvd_handle title_devs[9];
75     size_t title_sizes[ 9 ];
76
77     /* Calculated at open-time, size in blocks. */
78     ssize_t filesize;
79 };
80
81 /**
82  * Open a DVD image or block device file.
83  */
84 static dvd_reader_t *DVDOpenImageFile( const char *location )
85 {
86     dvd_reader_t *dvd;
87     dvd_handle dev = 0;
88
89     dev = pf_dvd_open( (char *) location );
90     if( !dev ) {
91         fprintf( stderr, "libdvdread: Can't open %s for reading.\n",
92                          location );
93         return 0;
94     }
95
96     dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
97     if( !dvd ) return 0;
98     dvd->isImageFile = 1;
99     dvd->dev = dev;
100     dvd->path_root = 0;
101     
102     return dvd;
103 }
104
105 static dvd_reader_t *DVDOpenPath( const char *path_root )
106 {
107     dvd_reader_t *dvd;
108
109     dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
110     if( !dvd ) return 0;
111     dvd->isImageFile = 0;
112     dvd->dev = 0;
113     dvd->path_root = strdup( path_root );
114
115     return dvd;
116 }
117
118 #if defined(__sun)
119 /* /dev/rdsk/c0t6d0s0 (link to /devices/...)
120    /vol/dev/rdsk/c0t6d0/??
121    /vol/rdsk/<name> */
122 static char *sun_block2char( const char *path )
123 {
124     char *new_path;
125
126     /* Must contain "/dsk/" */ 
127     if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
128
129     /* Replace "/dsk/" with "/rdsk/" */
130     new_path = malloc( strlen(path) + 2 );
131     strcpy( new_path, path );
132     strcpy( strstr( new_path, "/dsk/" ), "" );
133     strcat( new_path, "/rdsk/" );
134     strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
135
136     return new_path;
137 }
138 #endif
139
140 #if defined(SYS_BSD)
141 /* FreeBSD /dev/(r)(a)cd0 (a is for atapi, should work without r)
142    OpenBSD /dev/rcd0c
143    NetBSD  /dev/rcd0d or /dev/rcd0c (for non x86)
144    BSD/OS  /dev/sr0 (if not mounted) or /dev/rsr0 */
145 static char *bsd_block2char( const char *path )
146 {
147     char *new_path;
148
149     /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */ 
150     if( !strncmp( path, "/dev/",  5 ) || strncmp( path, "/dev/r", 6 ) ) 
151       return (char *) strdup( path );
152
153     /* Replace "/dev/" with "/dev/r" */
154     new_path = malloc( strlen(path) + 2 );
155     strcpy( new_path, "/dev/r" );
156     strcat( new_path, path + strlen( "/dev/" ) );
157
158     return new_path;
159 }
160 #endif
161
162 dvd_reader_t *DVDOpen( const char *path )
163 {
164     struct stat64 fileinfo;
165     int ret;
166
167     if( !path ) return 0;
168
169     ret = stat64( path, &fileinfo );
170     if( ret < 0 ) {
171         /* If we can't stat the file, give up */
172         fprintf( stderr, "libdvdread: Can't stat %s\n", path );
173         perror("");
174         return 0;
175     }
176
177     /* Try to open libdvdcss or fall back to standard functions */
178     DVDSetupRead();
179
180     /* First check if this is a block/char device or a file */
181     if( S_ISBLK( fileinfo.st_mode ) || 
182     S_ISCHR( fileinfo.st_mode ) || 
183     S_ISREG( fileinfo.st_mode ) ) {
184
185         /**
186          * Block devices and regular files are assumed to be DVD-Video images.
187          */
188 #if defined(__sun)
189         return DVDOpenImageFile( sun_block2char( path ) );
190 #elif defined(SYS_BSD)
191         return DVDOpenImageFile( bsd_block2char( path ) );
192 #else
193         return DVDOpenImageFile( path );
194 #endif
195
196     } else if( S_ISDIR( fileinfo.st_mode ) ) {
197         dvd_reader_t *auth_drive = 0;
198         char *path_copy;
199 #if defined(SYS_BSD)
200         struct fstab* fe;
201 #elif defined(__sun) || defined(__linux__)
202         FILE *mntfile;
203 #endif
204
205         fprintf(stderr, "libdvdread: file is directory\n" );
206         /* XXX: We should scream real loud here. */
207         if( !(path_copy = strdup( path ) ) ) return 0;
208
209         /* Resolve any symlinks and get the absolut dir name. */
210         {
211             char *new_path;
212             int cdir = open( ".", O_RDONLY );
213     
214             if( cdir >= 0 ) {
215                 chdir( path_copy );
216                 new_path = getcwd( NULL, PATH_MAX );
217                 fchdir( cdir );
218                 close( cdir );
219                 if( new_path ) {
220                     free( path_copy );
221                     path_copy = new_path;
222                 }
223             }
224         }
225
226         /**
227          * If we're being asked to open a directory, check if that directory
228          * is the mountpoint for a DVD-ROM which we can use instead.
229          */
230
231         if( strlen( path_copy ) > 1 ) {
232             if( path[ strlen( path_copy ) - 1 ] == '/' ) 
233                 path_copy[ strlen( path_copy ) - 1 ] = '\0';
234         }
235
236         if( strlen( path_copy ) > 9 ) {
237             if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]), 
238                  "/video_ts" ) ) {
239                 path_copy[ strlen( path_copy ) - 9 ] = '\0';
240             }
241         }
242
243 #if defined(SYS_BSD)
244         if( ( fe = getfsfile( path_copy ) ) ) {
245             char *dev_name = bsd_block2char( fe->fs_spec );
246             fprintf( stderr,
247                      "libdvdread: Attempting to use device %s"
248                      " mounted on %s for CSS authentication\n",
249                      dev_name,
250                      fe->fs_file );
251             auth_drive = DVDOpenImageFile( dev_name );
252             free( dev_name );
253         }
254 #elif defined(__sun)
255         mntfile = fopen( MNTTAB, "r" );
256         if( mntfile ) {
257             struct mnttab mp;
258             int res;
259
260             while( ( res = getmntent( mntfile, &mp ) ) != -1 ) {
261                 if( res == 0 && !strcmp( mp.mnt_mountp, path_copy ) ) {
262                     char *dev_name = sun_block2char( mp.mnt_special );
263                     fprintf( stderr, 
264                              "libdvdread: Attempting to use device %s"
265                              " mounted on %s for CSS authentication\n",
266                              dev_name,
267                              mp.mnt_mountp );
268                     auth_drive = DVDOpenImageFile( dev_name );
269                     free( dev_name );
270                     break;
271                 }
272             }
273             fclose( mntfile );
274         }
275 #elif defined(__linux__)
276         mntfile = fopen( MOUNTED, "r" );
277         if( mntfile ) {
278             struct mntent *me;
279  
280             while( ( me = getmntent( mntfile ) ) ) {
281                 if( !strcmp( me->mnt_dir, path_copy ) ) {
282                     fprintf( stderr, 
283                              "libdvdread: Attempting to use device %s"
284                              " mounted on %s for CSS authentication\n",
285                              me->mnt_fsname,
286                              me->mnt_dir );
287                     auth_drive = DVDOpenImageFile( me->mnt_fsname );
288                     break;
289                 }
290             }
291             fclose( mntfile );
292         }
293 #endif
294         if( !auth_drive ) {
295             fprintf( stderr, "libdvdread: Device inaccessible, "
296                      "CSS authentication not available.\n" );
297         }
298
299         free( path_copy );
300
301         /**
302          * If we've opened a drive, just use that.
303          */
304         if( auth_drive ) return auth_drive;
305
306         /**
307          * Otherwise, we now try to open the directory tree instead.
308          */
309         return DVDOpenPath( path );
310     }
311
312     /* If it's none of the above, screw it. */
313     fprintf( stderr, "libdvdread: Could not open %s\n", path );
314     return 0;
315 }
316
317 void DVDClose( dvd_reader_t *dvd )
318 {
319     if( dvd ) {
320         if( dvd->dev ) pf_dvd_close( dvd->dev );
321         if( dvd->path_root ) free( dvd->path_root );
322         free( dvd );
323         dvd = 0;
324     }
325 }
326
327 /**
328  * Open an unencrypted file on a DVD image file.
329  */
330 static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename )
331 {
332     uint32_t start, len;
333     dvd_file_t *dvd_file;
334
335     start = UDFFindFile( dvd, filename, &len );
336     if( !start ) return 0;
337
338     dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
339     if( !dvd_file ) return 0;
340     dvd_file->dvd = dvd;
341     dvd_file->lb_start = start;
342     dvd_file->seek_pos = 0;
343     memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
344     memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
345     dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
346
347     return dvd_file;
348 }
349
350 /**
351  * Searches for <file> in directory <path>, ignoring case.
352  * Returns 0 and full filename in <filename>.
353  *     or -1 on file not found.
354  *     or -2 on path not found.
355  */
356 static int findDirFile( const char *path, const char *file, char *filename ) 
357 {
358     DIR *dir;
359     struct dirent *ent;
360
361     dir = opendir( path );
362     if( !dir ) return -2;
363
364     while( ( ent = readdir( dir ) ) != NULL ) {
365         if( !strcasecmp( ent->d_name, file ) ) {
366             sprintf( filename, "%s%s%s", path,
367                      ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
368                      ent->d_name );
369             return 0;
370         }
371     }
372
373     return -1;
374 }
375
376 static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
377 {
378     char video_path[ PATH_MAX + 1 ];
379     const char *nodirfile;
380     int ret;
381
382     /* Strip off the directory for our search */
383     if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
384         nodirfile = &(file[ 10 ]);
385     } else {
386         nodirfile = file;
387     }
388
389     ret = findDirFile( dvd->path_root, nodirfile, filename );
390     if( ret < 0 ) {
391         /* Try also with adding the path, just in case. */
392         sprintf( video_path, "%s/VIDEO_TS/", dvd->path_root );
393         ret = findDirFile( video_path, nodirfile, filename );
394         if( ret < 0 ) {
395             /* Try with the path, but in lower case. */
396             sprintf( video_path, "%s/video_ts/", dvd->path_root );
397             ret = findDirFile( video_path, nodirfile, filename );
398             if( ret < 0 ) {
399                 return 0;
400             }
401         }
402     }
403
404     return 1;
405 }
406
407 /**
408  * Open an unencrypted file from a DVD directory tree.
409  */
410 static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename )
411 {
412     char full_path[ PATH_MAX + 1 ];
413     dvd_file_t *dvd_file;
414     struct stat fileinfo;
415     dvd_handle dev;
416
417     /* Get the full path of the file. */
418     if( !findDVDFile( dvd, filename, full_path ) ) return 0;
419
420     dev = pf_dvd_open( full_path );
421     if( dev == NULL ) return 0;
422
423     dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
424     if( !dvd_file ) return 0;
425     dvd_file->dvd = dvd;
426     dvd_file->lb_start = 0;
427     dvd_file->seek_pos = 0;
428     memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
429     memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
430     dvd_file->filesize = 0;
431
432     if( stat( full_path, &fileinfo ) < 0 ) {
433         fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
434         free( dvd_file );
435         return 0;
436     }
437     dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
438     dvd_file->title_devs[ 0 ] = dev;
439     dvd_file->filesize = dvd_file->title_sizes[ 0 ];
440
441     return dvd_file;
442 }
443
444 static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *dvd, int title, int menu )
445 {
446     char filename[ MAX_UDF_FILE_NAME_LEN ];
447     uint32_t start, len;
448     dvd_file_t *dvd_file;
449
450     if( title == 0 ) {
451         sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
452     } else {
453         sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
454     }
455     start = UDFFindFile( dvd, filename, &len );
456     if( start == 0 ) return 0;
457
458     dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
459     if( !dvd_file ) return 0;
460     dvd_file->dvd = dvd;
461     dvd_file->lb_start = start;
462     dvd_file->seek_pos = 0;
463     memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
464     memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
465     dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
466
467     /* Calculate the complete file size for every file in the VOBS */
468     if( !menu ) {
469         int cur;
470
471         for( cur = 2; cur < 10; cur++ ) {
472             sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
473             if( !UDFFindFile( dvd, filename, &len ) ) break;
474             dvd_file->filesize += len / DVD_VIDEO_LB_LEN;
475         }
476     }
477     
478     if( pf_dvd_seek( dvd_file->dvd->dev, (int)start, DVDCSS_SEEK_KEY ) < 0 ) {
479             fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
480                      filename );
481     }
482
483     return dvd_file;
484 }
485
486 static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *dvd, int title, int menu )
487 {
488     char filename[ MAX_UDF_FILE_NAME_LEN ];
489     char full_path[ PATH_MAX + 1 ];
490     struct stat fileinfo;
491     dvd_file_t *dvd_file;
492     int i;
493
494     dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
495     if( !dvd_file ) return 0;
496     dvd_file->dvd = dvd;
497     dvd_file->lb_start = 0;
498     dvd_file->seek_pos = 0;
499     memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
500     memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
501     dvd_file->filesize = 0;
502
503     if( menu ) {
504         dvd_handle dev;
505
506         if( title == 0 ) {
507             sprintf( filename, "VIDEO_TS.VOB" );
508         } else {
509             sprintf( filename, "VTS_%02i_0.VOB", title );
510         }
511         if( !findDVDFile( dvd, filename, full_path ) ) {
512             free( dvd_file );
513             return 0;
514         }
515
516         dev = pf_dvd_open( full_path );
517         if( dev == NULL ) {
518             free( dvd_file );
519             return 0;
520         }
521
522         if( stat( full_path, &fileinfo ) < 0 ) {
523             fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
524             free( dvd_file );
525             return 0;
526         }
527         dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
528         dvd_file->title_devs[ 0 ] = dev;
529         pf_dvd_seek( dvd_file->title_devs[0], 0, DVDCSS_SEEK_KEY );
530         dvd_file->filesize = dvd_file->title_sizes[ 0 ];
531
532     } else {
533         for( i = 0; i < 9; ++i ) {
534
535             sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
536             if( !findDVDFile( dvd, filename, full_path ) ) {
537                 break;
538             }
539
540             if( stat( full_path, &fileinfo ) < 0 ) {
541                 fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
542                 break;
543             }
544
545             dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
546             dvd_file->title_devs[ i ] = pf_dvd_open( full_path );
547             pf_dvd_seek( dvd_file->title_devs[i], 0, DVDCSS_SEEK_KEY );
548             dvd_file->filesize += dvd_file->title_sizes[ i ];
549         }
550         if( !(dvd_file->title_sizes[ 0 ]) ) {
551             free( dvd_file );
552             return 0;
553         }
554     }
555
556     return dvd_file;
557 }
558
559 dvd_file_t *DVDOpenFile( dvd_reader_t *dvd, int titlenum, 
560                          dvd_read_domain_t domain )
561 {
562     char filename[ MAX_UDF_FILE_NAME_LEN ];
563
564     switch( domain ) {
565     case DVD_READ_INFO_FILE:
566         if( titlenum == 0 ) {
567             sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
568         } else {
569             sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
570         }
571         break;
572     case DVD_READ_INFO_BACKUP_FILE:
573         if( titlenum == 0 ) {
574             sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
575         } else {
576             sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
577         }
578         break;
579     case DVD_READ_MENU_VOBS:
580         if( dvd->isImageFile ) {
581             return DVDOpenVOBUDF( dvd, titlenum, 1 );
582         } else {
583             return DVDOpenVOBPath( dvd, titlenum, 1 );
584         }
585         break;
586     case DVD_READ_TITLE_VOBS:
587         if( titlenum == 0 ) return 0;
588         if( dvd->isImageFile ) {
589             return DVDOpenVOBUDF( dvd, titlenum, 0 );
590         } else {
591             return DVDOpenVOBPath( dvd, titlenum, 0 );
592         }
593         break;
594     default:
595         fprintf( stderr, "libdvdread: Invalid domain for file open.\n" );
596         return 0;
597     }
598     
599     if( dvd->isImageFile ) {
600         return DVDOpenFileUDF( dvd, filename );
601     } else {
602         return DVDOpenFilePath( dvd, filename );
603     }
604 }
605
606 void DVDCloseFile( dvd_file_t *dvd_file )
607 {
608     int i;
609
610     if( dvd_file ) {
611         if( !dvd_file->dvd->isImageFile ) {
612             for( i = 0; i < 9; ++i ) {
613                 if( dvd_file->title_devs[ i ] ) {
614                     pf_dvd_close( dvd_file->title_devs[i] );
615                 }
616             }
617         }
618
619         free( dvd_file );
620         dvd_file = 0;
621     }
622 }
623
624 int64_t DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
625                       size_t block_count, unsigned char *data, 
626                       int encrypted )
627 {
628     int ret;
629
630     if( !device->dev ) {
631         fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
632         return 0;
633     }
634
635     ret = pf_dvd_seek( device->dev, (int) lb_number, DVDCSS_NOFLAGS );
636     if( ret != (int) lb_number ) {
637         fprintf( stderr, "libdvdread: Can't seek to block %u\n", 
638                          lb_number );
639         return 0;
640     }
641
642     return (int64_t) ( pf_dvd_read( device->dev, (char *) data, 
643                            (int) block_count, encrypted ) 
644                   * (uint64_t) DVD_VIDEO_LB_LEN );
645 }
646
647 static int64_t DVDReadBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
648                                  size_t block_count, unsigned char *data )
649 {
650     return DVDReadLBUDF( dvd_file->dvd, dvd_file->lb_start + offset,
651                          block_count, data, DVDCSS_READ_DECRYPT );
652 }
653
654 static int64_t DVDReadBlocksPath( dvd_file_t *dvd_file, size_t offset,
655                                   size_t block_count, unsigned char *data )
656 {
657     int i;
658     ssize_t ret, ret2;
659     off64_t off;
660
661     ret = 0;
662     ret2 = 0;
663     for( i = 0; i < 9; ++i ) {
664         if( !dvd_file->title_sizes[ i ] ) return 0;
665
666         if( offset < dvd_file->title_sizes[ i ] ) {
667             if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
668                 off = pf_dvd_seek( dvd_file->title_devs[ i ], (int)offset, 0 );
669                 if( off != (int)offset ) {
670                     fprintf( stderr, "libdvdread: Can't seek to block %d\n", 
671                                      offset );
672                     return 0;
673                 }
674                 ret = pf_dvd_read( dvd_file->title_devs[ i ], data,
675                                    (int)block_count, DVDCSS_READ_DECRYPT );
676                 break;
677             } else {
678                 size_t part1_size = dvd_file->title_sizes[ i ] - offset;
679                 /* FIXME: Really needs to be a while loop.
680                  * (This is only true if you try and read >1GB at a time) */
681
682                 /* Read part 1 */
683                 off = pf_dvd_seek( dvd_file->title_devs[ i ], offset, 0 );
684                 if( off != offset ) {
685                     fprintf( stderr, "libdvdread: Can't seek to block %d\n", 
686                                      offset );
687                     return 0;
688                 }
689                 ret = pf_dvd_read( dvd_file->title_devs[ i ], data,
690                                    part1_size, DVDCSS_READ_DECRYPT );
691                 if( ret < 0 ) return ret;
692                 /* FIXME: This is wrong if i is the last file in the set. 
693                  * also error from this read will not show in ret. */
694
695                 /* Read part 2 */
696                 pf_dvd_seek( dvd_file->title_devs[ i + 1 ], 0, 0 );
697                 ret2 = pf_dvd_read( dvd_file->title_devs[ i + 1 ], data +
698                                     ( part1_size * (int64_t)DVD_VIDEO_LB_LEN ),
699                                     block_count - part1_size,
700                                     DVDCSS_READ_DECRYPT );
701                 if( ret2 < 0 ) return ret2;
702                 break;
703             }
704         } else {
705             offset -= dvd_file->title_sizes[ i ];
706         }
707     }
708
709     return ( (int64_t) ret + (int64_t) ret2 ) * DVD_VIDEO_LB_LEN;
710 }
711
712 /* These are broken for some cases reading more than 2Gb at a time. */
713 ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset, 
714                        size_t block_count, unsigned char *data )
715 {
716     int64_t ret;
717   
718     if( dvd_file->dvd->isImageFile ) {
719         ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset, 
720                                 block_count, data );
721     } else {
722         ret = DVDReadBlocksPath( dvd_file, (size_t) offset, 
723                                  block_count, data );
724     }
725     if( ret <= 0 ) {
726         return (ssize_t) ret;
727     }
728
729     {
730       ssize_t sret = (ssize_t) (ret / (int64_t)DVD_VIDEO_LB_LEN );
731       if( sret == 0 ) {
732         fprintf(stderr, "libdvdread: DVDReadBlocks got %d bytes\n", (int)ret );
733       }
734       return sret;
735     }
736 }
737
738 int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset )
739 {
740     if( dvd_file->dvd->isImageFile ) {
741         dvd_file->seek_pos = (uint32_t) offset;
742         return offset;
743     } else {
744         return (int32_t) ( lseek( dvd_file->title_devs[0]->i_fd,
745                             (off_t)offset, SEEK_SET ) );
746     }
747 }
748
749 static ssize_t DVDReadBytesUDF( dvd_file_t *dvd_file, void *data, 
750                                 size_t byte_size )
751 {
752     unsigned char *secbuf;
753     unsigned int numsec, seek_sector, seek_byte;
754     int64_t len;
755     
756     seek_sector = dvd_file->seek_pos / DVD_VIDEO_LB_LEN;
757     seek_byte   = dvd_file->seek_pos % DVD_VIDEO_LB_LEN;
758
759     numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) + 1;
760     secbuf = (unsigned char *) malloc( numsec * DVD_VIDEO_LB_LEN );
761     if( !secbuf ) {
762         fprintf( stderr, "libdvdread: Can't allocate memory " 
763                          "for file read!\n" );
764         return 0;
765     }
766
767     len = DVDReadLBUDF( dvd_file->dvd, dvd_file->lb_start + seek_sector,
768                         numsec, secbuf, DVDCSS_NOFLAGS );
769     if( len != numsec * (int64_t) DVD_VIDEO_LB_LEN ) {
770         free( secbuf );
771         return 0;
772     }
773
774     dvd_file->seek_pos += byte_size;
775
776     memcpy( data, &(secbuf[ seek_byte ]), byte_size );
777     free( secbuf );
778
779     return byte_size;
780 }
781
782 static ssize_t DVDReadBytesPath( dvd_file_t *dvd_file, void *data, 
783                                  size_t byte_size )
784 {
785     return read( dvd_file->title_devs[0]->i_fd, data, byte_size );
786 }
787
788 ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
789 {
790     if( dvd_file->dvd->isImageFile ) {
791         return DVDReadBytesUDF( dvd_file, data, byte_size );
792     } else {
793         return DVDReadBytesPath( dvd_file, data, byte_size );
794     }
795 }
796
797 ssize_t DVDFileSize( dvd_file_t *dvd_file )
798 {
799     return dvd_file->filesize;
800 }
801
802 int64_t DVDReadVLBUDF( dvd_reader_t *device, uint32_t lb_number,
803                size_t block_count, struct iovec * vector, int encrypted )
804 {
805     int ret;
806
807     if( !device->dev ) {
808         fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
809         return 0;
810     }
811
812     ret = pf_dvd_seek( device->dev, (int) lb_number, 0 );
813     if( ret != (int) lb_number ) {
814         fprintf( stderr, "libdvdread: Can't seek to block %u\n",
815                  lb_number );
816         return 0;
817     }
818
819     return (int64_t) ( pf_dvd_readv( device->dev, vector,
820                                      (int)block_count, encrypted )
821          * (uint64_t) DVD_VIDEO_LB_LEN );
822 }
823
824 static int64_t DVDReadVBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
825                  size_t block_count, struct iovec *vector )
826 {
827     return DVDReadVLBUDF( dvd_file->dvd, dvd_file->lb_start + offset,
828                          block_count, vector, DVDCSS_READ_DECRYPT );
829 }
830
831 static int64_t DVDReadVBlocksPath( dvd_file_t *dvd_file, size_t offset,
832                                    size_t block_count, struct iovec *vector )
833 {
834     int i;
835     int ret, ret2;
836     int off;
837
838     ret = 0;
839     ret2 = 0;
840     for( i = 0 ; i < 9 ; ++i ) {
841         if( !dvd_file->title_sizes[ i ] ) {
842             return 0;
843         }
844
845         if( offset < dvd_file->title_sizes[ i ] ) {
846             if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
847                 off = pf_dvd_seek( dvd_file->title_devs[ i ],
848                                    (int)offset, 0 );
849                 if( off != (int)offset ) {
850                     fprintf( stderr, "libdvdread: Can't seek to block %d\n", 
851                                      offset );
852                     return 0;
853                 }
854                 ret = pf_dvd_readv( dvd_file->title_devs[ i ], vector,
855                             (int)block_count, DVDCSS_READ_DECRYPT );
856                 break;
857             } else {
858                 int part1_size = dvd_file->title_sizes[ i ] - offset;
859                 /* FIXME: Really needs to be a while loop.
860                  * (This is only true if you try and read >1GB at a time) */
861
862                 /* Read part 1 */
863                 off = pf_dvd_seek( dvd_file->title_devs[ i ], offset, 0 );
864                 if( off != offset ) {
865                     fprintf( stderr, "libdvdread: Can't seek to block %d\n", 
866                                      offset );
867                     return 0;
868                 }
869                 ret = pf_dvd_readv( dvd_file->title_devs[ i ], vector,
870                                     part1_size, DVDCSS_READ_DECRYPT );
871                 
872                 if( ret < 0 ) return ret;
873                 /* FIXME: This is wrong if i is the last file in the set. 
874                  * also error from this read will not show in ret. */
875
876                 /* Read part 2 */
877                 pf_dvd_seek( dvd_file->title_devs[ i + 1 ], 0, 0 );
878                 ret2 = pf_dvd_readv( dvd_file->title_devs[ i + 1 ],
879                                      vector + part1_size,
880                                      (int)(block_count - part1_size),
881                                      DVDCSS_READ_DECRYPT );
882                 if( ret2 < 0 ) return ret2;
883                 break;
884             }
885         } else {
886             offset -= dvd_file->title_sizes[ i ];
887         }
888     }
889
890     return ( ret + ret2 ) * (int64_t) DVD_VIDEO_LB_LEN;
891 }
892
893
894 ssize_t DVDReadVBlocks( dvd_file_t *dvd_file, int offset,
895                         size_t block_count, struct iovec * vector )
896 {
897     int64_t ret;
898
899     if( dvd_file->dvd->isImageFile ) {
900         ret = DVDReadVBlocksUDF( dvd_file, (uint32_t)offset,
901                                  block_count, vector );
902     } else {
903         ret = DVDReadVBlocksPath( dvd_file, (size_t) offset, 
904                                   block_count, vector );
905     }
906     if( ret <= 0 ) {
907         return (ssize_t) ret;
908     }
909     
910     {
911       ssize_t sret = (ssize_t) (ret / (int64_t)DVD_VIDEO_LB_LEN );
912       if( sret == 0 )
913       {
914         fprintf(stderr, "libdvdread: DVDReadVBlocks got %d bytes\n", (int)ret );
915       }
916       return sret;
917     }
918 }
919