1 /* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
3 * Copyright (C) 2001, 2002, 2003 Billy Biggs <vektor@dumbterm.net>,
4 * Håkan Hjort <d95hjort@dtek.chalmers.se>,
5 * Björn Englund <d4bjorn@dtek.chalmers.se>
7 * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
8 * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or (at
14 * your option) any later version.
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
28 #include <sys/types.h>
30 #include <sys/time.h> /* For the timing of dvdcss_title crack. */
40 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__) || defined(__DARWIN__) || defined(__DragonFly__)
45 #include <sys/mnttab.h>
47 #include </usr/conf/h/mnttab.h>
48 #elif defined(SYS_BSD)
50 #elif defined(__linux__) || defined(__CYGWIN__)
54 #include "dvd_reader.h"
55 #include "dvd_input.h"
59 #include "dvdread_internal.h"
61 #define DEFAULT_UDF_CACHE_LEVEL 0
64 /* Basic information. */
67 /* Hack for keeping track of the css status.
68 * 0: no css, 1: perhaps (need init of keys), 2: have done init */
70 int css_title
; /* Last title that we have called dvdinpute_title for. */
72 /* Information required for an image file. */
75 /* Information required for a directory path drive. */
78 /* Filesystem cache */
79 int udfcache_level
; /* 0 - turned off, 1 - on */
82 /* block aligned malloc */
85 /* error message verbosity level */
90 /* Basic information. */
93 /* Hack for selecting the right css title. */
96 /* Information required for an image file. */
100 /* Information required for a directory path drive. */
101 size_t title_sizes
[ 9 ];
102 dvd_input_t title_devs
[ 9 ];
104 /* Calculated at open-time, size in blocks. */
109 #define DVDREAD_VERBOSE_DEFAULT 0
111 int get_verbose(void)
113 char *dvdread_verbose
;
116 dvdread_verbose
= getenv("DVDREAD_VERBOSE");
117 if(dvdread_verbose
) {
118 verbose
= (int)strtol(dvdread_verbose
, NULL
, 0);
120 verbose
= DVDREAD_VERBOSE_DEFAULT
;
125 int dvdread_verbose(dvd_reader_t
*dvd
)
130 dvd_reader_t
*device_of_file(dvd_file_t
*file
)
136 * Returns the compiled version. (DVDREAD_VERSION as an int)
140 return DVDREAD_VERSION
;
145 * Set the level of caching on udf
146 * level = 0 (no caching)
147 * level = 1 (caching filesystem info)
149 int DVDUDFCacheLevel(dvd_reader_t
*device
, int level
)
151 struct dvd_reader_s
*dev
= (struct dvd_reader_s
*)device
;
155 } else if(level
< 0) {
156 return dev
->udfcache_level
;
159 dev
->udfcache_level
= level
;
164 void *GetUDFCacheHandle(dvd_reader_t
*device
)
166 struct dvd_reader_s
*dev
= (struct dvd_reader_s
*)device
;
168 return dev
->udfcache
;
171 void SetUDFCacheHandle(dvd_reader_t
*device
, void *cache
)
173 struct dvd_reader_s
*dev
= (struct dvd_reader_s
*)device
;
175 dev
->udfcache
= cache
;
178 void *GetAlignHandle(dvd_reader_t
*device
)
180 struct dvd_reader_s
*dev
= (struct dvd_reader_s
*)device
;
185 void SetAlignHandle(dvd_reader_t
*device
, void *align
)
187 struct dvd_reader_s
*dev
= (struct dvd_reader_s
*)device
;
193 /* Loop over all titles and call dvdcss_title to crack the keys. */
194 static int initAllCSSKeys( dvd_reader_t
*dvd
)
196 struct timeval all_s
, all_e
;
197 struct timeval t_s
, t_e
;
198 char filename
[ MAX_UDF_FILE_NAME_LEN
];
202 char *nokeys_str
= getenv("DVDREAD_NOKEYS");
203 if(nokeys_str
!= NULL
)
206 if(dvd
->verbose
>= 1) {
207 fprintf( stderr
, "\n" );
208 fprintf( stderr
, "libdvdread: Attempting to retrieve all CSS keys\n" );
209 fprintf( stderr
, "libdvdread: This can take a _long_ time, "
210 "please be patient\n\n" );
212 gettimeofday(&all_s
, NULL
);
214 for( title
= 0; title
< 100; title
++ ) {
215 gettimeofday( &t_s
, NULL
);
217 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.VOB" );
219 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, 0 );
221 start
= UDFFindFile( dvd
, filename
, &len
);
222 if( start
!= 0 && len
!= 0 ) {
223 /* Perform CSS key cracking for this title. */
224 if(dvd
->verbose
>= 1) {
225 fprintf( stderr
, "libdvdread: Get key for %s at 0x%08x\n",
228 if( dvdinput_title( dvd
->dev
, (int)start
) < 0 ) {
229 if(dvd
->verbose
>= 0) {
230 fprintf( stderr
, "libdvdread: Error cracking CSS key for %s (0x%08x)\n", filename
, start
);
233 gettimeofday( &t_e
, NULL
);
234 if(dvd
->verbose
>= 1) {
235 fprintf( stderr
, "libdvdread: Elapsed time %ld\n",
236 (long int) t_e
.tv_sec
- t_s
.tv_sec
);
240 if( title
== 0 ) continue;
242 gettimeofday( &t_s
, NULL
);
243 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, 1 );
244 start
= UDFFindFile( dvd
, filename
, &len
);
245 if( start
== 0 || len
== 0 ) break;
247 /* Perform CSS key cracking for this title. */
248 if(dvd
->verbose
>= 1) {
249 fprintf( stderr
, "libdvdread: Get key for %s at 0x%08x\n",
252 if( dvdinput_title( dvd
->dev
, (int)start
) < 0 ) {
253 if(dvd
->verbose
>= 0) {
254 fprintf( stderr
, "libdvdread: Error cracking CSS key for %s (0x%08x)!!\n", filename
, start
);
257 gettimeofday( &t_e
, NULL
);
258 if(dvd
->verbose
>= 1) {
259 fprintf( stderr
, "libdvdread: Elapsed time %ld\n",
260 (long int) t_e
.tv_sec
- t_s
.tv_sec
);
265 if(dvd
->verbose
>= 1) {
266 fprintf( stderr
, "libdvdread: Found %d VTS's\n", title
);
268 gettimeofday(&all_e
, NULL
);
269 if(dvd
->verbose
>= 1) {
270 fprintf( stderr
, "libdvdread: Elapsed time %ld\n",
271 (long int) all_e
.tv_sec
- all_s
.tv_sec
);
279 * Open a DVD image or block device file.
280 * Checks if the root directory in the udf image file can be found.
281 * If not it assumes this isn't a valid udf image and returns NULL
283 static dvd_reader_t
*DVDOpenImageFile( const char *location
, int have_css
)
289 verbose
= get_verbose();
291 dev
= dvdinput_open( location
);
294 fprintf( stderr
, "libdvdread: Can't open '%s' for reading: %s\n",
295 location
, strerror(errno
));
300 dvd
= (dvd_reader_t
*) malloc( sizeof( dvd_reader_t
) );
302 int tmp_errno
= errno
;
307 dvd
->verbose
= verbose
;
308 dvd
->isImageFile
= 1;
310 dvd
->path_root
= NULL
;
312 dvd
->udfcache_level
= DEFAULT_UDF_CACHE_LEVEL
;
313 dvd
->udfcache
= NULL
;
318 /* Only if DVDCSS_METHOD = title, a bit if it's disc or if
319 * DVDCSS_METHOD = key but region missmatch. Unfortunaly we
320 * don't have that information. */
322 dvd
->css_state
= 1; /* Need key init. */
326 /* sanity check, is it a valid UDF image, can we find the root dir */
327 if(!UDFFindFile(dvd
, "/", NULL
)) {
328 dvdinput_close(dvd
->dev
);
330 FreeUDFCache(dvd
, dvd
->udfcache
);
333 if(dvd
->verbose
>= 0) {
334 fprintf(stderr
, "libdvdread: DVDOpenImageFile(): Memory leak in align functions 1\n");
343 static dvd_reader_t
*DVDOpenPath( const char *path_root
)
347 dvd
= (dvd_reader_t
*) malloc( sizeof( dvd_reader_t
) );
351 dvd
->verbose
= get_verbose();
352 dvd
->isImageFile
= 0;
354 dvd
->path_root
= strdup( path_root
);
355 if(!dvd
->path_root
) {
359 dvd
->udfcache_level
= DEFAULT_UDF_CACHE_LEVEL
;
360 dvd
->udfcache
= NULL
;
364 dvd
->css_state
= 0; /* Only used in the UDF path */
365 dvd
->css_title
= 0; /* Only matters in the UDF path */
371 /* /dev/rdsk/c0t6d0s0 (link to /devices/...)
372 /vol/dev/rdsk/c0t6d0/??
374 static char *sun_block2char( const char *path
)
378 /* Must contain "/dsk/" */
379 if( !strstr( path
, "/dsk/" ) ) return (char *) strdup( path
);
381 /* Replace "/dsk/" with "/rdsk/" */
382 new_path
= malloc( strlen(path
) + 2 );
383 strcpy( new_path
, path
);
384 strcpy( strstr( new_path
, "/dsk/" ), "" );
385 strcat( new_path
, "/rdsk/" );
386 strcat( new_path
, strstr( path
, "/dsk/" ) + strlen( "/dsk/" ) );
393 /* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recomended to _not_ use r
394 update: FreeBSD and DragonFly no longer uses the prefix so don't add it.
396 OpenBSD /dev/rcd0c, it needs to be the raw device
397 NetBSD /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others
398 Darwin /dev/rdisk0, it needs to be the raw device
399 BSD/OS /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do)
401 returns a string allocated with strdup which should be free()'d when
404 static char *bsd_block2char( const char *path
)
406 #if defined(__FreeBSD__) || defined(__DragonFly__)
407 return (char *) strdup( path
);
411 /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */
412 if( strncmp( path
, "/dev/", 5 ) || !strncmp( path
, "/dev/r", 6 ) )
413 return (char *) strdup( path
);
415 /* Replace "/dev/" with "/dev/r" */
416 new_path
= malloc( strlen(path
) + 2 );
417 strcpy( new_path
, "/dev/r" );
418 strcat( new_path
, path
+ strlen( "/dev/" ) );
421 #endif /* __FreeBSD__ || __DragonFly__ */
426 dvd_reader_t
*DVDOpen( const char *path
)
428 struct stat fileinfo
;
430 char *dev_name
= NULL
;
431 int internal_errno
= 0;
439 verbose
= get_verbose();
441 #if defined(__CYGWIN__) || defined(__MINGW32__)
442 /* Stat doesn't work on devices under mingwin/cygwin. */
443 if( path
[0] && path
[1] == ':' && path
[2] == '\0' )
445 /* Don't try to stat the file */
446 fileinfo
.st_mode
= S_IFBLK
;
451 ret
= stat( path
, &fileinfo
);
453 int tmp_errno
= errno
;
454 /* If we can't stat the file, give up */
456 fprintf( stderr
, "libdvdread: Can't stat '%s': %s\n",
457 path
, strerror(errno
));
464 /* Try to open libdvdcss or fall back to standard functions */
465 have_css
= dvdinput_setup();
467 /* First check if this is a block/char device or a file*/
468 if( S_ISBLK( fileinfo
.st_mode
) ||
469 S_ISCHR( fileinfo
.st_mode
) ||
470 S_ISREG( fileinfo
.st_mode
) ) {
472 * Block devices and regular files are assumed to be DVD-Video images.
474 dvd_reader_t
*dvd
= NULL
;
476 dev_name
= sun_block2char( path
);
477 #elif defined(SYS_BSD)
478 dev_name
= bsd_block2char( path
);
480 dev_name
= strdup( path
);
482 dvd
= DVDOpenImageFile( dev_name
, have_css
);
486 } else if( S_ISDIR( fileinfo
.st_mode
) ) {
487 dvd_reader_t
*auth_drive
= 0;
491 #elif defined(__sun) || defined(__linux__) || defined(__CYGWIN__)
495 /* XXX: We should scream real loud here. */
496 if( !(path_copy
= strdup( path
) ) ) return 0;
498 /* don't have fchdir, and getcwd( NULL, ... ) is strange */
499 #if !(defined(__CYGWIN__) || defined(__MINGW32__))
500 /* Resolve any symlinks and get the absolut dir name. */
505 current_path
= malloc(PATH_MAX
);
507 if(!getcwd(current_path
, PATH_MAX
)) {
514 new_path
= malloc(PATH_MAX
);
516 if(!getcwd(new_path
, PATH_MAX
)) {
526 path_copy
= new_path
;
533 * If we're being asked to open a directory, check if that directory
534 * is the mountpoint for a DVD-ROM which we can use instead.
537 if( strlen( path_copy
) > 1 ) {
538 if( path_copy
[ strlen( path_copy
) - 1 ] == '/' ) {
539 path_copy
[ strlen( path_copy
) - 1 ] = '\0';
543 if( strlen( path_copy
) >= 9 ) {
544 if( !strcasecmp( &(path_copy
[ strlen( path_copy
) - 9 ]),
546 path_copy
[ strlen( path_copy
) - 9 ] = '\0';
547 if(path_copy
[0] == '\0') {
555 if( ( fe
= getfsfile( path_copy
) ) ) {
556 dev_name
= bsd_block2char( fe
->fs_spec
);
559 "libdvdread: Attempting to use device %s"
560 " mounted on %s%s\n",
563 have_css
? " for CSS authentication" : "");
565 auth_drive
= DVDOpenImageFile( dev_name
, have_css
);
567 internal_errno
= errno
;
571 mntfile
= fopen( MNTTAB
, "r" );
576 while( ( res
= getmntent( mntfile
, &mp
) ) != -1 ) {
577 if( res
== 0 && !strcmp( mp
.mnt_mountp
, path_copy
) ) {
578 dev_name
= sun_block2char( mp
.mnt_special
);
581 "libdvdread: Attempting to use device %s"
582 " mounted on %s%s\n",
585 have_css
? " for CSS authentication" : "");
587 auth_drive
= DVDOpenImageFile( dev_name
, have_css
);
589 internal_errno
= errno
;
596 #elif defined(__linux__) || defined(__CYGWIN__)
597 mntfile
= fopen( MOUNTED
, "r" );
601 while( ( me
= getmntent( mntfile
) ) ) {
602 if( !strcmp( me
->mnt_dir
, path_copy
) ) {
605 "libdvdread: Attempting to use device %s"
606 " mounted on %s%s\n",
609 have_css
? " for CSS authentication" : "");
611 auth_drive
= DVDOpenImageFile( me
->mnt_fsname
, have_css
);
613 internal_errno
= errno
;
615 dev_name
= strdup(me
->mnt_fsname
);
621 #elif defined(__MINGW32__)
622 dev_name
= strdup(path
);
623 auth_drive
= DVDOpenImageFile( path
, have_css
);
627 fprintf( stderr
, "libdvdread: Couldn't find device name.\n" );
629 } else if( !auth_drive
) {
631 fprintf( stderr
, "libdvdread: Device %s inaccessible%s: %s\n",
633 have_css
? ", CSS authentication not available" : "",
634 strerror(internal_errno
));
642 * If we've opened a drive, just use that.
648 * Otherwise, we now try to open the directory tree instead.
650 return DVDOpenPath( path
);
653 /* If it's none of the above, screw it. */
655 fprintf( stderr
, "libdvdread: Could not open %s\n", path
);
660 void DVDClose( dvd_reader_t
*dvd
)
663 if( dvd
->dev
) dvdinput_close( dvd
->dev
);
664 if( dvd
->path_root
) free( dvd
->path_root
);
665 if( dvd
->udfcache
) FreeUDFCache( dvd
, dvd
->udfcache
);
667 if(dvd
->verbose
>= 0) {
668 fprintf(stderr
, "libdvdread: DVDClose(): Memory leak in align functions\n");
687 * Open an unencrypted file on a DVD image file.
689 static dvd_file_t
*DVDOpenFileUDF( dvd_reader_t
*dvd
, char *filename
)
692 dvd_file_t
*dvd_file
;
694 start
= UDFFindFile( dvd
, filename
, &len
);
695 if( !start
) return 0;
697 dvd_file
= (dvd_file_t
*) malloc( sizeof( dvd_file_t
) );
698 if( !dvd_file
) return 0;
700 dvd_file
->lb_start
= start
;
701 dvd_file
->seek_pos
= 0;
702 memset( dvd_file
->title_sizes
, 0, sizeof( dvd_file
->title_sizes
) );
703 memset( dvd_file
->title_devs
, 0, sizeof( dvd_file
->title_devs
) );
704 dvd_file
->filesize
= len
/ DVD_VIDEO_LB_LEN
;
710 * Searches for <file> in directory <path>, ignoring case.
711 * Returns 0 and full filename in <filename>.
712 * or -1 on file not found.
713 * or -2 on path not found.
715 static int findDirFile( const char *path
, const char *file
, char *filename
)
720 dir
= opendir( path
);
721 if( !dir
) return -2;
723 while( ( ent
= readdir( dir
) ) != NULL
) {
724 if( !strcasecmp( ent
->d_name
, file
) ) {
725 sprintf( filename
, "%s%s%s", path
,
726 ( ( path
[ strlen( path
) - 1 ] == '/' ) ? "" : "/" ),
736 static int findDVDFile( dvd_reader_t
*dvd
, const char *file
, char *filename
)
738 char video_path
[ PATH_MAX
+ 1 ];
739 const char *nodirfile
;
742 /* Strip off the directory for our search */
743 if( !strncasecmp( "/VIDEO_TS/", file
, 10 ) ) {
744 nodirfile
= &(file
[ 10 ]);
749 ret
= findDirFile( dvd
->path_root
, nodirfile
, filename
);
751 /* Try also with adding the path, just in case. */
752 sprintf( video_path
, "%s/VIDEO_TS/", dvd
->path_root
);
753 ret
= findDirFile( video_path
, nodirfile
, filename
);
755 /* Try with the path, but in lower case. */
756 sprintf( video_path
, "%s/video_ts/", dvd
->path_root
);
757 ret
= findDirFile( video_path
, nodirfile
, filename
);
768 * Open an unencrypted file from a DVD directory tree.
770 static dvd_file_t
*DVDOpenFilePath( dvd_reader_t
*dvd
, char *filename
)
772 char full_path
[ PATH_MAX
+ 1 ];
773 dvd_file_t
*dvd_file
;
774 struct stat fileinfo
;
777 /* Get the full path of the file. */
778 if( !findDVDFile( dvd
, filename
, full_path
) ) return 0;
780 dev
= dvdinput_open( full_path
);
783 dvd_file
= (dvd_file_t
*) malloc( sizeof( dvd_file_t
) );
784 if( !dvd_file
) return 0;
786 dvd_file
->lb_start
= 0;
787 dvd_file
->seek_pos
= 0;
788 memset( dvd_file
->title_sizes
, 0, sizeof( dvd_file
->title_sizes
) );
789 memset( dvd_file
->title_devs
, 0, sizeof( dvd_file
->title_devs
) );
790 dvd_file
->filesize
= 0;
792 if( stat( full_path
, &fileinfo
) < 0 ) {
793 if(dvd
->verbose
>= 1) {
794 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
799 dvd_file
->title_sizes
[ 0 ] = fileinfo
.st_size
/ DVD_VIDEO_LB_LEN
;
800 dvd_file
->title_devs
[ 0 ] = dev
;
801 dvd_file
->filesize
= dvd_file
->title_sizes
[ 0 ];
806 static dvd_file_t
*DVDOpenVOBUDF( dvd_reader_t
*dvd
, int title
, int menu
)
808 char filename
[ MAX_UDF_FILE_NAME_LEN
];
810 dvd_file_t
*dvd_file
;
813 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.VOB" );
815 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, menu
? 0 : 1 );
817 start
= UDFFindFile( dvd
, filename
, &len
);
818 if( start
== 0 ) return 0;
820 dvd_file
= (dvd_file_t
*) malloc( sizeof( dvd_file_t
) );
821 if( !dvd_file
) return 0;
823 /*Hack*/ dvd_file
->css_title
= title
<< 1 | menu
;
824 dvd_file
->lb_start
= start
;
825 dvd_file
->seek_pos
= 0;
826 memset( dvd_file
->title_sizes
, 0, sizeof( dvd_file
->title_sizes
) );
827 memset( dvd_file
->title_devs
, 0, sizeof( dvd_file
->title_devs
) );
828 dvd_file
->filesize
= len
/ DVD_VIDEO_LB_LEN
;
830 /* Calculate the complete file size for every file in the VOBS */
834 for( cur
= 2; cur
< 10; cur
++ ) {
835 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, cur
);
836 if( !UDFFindFile( dvd
, filename
, &len
) ) break;
837 dvd_file
->filesize
+= len
/ DVD_VIDEO_LB_LEN
;
841 if( dvd
->css_state
== 1 /* Need key init */ ) {
842 // initAllCSSKeys( dvd );
843 // dvd->css_state = 2;
846 if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
847 fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
855 static dvd_file_t
*DVDOpenVOBPath( dvd_reader_t
*dvd
, int title
, int menu
)
857 char filename
[ MAX_UDF_FILE_NAME_LEN
];
858 char full_path
[ PATH_MAX
+ 1 ];
859 struct stat fileinfo
;
860 dvd_file_t
*dvd_file
;
863 dvd_file
= (dvd_file_t
*) malloc( sizeof( dvd_file_t
) );
864 if( !dvd_file
) return 0;
866 /*Hack*/ dvd_file
->css_title
= title
<< 1 | menu
;
867 dvd_file
->lb_start
= 0;
868 dvd_file
->seek_pos
= 0;
869 memset( dvd_file
->title_sizes
, 0, sizeof( dvd_file
->title_sizes
) );
870 memset( dvd_file
->title_devs
, 0, sizeof( dvd_file
->title_devs
) );
871 dvd_file
->filesize
= 0;
877 sprintf( filename
, "VIDEO_TS.VOB" );
879 sprintf( filename
, "VTS_%02i_0.VOB", title
);
881 if( !findDVDFile( dvd
, filename
, full_path
) ) {
886 dev
= dvdinput_open( full_path
);
892 if( stat( full_path
, &fileinfo
) < 0 ) {
893 if(dvd
->verbose
>= 1) {
894 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
899 dvd_file
->title_sizes
[ 0 ] = fileinfo
.st_size
/ DVD_VIDEO_LB_LEN
;
900 dvd_file
->title_devs
[ 0 ] = dev
;
901 dvdinput_title( dvd_file
->title_devs
[0], 0);
902 dvd_file
->filesize
= dvd_file
->title_sizes
[ 0 ];
905 for( i
= 0; i
< 9; ++i
) {
907 sprintf( filename
, "VTS_%02i_%i.VOB", title
, i
+ 1 );
908 if( !findDVDFile( dvd
, filename
, full_path
) ) {
912 if( stat( full_path
, &fileinfo
) < 0 ) {
913 if(dvd
->verbose
>= 1) {
914 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
919 dvd_file
->title_sizes
[ i
] = fileinfo
.st_size
/ DVD_VIDEO_LB_LEN
;
920 dvd_file
->title_devs
[ i
] = dvdinput_open( full_path
);
921 dvdinput_title( dvd_file
->title_devs
[ i
], 0 );
922 dvd_file
->filesize
+= dvd_file
->title_sizes
[ i
];
924 if( !dvd_file
->title_devs
[ 0 ] ) {
933 dvd_file_t
*DVDOpenFile( dvd_reader_t
*dvd
, int titlenum
,
934 dvd_read_domain_t domain
)
936 char filename
[ MAX_UDF_FILE_NAME_LEN
];
938 /* Check arguments. */
939 if( dvd
== NULL
|| titlenum
< 0 ) {
945 case DVD_READ_INFO_FILE
:
946 if( titlenum
== 0 ) {
947 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.IFO" );
949 sprintf( filename
, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum
);
952 case DVD_READ_INFO_BACKUP_FILE
:
953 if( titlenum
== 0 ) {
954 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.BUP" );
956 sprintf( filename
, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum
);
959 case DVD_READ_MENU_VOBS
:
960 if( dvd
->isImageFile
) {
961 return DVDOpenVOBUDF( dvd
, titlenum
, 1 );
963 return DVDOpenVOBPath( dvd
, titlenum
, 1 );
966 case DVD_READ_TITLE_VOBS
:
967 if( titlenum
== 0 ) return 0;
968 if( dvd
->isImageFile
) {
969 return DVDOpenVOBUDF( dvd
, titlenum
, 0 );
971 return DVDOpenVOBPath( dvd
, titlenum
, 0 );
975 if(dvd
->verbose
>= 1) {
976 fprintf( stderr
, "libdvdread: Invalid domain for file open.\n" );
982 if( dvd
->isImageFile
) {
983 return DVDOpenFileUDF( dvd
, filename
);
985 return DVDOpenFilePath( dvd
, filename
);
989 void DVDCloseFile( dvd_file_t
*dvd_file
)
994 if( dvd_file
->dvd
->isImageFile
) {
997 for( i
= 0; i
< 9; ++i
) {
998 if( dvd_file
->title_devs
[ i
] ) {
999 dvdinput_close( dvd_file
->title_devs
[i
] );
1009 static int DVDFileStatVOBUDF(dvd_reader_t
*dvd
, int title
,
1010 int menu
, dvd_stat_t
*statbuf
)
1012 char filename
[ MAX_UDF_FILE_NAME_LEN
];
1015 off_t parts_size
[9];
1020 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.VOB" );
1022 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, menu
? 0 : 1 );
1024 if(!UDFFindFile( dvd
, filename
, &size
)) {
1029 parts_size
[0] = size
;
1034 for( cur
= 2; cur
< 10; cur
++ ) {
1035 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, cur
);
1036 if( !UDFFindFile( dvd
, filename
, &size
) ) {
1039 parts_size
[nr_parts
] = size
;
1045 statbuf
->size
= tot_size
;
1046 statbuf
->nr_parts
= nr_parts
;
1047 for(n
= 0; n
< nr_parts
; n
++) {
1048 statbuf
->parts_size
[n
] = parts_size
[n
];
1054 static int DVDFileStatVOBPath( dvd_reader_t
*dvd
, int title
,
1055 int menu
, dvd_stat_t
*statbuf
)
1057 char filename
[ MAX_UDF_FILE_NAME_LEN
];
1058 char full_path
[ PATH_MAX
+ 1 ];
1059 struct stat fileinfo
;
1061 off_t parts_size
[9];
1068 sprintf( filename
, "VIDEO_TS.VOB" );
1070 sprintf( filename
, "VTS_%02d_%d.VOB", title
, menu
? 0 : 1 );
1072 if( !findDVDFile( dvd
, filename
, full_path
) ) {
1076 if( stat( full_path
, &fileinfo
) < 0 ) {
1077 if(dvd
->verbose
>= 1) {
1078 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
1084 tot_size
= fileinfo
.st_size
;
1086 parts_size
[0] = fileinfo
.st_size
;
1091 for( cur
= 2; cur
< 10; cur
++ ) {
1093 sprintf( filename
, "VTS_%02d_%d.VOB", title
, cur
);
1094 if( !findDVDFile( dvd
, filename
, full_path
) ) {
1098 if( stat( full_path
, &fileinfo
) < 0 ) {
1099 if(dvd
->verbose
>= 1) {
1100 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
1105 parts_size
[nr_parts
] = fileinfo
.st_size
;
1106 tot_size
+= parts_size
[nr_parts
];
1111 statbuf
->size
= tot_size
;
1112 statbuf
->nr_parts
= nr_parts
;
1113 for(n
= 0; n
< nr_parts
; n
++) {
1114 statbuf
->parts_size
[n
] = parts_size
[n
];
1120 int DVDFileStat(dvd_reader_t
*dvd
, int titlenum
,
1121 dvd_read_domain_t domain
, dvd_stat_t
*statbuf
)
1123 char filename
[ MAX_UDF_FILE_NAME_LEN
];
1124 char full_path
[ PATH_MAX
+ 1 ];
1125 struct stat fileinfo
;
1128 /* Check arguments. */
1129 if( dvd
== NULL
|| titlenum
< 0 ) {
1135 case DVD_READ_INFO_FILE
:
1136 if( titlenum
== 0 ) {
1137 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.IFO" );
1139 sprintf( filename
, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum
);
1142 case DVD_READ_INFO_BACKUP_FILE
:
1143 if( titlenum
== 0 ) {
1144 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.BUP" );
1146 sprintf( filename
, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum
);
1149 case DVD_READ_MENU_VOBS
:
1150 if( dvd
->isImageFile
) {
1151 return DVDFileStatVOBUDF( dvd
, titlenum
, 1, statbuf
);
1153 return DVDFileStatVOBPath( dvd
, titlenum
, 1, statbuf
);
1156 case DVD_READ_TITLE_VOBS
:
1157 if( titlenum
== 0 ) {
1160 if( dvd
->isImageFile
) {
1161 return DVDFileStatVOBUDF( dvd
, titlenum
, 0, statbuf
);
1163 return DVDFileStatVOBPath( dvd
, titlenum
, 0, statbuf
);
1167 if(dvd
->verbose
>= 1) {
1168 fprintf( stderr
, "libdvdread: Invalid domain for file stat.\n" );
1174 if( dvd
->isImageFile
) {
1175 if( UDFFindFile( dvd
, filename
, &size
) ) {
1176 statbuf
->size
= size
;
1177 statbuf
->nr_parts
= 1;
1178 statbuf
->parts_size
[0] = size
;
1182 if( findDVDFile( dvd
, filename
, full_path
) ) {
1183 if( stat( full_path
, &fileinfo
) < 0 ) {
1184 if(dvd
->verbose
>= 1) {
1185 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
1188 statbuf
->size
= fileinfo
.st_size
;
1189 statbuf
->nr_parts
= 1;
1190 statbuf
->parts_size
[0] = statbuf
->size
;
1199 * Internal, but used from dvd_udf.c
1201 * @param device A read handle.
1202 * @param lb_number Logical block number to start read from.
1203 * @param block_count Number of logical blocks to read.
1204 * @param data Pointer to buffer where read data should be stored.
1205 * This buffer must be large enough to hold lb_number*2048 bytes.
1206 * The pointer must be aligned to the logical block size when
1207 * reading from a raw/O_DIRECT device.
1208 * @param encrypted 0 if no decryption shall be performed,
1209 * 1 if decryption shall be performed
1210 * @param return Returns number of blocks read on success, negative on error
1212 int UDFReadBlocksRaw( dvd_reader_t
*device
, uint32_t lb_number
,
1213 size_t block_count
, unsigned char *data
,
1218 if( !device
->dev
) {
1219 if(device
->verbose
>= 1) {
1220 fprintf( stderr
, "libdvdread: Fatal error in block read.\n" );
1225 ret
= dvdinput_seek( device
->dev
, (int) lb_number
);
1226 if( ret
!= (int) lb_number
) {
1227 if(device
->verbose
>= 1) {
1229 "libdvdread: UDFReadBlocksRaw: Can't seek to block %u\n",
1235 return dvdinput_read( device
->dev
, (char *) data
,
1236 (int) block_count
, encrypted
);
1240 * This is using a single input and starting from 'dvd_file->lb_start' offset.
1242 * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
1243 * into the buffer located at 'data' and if 'encrypted' is set
1244 * descramble the data if it's encrypted. Returning either an
1245 * negative error or the number of blocks read.
1247 * @param data Pointer to buffer where read data should be placed.
1248 * This buffer must be large enough to hold block_count*2048 bytes.
1249 * The pointer must be aligned to 2048 bytes when reading from
1250 * a raw/O_DIRECT device.
1251 * @return Returns the number of blocks read on success or a negative error.
1253 static int DVDReadBlocksUDF( dvd_file_t
*dvd_file
, uint32_t offset
,
1254 size_t block_count
, unsigned char *data
,
1257 return UDFReadBlocksRaw( dvd_file
->dvd
, dvd_file
->lb_start
+ offset
,
1258 block_count
, data
, encrypted
);
1262 * This is using possibly several inputs and starting from an offset of '0'.
1263 * data must be aligned to logical block size (2048 bytes) of the device
1264 * for raw/O_DIRECT devices to work
1265 * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
1266 * into the buffer located at 'data' and if 'encrypted' is set
1267 * descramble the data if it's encrypted. Returning either an
1268 * negative error or the number of blocks read.
1270 * @param dvd_file A file read handle.
1271 * @param offset Block offset from start of file.
1272 * @return Returns number of blocks read on success, negative on error.
1274 static int DVDReadBlocksPath( dvd_file_t
*dvd_file
, unsigned int offset
,
1275 size_t block_count
, unsigned char *data
,
1283 for( i
= 0; i
< 9; ++i
) {
1284 if( !dvd_file
->title_sizes
[ i
] ) return 0; /* Past end of file */
1286 if( offset
< dvd_file
->title_sizes
[ i
] ) {
1287 if( ( offset
+ block_count
) <= dvd_file
->title_sizes
[ i
] ) {
1288 off
= dvdinput_seek( dvd_file
->title_devs
[ i
], (int)offset
);
1289 if( off
< 0 || off
!= (int)offset
) {
1290 if(dvd_file
->dvd
->verbose
>= 1) {
1291 fprintf( stderr
, "libdvdread: DVDReadBlocksPath1: Can't seek to block %d\n",
1294 return off
< 0 ? off
: 0;
1296 ret
= dvdinput_read( dvd_file
->title_devs
[ i
], data
,
1297 (int)block_count
, encrypted
);
1300 size_t part1_size
= dvd_file
->title_sizes
[ i
] - offset
;
1301 /* FIXME: Really needs to be a while loop.
1302 * (This is only true if you try and read >1GB at a time) */
1305 off
= dvdinput_seek( dvd_file
->title_devs
[ i
], (int)offset
);
1306 if( off
< 0 || off
!= (int)offset
) {
1307 if(dvd_file
->dvd
->verbose
>= 1) {
1308 fprintf( stderr
, "libdvdread: DVDReadBlocksPath2: Can't seek to block %d\n",
1311 return off
< 0 ? off
: 0;
1313 ret
= dvdinput_read( dvd_file
->title_devs
[ i
], data
,
1314 (int)part1_size
, encrypted
);
1315 if( ret
< 0 ) return ret
;
1316 /* FIXME: This is wrong if i is the last file in the set.
1317 * also error from this read will not show in ret. */
1319 /* Does the next part exist? If not then return now. */
1320 if( !dvd_file
->title_devs
[ i
+ 1 ] ) return ret
;
1323 off
= dvdinput_seek( dvd_file
->title_devs
[ i
+ 1 ], 0 );
1324 if( off
< 0 || off
!= 0 ) {
1325 if(dvd_file
->dvd
->verbose
>= 1) {
1326 fprintf( stderr
, "libdvdread: DVDReadBlocksPath3: Can't seek to block %d\n", 0 );
1328 return off
< 0 ? off
: 0;
1330 ret2
= dvdinput_read( dvd_file
->title_devs
[ i
+ 1 ],
1332 * (int64_t)DVD_VIDEO_LB_LEN
),
1333 (int)(block_count
- part1_size
),
1335 if( ret2
< 0 ) return ret2
;
1339 offset
-= dvd_file
->title_sizes
[ i
];
1347 * This is broken reading more than 2Gb at a time if ssize_t is 32-bit.
1349 ssize_t
DVDReadBlocks( dvd_file_t
*dvd_file
, int offset
,
1350 size_t block_count
, unsigned char *data
)
1354 /* Check arguments. */
1355 if( dvd_file
== NULL
|| offset
< 0 || data
== NULL
)
1358 /* Hack, and it will still fail for multiple opens in a threaded app ! */
1359 if( dvd_file
->dvd
->css_title
!= dvd_file
->css_title
) {
1360 dvd_file
->dvd
->css_title
= dvd_file
->css_title
;
1361 if( dvd_file
->dvd
->isImageFile
) {
1362 dvdinput_title( dvd_file
->dvd
->dev
, (int)dvd_file
->lb_start
);
1364 /* Here each vobu has it's own dvdcss handle, so no need to update
1366 dvdinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start );
1370 if( dvd_file
->dvd
->isImageFile
) {
1371 ret
= DVDReadBlocksUDF( dvd_file
, (uint32_t)offset
,
1372 block_count
, data
, DVDINPUT_READ_DECRYPT
);
1374 ret
= DVDReadBlocksPath( dvd_file
, (unsigned int)offset
,
1375 block_count
, data
, DVDINPUT_READ_DECRYPT
);
1378 return (ssize_t
)ret
;
1381 int DVDFileSeek( dvd_file_t
*dvd_file
, int offset
)
1383 /* Check arguments. */
1384 if( dvd_file
== NULL
|| offset
< 0 )
1387 if( offset
> dvd_file
->filesize
* DVD_VIDEO_LB_LEN
) {
1390 dvd_file
->seek_pos
= (uint32_t) offset
;
1394 #ifndef HAVE_UINTPTR_T
1395 #warning "Assuming that (unsigned long) can hold (void *)"
1396 typedef unsigned long uintptr_t;
1399 #define DVD_ALIGN(ptr) (void *)((((uintptr_t)(ptr)) + (DVD_VIDEO_LB_LEN-1)) \
1400 / DVD_VIDEO_LB_LEN * DVD_VIDEO_LB_LEN)
1402 ssize_t
DVDReadBytes( dvd_file_t
*dvd_file
, void *data
, size_t byte_size
)
1404 unsigned char *secbuf_start
;
1405 unsigned char *secbuf
; //must be aligned to 2048-bytes for raw/O_DIRECT
1406 unsigned int numsec
, seek_sector
, seek_byte
;
1409 /* Check arguments. */
1410 if( dvd_file
== NULL
|| data
== NULL
) {
1414 seek_sector
= dvd_file
->seek_pos
/ DVD_VIDEO_LB_LEN
;
1415 seek_byte
= dvd_file
->seek_pos
% DVD_VIDEO_LB_LEN
;
1417 numsec
= ( ( seek_byte
+ byte_size
) / DVD_VIDEO_LB_LEN
) +
1418 ( ( ( seek_byte
+ byte_size
) % DVD_VIDEO_LB_LEN
) ? 1 : 0 );
1420 /* must align to 2048 bytes if we are reading from raw/O_DIRECT */
1421 secbuf_start
= (unsigned char *) malloc( (numsec
+1) * DVD_VIDEO_LB_LEN
);
1422 if( !secbuf_start
) {
1423 /* errno will be set to ENOMEM by malloc */
1427 secbuf
= DVD_ALIGN(secbuf_start
);
1429 if( dvd_file
->dvd
->isImageFile
) {
1430 ret
= DVDReadBlocksUDF( dvd_file
, (uint32_t) seek_sector
,
1431 (size_t) numsec
, secbuf
, DVDINPUT_NOFLAGS
);
1433 ret
= DVDReadBlocksPath( dvd_file
, seek_sector
,
1434 (size_t) numsec
, secbuf
, DVDINPUT_NOFLAGS
);
1437 if( ret
!= (int) numsec
) {
1438 free( secbuf_start
);
1439 return ret
< 0 ? ret
: 0;
1442 memcpy( data
, &(secbuf
[ seek_byte
]), byte_size
);
1443 free( secbuf_start
);
1445 dvd_file
->seek_pos
+= byte_size
;
1449 ssize_t
DVDFileSize( dvd_file_t
*dvd_file
)
1451 /* Check arguments. */
1452 if( dvd_file
== NULL
)
1455 return dvd_file
->filesize
;
1458 int DVDDiscID( dvd_reader_t
*dvd
, unsigned char *discid
)
1462 int nr_of_files
= 0;
1464 int nofiles_errno
= ENOENT
;
1465 /* Check arguments. */
1466 if( dvd
== NULL
|| discid
== NULL
) {
1470 /* Go through the first 10 IFO:s, in order,
1471 * and md5sum them, i.e VIDEO_TS.IFO and VTS_0?_0.IFO */
1472 md5_init_ctx( &ctx
);
1473 for( title
= 0; title
< 10; title
++ ) {
1474 dvd_file_t
*dvd_file
= DVDOpenFile( dvd
, title
, DVD_READ_INFO_FILE
);
1475 if( dvd_file
!= NULL
) {
1477 size_t file_size
= dvd_file
->filesize
* DVD_VIDEO_LB_LEN
;
1478 char *buffer
= malloc( file_size
);
1482 if( buffer
== NULL
) {
1483 /* errno will be set to ENOMEM by malloc */
1487 bytes_read
= DVDReadBytes( dvd_file
, buffer
, file_size
);
1488 if( bytes_read
!= file_size
) {
1490 if(dvd
->verbose
>= 1) {
1491 fprintf( stderr
, "libdvdread: DVDDiscId read returned %d bytes"
1492 ", wanted %d\n", (int)bytes_read
, (int)file_size
);
1495 DVDCloseFile( dvd_file
);
1500 md5_process_bytes( buffer
, file_size
, &ctx
);
1502 DVDCloseFile( dvd_file
);
1505 if(errno
!= ENOENT
) {
1506 nofiles_errno
= errno
;
1510 md5_finish_ctx( &ctx
, discid
);
1511 if(nr_of_files
== 0) {
1512 errno
= nofiles_errno
;
1519 int DVDISOVolumeInfo( dvd_reader_t
*dvd
,
1520 char *volid
, unsigned int volid_size
,
1521 unsigned char *volsetid
, unsigned int volsetid_size
)
1523 unsigned char *buffer
; /* must be aligned to 2048 for raw/O_DIRECT */
1524 unsigned char *buffer_start
;
1527 /* Check arguments. */
1533 if( dvd
->dev
== NULL
) {
1534 /* No block access, so no ISO... */
1539 buffer_start
= malloc( 2 * DVD_VIDEO_LB_LEN
);
1540 if( buffer_start
== NULL
) {
1544 buffer
= DVD_ALIGN(buffer_start
);
1546 ret
= UDFReadBlocksRaw( dvd
, 16, 1, buffer
, 0 );
1548 if(dvd
->verbose
>= 1) {
1549 fprintf( stderr
, "libdvdread: DVDISOVolumeInfo, failed to "
1550 "read ISO9660 Primary Volume Descriptor!\n" );
1556 if( (volid
!= NULL
) && (volid_size
> 0) ) {
1558 for(n
= 0; n
< 32; n
++) {
1559 if(buffer
[40+n
] == 0x20) {
1564 if(volid_size
> n
+1) {
1568 memcpy(volid
, &buffer
[40], volid_size
-1);
1569 volid
[volid_size
-1] = '\0';
1572 if( (volsetid
!= NULL
) && (volsetid_size
> 0) ) {
1573 if(volsetid_size
> 128) {
1574 volsetid_size
= 128;
1576 memcpy(volsetid
, &buffer
[190], volsetid_size
);
1584 int DVDUDFVolumeInfo( dvd_reader_t
*dvd
,
1585 char *volid
, unsigned int volid_size
,
1586 unsigned char *volsetid
, unsigned int volsetid_size
)
1589 /* Check arguments. */
1593 if( dvd
->dev
== NULL
) {
1594 /* No block access, so no UDF VolumeSet Identifier */
1598 if( (volid
!= NULL
) && (volid_size
> 0) ) {
1599 ret
= UDFGetVolumeIdentifier(dvd
, volid
, volid_size
);
1604 if( (volsetid
!= NULL
) && (volsetid_size
> 0) ) {
1605 ret
= UDFGetVolumeSetIdentifier(dvd
, volsetid
, volsetid_size
);