2 * Copyright (C) 2001-2004 Billy Biggs <vektor@dumbterm.net>,
3 * Håkan Hjort <d95hjort@dtek.chalmers.se>,
4 * Björn Englund <d4bjorn@dtek.chalmers.se>
6 * This file is part of libdvdread.
8 * libdvdread is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * libdvdread is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with libdvdread; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <sys/types.h>
25 #include <sys/time.h> /* For the timing of dvdcss_title crack. */
35 /* misc win32 helpers */
37 #ifndef HAVE_GETTIMEOFDAY
38 /* replacement gettimeofday implementation */
39 #include <sys/timeb.h>
40 static inline int _private_gettimeofday( struct timeval
*tv
, void *tz
)
45 tv
->tv_usec
= t
.millitm
* 1000;
48 #define gettimeofday(TV, TZ) _private_gettimeofday((TV), (TZ))
50 #include <io.h> /* read() */
51 #define lseek64 _lseeki64
54 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__) || defined(__APPLE__)
59 #include <sys/mnttab.h>
60 #elif defined(SYS_BSD)
62 #elif defined(__linux__)
66 #include "dvdread/dvd_udf.h"
67 #include "dvd_input.h"
68 #include "dvdread/dvd_reader.h"
71 #define DEFAULT_UDF_CACHE_LEVEL 1
74 /* Basic information. */
77 /* Hack for keeping track of the css status.
78 * 0: no css, 1: perhaps (need init of keys), 2: have done init */
80 int css_title
; /* Last title that we have called dvdinpute_title for. */
82 /* Information required for an image file. */
85 /* Information required for a directory path drive. */
88 /* Filesystem cache */
89 int udfcache_level
; /* 0 - turned off, 1 - on */
96 /* Basic information. */
99 /* Hack for selecting the right css title. */
102 /* Information required for an image file. */
106 /* Information required for a directory path drive. */
107 size_t title_sizes
[ TITLES_MAX
];
108 dvd_input_t title_devs
[ TITLES_MAX
];
110 /* Calculated at open-time, size in blocks. */
114 int UDFReadBlocksRaw( dvd_reader_t
*device
, uint32_t lb_number
,
115 size_t block_count
, unsigned char *data
,
119 * Set the level of caching on udf
120 * level = 0 (no caching)
121 * level = 1 (caching filesystem info)
123 int DVDUDFCacheLevel(dvd_reader_t
*device
, int level
)
125 struct dvd_reader_s
*dev
= (struct dvd_reader_s
*)device
;
129 } else if(level
< 0) {
130 return dev
->udfcache_level
;
133 dev
->udfcache_level
= level
;
138 void *GetUDFCacheHandle(dvd_reader_t
*device
)
140 struct dvd_reader_s
*dev
= (struct dvd_reader_s
*)device
;
142 return dev
->udfcache
;
145 void SetUDFCacheHandle(dvd_reader_t
*device
, void *cache
)
147 struct dvd_reader_s
*dev
= (struct dvd_reader_s
*)device
;
149 dev
->udfcache
= cache
;
154 /* Loop over all titles and call dvdcss_title to crack the keys. */
155 static int initAllCSSKeys( dvd_reader_t
*dvd
)
157 struct timeval all_s
, all_e
;
158 struct timeval t_s
, t_e
;
159 char filename
[ MAX_UDF_FILE_NAME_LEN
];
163 char *nokeys_str
= getenv("DVDREAD_NOKEYS");
164 if(nokeys_str
!= NULL
)
167 fprintf( stderr
, "\n" );
168 fprintf( stderr
, "libdvdread: Attempting to retrieve all CSS keys\n" );
169 fprintf( stderr
, "libdvdread: This can take a _long_ time, "
170 "please be patient\n\n" );
171 gettimeofday(&all_s
, NULL
);
173 for( title
= 0; title
< 100; title
++ ) {
174 gettimeofday( &t_s
, NULL
);
176 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.VOB" );
178 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, 0 );
180 start
= UDFFindFile( dvd
, filename
, &len
);
181 if( start
!= 0 && len
!= 0 ) {
182 /* Perform CSS key cracking for this title. */
183 fprintf( stderr
, "libdvdread: Get key for %s at 0x%08x\n",
185 if( dvdinput_title( dvd
->dev
, (int)start
) < 0 ) {
186 fprintf( stderr
, "libdvdread: Error cracking CSS key for %s (0x%08x)\n", filename
, start
);
188 gettimeofday( &t_e
, NULL
);
189 fprintf( stderr
, "libdvdread: Elapsed time %ld\n",
190 (long int) t_e
.tv_sec
- t_s
.tv_sec
);
193 if( title
== 0 ) continue;
195 gettimeofday( &t_s
, NULL
);
196 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, 1 );
197 start
= UDFFindFile( dvd
, filename
, &len
);
198 if( start
== 0 || len
== 0 ) break;
200 /* Perform CSS key cracking for this title. */
201 fprintf( stderr
, "libdvdread: Get key for %s at 0x%08x\n",
203 if( dvdinput_title( dvd
->dev
, (int)start
) < 0 ) {
204 fprintf( stderr
, "libdvdread: Error cracking CSS key for %s (0x%08x)!!\n", filename
, start
);
206 gettimeofday( &t_e
, NULL
);
207 fprintf( stderr
, "libdvdread: Elapsed time %ld\n",
208 (long int) t_e
.tv_sec
- t_s
.tv_sec
);
212 fprintf( stderr
, "libdvdread: Found %d VTS's\n", title
);
213 gettimeofday(&all_e
, NULL
);
214 fprintf( stderr
, "libdvdread: Elapsed time %ld\n",
215 (long int) all_e
.tv_sec
- all_s
.tv_sec
);
223 * Open a DVD image or block device file.
225 static dvd_reader_t
*DVDOpenImageFile( const char *location
, int have_css
)
230 dev
= dvdinput_open( location
);
232 fprintf( stderr
, "libdvdread: Can't open %s for reading\n", location
);
236 dvd
= (dvd_reader_t
*) malloc( sizeof( dvd_reader_t
) );
241 dvd
->isImageFile
= 1;
243 dvd
->path_root
= NULL
;
245 dvd
->udfcache_level
= DEFAULT_UDF_CACHE_LEVEL
;
246 dvd
->udfcache
= NULL
;
249 /* Only if DVDCSS_METHOD = title, a bit if it's disc or if
250 * DVDCSS_METHOD = key but region mismatch. Unfortunately we
251 * don't have that information. */
253 dvd
->css_state
= 1; /* Need key init. */
260 static dvd_reader_t
*DVDOpenPath( const char *path_root
)
264 dvd
= (dvd_reader_t
*) malloc( sizeof( dvd_reader_t
) );
265 if( !dvd
) return NULL
;
266 dvd
->isImageFile
= 0;
268 dvd
->path_root
= strdup( path_root
);
269 if(!dvd
->path_root
) {
273 dvd
->udfcache_level
= DEFAULT_UDF_CACHE_LEVEL
;
274 dvd
->udfcache
= NULL
;
276 dvd
->css_state
= 0; /* Only used in the UDF path */
277 dvd
->css_title
= 0; /* Only matters in the UDF path */
283 /* /dev/rdsk/c0t6d0s0 (link to /devices/...)
284 /vol/dev/rdsk/c0t6d0/??
286 static char *sun_block2char( const char *path
)
290 /* Must contain "/dsk/" */
291 if( !strstr( path
, "/dsk/" ) ) return (char *) strdup( path
);
293 /* Replace "/dsk/" with "/rdsk/" */
294 new_path
= malloc( strlen(path
) + 2 );
295 strcpy( new_path
, path
);
296 strcpy( strstr( new_path
, "/dsk/" ), "" );
297 strcat( new_path
, "/rdsk/" );
298 strcat( new_path
, strstr( path
, "/dsk/" ) + strlen( "/dsk/" ) );
305 /* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recommended to _not_ use r
306 OpenBSD /dev/rcd0c, it needs to be the raw device
307 NetBSD /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others
308 Darwin /dev/rdisk0, it needs to be the raw device
309 BSD/OS /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do) */
310 static char *bsd_block2char( const char *path
)
314 /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */
315 if( !strncmp( path
, "/dev/", 5 ) || strncmp( path
, "/dev/r", 6 ) )
316 return (char *) strdup( path
);
318 /* Replace "/dev/" with "/dev/r" */
319 new_path
= malloc( strlen(path
) + 2 );
320 strcpy( new_path
, "/dev/r" );
321 strcat( new_path
, path
+ strlen( "/dev/" ) );
328 dvd_reader_t
*DVDOpen( const char *ppath
)
330 struct stat fileinfo
;
331 int ret
, have_css
, retval
, cdir
= 0;
332 dvd_reader_t
*ret_val
= NULL
;
333 char *dev_name
= NULL
;
334 char *path
= NULL
, *new_path
= NULL
, *path_copy
= NULL
;
343 path
= strdup(ppath
);
347 /* Try to open libdvdcss or fall back to standard functions */
348 have_css
= dvdinput_setup();
351 /* Strip off the trailing \ if it is not a drive */
354 (path
[len
- 1] == '\\') &&
355 (path
[len
- 2] != ':'))
361 ret
= stat( path
, &fileinfo
);
365 /* maybe "host:port" url? try opening it with acCeSS library */
366 if( strchr(path
,':') ) {
367 ret_val
= DVDOpenImageFile( path
, have_css
);
372 /* If we can't stat the file, give up */
373 fprintf( stderr
, "libdvdread: Can't stat %s\n", path
);
378 /* First check if this is a block/char device or a file*/
379 if( S_ISBLK( fileinfo
.st_mode
) ||
380 S_ISCHR( fileinfo
.st_mode
) ||
381 S_ISREG( fileinfo
.st_mode
) ) {
384 * Block devices and regular files are assumed to be DVD-Video images.
387 ret_val
= DVDOpenImageFile( sun_block2char( path
), have_css
);
388 #elif defined(SYS_BSD)
389 ret_val
= DVDOpenImageFile( bsd_block2char( path
), have_css
);
391 ret_val
= DVDOpenImageFile( path
, have_css
);
397 } else if( S_ISDIR( fileinfo
.st_mode
) ) {
398 dvd_reader_t
*auth_drive
= 0;
401 #elif defined(__sun) || defined(__linux__)
405 /* XXX: We should scream real loud here. */
406 if( !(path_copy
= strdup( path
) ) )
409 #ifndef WIN32 /* don't have fchdir, and getcwd( NULL, ... ) is strange */
410 /* Also WIN32 does not have symlinks, so we don't need this bit of code. */
412 /* Resolve any symlinks and get the absolute dir name. */
414 if( ( cdir
= open( ".", O_RDONLY
) ) >= 0 ) {
415 if( chdir( path_copy
) == -1 ) {
418 new_path
= malloc(PATH_MAX
+1);
422 if( getcwd( new_path
, PATH_MAX
) == NULL
) {
425 retval
= fchdir( cdir
);
431 path_copy
= new_path
;
438 * If we're being asked to open a directory, check if that directory
439 * is the mount point for a DVD-ROM which we can use instead.
442 if( strlen( path_copy
) > 1 ) {
443 if( path_copy
[ strlen( path_copy
) - 1 ] == '/' ) {
444 path_copy
[ strlen( path_copy
) - 1 ] = '\0';
448 if( strlen( path_copy
) > TITLES_MAX
) {
449 if( !strcasecmp( &(path_copy
[ strlen( path_copy
) - TITLES_MAX
]),
451 path_copy
[ strlen( path_copy
) - TITLES_MAX
] = '\0';
455 if(path_copy
[0] == '\0') {
461 if( ( fe
= getfsfile( path_copy
) ) ) {
462 dev_name
= bsd_block2char( fe
->fs_spec
);
464 "libdvdread: Attempting to use device %s"
465 " mounted on %s for CSS authentication\n",
468 auth_drive
= DVDOpenImageFile( dev_name
, have_css
);
471 mntfile
= fopen( MNTTAB
, "r" );
476 while( ( res
= getmntent( mntfile
, &mp
) ) != -1 ) {
477 if( res
== 0 && !strcmp( mp
.mnt_mountp
, path_copy
) ) {
478 dev_name
= sun_block2char( mp
.mnt_special
);
480 "libdvdread: Attempting to use device %s"
481 " mounted on %s for CSS authentication\n",
484 auth_drive
= DVDOpenImageFile( dev_name
, have_css
);
490 #elif defined(__linux__)
491 mntfile
= fopen( MOUNTED
, "r" );
495 while( ( me
= getmntent( mntfile
) ) ) {
496 if( !strcmp( me
->mnt_dir
, path_copy
) ) {
498 "libdvdread: Attempting to use device %s"
499 " mounted on %s for CSS authentication\n",
502 auth_drive
= DVDOpenImageFile( me
->mnt_fsname
, have_css
);
503 dev_name
= strdup(me
->mnt_fsname
);
509 #elif defined(_MSC_VER) || defined(__OS2__)
510 auth_drive
= DVDOpenImageFile( path
, have_css
);
513 #if !defined(_MSC_VER) && !defined(__OS2__)
515 fprintf( stderr
, "libdvdread: Couldn't find device name.\n" );
516 } else if( !auth_drive
) {
517 fprintf( stderr
, "libdvdread: Device %s inaccessible, "
518 "CSS authentication not available.\n", dev_name
);
522 fprintf( stderr
, "libdvdread: Device %s inaccessible, "
523 "CSS authentication not available.\n", dev_name
);
533 * If we've opened a drive, just use that.
540 * Otherwise, we now try to open the directory tree instead.
542 ret_val
= DVDOpenPath( path
);
548 /* If it's none of the above, screw it. */
549 fprintf( stderr
, "libdvdread: Could not open %s\n", path
);
552 if ( path_copy
!= NULL
)
556 if ( new_path
!= NULL
)
561 void DVDClose( dvd_reader_t
*dvd
)
564 if( dvd
->dev
) dvdinput_close( dvd
->dev
);
565 if( dvd
->path_root
) free( dvd
->path_root
);
566 if( dvd
->udfcache
) FreeUDFCache( dvd
->udfcache
);
572 * Open an unencrypted file on a DVD image file.
574 static dvd_file_t
*DVDOpenFileUDF( dvd_reader_t
*dvd
, char *filename
)
577 dvd_file_t
*dvd_file
;
579 start
= UDFFindFile( dvd
, filename
, &len
);
581 fprintf( stderr
, "libdvdnav:DVDOpenFileUDF:UDFFindFile %s failed\n", filename
);
585 dvd_file
= (dvd_file_t
*) malloc( sizeof( dvd_file_t
) );
587 fprintf( stderr
, "libdvdnav:DVDOpenFileUDF:malloc failed\n" );
591 dvd_file
->lb_start
= start
;
592 dvd_file
->seek_pos
= 0;
593 memset( dvd_file
->title_sizes
, 0, sizeof( dvd_file
->title_sizes
) );
594 memset( dvd_file
->title_devs
, 0, sizeof( dvd_file
->title_devs
) );
595 dvd_file
->filesize
= len
/ DVD_VIDEO_LB_LEN
;
601 * Searches for <file> in directory <path>, ignoring case.
602 * Returns 0 and full filename in <filename>.
603 * or -1 on file not found.
604 * or -2 on path not found.
606 static int findDirFile( const char *path
, const char *file
, char *filename
)
611 dir
= opendir( path
);
612 if( !dir
) return -2;
614 while( ( ent
= readdir( dir
) ) != NULL
) {
615 if( !strcasecmp( ent
->d_name
, file
) ) {
616 sprintf( filename
, "%s%s%s", path
,
617 ( ( path
[ strlen( path
) - 1 ] == '/' ) ? "" : "/" ),
627 static int findDVDFile( dvd_reader_t
*dvd
, const char *file
, char *filename
)
629 char video_path
[ PATH_MAX
+ 1 ];
630 const char *nodirfile
;
633 /* Strip off the directory for our search */
634 if( !strncasecmp( "/VIDEO_TS/", file
, 10 ) ) {
635 nodirfile
= &(file
[ 10 ]);
640 ret
= findDirFile( dvd
->path_root
, nodirfile
, filename
);
642 /* Try also with adding the path, just in case. */
643 sprintf( video_path
, "%s/VIDEO_TS/", dvd
->path_root
);
644 ret
= findDirFile( video_path
, nodirfile
, filename
);
646 /* Try with the path, but in lower case. */
647 sprintf( video_path
, "%s/video_ts/", dvd
->path_root
);
648 ret
= findDirFile( video_path
, nodirfile
, filename
);
659 * Open an unencrypted file from a DVD directory tree.
661 static dvd_file_t
*DVDOpenFilePath( dvd_reader_t
*dvd
, char *filename
)
663 char full_path
[ PATH_MAX
+ 1 ];
664 dvd_file_t
*dvd_file
;
665 struct stat fileinfo
;
668 /* Get the full path of the file. */
669 if( !findDVDFile( dvd
, filename
, full_path
) ) {
670 fprintf( stderr
, "libdvdnav:DVDOpenFilePath:findDVDFile %s failed\n", filename
);
674 dev
= dvdinput_open( full_path
);
676 fprintf( stderr
, "libdvdnav:DVDOpenFilePath:dvdinput_open %s failed\n", full_path
);
680 dvd_file
= (dvd_file_t
*) malloc( sizeof( dvd_file_t
) );
682 fprintf( stderr
, "libdvdnav:DVDOpenFilePath:dvd_file malloc failed\n" );
687 dvd_file
->lb_start
= 0;
688 dvd_file
->seek_pos
= 0;
689 memset( dvd_file
->title_sizes
, 0, sizeof( dvd_file
->title_sizes
) );
690 memset( dvd_file
->title_devs
, 0, sizeof( dvd_file
->title_devs
) );
691 dvd_file
->filesize
= 0;
693 if( stat( full_path
, &fileinfo
) < 0 ) {
694 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
698 dvd_file
->title_sizes
[ 0 ] = fileinfo
.st_size
/ DVD_VIDEO_LB_LEN
;
699 dvd_file
->title_devs
[ 0 ] = dev
;
700 dvd_file
->filesize
= dvd_file
->title_sizes
[ 0 ];
705 static dvd_file_t
*DVDOpenVOBUDF( dvd_reader_t
*dvd
, int title
, int menu
)
707 char filename
[ MAX_UDF_FILE_NAME_LEN
];
709 dvd_file_t
*dvd_file
;
712 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.VOB" );
714 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, menu
? 0 : 1 );
716 start
= UDFFindFile( dvd
, filename
, &len
);
717 if( start
== 0 ) return NULL
;
719 dvd_file
= (dvd_file_t
*) malloc( sizeof( dvd_file_t
) );
720 if( !dvd_file
) return NULL
;
722 /*Hack*/ dvd_file
->css_title
= title
<< 1 | menu
;
723 dvd_file
->lb_start
= start
;
724 dvd_file
->seek_pos
= 0;
725 memset( dvd_file
->title_sizes
, 0, sizeof( dvd_file
->title_sizes
) );
726 memset( dvd_file
->title_devs
, 0, sizeof( dvd_file
->title_devs
) );
727 dvd_file
->filesize
= len
/ DVD_VIDEO_LB_LEN
;
729 /* Calculate the complete file size for every file in the VOBS */
733 for( cur
= 2; cur
< 10; cur
++ ) {
734 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, cur
);
735 if( !UDFFindFile( dvd
, filename
, &len
) ) break;
736 dvd_file
->filesize
+= len
/ DVD_VIDEO_LB_LEN
;
740 if( dvd
->css_state
== 1 /* Need key init */ ) {
741 initAllCSSKeys( dvd
);
745 if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
746 fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
754 static dvd_file_t
*DVDOpenVOBPath( dvd_reader_t
*dvd
, int title
, int menu
)
756 char filename
[ MAX_UDF_FILE_NAME_LEN
];
757 char full_path
[ PATH_MAX
+ 1 ];
758 struct stat fileinfo
;
759 dvd_file_t
*dvd_file
;
762 dvd_file
= (dvd_file_t
*) malloc( sizeof( dvd_file_t
) );
763 if( !dvd_file
) return NULL
;
765 /*Hack*/ dvd_file
->css_title
= title
<< 1 | menu
;
766 dvd_file
->lb_start
= 0;
767 dvd_file
->seek_pos
= 0;
768 memset( dvd_file
->title_sizes
, 0, sizeof( dvd_file
->title_sizes
) );
769 memset( dvd_file
->title_devs
, 0, sizeof( dvd_file
->title_devs
) );
770 dvd_file
->filesize
= 0;
776 sprintf( filename
, "VIDEO_TS.VOB" );
778 sprintf( filename
, "VTS_%02i_0.VOB", title
);
780 if( !findDVDFile( dvd
, filename
, full_path
) ) {
785 dev
= dvdinput_open( full_path
);
791 if( stat( full_path
, &fileinfo
) < 0 ) {
792 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
797 dvd_file
->title_sizes
[ 0 ] = fileinfo
.st_size
/ DVD_VIDEO_LB_LEN
;
798 dvd_file
->title_devs
[ 0 ] = dev
;
799 dvdinput_title( dvd_file
->title_devs
[0], 0);
800 dvd_file
->filesize
= dvd_file
->title_sizes
[ 0 ];
803 for( i
= 0; i
< TITLES_MAX
; ++i
) {
805 sprintf( filename
, "VTS_%02i_%i.VOB", title
, i
+ 1 );
806 if( !findDVDFile( dvd
, filename
, full_path
) ) {
810 if( stat( full_path
, &fileinfo
) < 0 ) {
811 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
815 dvd_file
->title_sizes
[ i
] = fileinfo
.st_size
/ DVD_VIDEO_LB_LEN
;
816 dvd_file
->title_devs
[ i
] = dvdinput_open( full_path
);
817 dvdinput_title( dvd_file
->title_devs
[ i
], 0 );
818 dvd_file
->filesize
+= dvd_file
->title_sizes
[ i
];
820 if( !dvd_file
->title_devs
[ 0 ] ) {
829 dvd_file_t
*DVDOpenFile( dvd_reader_t
*dvd
, int titlenum
,
830 dvd_read_domain_t domain
)
832 char filename
[ MAX_UDF_FILE_NAME_LEN
];
834 /* Check arguments. */
835 if( dvd
== NULL
|| titlenum
< 0 )
839 case DVD_READ_INFO_FILE
:
840 if( titlenum
== 0 ) {
841 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.IFO" );
843 sprintf( filename
, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum
);
846 case DVD_READ_INFO_BACKUP_FILE
:
847 if( titlenum
== 0 ) {
848 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.BUP" );
850 sprintf( filename
, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum
);
853 case DVD_READ_MENU_VOBS
:
854 if( dvd
->isImageFile
) {
855 return DVDOpenVOBUDF( dvd
, titlenum
, 1 );
857 return DVDOpenVOBPath( dvd
, titlenum
, 1 );
860 case DVD_READ_TITLE_VOBS
:
861 if( titlenum
== 0 ) return 0;
862 if( dvd
->isImageFile
) {
863 return DVDOpenVOBUDF( dvd
, titlenum
, 0 );
865 return DVDOpenVOBPath( dvd
, titlenum
, 0 );
869 fprintf( stderr
, "libdvdread: Invalid domain for file open.\n" );
873 if( dvd
->isImageFile
) {
874 return DVDOpenFileUDF( dvd
, filename
);
876 return DVDOpenFilePath( dvd
, filename
);
880 void DVDCloseFile( dvd_file_t
*dvd_file
)
885 if( dvd_file
->dvd
->isImageFile
) {
888 for( i
= 0; i
< TITLES_MAX
; ++i
) {
889 if( dvd_file
->title_devs
[ i
] ) {
890 dvdinput_close( dvd_file
->title_devs
[i
] );
900 /* Internal, but used from dvd_udf.c */
901 int UDFReadBlocksRaw( dvd_reader_t
*device
, uint32_t lb_number
,
902 size_t block_count
, unsigned char *data
,
908 fprintf( stderr
, "libdvdread: Fatal error in block read.\n" );
912 ret
= dvdinput_seek( device
->dev
, (int) lb_number
);
913 if( ret
!= (int) lb_number
) {
914 fprintf( stderr
, "libdvdread: Can't seek to block %u\n", lb_number
);
918 ret
= dvdinput_read( device
->dev
, (char *) data
,
919 (int) block_count
, encrypted
);
923 /* This is using a single input and starting from 'dvd_file->lb_start' offset.
925 * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
926 * into the buffer located at 'data' and if 'encrypted' is set
927 * descramble the data if it's encrypted. Returning either an
928 * negative error or the number of blocks read. */
929 static int DVDReadBlocksUDF( dvd_file_t
*dvd_file
, uint32_t offset
,
930 size_t block_count
, unsigned char *data
,
933 return UDFReadBlocksRaw( dvd_file
->dvd
, dvd_file
->lb_start
+ offset
,
934 block_count
, data
, encrypted
);
937 /* This is using possibly several inputs and starting from an offset of '0'.
939 * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
940 * into the buffer located at 'data' and if 'encrypted' is set
941 * descramble the data if it's encrypted. Returning either an
942 * negative error or the number of blocks read. */
943 static int DVDReadBlocksPath( dvd_file_t
*dvd_file
, unsigned int offset
,
944 size_t block_count
, unsigned char *data
,
952 for( i
= 0; i
< TITLES_MAX
; ++i
) {
953 if( !dvd_file
->title_sizes
[ i
] ) return 0; /* Past end of file */
955 if( offset
< dvd_file
->title_sizes
[ i
] ) {
956 if( ( offset
+ block_count
) <= dvd_file
->title_sizes
[ i
] ) {
957 off
= dvdinput_seek( dvd_file
->title_devs
[ i
], (int)offset
);
958 if( off
< 0 || off
!= (int)offset
) {
959 fprintf( stderr
, "libdvdread: Can't seek to block %d\n",
961 return off
< 0 ? off
: 0;
963 ret
= dvdinput_read( dvd_file
->title_devs
[ i
], data
,
964 (int)block_count
, encrypted
);
967 size_t part1_size
= dvd_file
->title_sizes
[ i
] - offset
;
968 /* FIXME: Really needs to be a while loop.
969 * (This is only true if you try and read >1GB at a time) */
972 off
= dvdinput_seek( dvd_file
->title_devs
[ i
], (int)offset
);
973 if( off
< 0 || off
!= (int)offset
) {
974 fprintf( stderr
, "libdvdread: Can't seek to block %d\n",
976 return off
< 0 ? off
: 0;
978 ret
= dvdinput_read( dvd_file
->title_devs
[ i
], data
,
979 (int)part1_size
, encrypted
);
980 if( ret
< 0 ) return ret
;
981 /* FIXME: This is wrong if i is the last file in the set.
982 * also error from this read will not show in ret. */
984 /* Does the next part exist? If not then return now. */
985 if( i
+ 1 >= TITLES_MAX
|| !dvd_file
->title_devs
[ i
+ 1 ] )
989 off
= dvdinput_seek( dvd_file
->title_devs
[ i
+ 1 ], 0 );
990 if( off
< 0 || off
!= 0 ) {
991 fprintf( stderr
, "libdvdread: Can't seek to block %d\n",
993 return off
< 0 ? off
: 0;
995 ret2
= dvdinput_read( dvd_file
->title_devs
[ i
+ 1 ],
997 * (int64_t)DVD_VIDEO_LB_LEN
),
998 (int)(block_count
- part1_size
),
1000 if( ret2
< 0 ) return ret2
;
1004 offset
-= dvd_file
->title_sizes
[ i
];
1011 /* This is broken reading more than 2Gb at a time is ssize_t is 32-bit. */
1012 ssize_t
DVDReadBlocks( dvd_file_t
*dvd_file
, int offset
,
1013 size_t block_count
, unsigned char *data
)
1017 /* Check arguments. */
1018 if( dvd_file
== NULL
|| offset
< 0 || data
== NULL
)
1021 /* Hack, and it will still fail for multiple opens in a threaded app ! */
1022 if( dvd_file
->dvd
->css_title
!= dvd_file
->css_title
) {
1023 dvd_file
->dvd
->css_title
= dvd_file
->css_title
;
1024 if( dvd_file
->dvd
->isImageFile
) {
1025 dvdinput_title( dvd_file
->dvd
->dev
, (int)dvd_file
->lb_start
);
1027 /* Here each vobu has it's own dvdcss handle, so no need to update
1029 dvdinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start );
1033 if( dvd_file
->dvd
->isImageFile
) {
1034 ret
= DVDReadBlocksUDF( dvd_file
, (uint32_t)offset
,
1035 block_count
, data
, DVDINPUT_READ_DECRYPT
);
1037 ret
= DVDReadBlocksPath( dvd_file
, (unsigned int)offset
,
1038 block_count
, data
, DVDINPUT_READ_DECRYPT
);
1041 return (ssize_t
)ret
;
1044 int32_t DVDFileSeek( dvd_file_t
*dvd_file
, int32_t offset
)
1046 /* Check arguments. */
1047 if( dvd_file
== NULL
|| offset
< 0 )
1050 if( offset
> dvd_file
->filesize
* DVD_VIDEO_LB_LEN
) {
1053 dvd_file
->seek_pos
= (uint32_t) offset
;
1057 int DVDFileSeekForce(dvd_file_t
*dvd_file
, int offset
, int force_size
)
1059 /* Check arguments. */
1060 if( dvd_file
== NULL
|| offset
<= 0 )
1063 if( dvd_file
->dvd
->isImageFile
) {
1064 if( force_size
< 0 )
1065 force_size
= (offset
- 1) / DVD_VIDEO_LB_LEN
+ 1;
1066 if( dvd_file
->filesize
< force_size
) {
1067 dvd_file
->filesize
= force_size
;
1068 fprintf(stderr
, "libdvdread: Ignored size of file indicated in UDF.\n");
1072 if( offset
> dvd_file
->filesize
* DVD_VIDEO_LB_LEN
)
1075 dvd_file
->seek_pos
= (uint32_t) offset
;
1079 ssize_t
DVDReadBytes( dvd_file_t
*dvd_file
, void *data
, size_t byte_size
)
1081 unsigned char *secbuf_base
, *secbuf
;
1082 unsigned int numsec
, seek_sector
, seek_byte
;
1085 /* Check arguments. */
1086 if( dvd_file
== NULL
|| data
== NULL
)
1089 seek_sector
= dvd_file
->seek_pos
/ DVD_VIDEO_LB_LEN
;
1090 seek_byte
= dvd_file
->seek_pos
% DVD_VIDEO_LB_LEN
;
1092 numsec
= ( ( seek_byte
+ byte_size
) / DVD_VIDEO_LB_LEN
) +
1093 ( ( ( seek_byte
+ byte_size
) % DVD_VIDEO_LB_LEN
) ? 1 : 0 );
1095 secbuf_base
= (unsigned char *) malloc( numsec
* DVD_VIDEO_LB_LEN
+ 2048 );
1096 secbuf
= (unsigned char *)(((uintptr_t)secbuf_base
& ~((uintptr_t)2047)) + 2048);
1097 if( !secbuf_base
) {
1098 fprintf( stderr
, "libdvdread: Can't allocate memory "
1099 "for file read!\n" );
1103 if( dvd_file
->dvd
->isImageFile
) {
1104 ret
= DVDReadBlocksUDF( dvd_file
, (uint32_t) seek_sector
,
1105 (size_t) numsec
, secbuf
, DVDINPUT_NOFLAGS
);
1107 ret
= DVDReadBlocksPath( dvd_file
, seek_sector
,
1108 (size_t) numsec
, secbuf
, DVDINPUT_NOFLAGS
);
1111 if( ret
!= (int) numsec
) {
1112 free( secbuf_base
);
1113 return ret
< 0 ? ret
: 0;
1116 memcpy( data
, &(secbuf
[ seek_byte
]), byte_size
);
1117 free( secbuf_base
);
1119 DVDFileSeekForce(dvd_file
, dvd_file
->seek_pos
+ byte_size
, -1);
1123 ssize_t
DVDFileSize( dvd_file_t
*dvd_file
)
1125 /* Check arguments. */
1126 if( dvd_file
== NULL
)
1129 return dvd_file
->filesize
;
1132 int DVDDiscID( dvd_reader_t
*dvd
, unsigned char *discid
)
1136 int nr_of_files
= 0;
1138 /* Check arguments. */
1139 if( dvd
== NULL
|| discid
== NULL
)
1142 /* Go through the first 10 IFO:s, in order,
1143 * and md5sum them, i.e VIDEO_TS.IFO and VTS_0?_0.IFO */
1144 md5_init_ctx( &ctx
);
1145 for( title
= 0; title
< 10; title
++ ) {
1146 dvd_file_t
*dvd_file
= DVDOpenFile( dvd
, title
, DVD_READ_INFO_FILE
);
1147 if( dvd_file
!= NULL
) {
1149 size_t file_size
= dvd_file
->filesize
* DVD_VIDEO_LB_LEN
;
1150 char *buffer_base
= malloc( file_size
+ 2048 );
1151 char *buffer
= (char *)(((uintptr_t)buffer_base
& ~((uintptr_t)2047)) + 2048);
1153 if( buffer_base
== NULL
) {
1154 DVDCloseFile( dvd_file
);
1155 fprintf( stderr
, "libdvdread: DVDDiscId, failed to "
1156 "allocate memory for file read!\n" );
1160 bytes_read
= DVDReadBytes( dvd_file
, buffer
, file_size
);
1161 if( bytes_read
!= file_size
) {
1162 fprintf( stderr
, "libdvdread: DVDDiscId read returned %zd bytes"
1163 ", wanted %zd\n", bytes_read
, file_size
);
1164 DVDCloseFile( dvd_file
);
1165 free( buffer_base
);
1169 md5_process_bytes( buffer
, file_size
, &ctx
);
1171 DVDCloseFile( dvd_file
);
1172 free( buffer_base
);
1176 md5_finish_ctx( &ctx
, discid
);
1184 int DVDISOVolumeInfo( dvd_reader_t
*dvd
,
1185 char *volid
, unsigned int volid_size
,
1186 unsigned char *volsetid
, unsigned int volsetid_size
)
1188 unsigned char *buffer
, *buffer_base
;
1191 /* Check arguments. */
1195 if( dvd
->dev
== NULL
) {
1196 /* No block access, so no ISO... */
1200 buffer_base
= malloc( DVD_VIDEO_LB_LEN
+ 2048 );
1201 buffer
= (unsigned char *)(((uintptr_t)buffer_base
& ~((uintptr_t)2047)) + 2048);
1203 if( buffer_base
== NULL
) {
1204 fprintf( stderr
, "libdvdread: DVDISOVolumeInfo, failed to "
1205 "allocate memory for file read!\n" );
1209 ret
= UDFReadBlocksRaw( dvd
, 16, 1, buffer
, 0 );
1211 fprintf( stderr
, "libdvdread: DVDISOVolumeInfo, failed to "
1212 "read ISO9660 Primary Volume Descriptor!\n" );
1213 free( buffer_base
);
1217 if( (volid
!= NULL
) && (volid_size
> 0) ) {
1219 for(n
= 0; n
< 32; n
++) {
1220 if(buffer
[40+n
] == 0x20) {
1225 if(volid_size
> n
+1) {
1229 memcpy(volid
, &buffer
[40], volid_size
-1);
1230 volid
[volid_size
-1] = '\0';
1233 if( (volsetid
!= NULL
) && (volsetid_size
> 0) ) {
1234 if(volsetid_size
> 128) {
1235 volsetid_size
= 128;
1237 memcpy(volsetid
, &buffer
[190], volsetid_size
);
1239 free( buffer_base
);
1244 int DVDUDFVolumeInfo( dvd_reader_t
*dvd
,
1245 char *volid
, unsigned int volid_size
,
1246 unsigned char *volsetid
, unsigned int volsetid_size
)
1249 /* Check arguments. */
1253 if( dvd
->dev
== NULL
) {
1254 /* No block access, so no UDF VolumeSet Identifier */
1258 if( (volid
!= NULL
) && (volid_size
> 0) ) {
1259 ret
= UDFGetVolumeIdentifier(dvd
, volid
, volid_size
);
1264 if( (volsetid
!= NULL
) && (volsetid_size
> 0) ) {
1265 ret
= UDFGetVolumeSetIdentifier(dvd
, volsetid
, volsetid_size
);