2 * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>.
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.
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.
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.
19 #include <sys/types.h>
21 #include <sys/uio.h> /* readv() */
31 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__)
36 #include <sys/mnttab.h>
37 #elif defined(SYS_BSD)
39 #elif defined(__linux__)
44 typedef off_t off64_t;
50 #include "dvd_reader.h"
52 #include "dvd_setup.h"
55 /* Basic information. */
58 /* Information required for an image file. */
61 /* Information required for a directory path drive. */
66 /* Basic information. */
69 /* Information required for an image file. */
73 /* Information required for a directory path drive. */
74 dvd_handle title_devs[9];
75 size_t title_sizes[ 9 ];
77 /* Calculated at open-time, size in blocks. */
82 * Open a DVD image or block device file.
84 static dvd_reader_t *DVDOpenImageFile( const char *location )
89 dev = pf_dvd_open( (char *) location );
91 fprintf( stderr, "libdvdread: Can't open %s for reading.\n",
96 dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
105 static dvd_reader_t *DVDOpenPath( const char *path_root )
109 dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
111 dvd->isImageFile = 0;
113 dvd->path_root = strdup( path_root );
119 /* /dev/rdsk/c0t6d0s0 (link to /devices/...)
120 /vol/dev/rdsk/c0t6d0/??
122 static char *sun_block2char( const char *path )
126 /* Must contain "/dsk/" */
127 if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
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/" ) );
141 /* FreeBSD /dev/(r)(a)cd0 (a is for atapi, should work without r)
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 )
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 );
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/" ) );
162 dvd_reader_t *DVDOpen( const char *path )
164 struct stat64 fileinfo;
167 if( !path ) return 0;
169 ret = stat64( path, &fileinfo );
171 /* If we can't stat the file, give up */
172 fprintf( stderr, "libdvdread: Can't stat %s\n", path );
177 /* Try to open libdvdcss or fall back to standard functions */
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 ) ) {
186 * Block devices and regular files are assumed to be DVD-Video images.
189 return DVDOpenImageFile( sun_block2char( path ) );
190 #elif defined(SYS_BSD)
191 return DVDOpenImageFile( bsd_block2char( path ) );
193 return DVDOpenImageFile( path );
196 } else if( S_ISDIR( fileinfo.st_mode ) ) {
197 dvd_reader_t *auth_drive = 0;
201 #elif defined(__sun) || defined(__linux__)
205 fprintf(stderr, "libdvdread: file is directory\n" );
206 /* XXX: We should scream real loud here. */
207 if( !(path_copy = strdup( path ) ) ) return 0;
209 /* Resolve any symlinks and get the absolut dir name. */
212 int cdir = open( ".", O_RDONLY );
216 new_path = getcwd( NULL, PATH_MAX );
221 path_copy = new_path;
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.
231 if( strlen( path_copy ) > 1 ) {
232 if( path[ strlen( path_copy ) - 1 ] == '/' )
233 path_copy[ strlen( path_copy ) - 1 ] = '\0';
236 if( strlen( path_copy ) > 9 ) {
237 if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]),
239 path_copy[ strlen( path_copy ) - 9 ] = '\0';
244 if( ( fe = getfsfile( path_copy ) ) ) {
245 char *dev_name = bsd_block2char( fe->fs_spec );
247 "libdvdread: Attempting to use device %s"
248 " mounted on %s for CSS authentication\n",
251 auth_drive = DVDOpenImageFile( dev_name );
255 mntfile = fopen( MNTTAB, "r" );
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 );
264 "libdvdread: Attempting to use device %s"
265 " mounted on %s for CSS authentication\n",
268 auth_drive = DVDOpenImageFile( dev_name );
275 #elif defined(__linux__)
276 mntfile = fopen( MOUNTED, "r" );
280 while( ( me = getmntent( mntfile ) ) ) {
281 if( !strcmp( me->mnt_dir, path_copy ) ) {
283 "libdvdread: Attempting to use device %s"
284 " mounted on %s for CSS authentication\n",
287 auth_drive = DVDOpenImageFile( me->mnt_fsname );
295 fprintf( stderr, "libdvdread: Device inaccessible, "
296 "CSS authentication not available.\n" );
302 * If we've opened a drive, just use that.
304 if( auth_drive ) return auth_drive;
307 * Otherwise, we now try to open the directory tree instead.
309 return DVDOpenPath( path );
312 /* If it's none of the above, screw it. */
313 fprintf( stderr, "libdvdread: Could not open %s\n", path );
317 void DVDClose( dvd_reader_t *dvd )
320 if( dvd->dev ) pf_dvd_close( dvd->dev );
321 if( dvd->path_root ) free( dvd->path_root );
328 * Open an unencrypted file on a DVD image file.
330 static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename )
333 dvd_file_t *dvd_file;
335 start = UDFFindFile( dvd, filename, &len );
336 if( !start ) return 0;
338 dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
339 if( !dvd_file ) return 0;
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;
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.
356 static int findDirFile( const char *path, const char *file, char *filename )
361 dir = opendir( path );
362 if( !dir ) return -2;
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 ] == '/' ) ? "" : "/" ),
376 static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
378 char video_path[ PATH_MAX + 1 ];
379 const char *nodirfile;
382 /* Strip off the directory for our search */
383 if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
384 nodirfile = &(file[ 10 ]);
389 ret = findDirFile( dvd->path_root, nodirfile, filename );
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 );
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 );
408 * Open an unencrypted file from a DVD directory tree.
410 static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename )
412 char full_path[ PATH_MAX + 1 ];
413 dvd_file_t *dvd_file;
414 struct stat fileinfo;
417 /* Get the full path of the file. */
418 if( !findDVDFile( dvd, filename, full_path ) ) return 0;
420 dev = pf_dvd_open( full_path );
421 if( dev == NULL ) return 0;
423 dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
424 if( !dvd_file ) return 0;
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;
432 if( stat( full_path, &fileinfo ) < 0 ) {
433 fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
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 ];
444 static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *dvd, int title, int menu )
446 char filename[ MAX_UDF_FILE_NAME_LEN ];
448 dvd_file_t *dvd_file;
451 sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
453 sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
455 start = UDFFindFile( dvd, filename, &len );
456 if( start == 0 ) return 0;
458 dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
459 if( !dvd_file ) return 0;
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;
467 /* Calculate the complete file size for every file in the VOBS */
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;
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",
486 static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *dvd, int title, int menu )
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;
494 dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
495 if( !dvd_file ) return 0;
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;
507 sprintf( filename, "VIDEO_TS.VOB" );
509 sprintf( filename, "VTS_%02i_0.VOB", title );
511 if( !findDVDFile( dvd, filename, full_path ) ) {
516 dev = pf_dvd_open( full_path );
522 if( stat( full_path, &fileinfo ) < 0 ) {
523 fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
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 ];
533 for( i = 0; i < 9; ++i ) {
535 sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
536 if( !findDVDFile( dvd, filename, full_path ) ) {
540 if( stat( full_path, &fileinfo ) < 0 ) {
541 fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
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 ];
550 if( !(dvd_file->title_sizes[ 0 ]) ) {
559 dvd_file_t *DVDOpenFile( dvd_reader_t *dvd, int titlenum,
560 dvd_read_domain_t domain )
562 char filename[ MAX_UDF_FILE_NAME_LEN ];
565 case DVD_READ_INFO_FILE:
566 if( titlenum == 0 ) {
567 sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
569 sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
572 case DVD_READ_INFO_BACKUP_FILE:
573 if( titlenum == 0 ) {
574 sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
576 sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
579 case DVD_READ_MENU_VOBS:
580 if( dvd->isImageFile ) {
581 return DVDOpenVOBUDF( dvd, titlenum, 1 );
583 return DVDOpenVOBPath( dvd, titlenum, 1 );
586 case DVD_READ_TITLE_VOBS:
587 if( titlenum == 0 ) return 0;
588 if( dvd->isImageFile ) {
589 return DVDOpenVOBUDF( dvd, titlenum, 0 );
591 return DVDOpenVOBPath( dvd, titlenum, 0 );
595 fprintf( stderr, "libdvdread: Invalid domain for file open.\n" );
599 if( dvd->isImageFile ) {
600 return DVDOpenFileUDF( dvd, filename );
602 return DVDOpenFilePath( dvd, filename );
606 void DVDCloseFile( dvd_file_t *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] );
624 int64_t DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
625 size_t block_count, unsigned char *data,
631 fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
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",
642 return (int64_t) ( pf_dvd_read( device->dev, (char *) data,
643 (int) block_count, encrypted )
644 * (uint64_t) DVD_VIDEO_LB_LEN );
647 static int64_t DVDReadBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
648 size_t block_count, unsigned char *data )
650 return DVDReadLBUDF( dvd_file->dvd, dvd_file->lb_start + offset,
651 block_count, data, DVDCSS_READ_DECRYPT );
654 static int64_t DVDReadBlocksPath( dvd_file_t *dvd_file, size_t offset,
655 size_t block_count, unsigned char *data )
663 for( i = 0; i < 9; ++i ) {
664 if( !dvd_file->title_sizes[ i ] ) return 0;
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",
674 ret = pf_dvd_read( dvd_file->title_devs[ i ], data,
675 (int)block_count, DVDCSS_READ_DECRYPT );
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) */
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",
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. */
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;
705 offset -= dvd_file->title_sizes[ i ];
709 return ( (int64_t) ret + (int64_t) ret2 ) * DVD_VIDEO_LB_LEN;
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 )
718 if( dvd_file->dvd->isImageFile ) {
719 ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset,
722 ret = DVDReadBlocksPath( dvd_file, (size_t) offset,
726 return (ssize_t) ret;
730 ssize_t sret = (ssize_t) (ret / (int64_t)DVD_VIDEO_LB_LEN );
732 fprintf(stderr, "libdvdread: DVDReadBlocks got %d bytes\n", (int)ret );
738 int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset )
740 if( dvd_file->dvd->isImageFile ) {
741 dvd_file->seek_pos = (uint32_t) offset;
744 return (int32_t) ( lseek( dvd_file->title_devs[0]->i_fd,
745 (off_t)offset, SEEK_SET ) );
749 static ssize_t DVDReadBytesUDF( dvd_file_t *dvd_file, void *data,
752 unsigned char *secbuf;
753 unsigned int numsec, seek_sector, seek_byte;
756 seek_sector = dvd_file->seek_pos / DVD_VIDEO_LB_LEN;
757 seek_byte = dvd_file->seek_pos % DVD_VIDEO_LB_LEN;
759 numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) + 1;
760 secbuf = (unsigned char *) malloc( numsec * DVD_VIDEO_LB_LEN );
762 fprintf( stderr, "libdvdread: Can't allocate memory "
763 "for file read!\n" );
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 ) {
774 dvd_file->seek_pos += byte_size;
776 memcpy( data, &(secbuf[ seek_byte ]), byte_size );
782 static ssize_t DVDReadBytesPath( dvd_file_t *dvd_file, void *data,
785 return read( dvd_file->title_devs[0]->i_fd, data, byte_size );
788 ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
790 if( dvd_file->dvd->isImageFile ) {
791 return DVDReadBytesUDF( dvd_file, data, byte_size );
793 return DVDReadBytesPath( dvd_file, data, byte_size );
797 ssize_t DVDFileSize( dvd_file_t *dvd_file )
799 return dvd_file->filesize;
802 int64_t DVDReadVLBUDF( dvd_reader_t *device, uint32_t lb_number,
803 size_t block_count, struct iovec * vector, int encrypted )
808 fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
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",
819 return (int64_t) ( pf_dvd_readv( device->dev, vector,
820 (int)block_count, encrypted )
821 * (uint64_t) DVD_VIDEO_LB_LEN );
824 static int64_t DVDReadVBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
825 size_t block_count, struct iovec *vector )
827 return DVDReadVLBUDF( dvd_file->dvd, dvd_file->lb_start + offset,
828 block_count, vector, DVDCSS_READ_DECRYPT );
831 static int64_t DVDReadVBlocksPath( dvd_file_t *dvd_file, size_t offset,
832 size_t block_count, struct iovec *vector )
840 for( i = 0 ; i < 9 ; ++i ) {
841 if( !dvd_file->title_sizes[ i ] ) {
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 ],
849 if( off != (int)offset ) {
850 fprintf( stderr, "libdvdread: Can't seek to block %d\n",
854 ret = pf_dvd_readv( dvd_file->title_devs[ i ], vector,
855 (int)block_count, DVDCSS_READ_DECRYPT );
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) */
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",
869 ret = pf_dvd_readv( dvd_file->title_devs[ i ], vector,
870 part1_size, DVDCSS_READ_DECRYPT );
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. */
877 pf_dvd_seek( dvd_file->title_devs[ i + 1 ], 0, 0 );
878 ret2 = pf_dvd_readv( dvd_file->title_devs[ i + 1 ],
880 (int)(block_count - part1_size),
881 DVDCSS_READ_DECRYPT );
882 if( ret2 < 0 ) return ret2;
886 offset -= dvd_file->title_sizes[ i ];
890 return ( ret + ret2 ) * (int64_t) DVD_VIDEO_LB_LEN;
894 ssize_t DVDReadVBlocks( dvd_file_t *dvd_file, int offset,
895 size_t block_count, struct iovec * vector )
899 if( dvd_file->dvd->isImageFile ) {
900 ret = DVDReadVBlocksUDF( dvd_file, (uint32_t)offset,
901 block_count, vector );
903 ret = DVDReadVBlocksPath( dvd_file, (size_t) offset,
904 block_count, vector );
907 return (ssize_t) ret;
911 ssize_t sret = (ssize_t) (ret / (int64_t)DVD_VIDEO_LB_LEN );
914 fprintf(stderr, "libdvdread: DVDReadVBlocks got %d bytes\n", (int)ret );